DHGEX 在 Java 8 下使用 2048 位密钥失败,但使用 1024 位密钥成功



我正在使用JSCH 0.1.53连接到远程SSH服务器,该服务器使用1024位RSA密钥。 当我们也使用 1024 位 RSA 密钥时,我们能够成功连接到远程服务器,但是当我们生成更强的 2048 位密钥时,我们停止了连接。 我们收到一条错误消息,内容为"素数大小必须是 64 的倍数,并且只能从 512 到 2048",并且源自对 DHGEX.java(Diffie-Hellman Group EXchange )的调用。

我们运行的是 Java 1.8,错误消息正确指定的最大位大小为 2048,因此问题不在于 Java 1.6 和 1.7 中 1024 位的 JCE 密钥限制。 我们已经确认我们的私钥和公钥实际上是2048位,通过openssl rsa -text -noout -in id_rsa和ssh-keygen -lf id_rsa.pub。

由于我们这边一切看起来都很好,我开始在 JSCH 代码中添加调试行并重新编译 JAR,我最终能够确定在密钥交换期间传递给我们的模数实际上是 2047 位长。 现在,长度为 2047 位并不意味着您没有生成 2048 位密钥,或者它不如实际包含 2048 位的密钥强,它只是意味着您碰巧得到了两个素数,它们相乘到第一个位是 0 的东西。所以这是预期的行为(某些时候),JCE 检查可能应该是 (n % 64 == 0 || n % 64 == 63)。 但是 JCE 在这一点上是一个坚持者,所以它拒绝这个密钥,因为它的长度不是它认为有效的。

基于此,我认为我发现了问题:远程服务器生成了一个仅包含 2047 位的 2048 位密钥,因此他们只需要生成一个新的密钥(并继续这样做,直到他们得到一个真正的 2048 位)。 但是当我向他们的管理员询问时,他们坚持认为他们使用的是 1024 位密钥,事实上,当您通过 SSH 时,这就是您在known_hosts文件中得到的内容。 所以这似乎毕竟不是原因。

因此,我

开始记录包含他们发送给我们的缓冲区的内容,并提取p和g值(模数和组),我发现在短短几天的测试中,有33个不同的模量值,当以64或10为基数编码时,所有这些值都只差最后几个字符。 Modulii 值被重用,有时只使用一次,有时十几次,但有很多不同的值,因此键既不是一次性生成的,也不是生成一次并永远重复使用的。

这(让服务器发送许多不同的密钥,这些密钥在数字上非常接近,有一些重用但有许多唯一值)在任何情况下都是预期行为,尤其是当客户端使用 2048 位密钥但服务器使用 1024 位密钥时,这是预期行为? 自从我上周开始调查以来,除了我读到的内容之外,我对Diffie-Hellman小组交流一无所知,所以也许这就是它的工作原理,但对我来说似乎很奇怪。

另外,SSH 标准是否指定了在这种情况下应如何生成密钥的任何内容? 我还没有发现远端正在使用什么SSH服务器(我怀疑OpenSSH,但不确定,也不知道是什么版本),但我希望可能有一些标准强制使用与请求大小相同的密钥(在1^(n-1)和1^n - 1之间), 并且远程服务器可能有一个选项来强制执行此操作,或者我可以针对它们提交错误以让他们更改行为。 我可能还会针对 JDK 提交一个错误,以允许 n-1 位的键,第一个位填充 0。

任何人可以提供的任何指导将不胜感激。

我也把这个问题发布到JSCH邮件列表:https://sourceforge.net/p/jsch/mailman/message/35042955/

更新:

经过进一步阅读,我相信 Diffie-Hellman 的前向保密特性意味着每个会话将使用不同的素数(通常来自存储在/etc/ssl/moduli 等地方的预生成集合)(来源:https://en.wikipedia.org/wiki/Diffie%E2%80%93Hellman_key_exchange#Forward_secrecy),并且使用的素数实际上不是 RSA 密钥(来源:https://stackoverflow.com/a/23346185/1247705),因此看到许多不同的 p 值这一事实似乎不再是一个问题。 我仍然对它们的价值如此接近感到惊讶,但也许这也是意料之中的。

另一端是使用Solaris SSH 1.1.4(据我所知,它基于OpenSSH)作为SSH守护程序。 作为 Diffie-Hellman 密钥交换的一部分,该守护进程是否会通过 2047 位素数,是否可以做些什么来让它发送 2048 位素数?

我们通过以下方式修复了类似的症状:

Security.insertProviderAt(new BouncyCastleProvider(), 1)

我们使用 Jsch 0.1.54 并看到:

java.security.InvalidAlgorithmParameterException:DH 密钥大小必须是 64 的倍数,并且只能介于 512 到 4096(含)之间。不支持特定密钥大小 2047

可能相关的是 JDK-8164963:使用 JSCH 库升级 JDK 后出现无效算法参数异常素数大小问题

我最终通过禁用使用 Diffie-Hellman 组密钥交换变体的密钥交换算法来解决此问题。 @Brian Low似乎通过使用BouncyCastle而不是JDK的内置安全提供程序来解决它。

我认为这两种方法都是无法解决根本问题的解决方法(这似乎是JDK中他们接受的密钥大小的错误或OpenSSH中他们生成的密钥大小的错误),但我和我的项目都不够关心花费时间和金钱试图迫使其中一个拥有这个问题。

最新更新