一、问题描述
在服务协议由 http 迁移至 https 时,我发现线上环境报错
javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path validation failed: java.security.cert.CertPathValidatorException: timestamp check failed
Caused by: java.security.cert.CertificateExpiredException: NotAfter: Sat May 30 18:48:38 CST 2020
二、问题排查
根据报错 CertificateExpiredException: NotAfter: Sat May 30 18:48:38 CST 2020 是因为证书过期,我立即联系客户技术部确认他们的证书是否过期,经查证没有过期
但是在生产和测试环境都报证书过期错误,奇怪的是本地环境可以正常访问
于是我在 https://whatsmychaincert.com/ 验证其证书链是否有问题,果然经查证其证书链中有过期的证书:
所以原因定位在客户服务器的证书链中存在过期证书
三、什么是证书链
简单来说,当客户端访问 TLS 服务器时,服务端会发送一个证书给客户端来证明其身份,客户端需要构建一条由服务端证书到根证书的证书链,以便对其进行验证。服务端很贴心,帮客户端返回了一条证书链
如果服务端的证书链是这个样子:
那么对于现代浏览器或客户端来说,C 没有过期就可以了,直接忽略了 D
但对于 OpenSSL 1.0.x or GnuTLS 来说,它们会一直检测到 D,因为 D 已经失效了 2019(现在是 2021 年),所以会返回证书过期
四、解决方案
鉴于此,我赶紧去生产和测试机器查了一下 openssl 的版本,果然是 OpenSSL 1.0.x,和上述结论一致。本地环境用的是 LibreSSL 2.8.3,因此访问没有问题
4.1 直接禁用 ssl 证书校验
```java
// 在调用SSL之前需要重写验证方法,取消检测 SSL
X509TrustManager trustManager = new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
}
@Override
public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
};
SSLContext ctx = SSLContext.getInstance(SSLConnectionSocketFactory.TLS);
ctx.init(null, new TrustManager[]{trustManager}, null);
SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(ctx, NoopHostnameVerifier.INSTANCE);
Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", PlainConnectionSocketFactory.INSTANCE)
.register("https", socketFactory).build();
// 创建ConnectionManager,添加Connection配置信息
connectionManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
connectionManager.setMaxTotal(MAX_CONNECTION_NUM);
connectionManager.setDefaultMaxPerRoute(MAX_PER_ROUTE);
HttpClients.custom().setDefaultRequestConfig(requestConfig).setConnectionManager(connectionManager).build();
4.2 更新服务器 openssl 库版本
4.3 更新服务端证书
访问 https://whatsmychaincert.com/,直接点击链接下载新证书,安装即可
五、其他
证书类型验证网站:https://csr.chinassl.net/ssl-checker.html
证书过期问题:https://whatsmychaincert.com
https://www.agwa.name/blog/post/fixing_the_addtrust_root_expiration#2099-client_operators