發生 PKIX path building failed 錯誤的程式碼 (Line 82)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 | package test.service; import java.io.IOException; import java.io.InputStream; import java.io.StringReader; import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.List; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; import org.apache.commons.csv.CSVFormat; import org.apache.commons.csv.CSVParser; import org.apache.commons.csv.CSVRecord; import org.apache.commons.io.IOUtils; import org.apache.http.HttpHost; import org.apache.http.HttpResponse; import org.apache.http.client.config.RequestConfig; import org.apache.http.client.methods.HttpGet; import org.apache.http.conn.socket.LayeredConnectionSocketFactory; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import test.vo.Bank; import test.vo.PostOffice; public class OpenDataService { private static final String BANK_OPEN_DATA_URL = "https://quality.data.gov.tw/dq_download_csv.php?nid=6041&md5_url=4cbb7958872cb677421021ef63b2e2b9"; public List<Bank> parseBankData() throws IOException { String csvData = ""; try { csvData = getBankData(); } catch (Exception e1) { throw new RuntimeException("金融機構基本資料 取得失敗, 錯誤原因: " + e1.getMessage(), e1); } List<Bank> bankData = new ArrayList<>(); String[] FILE_HEADER = { "類別", "總機構代號", "分支機構代號", "機構名稱", "地址", "緯度", "經度", "電話", "負責人", "異動日期", "金融機構網址" }; CSVFormat csvFileFormat = CSVFormat.DEFAULT.withHeader(FILE_HEADER); try (CSVParser csvFileParser = new CSVParser(new StringReader(csvData), csvFileFormat);) { List<CSVRecord> csvRecords = csvFileParser.getRecords(); for (int i = 1; i < csvRecords.size(); i++) { CSVRecord record = csvRecords.get(i); String type = record.get("類別"); String headquarterCode = record.get("總機構代號"); String branchCode = record.get("分支機構代號"); String branchName = record.get("機構名稱"); String address = record.get("地址"); String latitude = record.get("緯度"); String longitude = record.get("經度"); String phone = record.get("電話"); String representative = record.get("負責人"); String updateDate = record.get("異動日期"); String url = record.get("金融機構網址"); Bank bank = Bank.builder().type(type).headquarterCode(headquarterCode).branchCode(branchCode) .branchName(branchName).address(address).latitude(latitude).longitude(longitude).phone(phone) .representative(representative).updateDate(updateDate).url(url).build(); bankData.add(bank); } } catch (IOException e) { throw new RuntimeException("金融機構基本資料 解析失敗, 錯誤原因: " + e.getMessage(), e); } return bankData; } public String getBankData() throws Exception { String bankDataStr = ""; InputStream inputStream = null; try (CloseableHttpClient httpClient = HttpClients.createDefault();) { HttpGet httpGet = new HttpGet(BANK_OPEN_DATA_URL); HttpResponse httpResponse = httpClient.execute(httpGet); inputStream = httpResponse.getEntity().getContent(); bankDataStr = IOUtils.toString(inputStream, "UTF-8"); } catch (IOException e) { throw e; } finally { IOUtils.closeQuietly(inputStream); } return bankDataStr; } } |
錯誤訊息如下:
Exception in thread "main" java.lang.RuntimeException: 金融機構基本資料 取得失敗, 錯誤原因: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target at test.service.OpenDataService.parseBankData(OpenDataService.java:41) at test.OpenDataClient.main(OpenDataClient.java:12) Caused by: javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target at sun.security.ssl.Alerts.getSSLException(Alerts.java:192) at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1959) at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:302) at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:296) at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1514) at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:216) at sun.security.ssl.Handshaker.processLoop(Handshaker.java:1026) at sun.security.ssl.Handshaker.process_record(Handshaker.java:961) at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1072) at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1385) at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1413) at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1397)
How-To
依據參考文獻,透過暴力法,overwrite TrustManager,使其忽略檢查、校驗。程式碼改寫如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 | package test.service; import java.io.IOException; import java.io.InputStream; import java.io.StringReader; import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.List; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; import org.apache.commons.csv.CSVFormat; import org.apache.commons.csv.CSVParser; import org.apache.commons.csv.CSVRecord; import org.apache.commons.io.IOUtils; import org.apache.http.HttpHost; import org.apache.http.HttpResponse; import org.apache.http.client.config.RequestConfig; import org.apache.http.client.methods.HttpGet; import org.apache.http.conn.socket.LayeredConnectionSocketFactory; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import test.vo.Bank; import test.vo.PostOffice; public class OpenDataService { private static final String BANK_OPEN_DATA_URL = "https://quality.data.gov.tw/dq_download_csv.php?nid=6041&md5_url=4cbb7958872cb677421021ef63b2e2b9"; public List<Bank> parseBankData() throws IOException { String csvData = ""; try { csvData = getBankData(); } catch (Exception e1) { throw new RuntimeException("金融機構基本資料 取得失敗, 錯誤原因: " + e1.getMessage(), e1); } List<Bank> bankData = new ArrayList<>(); String[] FILE_HEADER = { "類別", "總機構代號", "分支機構代號", "機構名稱", "地址", "緯度", "經度", "電話", "負責人", "異動日期", "金融機構網址" }; CSVFormat csvFileFormat = CSVFormat.DEFAULT.withHeader(FILE_HEADER); try (CSVParser csvFileParser = new CSVParser(new StringReader(csvData), csvFileFormat);) { List<CSVRecord> csvRecords = csvFileParser.getRecords(); for (int i = 1; i < csvRecords.size(); i++) { CSVRecord record = csvRecords.get(i); String type = record.get("類別"); String headquarterCode = record.get("總機構代號"); String branchCode = record.get("分支機構代號"); String branchName = record.get("機構名稱"); String address = record.get("地址"); String latitude = record.get("緯度"); String longitude = record.get("經度"); String phone = record.get("電話"); String representative = record.get("負責人"); String updateDate = record.get("異動日期"); String url = record.get("金融機構網址"); Bank bank = Bank.builder().type(type).headquarterCode(headquarterCode).branchCode(branchCode) .branchName(branchName).address(address).latitude(latitude).longitude(longitude).phone(phone) .representative(representative).updateDate(updateDate).url(url).build(); bankData.add(bank); } } catch (IOException e) { throw new RuntimeException("金融機構基本資料 解析失敗, 錯誤原因: " + e.getMessage(), e); } return bankData; } public String getBankData() throws Exception { String bankDataStr = ""; InputStream inputStream = null; try (CloseableHttpClient httpClient = createHttpsClient();) { HttpGet httpGet = new HttpGet(BANK_OPEN_DATA_URL); HttpResponse httpResponse = httpClient.execute(httpGet); inputStream = httpResponse.getEntity().getContent(); bankDataStr = IOUtils.toString(inputStream, "UTF-8"); } catch (NoSuchAlgorithmException | KeyManagementException | IOException e) { throw e; } finally { IOUtils.closeQuietly(inputStream); } return bankDataStr; } /** * 解決 PKIX path building failed 問題 * * @return HttpClient * @throws NoSuchAlgorithmException * @throws KeyManagementException */ private CloseableHttpClient createHttpsClient() throws NoSuchAlgorithmException, KeyManagementException { // Create a trust manager that does not validate certificate chains TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() { @Override public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException { } @Override public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException { } @Override public X509Certificate[] getAcceptedIssuers() { return null; } } }; SSLContext ctx = SSLContext.getInstance("TLS"); ctx.init(null, trustAllCerts, null); LayeredConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(ctx); CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslSocketFactory).build(); return httpclient; } } |
Reference
[1] http://www.aneasystone.com/archives/2016/04/java-and-https.html