{"record":{"id":"6f6d9e5e52227566","repo":"passbolt/passbolt_api","slug":"failed-to-read-certificate-0","errorCode":null,"errorMessage":"Failed to read certificate: {0}","messagePattern":"Failed to read certificate: (.+?)","errorType":"exception","errorClass":"InternalErrorException","httpStatus":500,"severity":"error","filePath":"plugins/PassboltEe/Sso/src/Utility/Azure/Provider/AzureProvider.php","lineNumber":205,"sourceCode":"     *\n     * @param array $responseKeys keys from Jwks endpoint\n     * @return array of openssl compatible keys\n     */\n    protected function parseJwksKeys(array $responseKeys): array\n    {\n        $keys = [];\n        foreach ($responseKeys as $keyinfo) {\n            if (isset($keyinfo['x5c']) && is_array($keyinfo['x5c'])) {\n                foreach ($keyinfo['x5c'] as $encodedkey) {\n                    $cert =\n                        '-----BEGIN CERTIFICATE-----' . PHP_EOL\n                        . chunk_split($encodedkey, 64, PHP_EOL)\n                        . '-----END CERTIFICATE-----' . PHP_EOL;\n\n                    $cert_object = openssl_x509_read($cert);\n\n                    if ($cert_object === false) {\n                        throw new InternalErrorException(__('Failed to read certificate: {0}', $encodedkey));\n                    }\n\n                    $pkey_object = openssl_pkey_get_public($cert_object);\n\n                    if ($pkey_object === false) {\n                        $msg = __('Failed to read public key from certificate: {0}', $encodedkey);\n                        throw new InternalErrorException($msg);\n                    }\n\n                    $pkey_array = openssl_pkey_get_details($pkey_object);\n\n                    if ($pkey_array === false) {\n                        $msg = __('Failed to public key properties from certificate: {0}', $encodedkey);\n                        throw new InternalErrorException($msg);\n                    }\n\n                    $publicKey = $pkey_array['key'];\n","sourceCodeStart":187,"sourceCodeEnd":223,"githubUrl":"https://github.com/passbolt/passbolt_api/blob/31c1bbc10f32808a607fa9bd81891e898779c0bc/plugins/PassboltEe/Sso/src/Utility/Azure/Provider/AzureProvider.php#L187-L223","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"null","handlingStrategy":"validation","validationCode":"foreach ($jwks['keys'] as $key) {\n    foreach ($key['x5c'] ?? [] as $x5c) {\n        $der = base64_decode($x5c, true);\n        if ($der === false || openssl_x509_parse($der) === false) {\n            throw new RuntimeException('Invalid x5c certificate in JWKS');\n        }\n    }\n}","typeGuard":"function isValidX5c(mixed $x5c): bool {\n    return is_string($x5c)\n        && base64_decode($x5c, true) !== false\n        && openssl_x509_read(\"-----BEGIN CERTIFICATE-----\\n\" . chunk_split($x5c, 64, \"\\n\") . \"-----END CERTIFICATE-----\\n\") !== false;\n}","tryCatchPattern":"try {\n    $keys = $provider->getJwtVerificationKeys();\n} catch (InternalErrorException $e) {\n    if (str_starts_with($e->getMessage(), 'Failed to read certificate')) {\n        Log::error('Corrupt x5c certificate in JWKS response');\n    }\n    throw $e;\n}","preventionTips":["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"],"tags":["sso","azure","openssl","x509","jwks"],"backgroundTag":"invalid-argument-format","analyzedSha":"31c1bbc10f32808a607fa9bd81891e898779c0bc","analyzedAt":"2026-09-17T00:04:38.960Z","contentChangedAt":"2026-09-17T00:04:38.960Z","schemaVersion":2},"datasetVersion":"2026-09-21T04:17:39.646Z"}