サーバーに設定する秘密鍵と証明書が、ペアとして正しいか確認したいときは、それぞれから公開鍵を取り出して比較する。

$ diff <(openssl pkey -in server.key -pubout) <(openssl x509 -in server.crt -noout -pubkey) && echo MATCH
MATCH

秘密鍵と証明書の公開鍵が一致すれば、MATCHと表示される。一致しない場合は、diffが差分を出力して、MATCHは表示されない。

<(...)はプロセス置換で、bashやzshで使える。実行例はUbuntu 24.04のOpenSSL 3.0.13のもの。

公開鍵のハッシュ値で比較する

プロセス置換が使えない環境や、比較結果をログに残したい場合は、公開鍵のハッシュ値を比較する。

$ openssl pkey -in server.key -pubout | openssl dgst -sha256
SHA2-256(stdin)= 146e1e183da1299e3d7853b03e191eafd68244ddeaa8319d2b3f717783ec1e9f
$ openssl x509 -in server.crt -noout -pubkey | openssl dgst -sha256
SHA2-256(stdin)= 146e1e183da1299e3d7853b03e191eafd68244ddeaa8319d2b3f717783ec1e9f

ハッシュ値が同じであれば、対応している。ハッシュ値が異なる場合は、別の鍵から作られた証明書である。

$ openssl pkey -in other.key -pubout | openssl dgst -sha256
SHA2-256(stdin)= c1178968f53100d192cb9be7dcae135451536fb458c611a4f221516e091b811b

ハッシュ値は、公開鍵をPEM形式で出力した結果に対して計算している。公開鍵のハッシュ値は秘密情報ではないため、ログに残してもよい。

参考: 【openssl】ファイルのハッシュ値を計算する (dgst)

何を比較しているか

秘密鍵のファイルには、公開鍵を計算するために必要な情報が含まれている。openssl pkey -puboutは、秘密鍵から公開鍵を取り出す。証明書には公開鍵が含まれており、openssl x509 -pubkeyで取り出せる。

graph LR
  K[秘密鍵] -->|pkey -pubout| P1[公開鍵]
  C[証明書] -->|x509 -pubkey| P2[公開鍵]
  P1 --- M{一致するか}
  P2 --- M

秘密鍵とCSR、証明書とCSRの対応も、同じ方法で確認できる。CSRの公開鍵はopenssl req -noout -pubkeyで取り出す。

$ openssl req -in server.csr -noout -pubkey | openssl dgst -sha256
SHA2-256(stdin)= 146e1e183da1299e3d7853b03e191eafd68244ddeaa8319d2b3f717783ec1e9f

参考: 【openssl】秘密鍵から公開鍵を取り出す (pkey)

鍵の種類によらず使える

openssl pkeyとopenssl x509 -pubkeyは、鍵の種類を問わない。RSAだけでなく、ECDSAやEd25519の鍵でも同じ方法で比較できる。

$ openssl pkey -in ec.key -pubout | md5sum
f8d30a23db9ed4463ae717e58607825f  -
$ openssl x509 -in ec.crt -noout -pubkey | md5sum
f8d30a23db9ed4463ae717e58607825f  -

パスフレーズ付きの秘密鍵を比較する

パスフレーズで保護された秘密鍵は、-passinでパスフレーズを渡す。

$ openssl pkey -in server-enc.key -passin env:KEY_PW -pubout | openssl dgst -sha256

-passinを省略すると、対話的に入力を求められる。

証明書と秘密鍵を1つのファイルにまとめている場合

server.pemのように、証明書と秘密鍵を1つのファイルにまとめている場合も、同じファイルを両方のコマンドに渡せる。

$ openssl x509 -in combined.pem -noout -pubkey | openssl dgst -sha256
$ openssl pkey -in combined.pem -pubout | openssl dgst -sha256

複数の証明書を連結したファイルに対してopenssl x509を実行すると、先頭の1枚だけを処理する。サーバー証明書が先頭にあることを確認する。

署名で確認する

公開鍵の比較のほかに、秘密鍵で署名した結果を、証明書の公開鍵で検証する方法もある。

$ echo test > t.txt
$ openssl dgst -sha256 -sign server.key -out t.sig t.txt
$ openssl x509 -in server.crt -noout -pubkey > server.pub
$ openssl dgst -sha256 -verify server.pub -signature t.sig t.txt
Verified OK

対応していない証明書の公開鍵で検証すると、Verification failureになる。

$ openssl dgst -sha256 -verify other.pub -signature t.sig t.txt
Verification failure

署名を使う方法は手順が多い。確認だけが目的であれば、公開鍵の比較を使う。

RSAではmodulusを比較する方法もある

古い記事では、RSAのmodulus(公開鍵の一部)を比較する方法がよく紹介されている。

$ openssl x509 -in server.crt -noout -modulus | openssl md5
$ openssl rsa -in server.key -noout -modulus | openssl md5
$ openssl req -in server.csr -noout -modulus | openssl md5

3つのハッシュ値が一致すれば対応している。ただし、modulusはRSAの鍵にしかないため、ECDSAやEd25519の証明書では使えない。

$ openssl x509 -in ec.crt -noout -modulus
Modulus=No modulus for this public key type

また、modulusはRSAの公開鍵の一部でしかなく、公開指数などは比較できない。公開鍵全体を比較できるpkey -puboutのほうが確実である。

サーバーの起動時に検出される

openssl s_serverなどは、起動時に証明書と秘密鍵の対応を検証して、一致しない場合はエラーで終了する。

$ openssl s_server -accept 4433 -cert a.crt -key b.key -www
error setting private key
...error:05800074:x509 certificate routines:X509_check_private_key:key values mismatch...

Webサーバーの設定を変更する前に、公開鍵の比較で確認しておくと、設定後の起動失敗を避けられる。

バージョンによる違い

OpenSSL 3.6でも、ここまでの例は同じように動く。

macOS標準の/usr/bin/openssl(LibreSSL 3.3.6)でも、pkey -puboutとx509 -pubkeyの比較は動作する。modulusによる比較も使える。Ed25519の証明書は、LibreSSL 3.3.6が対応していないため、公開鍵を取り出せない。

参考

openssl-pkey - OpenSSL Documentation