Java SSL DH Keypair Generation - Prime Size Error
Asked Answered
I

4

6

I'm currently implementing Reddit OAuth2 login into my web app. The handshake and token exchange work fine when testing locally but when running on the server (hosted on 'OpenShift' DIY cartridge) I get the following error:

java.security.InvalidAlgorithmParameterException: Prime size must be 
multiple of 64, and can only range from 512 to 1024 (inclusive)

Which is results in

java.lang.RuntimeException: Could not generate DH keypair 

I've been searching most of the day and have found different solutions ranging from changing Java version to using BouncyCastle. However, I'm using the Scribe library so I don't think I can implement BouncyCastle without forking and changing the base of scribe, which kind of defeats it's purpose.

Installing JCE Unlimited Strength also came up but I can't do that on OpenShift as there's no root access (might be able to get one of their team to do it).

The java versions in use are (taken from java -version):

Local Testing Machine:

java version "1.7.0_51"
OpenJDK Runtime Environment (IcedTea 2.4.4) (7u51-2.4.4-1ubuntu1)
OpenJDK 64-Bit Server VM (build 24.45-b08, mixed mode)

OpenShift Server:

java version "1.7.0_51"
OpenJDK Runtime Environment (rhel-2.4.4.1.el6_5-i386 u51-b02)
OpenJDK Server VM (build 24.45-b08, mixed mode)

I'm at a loss as to what I can do to solve this. Hopefully I'm being stupid or am misunderstanding something, so any possible solutions would be great!

--

EDIT 1

The request code that returns the error (using Scribe, as I mentioned, so might not be much use). The token endpoint is https://ssl.reddit.com/api/v1/access_token using POST. As I said above, this works on my testing machine.

OAuthRequest request = new OAuthRequest(getAccessTokenVerb(), getAccessTokenEndpoint());

request.addHeader("Authorization", "Basic"
    +Base64.encode((config.getApiKey()+":"+config.getApiSecret()).getBytes()));

request.addBodyParameter("state", "none");
request.addBodyParameter(OAuthConstants.SCOPE, config.getScope());
request.addBodyParameter(OAuthConstants.CLIENT_ID, config.getApiKey());
request.addBodyParameter(OAuthConstants.REDIRECT_URI, config.getCallback());
request.addBodyParameter(OAuthConstants.CODE, verifier.getValue());
request.addBodyParameter("grant_type", "authorization_code");

Response response = request.send();  // Errors here from Request.createConnection in the Scribe code
return getAccessTokenExtractor().extract(response.getBody());
Isopiestic answered 29/1, 2014 at 21:0 Comment(2)
Can you post the code you are using to generate the keypair so we can test with it?Xylophagous
See also #6851961Playbill
M
6

First, "Unlimited Strength" is irrelevant here. That would fix the entirely different problem that you can't use cipher suites using AES-256 (and if the peer insists on them can't handshake at all). Also bitsize of the JVM doesn't matter; this (not really justified) restriction on DH is in the "run-everywhere" bytecode in SunJCE.

You can use BouncyCastle as a crypto provider without changing the code that does SSL connections (in your case Scribe), but from what I've read making BC the preferred provider causes other problems. If you want to try anyway, either put bcprov-version.jar in JRE/lib/exit and edit JRE/lib/security/java.security; or put it anywhere in your classpath and have your init code call java.security.Security.insertProviderAt (new org.bouncycastle.jce.provider.BouncyCastleProvider(), position);

I suggest starting from why your local system DOES work. When I try ssl.reddit.com with openssl, it supports both ECDHE-RSA (with P-256) and DHE-RSA with dh 2048 bits. Suncle Java 7 does support and prefer ECDHE, and I would expect OpenJDK also does but maybe not or maybe sometimes not; I know RedHat until recently nobbled ECC in its rpms of openssl, and it wouldn't astonish me if they did so in openjdk also. If you compile and run the following (with ssl.reddit.com 443) it will tell what suite gets negotiated on your system, using all default SSL settings of your JRE (which I expect/hope Scribe is also using):

//nopackage DThompson 2012.08.13b
import java.net.InetSocketAddress;
import java.net.Socket;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;

public class JustBConnectSSL {

    /* (optionally bind and) just make SSL connection, for testing reach and trust
     * uses default providers, truststore (normally JRE/lib/security/[jsse]cacerts), 
     * and keystore (normally none), override with -Djavax.net.ssl.{trust,key}Store* 
     */
    public static void main (String[] args) throws Exception {
        if( args.length < 2 ){ System.out.println ("Usage: tohost port [fromaddr [fromport]]"); return; }
        Socket sock = SSLSocketFactory.getDefault().createSocket();
        if( args.length > 2 )
            sock.bind (new InetSocketAddress (args[2], args.length>3? Integer.parseInt(args[3]): 0));
        sock.connect (new InetSocketAddress (args[0], Integer.parseInt(args[1])));
        System.out.println (sock.getInetAddress().getHostName() + " = " + sock.getInetAddress().getHostAddress());
        ((SSLSocket)sock).startHandshake();
        System.out.println ("connect okay " + ((SSLSocket)sock).getSession().getCipherSuite());
    }
}

If test gets _DHE_RSA_something, the crypto providers in your JRE must be different from the Suncle ones, either changed by Ubuntu or some customization or patch on your system. If test gets _ECDHE_RSA_something but OpenShift doesn't, they may have disabled ECC/ECDHE somehow. If they can enable that's best (ECDHE-P-256 is at least as secure and probably more efficient than DH-2048). Otherwise until Oracle fixes this (apparently in 8) the only way I think can be relied on is to disable DHE suites (and drop back to plain RSA, which may not be safe against NSA); that is simplest in the code that actually creates the SSLSocket, but if Scribe (like most java web clients) uses URL -> HttpsUrlConnection with its default SSLSocketFactory you can substitute a tweaked factory that changes the EnabledCiphers list along the lines of question #6851461 (although for a host with a good public certificate you don't need the custom-trustmanager parts of that solution).

Merci answered 1/2, 2014 at 11:22 Comment(4)
Thanks a lot for your detailed reply! I ran your code on both my local machine and OpenShift. Locally, it returns 'TLS_ECDHE_RSA_WITH_RC4_128_SHA' but on OpenShift I get the 'Could not generate DH keypair' error. I checked it on ssllabs.com/ssltest as well and it also returns ECDHE for the handshake. Do you think it is a case of ECDHE being disabled/not present for whatever reason on OpenShift? I could try BouncyCastle, although OpenShift allows read-only access to the JRE files but your class path suggestion is an option. Also found this bugzilla.redhat.com/show_bug.cgi?id=1052363Isopiestic
(Sorry for delay) That bugzilla is for openssl and apparently server side (most likely httpd?). But it is certainly consistent with a lack of support for ECDHE. Yes, since the server is known to support ECDHE then OpenShift clearly isn't requesting it -- or not correctly: there are two gotchas that could interfere here, if the SSL provider in OpenShift/OpenJDK offers DHE suites BEFORE ECDHE suites, or it offers only limited curves. ButMerci
(fix, too slow) That bugzilla is for openssl (C) and apparently httpd server side, your problem is Java client. But it is certainly consistent with no desire to support ECDHE. Since the server you want supports ECDHE the OpenShift Java clearly isn't requesting it (or not correctly but that's unlikely). Try a program that just does javax.crypto.KeyAgreement.getInstance("ECDH") and if that throws -- there is no provider -- you could modify what I said above and .addProvider (new bouncy) so that standard(?) JSSE still is selected but gets ECC from Bouncy. If not I'm out of space on this round.Merci
Thanks for following up. getInstance("ECDH") threw "NoSuchAlgorithmException: Algorithm ECDHE not available" on OpenShift. As you suggested, I added BouncyCastle as the provider and connected to ssl.reddit.com. This did in fact connect successfully and works through the Reddit OAuth authentication process, so that solves it! Seems a shame, though, to have to add another library just because OpenShift for some reason doesn't support ECDHE. Either way, I'll use BouncyCastle as the provider for now. I've also posted on the OpenShift forum about it. Thanks again, I learned a lot from your posts!Isopiestic
I
3

I know I am very late to answer this but I was struggling with the similar problem and then solved it. My solution is for MAC-OS.

  1. Install Bouncy Castle jar from http://www.bouncycastle.org/latest_releases.html, in /Library/Java/JavaVirtualMachines//Contents/Home/jre/lib/ext
  2. edit java.security file add the below line security.provider.2=org.bouncycastle.jce.provider.BouncyCastleProvider and then reorder all the other providers you may have.
  3. voila
Interpretive answered 28/1, 2016 at 18:53 Comment(2)
Thanks..This solution is working for Linux-Mint (Java 1.6) as well.Truancy
I confirm it is working with CentOS 7 and Java 1.6 too.Chaudoin
I
2

I solved the problem on oracle java 8 by switching to bouncycastle provider for ssl/tls:

  1. Added bouncycastle to my project

    <dependency>
        <groupId>org.bouncycastle</groupId>
        <artifactId>bcprov-jdk15on</artifactId>
        <version>1.54</version>
    </dependency>
    
  2. Before I do any SSL stuff, I add the BouncyCastle provider as 1st provider to the list:

    Security.insertProviderAt(new BouncyCastleProvider(),1);
    

That's all. Now my connections to sites with 4096 bit DH parameters works as expected (I'm using Apache HTTP Client). This should also work with jdk 7.

Influx answered 9/8, 2016 at 9:55 Comment(1)
This worked perfectly for me. I like it because it's one line of code and doesn't require any changes to the JVM.Form
X
0

Not sure if it matters, but the version of OpenJDK running on OpenShift is 32 bit, you can view this by using the System.getProperty("sun.arch.data.model") in your code.

I wrote a quick class to just output the bit-ness and compiled it on the OpenShift gear and ran it and got 32

class Test {

public static void main(String[] args) {

System.out.println(System.getProperty("sun.arch.data.model"));

}

}
Xylophagous answered 30/1, 2014 at 20:5 Comment(3)
Thanks for your reply! I assumed OpenShift was using 32 bit Java as is shown by the java -version output. Could that be the problem? I can't really see how it should affect it but I'll try using a 32 bit version on my testing machine. I've also attached the code you asked for, it is using Scribe though, so might not be much useIsopiestic
Maybe this bug (bugs.java.com/bugdatabase/view_bug.do?bug_id=6521495) but on the OpenJDK side, was not fixed in the 32 bit version?Xylophagous
That's what I thought after you mentioned the version, but I just tested it on my local machine using the 32 bit OpenJDK version and it worked fine.Isopiestic

© 2022 - 2024 — McMap. All rights reserved.