Apache HTTP服务器发生间歇性SSL握手错误
我发现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 ...
问题运行:
- 是什么让服务器发送证书请求,在客户端上引起 输出中 “*** CertificateRequest”?
- 设置“SSLVerifyClient none”是否阻止证书请求?
- 关于接下来看什么的任何建议?
更新:我注意到,当使用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是否会导致问题消失。
是什么让服务器发送证书请求,导致客户端输出“*** 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版本。
是什么让服务器发送证书请求,导致客户端输出“*** CertificateRequest”?
某处你有SSLVerifyClient
设置为“可选”或更高,可能在.htaccess文件,这就是为什么你不能在配置文件中找到它。这将覆盖全局设置的“SSLVerifyClient none”。
系统管理员告诉我周围没有.htaccess文件。 “SSLVerifyClient none”仅在
某处或其他地方的配置比'无'高。否则,证书请求将不会被发送。对此没有任何疑问。 – EJP
非常感谢您的帮助!由于我无法控制的情况,进展缓慢。我要求配置mod_info,以便配置可以在运行时显示;也许它会显示一个意外的SSLVerifyClient设置。我通过启动新的JVM并反复访问相同的URL来生成零星的证书请求。这些调用的一小部分显示证书请求并失败。任何想法为什么这只会偶尔发生?据我所知,证书请求是服务器SSL状态的一部分,为什么它会对同一请求做出不同反应? – JanDasWiesel
经过漫长而乏味的调试和分析,我想报告调查结果。该问题与客户端发送的TLS握手的第一个ClientHello
消息有关。
Java 7和Java 8客户端(也可能是所有现代浏览器)都使用“服务器名称指示”(SNI)扩展名。只有使用此扩展时才会出现此问题。 Java 6客户端不会发送它,并且问题从未发生过。 TLSv1和TLSv1.2出现问题 - 我们从未观察到TLSv1.1请求,因此我无法发表评论。
对于Java 7和8客户端来说,我们可以设置-Djsse.enableSNIExtension=false
来阻止客户端发送SNI扩展。
我们将尝试更改httpd.conf
配置(以及包含的文件)以查看我们是否可以使服务器正常运行。如果我们成功,我可能会发布信息,例如,如果有服务器端解决方法。上面提到的Java开关是一种客户端解决方法,它至少足以满足我们每晚的构建。
我在jre 8的tomcat 8.5上有这个确切的问题。不幸的是,禁用SNI并不能解决它(至少在我的情况下)。 – spy
它在CertificateRequest中发送给客户端。没有理由发送它,否则。 – EJP
@EJP - 啊,你说得对。对于那个很抱歉。 – jww
增加了Java示例代码,OpenSSL不在那里使用。我无法访问实际的机器。系统管理员告诉我“安装了OpenSSL 1.0.1h 2014年6月5日,扩展3.0.16.0”。我假设Apache使用共享库。 – JanDasWiesel