{"record":{"id":"f6cb136f58fabeb0","repo":"quickwit-oss/quickwit","slug":"private-key-in-key-path-does-not-match-certifi","errorCode":null,"errorMessage":"private key in `{key_path}` does not match certificate in `{cert_path}`","messagePattern":"private key in `(.+?)` does not match certificate in `(.+?)`","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"quickwit/quickwit-transport/src/tls.rs","lineNumber":108,"sourceCode":"    let certs = load_certs(cert_path)?;\n    if certs.is_empty() {\n        anyhow::bail!(\"no certificate found in `{cert_path}`\");\n    }\n    let key = load_private_key(key_path)?;\n    let crypto_provider = rustls::crypto::CryptoProvider::get_default()\n        .context(\"no default rustls crypto provider is installed\")?;\n    let signing_key = crypto_provider\n        .key_provider\n        .load_private_key(key)\n        .with_context(|| format!(\"private key in `{key_path}` is not usable\"))?;\n    let certified_key = CertifiedKey::new(certs, signing_key);\n    // Guard against swapping in a mismatched cert/key pair, e.g. if the reload task reads the two\n    // files mid-rotation. A definite mismatch is fatal; an inconclusive result (key type that\n    // cannot expose its public key) is tolerated since we cannot do better.\n    match certified_key.keys_match() {\n        Ok(()) => {}\n        Err(rustls::Error::InconsistentKeys(rustls::InconsistentKeys::KeyMismatch)) => {\n            anyhow::bail!(\n                \"private key in `{key_path}` does not match certificate in `{cert_path}`\"\n            );\n        }\n        Err(error) => {\n            warn!(\"could not verify that private key matches certificate: {error}\");\n        }\n    }\n    Ok(certified_key)\n}\n\n/// A cert resolver whose certificate can be swapped atomically at runtime. rustls calls `resolve`\n/// on each handshake, so the latest stored certificate is always served. The same resolver works\n/// for both server handshakes ([`ResolvesServerCert`]) and client handshakes\n/// ([`ResolvesClientCert`], for gRPC mTLS client identities).\npub(crate) struct ReloadableCertResolver {\n    cert_path: String,\n    key_path: String,\n    certified_key: ArcSwap<CertifiedKey>,","sourceCodeStart":90,"sourceCodeEnd":126,"githubUrl":"https://github.com/quickwit-oss/quickwit/blob/a39730c5cdcd1a4fe798403737ae293999ea21f8/quickwit/quickwit-transport/src/tls.rs#L90-L126","documentation":"After assembling a rustls CertifiedKey, quickwit explicitly calls `keys_match()` to verify the private key's public half equals the certificate's public key. A definite KeyMismatch is fatal — bail with this message. Inconclusive results (key type can't expose its public key) are only logged as a warning, not fatal. This guards against serving TLS with a cert/key pair from different rotations.","triggerScenarios":"TLS reload (reload_and_compare) or initial load where cert_path and key_path files hold mismatched pairs — e.g. the reload task reads the two files mid-rotation and picks up new cert with old key.","commonSituations":"Certificate rotation replacing cert.pem and key.pem non-atomically; copying a renewed certificate without renewing the key; misconfigured paths pointing to two different services' pairs.","solutions":["Redeploy a matching cert/key pair (both from the same issuance).","Make rotation atomic: write both files to temp names, validate, then rename — so reload never sees a mixed pair.","If rotation is the cause, reload again once both files are updated."],"exampleFix":"// before: cp new-cert.pem cert.pem; cp new-key.pem key.pem  (non-atomic)\n// after: install both atomically\ncp new-cert.pem cert.pem.tmp && cp new-key.pem key.pem.tmp\nmv cert.pem.tmp cert.pem && mv key.pem.tmp key.pem","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"match tls_config.load() {\n    Err(e) if e.to_string().contains(\"does not match certificate\") => {\n        // cert/key pair out of sync — re-deploy matching pair and reload\n    }\n    other => other?,\n}","preventionTips":["Rotate cert and key atomically so reload never sees a mixed pair.","Verify pairs match offline with openssl before deploying.","Always issue cert and key together from the same CA/issuance."],"tags":["tls","certificate","key-mismatch"],"backgroundTag":"authentication-required","analyzedSha":"a39730c5cdcd1a4fe798403737ae293999ea21f8","analyzedAt":"2026-09-08T13:19:37.784Z","contentChangedAt":"2026-09-08T13:19:37.784Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}