How to create a secure random AES key in Java?
I would use your suggested code, but with a slight simplification:
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
keyGen.init(256); // for example
SecretKey secretKey = keyGen.generateKey();
Let the provider select how it plans to obtain randomness - don't define something that may not be as good as what the provider has already selected.
This code example assumes (as Maarten points out below) that you've configured your java.security
file to include your preferred provider at the top of the list. If you want to manually specify the provider, just call KeyGenerator.getInstance("AES", "providerName");
.
For a truly secure key, you need to be using a hardware security module (HSM) to generate and protect the key. HSM manufacturers will typically supply a JCE provider that will do all the key generation for you, using the code above.
Using KeyGenerator
would be the preferred method. As Duncan indicated, I would certainly give the key size during initialization. KeyFactory
is a method that should be used for pre-existing keys.
OK, so lets get to the nitty-gritty of this. In principle AES keys can have any value. There are no "weak keys" as in (3)DES. Nor are there any bits that have a specific meaning as in (3)DES parity bits. So generating a key can be as simple as generating a byte array with random values, and creating a SecretKeySpec
around it.
But there are still advantages to the method you are using: the KeyGenerator
is specifically created to generate keys. This means that the code may be optimized for this generation. This could have efficiency and security benefits. It might be programmed to avoid a timing side channel attacks that would expose the key, for instance. Note that it may already be a good idea to clear any byte[]
that hold key information as they may be leaked into a swap file (this may be the case anyway though).
Furthermore, as said, not all algorithms are using fully random keys. So using KeyGenerator
would make it easier to switch to other algorithms. More modern ciphers will only accept fully random keys though; this is seen as a major benefit over e.g. DES.
Finally, and in my case the most important reason, it that the KeyGenerator
method is the only valid way of handling AES keys within a secure token (smart card, TPM, USB token or HSM). If you create the byte[]
with the SecretKeySpec
then the key must come from memory. That means that the key may be put in the secure token, but that the key is exposed in memory regardless. Normally, secure tokens only work with keys that are either generated in the secure token or are injected by e.g. a smart card or a key ceremony. A KeyGenerator
can be supplied with a provider so that the key is directly generated within the secure token.
As indicated in Duncan's answer: always specify the key size (and any other parameters) explicitly. Do not rely on provider defaults as this will make it unclear what your application is doing, and each provider may have its own defaults.
Lots of good advince in the other posts. This is what I use:
Key key;
SecureRandom rand = new SecureRandom();
KeyGenerator generator = KeyGenerator.getInstance("AES");
generator.init(256, rand);
key = generator.generateKey();
If you need another randomness provider, which I sometime do for testing purposes, just replace rand with
MySecureRandom rand = new MySecureRandom();