[转帖]一次SSL握手异常,我发现JDK还有发行版区别
https://www.cnblogs.com/codelogs/p/16633704.html
原创:扣钉日记(微信公众号ID:codelogs),欢迎分享,转载请保留出处。
简介#
最近,我们一个多机房部署的服务,调用方反馈有问题,在调用新加坡机房时正常,而调用印度机房则报SSL握手异常。
排查花了一些时间,同时也积累了一些经验,故记录一下,读完本文,你将了解到如下内容:
- SSL握手过程
- SSL握手异常时的排查思路与工具
- 同版本的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/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个疑问了,如下:
- 为啥新加坡机房正常,而印度机房SSL握手报错?
- 为啥相同版本的JDK,自己编译的没有问题?
为啥SSL握手报错?#
由于我之前解决过一次SSL握手异常的bug,也写成了一篇文章 一次IOS通知推送问题排查全过程,原因是由于客户端与服务端密码套件不一致导致的。
粗略来讲,SSL握手过程如下:
- 客户端发送Client Hello包给服务端,其中除了包含密钥协商相关的数据外,还会告知自己支持的密码套件列表。
- 服务端收到Client Hello包后,会给客户端回复Server Hello,其中也包含了密钥协商数据,以及服务端选择了哪个密码套件。
但有一种情况是,客户端第一步发送的所有密码套件,服务端都不支持,因此服务端会回复一个SSL握手异常包,进而导致客户端失败报错。
注:密码套件,指的是加密系统将多种密码学算法混合使用,以实现多种安全需求,如TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,使用ECDHE实现密钥协商、RSA实现证书认证、AES实现加密、SHA256实现消息防篡改。
如何确认是否是上面原因呢?我进行了如下测试:
- 添加JVM参数
-Djavax.net.debug=SSL,并调用正常的新加坡机房,看看SSL握手选择的是什么密码套件。
$ bin/java -Djavax.net.debug=SSL SgSendRequest

可以看到,客户端提供了很多密码套件,服务端选择了TLS_DHE_RSA_WITH_AES_128_GCM_SHA256,那么极有可能是印度机房不支持此密码套件,导致印度机房请求失败,可用curl确认下:
- 使用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-ciphers.html
这也就是说,此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系列算法,也没找到相关解释,只好就此打住。
总结#
这个问题在报错能被稳定重现出来时,其实就不难了,但排查思路与使用到的工具还是挺值得分享的,如下:
- 客户端与服务端支持的密码套件没有交集,会导致SSL握手失败。
- 使用
-Djavax.net.debug=SSL可以调试java的SSL握手过程。 - 通过
curl --ciphers指定客户端密码套件访问服务端,可以确认服务端是否支持此密码套件。 - 通过
SSLServerSocketFactory.getSupportedCipherSuites()可获取JDK支持的密码套件。 - 使用
nmap --script ssl-enum-ciphers可扫描出服务端支持的密码套件。 - 同样版本的JDK,不同发行商发行的,也可能存在着差异。
[转帖]一次SSL握手异常,我发现JDK还有发行版区别的更多相关文章
- MacOS升级到Monterey后python SSL握手失败问题
MacOS升级到Monterey 12.0.1后,忽然发现原来工作正常的python3请求华为restconf API报错失败,提示 ssl.SSLError: [SSL: SSLV3_ALERT_H ...
- SSL握手过程
原文地址: http://my.oschina.net/u/1188877/blog/164982 一.SSL握手有三个目的:1. 客户端与服务器需要就一组用于保护数据的算法达成一致:2. 它们需要确 ...
- SSL握手流程
一.SSL是什么? 安全套接字(SSL)协议是Web浏览器和Web服务器之间安全交换信息的协议. SSL介于应用层和TCP层之间,应用层数据不再直接传递给传输层,而是传递给SSL层,SSL层对从应用层 ...
- ssl握手数据结构
ssl握手 SSL记录头(5字节) 字节0:记录内容的类型 Content Type Hex Code Description Change_Cipher_Spec 0x14 指示加密方式的更改 Al ...
- HTTPS和SSL握手过程(转载)
https介绍 HTTPS = HTTP + 一组对称.非对称和基于证书的加密技术 HTTPS是最常见的HTTP安全版本.它得到了很广泛的应用,所有主要的商业浏览器和服务器都提供HTTPS.HTTPS ...
- 加密、签名和SSL握手机制细节
openssl系列文章:http://www.cnblogs.com/f-ck-need-u/p/7048359.html 1.1 背景知识 对称加密 :加密解密使用同一密钥,加解密速度快.随 ...
- SSL握手通信详解及linux下c/c++ SSL Socket代码举例
SSL握手通信详解及linux下c/c++ SSL Socket代码举例 摘自:http://www.169it.com/article/3215130236.html 分享到:8 发布时 ...
- SSL握手通信详解及linux下c/c++ SSL Socket代码举例(另附SSL双向认证客户端代码)
SSL握手通信详解及linux下c/c++ SSL Socket代码举例(另附SSL双向认证客户端代码) 摘自: https://blog.csdn.net/sjin_1314/article/det ...
- SSL 握手协议详解
这里重点介绍一下服务端的验证和密钥交换.这个阶段的前面的(a)证书 和(b)服务器密钥交换是基于密钥交换方法的.而在SSL中密钥交换算法有6种:无效(没有密钥交换).RSA.匿名Diffie-Hell ...
- 转:SSL 握手协议详解
SSL 握手协议详解 RSA作为身份认证,ECDHE来交换加密密钥,AES/DES等作为加密. 如果RSA来加解密,那么身份认证后,直接用认证后的RSA公钥解密.不需要再额外交换加密密钥了. 相关报文 ...
随机推荐
- 2021-01-16:我截获了登录token的话,是不是就获得了登录状态,这样就不安全了。如何保证安全?
福哥答案2021-01-06: 知乎答案: 首先,Token 一般放在 Header 或者 Cookies 中,Http 是明文传输,Https 是密文传输.可以一定程度防止Token 截获. 第二, ...
- BugBuilder: 高质量大规模缺陷库自动构建方法
摘要:本文提出并开发了高质量大规模缺陷库全自动构建方法BugBuilder,自动从版本控制系统中的人为编写的补丁中提取完整且精准的缺陷修复补丁. 本文分享自华为云社区<BugBuilder: 高 ...
- 直播实时数仓基于DataLeap开放平台在发布管控场景的业务实践
更多技术交流.求职机会,欢迎关注字节跳动数据平台微信公众号,回复[1]进入官方交流群 背景 业务背景 随着字节业务的高速增长,业务场景越来越丰富,业务基于数据做的决策也越来越多,对数据的时效性要求也越 ...
- 浅谈splice( )与slice( )
1.splice( ) 概念:splice( )用于修改原始数组,它可以删除.插入.替换数组的元素,并返回被删除的元素组成的新数组. 语法:splice(start,deleteCount,item1 ...
- Clion 中 Rust 插件开启 WSL 调试
Rust Linux 配置 wsl 中执行命令: curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh 按照可能会卡住,需要在本 ...
- 数据结构——AVL树
AVL树是一种特殊的二叉查找树,其特征在于:对所有节点来说,其左子树和右子树间的高度差小于等于1.本文简要总结下AVL树的几种基本操作. 节点结构体定义 typedef struct Node_s { ...
- The 18th Zhejiang Provincial Collegiate Programming Contest 补题记录(ACFGJLM)
补题链接:Here A. League of Legends 签到题,求和判断即可 ll suma, sumb; void solve() { ll x; for (int i = 1; i < ...
- 制作PE工具箱
事前准备: 能上网的电脑 x1 台 大于8G的U盘 x一个(如果需要储存安装镜像的话,如果不需要的话大于1G即可) 一.下载PE工具箱 推荐使用WEPE工具箱,无广告无推广.不推荐老X桃,大X菜,大X ...
- 无需代码绘制人工神经网络ANN模型结构图的方法
本文介绍几种基于在线网页或软件的.不用代码的神经网络模型结构可视化绘图方法. 之前向大家介绍了一种基于Python第三方ann_visualizer模块的神经网络结构可视化方法,大家可以直接点 ...
- Java内部类详解--成员内部类,局部内部类,匿名内部类,静态内部类
一.内部类基础 在Java中,可以将一个类定义在另一个类里面或者一个方法里面,这样的类称为内部类.广泛意义上的内部类一般来说包括这四种:成员内部类.局部内部类.匿名内部类和静态内部类.下面就先来了解一 ...