passbolt/passbolt_api · critical · InternalErrorException
The OpenPGP server key defined in the config cannot be used…
Error message
The OpenPGP server key defined in the config cannot be used to sign.
What it means
Thrown by setSignKeyWithServerKey when the GnuPG backend cannot set the configured server key as signing key (with passphrase), even after importing it into the keyring. Signing of server responses/JWT-related payloads fails. The wrapped gnupg exception message is appended to the 500 response.
Solutions
- Read the appended exception message: 'bad passphrase' → correct passbolt.gpg.serverKey.passphrase; 'get key failed' → the secret key is not in the keyring.
- Confirm the secret (private) key is imported: `sudo -H -u www-data gpg --home <gnupghome> --list-secret-keys`; if missing, import the private key file, not just the public one.
- Verify the fingerprint config matches the secret key and that the key has signing capability (`gpg --edit-key <fp>` then `showpref`/usage flags).
- Fix ownership/permissions of GNUPGHOME so the web server user can read the private key material.
- Restart gpg-agent / clear stale agent state if passphrase prompt loops occur: `gpgconf --kill gpg-agent` as the web user.
Example fix
// before $ gpg --import /etc/passbolt/serverkey.asc // public key only // after $ sudo -H -u www-data gpg --home /var/lib/passbolt/.gnupg --import /etc/passbolt/serverkey_private.asc
Defensive patterns
Strategy: try-catch
Validate before calling
$fp = Configure::read('passbolt.gpg.serverKey.fingerprint');
exec(sprintf('sudo -H -u www-data gpg --batch --pinentry-mode loopback --list-secret-keys %s 2>/dev/null', escapeshellarg($fp)), $out, $code);
if ($code !== 0) {
throw new Exception('Server secret key unavailable for signing');
}
if (!is_string(Configure::read('passbolt.gpg.serverKey.passphrase'))) {
throw new Exception('Passphrase config must be a string');
} Type guard
function isStringOrThrow(mixed $v, string $name): string
{
if (!is_string($v)) {
throw new InvalidArgumentException("{$name} must be a string");
}
return $v;
} Try / catch
try {
$gpg = $this->setSignKeyWithServerKey($gpg);
} catch (InternalErrorException $e) {
Log::error('Sign key setup failed: ' . $e->getMessage());
throw new InternalErrorException('Server cannot sign payloads; verify server key and passphrase.');
} Prevention
- Verify a secret key with signing capability exists: gpg --list-secret-keys as the web user.
- Keep passphrase in sync with the key; update config on every key rotation.
- Ensure gpg-agent loopback pinentry is available in headless environments.
- Check GNUPGHOME permissions after restores; gpg agent caches can hold stale passphrase state.
- Generate keys with both sign and encrypt capabilities.
When it happens
Trigger: Calling setSignKeyWithServerKey when: the secret key is absent from the keyring and import fails; the passphrase is wrong so gnupg cannot unlock the secret key; the key lacks signing capability (usage flag S); keyring/GNUPGHOME permission problems; fingerprint in config does not match the secret key.
Common situations: Key regenerated with a different passphrase than configured; only the public key (serverkey.asc) present while signing needs the private key; www-data cannot read private-keys-v1.d after backup restore; gpg-agent socket/permission issues in containers.
Understand the failure class
Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.
Related errors
- The anonymous user id should be a UUID
- The OpenPGP server key defined in the config cannot be used…
- A value for the theme should be provided.
- Can not upgrade. Some tables are missing.
- Could not sign the text.
AI-assisted analysis of passbolt/passbolt_api@31c1bbc10f (2026-09-17).
Data as JSON: /api/errors/896edee1835986ab.
Report an issue: GitHub.
Appendix: source
Thrown at src/Service/OpenPGP/OpenPGPCommonServerOperationsTrait.php:147
// Check if config contains fingerprint
$fingerprint = Configure::read('passbolt.gpg.serverKey.fingerprint');
$this->assertServerFingerprint($fingerprint);
// Check if config contains valid passphrase
$passphrase = Configure::read('passbolt.gpg.serverKey.passphrase');
$this->assertServerPassphrase($passphrase);
// Set sign key as the one from the server
try {
$gpg->setSignKeyFromFingerprint($fingerprint, $passphrase);
} catch (Exception $exception) {
try {
$gpg->importServerKeyInKeyring();
$gpg->setSignKeyFromFingerprint($fingerprint, $passphrase);
} catch (Exception $exception) {
$msg = __('The OpenPGP server key defined in the config cannot be used to sign.') . ' ';
$msg .= $exception->getMessage();
throw new InternalErrorException($msg, 500, $exception);
}
}
return $gpg;
}
/**
* @param mixed $fingerprint fingerprint
* @return void
* @throws \Cake\Http\Exception\InternalErrorException if the server key fingerprint cannot be loaded
*/
private function assertServerFingerprint(mixed $fingerprint): void
{
if (!is_string($fingerprint) || !PublicKeyValidationService::isValidFingerprint($fingerprint)) {
$msg = __('The config for the server private key fingerprint is not available or incomplete.');
throw new InternalErrorException($msg);
}
}View on GitHub (pinned to 31c1bbc10f)