我使用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中的JCE密钥限制为1024位。我们已经确认我们的私钥和公钥实际上都是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个不同的模数值,并且当它们在base 64或base 10中编码时,它们都只有最后几个字符不同。模数值被重用,有时只有一次,有时十几次,但有很多不同的值,所以密钥既不是为一次性使用而生成的,也不是一次生成并永久重用。
这是(让服务器发送许多不同的密钥,在数值上非常接近,有一些重用但许多唯一值)在任何条件下的预期行为,尤其是当客户端使用2048位密钥时这种预期行为但是服务器使用1024位密钥?自从我上周开始调查以来,我对Diffie-Hellman小组交流一无所知,所以也许这就是它的工作方式,但对我来说似乎很奇怪。
此外,SSH标准是否指定了在这些情况下应如何生成密钥的任何内容?我还没有找到远方使用的SSH服务器(我怀疑OpenSSH,但不确定并且不知道是什么版本),但我很有希望可能有一些标准强制使用与请求大小相同的密钥(在1 ^(n-1)和1 ^ n - 1之间),并且远程服务器可能有强制选择或我可以提交一个针对他们的bug来让他们改变行为。我可能还会提交一个针对JDK的错误,允许n-1位的密钥,第一位使用0-padding。
任何人都可以给予任何指导,我们将不胜感激。
我还将此问题发布到了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位素数而不是?
答案 0 :(得分:4)
我们修复了类似的症状:
Security.insertProviderAt(new BouncyCastleProvider(), 1)
我们使用Jsch 0.1.54并看到:
java.security.InvalidAlgorithmParameterException:DH密钥大小必须是64的倍数,并且只能在512到4096(含)之间。不支持特定密钥大小2047
答案 1 :(得分:0)
我最终通过禁用使用Diffie-Hellman组密钥交换变体的密钥交换算法解决了这个问题。 @Brian Low似乎通过使用BouncyCastle而不是JDK的内置安全提供程序来解决它。
我认为这两个解决方案都没有解决根本问题(这似乎是JDK中他们接受的密钥大小或OpenSSH中的错误,因为它们生成的密钥大小),但我都不知道也不是我的项目非常关心花费时间和金钱试图迫使其中一方取得这个问题的所有权。