【问题标题】:Java SSL DH Keypair Generation - Prime Size ErrorJava SSL DH 密钥对生成 - 素数大小错误
【发布时间】:2014-02-21 22:23:18
【问题描述】:

我目前正在我的网络应用程序中实现 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 库,所以我认为如果不分叉和更改 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,正如我所提到的,因此可能没有多大用处)。令牌端点是 https://ssl.reddit.com/api/v1/access_token 使用 POST。正如我上面所说,这适用于我的测试机器。

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

【问题讨论】:

标签: java ssl oauth-2.0 openshift scribe


【解决方案1】:

首先,“无限力量”在这里是无关紧要的。这将解决完全不同的问题,即您不能使用使用 AES-256 的密码套件(如果对等方坚持他们根本不能握手)。 JVM的位大小也无关紧要;对 DH 的这种(并非真正合理的)限制在 SunJCE 中的“到处运行”字节码中。

您可以将 BouncyCastle 用作加密提供程序,而无需更改执行 SSL 连接的代码(在您的情况下为 Scribe),但根据我所读到的内容,使 BC 成为首选提供程序会导致其他问题。如果您仍然想尝试,请将 bcprov-version.jar 放入 JRE/lib/exit 并编辑 JRE/lib/security/java.security;或者把它放在你的类路径中的任何地方,让你的初始化代码调用 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 rpm 中使用 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,您可以替换一个调整过的工厂,该工厂会按照问题 #6851461 的行更改 EnabledCiphers 列表(尽管对于具有良好公共证书的主机,您不需要该解决方案的 custom-trustmanager 部分)。

【讨论】:

  • 非常感谢您的详细回复!我在本地机器和 OpenShift 上运行了您的代码。在本地,它返回“TLS_ECDHE_RSA_WITH_RC4_128_SHA”,但在 OpenShift 上我收到“无法生成 DH 密钥对”错误。我也在 ssllabs.com/ssltest 上检查了它,它还返回 ECDHE 进行握手。您是否认为这是 ECDHE 因任何原因在 OpenShift 上被禁用/不存在的情况?我可以尝试 BouncyCastle,尽管 OpenShift 允许只读访问 JRE 文件,但您的类路径建议是一个选项。还发现了这个bugzilla.redhat.com/show_bug.cgi?id=1052363
  • (抱歉耽搁了)那个bugzilla是针对openssl的,显然是服务器端(很可能是httpd?)。但这肯定与缺乏对 ECDHE 的支持是一致的。是的,由于已知服务器支持 ECDHE,那么 OpenShift 显然没有请求它——或者不正确:如果 OpenShift/OpenJDK 中的 SSL 提供程序在 ECDHE 套件之前提供 DHE 套件,或者它仅提供有限的曲线。但是
  • (修复,太慢了)那个bugzilla是针对openssl(C)和httpd服务器端的,你的问题是Java客户端。但这肯定与不希望支持 ECDHE 是一致的。由于您想要的服务器支持 ECDHE,OpenShift Java 显然没有请求它(或不正确,但不太可能)。尝试一个只执行 javax.crypto.KeyAgreement.getInstance("ECDH") 的程序,如果抛出 - 没有提供者 - 你可以修改我上面所说的和 .addProvider (new bouncy) 以便标准(?) JSSE 仍然被选中,但从 Bouncy 获得 ECC。如果不是,我这一轮的空间不足。
  • 感谢您的跟进。 getInstance("ECDH") 在 OpenShift 上抛出“NoSuchAlgorithmException:算法 ECDHE 不可用”。正如您所建议的,我将 BouncyCastle 添加为提供程序并连接到 ssl.reddit.com。事实上,这确实连接成功并通过 Reddit OAuth 身份验证过程工作,这样就解决了!但是,仅仅因为 OpenShift 出于某种原因不支持 ECDHE 而不得不添加另一个库似乎很可惜。无论哪种方式,我现在都会使用 BouncyCastle 作为提供者。我还在 OpenShift 论坛上发布了有关它的信息。再次感谢,我从您的帖子中学到了很多!
【解决方案2】:

我知道我回答这个问题已经很晚了,但我一直在努力解决类似的问题,然后解决了。我的解决方案是针对 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 然后重新排序您可能拥有的所有其他提供商。

【讨论】:

  • 谢谢..这个解决方案也适用于 Linux-Mint (Java 1.6)。
  • 我确认它也适用于 CentOS 7 和 Java 1.6。
【解决方案3】:

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

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

    <dependency>
        <groupId>org.bouncycastle</groupId>
        <artifactId>bcprov-jdk15on</artifactId>
        <version>1.54</version>
    </dependency>
    
  2. 在我做任何 SSL 工作之前,我将 BouncyCastle 提供者作为第一个提供者添加到列表中:

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

就是这样。现在我与具有 4096 位 DH 参数的站点的连接按预期工作(我使用的是 Apache HTTP 客户端)。这也应该适用于 jdk 7。

【讨论】:

  • 这对我来说非常有效。我喜欢它,因为它是一行代码,不需要对 JVM 进行任何更改。
【解决方案4】:

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

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

class Test {

public static void main(String[] args) {

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

}

}

【讨论】:

  • 感谢您的回复!我假设 OpenShift 使用的是 32 位 Java,如 java -version 输出所示。这可能是问题吗?我真的看不出它应该如何影响它,但我会尝试在我的测试机器上使用 32 位版本。我还附上了您要求的代码,但它正在使用 Scribe,所以可能用处不大
  • 也许这个错误 (bugs.java.com/bugdatabase/view_bug.do?bug_id=6521495) 但在 OpenJDK 方面,在 32 位版本中没有修复?
  • 你提到版本后我就是这么想的,但我只是在我的本地机器上使用 32 位 OpenJDK 版本测试它,它运行良好。
猜你喜欢
  • 1970-01-01
  • 2012-05-28
  • 2020-06-25
  • 1970-01-01
  • 2011-01-28
  • 1970-01-01
  • 2015-11-20
  • 1970-01-01
相关资源
最近更新 更多