Java 7 和无法生成 DH 密钥对

2022-09-03 00:47:13

我读过一篇关于错误“无法生成DH密钥对”的文章,当服务器发送长度超过1024位的密钥时触发。下载 JCE 无限 jar 应该可以解决此问题。在我遇到的测试环境中,如果我使用Java 6,对于同一Web服务器,我在执行https查询时不会遇到任何错误,但是如果我使用Java 7,那么我会得到“无法生成DH密钥对”。

我尝试将jar文件替换为JCE Unlimited,但仍然得到相同的错误。该错误自2007年以来一直被报告,但是为什么它适用于Java 6而不是Java 7?要下载的文件是否不正确?我从之前的一篇文章Java中获得了链接:为什么SSL握手给出“无法生成DH密钥对”异常?

在这一点上,我不知道该怎么办。如果我尝试加载 BouncyCastle 提供程序,我会收到 ArrayOutOfIndex 异常。我的服务器只允许DH算法,所以我不能使用上面帖子中建议的其他算法。


答案 1

一些补充或澄清:

(太阳)自7u09以来的Java 7默认使用更合理的一致密码套件顺序,这与7u04中看似随机的顺序不同。(我没有04和09之间的测试。此顺序将 ECDHE 和 plain-RSA(又名 akRSA)放在 DHE 之前,因此仅当服务器支持 ECDHE 或 RSA 并同意客户端首选项时,才避免此问题。(或者ECDH固定,但几乎没有人使用它。如果服务器坚持使用DHE(无论出于何种原因)并使用DH>1024位,您仍然会遇到问题。

如果提问者(或其他任何人)连接到真正需要整数 DH(而不是 ECDH 或 RSA)的服务器,则在 8 之前使用 Java 的唯一方法是让服务器使用 DH 1024 位。哪个AFAWK在技术上可以再安全几年,但其利润微薄,因此被NIST等重要当局禁止(参见 csrc.nist.gov 特别Pub 800-57)。(甚至RSA 1024实际上还没有损坏,但它可能很快就会损坏,因此被禁止。

“无限实力政策”与这个问题无关,或者至少与直接无关,对#6851461的良好答案也没有说是。它不会更改 SunJCE 中对 DH 参数的限制,这(错误地)被视为标准问题而不是强度问题。(具体来说,它采用了过去对DSA正确的限制,并将其应用于DH。它确实启用了AES-256和SHA-2(仅适用于TLSv1.2)套件,并给出了一个足够奇怪的首选项列表,可能会将选择结果从DHE(失败)更改为非DHE(工作)。

您不需要完全回到Java 6列表,只需要将其他密钥交换优先于DHE,或者对于顽固的服务器,则完全丢弃DHE。您绝对不应该回到启用任何EXPORT或单DES套件,除非遗留服务器绝对需要;它们已经不安全好几年了,并且默认情况下在6中保持启用状态的时间远远超过它们应该启用的时间。


答案 2

我在SSScokets上偶然发现了同样的问题,我想我确定了Java 7回归的原因。原因在于客户端和服务器之间协商的密码。

缺省情况下,Java 6 为 TLS 连接启用这些密码(按优先级顺序):

SSL_RSA_WITH_RC4_128_MD5
SSL_RSA_WITH_RC4_128_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
SSL_RSA_WITH_DES_CBC_SHA
SSL_DHE_RSA_WITH_DES_CBC_SHA
SSL_DHE_DSS_WITH_DES_CBC_SHA
SSL_RSA_EXPORT_WITH_RC4_40_MD5
SSL_RSA_EXPORT_WITH_DES40_CBC_SHA
SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA
SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA
TLS_EMPTY_RENEGOTIATION_INFO_SCSV

Java 7 支持以下密码:

TLS_DHE_RSA_WITH_AES_128_CBC_SHA
TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA
SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA
SSL_RSA_WITH_RC4_128_SHA
TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA
TLS_ECDHE_RSA_WITH_RC4_128_SHA
TLS_ECDH_ECDSA_WITH_RC4_128_SHA
TLS_ECDHE_ECDSA_WITH_RC4_128_SHA
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA
TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA
TLS_ECDH_RSA_WITH_RC4_128_SHA
TLS_EMPTY_RENEGOTIATION_INFO_SCSV
TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA
TLS_ECDH_RSA_WITH_AES_128_CBC_SHA
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA
TLS_RSA_WITH_AES_128_CBC_SHA
TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA
SSL_RSA_WITH_RC4_128_MD5
TLS_DHE_DSS_WITH_AES_128_CBC_SHA
SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA
SSL_RSA_WITH_3DES_EDE_CBC_SHA

使用Diffie-Hellman的密码在Java 7上的优先级更高,但它们似乎不支持超过1024位的密钥,除非安装了强加密包。

我使用的解决方法是在 上指定 Java 6 启用的密码:SSLSocket

SSLSocketFactory socketFactory = SSLContext.getInstance("TLS").getSocketFactory();
SSLSocket socket = (SSLSocket) socketFactory.createSocket(InetAddress.getByName(hostname), port);
socket.setEnabledCipherSuites(new String[] {
        "SSL_RSA_WITH_RC4_128_MD5",
        "SSL_RSA_WITH_RC4_128_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",
        "SSL_RSA_WITH_DES_CBC_SHA",
        "SSL_DHE_RSA_WITH_DES_CBC_SHA",
        "SSL_DHE_DSS_WITH_DES_CBC_SHA",
        "SSL_RSA_EXPORT_WITH_RC4_40_MD5",
        "SSL_RSA_EXPORT_WITH_DES40_CBC_SHA",
        "SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA",
        "SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA",
        "TLS_EMPTY_RENEGOTIATION_INFO_SCSV"});

socket.startHandshake();