Java SSL DH密钥对生成 – Prime大小错误

我目前正在将Reddit OAuth2登录到我的网络应用程序中。 握手和令牌交换在本地测试时工作正常但在服务器上运行时(托管在’OpenShift’DIY盒式磁带上)我收到以下错误:

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

结果如何

 java.lang.RuntimeException: Could not generate DH keypair 

我一直在搜索大部分时间,并找到了不同的解决方案,从更改Java版本到使用BouncyCastle。 但是,我正在使用Scribe库,所以我不认为我可以实现BouncyCastle而不需要改变划线的基础,这会破坏它的目的。

安装JCE Unlimited Strength也出现了,但是我不能在OpenShift上做到这一点,因为没有root访问权限(也许可以让他们的团队之一去做)。

使用的java版本(取自java -version ):

本地试验机:

 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服务器:

 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) 

我无法解决这个问题。 希望我是愚蠢的或误解的东西,所以任何可能的解决方案都会很棒!

编辑1

返回错误的请求代码(正如我所提到的,使用Scribe,因此可能没什么用处)。 令牌端点使用POST为https://ssl.reddit.com/api/v1/access_token 。 正如我上面所说,这适用于我的测试机器。

 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()); 

首先,“无限的力量”在这里无关紧要。 这将解决完全不同的问题,即你不能使用AES-256使用密码套件(并且如果对等方坚持它们根本不能握手)。 JVM的比特也无关紧要; 这个(不是很合理)对DH的限制是在SunJCE的“无处不在”字节码中。

您可以使用BouncyCastle作为加密提供程序,而无需更改执行SSL连接的代码(在您的案例中为Scribe),但是从我读过的内容中,BC首选提供程序会导致其他问题。 如果你想尝试,或者将bcprov-version.jar放在JRE / lib / exit中并编辑JRE / lib / security / java.security; 或者将它放在类路径中的任何位置,并让你的init代码调用java.security.Security.insertProviderAt(new org.bouncycastle.jce.provider.BouncyCastleProvider(),position);

我建议从你的本地系统工作的原因开始。 当我用openssl尝试ssl.reddit.com时,它支持ECDHE-RSA(带有P-256)和带有dh 2048位的DHE-RSA。 Suncle Java 7确实支持并且更喜欢ECDHE,我希望OpenJDK也可以,但可能没有,或者有时候没有; 我知道RedHat直到最近才在其openssl的rpms中使用ECC,如果他们在openjdk中这样做的话,我也不会感到惊讶。 如果您编译并运行以下命令(使用ssl.reddit.com 443),它将使用您的JRE的所有默认SSL设置(我希望/希望Scribe也在使用)来告诉您在系统上协商的套件:

 //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()); } } 

如果测试获得_DHE_RSA_something,则JRE中的加密提供程序必须与Suncle中的加密提供程序不同,可以由Ubuntu更改,也可以在系统上进行某些自定义或修补。 如果测试得到_ECDHE_RSA_something但OpenShift没有,他们可能会以某种方式禁用ECC / ECDHE。 如果他们能够做到最好(ECDHE-P-256至少与DH-2048一样安全,可能更有效)。 否则,直到Oracle修复此问题(显然是8),我认为可以依赖的唯一方法是禁用DHE套件(并回退到普通RSA,这可能对NSA不安全); 在实际创建SSLSocket的代码中最简单,但是如果Scribe(像大多数Java Web客户端一样)使用URL – > HttpsUrlConnection及其默认的SSLSocketFactory,您可以替换一个调整后的工厂,改变EnabledCiphers列表的问题#6851461(虽然对于拥有良好公共证书的主机,您不需要该解决方案的自定义信任管理器部分)。

我知道我很晚才回答这个问题,但我正在努力解决类似的问题然后解决了。 我的解决方案是针对MAC-OS。

  1. 从http://www.bouncycastle.org/latest_releases.html安装Bouncy Castle jar,在/ Library / Java / JavaVirtualMachines中// Contents / Home / jre / lib / ext
  2. 编辑java.security文件添加以下行security.provider.2 = org.bouncycastle.jce.provider.BouncyCastleProvider,然后重新排序您可能拥有的所有其他提供程序。

我通过切换到ssl / tls的bouncycastle提供程序解决了oracle java 8上的问题:

  1. 在我的项目中添加了bouncycastle

      org.bouncycastle bcprov-jdk15on 1.54  
  2. 在我做任何SSL之前,我将BouncyCastle提供程序添加为列表中的第一个提供程序:

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

就这样。 现在,我与4096位DH参数的站点的连接按预期工作(我正在使用Apache HTTP Client)。 这也适用于jdk 7。

不确定是否重要,但在OpenShift上运行的OpenJDK版本是32位,您可以在代码中使用System.getProperty(“sun.arch.data.model”)来查看。

我写了一个快速类来输出bit-ness并在OpenShift齿轮上编译并运行它得到32

 class Test { public static void main(String[] args) { System.out.println(System.getProperty("sun.arch.data.model")); } }