passbolt/passbolt_api · error · InternalErrorException

Failed to read public key from certificate

Error message

Failed to read public key from certificate: {0}

What it means

Thrown by AzureProvider::parseJwksKeys when openssl_pkey_get_public() cannot extract a public key from an X.509 certificate that was successfully read. This means the certificate parsed but OpenSSL does not recognize/extract an embedded public key from it, so the RS256 verification key cannot be built.

Solutions

  1. Dump the failing certificate (echo the base64 x5c into a .cer file and run openssl x509 -text -noout) to inspect its embedded public key.
  2. Re-fetch the JWKS endpoint directly to confirm the content is genuine Azure AD key material and not intercepted.
  3. Update the PHP OpenSSL extension / PHP version if the certificate uses a key algorithm your OpenSSL build does not support.
  4. If using a custom/mock JWKS, replace the certificate with one generated via openssl req/x509 containing a standard RSA public key.

Example fix

null
Defensive patterns

Strategy: try-catch

Validate before calling

$cert = openssl_x509_read($pem);
$details = openssl_x509_parse($cert);
if (!isset($details) || stripos($details['signature_algorithm'] ?? '', 'rsa') === false) {
    throw new RuntimeException('Certificate does not contain a supported RSA public key');
}

Type guard

function certificateHasPublicKey($certObject): bool {
    $pkey = openssl_pkey_get_public($certObject);
    return $pkey !== false && openssl_pkey_get_details($pkey) !== false;
}

Try / catch

try {
    $keys = $provider->getJwtVerificationKeys();
} catch (InternalErrorException $e) {
    if (str_starts_with($e->getMessage(), 'Failed to read public key')) {
        Log::error('OpenSSL could not extract public key from x5c certificate');
    }
    throw $e;
}

Prevention

When it happens

Trigger: An x5c certificate in the JWKS response contains no usable public key (corrupt or non-standard certificate). Rare in practice because Azure AD certificates are well-formed; usually indicates the response payload was substituted or the certificate data was corrupted in transit.

Common situations: Tampered/intercepted JWKS payloads; custom JWKS fixtures using certificates without a public key; OpenSSL version issues failing on unusual key types (e.g. exotic algorithms OpenSSL was not built to support).

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


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

Appendix: source

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

        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'];

                    $keys[$keyinfo['kid']] = new Key($publicKey, 'RS256');
                }
            }
        }

        if (empty($keys)) {
            throw new InternalErrorException('No JWT key defined for Azure service.');

View on GitHub (pinned to 31c1bbc10f)