【问题标题】:SSLHandShakeException No Appropriate ProtocolSSLHandShakeException 没有适当的协议
【发布时间】:2019-06-20 14:34:35
【问题描述】:

我最近在我的网站上添加了 SSL,它可以通过 https 访问。现在,当我的 java 应用程序尝试向我的网站发出请求并使用缓冲读取器从中读取时,它会产生此堆栈跟踪

我没有使用自签名证书,该证书来自使用 COMODO SSL 作为 CA 签署我的证书的 Namecheap。我正在使用 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版本?
  • 我没有使用自签名证书,该证书来自 Namecheap,他使用 COMODO SSL 作为 CA 来签署我的证书。我正在使用 java 8
  • (1) 证书与此错误无关。 (2) 您使用的是 Java 8 的 Sun/Oracle 版本,如果是,是哪个更新,还是其他 Java?是否在 JRE 中进行了任何配置更改,尤其是在文件 $JRE/lib/security/java.security 中? (3) 您是否设置了任何涉及https 尤其是https.protocols 的系统属性? (4) 尝试使用 sysprop javax.net.debug=ssl 运行并发布结果,除非您的信任库有很多证书(默认情况下),您可以将这部分减少到最低限度。
  • 请注明您使用的Java版本。如果您添加-Djavax.net.debug=ssl:handshake:verbose,它将允许您更详细地检查握手问题。

标签: java ssl bufferedreader


【解决方案1】:

从 1 升级到 2 + 修改 $JRE/lib/security/java.security 文件就可以了。 before after mysql driver

【讨论】:

    【解决方案2】:

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

    我从文件内的jdk.tls.disabledAlgorithms 列表中删除了TLSv1TLSv1.1

    /etc/java-8-openjdk/security/java.security

    检查后:

    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
    

    【讨论】:

    • 最新的openjdk 11版本(11.0.11+9-0ubuntu2~18.04)也会出现问题。我不明白的是,既然双方似乎都支持它,为什么java和mysql不同意使用TLSv1.2?另外,仅仅修补java.security 文件不是一个长期的解决方案,这个问题的解决方法是什么?
    • 我在 Centos 7 上遇到了这个问题
    • 同样使用 openjdk 11.0.11+9,同样的问题。在尝试解决问题时不得不回滚以使用较旧的 openjdk 映像。
    • 非常感谢您发布此信息。它解决了我的问题!我不明白的是为什么它首先成为一个问题 - 这在 Raspberry Pi 上运行了一年左右没有任何变化。
    【解决方案3】:

    我在tomcat7服务器上遇到了同样的情况,5.7.34-0ubuntu0.18.04.1,openjdk版本“1.8.0_292”

    我尝试了许多方法,例如在 server.xml 文件中禁用 SSL、更改连接字符串等

    但最后我所做的只是编辑文件 java.security sudo nano /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/security/java.security

    注释掉并删除 TLSv1 和 TLSv1.1

    # Comment the line below
    #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
    
    # your new line should read as beloew
    jdk.tls.disabledAlgorithms=SSLv3, RC4, DES, MD5withRSA, \
    DH keySize < 1024, EC keySize < 224, 3DES_EDE_CBC, anon, NULL, \
    include jdk.disabled.namedCurves
    

    【讨论】:

      【解决方案4】:

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

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

      这解决了我的问题。

      【讨论】:

      • 在 AWS RDS MariaDB 中,这不起作用。通过查看与 Wireshark 的连接,它确实会进行到TLSv1 Record Layer: Alert (Level: Fatal, Description: Protocol Version) 将被抛出的地步。如果没有enabledTLSProtocols=TLSv1.2,连接会在早期被丢弃。
      • 适用于 Amazon Aurora MySQL
      【解决方案5】:

      在这种情况下,对于我来说:

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

      我发现这是 JDK/JRE (Java\jdk1.8.0_291\jre\lib\security) 配置相关的,为了解决它,您需要 禁用 TLS anon 和 NULL 密码套件强>。

      您可以在此处的官方文档中找到如何执行此操作: https://www.java.com/en/configure_crypto.html

      在执行此操作之前,请考虑使用 LEGACY 算法的影响。

      【讨论】:

        【解决方案6】:

        就我而言,我必须将 mysql 客户端库升级到最新版本,然后它又开始工作了:

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

        【讨论】:

        • 我在 8.0.16 上,它似乎对我有用!谢谢你的主意!我的情况是部署到 Heroku 的 Spring Boot 应用程序
        • 将我的驱动程序升级到 8.0.25,这似乎成功了。
        【解决方案7】:

        我们在升级到 jre1.8.0_291 后开始遇到这个问题。我注释掉了“jdk.tls.disabledAlgorithms=SSLv3, TLSv1, TLSv1.1, RC4, DES, MD5withRSA,
        DH keySize 在位于 C:\Program Files\Java\jre1.8.0_291\lib\security 的 java.security 中包含 jdk.disabled.namedCurves" 解决了问题。

        【讨论】:

        • 我们遇到了同样的问题,但我们没有完全注释掉$JRE_HOME/lib/security/java.security 中的jdk.tls.disabledAlgorithms 行,而是从列表中删除了TLSv1TLSv1.1,这足以解决问题.这也是 OpenJDK/Oracle 在这里建议的:oracle.com/java/technologies/javase/…
        【解决方案8】:

        javax.net.ssl.SSLHandshakeException: 没有合适的协议(协议被禁用或密码套件不合适)

        为了后代,我最近使用 IBM 的 JDK8 实现遇到了这个问题,该实现在默认情况下专门禁用了 TLS1.1 和 1.2(原文如此)。如果您想查看 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)
        【解决方案9】:

        我遇到过

        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。

        【讨论】:

          【解决方案10】:

          显然,如果您禁用了 TLS 1.0,则不会发送电子邮件。 TLS 版本 1.1 和 1.2 不起作用。彼得的建议对我有用。

          【讨论】:

            【解决方案11】:

            就我而言,我正在运行 Centos 8,并且在使用 Imap/Java 时遇到了同样的问题。 必须更新系统范围的加密策略级别。

            1. update-crypto-policies --set LEGACY
            2. 重启机器。

            就是这样。

            https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/8/html/considerations_in_adopting_rhel_8/security_considerations-in-adopting-rhel-8#tls-v10-v11_security

            【讨论】:

            • 谢谢!这确实解决了我的 MySQL 连接问题。
            • 谢谢。其他解决方案解决了我的本地问题,但我在测试服务器中仍然面临同样的问题。这个修好了。
            【解决方案12】:
            protocol is disabled or cipher suites are inappropriate
            

            问题的关键在于那句话。它的基本意思是:

            1. 客户端使用的 TLS 实现不支持服务器证书使用的密码套件。
            2. 服务器上的 TLS 配置已禁用客户端支持的密码套件。
            3. 客户端上的 TLS 配置禁用服务器提供的密码套件。
            4. 客户端和服务器之间的 TLS 版本不兼容。

            这会导致 TLS 中的握手失败,连接失败。检查上述三种情况中的一种或全部。

            【讨论】:

            • 这个异常在发送到服务器之前就发生了。
            • 它似乎发生在 TLS 握手期间。
            • 它在握手逻辑中,但如果你跟踪代码,它是在任何东西(特别是ClientHello)在socket=TCP级别发送之前的某个时间点。 (如果算上 TCP SYN/ACK 已经完成,但这与 SSL/TLS 无关。)
            • 是的,我必须在我的setSecureSocketProtocol 中将“SSL”切换为“TLS”,并且效果很好
            • SecureSocketProtocol 设置在哪里
            【解决方案13】:

            $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”。

            【讨论】:

            • 我的理解不同,这是禁用弱算法,你应该使用更好的算法。
            • 对于 Java 11+,该文件位于“$JAVA_HOME/conf/security”中。
            • 如果有人发现由于 MySQL JDBC Driver 错误,您只需从该列表中排除 TLSv1 和 TLSv1.1 即可。
            • @OlegKurbatov 谢谢!它对我的问题有帮助。根据oracle.com/java/technologies/javase/8u291-relnotes.html,上周发布的 JDK8-u292 中禁用了 TLS 1.0 和 1.1。您可以尝试在连接字符串中明确使用enabledTLSProtocols=TLSv1.2 吗?希望它也会对您有所帮助,因为 TLSv1.0 和 TLSv1.1 已被弃用。
            • @wwjih123 你能解释一下为什么这个错误会突然出现吗?直到今天,我的解决方案使用 Tomcat9 和 JDK8 运行良好,我没有进行任何更改,突然我开始收到此错误。不知道为什么?请帮忙
            猜你喜欢
            • 2021-05-02
            • 2017-04-27
            • 1970-01-01
            • 1970-01-01
            • 2020-06-24
            • 2021-08-05
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多