Windows 10下JRE访问HTTPS站点出现SSL握手错误的排查与解决
问题描述
我构建了一个简单的HTTPS客户端,代码如下:
package src; import java.net.URL; import java.io.*; import javax.net.ssl.HttpsURLConnection; public class JavaHttpsExample { public static void main(String[] args) throws Exception { String httpsURL = "https://services.gradle.org/"; URL myUrl = new URL(httpsURL); HttpsURLConnection conn = (HttpsURLConnection)myUrl.openConnection(); InputStream is = conn.getInputStream(); InputStreamReader isr = new InputStreamReader(is); BufferedReader br = new BufferedReader(isr); String inputLine; while ((inputLine = br.readLine()) != null) { System.out.println(inputLine); } br.close(); } }
在Windows 10 Pro 版本19044上运行时出现错误:
Exception in thread "main" javax.net.ssl.SSLHandshakeException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target at java.base/sun.security.ssl.Alert.createSSLException(Alert.java:131) at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:371) at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:314) at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:309) ...
而在Windows 8.1 Pro 版本6.3.9600上连接成功。两台系统使用的JRE版本一致:
java version "17.0.5" 2022-10-18 LTS Java(TM) SE Runtime Environment (build 17.0.5+9-LTS-191) Java HotSpot(TM) 64-Bit Server VM (build 17.0.5+9-LTS-191, mixed mode, sharing)
添加-Djavax.net.debug=ssl:handshake参数运行后,Windows 10的日志片段显示服务器返回的证书颁发者为"CN=Generic Root CA 3, C=EN";而Windows 8的日志显示证书颁发者为"CN=Cloudflare Inc ECC CA-3, O="Cloudflare, Inc.", C=US",且包含完整证书链。
我尝试访问多个HTTPS站点均出现相同错误,但这些站点在浏览器中均可正常访问。
请问为何Windows 10会返回错误的证书?该如何解决此问题?
原因分析
从日志差异来看,Windows 10上Java客户端拿到的并非目标站点的真实证书,而是由"Generic Root CA 3"颁发的证书,核心原因是系统存在SSL拦截/劫持:
- 大概率是Windows 10上安装的第三方安全软件(杀毒、防火墙、VPN)、全局代理工具通过中间人(MITM)方式拦截HTTPS请求,替换了真实服务器证书;
- 浏览器能正常访问是因为这类拦截工具的根证书已被导入浏览器的信任列表,但Java默认信任库(
cacerts)未包含该根证书,导致SSL握手失败。
解决方法
方法1:排查并关闭SSL拦截源
- 暂时关闭系统中的安全软件、VPN、代理工具,重新运行Java客户端验证是否恢复正常;
- 若为企业环境强制拦截,联系IT部门获取对应的根证书。
方法2:将拦截根证书导入Java信任库
- 导出根证书:
打开控制面板 > 网络和 Internet > Internet 选项 > 内容 > 证书 > 受信任的根证书颁发机构,找到"Generic Root CA 3",导出为Base64编码的X.509(.CER)格式文件; - 使用
keytool导入:
打开命令提示符,进入JRE的lib/security目录(例:C:\Program Files\Java\jdk-17.0.5\jre\lib\security),执行命令:
默认信任库密码为keytool -importcert -file "C:\你的证书路径\generic-root-ca3.cer" -keystore cacerts -alias genericrootca3changeit,按提示确认导入即可。
方法3:让Java使用系统信任库(Java 9+支持)
从Java 9开始,可通过启动参数让Java直接调用Windows系统的信任证书存储,自动信任浏览器已认可的证书:
运行程序时添加以下参数:
-Djavax.net.ssl.trustStoreType=Windows-ROOT -Djavax.net.ssl.trustStore=NUL
内容的提问来源于stack exchange,提问作者GorDAn
相关产品推荐
相关产品推荐

