为啥 SSL 握手会给出“无法生成 DH 密钥对”异常?

Posted

技术标签:

【中文标题】为啥 SSL 握手会给出“无法生成 DH 密钥对”异常?【英文标题】:Why does SSL handshake give 'Could not generate DH keypair' exception?为什么 SSL 握手会给出“无法生成 DH 密钥对”异常? 【发布时间】:2011-10-14 15:40:59 【问题描述】:

当我与一些 IRC 服务器(但不是其他服务器 - 可能是由于服务器的首选加密方法)建立 SSL 连接时,我收到以下异常:

Caused by: java.lang.RuntimeException: Could not generate DH keypair
    at com.sun.net.ssl.internal.ssl.DHCrypt.<init>(DHCrypt.java:106)
    at com.sun.net.ssl.internal.ssl.ClientHandshaker.serverKeyExchange(ClientHandshaker.java:556)
    at com.sun.net.ssl.internal.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:183)
    at com.sun.net.ssl.internal.ssl.Handshaker.processLoop(Handshaker.java:593)
    at com.sun.net.ssl.internal.ssl.Handshaker.process_record(Handshaker.java:529)
    at com.sun.net.ssl.internal.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:893)
    at com.sun.net.ssl.internal.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1138)
    at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1165)
    ... 3 more

最终原因:

Caused by: java.security.InvalidAlgorithmParameterException: Prime size must be multiple of 64, and can only range from 512 to 1024 (inclusive)
    at com.sun.crypto.provider.DHKeyPairGenerator.initialize(DashoA13*..)
    at java.security.KeyPairGenerator$Delegate.initialize(KeyPairGenerator.java:627)
    at com.sun.net.ssl.internal.ssl.DHCrypt.<init>(DHCrypt.java:100)
    ... 10 more

aperture.esper.net:6697(这是一个 IRC 服务器)是演示此问题的服务器示例。 kornbluth.freenode.net:6697 是未证明该问题的服务器示例。 [毫不奇怪,每个网络上的所有服务器都共享相同的各自行为。]

我的代码(如前所述,在连接到某些 SSL 服务器时确实有效)是:

    SSLContext sslContext = SSLContext.getInstance("SSL");
    sslContext.init(null, trustAllCerts, new SecureRandom());
    s = (SSLSocket)sslContext.getSocketFactory().createSocket();
    s.connect(new InetSocketAddress(host, port), timeout);
    s.setSoTimeout(0);
    ((SSLSocket)s).startHandshake();

最后一次 startHandshake 引发了异常。是的,“trustAllCerts”有一些魔力;该代码强制 SSL 系统不验证证书。 (所以...不是证书问题。)

很明显,一种可能性是 esper 的服务器配置错误,但我搜索并没有找到任何其他关于 esper 的 SSL 端口有问题的人的引用,并且“openssl”连接到它(见下文)。所以我想知道这是否是 Java 默认 SSL 支持的限制,或者其他什么。有什么建议吗?

当我从命令行使用“openssl”连接到 Aperture.esper.net 6697 时会发生以下情况:

~ $ openssl s_client -connect aperture.esper.net:6697
CONNECTED(00000003)
depth=0 /C=GB/ST=England/L=London/O=EsperNet/OU=aperture.esper.net/CN=*.esper.net/emailAddress=support@esper.net
verify error:num=18:self signed certificate
verify return:1
depth=0 /C=GB/ST=England/L=London/O=EsperNet/OU=aperture.esper.net/CN=*.esper.net/emailAddress=support@esper.net
verify return:1
---
Certificate chain
 0 s:/C=GB/ST=England/L=London/O=EsperNet/OU=aperture.esper.net/CN=*.esper.net/emailAddress=support@esper.net
   i:/C=GB/ST=England/L=London/O=EsperNet/OU=aperture.esper.net/CN=*.esper.net/emailAddress=support@esper.net
---
Server certificate
-----BEGIN CERTIFICATE-----
[There was a certificate here, but I deleted it to save space]
-----END CERTIFICATE-----
subject=/C=GB/ST=England/L=London/O=EsperNet/OU=aperture.esper.net/CN=*.esper.net/emailAddress=support@esper.net
issuer=/C=GB/ST=England/L=London/O=EsperNet/OU=aperture.esper.net/CN=*.esper.net/emailAddress=support@esper.net
---
No client certificate CA names sent
---
SSL handshake has read 2178 bytes and written 468 bytes
---
New, TLSv1/SSLv3, Cipher is DHE-RSA-AES256-SHA
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
SSL-Session:
    Protocol  : TLSv1
    Cipher    : DHE-RSA-AES256-SHA
    Session-ID: 51F1D40A1B044700365D3BD1C61ABC745FB0C347A334E1410946DCB5EFE37AFD
    Session-ID-ctx: 
    Master-Key: DF8194F6A60B073E049C87284856B5561476315145B55E35811028C4D97F77696F676DB019BB6E271E9965F289A99083
    Key-Arg   : None
    Start Time: 1311801833
    Timeout   : 300 (sec)
    Verify return code: 18 (self signed certificate)
---

如上所述,毕竟它确实连接成功,这比我的 Java 应用程序所能说的要多。

如果相关的话,我使用的是 OS X 10.6.8,Java 版本 1.6.0_26。

【问题讨论】:

原因似乎是这样的:Prime size must be multiple of 64, and can only range from 512 to 1024 (inclusive)。不知道服务器在这里发送了什么大小,以及规范对此有何规定。 @PaŭloEbermann:实际上您可以在问题的openssl 输出中看到服务器使用的大小:“密码是 DHE-RSA-AES256-SHA,服务器公钥是 2048 位”。并且 2048 > 1024 :-)。 @sleske:不完全是。 Server public key (size) 曾经是并且现在是证书中的密钥。 2011 年的s_client 根本没有显示临时密钥; 2015 年及更高版本的 1.0.2 与 Server Temp Key 相比高出几行。虽然一个好的服务器通常应该使 DHE 大小与 RSA-auth 大小相同。 【参考方案1】:

问题在于素数大小。 Java 接受的最大可接受大小是 1024 位。这是一个已知问题(请参阅JDK-6521495)。

我链接到的错误报告提到了使用 BouncyCastle 的 JCE 实现的workaround。希望这对你有用。

更新

这被报告为错误JDK-7044060 并在最近修复。

但请注意,该限制仅提高到 2048 位。对于大小 > 2048 位,有JDK-8072452 - Remove the maximum prime size of DH Keys;修复似乎适用于 9。

【讨论】:

+1。拥有 Sun Developer Network 帐户的每个人,请为这个 bug 投票。 谢谢。鉴于存在请求更大尺寸的服务器,这似乎是一个非常严重的问题! :( 我尝试了 BouncyCastle;如果您将其设置为首选提供程序,它会因不同的异常而崩溃(叹气),而且我看不到仅将其用于 DH 的明显方法。但是,我找到了另一种解决方案,我'将添加为新答案。(它不漂亮。) 酷。这已在较新版本的 Java 中得到修复。但我的问题是关于使用旧版本..当我使用旧版本时,有时它可以工作,有时它会给出上述异常..为什么这么随机的行为?如果它是java中的一个错误,那么我想它应该永远不会工作? 2048 修复被反向移植到 IcedTea 2.5.3 。较新版本的 IcedTea 将其增加到 4096。 问题是 DH 素数大小。 Java 接受的最大可接受大小是 2048 位。在我们等待 Oracle 扩大限制的同时,您可以使用具有扩大限制的 Excelsior Jet 进行编译。【参考方案2】:

“Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files”答案对我不起作用,但 The BouncyCastle 的 JCE 提供者建议对我有用。

这是我在 Mac OSC 10.7.5 上使用 Java 1.6.0_65-b14-462 所采取的步骤

1) 下载这些 jar:

bcprov-jdk15on-154.jar

bcprov-ext-jdk15on-154.jar

2) 将这些 jar 移动到 $JAVA_HOME/lib/ext

3) 编辑 $JAVA_HOME/lib/security/java.security 如下: security.provider.1=org.bouncycastle.jce.provider.BouncyCastleProvider

使用 JRE 重启应用并试一试

【讨论】:

security.provider.2=org.bouncycastle.jce.provider.BouncyCastleProvider 做得更好,将其置于 1. 导致默认软件出错。 这也适用于我,但我已经动态添加了一个提供程序。详情请参考我的answer。 非常感谢,这对我有用,并且是成功构建 Tomcat 7.0.78 源代码所必需的。 @mjj1409,在 Java 1.5 中我们没有 $JAVA_HOME/lib/security 路径【参考方案3】:

这是我的解决方案(java 1.6),我也会对为什么必须这样做感兴趣:

我从 javax.security.debug=ssl 中注意到,有时使用的密码套件是 TLS_DHE_...,有时是 TLS_ECDHE_...。如果我添加 BouncyCastle,则会发生后者。如果选择了 TLS_ECDHE_,则它大部分时间都有效,但并非总是如此,因此即使添加 BouncyCastle 提供程序也是不可靠的(每隔一段时间左右都会失败并出现相同的错误)。我猜在 Sun SSL 实现中的某个地方,有时它会选择 DHE,有时它会选择 ECDHE

因此,此处发布的解决方案依赖于完全删除 TLS_DHE_ 密码。注意:该解决方案不需要 BouncyCastle。

所以通过以下方式创建服务器认证文件:

echo |openssl s_client -connect example.org:443 2>&1 |sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p'

保存它,因为稍后会引用它,而不是这里是 SSL http get 的解决方案,不包括 TLS_DHE_ 密码套件。

package org.example.security;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.InetAddress;
import java.net.Socket;
import java.net.URL;
import java.net.UnknownHostException;
import java.security.KeyStore;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLParameters;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManagerFactory;

import org.apache.log4j.Logger;

public class SSLExcludeCipherConnectionHelper 

    private Logger logger = Logger.getLogger(SSLExcludeCipherConnectionHelper.class);

    private String[] exludedCipherSuites = "_DHE_","_DH_";

    private String trustCert = null;

    private TrustManagerFactory tmf;

    public void setExludedCipherSuites(String[] exludedCipherSuites) 
        this.exludedCipherSuites = exludedCipherSuites;
    

    public SSLExcludeCipherConnectionHelper(String trustCert) 
        super();
        this.trustCert = trustCert;
        //Security.addProvider(new BouncyCastleProvider());
        try 
            this.initTrustManager();
         catch (Exception ex) 
            ex.printStackTrace();
        
    

    private void initTrustManager() throws Exception 
        CertificateFactory cf = CertificateFactory.getInstance("X.509");
        InputStream caInput = new BufferedInputStream(new FileInputStream(trustCert));
        Certificate ca = null;
        try 
            ca = cf.generateCertificate(caInput);
            logger.debug("ca=" + ((X509Certificate) ca).getSubjectDN());
         finally 
            caInput.close();
        

        // Create a KeyStore containing our trusted CAs
        KeyStore keyStore = KeyStore.getInstance("jks");
        keyStore.load(null, null);
        keyStore.setCertificateEntry("ca", ca);

        // Create a TrustManager that trusts the CAs in our KeyStore
        String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
        tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
        tmf.init(keyStore);
    

    public String get(URL url) throws Exception 
        // Create an SSLContext that uses our TrustManager
        SSLContext context = SSLContext.getInstance("TLS");
        context.init(null, tmf.getTrustManagers(), null);
        SSLParameters params = context.getSupportedSSLParameters();
        List<String> enabledCiphers = new ArrayList<String>();
        for (String cipher : params.getCipherSuites()) 
            boolean exclude = false;
            if (exludedCipherSuites != null) 
                for (int i=0; i<exludedCipherSuites.length && !exclude; i++) 
                    exclude = cipher.indexOf(exludedCipherSuites[i]) >= 0;
                
            
            if (!exclude) 
                enabledCiphers.add(cipher);
            
        
        String[] cArray = new String[enabledCiphers.size()];
        enabledCiphers.toArray(cArray);

        // Tell the URLConnection to use a SocketFactory from our SSLContext
        HttpsURLConnection urlConnection =
            (HttpsURLConnection)url.openConnection();
        SSLSocketFactory sf = context.getSocketFactory();
        sf = new DOSSLSocketFactory(sf, cArray);
        urlConnection.setSSLSocketFactory(sf);
        BufferedReader in = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()));
        String inputLine;
        StringBuffer buffer = new StringBuffer();
        while ((inputLine = in.readLine()) != null) 
            buffer.append(inputLine);
        in.close();

        return buffer.toString();
    

    private class DOSSLSocketFactory extends javax.net.ssl.SSLSocketFactory 

        private SSLSocketFactory sf = null;
        private String[] enabledCiphers = null;

        private DOSSLSocketFactory(SSLSocketFactory sf, String[] enabledCiphers) 
            super();
            this.sf = sf;
            this.enabledCiphers = enabledCiphers;
        

        private Socket getSocketWithEnabledCiphers(Socket socket) 
            if (enabledCiphers != null && socket != null && socket instanceof SSLSocket)
                ((SSLSocket)socket).setEnabledCipherSuites(enabledCiphers);

            return socket;
        

        @Override
        public Socket createSocket(Socket s, String host, int port,
                boolean autoClose) throws IOException 
            return getSocketWithEnabledCiphers(sf.createSocket(s, host, port, autoClose));
        

        @Override
        public String[] getDefaultCipherSuites() 
            return sf.getDefaultCipherSuites();
        

        @Override
        public String[] getSupportedCipherSuites() 
            if (enabledCiphers == null)
                return sf.getSupportedCipherSuites();
            else
                return enabledCiphers;
        

        @Override
        public Socket createSocket(String host, int port) throws IOException,
                UnknownHostException 
            return getSocketWithEnabledCiphers(sf.createSocket(host, port));
        

        @Override
        public Socket createSocket(InetAddress address, int port)
                throws IOException 
            return getSocketWithEnabledCiphers(sf.createSocket(address, port));
        

        @Override
        public Socket createSocket(String host, int port, InetAddress localAddress,
                int localPort) throws IOException, UnknownHostException 
            return getSocketWithEnabledCiphers(sf.createSocket(host, port, localAddress, localPort));
        

        @Override
        public Socket createSocket(InetAddress address, int port,
                InetAddress localaddress, int localport) throws IOException 
            return getSocketWithEnabledCiphers(sf.createSocket(address, port, localaddress, localport));
        

    

最后这里是如何使用的(certFilePath如果是从openssl保存的证书的路径):

try 
            URL url = new URL("https://www.example.org?q=somedata");            
            SSLExcludeCipherConnectionHelper sslExclHelper = new SSLExcludeCipherConnectionHelper(certFilePath);
            logger.debug(
                    sslExclHelper.get(url)
            );
         catch (Exception ex) 
            ex.printStackTrace();
        

【讨论】:

刚刚测试了您的解决方案。它按预期工作。谢谢。实际上,只需在 JDK_HOME/jre/lib/security/java.security 中添加 jdk.tls.disabledAlgorithms=DHE, ECDHE 也可以并避免所有这些代码。 禁用 DHE 对我有用:jdk.tls.disabledAlgorithms=DHE。使用 1.7.0_85-b15。 在 JDK_HOME/jre/lib/security/java.security 中添加了 jdk.tls.disabledAlgorithms=DHE, ECDHE 并且工作正常!谢谢!使用 1.7.0_79 不要禁用 ECDHE。它与 DHE 不同,甚至不使用素数。 有人可以告诉我如何获得“certFilePath”。我已经坚持了一周。 @Zsozso【参考方案4】:

上面的答案是正确的,但就解决方法而言,当我将 BouncyCastle 设置为首选提供者时,我遇到了问题:

java.lang.ArrayIndexOutOfBoundsException: 64
    at com.sun.crypto.provider.TlsPrfGenerator.expand(DashoA13*..)

这也在我发现的一个论坛帖子中进行了讨论,但没有提及解决方案。 http://www.javakb.com/Uwe/Forum.aspx/java-programmer/47512/TLS-problems

我找到了一种适用于我的情况的替代解决方案,尽管我对此并不满意。解决方案是设置它,使 Diffie-Hellman 算法根本不可用。然后,假设服务器支持替代算法,它将在正常协商期间进行选择。显然,这样做的缺点是,如果有人设法找到一个仅支持 1024 位或更低位 Diffie-Hellman 的服务器,那么这实际上意味着它无法在以前工作的地方工作。

这是给定 SSLSocket 的代码(在连接之前):

List<String> limited = new LinkedList<String>();
for(String suite : ((SSLSocket)s).getEnabledCipherSuites())

    if(!suite.contains("_DHE_"))
    
        limited.add(suite);
    

((SSLSocket)s).setEnabledCipherSuites(limited.toArray(
    new String[limited.size()]));

讨厌。

【讨论】:

遗憾的是,这是唯一的方法 :(。这张票自 07 年以来一直开放。奇怪的是 4 年来什么都没做。 我是 Java 证券新手。请帮忙我应该在哪里写这段代码? 我尝试了这个线程中的所有解决方案,这是唯一一个适用于我的 ibm jvm (ibm-java-s390x-60) 的解决方案。 @Sam , ((SSLSocket)s) 中的变量是什么?【参考方案5】:

您可以在 jdk 中完全禁用 DHE,编辑 jre/lib/security/java.security 并确保禁用 DHE,例如。喜欢

jdk.tls.disabledAlgorithms=SSLv3, DHE.

【讨论】:

仅在 JDK 1.7 及更高版本中可用。 link 为我解决了这个问题 JDK 1.8.0_144-b01【参考方案6】:

您可以动态安装提供程序:

1) 下载这些 jar:

bcprov-jdk15on-152.jar bcprov-ext-jdk15on-152.jar

2) 将 jar 复制到 WEB-INF/lib(或您的类路径)

3) 动态添加提供者:

import org.bouncycastle.jce.provider.BouncyCastleProvider;

...

Security.addProvider(new BouncyCastleProvider());

【讨论】:

此解决方案运行良好,非常适合我的情况,因为我只想在项目级别而不是服务器/环境级别进行更改。谢谢 谢谢!这对我有用。在我的情况下,它是 itext 导入 bcp 1.4 导致电子邮件到谷歌失败。 我收到 javax.net.ssl.SSLHandshakeException: Unsupported curveId: 29 with java version 1.6.0_27【参考方案7】:

这是一篇相当老的帖子,但如果您使用 Apache HTTPD,您可以限制 DH 大小。 见http://httpd.apache.org/docs/current/ssl/ssl_faq.html#javadh

【讨论】:

【参考方案8】:

您可能有不正确的 Maven 依赖项。 您必须在 Maven 依赖层次结构中找到这些库:

bcprov-jdk14, bcpkix-jdk14, bcmail-jdk14

如果你有这些依赖,那就是错误,你应该这样做:

添加依赖:

<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcmail-jdk15on</artifactId>
    <version>1.59</version>
</dependency>

从包含错误依赖项的工件中排除这些依赖项,在我的例子中是:

<dependency>
    <groupId>com.lowagie</groupId>
    <artifactId>itext</artifactId>
    <version>2.1.7</version>
    <exclusions>
        <exclusion>
            <groupId>org.bouncycastle</groupId>
            <artifactId>bctsp-jdk14</artifactId>                
        </exclusion>
        <exclusion>
            <groupId>bouncycastle</groupId>
            <artifactId>bcprov-jdk14</artifactId>               
        </exclusion>
        <exclusion>
            <groupId>bouncycastle</groupId>
            <artifactId>bcmail-jdk14</artifactId>               
        </exclusion>
    </exclusions>       
</dependency>

【讨论】:

【参考方案9】:

如果您使用的是 jdk1.7.0_04,请升级到 jdk1.7.0_21。该问题已在该更新中得到解决。

【讨论】:

我刚下载了Java SE Development Kit 7u25,根据little program I wrote to determine the maximum supported DH size,还是1024。 JDK 1.7.0_25 仍然存在问题 更新 jre 对我有用。将 6.22 更新为 7.59 。问题解决了。 @Shashank - 升级到 JDK 1.8.0_73 对我有用。 从JDK 1.7.0_91 引入的位长大于 1024 的 DHKeyPairs,但不公开。【参考方案10】:

尝试从Java download site 下载“Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files”并替换 JRE 中的文件。

这对我有用,我什至不需要使用 BouncyCastle - 标准的 Sun JCE 能够连接到服务器。

附言。我在更改策略文件之前尝试使用 BouncyCastle 时遇到了同样的错误 (ArrayIndexOutOfBoundsException: 64),所以看起来我们的情况非常相似。

【讨论】:

你还做了什么吗?还是只是将 2 个文件复制到文件夹中? 已经有一段时间了,但据我所知,这就是我需要做的。除了之后重新启动任何正在运行的 Java 进程。【参考方案11】:

如果你仍然被这个问题困扰并且你正在使用 Apache httpd v> 2.4.7,试试这个:http://httpd.apache.org/docs/current/ssl/ssl_faq.html#javadh

从网址复制

从 2.4.7 版本开始,mod_ssl 将使用 DH 参数,其中包括长度超过 1024 位的素数。但是,Java 7 及更早版本将其对 DH 素数大小的支持限制为最多 1024 位。

如果您的基于 Java 的客户端异常中止,例如 java.lang.RuntimeException: Could not generate DH keypair and java.security.InvalidAlgorithmParameterException: Prime size must be multiple of 64, and can only range from 512 to 1024 (inclusive) ) 和 httpd 记录 tlsv1 警报内部错误(SSL 警报编号 80)(在 LogLevel 信息或更高级别),您可以使用 SSLCipherSuite(可能与 SSLHonorCipherOrder 一起)重新排列 mod_ssl 的密码列表,或者您可以使用带有 1024 的自定义 DH 参数-bit prime,它总是优先于任何内置的 DH 参数。

要生成自定义 DH 参数,请使用

openssl dhparam 1024

命令。或者,您可以使用 RFC 2409 第 6.2 节中的以下标准 1024 位 DH 参数:

-----BEGIN DH PARAMETERS-----
MIGHAoGBAP//////////yQ/aoiFowjTExmKLgNwc0SkCTgiKZ8x0Agu+pjsTmyJR
Sgh5jjQE3e+VGbPNOkMbMCsKbfJfFDdP4TVtbVHCReSFtXZiXn7G9ExC6aY37WsL
/1y29Aa37e44a/taiZ+lrp8kEXxLH+ZJKGZR7OZTgf//////////AgEC
-----END DH PARAMETERS-----

将包括“BEGIN DH PARAMETERS”和“END DH PARAMETERS”行在内的自定义参数添加到您使用 SSLCertificateFile 指令配置的第一个证书文件的末尾。


我在客户端使用 java 1.6,它解决了我的问题。我没有降低密码套件之类的,而是在证书文件中添加了一个自定义生成的 DH 参数..

【讨论】:

请注意,现在人们认为 1024 位 DH 在计算上是可行的(尽管非常昂贵)。【参考方案12】:

Yandex Maps 服务器、JDK 1.6 和 Apache HttpClient 4.2.1 也有同样的问题。错误是

javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated

-Djavax.net.debug=all 启用调试后,日志中有一条消息

Could not generate DH keypair

我已通过添加 BouncyCastle 库 bcprov-jdk16-1.46.jar 并在地图服务类中注册提供程序来解决此问题

public class MapService 

    static 
        Security.addProvider(new BouncyCastleProvider());
    

    public GeocodeResult geocode() 

    


在第一次使用MapService 时注册提供程序。

【讨论】:

【参考方案13】:

我在运行 JDK 6 的 CentOS 服务器上遇到 SSL 错误。

我的计划是安装更高版本的 JDK(JDK 7)以与 JDK 6 共存,但事实证明,仅使用 rpm -i 安装较新的 JDK 是不够的。

JDK 7 安装只有使用rpm -U 升级选项才能成功,如下所示。

1。下载 JDK 7

wget -O /root/jdk-7u79-linux-x64.rpm --no-cookies --no-check-certificate --header "Cookie: gpw_e24=http%3A%2F%2Fwww.oracle.com%2F; o raclelicense=accept-securebackup-cookie" "http://download.oracle.com/otn-pub/java/jdk/7u79-b15/jdk-7u79-linux-x64.rpm"

2。 RPM 安装失败

rpm -ivh jdk-7u79-linux-x64.rpm
Preparing...                ########################################### [100%]
        file /etc/init.d/jexec from install of jdk-2000:1.7.0_79-fcs.x86_64 conflicts with file from package jdk-2000:1.6.0_43-fcs.x86_64

3。 RPM 升级成功

rpm -Uvh jdk-7u79-linux-x64.rpm
Preparing...                ########################################### [100%]
   1:jdk                    ########################################### [100%]
Unpacking JAR files...
        rt.jar...
        jsse.jar...
        charsets.jar...
        tools.jar...
        localedata.jar...
        jfxrt.jar...

4。确认新版本

java -version
java version "1.7.0_79"
Java(TM) SE Runtime Environment (build 1.7.0_79-b15)
Java HotSpot(TM) 64-Bit Server VM (build 24.79-b02, mixed mode)

【讨论】:

【参考方案14】:

通过升级到 JDK 8 解决了这个问题。

【讨论】:

【参考方案15】:

我在 JDK 1.6.45 上使用了 Coldfusion 8,但在给我的只是红十字而不是图像时遇到了问题,而且 cfhttp 无法使用 ssl 连接到本地网络服务器。

我用 Coldfusion 8 重现的测试脚本是

<CFHTTP URL="https://www.onlineumfragen.com" METHOD="get" ></CFHTTP>
<CFDUMP VAR="#CFHTTP#">

这给了我“I/O 异常:对等体未通过身份验证”的非常普遍的错误。 然后,我尝试将服务器的证书(包括根证书和中间证书)添加到 java 密钥库以及冷融合密钥库,但没有任何帮助。 然后我用

调试了问题
java SSLPoke www.onlineumfragen.com 443

得到了

javax.net.ssl.SSLException: java.lang.RuntimeException: Could not generate DH keypair

Caused by: java.security.InvalidAlgorithmParameterException: Prime size must be
multiple of 64, and can only range from 512 to 1024 (inclusive)
    at com.sun.crypto.provider.DHKeyPairGenerator.initialize(DashoA13*..)
    at java.security.KeyPairGenerator$Delegate.initialize(KeyPairGenerator.java:627)
    at com.sun.net.ssl.internal.ssl.DHCrypt.<init>(DHCrypt.java:107)
    ... 10 more

然后我想到网络服务器(在我的例子中是 apache)有非常现代的 ssl 密码并且非常严格(qualys 得分 a+)并且使用超过 1024 位的强 diffie hellmann 密钥。显然,coldfusion 和 java jdk 1.6.45 无法做到这一点。 odysee 的下一步是考虑为 java 安装一个替代安全提供程序,我决定使用 bouncy castle。 另见http://www.itcsolutions.eu/2011/08/22/how-to-use-bouncy-castle-cryptographic-api-in-netbeans-or-eclipse-for-java-jse-projects/

然后我下载了

bcprov-ext-jdk15on-156.jar

来自http://www.bouncycastle.org/latest_releases.html 并安装在 C:\jdk6_45\jre\lib\ext 或您的 jdk 所在的位置,在冷融合 8 的原始安装中,它将位于 C:\JRun4\jre\lib\ext 下,但我使用位于外部的较新 jdk (1.6.45)冷融合目录。将 bcprov-ext-jdk15on-156.jar 放在 \ext 目录中非常重要(这花了我大约两个小时和一些头发 ;-) 然后我编辑了文件 C:\jdk6_45\jre\lib\security\java.security (使用写字板而不是 editor.exe!)并为新提供者放入一行。之后列表看起来像

#
# List of providers and their preference orders (see above):
#
security.provider.1=org.bouncycastle.jce.provider.BouncyCastleProvider
security.provider.2=sun.security.provider.Sun
security.provider.3=sun.security.rsa.SunRsaSign
security.provider.4=com.sun.net.ssl.internal.ssl.Provider
security.provider.5=com.sun.crypto.provider.SunJCE
security.provider.6=sun.security.jgss.SunProvider
security.provider.7=com.sun.security.sasl.Provider
security.provider.8=org.jcp.xml.dsig.internal.dom.XMLDSigRI
security.provider.9=sun.security.smartcardio.SunPCSC
security.provider.10=sun.security.mscapi.SunMSCAPI

(见位置 1 的新的)

然后完全重启coldfusion服务。 然后就可以了

java SSLPoke www.onlineumfragen.com 443 (or of course your url!)

并享受这种感觉... 而且当然

多么美好的夜晚和多么美好的一天。希望这会(部分或全部)对那里的人有所帮助。如果您有任何问题,请发送邮件至 info ...(上面的域)。

【讨论】:

【参考方案16】:

我们返回了完全相同的异常错误,在上网数小时后修复它很容易。

我们下载了我们在 oracle.com 上可以找到的最高版本的 jdk,安装它并将 Jboss 应用服务器指向安装的新 jdk 的目录。

重启Jboss,重新处理,问题修复!!!

【讨论】:

【参考方案17】:

Bamboo 5.7 + Gradle 项目 + Apache 出现此错误。 Gradle 尝试通过 SSL 从我们的一台服务器获取一些依赖项。

解决方案:

    生成 DH 参数:

使用 OpenSSL:

openssl dhparam 1024

示例输出:

-----BEGIN DH PARAMETERS-----
MIGHfoGBALxpfMrDpImEuPlhopxYX4L2CFqQov+FomjKyHJrzj/EkTP0T3oAkjnS
oCGh6p07kwSLS8WCtYJn1GzItiZ05LoAzPs7T3ST2dWrEYFg/dldt+arifj6oWo+
vctDyDqIjlevUE+vyR9MF6B+Rfm4Zs8VGkxmsgXuz0gp/9lmftY7AgEC
-----END DH PARAMETERS-----

    将输出附加到证书文件(对于 Apache - SSLCertificateFile 参数)

    重启apache

    重启 Bamboo

    再次尝试构建项目

【讨论】:

【参考方案18】:

我曾经在使用 IBM JDK 的 java SVN 客户端访问 svn.apache.org 时遇到类似的错误。目前,svn.apache.org 使用客户端密码偏好。

在使用数据包捕获/javax.net.debug=ALL 运行一次后,我能够将一个 DHE 密码列入黑名单,并且一切正常(ECDHE 是协商的)。

.../java/jre/lib/security/java.security:
    jdk.tls.disabledAlgorithms=SSL_DHE_RSA_WITH_AES_256_CBC_SHA

在不容易更改客户端时的快速修复。

【讨论】:

【参考方案19】:

最近我遇到了同样的问题,将 jdk 版本从 1.6.0_45 升级到 jdk1.7.0_191 后,问题解决了。

【讨论】:

【参考方案20】:

如果服务器支持不包含 DH 的密码,您可以强制客户端选择该密码并避免 DH 错误。如:

String pickedCipher[] ="TLS_RSA_WITH_AES_256_CBC_SHA";
sslsocket.setEnabledCipherSuites(pickedCipher);

请记住,从长远来看,指定精确的密码很容易被破坏。

【讨论】:

【参考方案21】:

对我来说,以下命令行解决了这个问题:

java -jar -Dhttps.protocols=TLSv1.2 -Ddeployment.security.TLSv1.2=true -Djavax.net.debug=ssl:handshake XXXXX.jar

我使用的是 JDK 1.7.0_79

【讨论】:

我在使用 JDK 1.7.0_xx 时遇到了同样的情况。默认问题将在使用 jdk 1.8 后得到解决。但有时我们无法快速升级 jdk。所以我的问题在添加系统配置后解决了: System.setProperty("https.protocols", "TLSv1.2"); System.setProperty("deployment.security.TLSv1.2", "true");

以上是关于为啥 SSL 握手会给出“无法生成 DH 密钥对”异常?的主要内容,如果未能解决你的问题,请参考以下文章

为啥我找不到 SSL 握手的信任库?

为啥 java 在 SSL 握手期间不发送客户端证书?

请教SSL握手过程中,为啥抓包找不到pre-master-key

图解SSL/TLS协议

如何修复“SSL 握手失败”

SSL协议HTTP和HTTPS和区别