如何修复“ SSLHandshakeException:收到致命警报:decode_error”?

时间:2019-07-18 23:12:35

标签: java ssl calendar icloud

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

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

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

Google日历仍然有效。

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

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

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

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

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

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

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是否已拾取了这些证书。

这不起作用,我们得到了相同的错误。

我们正在运行openjdk版本“ 11.0.2”,因此我不认为SNI或密码丢失有任何问题(尽管我可能错了!)

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

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专家。

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环境中还有什么可以使它工作的?

这很难确定,但似乎这发生在最近的2-4周左右。我们有证据显示6月24日成功读取了icloud日历,从7月4日开始,我们看到了上述错误。

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

更新

我们强制使用TLS1.2,它可以正常工作。对于上面的示例:

java -Dhttps.protocols=TLSv1.2 CalReader

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

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

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

2 个答案:

答案 0 :(得分:1)

我昨天在尝试连接到https://apple.com:443的Java应用程序中遇到了同样的问题。我发现在其他(Docker)操作系统上使用其他JDK可以解决此问题。在我的特定情况下,Alpine上的OpenJDK无法正常工作,但是其他Dockerized OpenJDK(例如adoptopenjdk/openjdk11)也可以正常工作。

答案 1 :(得分:1)

在连接苹果云网址时,我遇到了Received fatal alert: decode_error。我可以通过将协议明确设置为TLS 1.2来解决此问题。

例如,在kotlin中,按如下所示更新您的spring RestTemplate

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() return RestTemplate(requestFactory) }