环境:
java-1.8.0-openjdk-1.8.0.131
hutool-all 依赖, 5.8.25版本
项目背景:
发版上线,用的hutool工具类 HttpUtil.createPost() ,请求域名为https://xxx.ik3cloud.com/k3cloud 的金蝶ERP webapi接口
问题:
报各类 SSLException 异常.....
原因:
服务端要求的TLS版本,与客户端实际请求的TLS版本不一致
解决过程:
6月19号,在发送post请求前,java代码中明确加上 System.setProperty("https.protocols", "TLSv1.2");代码后,接口能正常调用。
6月24号上午9点27分
日志突然出现大量 : SSLException: Received fatal alert: protocol_version 异常信息....
cn.hutool.core.io.IORuntimeException: SSLException: Received fatal alert: protocol_versionat cn.hutool.http.HttpRequest.send(HttpRequest.java:1350)at cn.hutool.http.HttpRequest.doExecute(HttpRequest.java:1188)at cn.hutool.http.HttpRequest.execute(HttpRequest.java:1051)at cn.hutool.http.HttpRequest.execute(HttpRequest.java:1027)Caused by: javax.net.ssl.SSLException: Received fatal alert: protocol_versionat sun.security.ssl.Alerts.getSSLException(Alerts.java:208)at sun.security.ssl.Alerts.getSSLException(Alerts.java:154)at sun.security.ssl.SSLSocketImpl.recvAlert(SSLSocketImpl.java:2020)at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1127)at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1367)at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1395)at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1379)at sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:559)at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:185)at sun.net.www.protocol.http.HttpURLConnection.getOutputStream0(HttpURLConnection.java:1334)at sun.net.www.protocol.http.HttpURLConnection.getOutputStream(HttpURLConnection.java:1309)at sun.net.www.protocol.https.HttpsURLConnectionImpl.getOutputStream(HttpsURLConnectionImpl.java:259)at cn.hutool.http.HttpConnection.getOutputStream(HttpConnection.java:458)at cn.hutool.http.HttpRequest.sendFormUrlEncoded(HttpRequest.java:1367)at cn.hutool.http.HttpRequest.send(HttpRequest.java:1342)... 8 common frames omitted
引入额外的openjsse依赖 Java8支持TLSv1.3协议请求,问题依旧。。。。。。
于是,查看 该域名用到的TLS具体版本:

yum install nmap
nmap --script ssl-enum-ciphers -p 443 xxx.ik3cloud.com

发现浏览器中 该域名的TLS版本 是1.3 ,但服务器端检查该域名的TLS版本是1.2 ,故项目中的java代码加上如下设置,兼容 TLSv1.2 与 TLSv1.3 版本:
System.setProperty("https.protocols", "TLSv1.2,TLSv1.3");
System.setProperty("jdk.tls.client.protocols", "TLSv1.2,TLSv1.3");
运行半小时后,依旧偶尔报错:
Caused by: javax.net.ssl.SSLHandshakeException: No appropriate protocol (protocol is disabled or cipher suites are inappropriate)
cn.hutool.core.io.IORuntimeException: SSLHandshakeException: No appropriate protocol (protocol is disabled or cipher suites are inappropriate)at cn.hutool.http.HttpRequest.send(HttpRequest.java:1350)at cn.hutool.http.HttpRequest.doExecute(HttpRequest.java:1188)at cn.hutool.http.HttpRequest.execute(HttpRequest.java:1051)at cn.hutool.http.HttpRequest.execute(HttpRequest.java:1027)Caused by: javax.net.ssl.SSLHandshakeException: No appropriate protocol (protocol is disabled or cipher suites are inappropriate)at sun.security.ssl.HandshakeContext.<init>(HandshakeContext.java:171)at sun.security.ssl.ClientHandshakeContext.<init>(ClientHandshakeContext.java:106)at sun.security.ssl.TransportContext.kickstart(TransportContext.java:245)at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:410)at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:389)at sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:558)at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:201)at sun.net.www.protocol.http.HttpURLConnection.getOutputStream0(HttpURLConnection.java:1390)at sun.net.www.protocol.http.HttpURLConnection.getOutputStream(HttpURLConnection.java:1365)at sun.net.www.protocol.https.HttpsURLConnectionImpl.getOutputStream(HttpsURLConnectionImpl.java:264)at cn.hutool.http.HttpConnection.getOutputStream(HttpConnection.java:458)at cn.hutool.http.HttpRequest.sendFormUrlEncoded(HttpRequest.java:1367)at cn.hutool.http.HttpRequest.send(HttpRequest.java:1342)... 7 common frames omitted
java代码中,使用与金蝶ERP服务器一致的加密套件,
# 通过如下命令,查询金蝶服务器端所用的加密套件
openssl s_client -connect XXX.ik3cloud.com:443 -tls1_2
// 使用与金蝶服务端 一致的加密套件
System.setProperty("https.cipherSuites", "ECDHE-RSA-AES256-GCM-SHA384");
也依旧报Caused by: javax.net.ssl.SSLHandshakeException: No appropriate protocol (protocol is disabled or cipher suites are inappropriate)
于是向金蝶客服提工单,金蝶技术建议用JDK11:
于是升级jdk为 open jdk11.0.2 ,刚开始正常,半个小时后 偶发性的报错 Caused by: javax.net.ssl.SSLException: No PSK available. Unable to resume.
javax.net.ssl.SSLException: No PSK available. Unable to resume.
原因jdk11版本过低: 这是一个jdk11的已知bug:https://bugs.openjdk.java.net/browse/JDK-8213202
最后使用jdk11的最新版本: oracle jdk11.0.27 ,再无以上SSLException异常,
至此,问题解决
附录:
Java默认使用的协议版本

参考文档:
https.protocols在Java中的使用
JAVA HTTPS单向认证和双向认证以及JDK低版本请求
引入额外的openjsse依赖 Java8支持TLSv1.3协议请求
解决No PSK available. Unable to resume编译报错的问题