{"record":{"id":"4a1d68fe3c99ea16","repo":"passbolt/passbolt_api","slug":"decryption-failed","errorCode":null,"errorMessage":"Decryption failed.","messagePattern":"Decryption failed\\.","errorType":"exception","errorClass":"Cake\\Core\\Exception\\CakeException","httpStatus":null,"severity":"critical","filePath":"src/Utility/OpenPGP/Backends/Gnupg.php","lineNumber":398,"sourceCode":"        $decrypted = false;\n        $fingerprint = null;\n        $signatureInfo = null;\n        $this->assertDecryptKey();\n        if ($verifySignature) {\n            $this->assertVerifyKey();\n            $fingerprint = $this->_verifyKeyFingerprint;\n            $this->clearVerifyKeys();\n        }\n        try {\n            if ($verifySignature === false) {\n                $decrypted = $this->_gpg->decrypt($text);\n            } else {\n                /** @psalm-suppress InvalidArgument  */\n                $signatureInfo = $this->_gpg->decryptverify($text, $decrypted);\n            }\n        } catch (Exception $e) {\n            $this->clearDecryptKeys();\n            throw new CakeException(__('Decryption failed.') . ' ' . $e->getMessage(), null, $e);\n        }\n        $this->clearDecryptKeys();\n\n        if ($decrypted === false) {\n            throw new CakeException(__('Decryption failed.'));\n        }\n        if ($verifySignature) {\n            if (empty($signatureInfo) || $signatureInfo[0]['fingerprint'] !== $fingerprint) {\n                $msg = __('Expected {0} and got {1}.', $fingerprint, $signatureInfo[0]['fingerprint']);\n                $msg = __('Decryption failed. Invalid signature.') . ' ' . $msg;\n                throw new CakeException($msg);\n            }\n        }\n\n        return $decrypted;\n    }\n\n    /**","sourceCodeStart":380,"sourceCodeEnd":416,"githubUrl":"https://github.com/passbolt/passbolt_api/blob/31c1bbc10f32808a607fa9bd81891e898779c0bc/src/Utility/OpenPGP/Backends/Gnupg.php#L380-L416","documentation":"Thrown when the gnupg extension's decrypt() or decryptverify() call throws while decrypting an armored message with the server private key. The backend clears the decrypt keys and rethrows as 'Decryption failed.' plus the gnupg error message.","triggerScenarios":"decrypt($text) where the ciphertext is corrupt/not valid OpenPGP, was encrypted for a different key, or the private key in the keyring is missing, passphrase-protected, or inaccessible.","commonSituations":"Data encrypted with a previous/rotated server key while the old private key was deleted; server key file re-imported with a passphrase; truncated or modified armored payload; keyring on read-only/incorrect-permission homedir; POST body corruption (encoding issues).","solutions":["Confirm the private key matching the recipient of the message is in the keyring and passphrase-less: gpg --list-secret-keys","Restore/rotate: re-import the old private key or decrypt legacy data before rotating passbolt.gpg.serverKey","Validate the armored input (no HTML entity mangling/truncation) before calling decrypt","Check GNUPGHOME permissions and that gpg-agent is running for the PHP user"],"exampleFix":"// before\n$decrypted = $gpg->decrypt($corruptedOrWrongKeyMessage); // CakeException: Decryption failed. ...\n// after\n$gpg->setDecryptKey($correctServerKeyFingerprint);\n$decrypted = $gpg->decrypt($validArmoredMessage);","handlingStrategy":"try-catch","validationCode":"if (!str_contains($armored, '-----BEGIN PGP MESSAGE-----')) {\n    throw new InvalidArgumentException('Payload is not an armored PGP message');\n}\n$hasSecret = str_contains((string) shell_exec('gpg --list-secret-keys --with-colons'), $serverFingerprint);","typeGuard":null,"tryCatchPattern":"try {\n    $plain = $gpg->decrypt($armored);\n} catch (CakeException $e) {\n    $this->log('Decrypt failed: ' . $e->getMessage());\n    throw new DecryptionException('Stored secret cannot be decrypted with current server key.', 0, $e);\n}","preventionTips":["Never delete old server private keys during rotation without re-encrypting legacy data","Keep unpassphrased server private keys in the keyring","Preserve armored payloads verbatim (beware of encoding/HTML mangling in transport)","Run `passbolt healthcheck` to detect keyring/key config drift"],"tags":["openpgp","decryption","gnupg","private-key"],"backgroundTag":"openpgp-decryption-failed","analyzedSha":"31c1bbc10f32808a607fa9bd81891e898779c0bc","analyzedAt":"2026-09-17T00:04:38.960Z","contentChangedAt":"2026-09-17T00:04:38.960Z","schemaVersion":2},"datasetVersion":"2026-09-21T04:17:39.646Z"}