編輯我意識到這個答案前接受了很長時間,也已經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。
嗯,我得到了兩個遠程證書。將嘗試從幾個不同的客戶端 –
從OSX,這兩個工作,從Linux,只有ssl3。將調查服務器配置。 –
這可能與密碼套件有關。在你的OSX客戶端上,'openssl ciphers DEFAULT'與你的Linux客戶端有相同的密碼套件嗎?一些會在OSX列表中,但不在Linux上? – Bruno