{"record":{"id":"30d0bb4d1745d598","repo":"passbolt/passbolt_api","slug":"the-key-0-cannot-be-used-to-encrypt","errorCode":null,"errorMessage":"The key {0} cannot be used to encrypt.","messagePattern":"The key (.+?) cannot be used to encrypt\\.","errorType":"exception","errorClass":"CakeException","httpStatus":500,"severity":"error","filePath":"src/Utility/OpenPGP/Backends/Gnupg.php","lineNumber":95,"sourceCode":"    {\n        $this->_encryptKeyFingerprint = null;\n        // Get the key info.\n        $encryptKeyInfo = $this->getPublicKeyInfo($armoredKey);\n        $fingerprint = $encryptKeyInfo['fingerprint'];\n\n        try {\n            $this->_gpg->addencryptkey($fingerprint);\n            $this->_encryptKeyFingerprint = $fingerprint;\n        } catch (Exception $e) {\n            // It didn't work, maybe only key is not in the keyring\n            // we import the key and retry\n            $this->importKeyIntoKeyring($armoredKey);\n            try {\n                $this->_gpg->addencryptkey($fingerprint);\n                $this->_encryptKeyFingerprint = $fingerprint;\n            } catch (Exception $e) {\n                $msg = __('The key {0} cannot be used to encrypt.', $fingerprint) . ' ' . $e->getMessage();\n                throw new CakeException($msg, null, $e);\n            }\n        }\n\n        return true;\n    }\n\n    /**\n     * Set a key for encryption.\n     *\n     * @param string $fingerprint fingerprint\n     * @throws \\Cake\\Core\\Exception\\CakeException if key is not present in keyring or there was an issue to use the key to encrypt\n     * @return bool true if success\n     */\n    public function setEncryptKeyFromFingerprint(string $fingerprint): bool\n    {\n        $this->_encryptKeyFingerprint = null;\n        $this->assertKeyInKeyring($fingerprint);\n        try {","sourceCodeStart":77,"sourceCodeEnd":113,"githubUrl":"https://github.com/passbolt/passbolt_api/blob/31c1bbc10f32808a607fa9bd81891e898779c0bc/src/Utility/OpenPGP/Backends/Gnupg.php#L77-L113","documentation":"setEncryptKey() parses an armored public key, extracts its fingerprint, and asks gnupg->addencryptkey() to register it. If that fails, it imports the key into the keyring and retries; if the retry also throws, it wraps the gnupg error in a CakeException 'The key {0} cannot be used to encrypt.' plus the underlying gnupg message. This means GnuPG could not select the key for encryption even after importing it.","triggerScenarios":"Calling setEncryptKey($armoredKey) when the keyring path (GNUPGHOME) is not writable by the web server user (import silently/exception fails), the armored key is corrupt or contains no usable encryption subkey (e.g. key with encryption capability revoked/removed, or a sign-only key), or the fingerprint from the parsed key does not match anything gpg can add.","commonSituations":"passbolt.gpg.keyring pointing to a directory not owned by www-data/http so imports fail; server keys imported into root's keyring instead of the web user's; user keys generated without an encryption-capable subkey; gpg agent errors or an outdated gpg binary producing gnupg exceptions on addencryptkey.","solutions":["Read the appended $e->getMessage() to see gnupg's actual cause, and check that passbolt.gpg.keyring exists, is owned by the web server user, and has correct permissions (700 dir owned by www-data/http).","Verify the key has a usable encryption subkey: gpg --list-packets on the armored key or `gpg --import` manually then `gpg --list-keys --with-subkey-fingerprints`; regenerate/re-export a key with encryption capability if missing.","Import the key manually as the web user (sudo -u www-data gpg --import key.asc) to see the raw gpg error, then fix accordingly (corrupt armor, unsupported algorithm, expired key).","If the key is expired or its encryption subkey revoked, the owner must generate/extend the key and re-upload it before encryption can proceed."],"exampleFix":"# before: keyring dir owned by root\nsudo chown -R www-data:www-data /var/lib/passbolt/.gnupg\nsudo chmod 700 /var/lib/passbolt/.gnupg\n# after: retry; if still failing, inspect key subkeys\ngpg --list-packets user_public.asc | grep -A2 'key *alg'\n# ensure a subkey with 'Encr' capability exists","handlingStrategy":"try-catch","validationCode":"use App\\Utility\\OpenPGP\\Backends\\Gnupg;\n$gpg = new Gnupg();\n$keyInfo = $gpg->getPublicKeyInfo($armoredKey); // throws early if armor is unparsable\n$fpr = $keyInfo['fingerprint'];\nif (!$gpg->isKeyInKeyring($fpr)) {\n    $gpg->importKeyIntoKeyring($armoredKey); // surface import errors before addencryptkey\n}\n","typeGuard":null,"tryCatchPattern":"try {\n    $gpg->setEncryptKey($armoredKey);\n} catch (\\Cake\\Core\\Exception\\CakeException $e) {\n    // $e->getMessage() contains the gnupg cause; check keyring perms and key subkeys\n    $this->log('Encryption key setup failed: ' . $e->getMessage());\n    throw new UserEncryptionKeyException('Recipient key unusable for encryption.');\n}\n","preventionTips":["Keep passbolt.gpg.keyring owned by the web server user with 700 permissions.","Validate user-uploaded keys for an encryption-capable, unexpired subkey at registration/upload time.","Test key import manually with sudo -u www-data gpg --import to catch environment issues early.","Pin and update gpg/gpgme versions; very old gpg builds can reject modern key algorithms."],"tags":["gpg","encryption","keyring","openpgp"],"backgroundTag":"key-cannot-be-used-for-encryption","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"}