Tomcat9中Java调用Google Web Risk服务遇PKIX证书路径构建失败求助
问题描述
环境与错误
- 基于Apache HttpClient开发的Java应用,部署在Tomcat 9服务器(无特殊配置),调用Google Web Risk服务时出现SSL握手错误:
PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
已排查内容
- 确认目标主机
https://webrisk.googleapis.com使用的根证书为GTS Root R1 - 通过以下命令验证本地Zulu 11 JVM信任库中存在该证书:
keytool -list -keystore /Library/Java/JavaVirtualMachines/zulu-11.jdk/Contents/Home/lib/security/cacerts -v
- 本地测试客户端无此问题,排除证书本身缺失,问题聚焦于应用加载信任库的方式
已尝试的无效方案
- 下载“GTS Root 1 cert”的.pem文件,通过SSLContext自定义配置编程加载,未成功
- 显式设置JVM参数指向验证过的cacerts文件,无效:
-Djavax.net.ssl.trustStore=/Library/Java/JavaVirtualMachines/zulu-11.jdk/Contents/Home/lib/security/cacerts -Djavax.net.ssl.trustStorePassword=changeit
握手日志
DEBUG org.apache.http.impl.conn.PoolingHttpClientConnectionManager - Connection request: [route: {s}->https://webrisk.googleapis.com:443][total available: 0; route allocated: 0 of 2; total allocated: 0 of 20] DEBUG org.apache.http.impl.conn.PoolingHttpClientConnectionManager - Connection leased: [id: 3][route: {s}->https://webrisk.googleapis.com:443][total available: 0; route allocated: 1 of 2; total allocated: 1 of 20] DEBUG org.apache.http.impl.execchain.MainClientExec - Opening connection {s}->https://webrisk.googleapis.com:443 DEBUG org.apache.http.impl.conn.DefaultHttpClientConnectionOperator - Connecting to webrisk.googleapis.com/142.251.142.202:443 DEBUG org.apache.http.conn.ssl.SSLConnectionSocketFactory - Connecting socket to webrisk.googleapis.com/142.251.142.202:443 with timeout 0 DEBUG org.apache.http.conn.ssl.SSLConnectionSocketFactory - Enabled protocols: [TLSv1.3, TLSv1.2] DEBUG org.apache.http.conn.ssl.SSLConnectionSocketFactory - Enabled cipher suites:[TLS_AES_256_GCM_SHA384, TLS_AES_128_GCM_SHA256, TLS_CHACHA20_POLY1305_SHA256, TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS_DHE_RSA_WITH_AES_256_GCM_SHA384, TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256, TLS_DHE_DSS_WITH_AES_256_GCM_SHA384, TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, TLS_DHE_DSS_WITH_AES_128_GCM_SHA256, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, TLS_DHE_DSS_WITH_AES_256_CBC_SHA256, TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, TLS_DHE_DSS_WITH_AES_128_CBC_SHA256, TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384, TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384, TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384, TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384, TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256, TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, TLS_DHE_RSA_WITH_AES_256_CBC_SHA, TLS_DHE_DSS_WITH_AES_256_CBC_SHA, TLS_DHE_RSA_WITH_AES_128_CBC_SHA, TLS_DHE_DSS_WITH_AES_128_CBC_SHA, TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA, TLS_ECDH_RSA_WITH_AES_256_CBC_SHA, TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDH_RSA_WITH_AES_128_CBC_SHA, TLS_RSA_WITH_AES_256_GCM_SHA384, TLS_RSA_WITH_AES_128_GCM_SHA256, TLS_RSA_WITH_AES_256_CBC_SHA256, TLS_RSA_WITH_AES_128_CBC_SHA256, TLS_RSA_WITH_AES_256_CBC_SHA, TLS_RSA_WITH_AES_128_CBC_SHA, TLS_EMPTY_RENEGOTIATION_INFO_SCSV] DEBUG org.apache.http.conn.ssl.SSLConnectionSocketFactory - Starting handshake DEBUG org.apache.http.impl.conn.DefaultManagedHttpClientConnection - http-outgoing-3: Shutdown connection DEBUG org.apache.http.impl.execchain.MainClientExec - Connection discarded DEBUG org.apache.http.impl.conn.PoolingHttpClientConnectionManager - Connection released: [id: 3][route: {s}->https://webrisk.googleapis.com:443][total available: 0; route allocated: 0 of 2; total allocated: 0 of 20] ERROR com.akamai.csi.pulsar.mitigation.client.GoogleWebRiskRestClient - PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
排查方向与解决方案
1. 确认Tomcat实际使用的JVM路径
本地验证的是Zulu 11的cacerts,但Tomcat运行时可能使用其他JDK/JRE:
- 检查Tomcat启动脚本(
catalina.sh/catalina.bat)中的JAVA_HOME配置,确认路径与Zulu 11一致 - 添加一个测试Servlet,输出
System.getProperty("java.home"),查看运行时JVM的实际路径
2. 检查Tomcat是否覆盖信任库配置
Tomcat的Connector配置可能覆盖JVM默认信任库:
- 查看
conf/server.xml中的<Connector>节点,是否设置了truststoreFile和truststorePass属性 - 如果存在上述配置,确保对应的信任库包含GTS Root R1证书
3. 验证Apache HttpClient的SSLContext配置
如果代码中自定义了SSLContext但未正确加载系统信任库,会导致默认信任库失效:
- 检查HttpClient创建代码,确认是否合并了系统默认信任库
- 正确的配置示例:
SSLContext sslContext = SSLContexts.custom() .loadTrustMaterial(TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm())) .build(); CloseableHttpClient client = HttpClients.custom() .setSSLContext(sslContext) .build();
4. 确认JVM参数是否传递给Tomcat
设置的-Djavax.net.ssl.trustStore参数可能未被Tomcat接收:
- 如果通过环境变量设置,确保Tomcat启动脚本引用了该变量
- 如果是IDE启动Tomcat,检查VM参数是否正确配置在启动项中
- 添加
-Djavax.net.debug=ssl参数启动Tomcat,查看SSL调试日志,确认实际加载的信任库路径
5. 检查证书链完整性
即使根证书存在,可能缺少中间证书导致路径构建失败:
- 使用
openssl s_client -connect webrisk.googleapis.com:443获取完整证书链 - 对比本地信任库,确认所有中间证书已被包含(或JVM能自动获取)
内容的提问来源于stack exchange,提问作者Tal Amishai
相关产品推荐
相关产品推荐

