SSL握手异常:没有适当的协议。

61

我最近给我的网站添加了SSL,现在可以通过https访问。现在当我的Java应用程序尝试使用缓冲读取器从我的网站进行请求和读取时,它会生成这个堆栈跟踪。

我没有使用自签名证书,该证书来自Namecheap,使用COMODO SSL作为CA来签署我的证书。我正在使用Java 8。

javax.net.ssl.SSLHandshakeException: No appropriate protocol (protocol is disabled or cipher suites are inappropriate)
at sun.security.ssl.Handshaker.activate(Handshaker.java:503)
at sun.security.ssl.SSLSocketImpl.kickstartHandshake(SSLSocketImpl.java:1482)
at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1351)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1403)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1387)
at sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:559)

我的代码非常基础,只是尝试使用缓冲读取器读取我的网站上的页面

 private void populateDataList() {
    try {
        URL url = new URL("https://myURL.com/Data/Data.txt");
        URLConnection con = url.openConnection();
        con.setRequestProperty("Connection", "close");
        con.setDoInput(true);
        con.setUseCaches(false);

        BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));
        String line;
        int i = 0;
        while((line = in.readLine()) != null) {
            this.url.add(i, line);
            i++;
        }
    }   catch (Exception e) {
        e.printStackTrace();
    }

}

我尝试将我的SSL证书添加到JVM的密钥库中,甚至尝试使用此代码接受每个证书(我知道这违背了SSL的目的)

 private void trustCertificate() {
    TrustManager[] trustAllCerts = new TrustManager[] {
            new X509TrustManager() {
                public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                    return new X509Certificate[0];
                }
                public void checkClientTrusted(
                        java.security.cert.X509Certificate[] certs, String authType) {
                }
                public void checkServerTrusted(
                        java.security.cert.X509Certificate[] certs, String authType) {
                }
            }
    };
    try {
        SSLContext sc = SSLContext.getInstance("TLS");
        sc.init(null, trustAllCerts, new java.security.SecureRandom());
        HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
    } catch (GeneralSecurityException e) {
    }
    try {
        URL url = new URL("https://myURL.com/index.php");
        URLConnection con = url.openConnection();
        BufferedReader br = new BufferedReader(new InputStreamReader(con.getInputStream()));
        String line;
        while((line = br.readLine()) != null) {
            System.out.println(line);
        }

    } catch (Exception e) {

    }
}

我被难住了,非常感谢任何帮助!


你可能需要提供更多的细节。使用自签名证书吗?Java 版本是什么? - D-Klotz
我没有使用自签名证书,我的证书来自Namecheap,他们使用COMODO SSL作为CA来签署我的证书。我正在使用Java 8。 - ChrisianBartram
1
(1) 证书与此错误无关。(2) 您是否使用Sun/Oracle版本的Java 8,如果是,是哪个更新版本,还是其他Java?在JRE中是否进行了任何配置更改,特别是在文件$JRE/lib/security/java.security中?(3) 是否设置了任何涉及https的系统属性,特别是https.protocols?(4) 尝试使用sysprop javax.net.debug=ssl运行并发布结果,但如果您的信任存储有大量证书(默认情况下),则可以将该部分缩小到最小。 - dave_thompson_085
1
请指明您正在使用的Java版本。如果您添加了“-Djavax.net.debug=ssl:handshake:verbose”,它将允许您更详细地检查握手问题。 - John Yeary
13个回答

68
$JRE/lib/security/java.security中:
jdk.tls.disabledAlgorithms=SSLv3, TLSv1, RC4, DES, MD5withRSA, DH keySize < 1024, \
EC keySize < 224, 3DES_EDE_CBC, anon, NULL

我注释掉这行代码后,一切都正常了。显然,在jre1.8.0_181之后,这行代码是启用的。

我的Java版本是“1.8.0_201”。


6
我有不同的理解,这是禁用弱算法,你应该使用更好的算法。 - Betlista
9
对于Java 11及以上版本,该文件位于"$JAVA_HOME/conf/security"目录下。 - MinionAttack
6
如果有人发现由于 MySQL JDBC 驱动程序错误而导致问题,您只需要从列表中排除 TLSv1 和 TLSv1.1 即可解决。 - Oleg Kurbatov
19
@OlegKurbatov 谢谢!这有助于我解决问题。根据 https://www.oracle.com/java/technologies/javase/8u291-relnotes.html,TLS 1.0 和 1.1 在上周发布的 JDK8-u292 中已被禁用。你可以尝试在连接字符串中明确使用 enabledTLSProtocols=TLSv1.2 吗?希望这也能帮到你,因为 TLSv1.0 和 TLSv1.1 已经过时。 - zhongxiao37
3
你能解释一下为什么突然出现了这个错误吗?我的解决方案在今天使用Tomcat9和JDK8时工作得很好,我没有做任何更改,但突然间开始出现这个错误。不知道为什么?请帮忙。 - Ankur Raiyani
显示剩余4条评论

39

我在使用Ubuntu 18.04的时候,也遇到了Java8更新版本1.8.0.229的这个问题。

我更改了以下部分:

# Example:
#   jdk.tls.disabledAlgorithms=MD5, SSLv3, DSA, RSA keySize < 2048
#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

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

我从文件 /etc/java-8-openjdk/security/java.securityjdk.tls.disabledAlgorithms 列表中移除了 TLSv1TLSv1.1

检查后:

Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 28
Server version: 5.7.33-0ubuntu0.18.04.1 (Ubuntu)

Copyright (c) 2000, 2021, Oracle and/or its affiliates.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> SHOW GLOBAL VARIABLES LIKE 'tls_version';
+---------------+-----------------------+
| Variable_name | Value                 |
+---------------+-----------------------+
| tls_version   | TLSv1,TLSv1.1,TLSv1.2 |
+---------------+-----------------------+
1 row in set (0.00 sec)

mysql> exit

4
最新的openjdk 11版本(11.0.11+9-0ubuntu2~18.04)也存在这个问题。我不明白的是,为什么Java和MySQL不能就使用TLSv1.2达成一致,因为双方似乎都支持它?此外,仅仅打补丁修改“java.security”文件并不是长期解决方案,那么这个问题的前进方向是什么? - bersling
同样使用openjdk 11.0.11+9,出现了相同的问题。在尝试解决问题时,不得不回滚到使用旧的openjdk镜像。 - Emily
1
非常感谢您发布这篇文章。它解决了我的问题!我不明白的是为什么它一开始就成为了一个问题 - 这个程序在树莓派上运行了一年左右,没有任何更改。 - David Powell
相关 https://bugs.openjdk.java.net/browse/JDK-8258597 - wpater
我在遗留的Spring Web 应用程序中使用Java电子邮件程序时遇到了问题。首先,有一个杀毒软件阻止对smtp.gmail.com的调用。所以我必须解决这个问题,并在Java 8 keystore中安装smtp.gmail.com服务器证书。然后,我必须按照不禁用java.security文件中的TLS协议的步骤进行操作。最终一切都运作良好!感谢这个解决方案。 - sunitkatkar
显示剩余2条评论

12
protocol is disabled or cipher suites are inappropriate
问题的关键在于该语句。它基本上意味着以下情况之一:
  1. 客户端使用的TLS实现不支持服务器证书使用的密码套件。
  2. 服务器上的TLS配置已禁用客户端支持的密码套件。
  3. 客户端的TLS配置禁用服务器提供的密码套件。
  4. 客户端和服务器之间的TLS版本不兼容。
这将导致TLS握手失败,连接将失败。检查以上三种情况中的一个或多个。

这个异常甚至在发送到服务器之前就发生了。 - dave_thompson_085
似乎是在TLS握手期间发生的。 - automaton
1
它在握手逻辑中,但如果你跟踪代码,它是在套接字=TCP级别发送任何东西(特别是ClientHello)之前的一个点。(如果你计算那个,TCP SYN/ACK已经完成,但这与SSL/TLS无关。) - dave_thompson_085
2
是的,我不得不在我的 setSecureSocketProtocol 中将 "SSL" 替换为 "TLS",然后它就正常工作了。 - Nicholas DiPiazza
1
setSecureSocketProtocol在哪里? - A.s.ALI

12

您可以像这样将期望使用的 TLS 协议添加到连接字符串中:

jdbc:mysql://localhost:3306/database_name?enabledTLSProtocols=TLSv1.2

这对我解决了问题。


2022年4月2日编辑:

如Yair的评论所说:

自Connector/J 8.0.28以来,enabledTLSProtocols已更名为tlsVersions。


在AWS RDS MariaDB中,这不起作用。通过使用Wireshark查看连接,它会继续到将抛出TLSv1记录层:警报(级别:致命,描述:协议版本)的点。如果没有enabledTLSProtocols=TLSv1.2,连接会在早期阶段中断。 - Jari Turkia
适用于Amazon Aurora MySQL,表现良好。 - kio21
自从Connector/J 8.0.28版本,enabledTLSProtocols已更名为tlsVersions。请参见https://dev.mysql.com/doc/connector-j/8.0/en/connector-j-reference-using-ssl.html。 - Yair Kukielka
@YairKukielka 谢谢您的评论。我已将其添加到答案中。 - Gerard de Visser

9

谢谢!这确实解决了我的MySQL连接问题。 - Jari Turkia
谢谢。其他解决方案解决了我的本地问题,但我在测试服务器上仍然面临同样的问题。这个解决了它。 - Saif

6
我们在升级到jre1.8.0_291之后遇到了这个问题。我在位于C:\ Program Files \ Java \ jre1.8.0_291 \ lib \ security中的java.security中注释掉了“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”,这解决了问题。

4
我们遇到了同样的问题,但是我们没有完全注释掉 $JRE_HOME/lib/security/java.security 中的 jdk.tls.disabledAlgorithms 行,而是从列表中删除了 TLSv1TLSv1.1 即可解决问题。这也得到了 OpenJDK/Oracle 的建议,详情请参见:https://www.oracle.com/java/technologies/javase/8u291-relnotes.html#JDK-8202343 - eidottermihi

5

javax.net.ssl.SSL握手异常:没有适当的协议(协议已禁用或密码套件不合适)

为了长期保存,最近我在使用IBM的JDK8实现时遇到了这个问题,它特别默认禁用TLS1.1和1.2 (sic)。 如果您想查看JVM支持哪些TLS版本,请运行类似下面代码的内容:

SSLContext context = SSLContext.getInstance("TLS");
context.init(null, null, null);
String[] supportedProtocols = context.getDefaultSSLParameters().getProtocols();
System.out.println(Arrays.toString(supportedProtocols));

代码在AIX JDK8下默认返回[TLSv1],这不好。但在Redhat和Solaris下则会返回[TLSv1, TLSv1.1, TLSv1.2]
我无法在java.security文件中找到任何值来解决此问题,但对于您的架构可能有一些解决方法。就IBM特定情况而言,我们需要添加以下内容:
-Dcom.ibm.jsse2.overrideDefaultTLS=true

我尝试使用 getInstance("TLSv1.2"),它返回了 [TLSv1.2](J9版本1.8.0_191)。 - Lorinczy Zsigmond

4

在我的情况下,我不得不将mysql客户端库升级到最新版本,然后它又开始工作了:

    <dependency>
       <groupId>mysql</groupId>
       <artifactId>mysql-connector-java</artifactId>
       <version>8.0.24</version>
    </dependency>

1
我使用的是8.0.16版本,对我来说似乎可以工作!感谢您提供的建议!我的情况是将Spring Boot应用部署到Heroku上。 - Braden Borman
1
我将驱动程序升级到8.0.25,看起来问题已经解决了。 - Brill Pappin

2

我遇到了

javax.net.ssl.SSLHandshakeException: No appropriate protocol (protocol is disabled or cipher suites are inappropriate)

当从Java 11应用程序访问启用TLS 1.3的端点时出现错误,这在GCP中是一个常见情况。

通过将运行时从Java 11升级到Java 14,问题已经得到解决,而不需要对代码进行任何更改。

默认情况下,Java 11不会弃用早期的TLS协议版本。而不是进行配置,简单地升级到Java 14运行时就可以解决问题。


1

显然,如果您禁用了TLS 1.0,则电子邮件将无法发送。 TLS版本1.1和1.2不起作用。彼得的建议为我解决了问题。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接