Skip to main content
RunBook Academy

Secrets, PKI & CertificatesV · X.509 Certificates in DepthX509

Proving a private key matches a certificate

Intermediate⏱ ~22 minopenssl

What you'll learn

  • Explain which structure is compared when a key is said to match a certificate
  • Prove or disprove correspondence with two commands that work for any key algorithm
  • Justify running the correspondence check before chain, name and expiry checks
  • Decide correctly between finding the original key and reissuing when the check fails

Prerequisites

Practice

Verified against OpenSSL 3.5.x teaching target; 3.0+ minimum · OpenSSH 10.x teaching target; 8.2+ minimum for certificate workflows · OpenBao 2.6.x · Smallstep step-ca 0.30.x · Certbot / Pebble Certbot current release; Pebble 2.10.x ACME test server · Kubernetes (cross-course target) 1.36.x · PostgreSQL 17.x · 2026-08-26

Not yet marked complete on this device.

Two files sit beside each other in a directory and a service will not start. One is a certificate, the other is a private key, and everybody assumes they belong together because they were copied at the same time. Very often they do not. Establishing correspondence takes two commands and about three seconds, it works for every key algorithm, and it either eliminates a whole family of explanations or hands you the answer immediately.

What matching actually means

A certificate carries a structure called subjectPublicKeyInfo, which holds an algorithm identifier and the public key bits. That structure is inside the signed body, so the issuer committed to it and it cannot be altered. A private key file, meanwhile, contains enough information to derive its own public key: for RSA the modulus and public exponent are part of the private key material, and for elliptic curve keys the public point is either stored or recomputed from the scalar and the curve.

So the question “does this key match this certificate” has a precise form. Derive the public key from the private key, encode it the same way the certificate does, and compare the two encodings byte for byte. If they are identical, the key can produce the signature that a client will verify with this certificate. If they differ by a single byte, the key cannot, and no amount of configuration will change that.

Comparing two blocks of base64 by eye is a poor use of an incident. Hashing each one and comparing digests is exact and fits on one line.

The two-command proof

Both commands print a public key in the same encoding, so both digests are taken over comparable input.

KEY=/etc/ssl/private/app.lab.example.key
CERT=/etc/ssl/certs/app.lab.example.pem
openssl pkey -in "$KEY" -pubout | openssl sha256
openssl x509 -in "$CERT" -noout -pubkey | openssl sha256

Against a key and certificate that genuinely belong together, the two lines are identical:

SHA2-256(stdin)= 75061de3387b8969c6c33ba0931ec0e541ad4c90a4ee2ec3e734e031af66874b
SHA2-256(stdin)= 75061de3387b8969c6c33ba0931ec0e541ad4c90a4ee2ec3e734e031af66874b

Three properties make this the right test. It is algorithm-agnostic: the same pair of commands works for RSA, for elliptic curve keys and for anything else OpenSSL can parse, which is not true of the older recipe that compares an RSA modulus and simply has nothing to compare on an elliptic-curve key. It is decisive: there is no partial match and no interpretation to argue about. And it leaks nothing, because both outputs are public values that the certificate already publishes.

The one prerequisite is read access to the private key, and if the key is passphrase-protected the first command will ask for the passphrase. That is worth knowing before you paste the command into an incident bridge with an audience watching.

flowchart LR
    K["Private key file"] --> P["Derive public key"]
    C["Certificate"] --> Q["Read subjectPublicKeyInfo"]
    P --> H1["SHA-256 digest"]
    Q --> H2["SHA-256 digest"]
    H1 --> D{"Digests equal?"}
    H2 --> D
    D -- "yes" --> Y["The pair is usable"]
    D -- "no" --> N["Find the right key, or reissue"]

The diagram makes the symmetry explicit. Two independent paths arrive at the same value when the pair is genuine, and the comparison at the end is the whole test. Nothing about names, dates, issuers or trust stores enters into it, which is exactly why it is such a clean first move.

Why this is the first check in so many incidents

Certificate deployment involves two files that must be replaced together, and almost every operational mishap breaks that pairing. Renewal writes a new certificate while a configuration file still points at last year’s key. A restore from backup brings back a key from a different date. A migration copies certificates from a build artefact and keys from a configuration management run, and the two sources are a week apart. Automation that renews with a fresh key each time leaves a stale key behind if the deployment step fails halfway.

The symptoms are unhelpful, and they vary by daemon. Most servers refuse to start and log a message naming both files without saying which one is wrong. Some start successfully and fail during the handshake instead, because the mismatch is only discovered when the server tries to sign with a key that does not correspond to the certificate it has just sent. From the client the failure looks like a generic TLS error, which sends teams looking at protocol versions and cipher configuration.

Running the correspondence check first cuts through all of that. It is cheap, it needs no network access, and it partitions the problem cleanly: if the digests match, the pairing is sound and the fault is elsewhere, in the chain, the names, the validity window or the trust store. If they differ, nothing else matters until the right key is found.

When the digests differ

There are only two honest outcomes, and choosing between them quickly matters more than choosing elegantly.

The first is that the correct key exists and has simply not been placed where the service expects it. Search the obvious hiding places before anything else. Automated clients frequently keep dated copies alongside the current one, with a stable path pointing at the newest set: certbot, for example, keeps a live directory of symlinks whose targets are the numbered files in an archive directory, so the previous generation is still on disk after a renewal. Backups, configuration management repositories and the host the certificate was originally generated on are the other three places worth checking. Run the digest comparison against each candidate key rather than guessing from filenames and timestamps.

The second outcome is that no matching key can be found. In that case the certificate is unusable and always will be, because the thing that made it useful was a private key nobody has. The correct response is reissuance with a freshly generated key, following the discipline from earlier in this part: generate on the host that will serve it, send only the request, and verify the issued certificate against the new key with the same two commands before reloading anything.

What must not happen is a search that quietly widens into copying keys between hosts to see which one works. That converts a contained availability incident into a key-handling problem, and every key touched in the process now has a history.

Production discipline

  1. Run the correspondence check before anything else. It costs one line, requires no network, and either eliminates a family of causes or gives you the answer outright.
  2. Deploy the certificate and its key as one unit. Whatever mechanism you use, it must fail as a whole rather than leaving a new certificate beside an old key.
  3. Verify the pair after deployment, not just before. Compare the digests on the target host after the files land, because the failure mode this lesson describes is created by the copy itself.
  4. Treat a key that arrived from another host as rotated. Once it has been on a machine it was not issued for, its custody is no longer what your records claim.

Cross-course references

  • Linux for Production Sysadmins - Part LXXII (Secrets) covers private key storage, passphrases and the keys you cannot rotate, which is the context a mismatch usually has to be resolved in.
  • Observability for Production Sysadmins - Part LXIV (TLSMonitoring) covers handshake failure as a signal, which is what a mismatch produces when a daemon starts successfully and fails later.
  • Kubernetes for Production Sysadmins - Part XXI (Secrets) covers Secrets that hold a certificate and a key together, where this pairing discipline applies to one object rather than two files.

Quiz

Knowledge check · 4 questions

  1. Q1. Why is comparing the SHA-256 digest of a derived public key preferable to the older recipe of comparing an RSA modulus?

  2. Q2. Deploying a private key that does not correspond to the certificate beside it exposes the key to clients, so it should be handled as a disclosure incident.

  3. Q3. Which structure inside a certificate is being compared when you check that a private key matches it, and which two openssl subcommands produce the two sides of the comparison?

  4. Q4. Lead the first ten minutes of this incident and say what you would establish, in what order, and why.

    At 23:10 UTC a scheduled renewal ran on web-01 and the reverse proxy failed to restart. The engineer on call sees a new certificate file dated tonight and a private key file dated fourteen months ago. A colleague suggests the intermediate is missing and proposes rebuilding the chain file. A second colleague suggests the CA issued from the wrong profile and proposes opening a reissuance ticket.

Passing score: 75%. Answers are checked in this browser.