当前位置: 首页 > 工具软件 > Java8kanren > 使用案例 >

Java8支持TLSv1.3协议请求

孙德宇
2023-12-01

Java默认使用的协议版本

Java版本HTTPS请求默认使用的TLS版本支持的协议
JDK7TLSv1TLSv1、TLSv1.1、TLSv1.2、SSLv3
JDK8TLSv1.2TLSv1、TLSv1.1、TLSv1.2、SSLv3
JDK11TLSv1.3TLSv1、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)

查看服务端支持的协议版本

  1. 网站查询:https://myssl.com/ 或者 https://www.ssllabs.com/

  2. 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("连接成功");
        }
    }
    

另外注意一个人为造成的坑

  • 服务端查询出来的协议版本可能因为人为因素导致设置错误,这个问题导致我在这个错误的排查上浪费了很多时间。
  • 如果指定了服务端使用的协议版本还是不能请求成功时,可以考虑搜索下加密套件,看是不是属于当前协议版本的加密套件。
  • 如果服务端返回的加密套件和协议版本对不上,则按照加密套件所属的协议版本进行请求。
  • 虽然这个问题是人为造成,但是会给我们错误的处理带来障碍,一条路走不通时可以换一条走走。
 类似资料: