https信任庫采坑記
最近在客戶現場遇到一個棘手的http問題,現象很直接,訪問某https的時候報錯:
javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated at sun.security.ssl.SSLSessionImpl.getPeerCertificates(SSLSessionImpl.java:431) at org.apache.http.conn.ssl.AbstractVerifier.verify(AbstractVerifier.java:128) at org.apache.http.conn.ssl.SSLSocketFactory.connectSocket(SSLSocketFactory.java:397) at org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.java:148) at org.apache.http.impl.conn.AbstractPoolEntry.open(AbstractPoolEntry.java:149) at org.apache.http.impl.conn.AbstractPooledConnAdapter.open(AbstractPooledConnAdapter.java:121) at org.apache.http.impl.client.DefaultRequestDirector.tryConnect(DefaultRequestDirector.java:573) at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:425) at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:820) at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:754) at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:732)
從線程棧來看,是使用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

浙公網安備 33010602011771號