我发现Java客户端和浏览器通过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)客户端日志(缩写)
*** 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”?
1.设置“SSLVerifyClient none”是否不阻止证书请求?
1.对下一步要看什么有什么建议吗?
更新:我注意到Java客户端在使用Java 6时从不显示错误,问题发生在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是否会使问题消失。
3条答案
按热度按时间xoshrz7s1#
是什么原因使服务器发送证书请求,并在客户端上导致输出“***CertificateRequest”?
在某个地方你将
SSLVerifyClient
设置为'optional'或更高,可能是在.htaccess文件中,这就是为什么你在配置文件中找不到它。这会覆盖全局设置的'SSLVerifyClient none'。qco9c6ql2#
是什么原因使服务器发送证书请求,并在客户端上导致输出“***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 none”是否不阻止证书请求?
有可能。在
SSL_CTX_set_verify
man page中,您需要SSL_VERIFY_PEER
,还可能需要SSL_VERIFY_FAIL_IF_NO_PEER_CERT
。对下一步要看什么有什么建议吗?
您应该显示相关的Java代码。
您应该说明服务器和客户端使用的OpenSSL版本。
6rvt4ljy3#
经过长时间的调试和分析,我想报告一下发现的问题。问题与客户端发送的TLS握手的第一条
ClientHello
消息有关。Java 7和Java 8客户端(可能还有所有现代浏览器)都使用“服务器名称指示”(SNI)扩展。只有在使用此扩展时才会出现问题。Java 6客户端不会发送此扩展,因此不会出现问题。问题出现在TLSv 1和TLSv1.2 -我们从未观察到TLSv1.1请求,因此我无法对此进行评论。
对于Java 7和8客户端,为了防止出现此问题,我们可以设置
-Djsse.enableSNIExtension=false
,以阻止客户端发送SNI扩展。我们将尝试更改
httpd.conf
配置(和包含的文件),看看我们是否能让服务器运行良好。如果我们成功了,我可能会发布信息,即是否有服务器端的解决方案。上面提到的Java开关是一个客户端的解决方案,它至少对我们的夜间构建足够好。