[转帖] 一次SSL握手异常,我发现JDK还有发行版区别

一次,ssl,握手,异常,发现,jdk,还有,发行版,区别 · 浏览次数 : 0

小编点评

## 问题解答 **1. SSL握手异常的原因** * **密码套件问题:** India 机房只支持一些较新的密码套件,而你的 JDK 版本可能不支持这些密码套件。 * **JDK 版本问题:** 由于你的编译工具可能使用一个旧的 JDK 版本,而 India 机房可能不支持的密码套件属于这个版本之外的。 **2. 编译器版本差异导致的 ECDH 问题** * 你的编译工具可能使用一个 older 的 Java 版本,该版本可能不支持 ECDH 密码套件。 * 你的编译工具可能使用一个 older 的 sunec 动态库版本,该版本可能不支持 ECDH 密码套件。 **3. 其他问题** * 你的代码可能在加载 sunec 动态库时遇到异常,这可能导致 SSL 握手失败。 * 你的代码可能使用一个与服务器端支持的密码套件不同的字符编码,这会导致 SSL 握手失败。 **4. 解决方案** * **升级 JDK 版本:** 如果你能,升级你的 JDK 版本到支持 ECDH 密码套件的版本。 * **调整服务器端 SSL 配置:** 修改服务器端的 SSL 配置文件以支持旧的密码套件。 * **调试代码:** 在运行时添加 `-Djavax.net.debug=SSL` 参数来启用 SSL 握手调试。 * **使用工具列出服务器端支持的密码套件:** 使用 `nmap --script ssl-enum-ciphers` 等工具列出服务器端支持的密码套件。 * **解决异常:** 仔细调试你的代码,以确定出现异常的原因。

正文

https://www.cnblogs.com/codelogs/p/16633704.html

 

简介#

最近,我们一个多机房部署的服务,调用方反馈有问题,在调用新加坡机房时正常,而调用印度机房则报SSL握手异常。

排查花了一些时间,同时也积累了一些经验,故记录一下,读完本文,你将了解到如下内容:

  1. SSL握手过程
  2. SSL握手异常时的排查思路与工具
  3. 同版本的JDK,也是有所差异的

废话不多说,往下看...

发现问题#

调用方调用印度机房服务时,报错信息如下:
handshake_failure

这个异常是同事一直在看,经过一翻搜索,怀疑是JDK版本的问题,经过询问调用方,发现调用方版本是1.8.0_91-b14,于是同事打算下载此版本JDK本地测试一下。

但这个版本JDK不太好找,于是同事就问了下我,我也找了一会也没找到,于是打算从源码编译一个此版本JDK。

经过一段时间,我通过源码编译出来了这个版本的jdk,同时同事也在网上找到了一个此版本的JDK,如下:
JDK源码:https://github.com/openjdk/jdk8u ,tag选择jdk8u91-b14即可。
网上的JDK包:https://github.com/ojdkbuild/ojdkbuild/releases/download/1.8.0.91-3/java-1.8.0-openjdk-1.8.0.91-1.b14.el6.x86_64.zip

弄到1.8.0_91-b14版JDK后,我和同事都进行了测试,奇怪的是,同事网上找的JDK重现了调用方的报错,即新加坡机房正常,而印度机房SSL握手失败,但我自己编译的JDK则两个机房都正常,我们可是相同版本的JDK啊!

好家伙,现在有2个疑问了,如下:

  1. 为啥新加坡机房正常,而印度机房SSL握手报错?
  2. 为啥相同版本的JDK,自己编译的没有问题?

为啥SSL握手报错?#

由于我之前解决过一次SSL握手异常的bug,也写成了一篇文章 一次IOS通知推送问题排查全过程,原因是由于客户端与服务端密码套件不一致导致的。

粗略来讲,SSL握手过程如下:

  1. 客户端发送Client Hello包给服务端,其中除了包含密钥协商相关的数据外,还会告知自己支持的密码套件列表。
  2. 服务端收到Client Hello包后,会给客户端回复Server Hello,其中也包含了密钥协商数据,以及服务端选择了哪个密码套件。

但有一种情况是,客户端第一步发送的所有密码套件,服务端都不支持,因此服务端会回复一个SSL握手异常包,进而导致客户端失败报错。

注:密码套件,指的是加密系统将多种密码学算法混合使用,以实现多种安全需求,如TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,使用ECDHE实现密钥协商、RSA实现证书认证、AES实现加密、SHA256实现消息防篡改。

如何确认是否是上面原因呢?我进行了如下测试:

  1. 添加JVM参数-Djavax.net.debug=SSL,并调用正常的新加坡机房,看看SSL握手选择的是什么密码套件。
$ bin/java -Djavax.net.debug=SSL SgSendRequest

image_2022-08-28_20220828172732
可以看到,客户端提供了很多密码套件,服务端选择了TLS_DHE_RSA_WITH_AES_128_GCM_SHA256,那么极有可能是印度机房不支持此密码套件,导致印度机房请求失败,可用curl确认下:

  1. 使用curl以指定密码套件DHE-RSA-AES128-GCM-SHA256访问印度机房。
$ curl -v https://in.xxx.be.srv.com --ciphers DHE-RSA-AES128-GCM-SHA256

image_2022-08-28_20220828173150
可以发现,印度机房确实不支持此密码套件。

注:jdk密码套件名称与curl的名称稍微有点不一致,curl的可以在这里查找 https://curl.se/docs/ssl-ciphers.html

这也就是说,此JDK支持的密码学套件与印度机房支持的密码学套件没有交集,服务端无法选出一个双方都支持的密码套件,可以进一步确认下,如下:
jdk支持的密码套件可以通过SSLServerSocketFactory.getSupportedCipherSuites()获取。

$ bin/jrunscript -e "print(java.util.Arrays.toString(javax.net.ssl.SSLServerSocketFactory.getDefault().getSupportedCipherSuites()))"

image_2022-08-28_20220828174526

印度机房支持的密码套件可以使用nmap扫描获取,如下:

$ nmap --script ssl-enum-ciphers -p 443 in.xxx.be.srv.com

image_2022-08-28_20220828175141
经过我的检查,发现jdk的密码套件与印度机房的密码套件确实没有交集,印度机房只支持一些较新的密码套件,这就是调用印度机房服务时SSL握手失败的原因。

用相同的方法,我也确认了新加坡机房,发现新加坡机房的密码套件与jdk的密码套件有交集,而TLS_DHE_RSA_WITH_AES_128_GCM_SHA256就在其中。

要解决这个问题也比较容易,要么让调用方升级jdk以支持新的密码套件,要么让印度机房SRE调整SSL配置以支持旧的密码套件,我们选择了前者。

那么,还有一个问题,为啥我自己编译的同版本的JDK就没有问题呢?

为啥自行编译的JDK没有问题?#

有点迷惑,我用上面相同方法确认了一下我自己编译的JDK支持哪些套件,如下:

$ bin/jrunscript -e "print(java.util.Arrays.toString(javax.net.ssl.SSLServerSocketFactory.getDefault().getSupportedCipherSuites()))"

image_2022-08-28_20220828180156
可以发现,我自己编译的JDK,支持ECDH系列的新密码套件,这是为啥?

为了弄清区别,我使用问题JDK进行了调试,如下:

import javax.crypto.KeyAgreement;
import java.security.NoSuchAlgorithmException;

public class EcdhTest {
    public static void main(String[] args) throws NoSuchAlgorithmException {
        KeyAgreement ka = KeyAgreement.getInstance("ECDH");
        System.out.println(ka);
    }
}

在问题JDK里面,会报如下异常:

$ bin/java EcdhTest
Exception in thread "main" java.security.NoSuchAlgorithmException: Algorithm ECDH not available
        at javax.crypto.KeyAgreement.getInstance(KeyAgreement.java:184)
        at EcdhTest.main(EcdhTest.java:6)

有异常就好办了,只要顺着异常产生的过程调试下去即可,大概调试了如下相关方法:

sun.security.ssl.JsseJce.getKeyAgreement("ECDH")  
sun.security.ec.SunEC  

当调试到SunEC类时,我发现在加载sunec动态库时会报错,如下:
image_2022-08-28_20220828181703

于是,我去问题jdk目录下查找这个动态库文件,动态库文件在Linux下一般是.so结尾,如下:

$ find | grep sunec
./jre/lib/ext/sunec.jar
./jre/lib/amd64/libsunec.so_DISABLED
./jre/lib/amd64/libsunec.diz

懵逼了,在这个问题JDK里,libsunec.so竟然被改名为了libsunec.so_DISABLED,而我看了下我自己编译的JDK,这个文件是没有改名的!

终于,第二个问题也找到了原因,原来是网上找的这个JDK,通过改名libsunec.so将EC系列算法禁用了。
我大概看了会那个JDK下载页面,这个JDK构建时间挺久了,是RedHat早期为CentOS6构建的一个JDK8版本,至于为啥要禁用EC系列算法,也没找到相关解释,只好就此打住。

总结#

这个问题在报错能被稳定重现出来时,其实就不难了,但排查思路与使用到的工具还是挺值得分享的,如下:

  1. 客户端与服务端支持的密码套件没有交集,会导致SSL握手失败。
  2. 使用-Djavax.net.debug=SSL可以调试java的SSL握手过程。
  3. 通过curl --ciphers指定客户端密码套件访问服务端,可以确认服务端是否支持此密码套件。
  4. 通过SSLServerSocketFactory.getSupportedCipherSuites()可获取JDK支持的密码套件。
  5. 使用nmap --script ssl-enum-ciphers可扫描出服务端支持的密码套件。
  6. 同样版本的JDK,不同发行商发行的,也可能存在着差异。

往期内容#

一次IOS通知推送问题排查全过程
密码学入门
接口偶尔超时,竟又是JVM停顿的锅!
耗时几个月,终于找到了JVM停顿十几秒的原因
mysql的timestamp会存在时区问题?
真正理解可重复读事务隔离级别
字符编码解惑

与[转帖] 一次SSL握手异常,我发现JDK还有发行版区别相似的内容:

[转帖] 一次SSL握手异常,我发现JDK还有发行版区别

https://www.cnblogs.com/codelogs/p/16633704.html 简介# 最近,我们一个多机房部署的服务,调用方反馈有问题,在调用新加坡机房时正常,而调用印度机房则报SSL握手异常。 排查花了一些时间,同时也积累了一些经验,故记录一下,读完本文,你将了解到如下内容:

[转帖]一次SSL握手异常,我发现JDK还有发行版区别

https://www.cnblogs.com/codelogs/p/16633704.html 原创:扣钉日记(微信公众号ID:codelogs),欢迎分享,转载请保留出处。 简介# 最近,我们一个多机房部署的服务,调用方反馈有问题,在调用新加坡机房时正常,而调用印度机房则报SSL握手异常。 排查

[转帖]SSL 配置优化的若干建议

转载自本人博客:https://dev.tail0r.com/ssl-optimization/ 如果你配置SSL只是为了网站的网址前有一把锁的标志,那不如直接送你把锁好了。 别想了,这句话不是哪个安全专家说的,是我说的(逃) 今天写一篇文章记录一下自己 SSL 的配置优化过程。以下设置均为 Ngi

[转帖]jconsole远程监控认证,java远程监控,jmx监控应用,jmx ssl配置,jconsole ssl连接远程应用

知识普及 jmx JMX(java Management Extensions)是一个Java平台的管理和监控接口。任何程序,只要按JMX规范访问这个接口,就可以获取所有管理与监控信息,jconsole与Java VisualVM等常见监测工具都是基于jmx,JMX不但可以用于管理JVM,还可以管理

[转帖]JMeter分布式测试环境搭建(禁用SSL)

https://www.cnblogs.com/MasterMonkInTemple/p/11978058.html JMeter分布式环境,一台Master,一到多台Slave,Master和Slave可以是同一台机器。 前提条件: 所有机器,包括master和slave的机器: 1.运行相同版本

[转帖]acme.sh-实现自动化部署SSL证书

https://cloud.tencent.com/developer/article/2055254 前言 ; acme 我一直使用的都是Let's Encrypt免费的证书,昨天看到群里的大佬发出了一个测试的证书申请的脚本命令,我才接触到acme.sh,于是我查阅了很多大佬的博文了解这个脚本的使

[转帖]Https、SSL/TLS相关知识及wireShark抓包分析

https://cloud.tencent.com/developer/article/1954070?areaSource=104001.56&traceId=7WZNP412yK3vh7ebw4th0 一、网络通信的三大风险 在HTTP协议中,所有报文的发送、接收都是以明文的形式进行的。也就是说

[转帖]SpringBoot配置SSL 坑点总结【密码验证失败、连接不安全】

文章目录 前言1.证书绑定问题2.证书和密码不匹配3.yaml配置文件问题3.1 解密类型和证书类型是相关的3.2 配置文件参数混淆 后记 前言 在SpringBoot服务中配置ssl,无非就是下载证书设置一下配置文件的问题,这里主要记录我在配置的过程中遇到的坑点。 如果是新手上道的话建议结合其他的

[转帖]SSL数字证书分类DV/OV/EV

SSL证书的分类主要是通过下面两个维度进行分类: 1.根据验证模式分类 根据CA机构对申请者的身份审核范围分为:DV证书、OV证书、EV证书。 1.1.DV证书(域名证书) DV(Domain Validated)证书是最常见的一种证书类型,大多数免费证书都此类证书。CA机构获取CSR证书请求后,从

[转帖]一次艰难的内存泄露排查

https://www.jianshu.com/p/d0dff28a4cce 一次艰难的内存泄露排查 现象 2019.4.26 22:00左右,通过jstat -gcutil pid 5000 ,发现fgc次数很多而且频繁,此时老年代占比已经大约70%左右,且已经回收不了内存,我们这边设置的fgc阈