Java默认使用的协议版本
Java版本 | HTTPS请求默认使用的TLS版本 | 支持的协议 |
---|---|---|
JDK7 | TLSv1 | TLSv1、TLSv1.1、TLSv1.2、SSLv3 |
JDK8 | TLSv1.2 | TLSv1、TLSv1.1、TLSv1.2、SSLv3 |
JDK11 | TLSv1.3 | TLSv1、TLSv1.1、TLSv1.2、TLSv1.3、SSLv3 |
协议版本不支持的异常信息
Exception in thread "main" javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure
at sun.security.ssl.Alerts.getSSLException(Alerts.java:192)
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.https.HttpsURLConnectionImpl.connect(HttpsURLConnectionImpl.java:162)
查看服务端支持的协议版本
-
网站查询:https://myssl.com/ 或者 https://www.ssllabs.com/
-
nmap命令查询:
## 执行如下命令 nmap --script ssl-enum-ciphers -p 443 xxxxxx.com.cn ## 响应信息如下 Starting Nmap 7.80 ( https://nmap.org ) at 2021-12-10 11:16 CST Nmap scan report for xxxxxx.com.cn (139.217.176.162) Host is up (0.034s latency). PORT STATE SERVICE 443/tcp open https | ssl-enum-ciphers: | TLSv1.3: # 这里是支持的协议版本 | ciphers: # 这里是支持的协议加密套件,不支持也无法连接成功 | TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 (ecdh_x25519) - A | TLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384 (ecdh_x25519) - A | TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256 (ecdh_x25519) - A | TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384 (ecdh_x25519) - A | TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 (ecdh_x25519) - A | TLS_RSA_WITH_AES_256_CCM_8 (rsa 2048) - A | TLS_RSA_WITH_AES_256_CCM (rsa 2048) - A | TLS_RSA_WITH_ARIA_256_GCM_SHA384 (rsa 2048) - A | TLS_RSA_WITH_AES_128_CCM_8 (rsa 2048) - A | TLS_RSA_WITH_AES_128_CCM (rsa 2048) - A | TLS_RSA_WITH_ARIA_128_GCM_SHA256 (rsa 2048) - A | TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256 (rsa 2048) - A | TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256 (rsa 2048) - A | TLS_RSA_WITH_CAMELLIA_256_CBC_SHA (rsa 2048) - A | TLS_RSA_WITH_CAMELLIA_128_CBC_SHA (rsa 2048) - A | compressors: | NULL | cipher preference: server |_ least strength: A
Java8支持TLSv1.3协议请求
-
Java8版本官方并没有提供TLSv1.3版本协议的支持,需要引入额外的openjsse依赖。
-
依赖源代码:https://github.com/openjsse/openjsse ,Github打不开使用https://mvnrepository.com/下载也可以。
-
注意JDK版本与依赖版本的对应,具体参考:https://github.com/openjsse/openjsse#openjdk-8-to-openjsse-version-mapping。
-
代码请求时引入协议支持,并指定协议版本进行请求。
package cn.devzyh.learning.http; import org.openjsse.net.ssl.OpenJSSE; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; import java.net.URL; import java.security.Security; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; /** * Java8发起TLSv1.3版本的https请求 */ public class RequestTLSv1_3Test { public static void main(String[] args) throws Exception { // 支持TLSv1.3协议的依赖注册到提供者中 Security.addProvider(new OpenJSSE()); // 指定请求的协议版本 SSLContext sslcontext = SSLContext.getInstance("TLSv1.2"); X509TrustManager x509TrustManager = new X509TrustManager() { @Override public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException { } @Override public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException { } @Override public X509Certificate[] getAcceptedIssuers() { return null; } }; sslcontext.init(null, new TrustManager[]{x509TrustManager}, new java.security.SecureRandom()); HttpsURLConnection.setDefaultSSLSocketFactory(sslcontext.getSocketFactory()); URL url = new URL("https://xxxxxx.com.cn"); HttpsURLConnection conn = (HttpsURLConnection) url.openConnection(); conn.connect(); System.out.println("连接成功"); } }
另外注意一个人为造成的坑
- 服务端查询出来的协议版本可能因为人为因素导致设置错误,这个问题导致我在这个错误的排查上浪费了很多时间。
- 如果指定了服务端使用的协议版本还是不能请求成功时,可以考虑搜索下加密套件,看是不是属于当前协议版本的加密套件。
- 如果服务端返回的加密套件和协议版本对不上,则按照加密套件所属的协议版本进行请求。
- 虽然这个问题是人为造成,但是会给我们错误的处理带来障碍,一条路走不通时可以换一条走走。