Java 8 上的 SQL Server JDBC 错误:驱动程序无法使用安全套接字层 (SSL) 加密与 SQL Server 建立安全连接 [英] SQL Server JDBC Error on Java 8: The driver could not establish a secure connection to SQL Server by using Secure Sockets Layer (SSL) encryption

查看:139
本文介绍了Java 8 上的 SQL Server JDBC 错误:驱动程序无法使用安全套接字层 (SSL) 加密与 SQL Server 建立安全连接的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am getting the following error when connecting to a SQL Server database using version the Microsoft JDBC Driver:

com.microsoft.sqlserver.jdbc.SQLServerException:驱动程序无法通过使用安全套接字层 (SSL) 加密建立到 SQL Server 的安全连接.错误:SQL Server 返回的响应不完整.连接已关闭.ClientConnectionId:98d0b6f4-f3ca-4683-939e-7c0a0fca5931".

com.microsoft.sqlserver.jdbc.SQLServerException: The driver could not establish a secure connection to SQL Server by using Secure Sockets Layer (SSL) encryption. Error: "SQL Server returned an incomplete response. The connection has been closed. ClientConnectionId:98d0b6f4-f3ca-4683-939e-7c0a0fca5931".

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

We recently upgraded our applications from Java 6 & Java 7 to Java 8. All systems running Java are running SUSE Linux Enterprise Server 11 (x86_64), VERSION = 11, PATCHLEVEL = 3.

以下是我使用我编写的 Java 程序收集的事实,该程序只是按顺序打开和关闭 1,000 个数据库连接.

Here are the facts I have collected with a Java program that I wrote which simply sequentially opens and closes 1,000 database connections.

  • 大约有 5%-10% 的时间会因为此错误而断开连接.该错误不会在每个连接上发生.
  • 该问题仅在 Java 8 中出现.我在 Java 7 上运行了相同的程序,但该问题无法重现.这与我们升级前的生产经验是一致的.我们在生产环境中在 Java 7 下运行的问题为零.
  • 该问题不会出现在我们所有运行 Java 8 的 Linux 服务器上,只会出现在其中一些服务器上.这让我很困惑,但是当我在不同 Linux 实例上的相同版本的 Linux JVM(1.8.0_60,64 位)上运行相同的测试程序时,问题并没有出现在其中一个 Linux 实例上,而是出现了问题确实发生在其他人身上.Linux 实例运行相同版本的 SUSE,并且它们处于相同的补丁级别.
  • 连接到 SQL Server 2008 和 SQL Server 2014 服务器/数据库时出现问题.
  • 无论我使用的是 4.0 版的 SQL Server JDBC 驱动程序还是更新的 4.1 版驱动程序,都会出现问题.

与网络上的其他人相比,让我的观察与众不同的是,虽然问题仅发生在 Java 8 上,但我无法在运行相同 Java 的看似相同的 Linux 服务器之一上发生问题8 虚拟机.其他人也曾在 Java 的早期版本中看到过这个问题,但我们没有经历过.

The thing that makes my observations unique on this compared to others on the web is that although the problem happens ONLY on Java 8, I cannot get the problem to occur on one of the seemingly identical Linux servers that is running the same Java 8 JVM. Other folks have seen this problem on earlier versions of Java as well, but that has not been our experience.

感谢您提供的任何意见、建议或意见.

Any input, suggestions, or observations you may have are appreciated.

推荐答案

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

I turned on SSL logging in the Java 8 JVM on a Linux instance which reproduces the problem. SSL logging is turned on using -Djavax.net.debug=ssl:handshake:verbose. This revealed some useful information.

我们在生产中使用并已证明对我们有效的解决方法是在 JVM 上设置此参数:

The workaround that we are using in production and has proven to work for us is to set this parameter on the JVM:

 -Djdk.tls.client.protocols=TLSv1

如果您想了解更多详情,请继续阅读.

If you want more details, please read on.

在可以重现问题的服务器上(同样,只有 5-10% 的时间),我观察到以下情况:

On a server where the problem can be reproduced (again, only 5-10% of the time), I observed the following:

*** 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% 的时间会失败.

Notice that TLSv1.2 is selected by the database server and used in this exchange. I've observed that, when connections fail from the problematic linux service, TLSv1.2 is ALWAYS the level which was selected. However, connections do not ALWAYS fail when TLSv1.2 is used. They only fail 5-10% of the time.

现在这是来自没有问题的服务器的交换.其他一切都是平等的.即连接到同一个数据库,同一个JVM版本(Java 1.8.0_60),同一个JDBC驱动等等.注意,这里,TLSv1是由数据库服务器选择的,而不是TLSv1.2就像在故障服务器的情况下一样.

Now here is an exchange from a server that does NOT have the problem. Everything else is equal. I.e., connecting to the same database, same version of the JVM (Java 1.8.0_60), same JDBC driver, etc. Notice that, here, TLSv1 is selected by the database server instead of TLSv1.2 as in the faulty server's case.

*** 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 时,我们会遇到偶发的连接失败.

So, when TLSv1 is negotiated between the Linux JVM and the SQL Server, connections are ALWAYS successful. When TLSv1.2 is negotiated, we get sporadic connection failures.

(注意:Java 7 (1.7.0_51) 总是协商 TLSv1,这就是为什么我们在使用 Java 7 JVM 时从未出现问题.)

(Note: Java 7 (1.7.0_51) always negotiates TLSv1, which is why the problem never occurred for us with a Java 7 JVM.)

我们仍然有待解决的问题是:

The open questions we still have are:

  1. 为什么从 2 个不同的 Linux 服务器运行的同一个 Java 8 JVM 将始终协商 TLSv1,但从另一个 Linux 服务器连接时,它始终协商 TLSv1.2.
  2. 还有为什么 TLSv1.2 协商连接在该服务器上大部分时间(但不是全部)成功?

2017 年 6 月 10 日更新:这篇来自 Microsoft 的帖子 描述了问题和他们提出的解决方案.

Update 6/10/2017: This posting from Microsoft describes the problem and their proposed solution.

资源:

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-无法建立安全连接到 sql-server-by-using-secure-sockets-layer-ssl-encryption.aspx

Java 8,JCE 无限强度政策和 SSL 握手通过 TLS

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/条目/java_8_will_use_tls

这篇关于Java 8 上的 SQL Server JDBC 错误:驱动程序无法使用安全套接字层 (SSL) 加密与 SQL Server 建立安全连接的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆