【问题标题】:SQL Server JDBC Error on Java 8: The driver could not establish a secure connection to SQL Server by using Secure Sockets Layer (SSL) encryptionJava 8 上的 SQL Server JDBC 错误:驱动程序无法使用安全套接字层 (SSL) 加密建立与 SQL Server 的安全连接
【发布时间】:2015-12-22 08:06:29
【问题描述】:

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

com.microsoft.sqlserver.jdbc.SQLServerException:驱动程序无法使用安全套接字层 (SSL) 加密建立与 SQL Server 的安全连接。错误:“SQL Server 返回不完整的响应。连接已关闭。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 程序收集的事实,该程序只是按顺序打开和关闭 1,000 个数据库连接。

  • 大约 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 上看到过这个问题,但我们的经验并非如此。

感谢您提出的任何意见、建议或意见。

【问题讨论】:

    标签: java sql-server linux ssl jdbc


    【解决方案1】:

    我在一个重现问题的 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. 为什么从 2 个不同的 Linux 服务器运行的同一个 Java 8 JVM 总是协商 TLSv1,但是当从另一个 Linux 服务器连接时,它总是协商 TLSv1.2。
    2. 还有为什么 TLSv1.2 协商连接在该服务器上大部分时间(但不是全部时间)都成功?

    2017 年 6 月 10 日更新: This posting from Microsoft 描述了问题及其建议的解决方案。

    资源:

    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 Unlimited Strength Policy and SSL Handshake over 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/entry/java_8_will_use_tls

    【讨论】:

    • 嗨,马克。不,我没有尝试过更新的版本,因为我不再在那个组织工作。
    【解决方案2】:

    在升级 SQL JDBC Driver 之前,请先检查兼容性:

    • 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

    【讨论】:

    • 将 jvm 版本升级到 Java 1.8,这是我开始看到问题的时候。升级驱动程序对我没有帮助。
    【解决方案3】:

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

    【讨论】:

    • 这是很好的信息。但是,4.2 版驱动程序在 Java 7 下无法运行(至少我在一个月前检查过)。微软声称它与 Java 6、7 和 8 兼容,但他们显然使用 Java 8 编译器编译了驱动程序代码。在我们运行 Java 7 和 Java 8 的混合环境中,由于这个问题,我们无法运行 4.2 驱动程序。见social.msdn.microsoft.com/Forums/en-US/…
    【解决方案4】:

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

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

    【讨论】:

      【解决方案5】:

      在我的情况下,我有一个使用 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

      为我工作。

      【讨论】:

      • 这也解决了我的问题。我在这个线程中尝试了其他一些提示,但没有任何效果。我想,这取决于服务器使用的加密算法。在我的例子中,它是一个相当老的 SQL Server 2005 实例。
      【解决方案6】:

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

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

      【讨论】:

        【解决方案7】:

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

        【讨论】:

          【解决方案8】:

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

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

          【讨论】:

            【解决方案9】:

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

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

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

            【讨论】:

              【解决方案10】:

              就像@2Aguy 写的,你可以改变JVM 参数。 在我的情况下,我无法更改它,并使用连接字符串“sslProtocol”参数,降低与 TLSV1 的连接。

              连接字符串: jdbc:sqlserver://:;encrypt=true;trustServerCertificate=true;sslProtocol=TLSv1;database=

              【讨论】:

                【解决方案11】:

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

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

                java版本 切换默认版本:

                update-alternatives --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

                Enter 以保留当前选择[+],或输入选择编号:

                在您希望我们提供的相应版本旁边输入数字,然后按 Enter。

                【讨论】:

                  猜你喜欢
                  • 2019-12-26
                  • 2019-09-25
                  • 1970-01-01
                  • 2015-11-03
                  • 1970-01-01
                  • 2021-07-08
                  • 2022-01-19
                  • 2022-06-17
                  • 2017-02-02
                  相关资源
                  最近更新 更多