I have a need to store a secret in encrypted form in a database, and later be able to retrieve the secret back in its original form. I want to use a passphrase as raw key material used to generate the cryptographic key used for encrypting and encrypting.
The prototype code below does what I want, and appears to work find when using the PBEWithMD5AndDES algorithm, but when I run the same code using the PBEWithHmacSHA256AndAES_256 algorithm (which required JCE Unlimited Strength Jurisdiction Policy Files), it fails with a InvalidAlgorithmParameterException exception stating that an initialization vector was expected.
Why do I see this with SHA256/AES256 and not with MD5/DES?
In addition to the encrypted data returned by the ciphering function, do I also need to somehow extract and store the initialization vector for used during encryption for use during decryption?
PBEWithMD5AndDES Original secret: MySecret
Encrypted secret: o7+tjKc3UnuFHeWmfVJBJg==
Decrypted secret: MySecret
PBEWithHmacSHA256AndAES_256 Original secret: MySecret
Encrypted secret: rwtIUzt/jtQoGC6ueADBzw==
java.security.InvalidAlgorithmParameterException: Missing parameter type: IV expected at com.sun.crypto.provider.PBES2Core.engineInit(PBES2Core.java:252)
Decrypted secret was null
Ron McLeod wrote:Why do I see this with SHA256/AES256 and not with MD5/DES?
It seems like depending for the mode of the cipher, it may or may not use an IV. The default mode for PBEWithMD5AndDES does not use an IV (ECB mode?), but PBEWithHmacSHA256AndAES_256 does (CBC mode?).
Ron McLeod wrote:In addition to the encrypted data returned by the ciphering function, do I also need to somehow extract and store the initialization vector for used during encryption for use during decryption?
I found that I could either specify a fixed IV for both encrypting and decrypting, or extract the IV used when encrypting (Cipher#getIV()), and specify it when decrypting.
Probably because whoever implemented the AES algorithm in Java decided to protect people from making some mistakes, while the DES implementer didn't.
There are some big issues with your code: You're using the same algorithm for key derivation and encryption, you're not using IVs and you're not authenticating your ciphertext.
An important pillar on which cryptography is based is that every time you encrypt some message, the resulting ciphertext should look different. For this reason, keys are derived from passwords using a unique and random salt, and ciphertexts are derived from messages using a unique and random IV.
Another important matter is that just encryption isn't enough. You need to verify ciphertexts before you decrypt them. This can be done easily using AES in Galois Counter Mode. This algorithm will sign your message after encryption, and verify it before decryption.
You should use something like PBKDF2WithHmacSHA256 to perform key derivation, and AES/GCM/NoPadding for encryption.
To configure PBKDF2WithHmacSHA256, you need a PBEParameterSpec. To configure AES/GCM/NoPadding you need a GCMParameterSpec. PBEParameterSpec requires a salt for your password, and GCMParameterSpec requires an IV for your message. These need to be random and unique every time your perform encryption. Generate these with a SecureRandom.
So then, if your salt and IV are random each time you encrypt the same message, how can you decrypt the ciphertext? You can just append the salt and the IV to your ciphertext before you encode it. Salts and IVs are not sensitive data. Before decryption, remove the salt and IV from the decoded ciphertext, use the salt to reconstruct the key, use the IV to reconstruct the cipher, and then you can decrypt the remainder of the ciphertext.
A final remark. Since you're not using the key for anything more than a single encryption operation, you can use the same piece of random data for both the salt and the IV. This no longer holds when you have to store the key somewhere to use later, for instance, when performing user authentication.
Oh! And I noticed you're performing only 1000 iterations. Of course it depends on how many messages per time unit you want to encrypt, but I usually use between 100.000 and 500.000 iterations.