passbolt/passbolt_api · error · InternalErrorException

Failed to read certificate

Error message

Failed to read certificate: {0}

What it means

Thrown by AzureProvider::parseJwksKeys when openssl_x509_read() fails to parse a base64 (x5c) value from the JWKS response as an X.509 certificate. The provider wraps each x5c entry in PEM markers and asks OpenSSL to parse it; a false return means the entry is not a valid DER/PEM certificate, so no verification key can be derived and token verification is aborted.

Solutions

  1. Fetch the jwks_uri from the server and validate that each x5c entry is valid base64 that decodes to a DER certificate (openssl x509 -inform DER).
  2. Re-check network interception (SSL inspection proxies) that may alter the JWKS payload.
  3. Ensure PHP's OpenSSL extension is installed and functional (php -m | grep openssl) to rule out local OpenSSL misbehavior.
  4. If using a custom JWKS source/fixture, regenerate the x5c certificate values from a real certificate.

Example fix

null
Defensive patterns

Strategy: validation

Validate before calling

foreach ($jwks['keys'] as $key) {
    foreach ($key['x5c'] ?? [] as $x5c) {
        $der = base64_decode($x5c, true);
        if ($der === false || openssl_x509_parse($der) === false) {
            throw new RuntimeException('Invalid x5c certificate in JWKS');
        }
    }
}

Type guard

function isValidX5c(mixed $x5c): bool {
    return is_string($x5c)
        && base64_decode($x5c, true) !== false
        && openssl_x509_read("-----BEGIN CERTIFICATE-----\n" . chunk_split($x5c, 64, "\n") . "-----END CERTIFICATE-----\n") !== false;
}

Try / catch

try {
    $keys = $provider->getJwtVerificationKeys();
} catch (InternalErrorException $e) {
    if (str_starts_with($e->getMessage(), 'Failed to read certificate')) {
        Log::error('Corrupt x5c certificate in JWKS response');
    }
    throw $e;
}

Prevention

When it happens

Trigger: A jwks key entry contains an 'x5c' array whose element is corrupt, truncated, or not base64-encoded certificate data — e.g. the JWKS endpoint returned malformed/intercepted content, or a custom/mock JWKS fixture with a bogus x5c value.

Common situations: Proxy or MITM device substituting JWKS content; hand-crafted test fixtures with placeholder x5c values; Azure changing/publishing keys where cached or tampered responses are served; string corruption from middleware that re-encodes the body.

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/6f6d9e5e52227566. Report an issue: GitHub.

Appendix: source

Thrown at plugins/PassboltEe/Sso/src/Utility/Azure/Provider/AzureProvider.php:205

     *
     * @param array $responseKeys keys from Jwks endpoint
     * @return array of openssl compatible keys
     */
    protected function parseJwksKeys(array $responseKeys): array
    {
        $keys = [];
        foreach ($responseKeys as $keyinfo) {
            if (isset($keyinfo['x5c']) && is_array($keyinfo['x5c'])) {
                foreach ($keyinfo['x5c'] as $encodedkey) {
                    $cert =
                        '-----BEGIN CERTIFICATE-----' . PHP_EOL
                        . chunk_split($encodedkey, 64, PHP_EOL)
                        . '-----END CERTIFICATE-----' . PHP_EOL;

                    $cert_object = openssl_x509_read($cert);

                    if ($cert_object === false) {
                        throw new InternalErrorException(__('Failed to read certificate: {0}', $encodedkey));
                    }

                    $pkey_object = openssl_pkey_get_public($cert_object);

                    if ($pkey_object === false) {
                        $msg = __('Failed to read public key from certificate: {0}', $encodedkey);
                        throw new InternalErrorException($msg);
                    }

                    $pkey_array = openssl_pkey_get_details($pkey_object);

                    if ($pkey_array === false) {
                        $msg = __('Failed to public key properties from certificate: {0}', $encodedkey);
                        throw new InternalErrorException($msg);
                    }

                    $publicKey = $pkey_array['key'];

View on GitHub (pinned to 31c1bbc10f)