关于https:一次SSL握手异常我发现JDK还有发行版区别

4次阅读

共计 3701 个字符,预计需要花费 10 分钟才能阅读完成。

原创:扣钉日记(微信公众号 ID:codelogs),欢送分享,转载请保留出处。

简介

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

排查花了一些工夫,同时也积攒了一些教训,故记录一下,读完本文,你将理解到如下内容:

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

废话不多说,往下看 …

发现问题

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

这个异样是共事始终在看,通过一翻搜寻,狐疑是 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/…

弄到 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

    能够看到,客户端提供了很多明码套件,服务端抉择了TLS_DHE_RSA_WITH_AES_128_GCM_SHA256,那么极有可能是印度机房不反对此明码套件,导致印度机房申请失败,可用 curl 确认下:

  2. 应用 curl 以指定明码套件 DHE-RSA-AES128-GCM-SHA256 拜访印度机房。

    $ curl -v https://in.xxx.be.srv.com --ciphers DHE-RSA-AES128-GCM-SHA256

    能够发现,印度机房的确不反对此明码套件。

    注:jdk 明码套件名称与 curl 的名称略微有点不统一,curl 的能够在这里查找 https://curl.se/docs/ssl-ciph…

这也就是说,此 JDK 反对的密码学套件与印度机房反对的密码学套件没有交加,服务端无奈选出一个单方都反对的明码套件,能够进一步确认下,如下:
jdk 反对的明码套件能够通过 SSLServerSocketFactory.getSupportedCipherSuites() 获取。

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

印度机房反对的明码套件能够应用 nmap 扫描获取,如下:

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

通过我的查看,发现 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()))"

能够发现,我本人编译的 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 动静库时会报错,如下:

于是,我去问题 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 会存在时区问题?
真正了解可反复读事务隔离级别
字符编码解惑

正文完
 0