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

82 人关注

当我使用Microsoft JDBC驱动版本连接到SQL Server数据库时,得到了以下错误。

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

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程序收集到的事实,这个程序只是按顺序打开和关闭了1000个数据库连接。

  • Connections are dropped with this error about 5%-10% of the time. The error DOES NOT occur on every connection.
  • The problem ONLY occurs with Java 8. I ran the same program on Java 7 and the problem is not reproducible. This is consistent with our experience in production prior to upgrading. We've had zero problems running under Java 7 in production.
  • The problem DOES NOT occur on all of our Linux servers running Java 8, it only occurs on some of them. This is perplexing to me, but when I run the same test program on the same version of the Linux JVM (1.8.0_60, 64 bit) on different Linux instances, the problem does not occur on one of the Linux instances, but the problem does occur on others. The Linux instances are running the same version of SUSE and they are at the same patch level.
  • The problem occurs when connecting to BOTH SQL Server 2008 and SQL Server 2014 servers/databases.
  • The problem occurs regardless if I am using the 4.0 version of the SQL Server JDBC driver or the newer 4.1 version of the driver.
  • 与网络上的其他人相比,我的观察是独特的,虽然这个问题只发生在Java 8上,但我不能让这个问题发生在运行相同的Java 8 JVM的一个看似相同的Linux服务器上。 其他的人在早期版本的Java上也看到过这个问题,但这不是我们的经验。

    感谢你们的任何意见、建议或观察。

    java
    sql-server
    linux
    ssl
    jdbc
    2Aguy
    2Aguy
    发布于 2015-09-25
    21 个回答
    Sunil Kumar
    Sunil Kumar
    发布于 2022-12-06
    已采纳
    0 人赞同

    你的网址应该是下面这样的,然后加上sql sqljdbc42.jar。这将解决你的问题

    url = "jdbc:sqlserver://" +serverName + ":1433;DatabaseName=" + dbName + ";encrypt=true;trustServerCertificate=true;
        
    你好,来自2022年,这在JetBrains DataGrip中对我有效。我的URL格式是这样的 jdbc:sqlserver://HOST_GOES_HERE;encrypt=true;trustServerCertificate=true。
    祝福你!这有助于解决Azure SQL Spark connector for Scala的一个问题。谢谢
    This is the source of the solution: learn.microsoft.com/en-us/sql/connect/jdbc/...
    这个方案似乎不适合生产,上面的链接明确指出:Microsoft JDBC Driver for SQL Server不会验证SQL Server TLS证书。这个设置在测试环境中允许连接是很常见的,...
    对于IntelliJ/DataGrip的人来说,如果添加encrypt=true;trustServerCertificate=true;对你不起作用,请确保'encrypt'和'trustServerCertificate'在'高级'标签中没有设置为false。
    2Aguy
    2Aguy
    发布于 2022-12-06
    0 人赞同

    我在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

    嗨,马克。 不,我没有试过更新的版本,因为我已经不在该组织工作了。
    RepentAndBelieveTheGospel
    RepentAndBelieveTheGospel
    发布于 2022-12-06
    0 人赞同

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

    ;encrypt=true;trustServerCertificate=true;
    

    I got the solution from this comment: Java 8上的SQL Server JDBC错误:驱动程序无法通过使用安全套接字层(SSL)加密与SQL Server建立安全连接

    非常感谢您!可以确认它在PhpStorm 2022.1.1上工作。

    从java 11到sql server数据库的连接也能正常工作。
    Juan hernan jaime arias
    Juan hernan jaime arias
    发布于 2022-12-06
    0 人赞同

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

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

    bbhagat
    bbhagat
    发布于 2022-12-06
    0 人赞同

    在升级SQL JDBC驱动之前,请先检查兼容性。

  • Sqljdbc.jar requires a JRE of 5 and supports the JDBC 3.0 API
  • Sqljdbc4.jar requires a JRE of 6 and supports the JDBC 4.0 API
  • Sqljdbc41.jar requires a JRE of 7 and supports the JDBC 4.1 API
  • Sqljdbc42.jar requires a JRE of 8 and supports the JDBC 4.2 API
  • Source: https://www.microsoft.com/en-us/download/details.aspx?id=11774

    将jvm版本升级到Java 1.8,这时我才开始看到这个问题。在我的情况下,升级驱动程序并没有帮助。
    Tore Refsnes
    Tore Refsnes
    发布于 2022-12-06
    0 人赞同

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

    That is good information. However, the version 4.2 driver does not work under Java 7 (at least last I checked about a month ago). Microsoft claims it is compatible with Java 6, 7 & 8, but they apparently compiled the driver code with a Java 8 compiler. In our mixed environment of running Java 7 and Java 8, we are unable to run the 4.2 driver due to this problem. See social.msdn.microsoft.com/Forums/en-US/...
    Thuan Tran
    Thuan Tran
    发布于 2022-12-06
    0 人赞同

    在connection.url中添加 ;encrypt=true;trustServerCertificate=true 。例子

    jdbc:sqlserver://localhost\SQLEXPRESS:1433;databaseName=testdb;encrypt=true;trustServerCertificate=true

    并检查pom文件中的兼容编译器版本java与mssql-jdbc的版本jre。像这样

    <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

    Ruhul Hussain
    Ruhul Hussain
    发布于 2022-12-06
    0 人赞同

    当我把sqljdbc-4.2.0 jar换成mssql-jdbc-8.4.1 jar时,问题得到解决。

    <dependency>
          <groupId>com.microsoft.sqlserver</groupId>
          <artifactId>mssql-jdbc</artifactId>
          <version>8.4.1.jre8</version>
    </dependency>
        
    我把我的POM从spring的自动生成的代码改成了上面发布的代码,经过长时间的搜索,它真的解决了这个问题。我不知道怎么解决的,也不知道为什么,但它确实解决了。+1
    cabaji99
    cabaji99
    发布于 2022-12-06
    0 人赞同

    在我的例子中,我有一个使用3DES_EDE_CBC算法的sql服务器,这在jdk 1.8上是默认禁用的,所以检查一下

    /Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home/jre/lib/security/java.security

    并消除了从的算法。

    jdk.tls.disabledAlgorithms=SSLv3, RC4, DES, MD5withRSA, DH keySize < 1024, \ EC keySize < 224, 3DES_EDE_CBC, anon, NULL

    Worked for me.

    这在我的情况下也解决了问题。我尝试了这个主题中的一些其他提示,但没有任何效果。我想,这取决于服务器使用的加密算法。在我的例子中,它是一个相当老的SQL Server 2005实例。
    0 人赞同

    像@2Aguy写的那样,你可以改变JVM的参数。 在我的例子中,我无法改变它,而使用连接字符串" sslProtocol "参数,将连接降低到TLSV1。

    Connection String: jdbc:sqlserver://<HOST>:<PORT>;encrypt=true;trustServerCertificate=true;sslProtocol=TLSv1;database=<DB NAME>

    我错过了这个机会,所以没有尝试,但我发现 encrypt=false 根本不起作用,所以需要一种方法来强制执行TLSv1。我采用了一种比较强硬的方式,在/usr/lib/jvm/java-11-openjdk-amd64/conf/security/java.security中禁用了TLSv1。这个选项可能会更好
    S3D
    S3D
    发布于 2022-12-06
    0 人赞同
  • application.properties:
  •     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
        
    Nikhil
    Nikhil
    发布于 2022-12-06
    0 人赞同

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

    Also you can find all preview versions on maven too. Supporting both JDK7 & JDK 8.

    coolhand
    coolhand
    发布于 2022-12-06
    0 人赞同

    I've also hit this issue on Windows Server 2012 R2, using JDBC driver 4.0 & 4.1 with Java 7. This 微软文章 将责任归咎于DHE密码套件,并建议如果你不能升级到JDBC驱动4.2,则禁用它们或降低其优先级。

    eby
    eby
    发布于 2022-12-06
    0 人赞同

    在我的案例中,问题是由于应用程序被设置为使用spring-boot-ext-security-starter-credhub-credential,而该设置存在一些问题。

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

    yolob 21
    yolob 21
    发布于 2022-12-06
    0 人赞同

    如果连接字符串中的服务器名称与SQL Server SSL证书中的服务器名称不一致,将发出以下错误。驱动程序无法通过使用安全套接字层(SSL)加密建立与SQL Server的安全连接。错误。"java.security.cert.CertificateException:在安全套接字层(SSL)初始化期间,验证证书中的服务器名称失败。"

    这帮助我解决了这个问题。在服务器名称中使用了localhost,最后在jdbc连接字符串中改成与CN相同的名称,才得以连接。

    Refer: https://wiki.deepnetsecurity.com/pages/viewpage.action?pageId=1410867 for more info

    Leo
    Leo
    发布于 2022-12-06
    0 人赞同

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

  • Switch the JDBC Driver dependency like below if your project is built by maven:
  • <dependency>
      <groupId>net.sourceforge.jtds</groupId>
      <artifactId>jtds</artifactId>
      <version>1.3.1</version>
    </dependency>
    

    2.另外更换驱动类名称。

    ru.yandex.clickhouse.ClickHouseDriver
    

    3.Modify the JDBC URL like this:

    jdbc:jtds:sqlserver://xxxx:1433;databaseName=xxx
        
    Application Automation
    Application Automation
    发布于 2022-12-06
    0 人赞同

    感谢@Sunil Kumar和@Joce。 我使用了jar和下面的语法。

    String myDriver = "com.microsoft.jdbc.sqlserver.SQLServerDriver";

    String myURL = "jdbc:sqlserver://DB_ipaddress/DB_instance;databaseName=DB_name;user=myusername; password=mypass;encrypt=true;trustServerCertificate=true;"; / ==> here the semicolon will be twice like shown /

    连接 con = DriverManager.getConnection(myURL)。

    按照目前的写法,你的答案是不清楚的。请 edit 添加额外的细节,以帮助其他人了解这如何解决所问的问题。你可以找到更多关于如何写好答案的信息 in the help center .
    Martin Karari
    Martin Karari
    发布于 2022-12-06
    0 人赞同

    我最近在客户运行RedHat 7的服务器中进行yum更新后遇到了这个问题。 由于上面的主题没有帮助我解决这个问题,所以我发布了这个答案。

    RedHat的Yum更新自动重新安装OpenJDK,我的应用程序使用oracle JDK。 验证默认的JDK。

    java版本 切换默认版本。

    更新-替代方案-配置java

    有2个程序提供 "java"。

    Selection Command

  • 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
  • Enter to keep the current selection[+], or type selection number:

    在你想要的相应版本旁边输入数字,然后点击回车。

    Sandeep Patel
    Sandeep Patel
    发布于 2022-12-06
    0 人赞同

    在用下面的连接字符串创建与数据库的连接时,我也遇到了同样的问题。

    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

    stoneshishang
    stoneshishang
    发布于 2022-12-06
    0 人赞同

    对我来说,我添加了 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);