本文介绍了如何修复“SSLHandshakeException:收到致命警报:decode_error"?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我们有一个 Java 应用程序可以读取我们客户的公共日历 - 例如来自 google (https://calendar.google.com/calendar/ical/...) 或 icloud (webcal://p58-caldav.icloud.com/published/...).

We have a java application that reads our customers public calendars - for example from google (https://calendar.google.com/calendar/ical/...) or icloud (webcal://p58-caldav.icloud.com/published/...).

直到最近这还有效,但现在 icloud 日历失败并出现以下错误:

Until recently this was working, but it is now failing for icloud calendars with the following error:

javax.net.ssl.SSLHandshakeException: Received fatal alert: decode_error

Google 日历仍然有效.

Google calendars still work.

我们的应用程序通过将 webcal://替换为 https://并在 URL 上执行 http get 来读取 icloud 日历.

Our application reads icloud calendars by swapping webcal:// for https:// and doing an http get on the url.

例如. https://p58-caldav.icloud.com/published/2/MTg3MDA3MjYwMTE4NzAwN1KPAcrTfGuhFJXbGYJ9wEYJFNzP10cp8mw6gSLjUVU_

鉴于在浏览器中点击此 url 成功返回了有效的 icalendar (ics) 结果,我们首先想到的是,当在 jvm 中运行时,此端点的根 ca 证书不在 jvm 的密钥库中.

Given that hitting this url in a browser successfully returns a valid icalendar (ics) result our first thought was that when running in a jvm this endpoint's root ca certificate were not in the keystore for the jvm.

我们使用以下命令来获取此端点的证书:

We used the following command to get the certificates for this endpoint:

openssl s_client -servername p58-caldav.icloud.com -host p58-caldav.icloud.com -port 443 -prexit -showcerts

然后将链中的两个证书都添加到我们的密钥库中:

And then added both of the certificates in the chain to our keystore:

keytool -importcert -file apple-ist-ca-2-g1.crt -alias apple-ist-ca-2-g1 -trustcacerts -keystore <java home>/lib/security/cacerts -storepass <pass>
keytool -importcert -file caldav.icloud.com.crt -alias caldav.icloud.com -trustcacerts -keystore <java home>/lib/security/cacerts -storepass <pass>

我们还在测试用例中添加了代码,以验证 jvm 是否正在获取这些证书.

We also added code to our test case to verify that these certificates were being picked up by the jvm.

这不起作用,我们得到了同样的错误.

This did not work and we get the same error.

我们正在运行 openjdk 版本11.0.2",所以我不认为 SNI 有任何问题或缺少密码(尽管我可能错了!)

We are running openjdk version "11.0.2" so I do not believe that there are any issues with SNI or missing ciphers (though I could be wrong!)

以下非常简单的代码示例重现了错误:

The following very simple code example reproduces the error:

public class CalReader {

    public static void main(String[] args) {
        hitAPI("webcal://p58-caldav.icloud.com/published/2/MTg3MDA3MjYwMTE4NzAwN1KPAcrTfGuhFJXbGYJ9wEYJFNzP10cp8mw6gSLjUVU_");
    }

    private static void hitAPI(final String url) {
        String cleaned = url.replace("webcal://", "https://").trim();
        InputStream in = null;
        try {
            URL toUse = new URL(cleaned);
            URLConnection conn = toUse.openConnection();
            in = conn.getInputStream();
        }
        catch (Exception e) {
            e.printStackTrace();
        }
        finally {
            try {
                if (in != null) {
                    in.close();
                }
            }
            catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

添加 ssl 调试 (-Djavax.net.debug=ssl:handshake:verbose) 不会(在我看来)提供任何相关信息.不过,我承认我不是 ssl 专家.

Adding ssl debugging (-Djavax.net.debug=ssl:handshake:verbose) does not (to my eye) provide any pertinent information. However, I admit that I am not an ssl expert.

javax.net.ssl|DEBUG|01|main|2019-07-18 11:51:53.042 AEST|ClientHello.java:651|Produced ClientHello handshake message (
"ClientHello": {
  "client version"      : "TLSv1.2",
  "random"              : "BC DF 54 80 F2 0B 9A F9 42 17 4A FF B9 B5 65 22 C4 9E 19 63 0A 88 81 1F 84 2A 74 2D 40 40 F2 F0",
  "session id"          : "A9 21 0D FE 9B 8E 9F B8 53 17 60 09 D4 B2 C8 EE BE FD 7F AC 93 57 12 4B 47 BA FF A9 7A DF C4 EF",
  "cipher suites"       : "[TLS_AES_128_GCM_SHA256(0x1301), TLS_AES_256_GCM_SHA384(0x1302), TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384(0xC02C), TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256(0xC02B), TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384(0xC030), TLS_RSA_WITH_AES_256_GCM_SHA384(0x009D), TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384(0xC02E), TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384(0xC032), TLS_DHE_RSA_WITH_AES_256_GCM_SHA384(0x009F), TLS_DHE_DSS_WITH_AES_256_GCM_SHA384(0x00A3), TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256(0xC02F), TLS_RSA_WITH_AES_128_GCM_SHA256(0x009C), TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256(0xC02D), TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256(0xC031), TLS_DHE_RSA_WITH_AES_128_GCM_SHA256(0x009E), TLS_DHE_DSS_WITH_AES_128_GCM_SHA256(0x00A2), TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384(0xC024), TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384(0xC028), TLS_RSA_WITH_AES_256_CBC_SHA256(0x003D), TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384(0xC026), TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384(0xC02A), TLS_DHE_RSA_WITH_AES_256_CBC_SHA256(0x006B), TLS_DHE_DSS_WITH_AES_256_CBC_SHA256(0x006A), TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA(0xC00A), TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA(0xC014), TLS_RSA_WITH_AES_256_CBC_SHA(0x0035), TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA(0xC005), TLS_ECDH_RSA_WITH_AES_256_CBC_SHA(0xC00F), TLS_DHE_RSA_WITH_AES_256_CBC_SHA(0x0039), TLS_DHE_DSS_WITH_AES_256_CBC_SHA(0x0038), TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256(0xC023), TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256(0xC027), TLS_RSA_WITH_AES_128_CBC_SHA256(0x003C), TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256(0xC025), TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256(0xC029), TLS_DHE_RSA_WITH_AES_128_CBC_SHA256(0x0067), TLS_DHE_DSS_WITH_AES_128_CBC_SHA256(0x0040), TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA(0xC009), TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA(0xC013), TLS_RSA_WITH_AES_128_CBC_SHA(0x002F), TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA(0xC004), TLS_ECDH_RSA_WITH_AES_128_CBC_SHA(0xC00E), TLS_DHE_RSA_WITH_AES_128_CBC_SHA(0x0033), TLS_DHE_DSS_WITH_AES_128_CBC_SHA(0x0032)]",
  "compression methods" : "00",
  "extensions"          : [

  ]
}
)
javax.net.ssl|DEBUG|01|main|2019-07-18 11:51:53.046 AEST|Alert.java:232|Received alert message (
"Alert": {
  "level"      : "fatal",
  "description": "decode_error"
}
)

我们刚刚在 java 8 环境中进行了尝试,它按预期工作.作为一个快速测试,我们将我们的 java 8 环境 cacerts 复制到我们的 java 11 环境,但是它仍然无法正常工作.java 8 环境中还有什么可以让它工作的?

We have just tried this in a java 8 environment and it works as expected. As a quick test, we copied our java 8 environment cacerts to our java 11 environment, however, it still does not work. What else is there in the java 8 environment that would allow it to work?

很难确定,但这似乎是在过去 2-4 周左右发生的.我们有证据表明在 6 月 24 日成功读取了 icloud 日历,并且我们从 7 月 4 日开始看到上述错误.

It is difficult to determine, but it appears that this has happened in the last 2-4 weeks or so. We have evidence of a successful read of an icloud calendar on June 24th, and we are seeing the above error from July 4th.

我们尝试了多个 icloud 日历,每个都得到相同的结果,所以它似乎与日历本身无关.

We have tried multiple icloud calendars and get the same result for each, so it does not appear to be related to the calendar itself.

更新

我们强制使用 TLS1.2 并且它有效.对于上面的例子:

We forced the use of TLS1.2 and it works. For the example above:

java -Dhttps.protocols=TLSv1.2 CalReader

这就引出了一个问题,Apple 端点是否存在 TLS1.3 问题?或者是别的什么?

This begs the question, do Apple endpoints have an issue with TLS1.3? Or is it something else?

我们确实找到了一篇文章,指出 java 11 中的 TLS1.3 存在问题 (https://webtide.com/openjdk-11-and-tls-1-3-issues/),但是我们已经尝试过使用 java 12 版本但没有成功,所以我没有'认为这不是问题.

We did find an article which indicated an issue with TLS1.3 in java 11 (https://webtide.com/openjdk-11-and-tls-1-3-issues/), but we had already tried with a java 12 version with no success, so I don't think that was the issue.

因此,我们现在必须决定是强制使用 TLS1.2 还是继续寻找其他解决方案.

So, we have to decide now whether to regress with a forced use of TLS1.2 or keep searching for another solution.

推荐答案

我在连接苹果云 url 时遇到 Received fatal alert: decode_error.我可以通过将协议显式设置为 TLS 1.2 来修复它.

I faced Received fatal alert: decode_error while connecting with apple cloud urls. I was able to fix it by explicitly setting the protocol as TLS 1.2.

例如:在 kotlin 中更新您的 spring RestTemplate 如下,

For eg: in kotlin update your spring RestTemplate as below ,

fun restTemplateWithTLS12(): RestTemplate {val requestFactory = HttpComponentsClientHttpRequestFactory()val sslContext = SSLContexts.custom().setProtocol("TLSv1.2").build()val clientBuilder =HttpClients.custom().setSSLSocketFactory(SSLConnectionSocketFactory(sslContext))requestFactory.httpClient = clientBuilder.build()返回 RestTemplate(requestFactory)}

这篇关于如何修复“SSLHandshakeException:收到致命警报:decode_error"?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

07-16 18:09
查看更多