Why does my AES Cipher throw an InvalidKeyException on init of DECRYPT_MODE
Asked Answered
B

2

5

Why would this init succeed:

Cipher AESCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
AESCipher.init(Cipher.ENCRYPT_MODE, secretKey, secRandom);

while this fails:

Cipher AESCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
AESCipher.init(Cipher.DECRYPT_MODE, secretKey, secRandom);

Throwing an Exception in thread "main" java.security.InvalidKeyException: Parameters missing

The secretKey is generated by a KeyGenerator, and the secureRandom by SecureRandom.getInstance("SHA1PRNG") with a random static seed set.

Thanks

Bendite answered 2/1, 2013 at 15:14 Comment(5)
Why do you pass a RNG to the decryption operation? I guess the RNG is used to generate the IV, so for decryption you probably need to pass in the IV and not a RNG.Carbylamine
Thanks. So why does init get a secRandom in the first place? Is the correct way to first get some bytes from the secRandom, save them as an IV and then use them as using a new IvParameterSpec? Later passing the IV on to decryption in the same manner?Bendite
It probably takes the RNG so it can create the IV itself instead of bothering you with it. But I'm not familiar with java crypto APIs.Carbylamine
@CodesInChaos: Ah, but you are correct on all counts.Tisza
@Carbylamine you are correct, however the SecureRandom implementation of "SHA1PRNG" does create a static value for a static seed (if used properly). Of course, there is no way for the init call to know that it is a static value, so it will simply exit with the exception specified in the JavaDoc.Tippets
T
5

As correctly surmised by CodeInChaos, the SecureRandom instance is used to derive a random IV when the AESCipher instance is created with Cipher.ENCRYPT_MODE. However, you supply it as a parameter when creating a Cipher instance in decrypt mode. This little pointless code fragment shows an example.

public static void main(String[] args) throws Exception {
    SecureRandom secRandom = SecureRandom.getInstance("SHA1PRNG");
    KeyGenerator kg = KeyGenerator.getInstance("AES");
    kg.init(128, secRandom);
    Key secretKey = kg.generateKey();
    Cipher AESCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    AESCipher.init(Cipher.ENCRYPT_MODE, secretKey, secRandom);
    IvParameterSpec iv = new IvParameterSpec(AESCipher.getIV());
    AESCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    AESCipher.init(Cipher.DECRYPT_MODE, secretKey,iv, secRandom);
}

Also, your claim that you are initializing your SecureRandom instance with a static seed suggest a misunderstanding of that class. SecureRandom does not guarantee that you will get the same output when you provide the same seed. If you look carefully at the Javadocs you'll see that it attempts to provide some true entropy from other sources if at all possible.

EDIT 1:

Thanks to owlstead for his usual thoroughness in reviewing answers. See his answer to a related question for additional discussion. The source code for the SHA1PRNG is available online here. It is a little tricky to follow but if you provide a seed before asking the instance for any random bytes then the output will be completely deterministic. So my earlier statement is incorrect.

Tisza answered 2/1, 2013 at 19:37 Comment(2)
Em, that's an oops I think, the "SHA1PRNG" implementation of Java guarantees same output when fed the same seed, if the seed is set before calling any method that retrieves random data, see may answer here and the JavaDoc of the SecureRandom classTippets
Do note that it is extremely unwise to use this method to generate a key. The output is not specified well enough to use it for generating keys or IV's out of static key material. So in that sense the remark is correct; don't use SecureRandom to generate keys or IV's out of static key material.Tippets
T
2

Just read the JavaDoc of the init method with SecureRandom that you are applying:

If this cipher requires any algorithm parameters that cannot be derived from the given key, the underlying cipher implementation is supposed to generate the required parameters itself (using provider-specific default or random values) if it is being initialized for encryption or key wrapping, and raise an InvalidKeyException if it is being initialized for decryption or key unwrapping. The generated parameters can be retrieved using getParameters or getIV (if the parameter is an IV).

You will have to transfer the encrypted IV to the decryption method, e.g. by prepending it to the cipher text. The IV may be transferred in the clear. Use IvParameterSpec instead of SecureRandom to set the IV for decryption.

Tippets answered 3/1, 2013 at 17:48 Comment(2)
Personally, I think the whole concept of generating default or provider specific values is a stupid function of the Java crypto API so I always use IvParameterSpec, and fill it with random values during encryption myself. Much easier to debug, and encryption and decryption calls are nicely symmetric too.Tippets
Agreed. I won't say defaults must theoretically always be a disaster but I've never seen an implementation yet where they didn't cause more problems then they solved. Don't use defaults in the Java crypto API, don't use default charsets ... just avoid defaults.Tisza

© 2022 - 2024 — McMap. All rights reserved.