調用 HTTPS WebService 時,若服務端證書不在 JDK 信任庫中,會拋出 SSLHandshakeException: PKIX path building failed。本文提供完整解決方案:使用 InstallCert 獲取證書 → 導入信任庫 → 配置調用端驗證。
核心邏輯:建立 SSL 連接 → 捕獲服務端證書鏈 → 交互式選擇導入 jssecacerts 文件。
public class InstallCert {
public static void main(String[] args) throws Exception {
// 1. 解析參數 host:port [passphrase]
// 2. 定位信任庫:優先 ./jssecacerts → $JAVA_HOME/lib/security/jssecacerts → cacerts
// 3. 初始化 SSLContext,掛載自定義 TrustManager 捕獲證書鏈
// 4. 建立 SSLSocket 連接目標主機,執行 startHandshake()
// 5. 列出證書鏈詳情(Subject、Issuer、SHA1、MD5)
// 6. 用戶選擇導入哪個證書,寫入 jssecacerts
}
}
SavingTrustManager:包裝默認 X509TrustManager,在 checkServerTrusted 時攔截證書鏈MessageDigest:計算 SHA1/MD5 指紋供人工核對KeyStore:操作 jssecacerts 文件,setCertificateEntry 添加信任條目# Eclipse 運行配置
Main class: com.InstallCert
Program arguments: www.baidu.com 443 changeit
# 或直接域名,默認端口 443、密碼 changeit
Enter certificate to add to trusted keystore or 'q' to quit: [1]jssecacerts 於項目目錄將生成的 jssecacerts 複製到 JDK 信任庫目錄:
# Windows 示例
copy jssecacerts "C:\Program Files\Java\jdk1.8.0_xxx\jre\lib\security\"
# Linux/Mac
cp jssecacerts $JAVA_HOME/jre/lib/security/
static {
// 1. 主機名驗證器:允許目標域名
HttpsURLConnection.setDefaultHostnameVerifier((hostname, session) ->
"www.baidu.com".equals(hostname)
);
// 2. 指定信任庫路徑與密碼
System.setProperty("javax.net.ssl.trustStore",
"C:\\Program Files\\Java\\jdk1.8.0_xxx\\jre\\lib\\security\\jssecacerts");
System.setProperty("javax.net.ssl.trustStorePassword", "changeit");
}
| 現象 | 原因 | 解決 |
|---|---|---|
| 仍報 PKIX path building failed | trustStore 路徑錯誤或未生效 | 檢查路徑、確認 JVM 讀取到該文件(-Djavax.net.debug=ssl 排查) |
| Hostname verification failed | 證書 CN/SAN 不匹配請求域名 | HostnameVerifier 返回 true、或證書包含正確域名 |
| 憑證過期/未生效 | 系統時間錯或證書確實過期 | 同步時間、更新證書 |
SSLContext → TrustManager[] → X509TrustManager 驗證服務端證書jssecacerts 優先於 cacerts,應用層證書隔離於系統級HTTPS 調用失敗 90% 為證書信任問題。InstallCert 是 JDK 官方提供的經典工具,掌握「獲取→導入→配置」三步曲,配合 -Djavax.net.debug=ssl 調試,可解決絕大多數握手失敗場景。生產環境建議使用正規 CA 簽發證書,避免自簽名證書維護成本。