【问题标题】:Java - HTTPS GET call fails from EC2 instance with handshake_failure, works locallyJava - HTTPS GET 调用从 EC2 实例失败并带有握手失败,在本地工作
【发布时间】:2026-01-01 17:00:01
【问题描述】:

我的想法已经用完了,这就是我在这里寻求帮助的原因。我有一个通过 HTTPS 进行 REST 调用的小类。我正在做某种抓取,并希望尽可能避免安装所有 SSL 证书。

代码在本地运行良好(来自 Eclipse 和独立 Tomcat),但每次从我的 AWS EC2 实例运行时,它都会失败并显示 javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure

代码是:

// apiUrl looks like https://hsreplay.net/api/v1/games/jdUbSjsEcBL5rCT7dgMXRn
private String restGetCall(String apiUrl) throws Exception {
    System.setProperty("javax.net.debug", "ALL");
    SSLContextBuilder builder = new SSLContextBuilder();
    builder.loadTrustMaterial(null, new TrustSelfSignedStrategy());
    SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(builder.build(),
            SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER) {
        @Override
        protected void prepareSocket(SSLSocket socket) throws IOException {
            try {
                log.debug("************ setting socket HOST property *************");
                PropertyUtils.setProperty(socket, "host", "hsreplay.net");
                socket.setEnabledProtocols(new String[] { "SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2" });
            }
            catch (IllegalAccessException | NoSuchMethodException | InvocationTargetException ex) {
                log.error(ex.getMessage());
                slackNotifier.notifyError(ex);
            }
            super.prepareSocket(socket);
        }

    };
    CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(sslsf).build();

    HttpGet httpGet = new HttpGet(apiUrl);
    CloseableHttpResponse response1 = httpClient.execute(httpGet);
    StringBuilder result = new StringBuilder();
    try {
        System.out.println(response1.getStatusLine());
        HttpEntity entity1 = response1.getEntity();
        BufferedReader rd = new BufferedReader(new InputStreamReader(entity1.getContent()));
        String line;
        while ((line = rd.readLine()) != null) {
            result.append(line);
        }
        // do something useful with the response body
        // and ensure it is fully consumed
        EntityUtils.consume(entity1);
    }
    finally {
        response1.close();
    }
}

我也尝试了基本版本(没有任何覆盖 SSLFactory 方法),但无济于事。

另外,我无法从 Tomcat 上的 javax.net.debug 获取日志(无论是在远程还是在本地),所以如果您在这里有任何想法,这也可能非常有用。

如果我可以提供更多信息,请告诉我,非常感谢您的帮助!

塞巴斯蒂安

【问题讨论】:

  • 你见过this吗?握手失败有许多不同的原因,但是使用最新的信任存储,您似乎应该能够在不安装特定证书的情况下完成这项工作。它似乎是来自 CloudFlare 的 ECC 证书,所以我会说它在服务器端是相对较新的技术,但不在主流之外。
  • @Michael-sqlbot:握手失败(几乎?)从未与服务器证书的验证相关联,因此对信任库的更改将无济于事。
  • @Michael-sqlbot 是的,我已经看到了。我会得出与 Steffen 相同的结论,所以我还没有修改过信任库,但如果我仍然被卡住,可能需要测试一下 :)
  • @SteffenUllrich 谢谢,我正试图解决/解雇“如果可能的话,我想避免安装所有 SSL 证书”,但没有很好地传达我的想法。我打算让这句话表明明确信任(“安装”)服务器的证书作为解决方案的一部分不应该是必要的,除非信任存储是(不太可能的)根本原因,而且我发现它并没有真正出现那样出去。对于不够清晰,我深表歉意。

标签: java ssl amazon-ec2 https


【解决方案1】:

@SteffenUlrich 的回答让我走上了正确的道路。这个问题确实是因为开放 jdk 7 不支持 ECDHE 密码。我迁移到 Oracle 的 JDK 8,现在一切正常。

【讨论】:

    【解决方案2】:

    握手失败可能是由许多不同的原因引起的,比如错误的 Java 版本、服务器设置、设置主机名验证器(无论如何都是坏主意)、SNI ...。此问题中的当前信息不足以确定问题所在。

    查看服务器的SSLLabs report 时,有两个信息看起来很有趣:

    • 服务器需要 SNI。
    • 服务器仅支持 ECDHE 密码。

    如果您设置了主机名验证器(尽管本网站不需要),第一个问题可能是旧 Java 版本甚至 JDK8 的问题。

    第二个可能是旧 Java 版本的问题,或者未安装无限强度的加密扩展。

    【讨论】:

    • 谢谢史蒂芬。您知道哪些附加信息可能有用吗? Java 版本是 openJDK 1.7.0_85,与我在本地环境中使用的版本相同,我没有收到任何错误。那么java版本可能是原因吗?主机名验证器应该是 Apache 的 HTTPClient 中始终返回 true 的验证器。
    • @SébastienTromp:我建议查看所有其他处理此问题的问题,即他们提供的信息。也许那里已经有一个完全适合您的环境的答案。但通常启用 TLS 调试或数据包捕获是一个不错的选择,以便可以查看 TLS 握手的详细信息。
    • 我基本上看了我能找到的所有东西 :) 我会更多地研究 TLS 调试 - 目前看起来 Tomcat 没有捕获输出,但这可能是我最好的机会。谢谢!
    • 打印启用的密码返回TLS_RSA_WITH_AES_256_CBC_SHA,TLS_DHE_RSA_WITH_AES_256_CBC_SHA,TLS_DHE_DSS_WITH_AES_256_CBC_SHA,TLS_RSA_WITH_AES_128_CBC_SHA,TLS_DHE_RSA_WITH_AES_128_CBC_SHA,TLS_DHE_DSS_WITH_AES_128_CBC_SHA,SSL_RSA_WITH_3DES_EDE_CBC_SHA,SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA,SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA,TLS_EMPTY_RENEGOTIATION_INFO_SCSV,因此没有ECDHE密码。 OpenJDK 7 显然不支持它们,*.com/questions/31971499/… 提出了一种支持它们的方法
    • 确实,您的建议让我走上了正确的道路。 openJDK 7 不支持 ECDHE 密码。我可以选择添加适当的加密库,或者迁移到 Sun 的 JDK。我做了后者,现在可以了。再次感谢!