63

What is the recommended way of generating a secure, random AES key in Java, using the standard JDK?

In other posts, I have found this, but using a SecretKeyFactory might be a better idea:

KeyGenerator keyGen = KeyGenerator.getInstance("AES");
SecureRandom random = new SecureRandom(); // cryptograph. secure random 
keyGen.init(random); 
SecretKey secretKey = keyGen.generateKey();

It would be great if the answer included an explanation of why it is a good way of generating the random key. Thanks!

Duncan Jones
  • 67,400
  • 29
  • 193
  • 254
barfuin
  • 16,865
  • 10
  • 85
  • 132
  • This mighth help http://stackoverflow.com/questions/10252449/is-aes-key-random – Tala Aug 14 '13 at 09:58
  • @Tala That's where I found the [cited code](http://stackoverflow.com/a/10252662/1005481). But from that post, I could not determine a consensus on how to create the random key, and why it was a secure way. – barfuin Aug 14 '13 at 10:01

3 Answers3

97

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.

Community
  • 1
  • 1
Duncan Jones
  • 67,400
  • 29
  • 193
  • 254
  • 1
    Normally I would leave out the provider name too. Configure the platform (e.g. using the `java.security` file or by configuring the `Provider` programmatically using `Security.addProvider()` and likewise). Otherwise the code will be less portable, and it would not let the user change to e.g. a HSM without changing the application code. – Maarten Bodewes Aug 14 '13 at 10:14
  • Why would I need to specify the provider? The default behavior is to use the highest-priority installed provider that supports the algorithm, which seems sufficient to me. – barfuin Aug 14 '13 at 11:33
  • 2
    Not all key generation methods are created equal, and you may want to explicitly choose e.g. the key generation method of a provider. This is especially of use for providers for security tokens. For AES though, the random number generator may be of more importance - you may for instance want to use a slower, more secure, FIPS certified random number generator instead of the default. – Maarten Bodewes Aug 14 '13 at 11:46
  • What is difference between keyGen.init(256); or keyGen.init(128); ? – Hemanth Peela Jun 12 '19 at 05:21
  • 1
    @HemanthPeela It defines the length of the keys to generate. 256-bit keys are stronger than 128-bit keys. – Duncan Jones Jun 13 '19 at 06:32
24

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.

Community
  • 1
  • 1
Maarten Bodewes
  • 90,524
  • 13
  • 150
  • 263
8

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();
Maarten Bodewes
  • 90,524
  • 13
  • 150
  • 263
Andy
  • 2,469
  • 1
  • 25
  • 25
  • The first time I specify the random generator, the second time the length of the AES key to use (256). They are separate init methods. – Andy Nov 15 '15 at 23:53