記錄一次解決httpcline請求https報handshake_failure錯誤

概述

當使用httpclinet發起https請求時報如下錯誤:
javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure
    at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Alerts.java:174)
    at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Alerts.java:136)
    at com.sun.net.ssl.internal.ssl.SSLSocketImpl.recvAlert(SSLSocketImpl.java:1657)
    at com.sun.net.ssl.internal.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:932)
    at com.sun.net.ssl.internal.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1096)
    at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1123)
    at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1107)
    at org.apache.http.conn.ssl.SSLConnectionSocketFactory.connectSocket(SSLConnectionSocketFactory.java:261)
    at org.apache.http.impl.conn.HttpClientConnectionOperator.connect(HttpClientConnectionOperator.java:118)
    at org.apache.http.impl.conn.PoolingHttpClientConnectionManager.connect(PoolingHttpClientConnectionManager.java:314)
    at org.apache.http.impl.execchain.MainClientExec.establishRoute(MainClientExec.java:357)
    at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:218)
    at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:194)
    at org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:85)
    at org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:108)
    at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:186)
    at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:82)
    at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:106)

未解決參照url

訪問https,拋出的異常javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure
使用HttpClient發送HTTPS請求以及配置Tomcat支持SSL

分析過程

背景交待

由於證書是字自籤自發,並且加固過,相信很多人會問爲什麼加固, 因爲你如果不加固的話https將在ff中無法訪問,錯誤如下:
記錄一次解決httpcline請求https報handshake_failure錯誤

解決方案參照Tomcat6+JDK6如何加固,解決Logjam attack

解決過程分析

不加固是否可以直接訪問

經測試 不加固的情況訪問沒有問題

加固後不能訪問原因分析

因爲加固主要是指定了protocols和ciphers,所以請求時是否也可以指定protocols和ciphers,查閱官方文檔發現如下信息
記錄一次解決httpcline請求https報handshake_failure錯誤
通過在httpclient請求之前設置protocols和ciphers,代碼如下:

        System.setProperty("https.protocols", "與server.xml中的protocols一致");
        System.setProperty("https.cipherSuites", "與server.xml中的ciphers一致");

重新發起請求,發現還是報錯

分析設置是否生效

通過debug httpclinet下HttpClientBuilder類的源代碼發現如下
記錄一次解決httpcline請求https報handshake_failure錯誤
則將代碼增加如下粗體:
HttpClients.custom().useSystemProperties().setDefaultRequestConfig(defaultRequestConfig).setSslcontext(sslcontext).build();
重新發起請求,發現還是報錯

查詢本地支持的協議及算法

代碼如下:

public class HttpsTest {
    public static void main(String[] args) {
        SSLContext sc;
        try {
            sc = SSLContext.getInstance("TLS");
            // 實現一個X509TrustManager接口,用於繞過驗證,不用修改裏面的方法
            X509TrustManager trustManager = new X509TrustManager() {
                @Override
                public void checkClientTrusted(
                        java.security.cert.X509Certificate[] paramArrayOfX509Certificate,
                        String paramString) throws CertificateException {
                }

                @Override
                public void checkServerTrusted(
                        java.security.cert.X509Certificate[] paramArrayOfX509Certificate,
                        String paramString) throws CertificateException {
                }

                @Override
                public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                    return null;
                }
            };

            sc.init(null, new TrustManager[] { trustManager }, null);
            System.out.println("缺省安全套接字使用的協議: " + sc.getProtocol());  
            // 獲取SSLContext實例相關的SSLEngine  
            SSLEngine en = sc.createSSLEngine();  
            System.out  
                    .println("支持的協議: " + Arrays.asList(en.getSupportedProtocols()));  
            System.out.println("啓用的協議: " + Arrays.asList(en.getEnabledProtocols()));  
            System.out.println("支持的加密套件: "  
                    + Arrays.asList(en.getSupportedCipherSuites()));  
            System.out.println("啓用的加密套件: "  
                    + Arrays.asList(en.getEnabledCipherSuites())); 
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }
}
然後在httpclient請求之前設置protocols和ciphers,
    System.setProperty("https.protocols", "其值爲服務器和本地相同的");
    System.setProperty("https.cipherSuites", "其值爲服務器和本地相同的");

重新發起請求,請求成功。

版本說明

httpclinet:4.3.1
jdk:1.6
tomcat:6

httpclient發起請求代碼

訪問 https://gitee.com/die/help_common.git中的httpclinet進行下載

參考文章

HttpClient如何指定CipherSuites

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章