Is it possible to use SHA3-512(a subset of keccak available in Java9) to generate keys in Java?
I have searched through a lot of noise and documentation to try to figure this out. Currently it seems SHA3-512 is available as a hash for MessageDigest but not for generating keys. My code below tries to generate keys predictably(for wallet purposes like BIP32 but beyond currency to blockchain uses)
public static String GenerateSeed() throws Exception {
SecureRandom random = new SecureRandom();
byte[] seed = random.generateSeed(512);
return Base64.getEncoder().encodeToString(seed);
}
public static Keyz GenerateKey(String seedString) {
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
KeyPairGenerator keyGen1 = KeyPairGenerator.getInstance("ECDSA");
ECGenParameterSpec ecSpec = new ECGenParameterSpec("secp256k1");
SecureRandom random1 = SecureRandom.getInstance("SHA1PRNG");
random1.setSeed(Base64.getDecoder().decode(seedString));
keyGen1.initialize(ecSpec, random1);
KeyPair keyPair1 = keyGen1.generateKeyPair();
PublicKey pub1 = keyPair1.getPublic();
PrivateKey priv1 = keyPair1.getPrivate();
//Keyz is a simple model that stores the 3 fields below and overrides equals and hashcode on those fields
return new Keyz("random", pub1, priv1);
}
As you can see, it uses SHA1PRNG to predictably generate keypair deterministically(I am fine with the security concerns on this) so that the keys can be recreated deterministically.
Here is a JUnit test to make sure the keys are deterministic(works for SHA1PRNG, needs to work in SHA3PRNG). Ideally what is needed is a SHA3-512 TRNG in the GenerateSeed and a SHA3PRNG in the GenerateKey. Since the keygenerator needs a SecureRandom I would be surprised if java.Security.SecureRandom is still on something as insecure as SHA1PRNG.
@Test
public void shouldReturnDeterministicKeys() throws Exception {
String seedString = GenerateSeed();
Keyz random1 = GenerateKey(seedString);
Keyz random2 = GenerateKey(seedString);
//This assertion works as we override equals and hashcode
assertEquals(random1, random2);
}
Can someone please let me know if they figured a way to get this to work
It seems what you are looking for is not available out of the box:
Note that SHA1
and SHA1PRNG
are not equivalent. While the former is a hash algorithm, the latter is a pseudo random generation algorithm (that uses SHA1
to update its internal state, of course.) One trivial result of this difference is, SHA1
outputs a fixed size of bits, where SHA1PRNG
outputs as many bits as you like.
Because of this difference, SHA3-512
cannot be used as PRNG
directly, although it is available in Java. What you need to do is, implement a PRNG
algorithm using SHA3-512
(this part is really tricky, since generating a pseudo random stream is quite difficult.) and register it through your custom Security Provider
(like Bouncy Castle
does) with some name MySHA3PRNG
. After that, you can get an instance of it with name MySHA3PRNG
as you do for SHA1PRNG
. The rest remains as-is.
A major problem with this tricky part might be as follows: Quoting from here,
The paper "Sponge-based pseudo-random number generators" talks about just that and it also describes a clean and efficient way to construct a re-seedable
PRNG
with a (Keccak) sponge function. What you'll get is aPRNG
based on a cryptographic hash function… with the usual security implications.For example: the paper explicitly states that you should reseed regularly with sufficient entropy to prevent an attacker from going backwards on the period of the
PRNG
(which is probably what you've been hearing about).
However, what you need is a PRNG
algorithm that does not need to be re-seeded. I hope you have sufficient theoretical background to prove that your custom PRNG
algorithm is secure.
Good luck!