Amazon Corretto JDK 11+Tomcat9从MMC读取证书3分钟后SSL请求失败
使用Amazon Corretto JDK 11 + Tomcat 9.x,配置Tomcat从Windows MMC(Windows-ROOT信任库)读取SSL证书,应用前3分钟可正常处理Web服务请求,但3分钟后所有请求失败,报错如下:
javax.net.ssl|ERROR|24 89|https-openssl-apr-8443-exec-4|2024-02-01 10:10:26.775 EST|TransportContext.java:341|Fatal (CERTIFICATE_UNKNOWN): PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target (
"throwable" : {
sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at java.base/sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:439)
at java.base/sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:306)
at java.base/sun.security.validator.Validator.validate(Validator.java:264)
at java.base/sun.security.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:313)
at java.base/sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:222)
at java.base/sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:129)
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at java.base/sun.security.provider.certpath.SunCertPathBuilder.build(SunCertPathBuilder.java:141)
at java.base/sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:126)
at java.base/java.security.cert.CertPathBuilder.build(CertPathBuilder.java:297)
at java.base/sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:434)
... 154 more}
添加以下Java参数开启调试日志(指定信任库类型为Windows-ROOT):
-Djavax.net.ssl.trustStoreType=Windows-ROOT -Djavax.net.debug=ssl:handshake:verbose:keymanager:trustmanager -Djava.security.debug=certpath
调试发现证书被加载两次:第一次加载360个(对应Windows MMC根证书),第二次仅加载150个,问题出现在第二次加载后。通过日志中X509TrustManagerImpl.java:79|adding as trusted certificates条目统计证书数量。
将目标证书加入JDK的cacerts文件后问题解决,但存在两个疑问:
- 第二次加载的证书来自哪里?
- 若来自
cacerts,为何指定Windows-ROOT时仍会加载该文件?
1. 第二次加载的证书来源
第二次加载的150个证书确实来自JDK默认的cacerts信任库。
2. 指定Windows-ROOT仍加载cacerts的原因
这是Corretto JDK 11结合Tomcat运行时的特定逻辑导致的,核心原因包括:
- Tomcat线程池触发SSL上下文重建:Tomcat的APR/nio线程池在运行一段时间后,可能触发SSL上下文重新初始化。第一次初始化严格遵循
javax.net.ssl.trustStoreType=Windows-ROOT配置,加载系统根证书;但后续重建时,部分场景下JVM参数未被正确传递,导致JDK fallback到默认的cacerts信任库。 - Windows-ROOT信任库的线程访问限制:Corretto JDK的
Windows-ROOT实现依赖Windows CryptoAPI,当Tomcat工作线程处于特殊上下文(如线程重新分配)时,可能无法正常访问系统证书存储,JDK会自动切换到cacerts作为备选。 - Tomcat配置优先级冲突:如果
server.xml的<Connector>节点中隐式配置了truststoreFile(即使未显式写值),会覆盖JVM层面的trustStoreType配置,导致后续加载cacerts。
验证与修复建议
- 显式配置Tomcat Connector信任库:在
server.xml的HTTPS Connector中直接指定信任库类型,避免依赖JVM参数的传递问题:<Connector port="8443" protocol="org.apache.coyote.http11.Http11AprProtocol" maxThreads="150" SSLEnabled="true"> <SSLHostConfig> <Certificate certificateKeystoreFile="conf/localhost-rsa.jks" type="RSA" truststoreType="Windows-ROOT"/> </SSLHostConfig> </Connector> - 禁用默认信任库 fallback:添加JVM参数
-Djavax.net.ssl.trustStore=NONE,强制JDK仅使用指定的Windows-ROOT信任库。 - 升级Corretto JDK版本:部分旧版Corretto 11存在Windows信任库的线程安全问题,升级到最新稳定版可解决上下文切换导致的加载异常。
内容的提问来源于stack exchange,提问作者Mounika

