yiisoft/yii2 · error · yii\base\Exception
OpenSSL failure on decryption: {error}
Error message
OpenSSL failure on decryption: {error} What it means
Security::decryptByKey()/decryptByPassword() first verify the MAC via validateData() — a wrong key or password returns false without throwing — and only then call openssl_decrypt(); a false return is converted into yii\base\Exception with OpenSSL's error text. Reaching the throw means the MAC passed but OpenSSL still could not decrypt: typically the cipher/block-size settings differ from those used at encryption time, or the runtime OpenSSL (e.g. OpenSSL 3 legacy provider) refuses the stored data's method.
Source
Thrown at framework/base/Security.php:278
$keySalt = StringHelper::byteSubstr($data, 0, $keySize);
if ($passwordBased) {
$key = $this->pbkdf2($this->kdfHash, $secret, $keySalt, $this->derivationIterations, $keySize);
} else {
$key = $this->hkdf($this->kdfHash, $secret, $keySalt, $info, $keySize);
}
$authKey = $this->hkdf($this->kdfHash, $key, null, $this->authKeyInfo, $keySize);
$data = $this->validateData(StringHelper::byteSubstr($data, $keySize, null), $authKey);
if ($data === false) {
return false;
}
$iv = StringHelper::byteSubstr($data, 0, $blockSize);
$encrypted = StringHelper::byteSubstr($data, $blockSize, null);
$decrypted = openssl_decrypt($encrypted, $this->cipher, $key, OPENSSL_RAW_DATA, $iv);
if ($decrypted === false) {
throw new \yii\base\Exception('OpenSSL failure on decryption: ' . openssl_error_string());
}
return $decrypted;
}
/**
* Derives a key from the given input key using the standard HKDF algorithm.
* Implements HKDF specified in [RFC 5869](https://tools.ietf.org/html/rfc5869).
* Recommend use one of the SHA-2 hash algorithms: sha224, sha256, sha384 or sha512.
* @param string $algo a hash algorithm supported by `hash_hmac()`, e.g. 'SHA-256'
* @param string $inputKey the source key
* @param string|null $salt the random salt
* @param string|null $info optional info to bind the derived key material to application-
* and context-specific information, e.g. a user ID or API version, see
* [RFC 5869](https://tools.ietf.org/html/rfc5869)
* @param int $length length of the output key in bytes. If 0, the output key is
* the length of the hash algorithm output.
* @throws InvalidArgumentException when HMAC generation fails.View on GitHub (pinned to 66f00d18a2)
Solutions
- Restore the exact cipher/kdf settings in force when the data was encrypted — check git history of the security config and instantiate a dedicated Security instance with those values for legacy data.
- On OpenSSL 3, enable the legacy provider for old methods, or run a one-time re-encryption migration to a modern cipher.
- Store ciphertext in binary-safe BLOB columns with sufficient length, never TEXT/VARCHAR.
- Catch the Exception and treat the record as undecryptable: fail closed, alert, and do not retry blindly.
Example fix
// before
$plain = Yii::$app->security->decryptByKey($row['secret'], $key); // cipher changed since encryption
// after
$legacy = new \yii\base\Security(['cipher' => 'AES-256-CBC']); // settings used when data was written
$plain = $legacy->decryptByKey($row['secret'], $key);
if ($plain === false) {
// MAC mismatch — wrong key or corrupted payload
} Defensive patterns
Strategy: try-catch
Validate before calling
// Guard the ciphertext before decrypting: sane length (keySalt + MAC + IV + data)
$minLength = Yii::$app->security->derivationKeySize ?? 32; // at least keySalt + MAC bytes
if ($stored === null || strlen($stored) < 64) {
return null; // not decryptable — treat as absent
}
try {
return Yii::$app->security->decryptByKey($stored, $key);
} catch (\yii\base\Exception $e) {
\Yii::error('Stored ciphertext undecryptable: ' . $e->getMessage(), 'security');
return null;
} Try / catch
try {
$plain = Yii::$app->security->decryptByKey($data, $key);
} catch (\yii\base\Exception $e) {
// MAC passed but OpenSSL failed — cipher/config drift or OpenSSL 3 legacy refusal
\Yii::error('Decrypt failed: ' . $e->getMessage(), 'security');
$plain = false;
}
if ($plain === false) {
// wrong key OR undecryptable — handle as invalid data, fail closed
} Prevention
- Version the encryption settings: store a header byte/key-derivation tag with each ciphertext so legacy blobs can be decrypted with matching settings.
- Never change cipher/KDF config without a planned re-encryption migration.
- Use binary-safe BLOB columns for ciphertext; avoid charset conversions on the column.
- On OpenSSL 3 hosts, decide up front whether legacy ciphers are needed and configure providers explicitly.
When it happens
Trigger: Changing Yii::$app->security->cipher (e.g. AES-256-CBC to AES-128-CBC) after data was encrypted — MAC still passes because the auth key does not depend on the cipher, but IV/ciphertext slicing breaks; decrypting legacy data on OpenSSL 3.x where the original method moved to the legacy provider; runtime config drift between machines sharing one database of encrypted blobs.
Common situations: Upgrading PHP 7.4 to 8.x with OpenSSL 1.1 to 3.x in between; an ops 'cleanup' of security component defaults without re-encryption; multi-region setups where one node has different OpenSSL; decrypting data in a new integration written years after encryption.
Related errors
- Encryption requires the OpenSSL PHP extension
- OpenSSL failure on encryption: {error}
- {cipher} is not an allowed cipher
- Invalid parameters to hash_hkdf()
- Invalid parameters to hash_pbkdf2()
AI-assisted analysis of yiisoft/yii2@66f00d18a2 (2026-08-17).
Data as JSON: /api/errors/15f7952996c8cfa5.
Report an issue: GitHub.