passbolt/passbolt_api · error · CakeException

The key cannot be used to encrypt.

Error message

The key {0} cannot be used to encrypt.

What it means

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.

Solutions

  1. 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).
  2. 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.
  3. 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).
  4. 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.

Example fix

# before: keyring dir owned by root
sudo chown -R www-data:www-data /var/lib/passbolt/.gnupg
sudo chmod 700 /var/lib/passbolt/.gnupg
# after: retry; if still failing, inspect key subkeys
gpg --list-packets user_public.asc | grep -A2 'key *alg'
# ensure a subkey with 'Encr' capability exists
Defensive patterns

Strategy: try-catch

Validate before calling

use App\Utility\OpenPGP\Backends\Gnupg;
$gpg = new Gnupg();
$keyInfo = $gpg->getPublicKeyInfo($armoredKey); // throws early if armor is unparsable
$fpr = $keyInfo['fingerprint'];
if (!$gpg->isKeyInKeyring($fpr)) {
    $gpg->importKeyIntoKeyring($armoredKey); // surface import errors before addencryptkey
}

Try / catch

try {
    $gpg->setEncryptKey($armoredKey);
} catch (\Cake\Core\Exception\CakeException $e) {
    // $e->getMessage() contains the gnupg cause; check keyring perms and key subkeys
    $this->log('Encryption key setup failed: ' . $e->getMessage());
    throw new UserEncryptionKeyException('Recipient key unusable for encryption.');
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of passbolt/passbolt_api@31c1bbc10f (2026-09-17). Data as JSON: /api/errors/30d0bb4d1745d598. Report an issue: GitHub.

Appendix: source

Thrown at src/Utility/OpenPGP/Backends/Gnupg.php:95

    {
        $this->_encryptKeyFingerprint = null;
        // Get the key info.
        $encryptKeyInfo = $this->getPublicKeyInfo($armoredKey);
        $fingerprint = $encryptKeyInfo['fingerprint'];

        try {
            $this->_gpg->addencryptkey($fingerprint);
            $this->_encryptKeyFingerprint = $fingerprint;
        } catch (Exception $e) {
            // It didn't work, maybe only key is not in the keyring
            // we import the key and retry
            $this->importKeyIntoKeyring($armoredKey);
            try {
                $this->_gpg->addencryptkey($fingerprint);
                $this->_encryptKeyFingerprint = $fingerprint;
            } catch (Exception $e) {
                $msg = __('The key {0} cannot be used to encrypt.', $fingerprint) . ' ' . $e->getMessage();
                throw new CakeException($msg, null, $e);
            }
        }

        return true;
    }

    /**
     * Set a key for encryption.
     *
     * @param string $fingerprint fingerprint
     * @throws \Cake\Core\Exception\CakeException if key is not present in keyring or there was an issue to use the key to encrypt
     * @return bool true if success
     */
    public function setEncryptKeyFromFingerprint(string $fingerprint): bool
    {
        $this->_encryptKeyFingerprint = null;
        $this->assertKeyInKeyring($fingerprint);
        try {

View on GitHub (pinned to 31c1bbc10f)