猿问

在 java11 上播种后 SHA1PRNG SecureRandom 行为不同

我正在使用java.security.SecureRandomangorithm"SHA1PRNG"来生成加密密钥。这是用于加密次要数据的历史代码。然而,当我们从java8切换到java11时,我们的代码停止工作。这是重现这种情况的测试用例:


@Test

void srEncryptionSeedTest() throws NoSuchAlgorithmException

{

    final long versionSalt = 1850498708034063014L;

    final long customSalt  = -919666267416765972L;


    final SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");

    sr.setSeed(versionSalt);

    final long l1 = sr.nextLong();

    final long l2 = sr.nextLong();


    sr.setSeed(customSalt);

    final long k1 = sr.nextLong();

    final long k2 = sr.nextLong();


    // check l1 and l2

    Assert.assertEquals(l1, 6338935000439666355L);

    Assert.assertEquals(l2, -7355545655857008441L);


    // Seeding

    // check k1 and k2

    Assert.assertEquals(k1, -2226559466996804670L); // 

    Assert.assertEquals(k2, -3123855249705841778L);

}

这在 java11 上工作正常,但在 java8 上我们有k1=-4273821888324981770and k2=3053251164341917236,所以测试失败。如您所见,在生成相同数量的相同随机数后设置完全相同的种子后测试开始失败,所以我怀疑 RNG 的状态不同,但调试对我没有帮助(我不明白为什么这不一样)。这可以很容易地在任何操作系统上重现。


关于 Java8 JVM 的一些事实:


java.vendor -> Oracle Corporation // same goes on OpenJDK builds

java.version -> 1.8.0_202-ea // same goes on 1.8.0_181

java.vm.info -> mixed mode

java.specification.version -> 1.8

java.runtime.name -> Java(TM) SE Runtime Environment

关于 Java11 JVM 的一些事实:


java.vendor -> AdoptOpenJDK

java.version -> 11.0.3

java.vm.info -> mixed mode

java.specification.version -> 11

java.runtime.name -> OpenJDK Runtime Environment

任何帮助将不胜感激。


达令说
浏览 186回答 1
1回答

哔哔one

[免责声明]: 不要这样做(除非你想要向后兼容)。如果你想要可预测性,你应该基于可靠的 RNG 实施你的解决方案,我也是。但不幸的是我们必须支持旧文件版本的格式,而且这些文件不包含任何敏感或个人数据,但我们不希望用户更改此数据,因为它以类似文本的格式存储,因此很容易被更改。我没有实现自己的“SHA1PRNG”,因为它太难了(在评论中提到)。相反,我破解了较新的 PRNG 版本,使其与旧版本完全一样。原因是因为 java9 OpenJDK 的创建者决定在每次调用setSeed()secureRandomSpi时使新版本重置整数字段的值,而旧版本则没有。remCountSecureRandom.如果你想实现这个hack,你要做的第一件事就是SecureRandom通过调用检查实例是否真的是SUN的“SHA1PRNG” "SUN".equals(secureRandom.getProvider().getName()) && "SHA1PRNG".equals(secureRandom.getAlgorithm());,然后通过反射获取它的SPI并保存它的remCount字段值。然后您可以打电话setSeed(),然后将保存的值安装回现场remCount。我不想在这里发布这段晦涩难懂的代码,但你已经明白了。谢谢。
随时随地看视频慕课网APP

相关分类

Java
我要回答