当使用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中无法访问,错误如下:
解决方案参照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() { ???????????????@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
记录一次解决httpcline请求https报handshake_failure错误
原文地址:http://blog.51cto.com/2074199/2088928