SonarSource/sonarqube · error · IllegalStateException

DECRYPTION_FAILURE_MESSAGE

Error message

DECRYPTION_FAILURE_MESSAGE

What it means

AesECBCipher.decrypt decodes Base64 ciphertext and decrypts it with the AES/ECB secret key loaded from the secret key file. BadPaddingException or IllegalBlockSizeException mean the data is not validly AES-encrypted with this key, so it is wrapped in this IllegalStateException. The library throws it because silently returning garbage from a bad ciphertext would be unsafe.

Source

Thrown at sonar-plugin-api-impl/src/main/java/org/sonar/api/config/internal/AesECBCipher.java:63

      cipher.init(javax.crypto.Cipher.ENCRYPT_MODE, loadSecretFile());
      byte[] cipherData = cipher.doFinal(clearText.getBytes(StandardCharsets.UTF_8.name()));
      return Base64.encodeBase64String(cipherData);
    } catch (RuntimeException e) {
      throw e;
    } catch (Exception e) {
      throw new IllegalStateException(e);
    }
  }

  @Override
  public String decrypt(String encryptedText) {
    try {
      javax.crypto.Cipher cipher = javax.crypto.Cipher.getInstance(CRYPTO_ALGO);
      cipher.init(javax.crypto.Cipher.DECRYPT_MODE, loadSecretFile());
      byte[] cipherData = cipher.doFinal(Base64.decodeBase64(StringUtils.trim(encryptedText)));
      return new String(cipherData, StandardCharsets.UTF_8);
    } catch (BadPaddingException | IllegalBlockSizeException e) {
      throw new IllegalStateException(DECRYPTION_FAILURE_MESSAGE, e);
    } catch (RuntimeException e) {
      throw e;
    } catch (Exception e) {
      throw new IllegalStateException(e);
    }
  }

}

View on GitHub (pinned to 184c821202)

Solutions

  1. Regenerate affected encrypted values with the current secret key (re-encrypt via the SonarQube UI/API)
  2. Ensure the same sonar-secret.txt used at encryption time is installed at the configured path
  3. Check the ciphertext is complete and not truncated (correct Base64 length, multiple of AES block size)
  4. Only call decrypt on values reported as encrypted (encryption.isEncrypted(value))

Example fix

// before
String clear = cipher.decrypt(suspiciousValue);
// after
if (cipher.isEncrypted(suspiciousValue)) {
  String clear = cipher.decrypt(suspiciousValue);
} else {
  String clear = suspiciousValue;
}
Defensive patterns

Strategy: validation

Validate before calling

// only decrypt values the Encryption reports as encrypted
if (encryptedText == null || encryptedText.trim().isEmpty()) {
  throw new IllegalArgumentException("no ciphertext");
}
byte[] raw = java.util.Base64.getDecoder().decode(encryptedText.trim());
if (raw.length == 0 || raw.length % 16 != 0) {
  throw new IllegalArgumentException("ciphertext is truncated or not AES-block aligned");
}

Try / catch

try {
  return cipher.decrypt(encryptedText);
} catch (IllegalStateException e) {
  log.error("Decryption failed - secret key mismatch or corrupt ciphertext: {}", e.getCause());
  throw new ConfigurationException("Re-encrypt this value with the current sonar-secret.txt", e);
}

Prevention

When it happens

Trigger: Calling decrypt() (or clearText()) with: text encrypted with a different secret key; truncated or manually corrupted Base64 input; plaintext or non-encrypted values passed to decrypt; ciphertext whose length is not a multiple of the AES block size.

Common situations: sonar-secret.txt regenerated or copied from another SonarQube instance so old encrypted values no longer match; settings pasted between environments; encrypted property truncated in a database or properties file; forgetting the {aes}... prefix handling and decrypting an unencrypted value.

Understand the failure class

Background: Checksum mismatch errors: "checksum verification failed", "digest mismatch", "expected vs actual checksum" — what they mean and how to fix them — this error's family across 41 libraries.

Related errors


AI-assisted analysis of SonarSource/sonarqube@184c821202 (2026-09-09). Data as JSON: /api/errors/86f5f08b189754ef. Report an issue: GitHub.