spring-projects/spring-security · error · IllegalStateException

Cannot encrypt

Error message

Cannot encrypt

What it means

RsaSecretEncryptor's static encrypt(byte[], PublicKey, RsaAlgorithm, String salt, boolean gcm) wraps any checked Exception (notably IOException from writeInt's ByteArrayOutputStream writes) into IllegalStateException("Cannot encrypt") with the cause attached. It signals failure while building the hybrid envelope (random AES key + RSA-encrypted key + payload).

Source

Thrown at crypto/src/main/java/org/springframework/security/crypto/encrypt/RsaSecretEncryptor.java:197

	private static byte[] encrypt(byte[] text, PublicKey key, RsaAlgorithm alg, String salt, boolean gcm) {
		byte[] random = KeyGenerators.secureRandom(16).generateKey();
		BytesEncryptor aes = gcm ? Encryptors.stronger(new String(Hex.encode(random)), salt)
				: Encryptors.standard(new String(Hex.encode(random)), salt);
		try {
			final Cipher cipher = Cipher.getInstance(alg.getJceName());
			cipher.init(Cipher.ENCRYPT_MODE, key);
			byte[] secret = cipher.doFinal(random);
			ByteArrayOutputStream result = new ByteArrayOutputStream(text.length + 20);
			writeInt(result, secret.length);
			result.write(secret);
			result.write(aes.encrypt(text));
			return result.toByteArray();
		}
		catch (RuntimeException ex) {
			throw ex;
		}
		catch (Exception ex) {
			throw new IllegalStateException("Cannot encrypt", ex);
		}
	}

	private static void writeInt(ByteArrayOutputStream result, int length) throws IOException {
		byte[] data = new byte[2];
		data[0] = (byte) ((length >> 8) & 0xFF);
		data[1] = (byte) (length & 0xFF);
		result.write(data);
	}

	private static int readInt(ByteArrayInputStream result) throws IOException {
		byte[] b = new byte[2];
		result.read(b);
		return ((b[0] & 0xFF) << 8) | (b[1] & 0xFF);
	}

	private static byte[] decrypt(byte[] text, @Nullable PrivateKey key, RsaAlgorithm alg, String salt, boolean gcm) {
		ByteArrayInputStream input = new ByteArrayInputStream(text);

View on GitHub (pinned to 96852e8860)

Solutions

  1. Inspect the wrapped cause via ex.getCause() to find the underlying exception.
  2. Ensure the JVM has adequate heap if encrypting very large payloads — consider streaming or splitting large data.
  3. Retry encryption; the random key generation makes failures essentially transient only if caused by resource pressure.
  4. Update/verify the Spring Security crypto version if the cause points to a library bug.

Example fix

// before
byte[] out = secretEncryptor.encrypt(hugePayload); // may wrap OOM/IO failure
// after
if (hugePayload.length > MAX_ENVELOPE_SIZE) {
    throw new IllegalArgumentException("payload too large for RSA envelope encryption");
}
byte[] out = secretEncryptor.encrypt(hugePayload);
Defensive patterns

Strategy: try-catch

Validate before calling

if (payload == null || payload.length == 0) throw new IllegalArgumentException("payload required");
if (publicKey == null) throw new IllegalArgumentException("public key required for encryption");

Type guard

boolean encryptable(RsaSecretEncryptor enc, byte[] payload) { return enc != null && payload != null && payload.length > 0; }

Try / catch

try {
    return encryptor.encrypt(payload);
} catch (IllegalStateException ex) {
    logger.error("Encryption failed: " + ex.getCause(), ex);
    throw new SecurityException("Encryption failed", ex);
}

Prevention

When it happens

Trigger: IOException while writing the envelope structure into the internal ByteArrayOutputStream during encryption; any checked exception thrown inside the static encrypt path.

Common situations: Extremely large output causing memory pressure/OutOfMemoryError surfacing near this path; subtle I/O failure in the envelope writer; debugging unexpected encryption failures where the real cause is in ex.getCause().

Related errors


AI-assisted analysis of spring-projects/spring-security@96852e8860 (2026-09-10). Data as JSON: /api/errors/ef71376d284ea4cb. Report an issue: GitHub.