Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java 7 and Could not generate DH keypair

Tags:

java

ssl

jce

I read a previous post regarding the error 'Could not generate DH keypair' fired when the server sents a key longer than 1024 bits. Downloading the JCE unlimited jars should fix this issue. In the test environment I have I encountered the following, for the same web server if I use Java 6 I don't get any errors when performing the https query but if I use Java 7 then I get 'Could not generate DH keypair'.

I tried replacing the jar files for JCE unlimited but still get the same error. The bug is reported since 2007, but why does it run for Java 6 and not for Java 7? Are the files to download not the proper ones? I got the link from a previous post Java: Why does SSL handshake give 'Could not generate DH keypair' exception?.

At this point I don't know what to do. If I try to load the BouncyCastle provider I get an ArrayOutOfIndex exception. My server only allows DH algorithm so I can't use another algorithm like suggested in the above post.

like image 450
user1408089 Avatar asked May 21 '12 14:05

user1408089


4 Answers

Some additions or clarifications:

(Suncle) Java 7 since 7u09 uses a more sensible consistent order of ciphersuites by default, unlike the seemingly random order in 7u04. (I don't have tests between 04 and 09.) This order puts ECDHE and plain-RSA (aka akRSA) before DHE, and thus avoids the problem if AND ONLY IF the server supports ECDHE or RSA and agrees to client preference. (Or ECDH-fixed, but practically nobody uses that.) If the server insists on DHE (for whatever reason) AND uses DH>1024 bits, you still have the problem.

If the asker (or anyone else) connects to a server that truly requires integer-DH (and not ECDH or RSA), the only way to work with Java before 8 is to get the server to use DH 1024-bit. Which AFAWK is technically secure for a few more years, but with a thin margin it is prohibited by important authorities like NIST (see Special Pub 800-57 at csrc.nist.gov). (Even RSA 1024 isn't actually broken yet, but it probably will be soon and so is prohibited.)

The "unlimited strength policy" is not relevant to this problem, or at least not directly, and the good answers to #6851461 did not say it is. It does not change the restriction on DH parameters in SunJCE, which is (wrongly) treated as a standard issue not a strength issue. (Specifically it takes the restrictions that used to be correct for DSA, and applies them to DH.) It does enable AES-256 and SHA-2 (only for TLSv1.2) suites, and given a sufficiently strange preference list, that might change the selection result from DHE (fails) to non-DHE (works).

You don't need to go entirely back to the Java 6 list, you just need to prioritize other key exchanges over DHE, or for a recalcitrant server drop DHE entirely. You definitely should NOT go back to enabling any EXPORT or single-DES suites, unless absolutely needed for a legacy server; they have been NOT SECURE for several years now, and remained enabled by default in 6 far longer than they should have.

like image 99
dave_thompson_085 Avatar answered Nov 08 '22 00:11

dave_thompson_085


I stumbled on the same issue with SSLScokets and I think I identified the reason for this regression with Java 7. The reason comes to the ciphers negotiated between the client and the server.

By default Java 6 enables these ciphers for a TLS connection (in priority order) :

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

And Java 7 enables these ciphers:

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

Ciphers using Diffie-Hellman come in higher priority on Java 7, but they don't seem to support keys longer than 1024 bits unless the strong crypto package is installed.

The workaround I used was to specify the ciphers enabled by Java 6 on the 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();
like image 33
Emmanuel Bourg Avatar answered Nov 07 '22 23:11

Emmanuel Bourg


Given you are using the latest java edition and still get the error, you can change a setting in java.security (e.g. in folder C:\Program Files\Java\jre1.8.0_xx\lib\security

# Example:
#   jdk.tls.disabledAlgorithms=MD5, SSLv3, DSA, RSA keySize < 2048
    jdk.tls.disabledAlgorithms=SSLv3, RC4

Add DH as disabled algorithm in jdk.tls.disabledAlgorithms

    jdk.tls.disabledAlgorithms=SSLv3, RC4, DH

Restart tomcat or rerun your program.

like image 45
Tolomir Avatar answered Nov 08 '22 00:11

Tolomir


We were also running into this problem with Java7 and Java8. We also used a workaround similar to Emanual Borg's suggestions. But our goal was to avoid hardcoding a fixed list of CipherSuites. So we tried removing the entries which caused the problem (by trial and error...).

String[] enabledCipherSuites = socket.getEnabledCipherSuites();

// avoid hardcoding a new list, we just remove the entries
// which cause the exception
List<String> asList = new ArrayList(Arrays.asList(enabledCipherSuites));

// we identified the following entries causeing the problems
// "Could not generate DH keypair"
// and "Caused by: java.security.InvalidAlgorithmParameterException: Prime size must be multiple of 64, and can only range from 512 to 1024 (inclusive)"
asList.remove("TLS_DHE_RSA_WITH_AES_128_CBC_SHA");
asList.remove("SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA");
asList.remove("TLS_DHE_RSA_WITH_AES_256_CBC_SHA");

String[] array = asList.toArray(new String[0]);
socket.setEnabledCipherSuites(array);

Question: Does anybody see a problem with this approach?

Btw: In case you are using Apache HTTPClient, then https://issues.apache.org/jira/browse/HTTPCLIENT-1111 is interesting which shows how to set the CipherSuites (starting with HTTPClient v4.2) via the method

SSLConnectionSocketFactory() {...}.prepareSocket(SSLSocket)

Update 2015/10/31: To help better understand the context where to use this, here as full pseudo-code example where you see how to hook-in to override the prepareSocket() method:

HttpClientBuilder builder = HttpClients.custom();

SSLContextBuilder sslContextBuilder = SSLContexts.custom();
SSLContext sslContext = sslContextBuilder.build();

SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext, hostNameVerfier)
{


    protected void prepareSocket(SSLSocket socket) throws IOException {

    // Workaround to use different order of CipherSuites used by Java6 in order
        // to avoid the the problem of java7 "Could not generate DH keypair"
        String[] enabledCipherSuites = socket.getEnabledCipherSuites();

        // but to avoid hardcoding a new list, we just remove the entries
        // which cause the exception (via TrialAndError)
        List<String> asList = new ArrayList(Arrays.asList(enabledCipherSuites));

        // we identified the following entries causeing the problems
        // "Could not generate DH keypair"
        // and "Caused by: java.security.InvalidAlgorithmParameterException: Prime size must be multiple of 64, and can only range from 512 to 1024 (inclusive)"
        asList.remove("TLS_DHE_RSA_WITH_AES_128_CBC_SHA");
        asList.remove("SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA");
        asList.remove("TLS_DHE_RSA_WITH_AES_256_CBC_SHA");

        String[] array = asList.toArray(new String[0]);
        socket.setEnabledCipherSuites(array);

    };
};

Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory> create().register("https", sslsf).build();

PoolingHttpClientConnectionManager conman = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
builder.setConnectionManager(conman);

CloseableHttpClient httpClient =  builder.build();

Be careful We are using this piece of code only in a context where the user explicitly enables to trust self-signed certificates (e.g. for test-environments etc.). If you don't want to do this, then better don't mess with the SSL-stuff.

like image 2
Christoph Avatar answered Nov 07 '22 23:11

Christoph