Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HttpClient4.5.x请求https异常javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure #54

Open
yankj12 opened this issue Sep 27, 2018 · 0 comments

Comments

@yankj12
Copy link
Owner

yankj12 commented Sep 27, 2018

HttpClient4.5.x请求https抛出handshake_failure异常

抛出handshake_failure异常

javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure

详细报错信息如下

main, handling exception: javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure
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:2033)
	at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1135)
	at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1385)
	at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1413)
	at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1397)
	at org.apache.http.conn.ssl.SSLConnectionSocketFactory.createLayeredSocket(SSLConnectionSocketFactory.java:396)
	at org.apache.http.conn.ssl.SSLConnectionSocketFactory.connectSocket(SSLConnectionSocketFactory.java:355)
	at org.apache.http.impl.conn.DefaultHttpClientConnectionOperator.connect(DefaultHttpClientConnectionOperator.java:142)
	at org.apache.http.impl.conn.PoolingHttpClientConnectionManager.connect(PoolingHttpClientConnectionManager.java:373)
	at org.apache.http.impl.execchain.MainClientExec.establishRoute(MainClientExec.java:394)
	at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:237)
	at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:185)
	at org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:89)
	at org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:110)
	at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:185)
	at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:83)
	at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:108)

替换jre中相关jar包

根据peer not authenticated的终极解决方案等链接中说明,可以替换jar包解决。但是我替换了jar包,eclipse启不来了,就换回去了
按照这篇文章中的说法

二、缘由
  首先,要知道导致报这个异常的原因不仅仅是因为证书校验不通过。
  都知道,在我们通过https链接服务器时,服务器会给我们返回一个证书,这个证书可能经过CA认证,也可能是未认证的自制证书,客户端拿到这个证书后会对这个证书进行验证,如果是经过CA验证的证书,自然证书校验就能通过,自制证书自然就校验不同过,从而导致上边的异常。
  证书校验通过后,还需要校验访问的域名是否和证书指定的域名是否匹配。未匹配也会导致如上异常。
  上边两步都校验通过了才开始进行握手,但握手也有可能失败,从而导致上边的异常。
  以上三个步骤中任何一个出了问题,都会连接失败。
按照文章中所有,有三个环节:验证证书、验证域名、握手,这三个环节都有可能出错。我下面的改动,似乎只改动了域名验证,就可以使用了,可能是SSLConnectionSocketFactory构造方法中没有传入证书套件相关参数,进而解决了握手的问题

HttpClient4.3实现https请求信任所有证书

HttpClient4.3实现https请求信任所有证书代码

SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy(){
    // 默认信任所有证书	
    public boolean isTrusted(X509Certificate[] arg0, String arg1) throws CertificateException{
        return true;
    }
}).build();

// AllowAllHostnameVerifier: 这种方式不对主机名进行验证,验证功能被关闭,是个空操作(域名验证)
SSLConnectionSocketFactory sslcsf = new SSLConnectionSocketFactory(sslContext, SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);

这样可以解决问题,但问题是如下代码在HttpClient4.5.x过时了

过时代码
new SSLConnectionSocketFactory(sslContext, SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);

过时的应该是
SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER

HttpClient4.5.x实现https请求信任所有证书

查看官方文档,发现HttpClient4.5.x doc 2.7.4. Hostname verification 中介绍NoopHostnameVerifier

NoopHostnameVerifier:  This hostname verifier essentially turns hostname verification off. It accepts any SSL session as valid and matching the target host.

意思是说NoopHostnameVerifier是把hostname验证关闭了
代码示例

SSLContext sslContext = SSLContexts.createSystemDefault();
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
        sslContext,
        NoopHostnameVerifier.INSTANCE);

基础知识

SSL协议的握手过程

SSL 协议既用到了公钥加密技术又用到了对称加密技术,对称加密技术虽然比公钥加密技术的速度快,可是公钥加密技术提供了更好的身份认证技术。SSL 的握手协议非常有效的让客户和服务器之间完成相互之间的身份认证,其主要过程如下:

  1. 客户端的浏览器向服务器传送客户端SSL 协议的版本号,加密算法的种类,产生的随机数,以及其他服务器和客户端之间通讯所需要的各种信息。
  2. 服务器向客户端传送SSL 协议的版本号,加密算法的种类,随机数以及其他相关信息,同时服务器还将向客户端传送自己的证书。
  3. 客户利用服务器传过来的信息验证服务器的合法性,服务器的合法性包括:证书是否过期,发行服务器证书的CA 是否可靠,发行者证书的公钥能否正确解开服务器证书的“发行者的数字签名”,服务器证书上的域名是否和服务器的实际域名相匹配。如果合法性验证没有通过,通讯将断开;如果合法性验证通过,将继续进行第四步。
  4. 用户端随机产生一个用于后面通讯的“对称密码”,然后用服务器的公钥(服务器的公钥从步骤②中的服务器的证书中获得)对其加密,然后传给服务器。
  5. 服务器用私钥解密“对称密码”(此处的公钥和私钥是相互关联的,公钥加密的数据只能用私钥解密,私钥只在服务器端保留。详细请参看: http://zh.wikipedia.org/wiki/RSA%E7%AE%97%E6%B3%95),然后用其作为服务器和客户端的“通话密码”加解密通讯。同时在SSL通讯过程中还要完成数据通讯的完整性,防止数据通讯中的任何变化。
  6. 客户端向服务器端发出信息,指明后面的数据通讯将使用的步骤⑤中的主密码为对称密钥,同时通知服务器客户端的握手过程结束。
  7. 服务器向客户端发出信息,指明后面的数据通讯将使用的步骤⑤中的主密码为对称密钥,同时通知客户端服务器端的握手过程结束。
  8. SSL 的握手部分结束,SSL 安全通道的数据通讯开始,客户和服务器开始使用相同的对称密钥进行数据通讯,同时进行通讯完整性的检验。

SSL证书(HTTPS)背后的加密算法

SSL证书(HTTPS)背后的加密算法

其他参考资料

InfoQ 保护好你的秘密
可以通过这个链接查询你要请求的网站支持的证书类型

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant