passbolt/passbolt_api · error · App\Error\Exception\CustomValidationException

A valid OpenPGP key must be provided.

Error message

A valid OpenPGP key must be provided.

What it means

GpgkeysTable::buildEntityFromArmoredKey() parses the armored key with PublicKeyValidationService::getPublicKeyInfo(). Any parsing exception is converted to a CustomValidationException stating a valid OpenPGP key must be provided, with an armored_key.isParsable error detail.

Solutions

  1. Validate the armored key client-side/server-side with PublicKeyValidationService::getPublicKeyInfo() before calling buildEntityFromArmoredKey().
  2. Ensure the key includes full ASCII armor headers ('-----BEGIN PGP PUBLIC KEY BLOCK-----').
  3. Re-export the key with gpg --armor --export <fingerprint>.
  4. Catch the CustomValidationException and return the field error to the user.

Example fix

// before
$gpgkey = $this->Gpgkeys->buildEntityFromArmoredKey($data['armored_key'], $userId); // throws
// after
try { PublicKeyValidationService::getPublicKeyInfo($data['armored_key']); }
catch (Exception $e) { throw new BadRequestException('Please provide a valid OpenPGP public key.'); }
$gpgkey = $this->Gpgkeys->buildEntityFromArmoredKey($data['armored_key'], $userId);
Defensive patterns

Strategy: validation

Validate before calling

use App\Service\OpenPGP\PublicKeyValidationService;
try {
  $info = PublicKeyValidationService::getPublicKeyInfo($armoredKey);
} catch (Exception $e) {
  throw new BadRequestException('Provide a valid OpenPGP armored public key.');
}

Type guard

function looksLikeArmoredKey(?string $key): bool {
  return is_string($key)
    && str_contains($key, '-----BEGIN PGP PUBLIC KEY BLOCK-----')
    && str_contains($key, '-----END PGP PUBLIC KEY BLOCK-----');
}

Try / catch

try { $entity = $gpgkeysTable->buildEntityFromArmoredKey($armoredKey, $userId); }
catch (CustomValidationException $e) {
  return $this->response->withStatus(400)->withStringBody(json_encode($e->getErrors()));
}

Prevention

When it happens

Trigger: Calling buildEntityFromArmoredKey() with a string that is not a parseable armored OpenPGP key — wrong armor format, truncated key, non-key data, or corrupt ASCII armor.

Common situations: Users pasting SSH keys or certificates instead of PGP keys, copy/paste losing the BEGIN/END armor lines, keys generated by incompatible tooling, or wrong encoding.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


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

Appendix: source

Thrown at src/Model/Table/GpgkeysTable.php:281

    /**
     * Build a Gpgkey entity from the armored key
     *
     * @param string $armoredKey ascii armored key
     * @param string $userId uuid of the user using the key
     * @throws \InvalidArgumentException if the user is not valid
     * @throws \App\Error\Exception\ValidationException if the key info can not be parsed
     * @return \App\Model\Entity\Gpgkey
     */
    public function buildEntityFromArmoredKey(string $armoredKey, string $userId): Gpgkey
    {
        if (!Validation::uuid($userId)) {
            throw new InvalidArgumentException('The user identifier should be a valid UUID.');
        }
        try {
            $info = PublicKeyValidationService::getPublicKeyInfo($armoredKey);
        } catch (Exception $e) {
            throw new CustomValidationException(__('A valid OpenPGP key must be provided.'), [
                'armored_key' => [
                    'isParsable' => __('The OpenPGP armored key could not be parsed.'),
                ],
            ]);
        }

        $data = [
            'user_id' => $userId,
            'fingerprint' => $info['fingerprint'],
            'bits' => $info['bits'],
            'type' => $info['type'],
            'key_id' => $info['key_id'],
            'uid' => $info['uid'],
            'armored_key' => $armoredKey,
            'deleted' => false,
            'key_created' => new DateTime($info['key_created']),
            'expires' => null,
        ];

View on GitHub (pinned to 31c1bbc10f)