2012-08-23 69 views
13

使用Apache HttpClient 4.2.1。使用從基於表單登錄例如Apache HTTPClient SSLPeerUnverifiedException

http://hc.apache.org/httpcomponents-client-ga/examples.html

訪問的SSL保護的登錄表單時,我得到一個異常複製的代碼:

Getting library items from https://appserver.gtportalbase.com/agileBase/AppController.servlet?return=blank 
javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated 
Closing http connection 
at sun.security.ssl.SSLSessionImpl.getPeerCertificates(SSLSessionImpl.java:397) 
at org.apache.http.conn.ssl.AbstractVerifier.verify(AbstractVerifier.java:128) 
at org.apache.http.conn.ssl.SSLSocketFactory.connectSocket(SSLSocketFactory.java:572) 
at org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.java:180) 
at org.apache.http.impl.conn.ManagedClientConnectionImpl.open(ManagedClientConnectionImpl.java:294) 
at org.apache.http.impl.client.DefaultRequestDirector.tryConnect(DefaultRequestDirector.java:640) 
at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:479) 
at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:906) 
at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:805) 
at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:784) 

據我可以告訴證書是罰款(見堆棧跟蹤之前的URL),未過期 - 瀏覽器不會投訴。

我試圖把該證書導入密鑰庫我一拉

How to handle invalid SSL certificates with Apache HttpClient?

沒有變化。我相信你可以創建一個自定義SSLContext來強制Java忽略錯誤,但我寧願修復根本原因,因爲我不想打開任何安全漏洞。

任何想法?

回答

20

編輯我意識到這個答案前接受了很長時間,也已經upvoted的3倍,但它是(至少部分)不正確,所以這裏是一個比較有關此異常。不便之處敬請原諒。

javax.net.ssl.SSLPeerUnverifiedException:同行不認證

這是通常當遠程服務器沒有證書在所有拋出的異常。但是,由於在此版本中實施的方式以及實施sun.security. ssl.SSLSocketImpl.getSession()的方式,在使用Apache HTTP Client時遇到了一個邊緣情況。

當使用Apache HTTP Client時,當遠程證書不可信時,這個異常也會被拋出,而這往往會拋出「sun.security.validator.ValidatorException: PKIX path building failed」。

發生這種情況的原因是因爲Apache HTTP客戶端在做其他事情之前嘗試獲取SSLSession和對等證書。

正如一個提醒,有3 ways of initiating the handshake with an SSLSocket

  • 調用startHandshake其中明確開始握手,或
  • 任何嘗試讀取或寫入應用數據在此插座會導致一個隱含的握手,或
  • 如果沒有當前有效的會話並且隱式握手已完成,則調用getSession會嘗試設置會話。

這裏有3個例子中,所有針對與一個證書的主機是不信任(使用javax.net.ssl.SSLSocketFactory,而不是Apache之一)。

實施例1:

SSLSocketFactory ssf = (SSLSocketFactory) sslContext.getSocketFactory(); 
    SSLSocket sslSocket = (SSLSocket) ssf.createSocket("untrusted.host.example", 
      443); 
    sslSocket.startHandshake(); 

這拋出 「javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed」(如預期)。

實施例2:

SSLSocketFactory ssf = (SSLSocketFactory) sslContext.getSocketFactory(); 
    SSLSocket sslSocket = (SSLSocket) ssf.createSocket("untrusted.host.example", 
      443); 
    sslSocket.getInputStream().read(); 

這也引發 「javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed」(如預期)。

實施例3:

SSLSocketFactory ssf = (SSLSocketFactory) sslContext.getSocketFactory(); 
    SSLSocket sslSocket = (SSLSocket) ssf.createSocket("untrusted.host.example", 
      443); 
    SSLSession sslSession = sslSocket.getSession(); 
    sslSession.getPeerCertificates(); 

然而,這將引發javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated

這是在版本4.2.1中由其org.apache.http.conn.ssl.SSLSocketFactory使用的Apache HTTP Client's AbstractVerifier中實施的邏輯。後續版本make an explicit call to startHandshake(),基於issue HTTPCLIENT-1346中的報告。

這最終似乎來自的sun.security. ssl.SSLSocketImpl.getSession()實施,其中捕獲電位IOException在叫startHandshake(false)(內部方法)時拋出,而無需進一步拋它。這可能是一個錯誤,雖然這不應該有一個巨大的安全影響,因爲SSLSocket仍然會被關閉。

例4:

SSLSocketFactory ssf = (SSLSocketFactory) sslContext.getSocketFactory(); 
    SSLSocket sslSocket = (SSLSocket) ssf.createSocket("untrusted.host.example", 
      443); 
    SSLSession sslSession = sslSocket.getSession(); 
    // sslSession.getPeerCertificates(); 
    sslSocket.getInputStream().read(); 

值得慶幸的是,這仍然會拋出「javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed」,只要你真正嘗試(通過獲取會話沒有得到同行的證書有沒有漏洞)使用SSLSocket


如何解決這個

像使用證書的任何其他問題不受信任的,這是確保你正在使用的信任存儲包含必要的信任錨的問題(即CA頒發您試圖驗證的鏈的證書,或者特殊情況下的實際服務器證書)。

要解決此問題,您應該將CA證書(或可能是服務器證書本身)導入您的信任存儲區。在您的信任存儲的本地副本

  • 在JRE信任庫,通常是cacerts文件(這並不一定是最好的,因爲這將使用該JRE會影響所有的應用程序),
  • :你可以這樣做(您可以使用-Djavax.net.ssl.trustStore=...選項進行配置),
  • 通過爲該連接創建特定SSLContext(如this answer中所述)。 (有人建議用它來,什麼也不做的信託經理,但這樣會使您的連接容易受到中間人攻擊。)

初始答案

javax.net.ssl中。SSLPeerUnverifiedException:同行不認證

這有什麼做與信任的證書或您不必創建一個自定義SSLContext:這是由於服務器不發送任何證書都沒有。

該服務器明顯未配置爲正確支持TLS。這種失敗(您將無法獲得遠程證書):

openssl s_client -tls1 -showcerts -connect appserver.gtportalbase.com:443 

然而,在SSLv3似乎工作:

openssl s_client -ssl3 -showcerts -connect appserver.gtportalbase.com:443 

如果您知道是誰在運行這個服務器,值得與他們聯繫解決這個問題。至少現在服務器應該至少支持TLSv1。

同時,解決此問題的一種方法是創建您自己的org.apache.http.conn.ssl.SSLSocketFactory並將其用於與Apache Http客戶端的此連接。

這個工廠需要像往常一樣創建SSLSocket,在返回該套接字之前使用sslSocket.setEnabledProtocols(new String[] {"SSLv3"});來禁用TLS,否則默認情況下會啓用TLS。

+0

嗯,我得到了兩個遠程證書。將嘗試從幾個不同的客戶端 –

+0

從OSX,這兩個工作,從Linux,只有ssl3。將調查服務器配置。 –

+0

這可能與密碼套件有關。在你的OSX客戶端上,'openssl ciphers DEFAULT'與你的Linux客戶端有相同的密碼套件嗎?一些會在OSX列表中,但不在Linux上? – Bruno

0

創建自定義上下文,以便您可以記錄證書無效的原因。或者只是調試到它。