Java 8上的SQL Server JDBC错误:驱动程序无法使用安全套接字层(SSL)encryption与SQL Serverbuild立安全连接

使用Microsoft JDBC驱动程序的版本连接到SQL Server数据库时,出现以下错误:

com.microsoft.sqlserver.jdbc.SQLServerException:驱动程序无法使用安全套接字层(SSL)encryption与SQL Serverbuild立安全连接。 错误:“SQL Server返回的响应不完整,连接已closures,ClientConnectionId:98d0b6f4-f3ca-4683-939e-7c0a0fca5931”。

我们最近将应用程序从Java 6和Java 7升级到了Java 8.所有运行Java的系统都运行SUSE Linux Enterprise Server 11(x86_64),VERSION = 11,PATCHLEVEL = 3。

下面是我用一个Java程序收集到的事实,它简单地按顺序打开和closures了1,000个数据库连接。

  • 大约5%-10%的时间连接将被丢弃。 错误不会发生在每个连接上。
  • 这个问题只发生在Java 8中。我在Java 7上运行了相同的程序,问题是不可重现的。 这与我们在升级之前的生产经验是一致的。 我们在生产环境下运行Java 7时没有遇到任何问题。
  • 运行Java 8的所有Linux服务器上都不会发生这个问题,它只发生在其中一些Linux服务器上。 这对我来说是令人困惑的,但是当我在不同的Linux实例上的相同版本的Linux JVM(1.8.0_60,64位)上运行相同的testing程序时,问题不会发生在其中一个Linux实例上,确实发生在别人身上。 Linux实例正在运行相同版本的SUSE,并且处于相同的修补程序级别。
  • 连接到SQL Server 2008和SQL Server 2014服务器/数据库时都会发生此问题。
  • 无论我使用SQL Server JDBC驱动程序的4.0版本还是驱动程序的更新版本4.1,都会出现问题。

与networking上的其他人相比,我的观察结果是独一无二的,即使问题只发生在Java 8上,但是在运行相同的Java 8 JVM的看似完全相同的Linux服务器之一上,却无法解决问题。 其他人也在早期版本的Java上看到过这个问题,但那不是我们的经验。

您可能有任何意见,build议或意见。

Solutions Collecting From Web of "Java 8上的SQL Server JDBC错误:驱动程序无法使用安全套接字层(SSL)encryption与SQL Serverbuild立安全连接"

我在一个Linux实例的Java 8 JVM上打开了SSL日志记录,这个实例再现了这个问题。 使用-Djavax.net.debug=ssl:handshake:verbose打开SSL日志记录。 这揭示了一些有用的信息。

我们在生产中使用的解决方法已经证明可以为我们工作,即在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从未发生过这个问题的原因。)

我们仍然存在的开放性问题是:

  1. 为什么从两台不同的Linux服务器上运行相同的Java 8 JVM将总是协商TLSv1,但是当从另一台Linux服务器连接时,它总是与TLSv1.2协商。
  2. 而且为什么TLSv1.2协商的连接在这台服务器上最成功,但不是全部?

更新6/10/2017: 微软的这篇文章介绍了这个问题以及他们提出的解决方案。

资源:

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-套接字层-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

这似乎已在MS SQL JDBC驱动程序的版本4.2中得到解决。 我创建了一个连接到服务器1000次的程序,在每次尝试之间暂停100ms。 在4.1版本中,我能够每次重现这个问题,尽管它只是虚假地发生了。 4.2版本无法重现问题。

在升级SQL JDBC驱动程序之前,请首先检查兼容性:

  • Sqljdbc.jar需要JRE为5,并支持JDBC 3.0 API
  • Sqljdbc4.jar需要JRE为6,并支持JDBC 4.0 API
  • Sqljdbc41.jar需要JRE为7,并支持JDBC 4.1 API
  • Sqljdbc42.jar需要JRE为8,并支持JDBC 4.2 API

资料来源: https : //www.microsoft.com/en-us/download/details.aspx?id=11774

我也在Windows server 2012 R2上使用JDBC驱动程序4.0&4.1和Java 7时遇到了这个问题。这篇Microsoft文章将矛头指向了DHE密码套件,并建议禁用它们,或者如果您无法升级到JDBC驱动程序4.2

微软最近开源了他们的驱动程序。 可以在GitHub上看到mssql-jdbc驱动程序的活动。 我猜最新的预览版本是6.1.5。

你也可以在maven上找到所有的预览版本。 支持JDK7和JDK 8。