How to check that a certificate and key belong together
A certificate and a private key are either a pair or not; a server that checks the pair at load, as nginx does, will not start with halves of two pairs [2] [6]. To check a certificate and key with OpenSSL, compare the public key inside the certificate with the public key derived from the private key: the same key means a pair [1]. Like the other SSL tools for decoding, converting and checking certificates, the check runs on your own machine, with no host, no connection and no upload.
- Runs
- on the machine that holds the key: both commands read local files [1]
- A mismatch shows as
key values mismatchfor two keys of the same type;key type mismatchorPrivate key does not match the certificate public keyfor an ECDSA key with an RSA certificate [6] [7]- Does not check
- names, dates or the chain a live host sends
Check before you restart
Match the certificate and key before you restart the service that uses them.
Copying the files to the server checks nothing. The check happens when the service loads them: OpenSSL returns an error when a private key does not belong to the certificate already set [2]. In our runs, openssl s_server given an unrelated key stopped with error setting private key and X509_check_private_key:key values mismatch; given an ECDSA key for an RSA certificate, it stopped with Private key does not match the certificate public key [7]. nginx documents key values mismatch for a key that does not match the first certificate in its file, and fails to start [6]. A service that does not start serves nothing, so the restart becomes an outage.
Run the check whenever one of the two files changes:
after a re-issue or renewal that generated a fresh key: a certificate request, as RFC 2986 defines it, carries the new public key [5], so the new certificate belongs to the new key, not to the one already installed [7];
when several
.keyfiles sit side by side after a copy;before you combine the two into a PFX file:
openssl pkcs12 -exportrefuses an unrelated key withNo cert in -in file 'example.com.crt' matches private key[7].
Why the pair has to match
A certificate carries a public key in its subjectPublicKeyInfo field, defined in RFC 5280 [3], and it is useful only to whoever holds the private key that goes with it.
In TLS 1.3 the server proves possession of that private key in the CertificateVerify message, a signature that the client checks against the public key in the certificate. RFC 9846 requires the client to end the handshake with a decrypt_error alert when the signature does not verify [4]. A server holding the wrong key cannot produce a valid signature, so no connection would complete. The signature can be RSA or ECDSA; either way, only the private key from the same key pair produces one that the public key accepts [4]. Nothing is wrong with the certificate, and nothing is wrong with the key: they are halves of two different pairs.
That is also why OpenSSL compares the two when a server loads them, before the first visitor arrives [2].
How the check works
Derive the public key from each file and compare the two. openssl x509 -pubkey prints the certificate's SubjectPublicKeyInfo block in PEM, and openssl pkey -pubout prints only the public part of a key [1]; for a pair, the two blocks are identical. In bash, one line compares them:
$ cmp -s <(openssl x509 -in example.com.crt -noout -pubkey) <(openssl pkey -in example.com.key -pubout) && echo match || echo mismatch
matchWith an unrelated key the same line printed mismatch [7]. The private key is read on your machine and never leaves it.
If someone else has to compare, send a digest rather than a file: pipe each command through openssl sha256 and compare the two lines. They were the same for the test pair [7], and a digest of a public key says nothing the certificate does not already show. The OpenSSL commands reference in the certificates section lists that digest form among the other everyday OpenSSL commands.
Why the modulus check fails on ECDSA keys
Another method compares moduli: openssl x509 -modulus for the certificate and openssl rsa -modulus for the key, each piped into a hash. openssl rsa processes RSA keys [1]. With an ECDSA pair from our test root, the certificate side printed Modulus=No modulus for this public key type and exited with status 0, while the key side failed with Not an RSA key [7].
The public-key comparison above returned match for the same ECDSA pair [7]. It does not depend on the key type, so one command covers both.
When a matching pair reports a mismatch
Two cases in our runs printed mismatch although the certificate and private key were one key pair: a bundle with the intermediate first fooled openssl x509, and a wrong passphrase stopped openssl pkey [7]. In both, one side of the comparison never produced the pair's public key.
The bundle starts with the wrong certificate
openssl x509 reads the first certificate in a file and prints that certificate's public key. For a full-chain file that starts with the site's certificate, the check returned match; for the same certificates with the intermediate first, it returned mismatch [7]. A server that loads the file through OpenSSL takes the first certificate as its own, and the chain loader expects the server certificate first, followed by the intermediates [2]; and nginx fails to start with key values mismatch when the bundle is concatenated in the wrong order [6]. Fix the order, not the private key.
The key is encrypted
An encrypted private key needs its passphrase before openssl pkey can derive the public key. With the wrong passphrase, the one-line check for a real key pair printed Could not read key from encrypted-key.pem on standard error and then mismatch, because cmp compared the certificate with empty input [7]. Read the error lines before you trust the verdict. Pass the passphrase with -passin file: rather than -passin pass:, which the OpenSSL manual warns is visible to utilities like ps [1].
Find the key that belongs to a certificate
When a folder holds several keys, compare the certificate's digest with each of them:
$ want=$(openssl x509 -in example.com.crt -noout -pubkey | openssl sha256)
$ for k in *.key; do [ "$(openssl pkey -in "$k" -pubout 2>/dev/null | openssl sha256)" = "$want" ] && echo "$k"; doneIn our runs the loop printed one name, the key the certificate was issued for. With an encrypted key in the folder, it stopped and waited for that key's passphrase, and 2>/dev/null hid the prompt; with </dev/null added to the loop, it skipped the encrypted key instead [7].
The same comparison works for a certificate signing request: put openssl req -in example.com.csr -noout -pubkey in place of the x509 command [1]. A request and its key matched in our runs, and the certificate issued for a fresh request did not match the key installed before it [7]. Checking the request before you submit it saves a re-issue.
To check more than the key, see how to read a CSR before it goes to the authority.
What the check does not tell you
A match proves only that the certificate and private key are one key pair. It does not tell you whether the certificate covers your names or is still valid; those questions have their own answers under certificates. On a live host, names, dates and the chain the server sends are part of SSL verification with OpenSSL.
FAQ
How do I know if my certificate matches my private key?
Compare the public key inside the certificate with the one derived from your private key. If they are the same, the files are a pair. If not, no configuration change will make them work together: install the certificate issued for the key you have, or the key that the certificate was issued for. [1] [2]
How do I verify that a private key matches a certificate with OpenSSL?
Can I use `openssl pkey -check` to check a certificate and key?
No. The -check option checks the consistency of a key pair for both public and private components, which is a test of the key itself. openssl pkey -in example.com.key -check -noout reads the key file alone and does not look at the certificate. In a further run with a self-signed test certificate and two RSA keys, one issued for the certificate and one unrelated, it printed Key is valid for both, while the public-key comparison printed match for the first and mismatch for the second. Use -check to test that a key file is sound, and the public-key comparison to test that it belongs to the certificate.
Sources
OpenSSL 3.0 manual pages: openssl-x509 (-pubkey, -modulus), openssl-pkey (-pubout), openssl-req (-pubkey), openssl-rsa, openssl-pkcs12 (-export, -inkey), openssl-passphrase-options (pass:, file:).
OpenSSL 3.0 manual, SSL_CTX_use_certificate: SSL_CTX_use_PrivateKey, SSL_CTX_use_certificate_file, SSL_CTX_use_certificate_chain_file.
RFC 5280, Internet X.509 Public Key Infrastructure Certificate and CRL Profile: §4.1.2.7.
RFC 9846, TLS 1.3: §4.5.2.
RFC 2986, PKCS #10: Certification Request Syntax Specification: §1.
nginx, Configuring HTTPS servers: SSL certificate chains.
Our runs: OpenSSL 3.0.13 and bash 5.2 on Linux, test certificates for example.com from our own lab root.
OpenSSL 3.0 manual: openssl-pkey (options -check, -noout), in addition to the options in item 1.
Lab run for the added FAQ: OpenSSL 3.0.13, a self-signed test certificate for example.com and two RSA test keys.