使用jdk1.8.0_322 版本时, https不支持SSLv3协议问题, 多种解决方案
https不支持SSLv3协议问题, 多种解决方案
文章目录
- https不支持SSLv3协议问题, 多种解决方案
- 报错原文 (No appropriate protocol )
- 原因分析
- 解决方案1: 网上大多数都是这么说的
- 解决方案2: 调整项目https协议为 TLS1.2
- 问题复现方式
报错原文 (No appropriate protocol )
javax.net.ssl.SSLHandshakeException: No appropriate protocol (protocol is disabled or cipher suites are inappropriate)
翻译为: 没有合适的协议(协议被禁用或密码套件不合适)
Exception in thread "main" 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:238)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:405)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:384)
at sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:587)
at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:197)
at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1584)
at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1512)
at java.net.HttpURLConnection.getResponseCode(HttpURLConnection.java:480)
at sun.net.www.protocol.https.HttpsURLConnectionImpl.getResponseCode(HttpsURLConnectionImpl.java:352)
at com.dycjr.xiakuan.payweb.service.impl.SinglePayServiceImpl.main(SinglePayServiceImpl.java:481)
进程已结束,退出代码1
原因分析
大概测试了下,如果项目使用的 https安全协议 < TLS1.2(比如 SSLv3) 并且 jdk版本 > 1.8.0_332 就会出现此问题。
高版本 jdk 现在用的 安全协议都是 TLSv1.3、TLSv1.2 默认已经禁用掉了 SSLv3协议
**解决方案大概分两种: **
- 强制开启jdk的sslv3协议 或者 降级jdk版本到 jdk 1.8.0_077
- 在项目中使用更新的https安全协议, 比如 TLSv3
很多时候遇到这种问题是因为https请求功能是三方(比如银行)提供好的,我们只能直接改, 可以考虑使用反射强制修改
解决方案1: 网上大多数都是这么说的
网上搜了下,对此问题基本都是修改jdk的, 然后需要重启对应的项目就好了
jdk降级这个方式这里就不说了 , 感兴趣的可以自行搜索, 通常项目里是不允许这么弄得
解决方案2: 调整项目https协议为 TLS1.2
我遇到的就是这种, https是三方jar包封装好的,无法更改协议版本
在项目启动的时候通过反射强行修改 SSLSocktFactory, 如下:
public static void main(String[] args) {
// ssl配置为 TLSv1.2
allinpaySslConf();
SpringApplication.run(PayWebApplication.class, args);
System.out.println("============= start success ===============");
System.out.println("user.dir = " + System.getProperty("user.dir"));
}
/**
* jdk1.8_332 版本不支持SSLv3问题, 通过反射强行改为 TLSv1.2
*/
private static void allinpaySslConf(){
try {
SSLContext tlsv12 = SSLContext.getInstance("TLSv1.2");
tlsv12.init((KeyManager[])null, null, (SecureRandom)null);
ReflectUtil.setFieldValue(XmlTools.class, "sslFactory", tlsv12.getSocketFactory());
} catch (Exception e) {
e.printStackTrace();
}
}
问题复现方式
这里分享一个简单的复现问题的方式, 感兴趣的可以在 IDEA 下载两个jdk 试下
@SneakyThrows
public static void main(String[] args) {
URL url = new URL("https://www.baidu.com");
URLConnection conn = url.openConnection();
conn.setDoInput(true);
conn.setDoOutput(true);
HttpsURLConnection httpsConn = (HttpsURLConnection)conn;
httpsConn.setRequestMethod("POST");
// sslv3
SSLContext sslV3 = SSLContext.getInstance("SSLv3");
sslV3.init((KeyManager[])null, null, (SecureRandom)null);
// tlsv2
SSLContext tlsv12 = SSLContext.getInstance("TLSv1.2");
tlsv12.init((KeyManager[])null, null, (SecureRandom)null);
// ReflectUtil.setFieldValue(XmlTools.class, "sslFactory", tlsv12.getSocketFactory());
// 这里修改具体的ssl版本 来复现问题
// httpsConn.setSSLSocketFactory(sslV3.getSocketFactory());
httpsConn.setSSLSocketFactory(XmlTools.getSSLSF());
int responseCode = httpsConn.getResponseCode();
System.out.println("responseCode = " + responseCode);
}
jdk版本参考如下:
// 补充: 可以直接把三方发送https的类和包位置复制到项目目录中来覆盖, 然后做下调整。 这样安全性更高。