2016-06-28 116 views
1

我發現Java客戶端和瀏覽器通過Apache HTTP服務器訪問站點時發生間歇性SSL握手錯誤。這種情況很少發生,但它每天都會破壞構建並影響用戶體驗。服務器配置爲建立安全連接,但不需要來自客戶端的證書。Apache HTTP服務器發生間歇性SSL握手錯誤

我已經在Java測試客戶端和服務器上開啓了SSL調試輸出(見下文)。我所觀察到的是,無論何時發生握手異常,服務器似乎都發送了客戶端證書的請求。我不明白爲什麼它會這樣做,爲什麼這隻會偶爾發生。當它發送證書請求時,幾乎總是發生錯誤,但是我也在成功的情況下捕獲了請求(可能有1%的時間,99%的失敗)。

客戶端使用Java 8(1.8.0_31-b13),Apache HTTP服務器版本爲2.2.19。

下面是從日誌片段:

1)Apache配置摘錄

SSLProtocol ALL -SSLv2 -SSLv3 
SSLCertificateFile <path-to-pem1> 
SSLCertificateChainFile <path-to-pem2> 
SSLCACertificateFile <path-to-pem3> 
SSLVerifyDepth 10 
SSLVerifyClient none 

的.PEM文件是可讀的。

2)客戶端日誌(severly略)

*** ClientHello, TLSv1.2 
*** 
*** ServerHello, TLSv1 
*** 
*** Certificate chain 
*** 
*** Diffie-Hellman ServerKeyExchange 
*** CertificateRequest 
*** ServerHelloDone 
*** Certificate chain 
*** 
*** ClientKeyExchange, DH 
*** Finished 
*** 
... 
main, WRITE: TLSv1 Handshake, length = 48 
main, waiting for close_notify or alert: state 1 
main, Exception while waiting for close java.net.SocketException: Software caused connection abort: recv failed 
main, handling exception: java.net.SocketException: Software caused connection abort: recv failed 
%% Invalidated: [Session-1, TLS_DHE_RSA_WITH_AES_128_CBC_SHA] 
main, SEND TLSv1 ALERT: fatal, description = unexpected_message 
... 
main, WRITE: TLSv1 Alert, length = 32 
main, Exception sending alert: java.net.SocketException: Software caused connection abort: socket write error 
main, called closeSocket() 

3)服務器日誌(略)

[info] [client x.x.x.x] Connection to child 1 established (server XXX:443) 
[info] Seeding PRNG with 0 bytes of entropy 
[debug] ssl_engine_kernel.c(1866): OpenSSL: Handshake: start 
[debug] ssl_engine_kernel.c(1874): OpenSSL: Loop: before/accept initialization 
[debug] ssl_engine_io.c(1897): OpenSSL: read 11/11 bytes from BIO#82f6820 [mem: 82c5290] (BIO dump follows) 
[debug] ssl_engine_io.c(1830): +-------------------------------------------------------------------------+ 
[debug] ssl_engine_io.c(1869): | 0000: XX XX XX XX XX XX XX XX-XX XX XX     ...........  | 
[debug] ssl_engine_io.c(1875): +-------------------------------------------------------------------------+ 
[debug] ssl_engine_io.c(1897): OpenSSL: read 245/245 bytes from BIO#82f6820 [mem: 82c529b] (BIO dump follows) 
[debug] ssl_engine_io.c(1830): +-------------------------------------------------------------------------+ 
[debug] ssl_engine_io.c(1869): | 0000: XX XX XX XX XX XX XX XX-XX XX XX XX XX XX XX XX ................ | 
... 
[debug] ssl_engine_io.c(1869): | 00f0: XX XX XX XX XX         .....   | 
[debug] ssl_engine_io.c(1875): +-------------------------------------------------------------------------+ 
[debug] ssl_engine_kernel.c(1987): [client x.x.x.x] SSL virtual host for servername XXX found 
[debug] ssl_engine_kernel.c(1874): OpenSSL: Loop: SSLv3 read client hello A 
[debug] ssl_engine_kernel.c(1874): OpenSSL: Loop: SSLv3 write server hello A 
[debug] ssl_engine_kernel.c(1874): OpenSSL: Loop: SSLv3 write certificate A 
[debug] ssl_engine_kernel.c(1274): [client x.x.x.x] handing out temporary 1024 bit DH key 
[debug] ssl_engine_kernel.c(1874): OpenSSL: Loop: SSLv3 write key exchange A 
[debug] ssl_engine_kernel.c(1874): OpenSSL: Loop: SSLv3 write certificate request A 
[debug] ssl_engine_kernel.c(1874): OpenSSL: Loop: SSLv3 flush data 
[debug] ssl_engine_io.c(1897): OpenSSL: read 5/5 bytes from BIO#82f6820 [mem: 82c5290] (BIO dump follows) 
[debug] ssl_engine_io.c(1830): +-------------------------------------------------------------------------+ 
[debug] ssl_engine_io.c(1869): | 0000: 16 03 01 00 8d         .....   | 
[debug] ssl_engine_io.c(1875): +-------------------------------------------------------------------------+ 
[debug] ssl_engine_io.c(1897): OpenSSL: read 141/141 bytes from BIO#82f6820 [mem: 82c5295] (BIO dump follows) 
[debug] ssl_engine_io.c(1830): +-------------------------------------------------------------------------+ 
[debug] ssl_engine_io.c(1869): | 0000: XX XX XX XX XX XX XX XX-XX XX XX XX XX XX XX XX ................ | 
... 
[debug] ssl_engine_io.c(1869): | 0080: XX XX XX XX XX XX XX XX-XX XX XX XX XX   ............. | 
[debug] ssl_engine_io.c(1875): +-------------------------------------------------------------------------+ 
[debug] ssl_engine_kernel.c(1884): OpenSSL: Write: SSLv3 read client certificate B 
[debug] ssl_engine_kernel.c(1903): OpenSSL: Exit: error in SSLv3 read client certificate B 
[debug] ssl_engine_kernel.c(1903): OpenSSL: Exit: error in SSLv3 read client certificate B 
[info] [client x.x.x.x] SSL library error 1 in handshake (server XXX:443) 
[info] SSL Library Error: 336105671 error:140890C7:SSL routines:SSL3_GET_CLIENT_CERTIFICATE:peer did not return a certificate No CAs known to server for verification? 
[info] [client x.x.x.x] Connection closed to child 1 with abortive shutdown (server XXX:443) 

在良好的情況下,我們始終沒有看到 「*** CertificateRequest」 在客戶端和這樣輸出的服務器上

[debug] ssl_engine_kernel.c(1987): [client x.x.x.x] SSL virtual host for servername XXX found 
[debug] ssl_engine_kernel.c(1874): OpenSSL: Loop: SSLv3 read client hello A 
[debug] ssl_engine_kernel.c(1874): OpenSSL: Loop: SSLv3 write server hello A 
[debug] ssl_engine_kernel.c(1874): OpenSSL: Loop: SSLv3 write certificate A 
[debug] ssl_engine_kernel.c(1274): [client x.x.x.x] handing out temporary 1024 bit DH key 
[debug] ssl_engine_kernel.c(1874): OpenSSL: Loop: SSLv3 write key exchange A 
[debug] ssl_engine_kernel.c(1874): OpenSSL: Loop: SSLv3 write server done A 
[debug] ssl_engine_kernel.c(1874): OpenSSL: Loop: SSLv3 flush data 
[debug] ssl_engine_io.c(1897): OpenSSL: read 5/5 bytes from BIO#82d82d8 [mem: 82c8790] (BIO dump follows) 
[debug] ssl_engine_io.c(1830): +-------------------------------------------------------------------------+ 
[debug] ssl_engine_io.c(1869): | 0000: 16 03 01 00 86         .....   | 
[debug] ssl_engine_io.c(1875): +-------------------------------------------------------------------------+ 
[debug] ssl_engine_io.c(1897): OpenSSL: read 134/134 bytes from BIO#82d82d8 [mem: 82c8795] (BIO dump follows) 
[debug] ssl_engine_io.c(1830): +-------------------------------------------------------------------------+ 
[debug] ssl_engine_io.c(1869): | 0000: XX XX XX XX XX XX XX XX-XX XX XX XX XX XX XX XX ................ | 
... 
[debug] ssl_engine_io.c(1869): | 0080: XX XX XX XX XX XX        ......   | 
[debug] ssl_engine_io.c(1875): +-------------------------------------------------------------------------+ 
[debug] ssl_engine_kernel.c(1874): OpenSSL: Loop: SSLv3 read client key exchange A 

Java客戶端代碼大綱:

URL url = new URL("https://..."); 
HttpsURLConnection connection = (HttpsURLConnection) url.openConnection(); 
connection.setRequestProperty("Authorization", "Basic " + DatatypeConverter.printBase64Binary((user + ":" + pass).getBytes(Charset.forName("UTF-8")))); 
connection.setReadTimeout(60*1000); 
connection.setUseCaches(false); 
connection.connect(); 

java -Djavax.net.ssl.trustStore=... -Djavax.net.ssl.trustStoreType=jks -Djavax.net.ssl.trustStorePassword=... -Djavax.net.debug=all ... 

問題運行:

  1. 是什麼讓服務器發送證書請求,在客戶端上引起 輸出中 「*** CertificateRequest」?
  2. 設置「SSLVerifyClient none」是否阻止證書請求?
  3. 關於接下來看什麼的任何建議?

更新:我注意到,當使用Java 6時,Java客戶端從不顯示錯誤。 Java 7和8以及Chrome,Internet Explorer和Firefox都會出現問題。這似乎指向TLSv1.1或TLSv1.2的問題(另請參閱https://serverfault.com/questions/513961/how-to-disable-tls-1-1-1-2-in-apache「OpenSSL v1.0.1在TLSv1.2中存在一些已知問題」)。我將盡力至少檢查在Java客戶端中禁用TLSv1.1/2是否會導致問題消失。

回答

0

是什麼讓服務器發送證書請求,導致客戶端輸出「*** CertificateRequest」?

在服務器上調用SSL_CTX_set_client_CA_list。它是一個可分辨名稱的列表。另請參閱SSL_CTX_set_client_CA_list man page

服務器還需要撥打SSL_CTX_load_verify_locations在CA中建立信任。另見SSL_CTX_load_verify_locations man page


是設置 「SSLVerifyClient無」 不會阻止證書請求?

可能。從SSL_CTX_set_verify man page開始,您需要SSL_VERIFY_PEER並可能需要SSL_VERIFY_FAIL_IF_NO_PEER_CERT


什麼就看未來有什麼建議?

您應該顯示相關的Java代碼。

您應該聲明服務器和客戶端使用的OpenSSL版本。

+0

它在CertificateRequest中發送給客戶端。沒有理由發送它,否則。 – EJP

+0

@EJP - 啊,你說得對。對於那個很抱歉。 – jww

+0

增加了Java示例代碼,OpenSSL不在那裏使用。我無法訪問實際的機器。系統管理員告訴我「安裝了OpenSSL 1.0.1h 2014年6月5日,<第三方公司名稱>擴展3.0.16.0」。我假設Apache使用共享庫。 – JanDasWiesel

0

是什麼讓服務器發送證書請求,導致客戶端輸出「*** CertificateRequest」?

某處你有SSLVerifyClient設置爲「可選」或更高,可能在.htaccess文件,這就是爲什麼你不能在配置文件中找到它。這將覆蓋全局設置的「SSLVerifyClient none」。

+0

系統管理員告訴我周圍沒有.htaccess文件。 「SSLVerifyClient none」僅在部分配置。將其移入httpd.conf(全局) - 看起來很有前途,但我明天才會知道更多。仍然困惑我爲什麼這個問題應該偶爾發生。 – JanDasWiesel

+0

某處或其他地方的配置比'無'高。否則,證書請求將不會被髮送。對此沒有任何疑問。 – EJP

+0

非常感謝您的幫助!由於我無法控制的情況,進展緩慢。我要求配置mod_info,以便配置可以在運行時顯示;也許它會顯示一個意外的SSLVerifyClient設置。我通過啓動新的JVM並反覆訪問相同的URL來生成零星的證書請求。這些調用的一小部分顯示證書請求並失敗。任何想法爲什麼這隻會偶爾發生?據我所知,證書請求是服務器SSL狀態的一部分,爲什麼它會對同一請求做出不同反應? – JanDasWiesel

0

經過漫長而乏味的調試和分析,我想報告調查結果。該問題與客戶端發送的TLS握手的第一個ClientHello消息有關。

Java 7和Java 8客戶端(也可能是所有現代瀏覽器)都使用「服務器名稱指示」(SNI)擴展名。只有使用此擴展時纔會出現此問題。 Java 6客戶端不會發送它,並且問題從未發生過。 TLSv1和TLSv1.2出現問題 - 我們從未觀察到TLSv1.1請求,因此我無法發表評論。

對於Java 7和8客戶端來說,我們可以設置-Djsse.enableSNIExtension=false來阻止客戶端發送SNI擴展。

我們將嘗試更改httpd.conf配置(以及包含的文件)以查看我們是否可以使服務器正常運行。如果我們成功,我可能會發布信息,例如,如果有服務器端解決方法。上面提到的Java開關是一種客戶端解決方法,它至少足以滿足我們每晚的構建。

+0

我在jre 8的tomcat 8.5上有這個確切的問題。不幸的是,禁用SNI並不能解決它(至少在我的情況下)。 – spy