为啥 Java 在最新的 JDK 更新后无法连接到 MySQL 5.7,应该如何解决? (ssl.SSLHandshakeException:没有适当的协议)

Posted

技术标签:

【中文标题】为啥 Java 在最新的 JDK 更新后无法连接到 MySQL 5.7,应该如何解决? (ssl.SSLHandshakeException:没有适当的协议)【英文标题】:Why can Java not connect to MySQL 5.7 after the latest JDK update and how should it be fixed? (ssl.SSLHandshakeException: No appropriate protocol)为什么 Java 在最新的 JDK 更新后无法连接到 MySQL 5.7,应该如何解决? (ssl.SSLHandshakeException:没有适当的协议) 【发布时间】:2021-07-23 17:44:04 【问题描述】:

在 2021 年 4 月对 JDK 的最新更新 (11.0.11+9-0ubuntu2~18.04) 中,已放弃对 TLSv1TLSv1.1 的支持,可能是因为自 2021 年 3 月起不再支持这些版本。 java.security 文件中的差异很明显:

之前:

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

之后:

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

这也在这个 SO 帖子中讨论:SSLHandShakeException No Appropriate Protocol。自更新 JDK 版本以来,最近几天在该线程中也弹出了更多答案。

JDK更新后,我们收到了错误

java.sql.SQLException: An attempt by a client to checkout a Connection has timed out.

c3p0

切换到hikari后我们得到了一个更有意义的错误:

ERROR [2021-04-29 16:21:16,426] com.zaxxer.hikari.pool.HikariPool: HikariPool-1 - Exception during pool initialization.
! javax.net.ssl.SSLHandshakeException: No appropriate protocol (protocol is disabled or cipher suites are inappropriate)

我们在 mysql 5.7.33-0ubuntu0.18.04.1 上运行。现在在我的理解中,如here 所述,MySQL 5.7 支持 TLSv1.2。此外,在运行SHOW VARIABLES LIKE 'tls_version'; 时,我们会得到TLSv1,TLSv1.1,TLSv1.2,这表明支持TLSv1.2

那么问题来了,为什么 JDK 和 MySQL 不同意使用 TLSv1.2,我们能做些什么来让它们与 TLSv1.2 通信?

注意:我不认为按照其他线程中的建议更改 java.security 文件是解决此问题的良好长期解决方案!

【问题讨论】:

现在的问题是什么?如何让 Java 再次接受 TLS 1.0 或如何让 MySQL 使用 TLS 1.2?这对我来说不是很清楚。 感谢您的提示,我重新表述了问题以使其更清楚。我认为目标应该是使用 TLSv1.2 作为前进的方向。 尝试将 'enabledTLSProtocols=TLSv1.2' 添加到 MySQL 连接字符串。这将为 java 的 mysql 连接强制执行 TLSV1.2 协议,您不必对 java.security 文件进行任何更改。 【参考方案1】:

正如@skelwa 已经评论的那样,您需要在连接字符串中添加enabledTLSProtocols=TLSv1.2 配置属性来解决您的问题。

Connector/J 的完整连接字符串可能如下所示:

jdbc:mysql://<host>:<port>/<dbname>?enabledTLSProtocols=TLSv1.2

对于r2dbc,您需要改用tlsVersion=TLSv1.2


剩下的问题是:

为什么 JDK 和 MySQL 不同意使用 TLSv1.2

虽然双方实际上都支持 TLSv1.2,但您遇到的问题是由 Connector/J 的默认行为引入的。出于兼容性原因,Connector/J 默认不启用 TLSv1.2 及更高版本。因此,必须明确启用它。

见下面note:

连接 MySQL 社区时适用于 Connector/J 8.0.18 及更早版本 使用 JDBC API 的服务器 5.6 和 5.7:由于兼容性问题 使用 yaSSL 编译的 MySQL 服务器,Connector/J 不启用 默认情况下与 TLSv1.2 及更高版本的连接。当连接到 限制连接以使用更高 TLS 版本的服务器, 通过设置 Connector/J 连接属性显式启用它们 enabledTLSProtocols(例如,设置 启用 TLSProtocols=TLSv1,TLSv1.1,TLSv1.2)。


警告:请注意,解决方案建议在jre/lib/security 内编辑jdk.tls.disabledAlgorithms 会带来安全风险 em> 到您的应用程序并更改其中的任何内容可能会产生严重影响! 这些协议被禁用是有原因的,人们不应该简单地从该列表中删除所有内容,甚至只是部分内容。


注意:如果您想从 JDK 获得更底层的见解来调试您的问题,您可以通过将以下配置传递给 java 命令来启用 ssl 调试日志:

-Djavax.net.debug=ssl,handshake 甚至 -Djavax.net.debug=all

在您的情况下,您会看到如下内容:

...(HANDSHAKE_FAILURE): Couldn't kickstart handshaking (
"throwable" : 
  javax.net.ssl.SSLHandshakeException: No appropriate protocol (protocol is disabled or cipher suites are inappropriate)
    at java.base/sun.security.ssl.HandshakeContext.<init>(HandshakeContext.java:170)
    at java.base/sun.security.ssl.ClientHandshakeContext.<init>(ClientHandshakeContext.java:98)
    ...

【讨论】:

【参考方案2】:

您可以执行以下步骤:

    打开 java.security 文件。 (它在 jre/lib/security 文件夹下) 找到以下行:jdk.tls.disabledAlgorithms 评论完整的行。

在此尝试运行后,它应该可以工作。

或者,如果您只想要特定的算法,请将其从禁用列表中删除。

您可以在配置中发送它并在 java.security 文件中对其进行注释

jdbc:mysql://<host>:<port>/<dbname>?enabledTLSProtocols=TLSv1.2

【讨论】:

注释掉“jdk.tls.disabledAlgorithms”是一个非常糟糕的建议......这有点像说:早上上班最快的方法是让你的车门不锁,钥匙在点火。选择性地删除项目只是稍微不那么糟糕...... @paulsm4 这是真实的,不鼓励在本地开发环境之外使用。 请记住:您在开发环境中所做的事情通常有一个讨厌的习惯,即不经意间泄漏到您的上游环境中。最佳建议:“只是说不”;)【参考方案3】:

我刚刚添加了 useSSL=false,它对我有用。

jdbc:mysql://&lt;host&gt;:&lt;port&gt;/&lt;dbname&gt;?useSSL=false

我有 JDK 8、MySQL 5.7 和 mysql-connector-java 库,版本为 5.1.38

当您只想在本地环境中快速执行(而不是 staging/test/prod)时,这很有用

【讨论】:

在 DMZ 中,没有加密而不是弱加密可能是可以的。在生产环境中通常不行。 问题标题:“应该如何解决?(ssl.SSLHandshakeException:没有合适的协议)”你的答案:禁用 SSL 这为我解决了。我只使用了这个选项,因为我在本地开发环境中。【参考方案4】:

我来这里是因为我有同样的问题,但不幸的是,

jdbc:mysql://host:port/dbname?enabledTLSProtocols=TLSv1.2

对我不起作用,因为我使用的是 r2dbc。稍微调试了一下,发现参数的名字一定是tlsVersion,所以你可以使用,例如:

spring.r2dbc.url=r2dbc:mysql://host:port/dname?tlsVersion=TLSv1.2

【讨论】:

以上是关于为啥 Java 在最新的 JDK 更新后无法连接到 MySQL 5.7,应该如何解决? (ssl.SSLHandshakeException:没有适当的协议)的主要内容,如果未能解决你的问题,请参考以下文章

为啥客户端无法连接到 Java Websocket 服务器?

为啥我可以将 java 应用程序连接到 MySQL DB 但无法执行查询? [复制]

荣耀v9连接到电脑后无法读取手机盘?为啥?

为啥我的 android 设备无法连接到 charles 代理,但在我连接到 Fiddler 代理时可以工作?

电脑Qt助手登陆后老是显示无法连接到服务器为啥

为啥 Ubuntu 14.04 上的 mod_jk 无法连接到 tomcat