概述
當使用httpclinet發起https請求時報如下錯誤:
javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failureat 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中無法訪問,錯誤如下:
解決方案參照Tomcat6+JDK6如何加固,解決Logjam attack
解決過程分析
不加固是否可以直接訪問
經測試 不加固的情況訪問沒有問題
加固后不能訪問原因分析
因為加固主要是指定了protocols和ciphers,所以請求時是否也可以指定protocols和ciphers,查閱官方文檔發現如下信息
通過在httpclient請求之前設置protocols和ciphers,代碼如下:
System.setProperty("https.protocols", "與server.xml中的protocols一致");System.setProperty("https.cipherSuites", "與server.xml中的ciphers一致");
重新發起請求,發現還是報錯
分析設置是否生效
通過debug httpclinet下HttpClientBuilder類的源代碼發現如下
則將代碼增加如下粗體:
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() {@Overridepublic void checkClientTrusted(java.security.cert.X509Certificate[] paramArrayOfX509Certificate,String paramString) throws CertificateException {}@Overridepublic void checkServerTrusted(java.security.cert.X509Certificate[] paramArrayOfX509Certificate,String paramString) throws CertificateException {}@Overridepublic 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 blocke.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
轉載于:https://blog.51cto.com/2074199/2088928