Spring应用调用Google Sheets API时出现SSLHandshakeException问题
Spring调用Google Sheets API时的SSLHandshakeException问题排查与解决
错误信息
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) ~[na:na] at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:371) ~[na:na] at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:314) ~[na:na] at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:309) ~[na:na] at java.base/sun.security.ssl.CertificateMessage$T12CertificateConsumer.checkServerCerts(CertificateMessage.java:654) ~[na:na] at java.base/sun.security.ssl.CertificateMessage$T12CertificateConsumer.onCertificate(CertificateMessage.java:473) ~[na:na] at java.base/sun.security.ssl.CertificateMessage$T12CertificateConsumer.consume(CertificateMessage.java:369) ~[na:na] at java.base/sun.security.ssl.SSLHandshake.consume(SSLHandshake.java:396) ~[na:na] at java.base/sun.security.ssl.HandshakeContext.dispatch(HandshakeContext.java:480) ~[na:na] at java.base/sun.security.ssl.HandshakeContext.dispatch(HandshakeContext.java:458) ~[na:na] at java.base/sun.security.ssl.TransportContext.dispatch(TransportContext.java:201) ~[na:na] at java.base/sun.security.ssl.SSLTransport.decode(SSLTransport.java:172) ~[na:na] at java.base/sun.security.ssl.SSLSocketImpl.decode(SSLSocketImpl.java:1500) ~[na:na] at java.base/sun.security.ssl.SSLSocketImpl.readHandshakeRecord(SSLSocketImpl.java:1415) ~[na:na] at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:450) ~[na:na] at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:421) ~[na:na] at java.base/sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:580) ~[na:na]
已尝试操作
- 获取Google Sheets证书:
echo | openssl s_client -servername sheets.googleapis.com -connect sheets.googleapis.com:443 2>/dev/null | openssl x509 > google_certificate5.crt - 将证书导入JDK信任库:
操作后提示sudo keytool -importcert -trustcacerts -file /Users/pavel1709/Desktop/google_certificate5.crt -alias googleCert5 -keystore /Library/Java/JavaVirtualMachines/jdk-17.jdk/Contents/Home/lib/security/cacertsCertificate was added to keystore,但错误依旧。 - 检查并手动设置信任库路径:
设置后问题仍未解决。System.out.println("javax.net.ssl.trustStore = " + System.getProperty("javax.net.ssl.trustStore")); // 输出null System.setProperty("javax.net.ssl.trustStore", "/Library/Java/JavaVirtualMachines/jdk-17.jdk/Contents/Home/lib/security/cacerts");
可行解决方向
1. 导入完整证书链而非单张证书
Google的SSL证书采用链式结构,直接导出的服务器证书缺少中间CA证书,导致验证失败。执行以下命令导出完整证书链:
echo | openssl s_client -servername sheets.googleapis.com -connect sheets.googleapis.com:443 2>/dev/null | openssl crl2pkcs7 -nocrl -certfile /dev/stdin | openssl pkcs7 -print_certs -out google_full_chain.crt
再将完整链证书导入信任库:
sudo keytool -importcert -trustcacerts -file /Users/pavel1709/Desktop/google_full_chain.crt -alias googleFullChain -keystore /Library/Java/JavaVirtualMachines/jdk-17.jdk/Contents/Home/lib/security/cacerts
2. 确认应用实际使用的JDK版本
检查Spring运行时的JDK路径,确保和导入证书的JDK一致:
System.out.println("Java Home: " + System.getProperty("java.home"));
如果输出路径与目标JDK不符,需将证书导入应用实际使用的JDK的cacerts库。
3. 验证证书是否成功导入
用以下命令检查信任库中是否存在目标证书:
keytool -list -keystore /Library/Java/JavaVirtualMachines/jdk-17.jdk/Contents/Home/lib/security/cacerts -alias googleCert5
若提示Alias does not exist,说明之前导入操作失败,需重新导入并确认密码(默认密码为changeit)。
4. 通过JVM启动参数设置信任库
代码中设置属性可能在Spring初始化后生效,建议通过JVM启动参数传递:
-Djavax.net.ssl.trustStore=/Library/Java/JavaVirtualMachines/jdk-17.jdk/Contents/Home/lib/security/cacerts -Djavax.net.ssl.trustStorePassword=changeit
Spring Boot应用可在application.properties中配置:
spring.jvm.args=-Djavax.net.ssl.trustStore=/Library/Java/JavaVirtualMachines/jdk-17.jdk/Contents/Home/lib/security/cacerts -Djavax.net.ssl.trustStorePassword=changeit
5. 排查网络代理/防火墙干扰
若网络环境存在代理或防火墙,可能拦截SSL连接导致验证失败。尝试关闭代理,或配置Spring应用的代理SSL信任规则。
内容的提问来源于stack exchange,提问作者Pavel Molchanov
相关产品推荐
相关产品推荐

