前几天解决了一个SSL握手失败问题,避免了一场史诗级生产事故,如果在IBM JDK握手失败,而Oracle JDK确成功,那就很有可能是证书链中的证书被重签,注意是证书链,并非信任链,详细过程问题分析报告的形式记录如下。
预植V3服务器证书换发后IBM JDK握手失败问题分析报告
问题描述
4月11日预植V3服务器更换后在IBM JDK环境下握手失败,导致采取了临时回退措施。
问题分析责任人
xxx
问题跟踪
4月11日晚按预定计划升级标准预植V3和中行预植服务器证书,由于之前已经提示过运行部同事证书换发时保持各项参数与老证书不变,避免因主题可选名和主题变化导致的服务器证书不安全更换报错问题,同时在生产迁移之前,我们也在测试系统进行了充分验证,并且本次升级不涉及任何应用以及系统运行环境的改变,理论上说,这次升级应该顺风顺水,平淡无奇,然而一系列反常的事情就在这样的背景下发生了,并最终导致我们生产升级被迫回退,下面详细叙述具体过程。
晚8点接到测试人员反馈,使用现有客户端证书在IBM JDK下无法连接至中行预植服务器,使用Oracle JDK能够正常连接,此时服务器证书尚未更换,接到测试反馈后,首先收集信息,Oracle JDK可以连通,说明证书链配置应该是正确的,中行预植服务器为Oracle JDK 1.6版本,是否为不同版本JDK在SSL握手过程中存在兼容性问题?然而中行生产PCMS客户端使用的JDK版本同样为IBM JDK1.6版本,目前生产上的PCMS客户端肯定是可以连通的,虽然我们使用的IBM JDK 与中行生产版本并不完全相同,但大版本均为1.6,按理说不应该有如此大的差异,种种“不合理”的现象无法解释,诡异之神再一次降临到了不幸的程序员身上。
测试同事提供了报错IBM
JDK版本,在本地搭建验证环境后,复现了上述问题,开启SSL debug参数,查看握手过程和报错日志,
No
trusted certificate found!!!这是个很常见的报错,一般证书信任链有问题的时候才会报这样的错误,于是检查了客户端通信证书JKS
该证书由OCA1签发,仔细检查后并未发现证书信任链有任何问题,而且使用Oracle JDK可以握手成功,也说明证书信任链配置是正确的
,否则Oracle JDK也会报连接错误,这样看来不同厂商JDK存在SSL握手兼容性问题的可能性更大,通过观察SSL握手日志发现不同JDK使用的SSL协议是不同的, 如下图:
IBM JDK
Oracle JDK
IBM JDK握手时采用了SSLv3协议,而Oracle JDK 则采用TLSv1,难道是IBM JDK所用协议版本过低导致握手失败的么?由于SSLv3协议存在一些安全漏洞,确实在高版本的JDK中默认禁止了SSLv3协议,于是调整客户端握手协议,将IBM JDK设置为使用TLSv1协议,握手依然失败,还是同样的报错,到此问题似乎陷入僵局,如果是JDK不兼容,到底哪里不兼容,怎样确定确实是JDK兼容性问题? 首先还是要确认问题的大方向,经过一番思考,决定用标准预植做对比试验,用一个以前可以正常连接标准预植的JKS在IBM JDK环境下重试,如果同样连不上标准预植,并且也报同样的错误,那么就应该和JDK的兼容性有关系,神奇的一幕就在这时出现了,使用标准预植JKS在IBM JDK环境下连接标准预植服务器和中行预植服务器,均能连上, 直接将JDK存在兼容性问题的假设推翻了,既然标准预植JKS能在两个JDK环境下都能连通,说明还是JKS的问题,于是仔细对比了两个JKS的信任链, 下图中左侧为标准预植JKS,右侧为中行JKS
对比发现两个JKS信任证书的主题、颁发者、序列号、有效期均一致,只是标准预植JKS多添加了几个信任证书,但这几个证书并不在证书链中,所以是多余的,尽管如此,为了保持一致,还是将标准预植JKS多余的信任证书也添加到中行JKS的信任链中,重试,依然失败,联想到以往在制作JKS的过程中也经常遇到各种各样的问题,如内外部容器不一致,导出时缺少某些关键项项勾选等,都会导致连接失败,于是采用了更直接的方法,直接将问题JKS的密钥对导入到正常JKS中,重试,握手成功!看来还真的是JKS的问题,由于此通信证书很早前就已经制作完成,对应pfx格式证书及原始安装浏览器已经无从查找,无法进一步排查JKS哪里出了问题,于是建议测试使用新的中行JKS做测试,到这里问题似乎都解决了,然而诡异之神并没有这么轻易的放过我们,标准预植和中行预植生产服务器证书更换后,发生了戏剧性的反转,使用修复后并且在服务器证书更换前能够正常连接的中行JKS再次连接,握手失败!依然是No trusted certificate
found的报错,使用修复前的中行原始JKS,反而能连上了,标准预植方面也同样发生了反转,之前能够正常连接的标准预植JKS,在服务器证书更换后,竟然握手失败了,报错同样是No trusted certificate found,当然问题还是仅限于IBM
JDK,使用Oracle JDK都能正常连接,此时已经过了凌晨,离对外公布的承诺升级结束时间还剩不到一小时,时间有限,此时我们开始怀疑服务器证书更换后对连接的建立产生了影响,我们把服务器证书回退,结果再次反转,现象又回到了升级前的状态,测试同事在本地搭建了相同环境,也复现了上述过程, 服务器证书更换后对连接建立产生影响的判断已经几乎确定,但到底是哪些因素产生了影响此时还不得而知,为什么在Oracle JDK下却能一切正常,由于我们使用的IBM JDK与客户版本并不完全相同,升级是否一定会对客户造成影响,此时还不能确定,预植服务器证书升级涉及到我司所有预植客户,领导曾多次强调预植不能出事,更何况这次升级还包括中行、民生等大客户,一旦连接出现问题,将会迫使众多客户业务中断,事关重大,项目组同事果断决策,放弃升级,立即回退,待问题查清后,再择机升级,测试同事也对回退后的系统运行情况进行了验证,确保回退后的系统能够正常对外服务。
4月12日上午,将11日晚发生的问题进行梳理和分析,升级前的连接状态如下图:
升级后连接状态如下图:
我们跟运行部同事索要了服务器升级前后的JKS,对比升级前后的服务器证书,发现两个证书都带有证书链,且信任链一致。在走投无路的情况下,决定采用正面硬碰硬的方式,对JDK源码进行debug,追踪代码执行过程,找到报错位置,从代码层面分析报错原因,IBM JDK为商业用途,为防止代码被剽窃,其对JDK内部代码做了混淆,增加了追踪难度,经过艰辛的断点追踪,终于在下图所示位置中找到了对比的代码。
由上图可以看到,IBM
JDK会将客户端信任链中的证书与服务器证书链做全量比对,将字节数据放到可视化工具内比较两张证书发现了其中的端倪,如下图:
客户端信任证书密钥用法与服务端证书链中相同证书密钥用法并不一致,IBM JDK全量比较证书实体后,认为是不同证书,从而导致没有建立有效的证书信任关系,而Oracle JDK校验并没有如此严格,所以在Oracle JDK环境下可以正常连接,那为什么会出现客户端信任链证书中与服务端证书链中证书不一致的情况呢?我们进一步查看了服务端证书的情况。
左侧为客户端信任链中的OCA1根证,右侧为服务端证书链中的OCA1根证,对比发现两张证书指纹并不一致,对比查看密钥用法扩展与属性发现值确实不同,如下图:
由此可以看出应该是我司OCA1根证做过重签,后来也从老同事那里得到了证实,并且是在五年以前,由于标准预植JKS添加的信任链为重签之前的老OCA1根证,而中行原始JKS信任的为重签之后的OCA1新根证,所以在升级之前,中行原始JKS无法与服务器建立连接,而将中行问题JKS修复后(对老OCA1根证添加信任)便可以正常连接,但在服务端新通信证书换发后(服务端新通信证书证书链中为新OCA1根证),再次发生了不一致的情况,导致升级前可以正常连接的标准预植JKS也出现了问题,反而用最原始的中行问题JKS可以连接的情况,至此所有诡异现象都得到了解释。
根本原因陈述
由于OCA1根证做过重签,服务器证书由其换发后,自带证书链中的证书与客户端证书信任链中的证书不一致,在IBM JDK下会将证书做全量比较,从而无法建立有效信任关系,最终导致握手失败。
解决方案
重新制作服务器证书JKS,将服务器证书pfx装入浏览器后,将证书链中的OCA1及OCA1颁发者根证替换为重签之前的老根证,导出新的pfx,再次制作服务器证书JKS.
总结和感悟
在这次问题的发生和处理过过程中,我们做了几点总结和感悟在这里做一下分享。
1、任何时候都不能轻视变动,哪怕是再小的升级也需要严苛的测试和演练。
2、升级要有回退方案,避免因各种原因导致的升级失败从而陷入到上上不去、下下不来的窘境。
3、遇到反常情况不能得过且过,用领导的话说就是一定要查明真相,究其本质,不然可能会因此收获一场史诗级的生产事故。
网友评论