package io.emax.cosigner.common; import org.bouncycastle.crypto.digests.SHA512Digest; import org.bouncycastle.crypto.prng.EntropySource; import org.bouncycastle.crypto.prng.SP800SecureRandomBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.security.SecureRandom; public class DeterministicRng { private static final Logger LOGGER = LoggerFactory.getLogger(DeterministicRng.class); private static final String RANDOM_NUMBER_ALGORITHM = "SHA1PRNG"; private static final String RANDOM_NUMBER_ALGORITHM_PROVIDER = "SUN"; public static SecureRandom getSecureRandom(byte[] userKey, byte[] serverKey) { SecureRandom secureRandom; try { secureRandom = SecureRandom.getInstance(RANDOM_NUMBER_ALGORITHM, RANDOM_NUMBER_ALGORITHM_PROVIDER); } catch (Exception e) { LOGGER.error(null, e); secureRandom = new SecureRandom(); } byte[] userSeed = new byte[Math.max(userKey.length, serverKey.length)]; // XOR the key parts to get our seed, repeating them if they lengths // don't match for (int i = 0; i < userSeed.length; i++) { userSeed[i] = (byte) (userKey[i % userKey.length] ^ serverKey[i % serverKey.length]); } // Set up out private key variables secureRandom.setSeed(userSeed); final SecureRandom finalSecureRandom = secureRandom; SP800SecureRandomBuilder sp800SecureRandomBuilder = new SP800SecureRandomBuilder(i -> new EntropySource() { @Override public boolean isPredictionResistant() { return true; } @Override public byte[] getEntropy() { byte[] entropy = new byte[(i + 7) / 8]; finalSecureRandom.nextBytes(entropy); return entropy; } @Override public int entropySize() { return i; } }); sp800SecureRandomBuilder.setPersonalizationString(userKey); secureRandom = sp800SecureRandomBuilder.buildHash(new SHA512Digest(), serverKey, true); return secureRandom; } }