SHA3-512 to Generate Keys in Java - java

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)
https://github.com/devssh/BlockchainFullNode/blob/d2978e598b4cdecdf4b3337713b2c3e839a6b181/src/main/java/app/model/Keyz.java#L111-L128
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.
https://github.com/devssh/BlockchainFullNode/blob/d2978e598b4cdecdf4b3337713b2c3e839a6b181/test/main/java/app/model/KeyzTest.java#L16-L22
#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 a PRNG 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!

Related

Generate a keypair from a password

I would like to use asymmetric encryption based with a private key based on a password. The requirement is the security level provided must be the same of (1) using password-based symmetric encryption, (2) using asymmetric encryption in a "regular" way.
I will have to use it in Java, but the answer can be generic.
I am aware that I can generate a keypair and encrypt the privateKey with a password-based symmetric key, however, in this way I will need a server (or other storage) to store this encrypted key. I would like to avoid that. If I could generate a keypair from a password, it is not needed.
Any suggestions?
If you base the private key solely on a password, it will only be as strong as the password, i.e. whoever can guess the password can get the private key.
This is comparable to generating a private/public key pair, encrypting the private key with a symmetric cipher and then publishing both together.
This of course makes the whole system weaker, since you no longer need to have the secret token - you only need to know the password.
Every time I see this question or a variant of it asked it's usually the result of bad design decisions. Almost always, the correct answer is to generate a random RSA keypair and protect the private key using standard password-based encryption like PBKDF2 or argon2. I've only seen one use case where this made at least a little sense and that was a cryptographic token back in the day with absolutely no nonvolatile storage. You won't find it around because there's no reason to build such a token, nonvolatile storage is not exotic in 2018.
In general you can do this: java's RSA key generation code accepts an instance of SecureRandom which the Oracle providers use to generate the candidate primes for RSA. You can subclass SecureRandom (I think) to provide a class that uses the password to seed a deterministic, repeatable sequence of random numbers such that every time you call KeyPairGenerator. generateKeyPair() the same keypair (including the private key) is generated. The bouncycastle library includes an example, FixedSecureRandom, that can be used as a model. Note that the deterministic RNG still has to be cryptographically secure, apart from the fact that it will not have enough entropy. FixedSecureRandom is not secure, it simply returns the pre-supplied bytes directly as output. Perhaps you can merge FixedSecureRandom with one of the other CSPRNGs in the org.bouncycastle.crypto.prng package.

AES random key generation

I see many examples where the secret key is generated this way:
KeyGenerator generator = KeyGenerator.getInstance("AES");
generator.init(KEY_LEN);
SecretKey secretKey = generator.generateKey();
I'm in doubt if there's a difference (even conceptual) with the following:
byte[] material = new byte[KEY_LEN / Byte.SIZE];
SecureRandom.getInstanceStrong().nextBytes(material);
SecretKey secretKey = new SecretKeySpec(material, "AES");
Stated that both methods are 3 lines, is there some practical impact in preferring the first over the second?
Thanks
You could look at the actual source code for generateKey() to see the difference but ultimately they are both going to do the same steps to generate an AES key. I would argue the latter
byte[] material = new byte[KEY_LEN / Byte.SIZE];
SecureRandom.getInstanceStrong().nextBytes(material);
SecretKey secretKey = new SecretKeySpec(material, "AES");
is a little more brittle for the average coder, requiring them to understand the SecureRandom class. If you eliminate the second line altogether the code runs just fine with an all zero key, an obvious vulnerability that's also easy for an attacker to check. Also, using generateKey() can produce a properly formatted key if the algorithm has some particular requirements. For example, the now obsolete DES and Triple DES algorithms had a weird parity bit in each byte that some DES implementations expected to see.
There are many reasons why you would want to use the KeyGenerator method, which was designed for the purpose:
readability: generateKey tells you exactly what the algorithm is doing;
portability: e.g. when choosing a different algorithm);
correctness: SecretKeySpec may not validate the key entirely;
security: you would leave the key material exposed in the material variable, which may not be cleared or even garbage collected after the key is not required anymore;
hardware support: importing key material is often not supported for hardware devices; keys should be generated on the device itself (using a specialized KeyFactory implementation).
There is no reason at all to use the second method. If you want to use a very specific random number generator for generating the key then you can use one of the specialized init methods but beware that this may not be compatible with hardware devices.
The two code examples seem to be doing the same and as already answered usually they are.
However using security devices (e. g. HSM, smartcards or other crypto devices) by default they won't allow exposing the key bytes so you would be able to generate a key from arbitritrary byte array only in very limited cases..

How to properly use setSeed() method in SecureRandom to generate RSA primes

I want to produce the two prime numbers for RSA key generation. I think in order to increase both primes' randomness, the random may be generated as the following:
SecureRandom r = SecureRandom.getInstance("SHA1PRNG");
r.setSeed(1232);
p = BigInteger.probablePrime(1024, r);
q = BigInteger.probablePrime(1024, r);
My question is: Do you think using SecureRandom will increase the p and q randomness? If so, how can I randomly set the value of setSeed() instead of making it a fixed value ( here i chose 1232)?
As CodesInChaos already shows, the default implementation of the SUN provider automatically seeds itself using the system random number generator. As Java itself doesn't have an (explicit) entropy source it is more or less dependent on the system for its seed.
You should never call setSeed before retrieving data from the "SHA1PRNG" in the SUN provider as that will make your RNG (Random Number Generator) into a Deterministic RNG - it will only use the given seed instead of adding the seed to the state. In other words, it will always generate the same stream of pseudo random bits or values.
The initial call to setSeed may differ per provider. Sometimes it will use the seed as only seed, but it may also just add the seed to the current state. On later Android versions (4.2 onwards) the seed is just added to the random state, so the "SHA1RNG" will stay fully random.
Probably the best way to generate your random number generator is just
SecureRandom r = new SecureRandom();
and let the Java runtime figure out the best one.
If you want to use an explicit algorithm (which is, however, ill-described by SUN/Oracle) then you could use:
SecureRandom r = SecureRandom.getInstance("SHA1PRNG");
as in your code.
Nowadays, it is also possible to use the NIST algorithms using "DRBG" as algorithm description, which you can then configure for the VM using the security properties.
Neither "SHA1PRNG" nor "DRBG" are implementation requirements, and it may differ per runtime / provider which algorithm is used or how they are seeded. I would never use them to re-generate a stream of previously generated bytes or values; please use a stream cipher for that. For instance, you could use a cipher instance Cipher for "AES/CTR/NoPadding" and encrypt zero-valued bytes to get to the key stream.
If you want to add entropy, use:
// just used to make sure that the SecureRandom is seeded by the OS
r.nextBytes(new byte[8]);
r.setSeed(1232);
A constant value or literal doesn't contain much (if any) entropy. Usual sources of entropy are the current time (or even better, System.nanoTime()), mouse movements etc.
For Java 8 there is a new method getInstanceStrong() with the following description:
Returns a SecureRandom object that was selected by using the algorithms/providers specified in the securerandom.strongAlgorithms Security property.
Some situations require strong random values, such as when creating high-value/long-lived secrets like RSA public/private keys. To help guide applications in selecting a suitable strong SecureRandom implementation, Java distributions include a list of known strong SecureRandom implementations in the securerandom.strongAlgorithms Security property.
Which should be used as a replacement for the call to the constructor. Be warned that this may return a blocking RNG, that is: an RNG that may block your thread until sufficient entropy has become available. It may also drain your OS entropy pool blocking other applications, so only use it sparingly.

RSA algorithm in Java : How works getEncoded method on java.security.Key interface?

I would like to be more familiar with the RSA algorithm and I have made small project for that. Actually everithing works fine, I can encrypt and decrypt a message correctly but I don't know if my key generation is performed correctly (my objective is to obtain a public key with a byte array representation that I can export for other parts).
Here comes how my keys are generated:
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
SecureRandom random = SecureRandom.getInstance("SHA1PRNG", "SUN");
random.nextBytes(new byte[128]);
keyGen.initialize(1024, random);
KeyPair keyPair = keyGen.generateKeyPair();
PublicKey pubKey = keyPair.getPublic();
PrivateKey privKey = keyPair.getPrivate();
byte[] publicKeyEncoded = pubKey.getEncoded();
byte[] privateKeyEncoded = privKey.getEncoded();
My question is about the getEncoded method, each time my program is executed a new key pair seems to be generated (according my trace) but when I call this method I have still the same value, it seems fixed. The value is changed when the method is called a second times and is changed each times that I call it in my program, but if a new execution is performed, the first value is the same as first value of the previous execution event if the keys differs.
According the documentation it seems that it "Returns the key in its primary encoding format ...", I have checked my key format, it is X.509, but there is nothing about the described behavior.
Can you please give me more details about that ?
I have tested the code snippet you provided in a standalone java program. Every time I call it, a new different key is generated. If your code always generate the same sequence of key it means that the random number generator produces the same sequence of number.
SHA1PRNG is a PRNG, it means that the sequence of number produced depends on a seed. If the PRGN is seeded twice with the same seed, it will produce the same sequence of numbers. The seed is set as follows:
by calling setSeed(byte[]) on SecureRandom
the first next/nextBytes is called and setSeed has not been previously called the PRGN is seeded with a secure mechanism provided by the underlying OS.
Since the code you posted here does not call setSeed, the SecureRandom object should be properly seeded with some true random value.
My suggestions:
Are you sure that you do not call setSeed (with the same seed value)? This could explain this behavour. Note also that the key encoding provided by getEncoded() always starts with the same byte sequence 30819f300d06092a864886f70d010101050003818d00
Try to use random = new SecureRandom(). It will use the default SecureRandom implementation for your platform (For windows as far as I known it is the SHA1PRNG implementation but Linux and MaxOSX are using another implementation)

Are java.lang.Long or java.util.UUID safe shared secrets?

In order to access resources in my web service application, client applications need to provide a shared secret unique for each resource.
Question is: Are Longs generated by a SecureRandom reasonably safe for this purpose (e.g. against brute force attacks)? Or should I use a UUID instead?
The web service runs over HTTPS and I can guarantee that no collision (with either Long or UUID) occurs. Question really only is whether the domain of a Long in Java is large enough to provide protection against exhaustive attacks over HTTPS.
The simple answer is that you can't EVER guarantee there won't be any collisions for a randomly generated number pair / sequence. All you can do is design things so that the probability of a collision is acceptably low for the application. How low that probability needs to be depends on the details of the application.
The thing that puzzles me about this is why a collision would be a problem with shared secrets at all. Are you really asking about the probability of someone guessing the shared secret?
OK, so this is a simple maths problem. Take the long example.
There are 2^64 possible values of a long.
About V of these are "valid secrets".
Some bad guy could plausibly try out N guessed secrets per second.
You can derive a formula for the probability P that someone can guess one of your secrets in a given time interval T.
Derive the formula, plug in the variables V, N and T, and decide whether P is acceptable.
Note that "a practically relevant chance" is not something we can advise you on. Rather you should be deciding what an acceptable risk ... based on an analysis of the costs / consequences of someone succeeding in breaking your scheme.
Perhaps, but why use them when the JCE supports this very function?
DHParameterSpec dhSkipParamSpec = new DHParameterSpec(skip1024Modulus, skip1024Base);
KeyPairGenerator aliceKpairGen = KeyPairGenerator.getInstance("DH", "BC");
aliceKpairGen.initialize(dhSkipParamSpec);
KeyPair aliceKpair = aliceKpairGen.generateKeyPair();
byte[] alicePubKeyEnc = aliceKpair.getPublic().getEncoded();
aliceKeyAgree = KeyAgreement.getInstance("DH", "BC");
aliceKeyAgree.init(aliceKpair.getPrivate());
//... obtaining Bob's Public Key
aliceKeyFac = KeyFactory.getInstance("DH", "BC");
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(bobPubKeyEnc);
bobPubKey = aliceKeyFac.generatePublic(x509KeySpec);
aliceKeyAgree.doPhase(bobPubKey, true);
SecretKey aliceAesKey = aliceKeyAgree.generateSecret("AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding", "BC");
cipher.init(Cipher.ENCRYPT_MODE, aliceAesKey);
byte[] cipherText = cipher.doFinal(plaintext.getBytes());
And your AES-encrypted text is in cipherText now. Hope that helped...

Categories

Resources