Better way to create AES keys than seeding SecureRandom
I need to send encrypted data from a Java
client to a C#
server. Right now I'm learning how to encrypt data using AES
(requirement). Following this accepted answer android encryption/decryption with AES I'm doing the following:
byte[] keyStart = "qweroiwejrwoejlsifeoisrn".getBytes(); // Random character string
byte[] toEncrypt = myMessageString.getBytes();
keyGen = KeyGenerator.getInstance("AES");
sr = SecureRandom.getInstance("SHA1PRNG");
sr.setSeed(keyStart);
keyGen.init(128, sr);
SecretKey secretKey = keyGen.generateKey();
byte[] secretKeyByte = secretKey.getEncoded();
SecretKeySpec skeySpec = new SecretKeySpec(secretKeyByte, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
cipher.doFinal(toEncrypt);
Since the algorithm uses a SecureRandom
using the keyStart
I am not sure if this can be decoded in C#
or even in another Java
program, without the SecureRandom
.
Will this encryption/decryption work with just knowing the value of keyStart
or since I'm using SecureRandom
I still need to pass something else in order to decrypt?
Also, is there a better way to do it or is this one just fine?
Solution 1:
No, the whole idea that you should use a SecureRandom
for key derivation from static data is rather bad:
-
SecureRandom
's main function is to generate random values, it should not be used as a generator for a key stream; -
SecureRandom
, when instantiated with"SHA1PRNG"
does not implement a well defined algorithm, and the algorithm has actually be known to change, even from one Sun JDK to another; - The Oracle provided implementation of
"SHA1PRNG"
uses the initial seed as only seed, others may just add the seed to the random pool.
Using "SHA1PRNG"
as key derivation function has been known to produce issues on several versions of Android, and may fail on any other Java RE.
So what should you do instead?
- Use
new SecureRandom()
or even better,KeyGenerator
to generate a truly random key, without seeding the random number generator if you need a brand new random key; - Directly provide a
byte[]
of a known key toSecretKeySpec
, or use a hexadecimal decoder to decode it from hexadecimals (note thatString
instances are hard to delete from memory, so only do this if there is no other way); - Use PBKDF2 if you want to create a key from a password (use a higher iteration count than the one provided in the link though);
- Use a true Key Based Key Derivation Mechanism if you want to create multiple keys from one key seed, e.g. use HKDF (see below).
Option 4 would be preferred if the seed was generated by e.g. a key agreement algorithm such as Diffie-Hellman or ECDH.
Note that for option 3, PBKDF2, you would be wise to keep to ASCII passwords only. This is due to the fact that the PBKDF2 implementation by Oracle does not use UTF-8 encoding.
As for option 4, I've helped with adding all good KBKDF's to the Bouncy Castle libraries so there isn't a need to implement a KBKDF yourself if you can add Bouncy Castle to your classpath and/or list of installed security providers. Probably the best KBKDF at the moment is HKDF. If you cannot add Bouncy Castle to your classpath then you might want to use the leftmost bytes of SHA-256 output over the derivation data as a "poor man's" KDF.