简体   繁体   中英

SHA1PRNG SecureRandom behavior is different after seeding on java11

I am using java.security.SecureRandom with "SHA1PRNG" angorithm to generate encryption keys. This is a historical code used to encrypt lesser important data. Nevertheless when we've switched from java8 to java11 , our code stopped working. Here's the test case made to reproduce the situation:

@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);
}

This works fine on java11, but on java8 we have k1=-4273821888324981770 and k2=3053251164341917236 , so the test fails. As you can see, the test starts failing after setting exactly the same seed after producing the same amount of same random numbers, so I suspect that the state of the RNG is different, but debugging wasn't helped me (I can not understand why it's different). This can be easily reproduced on any operation system.

Some facts about the 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

Some facts about the 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

Any help will be appreciated.

[DISCLAIMER]: Don't do it (unless you want backwards compatibility). You should implement your solutions based on reliable RNG if you want predictability, so did I. But unfortunately we had to support older file version's format, also these files does not contain any sensitive or personal data, but we didn't wanted users to alter this data since this is stored in the text-like format and thus easily could be altered.

I didn't implemented my own "SHA1PRNG", because it's too hard (mentioned in comments). Instead I hacked the newer PRNG version to behave exactly as the older one. The reason is the fact that since java9 OpenJDK creators decided to make the newer version of the secureRandomSpi to reset the value of the remCount integer field each time SecureRandom. setSeed() is called, which the older version did not.

If you want to implement this hack, first thing you have to do is to check whether the SecureRandom instance is truly a SUN's "SHA1PRNG" by calling "SUN".equals(secureRandom.getProvider().getName()) && "SHA1PRNG".equals(secureRandom.getAlgorithm()); and then get it's SPI via reflection and save the value of its remCount field. Then you may call setSeed() and then install the saved value back to the remCount field. I don't want to post this obscure code here but you've got the idea. Thank you.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM