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
- 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).
- Re-check network interception (SSL inspection proxies) that may alter the JWKS payload.
- Ensure PHP's OpenSSL extension is installed and functional (php -m | grep openssl) to rule out local OpenSSL misbehavior.
- 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
- Validate JWKS payloads (base64-decodable x5c) before feeding them into token verification
- Bypass TLS-intercepting proxies for Azure endpoints
- Pin/verify TLS certificates when fetching JWKS
- Keep PHP OpenSSL extension up to date
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.
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- Failed to public key properties from certificate
- Failed to read public key from certificate
- Invalid JWKS endpoint response. Keys missing.
- No JWT key defined for Azure service.
- Cannot parse JWKS endpoint response.
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)