关于java中用httpclient访问https服务报错

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target  
at sun.security.ssl.Alerts.getSSLException(Alerts.java:192)  
at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1949)  
at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:302)  
at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:296)  
at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1514)  
at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:216)  
at sun.security.ssl.Handshaker.processLoop(Handshaker.java:1026)  
at sun.security.ssl.Handshaker.process_record(Handshaker.java:961)  
at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1062)  
at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1375)  
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1403)  
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1387)  
at sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:559)  
at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:185)  
at sun.net.www.protocol.https.HttpsURLConnectionImpl.connect(HttpsURLConnectionImpl.java:153)  
at TestSecuredConnection.testConnectionTo(TestSecuredConnection.java:30)  
at TestSecuredConnection.main(TestSecuredConnection.java:16)  
Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target  
at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:387)  
at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:292)  
at sun.security.validator.Validator.validate(Validator.java:260)  
at sun.security.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:324)  
at sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:229)  
at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:124)  
at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1496)  
... 12 more  
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target  
at sun.security.provider.certpath.SunCertPathBuilder.build(SunCertPathBuilder.java:141)  
at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:126)  
at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:280)  
at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:382)  
... 18 more  

PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

主要看这一句,是说没有找到的指定的证书。

我们知道ssl握手是又服务端返回证书信息,客户端进行校验,这里有个隐藏的规则,因为证书是链式签发的

root ca–>一级ca -—>二级ca –>我的网站

问题1

很多用户在部署证书的时候都只部署了自己的证书,这就造成浏览器一般会把受信任的证书补全,类似curl,httpclient等工具会利用操作系统本地的cert列表 eg:/etc/local/cert 来补全公信的证书,某些时候如果操作系统的cert不全或者java的security\cacert里面没有添加公信ca就会出现问题了。(不同版本的jre带的cacert可能会不一样,所以同样的代码 有些环境就不报错)

注意
微信小程序在中级证书没有部署的情况下,是会ssl握手失败的,和java一样的机制,本质是android中java握手失败。但是浏览器却完全正常。 报错日志为:2019-1-9 17:19:52 [log] wx.request fail callback with msg request:fail ssl hand shake error with seq 0

解决办法:

服务器部署证书的时候部署全证书链,如下图即为典型的没有添加全证书链

通过myssl校验证书信息

enter image description here
在kong的certificate中补全证书链信息后就正常了 用java验证也通过:
验证代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
public void testConnectionTo(String aURL) throws Exception {  
URL destinationURL = new URL(aURL);  
HttpsURLConnection conn = (HttpsURLConnection) destinationURL  
.openConnection();  
conn.connect();  
Certificate[] certs = conn.getServerCertificates();  
for (Certificate cert : certs) {  
System.out.println("Certificate is: " + cert);  
if(cert instanceof X509Certificate) {  
try {  
( (X509Certificate) cert).checkValidity();  
System.out.println("Certificate is active for current date");  
} catch(CertificateExpiredException cee) {  
System.out.println("Certificate is expired");  
}  
}  
}  
}  

结果:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
[2]: ObjectId: 1.3.6.1.5.5.7.1.1 Criticality=false  
AuthorityInfoAccess [  
[  
accessMethod: ocsp  
accessLocation: URIName: http://ocsp2.digicert.com  
,  
accessMethod: caIssuers  
accessLocation: URIName: http://cacerts.digitalcertvalidation.com/TrustAsiaTLSRSACA.crt  
]  
]  
  

问题2

java中默认采用的cacert中没有对应的证书供应商的证书问题,需要导出证书文件才可以通过,推荐java中忽略ssl验证。

备注

java中默认请求https时候回失败,是因为默认java类库开启了ssl握手的校验,nginx中默认连接ssl的时候总是成功是默认没有校验证书

1
2
3
4
5
6
7
  
Syntax: proxy_ssl_verify on | off;  
Default:  
proxy_ssl_verify off;  
Context: http, server, location  
This directive appeared in version 1.7.0.  
  

Enables or disables verification of the proxied HTTPS server certificate.