我在Linux实例上的Java 8 JVM中打开了SSL日志,这重现了这个问题。 使用
-Djavax.net.debug=ssl:handshake:verbose
打开了SSL日志。 这揭示了一些有用的信息。
The
变通办法
我们在生产中使用的、被证明对我们有效的方法是在JVM上设置这个参数。
-Djdk.tls.client.protocols=TLSv1
如果你想了解更多细节,请继续阅读。
在一个可以重现该问题的服务器上(同样,只有5-10%的时间),我观察到如下情况。
*** ClientHello, TLSv1.2
--- 8<-- SNIP -----
main, WRITE: TLSv1.2 Handshake, length = 195
main, READ: TLSv1.2 Handshake, length = 1130
*** ServerHello, TLSv1.2
--- 8<-- SNIP -----
%% Initialized: [Session-79, TLS_DHE_RSA_WITH_AES_128_GCM_SHA256]
** TLS_DHE_RSA_WITH_AES_128_GCM_SHA256
--- 8<-- SNIP -----
Algorithm: [SHA1withRSA]
--- 8<-- SNIP -----
*** Diffie-Hellman ServerKeyExchange
--- 8<-- SNIP -----
*** ServerHelloDone
*** ClientKeyExchange, DH
--- 8<-- SNIP -----
main, WRITE: TLSv1.2 Handshake, length = 133
--- 8<-- SNIP -----
main, WRITE: TLSv1.2 Change Cipher Spec, length = 1
*** Finished
verify_data: { 108, 116, 29, 115, 13, 26, 154, 198, 17, 125, 114, 166 }
main, WRITE: TLSv1.2 Handshake, length = 40
main, called close()
main, called closeInternal(true)
main, SEND TLSv1.2 ALERT: warning, description = close_notify
main, WRITE: TLSv1.2 Alert, length = 26
main, called closeSocket(true)
main, waiting for close_notify or alert: state 5
main, received EOFException: ignored
main, called closeInternal(false)
main, close invoked again; state = 5
main, handling exception: java.io.IOException: SQL Server returned an incomplete response. The connection has been closed. ClientConnectionId:12a722b3-d61d-4ce4-8319-af049a0a4415
请注意TLSv1.2是由数据库服务器选择的,并在这个交换中使用。 我观察到,当来自有问题的Linux服务的连接失败时,TLSv1.2始终是被选择的级别。然而,当使用TLSv1.2时,连接并不总是失败。 他们只有5-10%的时间会失败。
现在是一个没有这个问题的服务器的交换信息。 其他方面都是一样的。即,连接到相同的数据库,相同版本的JVM(Java 1.8.0_60),相同的JDBC驱动,等等。 注意,这里。TLSv1被数据库服务器选择,而不是像有问题的服务器那样选择TLSv1.2。
*** ClientHello, TLSv1.2
--- 8<-- SNIP -----
main, WRITE: TLSv1.2 Handshake, length = 207
main, READ: TLSv1 Handshake, length = 604
*** ServerHello, TLSv1
--- 8<-- SNIP -----
Cipher Suite: TLS_RSA_WITH_AES_128_CBC_SHA
--- 8<-- SNIP -----
%% Initialized: [Session-79, TLS_RSA_WITH_AES_128_CBC_SHA]
** TLS_RSA_WITH_AES_128_CBC_SHA
--- 8<-- SNIP -----
Algorithm: [SHA1withRSA]
--- 8<-- SNIP -----
*** ServerHelloDone
*** ClientKeyExchange, RSA PreMasterSecret, TLSv1
--- 8<-- SNIP -----
main, WRITE: TLSv1 Handshake, length = 134
main, WRITE: TLSv1 Change Cipher Spec, length = 1
*** Finished
verify_data: { 26, 155, 166, 89, 229, 193, 126, 39, 103, 206, 126, 21 }
main, WRITE: TLSv1 Handshake, length = 48
main, READ: TLSv1 Change Cipher Spec, length = 1
main, READ: TLSv1 Handshake, length = 48
*** Finished
因此,当Linux JVM和SQL Server之间协商TLSv1时,连接总是成功的。 当TLSv1.2被协商时,我们得到零星的连接失败。
(注意:Java 7(1.7.0_51)总是协商TLSv1,这就是为什么我们使用Java 7 JVM时从未发生过这个问题)。
我们仍然存在的公开问题是。
WHY is that the same Java 8 JVM run from 2 different Linux servers will always negotiate TLSv1, but when connecting from another Linux server it always negotiates TLSv1.2.
And also why are TLSv1.2 negotiated connections successful most, but not all, of the time on that server?
2017年10月6日更新。
This posting from Microsoft描述了该问题和他们提出的解决方案。
Resources:
http://www.infoworld.com/article/2849292/operating-systems/more-patch-problems-reported-with-the-ms14-066-kb-2992611-winshock-mess.html
http://www.infoworld.com/article/2849292/operating-systems/more-patch-problems-reported-with-the-ms14-066-kb-2992611-winshock-mess.html
http://blogs.msdn.com/b/jdbcteam/archive/2008/09/09/the-driver-could-not-establish-a-secure-connection-to-sql-server-by-using-secure-sockets-layer-ssl-encryption.aspx
Java 8 , JCE无限强度策略和TLS的SSL握手协议
http://blogs.msdn.com/b/saponsqlserver/archive/2013/05/10/analyzing-jdbc-connection-issues.aspx
https://docs.oracle.com/javase/8/docs/technotes/guides/security/jsse/JSSERefGuide.html#descPhase2
https://blogs.oracle.com/java-platform-group/entry/java_8_will_use_tls