最近在客户现场遇到一个棘手的http问题,现象很直接,访问某https的时候报错:
javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated
at sun.security.ssl.SSLSessionImpl.getPeerCertificates(SSLSessionImpl.java:)
at org.apache.http.conn.ssl.AbstractVerifier.verify(AbstractVerifier.java:)
at org.apache.http.conn.ssl.SSLSocketFactory.connectSocket(SSLSocketFactory.java:)
at org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.java:)
at org.apache.http.impl.conn.AbstractPoolEntry.open(AbstractPoolEntry.java:)
at org.apache.http.impl.conn.AbstractPooledConnAdapter.open(AbstractPooledConnAdapter.java:)
at org.apache.http.impl.client.DefaultRequestDirector.tryConnect(DefaultRequestDirector.java:)
at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:)
at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:)
at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:)
at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:)
从线程栈来看,是使用httpclient访问https服务的时候报错了,初步猜测是证书没有配置对,就想是不是要配置双向认证。
经过和现场工程师交流,发现这个只是个简单的单向认证,而且在浏览器使用GET发送请求,居然也是返回的200。
于是想到可能是JDK和程序本身的问题,根据httpclient写了个很简单的应用去访问,调用的错误环境下实际使用的JDK,发现也没有问题。
正要开始排查程序问题的时候,现场工程师说是另一个测试的https服务器通过应用程序可以访问通。这下立马有了头绪,可以抓个包对比一下https握手的过程。
果然,差别发现了:能够正常握手的https服务端返回了3个证书,握手错误的只返回了2个证书:
(正确的https服务器的通讯)
(有问题的https服务器的通讯)
看到这里,需要了解一个知识点了:信任库,只有信任库信任了密钥库的证书,才能进行通信。
我们详细看下密钥库的证书链:
发现多出来的证书是Baltimore CyberTrust Root
我们发现jdk能够信任这个多出来的和共有的证书
那为什么应用程序就不行呢,这个时候我们就需要使用jvm属性 -Djavax.net.debug=ssl:handshake了,他可以看到非常详细的https握手信息。
果然,输出的第一段日志就是trustStore is: /home/***/config/security/cacerts,原来使用的不是jdk的默认cacerts,而是自定义了-Djavax.net.ssl.trustStore的值。
那么问题就好解决了,删除即可。
再来看下这个配置的cacerts为啥不能认证,
原来他只信任了这个多出来的证书,机缘凑巧地能够访问测试的机器。验证了为什么能否访问一台https服务器,而另一台访问不了的问题。
另外日志中的一些其他信息我们也可以关注一下,便于遇到类似问题的时候分析:
1)关键字chain可以看到证书链的详细信息
2)两个非常有用的异常
handling exception: javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building fail
ed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
IOException in getSession(): javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path bui
lding failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
参考:
1、HttpClient javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated when time shifted?
https://stackoverflow.com/questions/24752485/httpclient-javax-net-ssl-sslpeerunverifiedexception-peer-not-authenticated-when
2、Resolving javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed Error?
https://stackoverflow.com/questions/9619030/resolving-javax-net-ssl-sslhandshakeexception-sun-security-validator-validatore
3、javax.net.ssl.SSLHandshakeException的解决办法
https://blog.csdn.net/yiifaa/article/details/73148665
4、How to Analyze Java SSL Errors
https://dzone.com/articles/how-analyze-java-ssl-errors