Given a certificate¹ and a private key file², how can I determine if the public key on the certificate matches the private key?

My initial thought was to simply encrypt some text with the public key on the cert, and attempt to decrypt it with the private key. If it roundtrips, we've got a winner. I just can't figure out how to do this with OpenSSL.

Alternatively, if I could generate the public key from the private key, I could just compare their fingerprints. SSH seems to have a command for this (ssh-keygen -y -f my_key > my_key.pub), but the hashes don't match. (I'm nearly certain I have the key corresponding to the cert, as the webserver is serving with it, but I'd like an easier way that spinning up a server to check.)

¹ a .crt file, in x509 format, I think. OpenSSL can read it with openssl x509 -text -in that_cert.crt
² An RSA private key

up vote 46 down vote accepted

I'm going to assume you have ssl.crt and ssl.key in your current directory.

If you want to see what's in your certificate it's

# openssl x509 -in ssl.crt -text -noout

Two of the things in here will be the RSA public Key's Modulus and Exponent (in hex).

If you want to see what's in your private key it's

# openssl rsa -in ssl.key -text -noout

Note the public key is usually in there (at the very least the modulus is required to be in there for the private key to work, and the public exponent is usually 65537 or 3). So you can simply check if the modulus and public exponent match. Granted, if you want to check that the private key is actually valid (that is d and e are valid RSA exponents for the modulus m), you would need to run

# openssl rsa -check -in ssl.key -noout

EDIT (2018): Please note if you are checking that a private key coming from an untrusted source corresponds with a certificate, you MUST CHECK that the private key is valid. See here for an example where not checking the validity of a "leaked" private key lead to a CA improperly revoking a certificate. You may skip this step if you know you validly generated the keypair.

Now you can simply generate the public key from both the certificate and the private key and then use diff to check that they don't differ:

# openssl x509 -in ssl.crt -pubkey -noout > from_crt.pub
# openssl rsa -in ssl.key -pubout > from_key.pub
# diff from_crt.pub from_key.pub

Or as a one liner that doesn't create files (using process substitution):

# diff  <(openssl x509 -in ssl.crt -pubkey -noout) <(openssl rsa -in ssl.key -pubout)

If the keys match, diff shouldn't return anything. (You probably will see "writing RSA key" output to stderr from the second command).

Note your webserver probably would loudly complain if the certificate and private key didn't match. E.g., with nginx using the wrong key (same size, same public exponent, but last year's key) for the certificate nginx is using:

# sudo /etc/init.d/nginx restart
* Restarting nginx nginx                                                                                         
nginx: [emerg] SSL_CTX_use_PrivateKey_file("/etc/nginx/ssl/private/wrong_key.key") failed 
(SSL: error:0B080074:x509 certificate routines:X509_check_private_key:key values mismatch)
nginx: configuration file /etc/nginx/nginx.conf test failed
  • How this solution compares with the one proposed at sslshopper.com/certificate-key-matcher.html ?? – Leandro David Sep 19 '16 at 22:55
  • 3
    @LeandroDavid They only check the modulus is the same for the cert and key (by comparing MD5 hashes). In common practice with randomly generated keypairs and the same public exponent (typically e=65537) that would work, though you could make two different keypairs with the same prime numbers and modulus but different e. For toy #s, let p=29, q=59, so N=1711 and totient phi=(p-1)(q-1)=1624, you could have one keypair with e1 = 3 and private exponent d1 = 1083 (note e1*d1 % phi = 1) and another keypair with e2=17 and d2=1433 (note e2*d2 % phi = 1), but note e1*d2 % phi != 1. – dr jimbob Sep 20 '16 at 3:02
  • 2
    That is it is possible to have one certificate with modulus N=1711, public exponent e=3, that doesn't work with a private key with same modulus N=1711 but a private exponent d=1433 that corresponds to a different public exponent. Again, in practice, this doesn't matter as e is almost always set to 65537 and unless you manually specify the primes and they are randomly selected, it is very improbable that you would generate have two keypairs with the same primes. But in principle if you want to check that a private key corresponds to a certificate, you need to check e*d % phi = 1. – dr jimbob Sep 20 '16 at 3:05
  • Interestingly, according to How I tricked Symantec with a Fake Private Key this Q&A was the only place on the 'net to describe a proper check of a private key. – TripeHound Apr 24 at 10:23
  • 1
    It's interesting in that it was somewhat of an aside (and my main method of checking that a private key corresponds with a public certificate via diff will fail for forged invalid private keys). Probably should edit to emphasize this point. – dr jimbob Apr 25 at 16:11

The accepted answer is correct, but it only works for RSA keys. It will need to be modified to match other types of keys.

Instead, here's a one-liner that works for all sorts of keys that openssl supports.

 cmp <(openssl x509 -pubkey -in certificate.pem -noout) <(openssl pkey -pubout -in private-key.pem -outform PEM)

It will return 'true' if and only if the private key matches the public key in the certificate.

Your Answer

 

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.