spring-projects/spring-security · error · IllegalStateException
Cannot decrypt
Error message
Cannot decrypt
What it means
RsaSecretEncryptor.decrypt wraps any non-RuntimeException thrown during RSA decryption (JCE Cipher/BadPadding/IllegalBlockSizeException etc.) in an IllegalStateException with the message 'Cannot decrypt'. The library throws it because the input could not be decrypted with the configured RSA key — typically wrong key, corrupted/ciphertext mismatch, or a cipher operation failure. It deliberately preserves the original exception as the cause.
Source
Thrown at crypto/src/main/java/org/springframework/security/crypto/encrypt/RsaSecretEncryptor.java:234
ByteArrayOutputStream output = new ByteArrayOutputStream(text.length);
try {
int length = readInt(input);
byte[] random = new byte[length];
input.read(random);
final Cipher cipher = Cipher.getInstance(alg.getJceName());
cipher.init(Cipher.DECRYPT_MODE, key);
String secret = new String(Hex.encode(cipher.doFinal(random)));
byte[] buffer = new byte[text.length - random.length - 2];
input.read(buffer);
BytesEncryptor aes = gcm ? Encryptors.stronger(secret, salt) : Encryptors.standard(secret, salt);
output.write(aes.decrypt(buffer));
return output.toByteArray();
}
catch (RuntimeException ex) {
throw ex;
}
catch (Exception ex) {
throw new IllegalStateException("Cannot decrypt", ex);
}
}
private static boolean isHex(String input) {
try {
Hex.decode(input);
return true;
}
catch (Exception ex) {
return false;
}
}
public boolean canDecrypt() {
return this.privateKey != null;
}
}View on GitHub (pinned to 96852e8860)
Solutions
- Ensure decryption uses the exact same RsaSecretEncryptor/key pair that produced the ciphertext (same keystore, alias, and key).
- Verify the input is the raw ciphertext in the form the encryptor expects (bytes, not re-encoded hex/base64 unless matching how it was encrypted).
- Check that encrypt/decrypt use the same algorithm configuration; mixed-algorithm round trips fail by design (public key cannot decrypt its own output).
- Catch IllegalStateException and inspect getCause() to distinguish BadPadding/IllegalBlockSize (wrong key/data) from other failures.
- If keys were rotated, re-encrypt data with the new key before discarding the old one.
Example fix
// before
String plain = new String(encryptor.decrypt(cipherText));
// after
try {
String plain = new String(encryptor.decrypt(cipherText));
} catch (IllegalStateException ex) {
// ex.getCause() is the JCE failure: usually wrong key or corrupt ciphertext
throw new DecryptionException("Ciphertext not decryptable with configured RSA key", ex);
} Defensive patterns
Strategy: try-catch
Validate before calling
// Before decrypting, confirm you hold the same encryptor/key that encrypted the data
if (ciphertext == null || ciphertext.length == 0) {
throw new IllegalArgumentException("no ciphertext to decrypt");
}
// If ciphertext was stored as text, decode it exactly as it was encoded on write (hex vs base64) Type guard
boolean canDecrypt(RsaSecretEncryptor e, byte[] stored, byte[] probe) {
try { e.decrypt(e.encrypt(new byte[0])); return true; } catch (Exception ex) { return false; }
} Try / catch
try {
byte[] plain = encryptor.decrypt(ciphertext);
} catch (IllegalStateException ex) {
// cause is the JCE failure (wrong key / corrupt data)
logger.warn("Decryption failed: {}", ex.getCause() == null ? ex.getMessage() : ex.getCause().toString());
throw new DecryptionException(ex);
} Prevention
- Store the key/keystore version alongside ciphertext so the right encryptor can be selected on decrypt
- Never rotate keys without a re-encryption migration for existing data
- Do not re-encode ciphertext (hex/base64) inconsistently between encrypt and decrypt paths
- Log only ex.getCause() type, never ciphertext or keys, when handling this error
- Add round-trip encrypt/decrypt tests for each environment's key configuration
When it happens
Trigger: Calling decrypt() on ciphertext produced with a different key or algorithm; RSA cipher failure such as BadPaddingException/IllegalBlockSizeException because the data is not valid RSA ciphertext for this encryptor; using an encryptor whose key pair does not match the one used to encrypt; decryption path taken when the input was actually encrypted with the public-key-only mode or vice versa (see tests roundTripWithMixedAlgorithm, roundTripWithPublicKeyEncryption, publicKeyCannotDecrypt).
Common situations: Rotating or regenerating the keystore/KeyPair while old ciphertexts remain in the database; sharing ciphertext between environments with different keys; decrypting base64/hex-decoded data that was encrypted by another library or algorithm; a keystore alias pointing to the wrong private key.
Related errors
- Cannot load keys from store:
- unable to encrypt/decrypt
- Unable to initialize due to invalid decryption parameter spe
- Unable to invoke Cipher due to bad padding
- Only RSA is currently supported, but algorithm was
AI-assisted analysis of spring-projects/spring-security@96852e8860 (2026-09-10).
Data as JSON: /api/errors/05ad1ca76747a554.
Report an issue: GitHub.