Java8上的 SQL 服务器 JDBC 错误: 驱动程序无法使用传输层安全(SSL)加密建立到 SQL 服务器的安全连接

当使用 Microsoft JDBC Driver 版本连接到 SQLServer 数据库时,出现以下错误:

Sqlserver.jdbc.SQLServerException: 驱动程序无法使用传输层安全(SSL)加密建立到 SQL Server 的安全连接。错误: “ SQLServer 返回了一个不完整的响应。连接已经关闭。ClientConnectionId: 98d0b6f4-f3ca-4683-939e-7c0a0fca5931”。

我们最近将应用程序从 Java6和 Java7升级到 Java8。所有运行 Java 的系统都运行 SUSELinuxEnterpriseServer11(x86 _ 64) ,VERION = 11,PATCHLEVEL = 3。

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

  • 此错误发生的概率约为5% -10% 。此错误不会发生在每个连接上。
  • 问题只出现在 Java8上。我在 Java7上运行了相同的程序,这个问题是不可重复的。这与我们在升级之前的生产经验是一致的。在生产环境中,我们在运行 Java7时没有遇到任何问题。
  • 这个问题并没有出现在所有运行 Java8的 Linux 服务器上,它只出现在其中的一些服务器上。这让我很困惑,但是当我在不同 Linux 实例上的相同版本的 Linux JVM (1.8.0 _ 60,64位)上运行相同的测试程序时,问题不会出现在其中一个 Linux 实例上,而是会出现在其他 Linux 实例上。Linux 实例正在运行相同版本的 SUSE,并且它们处于相同的补丁级别。
  • 连接到 SQLServer2008和 SQLServer2014服务器/数据库时发生问题。
  • 无论我使用的是 SQL Server JDBC 驱动程序的4.0版本还是更新的4.1版本,都会出现这个问题。

与网络上的其他人相比,我在这方面观察到的独特之处在于,尽管这个问题只发生在 Java 8上,但我不能让这个问题发生在运行相同 Java 8 JVM 的看似相同的 Linux 服务器上。其他人也在 Java 的早期版本中看到了这个问题,但这并不是我们的经验。

任何投入,建议,或意见,你可能有感谢。

223677 次浏览

我在一个 Linux 实例上打开 Java8JVM 中的 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 (Java1.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

因此,当在 LinuxJVM 和 SQLServer 之间协商 TLSv1时,连接总是成功的。当协商 TLSv1.2时,会出现零星的连接故障。

(注意: Java7(1.7.0 _ 51)总是与 TLSv1协商,这就是为什么我们在使用 Java7 JVM 时从未出现问题的原因。)

我们仍然有一些悬而未决的问题:

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

2017年6月10日更新: 这篇来自微软的帖子描述了这个问题以及他们提出的解决方案。

资源:

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

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次,每次尝试之间暂停100毫秒。在4.1版本中,我每次都能够重现这个问题,尽管它只是偶尔发生。在4.2版本中,我无法重现这个问题。

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

  • Jar 需要 JRE 为5并支持 JDBC 3.0 API
  • Jar 需要 JRE 为6并支持 JDBC 4.0 API
  • Jar 需要 JRE 为7并支持 JDBC 4.1 API
  • Jar 要求 JRE 为8,并支持 JDBC 4.2 API

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

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

你也可以在 maven 上找到所有的预览版本,同时支持 JDK7和 JDK8。

我在 Windows Server 2012 R2上也遇到过这个问题,使用 JDBC 驱动程序4.0和4.1以及 Java 7。这个 微软文章将责任归咎于 DHE 密码套件,并建议如果您无法升级到 JDBC 驱动程序4.2,则禁用它们或降低它们的优先级

您的 url 应该如下所示,并添加 sql sqljdbc42.jar,这将解决您的问题

url = "jdbc:sqlserver://" +serverName + ":1433;DatabaseName=" + dbName + ";encrypt=true;trustServerCertificate=true;

在我的例子中,我有一个使用3DES _ EDE _ CBC 算法的 sql 服务器,在 jdk 1.8上默认禁用它,因此检查

/Library/Java/JavaVirtualMachines/opt topenjdk-8. jdk/content/Home/jre/lib/security/Java.security

然后把算法从:

禁用算法 = SSLv3,RC4,DES,MD5withRSA,DH keySize < 1024 ECkeySize < 224,3DES _ EDE _ CBC,anon,NULL

对我有用。

在我的例子中,问题是因为应用程序被设置为使用 spring-boot-ext-security-starter-credhub-credential,并且该设置存在一些问题。

因此,我从清单文件和 pom 中删除 credhub,并以不同的方式获取凭据; 然后错误就消失了。

如果连接字符串中的服务器名称与 SQLServer SSL 证书中的服务器名称不匹配,将发生以下错误: 驱动程序无法使用传输层安全(SSL)加密建立到 SQLServer 的安全连接。错误: “ java.security.cert。未能在传输层安全(SSL)初始化期间验证证书中的服务器名称。”

这帮助我解决了问题。正在 servername 中使用 localhost,最后在 jdbc 连接字符串中更改为 CN 能够连接的同一名称。

更多信息请参考: https://wiki.deepnetsecurity.com/pages/viewpage.action?pageId=1410867

像@2Aguy 写的那样,您可以更改 JVM 参数。 在我的示例中,我不能更改它,而是使用连接字符串“ 协议”参数,从而降低了到 TLSV1的连接。

连接字符串: SslProtocol = TLSv1; database = < DB NAME >

最近,我在运行 RedHat7的客户端服务器上进行了一次 yum 更新,之后遇到了这个问题。由于上面的帖子没有帮助我解决我的问题,我发布了这个答案。

发行:- RedHat 中的 Yum 更新会自动重新安装 OpenJDK,我的应用程序使用 oracleJDK。 验证默认 JDK:

Java 版本 切换默认版本:

Update-options —— config java

有2个程序提供’java’。

选择命令

  • 1 java-1.8.0-openjdk. x86 _ 64(/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.302. b08-0. el7 _ 9. x86 _ 64/jre/bin/java)
  • 2/usr/java/jdk1.8.0 _ 181-amd64/jre/bin/java

输入以保持当前选择[ + ]或类型选择编号:

在您想要的版本旁边输入数字,然后按回车键。

我已经在我的本地环境中解决了这个问题,它主要包括以下两个步骤。

  1. 如果您的项目是由 maven 构建的,则切换 JDBC Driver 依赖项,如下所示:
<dependency>
<groupId>net.sourceforge.jtds</groupId>
<artifactId>jtds</artifactId>
<version>1.3.1</version>
</dependency>

2. 另外替换驱动程序类名称:

ru.yandex.clickhouse.ClickHouseDriver

3. 像下面这样修改 JDBC URL:

jdbc:jtds:sqlserver://xxxx:1433;databaseName=xxx

感谢 Sunil Kumar 和 Joce。 我使用了 jar 和以下语法:

字符串 myDriver = “ com.microsoft.jdbc.sqlserver.SQLServerDriver”;

字符串 myURL = “ jdbc: sqlserver://DB _ ipaddress DB _ instance; database aseName = DB _ name; user = myusername; password = mypass;

Connection con = DriverManager.getConnection (myURL) ;

我也面临着同样的问题,当创建连接到数据库与下面的连接字符串。

DriverManager.getConnection("jdbc:sqlserver://" + host + ":" + port + ";databaseName=" + dbName + ";user=" + userName + ";password=" + password);

如下所示更新连接字符串之后,它工作了。

Connection con = DriverManager.getConnection("jdbc:sqlserver://" + host + ":" + port + ";databaseName=" + dbName + ";encrypt=true;trustServerCertificate=true;user=" + userName + ";password=" + password);

在这里,我在数据库名称后面添加了 encrypt=true;trustServerCertificate=true

当我用 mssql-jdbc-8.4.1 jar 更改 sqljdbc-4.2.0 jar 时,问题得到了解决

<dependency>
<groupId>com.microsoft.sqlserver</groupId>
<artifactId>mssql-jdbc</artifactId>
<version>8.4.1.jre8</version>
</dependency>

如果有人来这里寻找从 PhpStorm 连接到数据库的解决方案,只需在 URL 中的端口后面添加以下内容:

;encrypt=true;trustServerCertificate=true;

enter image description here

我从这条评论中得到了解决方案: Java8上的 SQL 服务器 JDBC 错误: 驱动程序无法使用传输层安全(SSL)加密建立到 SQL 服务器的安全连接

非常感谢! 可以确认它的工作在 PhpStorm 2022.1。

  • 应用性能:
    spring.datasource.url=jdbc:sqlserver://YourServerName:1433;database=<YourDatabaseName>;encrypt=true;trustServerCertificate=true;
spring.datasource.username=root
spring.datasource.password=1234
spring.datasource.driverClassName=com.microsoft.sqlserver.jdbc.SQLServerDriver
spring.jpa.show-sql=true
spring.jpa.hibernate.dialect=org.hibernate.dialect.SQLServer2012Dialect
spring.jpa.properties.hibernate.globally_quoted_identifiers=true
spring.jpa.hibernate.ddl-auto = create-drop

对我来说,我添加到 Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver"); 在两条线之间

DriverManager.registerDriver(new com.microsoft.sqlserver.jdbc.SQLServerDriver());

Connection con = DriverManager.getConnection(connectionUrl);

所以连接的最终代码是:

    DriverManager.registerDriver(new com.microsoft.sqlserver.jdbc.SQLServerDriver());
Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
Connection con = DriverManager.getConnection(connectionUrl);

使用 openjdk 11,可以向连接 URL 添加以下属性,以强制使用 SSL

;integratedSecurity=false;encrypt=false;trustServerCertificate=true;

;encrypt=true;trustServerCertificate=true添加到 connection.url.Example

Jdbc: sqlserver://localhost SQLEXPRESS: 1433; database aseName = testdb; crypt = true; trust ServerSecurities = true

检查 pom 文件中兼容编译器版本 java 和 mssql-jdbc 版本 maven

<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>

还有

<dependency>
<groupId>com.microsoft.sqlserver</groupId>
<artifactId>mssql-jdbc</artifactId>
<version>11.2.1.jre8</version>
</dependency>

编译器1.8和 jre8

如果你来这里是为了 Iigny8.1 + 错误,并且你在你的本地主机上使用 MSSQL 表达式,在你的“连接 URL”上输入: jdbc: sqlserver://localhost SQLEXPRESS; URL 设置

有效的联系