概述
当使用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