Loading... # 前提介绍 在掉坑的过程中,面对老证书的CA为DST,而新证书的CA为ISRG,客户端可以正常调用老证书,而无法正确调用新证书。虽然购买了TrustAsia付费证书,但是对于到底为什么不行还可以进一步探索一下。 下图表示的是tls验证错误。切换华为CDN的时候忘记了打开TLS1开关。 ![2024-02-19T08:37:18.png][1] 下图表示的证书路径错误,是个大问题。 ![2024-02-20T06:20:11.png][2] 通过尝试,使用jdk7直接复现了该问题。这一点与客户端技术人员所说,客户端程序采用jdk8符合。 ![2024-02-19T08:37:43.png][3] # 复现 复现的主要过程是,使用jetbrain开发工具,本地安装jdk7和jdk8,借助copilot编写简单的java程序,实现对站点www.op123.ren(ISRG证书)和www.sddts.cn(DST证书)和bapi.zcyun.cn(aaa)的测试。 ![2024-02-19T08:39:00.png][4] java代码 ``` import javax.net.ssl.HttpsURLConnection; import java.io.BufferedReader; import java.io.InputStreamReader; import java.net.URL; import java.security.cert.Certificate; import java.security.cert.X509Certificate; public class Main { public static void main(String[] args) { System.setProperty("javax.net.debug", "ssl"); try { // URL url = new URL("https://bapi.zcyun.cn"); // URL url = new URL("https://www.op123.ren"); URL url = new URL("https://www.sddts.cn"); HttpsURLConnection conn = (HttpsURLConnection) url.openConnection(); conn.connect(); Certificate[] certs = conn.getServerCertificates(); System.out.println("Number of certificates: " + certs.length); for (Certificate cert : certs) { X509Certificate x509Cert = (X509Certificate) cert; System.out.println("Version: " + x509Cert.getVersion()); System.out.println("Subject: " + x509Cert.getSubjectDN()); System.out.println("Signature Algorithm: " + x509Cert.getSigAlgName()); System.out.println("Key: " + x509Cert.getPublicKey()); System.out.println("Validity: From " + x509Cert.getNotBefore() + " to " + x509Cert.getNotAfter()); System.out.println("Issuer: " + x509Cert.getIssuerDN()); System.out.println("------------------------"); } // Fetch the website content BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream())); String line; StringBuilder content = new StringBuilder(); while ((line = reader.readLine()) != null) { content.append(line); } reader.close(); System.out.println("Website Content: " + content.toString()); } catch (Exception e) { e.printStackTrace(); } } } ``` # 针对jdk1.7版本测试 ## 访问bapi.zcyun.cn ![2024-02-19T08:52:45.png][5] ![2024-02-19T08:50:42.png][6] ![2024-02-19T08:51:06.png][7] ## 访问www.sddts.cn ![2024-02-19T08:53:06.png][8] ![2024-02-19T08:54:39.png][9] ![2024-02-19T08:53:42.png][10] ## 访问www.op123.ren ![2024-02-19T08:56:03.png][11] ![2024-02-19T08:55:56.png][12] 采用jdk7最后一个版本b147测试依旧不能正常访问。而且,jdk7默认情况下只能使用TLSv1,所以,测试过程中使用的Nginx或者bt面板提供的Nginx服务需要在配置文件中为配置参数ssl_protocols加上TLSv1。 ![2024-02-19T09:05:59.png][13] # 测试jdk8 ## 从低版本测试 使用xxx版本访问三个测试站点都没有问题。但是,估计早起的jdk8版本中可能有不行的。 从jdk早起版本开始测试,直到测试到jdk8u131,出现了与问题一样的现象,CA为DST生效,但是CA ISRG失效。 ![2024-02-19T09:24:34.png][14] ## 测试将jdk8u151的caccert文件拷贝到jdk8u131的对应位置 不可以直接拷贝整个security文件夹内容,会报错 ![2024-02-19T09:31:16.png][15] 只是拷贝jre/lib/security/cacerts文件到对应目录覆盖原有文件后,可以正常访问CA为ISRG的测试站点。 总结,在复现现象中,测试多个jdk8版本,从jdk8u131开始,到jdk8u151之间的某一个版本,与客户端技术人员所使用的jdk版本一致。 第二天 ,使用脚本将jdk8u131和jdk8u151的jre/lib/security/cacerts文件进行了对比,发现u8131只有CA DST的内容且过期,而且没有CA ISRG的内容;相比之下,8u151既有CA DST也有CA ISRG。 ![2024-02-20T01:38:12.png][16] 最终,测试了jdk8中的112 121 131 141 151,发现包括131版本在内以及之前的jdk8版本,均具有CA DST,但是不具有CA ISRG。据此推测,第三方公司使用的jdk版本在jdk131之前。 拒gpt给出的jdk8大致版本历史如下: ``` JDK 8u5 (2014-04-15): 早期的更新,包括bug修复。 JDK 8u20 (2014-08-19): 引入了Java Mission Control 5.4。 JDK 8u40 (2015-03-03): 增加了新的JavaFX功能,以及性能和安全性改进。 JDK 8u60 (2015-08-18): 包括对JavaFX的改进,性能增强和bug修复。 JDK 8u65/8u66 (2015-10-20): 主要是安全更新和bug修复。 JDK 8u91/8u92 (2016-04-19): 引入了一些新的特性,包括安全性增强和性能改进。 JDK 8u101/8u102 (2016-07-19): 安全性更新和bug修复。 JDK 8u111/8u112 (2016-10-18): 安全性更新和bug修复。 JDK 8u121 (2017-01-17): 引入了额外的安全强化功能。 JDK 8u131 (2017-04-18): 安全性和性能更新。 JDK 8u141 (2017-07-18): 包含安全性更新和bug修复。 JDK 8u151/8u152 (2017-10-17): 安全性更新,以及一些新增特性和bug修复。 JDK 8u161/8u162 (2018-01-16): 安全性更新和性能改进。 JDK 8u171/8u172 (2018-04-17): 包括安全性更新和bug修复。 JDK 8u181 (2018-07-17): 安全性更新和一些小的改进。 ... JDK 8u292 (2021-04-20): 包含了安全性更新和性能改进。 从JDK 8u211开始,Oracle对JDK的许可政策进行了更改,对于商业使用,需要根据Oracle的新许可协议获得许可。 ``` # 尝试使用keytool命令将CA文件加入到jdk的cacerts文件中 登录申请证书的服务器,进入到相应目录,可以看到ca.crt文件 ![2024-02-20T05:18:00.png][17] 查看根证书 ``` openssl x509 -in ca.cer -text -noout ``` ![2024-02-20T05:23:33.png][18] 添加 ``` keytool -import -trustcacerts -keystore /root/.jdks/jdk1.8.0_131/jre/lib/security/cacerts -storepass changeit -noprompt -alias isrgrootx1 -file /root/.jdks/jdk1.8.0_131/jre/lib/security/zcyun.cn.ca.crt ``` 使用命令将 ![2024-02-20T05:21:28.png][19] ![2024-02-20T05:22:20.png][20] 删除 ``` keytool -delete -alias isrgrootx1 -keystore /root/.jdks/jdk1.8.0_131/jre/lib/security/cacerts -storepass changeit ``` ![2024-02-20T05:20:33.png][21] ![2024-02-20T05:21:06.png][22] # 进一步分析,为什么jdk中对于CA DST已经过期,依旧可以正常访问 jdk中的cacert文件中,包括很多根证书,这些根证书在https验证过程中,是通过哪个名称或者id对应起来的。 分析脚本 ``` #!/bin/bash export LANG=en_US.UTF-8 export LANGUAGE=en_US # 设置Java的cacerts文件的路径 #CACERTS_PATH="/usr/java/jdk1.8.0_144/jre/lib/security/cacerts" CACERTS_PATH="/root/.jdks/jdk1.8.0_131/jre/lib/security/cacerts" #CACERTS_PATH="/root/.jdks/jdk1.8.0_131/jre/lib/security/cacerts.8u131" #CACERTS_PATH="/root/.jdks/jdk1.8.0_131/jre/lib/security/cacerts.8u151" # 默认的cacerts密码 STOREPASS="changeit" #keytool -list -v -keystore $CACERTS_PATH -storepass $STOREPASS | awk '/Alias name:/ {alias=$3} /Valid from:/ {print "Certificate: " alias ", " $0; alias=""}' keytool -list -v -keystore $CACERTS_PATH -storepass $STOREPASS | awk '/Alias name:/ {alias=$3} /Owner:/ {owner=$0} /Issuer:/ {issuer=$0} /Valid from:/ {print "Certificate: " alias "\n" owner "\n" issuer "\n" $0; alias=""; owner=""; issuer=""}' ``` ``` 在HTTPS验证过程中,cacerts文件中的根证书与服务器提供的证书链进行匹配,主要是通过证书的“主题”(Subject)和“颁发者”(Issuer)字段来实现的。 ``` 在一次https访问过程中,服务器返回的根证书为主要内容为:issuer=O=Digital Signature Trust Co., CN=DST Root CA X3,notBefore=Jan 20 19:14:03 2021 GMT notAfter=Sep 30 18:14:03 2024 GMT。而jdk中的同样的证书issuer具有不同的有效期,其有效期只是到2021年9月。为什么jdk依旧可以访问呢? ![2024-02-20T06:07:39.png][23] ![2024-02-20T06:08:07.png][24] gpt给的回复: ``` 在HTTPS通信过程中,当遇到服务器返回的根证书(如您提到的由Digital Signature Trust Co.颁发的DST Root CA X3)有效期与JDK中存储的同一根证书有效期不一致的情况时,连接依然可能成功。这种情况通常由以下几个因素中的一个或多个导致: 中间证书的使用:通常,在SSL/TLS握手过程中,服务器不仅返回其自身的证书,还可能返回一个或多个中间证书,这些中间证书形成了一个信任链,直至一个根证书。只要客户端信任链中的某个中间证书或根证书,且该证书未过期,客户端就能够建立信任关系。即使根证书在JDK的cacerts中过期,如果中间证书有效,并且由客户端信任的根证书颁发,连接仍然可以建立。 证书锚点(Trust Anchor)更新:某些情况下,证书颁发机构(CA)可能会更新根证书,延长其有效期。如果JDK或操作系统得到更新,以包含新的或更新的根证书,即使旧的根证书过期,新的信任锚(Trust Anchor)也可以用于验证证书链。在您的例子中,可能存在一个更新版本的DST Root CA X3证书,或者有一个接替的证书被JDK信任。 证书有效期宽容:在某些环境中,应用程序或库可能对证书的有效期有一定的宽容度,尤其是在证书链验证过程中。这不是最常见的情况,因为它可能引入安全风险。 交叉签名:根证书或中间证书可能通过交叉签名获得额外的信任路径。例如,一个根证书虽然在cacerts中过期,但它可能被另一个仍然有效且被信任的根证书交叉签名。这创建了一个备用的信任路径,允许证书链验证成功。 在您描述的情况下,最可能的解释是JDK信任的证书链中存在一个有效的中间证书,或者JDK已经包含了更新的根证书信任锚,这使得即使DST Root CA X3在cacerts中显示为已过期,HTTPS连接仍然可以成功建立。此外,某些SSL/TLS客户端实现可能在评估证书链时,优先考虑链中的有效证书,而不是直接依赖于根证书的有效性。 ``` # 最后,对于购买的TrustAsia证书,jdk支持程度如何呢? 至少用到2029年 ![2024-02-20T06:10:31.png][25] ![2024-02-20T06:10:48.png][26] # 回头看 本次jdk访问异常的原因是由于,letsencrypt官方过去采用的根证书DST即将过期,而本身过期是没有问题的,jdk8u131依旧可以访问,但是问题出在官方更换了根证书isrg,而isrg证书直到8u141开始才加入。 jdk8u131及之前版本只有dst根证书,没有isrg根证书,而操蛋的letsencrypt近期更换了根证书,抛弃了dst。老版本只有dst没有isrg。即使jdk8u131版本已经很老,dst的日期可能过期,但是依旧可能可以正常使用。 所以,以后遇到pkix或者说证书链路径错误的问题,有以下几种解决办法 * 购买专业证书,兼容性更好,可用时间更长 * 通过keytool命令手动添加CA证书到cacerts文件 * 更新至jdk的更高版本 # 客户反馈jdk版本为aliyun出品 ![2024-02-20T07:45:44.png][27] 对方版本大概是`dragonwell-8.4.7_jdk8u152-ga`,但是docker pull返回没有。 ``` dragonwell-registry.cn-hangzhou.cr.aliyuncs.com/dragonwell/dragonwell:dragonwell-8.4.7_jdk8u152-ga ``` 下载另一个版本测试 ``` docker run -itd dragonwell-registry.cn-hangzhou.cr.aliyuncs.com/dragonwell/dragonwell:dragonwell-8.10.11_jdk8u322-ga docker ps docker exec -itd dragonwell-registry.cn-hangzhou.cr.aliyuncs.com/dragonwell/dragonwell:dragonwell-8.10.11_jdk8u322-ga ``` ``` keytool -list -v -keystore $JAVA_HOME/jre/lib/security/cacerts -storepass changeit | awk '/Alias name:/ {alias=$3} /Owner:/ {owner=$0} /Issuer:/ {issuer=$0} /Valid from:/ {print "Certificate: " alias "\n" owner "\n" issuer "\n" $0; alias=""; owner=""; issuer=""}' | grep -i dst keytool -list -v -keystore $JAVA_HOME/jre/lib/security/cacerts -storepass changeit | awk '/Alias name:/ {alias=$3} /Owner:/ {owner=$0} /Issuer:/ {issuer=$0} /Valid from:/ {print "Certificate: " alias "\n" owner "\n" issuer "\n" $0; alias=""; owner=""; issuer=""}' | grep -i isrg ``` 返回结果正常 ![2024-02-20T07:47:16.png][28] 虽然openjdk从jdk8u131之后开始已经可以正常使用,但是这个jdk是ajdk,是aliyun出品,可能做了 稍许更改也不一定。 后来,对方兄弟使用命令查了下,果不其然,有dst 但是没有isrg。 [1]: https://www.sddts.cn/usr/uploads/2024/02/1196188901.png [2]: https://www.sddts.cn/usr/uploads/2024/02/2229593121.png [3]: https://www.sddts.cn/usr/uploads/2024/02/988146300.png [4]: https://www.sddts.cn/usr/uploads/2024/02/668655562.png [5]: https://www.sddts.cn/usr/uploads/2024/02/1351899149.png [6]: https://www.sddts.cn/usr/uploads/2024/02/52023707.png [7]: https://www.sddts.cn/usr/uploads/2024/02/202027633.png [8]: https://www.sddts.cn/usr/uploads/2024/02/2842108629.png [9]: https://www.sddts.cn/usr/uploads/2024/02/2411792423.png [10]: https://www.sddts.cn/usr/uploads/2024/02/2664360425.png [11]: https://www.sddts.cn/usr/uploads/2024/02/245355992.png [12]: https://www.sddts.cn/usr/uploads/2024/02/3057290765.png [13]: https://www.sddts.cn/usr/uploads/2024/02/4065326251.png [14]: https://www.sddts.cn/usr/uploads/2024/02/1409260535.png [15]: https://www.sddts.cn/usr/uploads/2024/02/2368049733.png [16]: https://www.sddts.cn/usr/uploads/2024/02/2722216399.png [17]: https://www.sddts.cn/usr/uploads/2024/02/4055685533.png [18]: https://www.sddts.cn/usr/uploads/2024/02/2714183474.png [19]: https://www.sddts.cn/usr/uploads/2024/02/117968828.png [20]: https://www.sddts.cn/usr/uploads/2024/02/4110694735.png [21]: https://www.sddts.cn/usr/uploads/2024/02/2809629034.png [22]: https://www.sddts.cn/usr/uploads/2024/02/1989643939.png [23]: https://www.sddts.cn/usr/uploads/2024/02/1874695755.png [24]: https://www.sddts.cn/usr/uploads/2024/02/812561105.png [25]: https://www.sddts.cn/usr/uploads/2024/02/3287424832.png [26]: https://www.sddts.cn/usr/uploads/2024/02/1218836143.png [27]: https://www.sddts.cn/usr/uploads/2024/02/3795440118.png [28]: https://www.sddts.cn/usr/uploads/2024/02/1417581185.png 最后修改:2024 年 05 月 11 日 © 允许规范转载 赞 如果觉得我的文章对你有用,请随意赞赏