Cipher.init() throws java.security.InvalidKeyException: Parameters missing when we start decrypting with a mode that needs an initialization vector (IV), such as AES/CBC, but pass only the key. The fix is to pass the same IV that encryption used, wrapped in an IvParameterSpec for CBC or a GCMParameterSpec for GCM.
The error shows up in code that encrypts a value in one place and decrypts it somewhere else, such as an encrypted database column, a token in a cookie or a file sent to another service. Encryption works, because the cipher creates a random IV on its own, but that IV is never stored.
The following example encrypts with AES/CBC and decrypts once without and once with the IV.
SecretKey key = KeyGenerator.getInstance("AES").generateKey();
Cipher encrypt = Cipher.getInstance("AES/CBC/PKCS5Padding");
encrypt.init(Cipher.ENCRYPT_MODE, key); // no IV given, the provider creates one
byte[] iv = encrypt.getIV(); // 16 random bytes, store them
Cipher noIv = Cipher.getInstance("AES/CBC/PKCS5Padding");
noIv.init(Cipher.DECRYPT_MODE, key); // InvalidKeyException: Parameters missing
Cipher withIv = Cipher.getInstance("AES/CBC/PKCS5Padding");
withIv.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));
String mode = withIv.getAlgorithm(); // "AES/CBC/PKCS5Padding", ready to decrypt
Notice that the IV comes from the encrypting cipher and must reach the decrypting side. We look at which modes need parameters, store the IV with AES-GCM, read existing CBC data, and cover InvalidAlgorithmParameterException, which the same code throws when the IV is there but wrong.
1. Why Decryption Fails With Parameters missing
An IV is a random value that makes the same plaintext encrypt to different ciphertexts under the same key. It is not secret, but decryption needs the same bytes that encryption used. When we call init(Cipher.ENCRYPT_MODE, key) without an IV, the provider generates a random one, and we read it back with getIV(). In decrypt mode, the provider cannot invent the IV, so init() fails.

Each AES mode needs different parameters, and the error message differs with them. The messages come from the SunJCE provider of JDK 25.
| Transformation | Parameter for decryption | Message when it is missing |
|---|---|---|
| AES/CBC/PKCS5Padding | IvParameterSpec, 16 bytes | InvalidKeyException: Parameters missing |
| AES/CTR/NoPadding, AES/CFB/NoPadding | IvParameterSpec, 16 bytes | InvalidKeyException: Parameters missing |
| AES/GCM/NoPadding | GCMParameterSpec, 12-byte IV and tag length | InvalidKeyException: No GCMParameterSpec specified |
| ChaCha20-Poly1305 | IvParameterSpec, 12-byte nonce | InvalidKeyException: Default parameter generation disallowed in DECRYPT and UNWRAP modes |
| AES/ECB/PKCS5Padding or plain AES | none | no exception, but ECB is not safe |
The last row explains why some old code never sees the error. Cipher.getInstance(“AES”) means AES/ECB/PKCS5Padding in the SunJCE provider, which takes no IV and encrypts equal blocks to equal output. Switching such code to CBC or GCM brings the IV requirement, and with it the exception.
2. Storing the IV With AES-GCM
The safe pattern is to generate a fresh random IV for every encryption and to put it in front of the ciphertext. For new code, we use AES-GCM with a 12-byte IV and a 128-bit authentication tag, which NIST SP 800-38D recommends. GCM also detects changed ciphertext, which CBC does not.
static String encrypt(String plainText, SecretKey key) throws GeneralSecurityException {
byte[] iv = new byte[12];
new SecureRandom().nextBytes(iv);
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, key, new GCMParameterSpec(128, iv));
byte[] cipherText = cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8));
byte[] message = ByteBuffer.allocate(iv.length + cipherText.length).put(iv).put(cipherText).array();
return Base64.getEncoder().encodeToString(message);
}
static String decrypt(String encoded, SecretKey key) throws GeneralSecurityException {
byte[] message = Base64.getDecoder().decode(encoded);
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(Cipher.DECRYPT_MODE, key, new GCMParameterSpec(128, message, 0, 12));
byte[] plain = cipher.doFinal(message, 12, message.length - 12);
return new String(plain, StandardCharsets.UTF_8);
}
KeyGenerator generator = KeyGenerator.getInstance("AES");
generator.init(256);
SecretKey aesKey = generator.generateKey();
String token = encrypt("hello", aesKey); // Base64 of IV + ciphertext + tag
int storedBytes = Base64.getDecoder().decode(token).length; // 33
String text = decrypt(token, aesKey); // "hello"
The 33 bytes are the 12-byte IV, the 5 bytes of hello and the 16-byte tag. The decrypt method never needs a separate IV argument, so the exception cannot come back. If someone changes a byte of the stored value, doFinal() throws AEADBadTagException: Tag mismatch instead of returning garbage.
For example, a recipe-sharing app stores each user’s API key for a nutrition service in an encrypted database column. The first version encrypts with AES/CBC in the save method and keeps only the ciphertext. A nightly sync job reads the column, calls init(Cipher.DECRYPT_MODE, key) and fails with Parameters missing for every row. Writing the IV in front of the ciphertext, as in encrypt(), fixes new rows, while the old rows cannot be decrypted at all, because their IVs are lost.
Never fix the error with a constant IV such as 16 zero bytes, because the same plaintext always gives the same ciphertext, and with GCM a repeated IV breaks the encryption completely. The AES-256 encryption and decryption tutorial covers key handling in more detail.
3. Decrypting Existing AES/CBC Data
Sometimes the data already exists in CBC format, produced by another service or an older version of our app. We cannot change the format, so we find out where the producer puts the IV. The common layout is the 16-byte IV followed by the ciphertext, and the decrypt code reads it the same way as in the GCM version.
static byte[] decryptCbc(byte[] message, SecretKey key) throws GeneralSecurityException {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(message, 0, 16));
return cipher.doFinal(message, 16, message.length - 16);
}
When the key comes from a password, both sides must also use the same salt and iteration count. The OWASP Password Storage Cheat Sheet recommends 600,000 iterations for PBKDF2 with HMAC-SHA256, and the salt is random and stored next to the IV.
static SecretKey keyFromPassword(char[] password, byte[] salt) throws GeneralSecurityException {
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
byte[] raw = factory.generateSecret(new PBEKeySpec(password, salt, 600_000, 256)).getEncoded();
return new SecretKeySpec(raw, "AES");
}
byte[] salt = new byte[16];
new SecureRandom().nextBytes(salt);
SecretKey passwordKey = keyFromPassword("pancakes".toCharArray(), salt);
int keyLength = passwordKey.getEncoded().length; // 32
4. Other InvalidKeyException Messages
The InvalidKeyException class covers every problem with the key passed to init(), and the message tells us which one we have.
| Message | Cause | Fix |
|---|---|---|
| Parameters missing | CBC, CTR or CFB decryption without an IV | Pass new IvParameterSpec(iv) |
| No GCMParameterSpec specified | GCM decryption without parameters | Pass new GCMParameterSpec(128, iv) |
| Invalid AES key length: 10 bytes | A key that is not 16, 24 or 32 bytes long, often a password used as key bytes | Derive the key with PBKDF2 or generate it with KeyGenerator |
| No installed provider supports this key: javax.crypto.spec.SecretKeySpec | A key for another algorithm, or a second init() on a Cipher whose first init() failed | Use an AES key and a new Cipher instance |
SecretKey shortKey = new SecretKeySpec(new byte[10], "AES");
Cipher shortKeyCipher = Cipher.getInstance("AES/GCM/NoPadding");
shortKeyCipher.init(Cipher.ENCRYPT_MODE, shortKey); // InvalidKeyException: Invalid AES key length: 10 bytes
Old answers also mention Illegal key size for 256-bit keys. That message came from the limited crypto policy of Java 8 before update 161, and current JDKs allow 256-bit AES keys by default.
5. InvalidAlgorithmParameterException
The InvalidAlgorithmParameterException is the sibling of InvalidKeyException. Cipher.init() throws it when we pass parameters, but they do not fit the transformation, such as an IV of the wrong length or the wrong spec class. In other words, the IV is there but wrong.
SecretKey cbcKey = KeyGenerator.getInstance("AES").generateKey();
Cipher cbc = Cipher.getInstance("AES/CBC/PKCS5Padding");
cbc.init(Cipher.DECRYPT_MODE, cbcKey, new IvParameterSpec(new byte[12])); // InvalidAlgorithmParameterException: Wrong IV length: must be 16 bytes long
SecretKey gcmKey = KeyGenerator.getInstance("AES").generateKey();
Cipher gcm = Cipher.getInstance("AES/GCM/NoPadding");
gcm.init(Cipher.ENCRYPT_MODE, gcmKey, new IvParameterSpec(new byte[12])); // InvalidAlgorithmParameterException: AlgorithmParameterSpec not of GCMParameterSpec
Each message on JDK 25 points to one mistake, so the fix follows from it.
| Message | Cause |
|---|---|
| Wrong IV length: must be 16 bytes long | A 12-byte GCM IV passed to CBC, CTR or CFB, or the IV was cut when it was stored |
| AlgorithmParameterSpec not of GCMParameterSpec | An IvParameterSpec passed to AES/GCM |
| Unsupported TLen value. Must be one of {128, 120, 112, 104, 96} | A GCM tag length such as 64, or bytes given instead of bits (16 instead of 128) |
| IV is empty | A zero-length IV in a GCMParameterSpec, often an empty column read from the database |
| Cannot reuse iv for GCM encryption | A second GCM encryption with the same key and IV on the same Cipher |
| ECB mode cannot use IV | An IV passed to AES/ECB or plain AES |
The GCM reuse check protects us from the worst AES-GCM mistake. The JDK detects it only on the same Cipher instance, so a fresh random IV per message, as in section 2, is still our job.
SecretKey reuseKey = KeyGenerator.getInstance("AES").generateKey();
GCMParameterSpec fixedIv = new GCMParameterSpec(128, new byte[12]);
Cipher reused = Cipher.getInstance("AES/GCM/NoPadding");
reused.init(Cipher.ENCRYPT_MODE, reuseKey, fixedIv);
byte[] first = reused.doFinal(new byte[] {1, 2, 3}); // encrypted once
reused.init(Cipher.ENCRYPT_MODE, reuseKey, fixedIv); // InvalidAlgorithmParameterException: Cannot reuse iv for GCM encryption
5.1. The trustAnchors Parameter Must Be Non-Empty
A different InvalidAlgorithmParameterException comes from TLS, not from Cipher. The message the trustAnchors parameter must be non-empty means Java opened a trust store that contains no trusted certificates, so it cannot check any server certificate. It often arrives wrapped in an SSLException on the first HTTPS call.
KeyStore emptyStore = KeyStore.getInstance("PKCS12");
emptyStore.load(null, null);
PKIXParameters pkix = new PKIXParameters(emptyStore); // InvalidAlgorithmParameterException: the trustAnchors parameter must be non-empty
In practice, the trust store is empty for one of these reasons.
- The javax.net.ssl.trustStore property points to a file that does not exist, often a relative path that worked only on a developer machine.
- The trust store file contains only a private key entry and no trustedCertEntry, for example a client keystore passed as trust store.
- The cacerts file of a Linux JDK package is empty or broken, which happens in some minimal Docker images.
The command keytool -list -cacerts shows how many entries the default trust store has, and JDK 25 ships with 118 of them. For details on the failing handshake, we start the app with JSSE debug logging turned on.
keytool -list -cacerts -storepass changeit | head -4
java -Djavax.net.debug=ssl:handshake -jar recipes.jar
The debug log prints which trust store file Java loads and every certificate it checks. The guide to SSLHandshakeException covers the handshake errors that follow once the trust store is correct.
6. AES IV and Parameters missing FAQs
The IV is required for decryption but is not a secret, so its storage and its length matter more than hiding it.
6.1. Does the IV need to be kept secret?
No. The IV only has to be unpredictable and unique per encryption with the same key. We store it in plain form next to the ciphertext, as the encrypt() method in section 2 does.
6.2. How long is the IV for AES?
The IV is 16 bytes for CBC, CTR and CFB, because that is the AES block size. For GCM, the recommended IV length is 12 bytes, and the Java default is also 12 bytes when the provider generates it.
6.3. Can I avoid the IV by using AES/ECB?
Technically yes, because ECB takes no parameters, but ECB encrypts equal 16-byte blocks to equal output and leaks patterns of the data. Use AES-GCM with a random IV instead.
6.4. Why does init() still fail after I added the IV?
If the same Cipher object threw an exception in an earlier init() call, JDK 25 rejects the next init() on it with No installed provider supports this key. Create a new Cipher with Cipher.getInstance() for each attempt, which is cheap.
7. Conclusion
A decrypting Cipher needs the same IV as the encrypting one, and Parameters missing tells us it got the key alone. Encryption hides the problem, because the provider generates the IV on its own, so the fix is to store that IV with the ciphertext and pass it back with IvParameterSpec or GCMParameterSpec.
For new code, we use AES-GCM with a fresh 12-byte random IV and a 128-bit tag, and we prepend the IV to the stored bytes. When the IV is present but wrong, InvalidAlgorithmParameterException names the exact problem, and its TLS variant about trust anchors points to an empty trust store. Both are subclasses of GeneralSecurityException and therefore checked exceptions, so the compiler makes us catch or declare them.
8. References
- Java 25 API, Cipher
- Java 25 API, GCMParameterSpec
- Java 25 API, InvalidAlgorithmParameterException
- NIST SP 800-38D, Galois/Counter Mode
- JSSE Reference Guide, Debugging Utilities
- OWASP Password Storage Cheat Sheet
Happy Learning !!