1. 程式人生 > >https信任庫採坑記

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