プライベート認証局(「プライベートCA」や「オレオレ認証局」とも呼ばれます)は、会社など限られた組織内でのみ運用する認証局です。認証局の構築用スクリプトでプライベート認証局を構築運用していたのですが、トラブルが発生した時に手も足も出ず苦労したことがありました。そこで今回はOpenSSLを使って設定の意味を理解しながらプライベート認証局を構築する手順をまとめてみました。
OpenSSLのバージョン
今回使用したOpenSSLのバージョンは次の通りです。バージョンによっては設定ファイルで指定できるオプションに違いがありますので、必ずそのバージョンのマニュアル(man)をご確認ください。
openssl version OpenSSL 3.0.7 1 Nov 2022 (Library: OpenSSL 3.0.7 1 Nov 2022)
また、プライベート認証局を構築する際に、OpenSSLの設定ファイルを多用しますので、OpenSSLの設定ファイルの使い方やディレクティブ(OpenSSLでは「オプション」と呼びます)について理解しておきましょう。
関連記事:OpenSSLの設定ファイル(openssl.cnf)の使い方メモ
プライベート認証局構築の概要
この記事で説明するプライベート認証局およびその周辺の環境は次の通りです。
今回はOpenSSLがインストールされたLinuxマシンにプライベート認証局を構築します。詳細は後述しますがCRL配布点として、HTTP(HTTPSである必要はありません)で通信できるサーバーを1つ用意します。クライアントPCとWebサーバーは、発行した証明書の確認用です。
実際の運用では規模や重要度にもよりますが、プライベート認証局(すなわちルート認証局)が攻撃され秘密鍵が漏洩してしまうと被害が広範囲に及ぶため、プライベート認証局はオフラインで運用することが求められます。
CRL と OCSP について
発行した証明書の失効情報の提示方法として、CRL(Certificate Revocation List、証明書失効リスト)とOCSP(Online Certificate Status Protocol)があります。
OCSPは失効情報をリアルタイムで検索できるという利点がありますが、OCSPサーバー(OCSPレスポンダとも呼ばれます)を構築運用する必要がありますので、規模にもよりますがプライベート認証局としてはオーバースペックな仕組みになります。そのためこの記事ではCRLを採用しています。
これは余談になりますが、CA/Browserフォーラムが発行している Baseline Requirements(認証局の運用基準のようなものです)には、CRLまたはOCSPのいずれかの方法で失効情報の提示することとされています。パブリック認証局ではCRLとOCSPの両方で失効情報が提示されることが多いですが、世界最大の認証局Let's Encryptは、OCSPがインターネット上のプライバシーに多大なリスクをもたらすことを理由として、OCSPの提供を終了することを発表し、今後はCRLのみを提供するそうです。
プライベート認証局の設定ファイルの作成
プライベート認証局を構築するためのOpenSSLの設定ファイル「private-ca.cnf」を作成します。(ファイル名はなんでも構いません)
それでは設定ファイルのセクションごとにオプションの意味をひとつずつ確認していきましょう。(リンクになっているオプションをクリックするとマニュアルの該当箇所が開きます)
デフォルトセクション
デフォルトセクションでは各セクションで共通して使う値(いわゆる変数)を指定します。
# default section # プライベート認証局の名前を指定します。 ca_name = private-ca
[ ca ]
ca セクションは openssl ca
コマンドを実行した時に参照されるセクションです。内容としては default_ca オプションでプライベート認証局の設定を行うセクションを指定するのみです。
[ ca ] # プライベート認証局の設定を記述するセクションを指定します。 default_ca = CA_default
[ CA_default ]
CA_default セクション(ca セクションの default_ca オプションで指定)では、プライベート認証局に関する設定を行います。
[ CA_default ] # プライベート認証局の証明書の有効期限までの日数を指定します。 default_days = 10950 # CRL(証明書失効リスト)の有効期限までの日数を指定します。 default_crl_days = 365 # プライベート認証局に関するファイルの保存場所を指定します。(変数です) # 説明の都合のため相対パスで指定していますが、絶対パスで指定することが強く推奨されています。 dir = ./CA # プライベート認証局の証明書を指定します。 certificate = $dir/$ca_name.crt # プライベート認証局の秘密鍵を指定します。 private_key = $dir/private/$ca_name.key # テキストデータベース用のファイルを指定します。内容が空のファイルをあらかじめ作成しておく必要があります。 database = $dir/db/database # 次に使用するシリアル番号を16進数で記述したテキストファイルです。このファイルには初期状態で有効なシリアル番号が含まれている必要があります。 serial = $dir/db/serial # 次に使用するCRL番号を16進数で記述したテキストファイルです。このファイルが存在する場合のみCRLにCRL番号が挿入されます。 # このファイルには初期状態で有効なCRL番号が含まれている必要があります。 crlnumber = $dir/db/crlnumber # 証明書が格納される場所を指定します。発行した証明書のコピーがここに配置されます。 new_certs_dir = $dir/newcerts # 署名に使用するメッセージダイジェストを指定します default_md = sha256 # 同じサブジェクトを持つ複数の証明書の作成を許可するには「no」を設定します。(「no」を設定することが推奨されています) unique_subject = no # このオプションを「copy」に設定した場合CSRから証明書へと拡張フィールドがコピーされます。 # 限定的ですが証明書に含める内容を他人が操作できてしまう可能性がありますので「none」を設定しておきましょう。 copy_extensions = none # プライベート認証局が発行する証明書の識別情報のポリシーを設定するセクションを指定します。 policy = policy
[ policy ]
policy セクション(CA_default セクションの policy オプションで指定)ではプライベート認証局が発行する証明書の識別情報(「DN」とも呼ばれます)のポリシーを指定します。指定できる値は「match」「supplied」「optional」の3種類で意味は次の通りです。
- match:認証局の証明書の識別情報と一致しなければならない
- supplied:必ず存在しなければならない(必須)
- optional:存在してもよい(任意)
参考資料:man openssl-ca POLICY-FORMAT
[ policy ] countryName = optional stateOrProvinceName = optional localityName = optional organizationName = optional organizationalUnitName = optional commonName = supplied emailAddress = optional
上の設定では識別情報は commonName を必須(supplied)とするのみで、他の識別情報は任意(optional)として制限を設定していませんが、例えば組織名をプライベート認証局の識別情報と一致させたい場合は「organizationName = match」のように指定します。
[ req ]
req セクションは openssl req
コマンドを実行した時に参照されるセクションです。プライベート認証局の自己署名証明書を作成する時に1回だけ使われます。
[ req ] # デフォルトの鍵サイズをビット単位で指定します。 default_bits = 4096 # 署名に使用するメッセージダイジェストを指定します。 # サポートされているメッセージダイジェストは openssl list -digest-algorithms コマンドで確認できますが # 私のような素人には分かりずらいため openssl --help の「Message Digest commands」を見るのがよいと思います。 default_md = sha256 # 「yes」を指定すると秘密鍵のパスフレーズの設定が要求され秘密鍵が暗号化されます。 # 「no」を指定することでパスフレーズの入力を省略できますが、秘密鍵のパスフレーズの設定は強く推奨されています。 encrypt_key = yes # デフォルトでは識別情報はASCII文字として解釈されますが、このオプションを「yes」に指定するとUTF8文字として解釈されます。 utf8 = yes # 文字列のマスクを指定します「utf8only」を指定することがが推奨されています。 string_mask = utf8only # このオプションを「no」に設定すると識別情報を入力するための対話式のプロンプトが表示されなくなり、設定ファイルから値を取得します。 prompt = no # プライベート認証局の識別情報を設定するセクションを指定します。 distinguished_name = req_distinguished_name # プライベート認証局の証明書の拡張フィールドに含む情報を設定するセクションを指定します。 # 拡張フィールドはX.509バージョン3で追加されためかセクション名を「v3_XXX」とすることが多いようです。 req_extensions = v3_ca
[ req_distinguished_name ]
req_distinguished_name セクション(req セクションの distinguished_name オプションで指定)には、プライベート認証局の識別情報を設定します。書式は man openssl-req の EXAMPLES を参照してください。organizationName と commonName は、Webブラウザなどに証明書の情報として表示されますので、分かりやすい名前を設定しておきましょう。
[ req_distinguished_name ] countryName = JP organizationName = Apar Blog commonName = Apar Blog Private CA
[ v3_ca ]
v3_ca セクション(req セクションの req_extensions オプションで指定)ではプライベート認証局の自己署名証明書の拡張フィールドに含む情報を指定します。
拡張フィールドは以下の書式で指定し値は「,」カンマ区切りで複数指定できます。また値の先頭にその拡張が重要であることを示す「critica」を指定することで、その拡張が必ず処理されるように指定することができます。
識別子 = [critical, ]値(複数指定可能)
拡張フィールドの書式や詳細については man x509v3_config を参照してください。
[ v3_ca ] # 認証局の証明書であることを示す「CA:true」を指定します。重要な拡張であるため先頭に「critical」を指定します。 basicConstraints = critical,CA:true # 自己署名証明書の用途を指定します。重要な拡張であるため先頭に「critical」を指定します。 keyUsage = critical,keyCertSign,cRLSign # 「hash」を指定することによりRFC5280セクション4.2.1.2で指定されている処理が実行されます。(「hash」を指定することが推奨されています) subjectKeyIdentifier = hash
プライベート認証局の構築
完成した設定ファイルを使ってプライベート認証局を構築します。
プライベート認証局用のディレクトリとファイルの作成
CA_default セクションで指定したプライベート認証局が使うディレクトリとファイルを作成します。ここにはプライベート認証局の秘密鍵が保存されますので他のユーザーがアクセスできない場所(ホームディレクトリなど)に作成しましょう。
プライベート認証局用のディレクトリの作成
mkdir CA mkdir CA/db mkdir CA/newcerts mkdir CA/private chmod 700 CA/private
プライベート認証局用のファイルの作成
touch CA/db/database openssl rand -hex 16 > CA/db/serial echo 1001 > CA/db/crlnumber
設定ファイルの配置場所に決まりはありませんが、説明の都合のため設定ファイル「private-ca.cnf」はプライベート認証局用のディレクトリの直下に配置してください。
mv -i private-ca.cnf CA/
プライベート認証局の自己署名証明書の発行
まず、プライベート認証局の秘密鍵とCSRを作成します。
openssl req -new \ -config CA/private-ca.cnf \ -out CA/private-ca.csr \ -keyout CA/private/private-ca.key
プライベート認証局の秘密鍵用のパスフレーズの設定が要求されますので、適当なパスフレーズを設定してください。(設定したパスフレーズは忘れないように控えておきましょう)
Enter PEM pass phrase:(パスフレーズを入力) Verifying - Enter PEM pass phrase:(パスフレーズを入力)
作成した秘密鍵とCSRが設定ファイルの意図通りに作成されていることを確認しておきましょう。(-noout
オプションを指定することによりエンコードされた部分を非表示にできます)
作成した秘密鍵の確認
openssl pkey -text -noout -in CA/private/private-ca.key Private-Key: (4096 bit, 2 primes) (略)
作成したCSRの確認
openssl req -text -noout -in CA/private-ca.csr Certificate Request: Data: Version: 1 (0x0) Subject: C = JP, O = Apar Blog, CN = Apar Blog Private CA Subject Public Key Info: Public Key Algorithm: rsaEncryption Public-Key: (4096 bit) (略) Attributes: Requested Extensions: X509v3 Basic Constraints: critical CA:TRUE X509v3 Key Usage: critical Certificate Sign, CRL Sign X509v3 Subject Key Identifier: 8E:B6:D8:71:F8:4B:9D:1D:68:72:E6:FF:1E:DD:30:7A:45:9D:B2:17 (略)
秘密鍵とCSRに問題ないことが確認できたらプライベート認証局の自己署名証明書を発行します。-extensions
オプションで拡張フィールドの情報となる v3_ca セクションの指定が必要です。(CSRに拡張フィールドの情報が含まれていますが「copy_extensions = none」を設定しているため、CSRの拡張フィールドは自己署名証明書にコピーされません)
openssl ca -selfsign \ -config CA/private-ca.cnf \ -in CA/private-ca.csr \ -out CA/private-ca.crt \ -extensions v3_ca
秘密鍵のパスフレーズと確認が求められます。
Enter pass phrase for ./CA/private/private-ca.key:(パスフレーズを入力) Check that the request matches the signature Signature ok The Subject's Distinguished Name is as follows countryName :PRINTABLE:'JP' organizationName :ASN.1 12:'Apar Blog' commonName :ASN.1 12:'Apar Blog Private CA' Certificate is to be certified until Apr 14 22:57:31 2054 GMT (10950 days) Sign the certificate? [y/n]:y(←「y」を入力) 1 out of 1 certificate requests certified, commit? [y/n]y(←「y」を入力) Write out database with 1 new entries Data Base Updated
以上でプライベート認証局の構築完了です。自己署名証明書が設定ファイルの意図通りに作成されていることを確認します。
openssl x509 -text -noout -in CA/private-ca.crt Certificate: Data: Version: 3 (0x2) Serial Number: ca:ad:24:0b:ca:cd:6b:f1:94:51:e0:c6:96:20:ba:40 Signature Algorithm: sha256WithRSAEncryption Issuer: C = JP, O = Apar Blog, CN = Apar Blog Private CA Validity Not Before: Apr 21 22:57:31 2024 GMT Not After : Apr 14 22:57:31 2054 GMT Subject: C = JP, O = Apar Blog, CN = Apar Blog Private CA Subject Public Key Info: Public Key Algorithm: rsaEncryption Public-Key: (4096 bit) (略) X509v3 extensions: X509v3 Basic Constraints: critical CA:TRUE X509v3 Key Usage: critical Certificate Sign, CRL Sign X509v3 Subject Key Identifier: 8E:B6:D8:71:F8:4B:9D:1D:68:72:E6:FF:1E:DD:30:7A:45:9D:B2:17 Signature Algorithm: sha256WithRSAEncryption (略)
CRL(証明書失効リスト)の作成
続いて以下のコマンドを実行してCRL配布点で公開するCRL(証明書失効リスト)を作成します。
openssl ca -gencrl \ -config CA/private-ca.cnf \ -out CA/private-ca.crl
CRLの内容を確認してみましょう。まだ証明書を発行していなければ失効もしていないので「No Revoked Certificates.」(失効した証明書はない)の文字列が確認できると思います。
openssl crl -inform PEM -text -noout -in CA/private-ca.crl Certificate Revocation List (CRL): Version 2 (0x1) Signature Algorithm: sha256WithRSAEncryption Issuer: C = JP, O = Apar Blog, CN = Apar Blog Private CA Last Update: Apr 22 21:54:38 2024 GMT Next Update: Apr 22 21:54:38 2025 GMT CRL extensions: X509v3 CRL Number: 4097 No Revoked Certificates. Signature Algorithm: sha256WithRSAEncryption (略)
作成したCRLと署名の検証に使うプライベート認証局の証明書をCRL配布ポイントのサーバーで公開すれば完了です。CRL配布ポイントのサーバーには特別な仕組みは必要なく HTTP接続ができるWebサーバーであれば問題ありません。
(補足)CRL配布ポイントが HTTP である理由
HTTPSが当たり前になっている世の中で、なぜCRL配布ポイントは HTTP なのでしょうか?
例えばCRL配布ポイント(これを「A」とします)が HTTPS(TLS)で公開されていたとして、そのサーバー証明のCRL配布ポイントが「A」であった場合、HTTPS通信をはじめる前にそのサーバー証明が失効されているか否かを確認できないことが理由のようです。
また、CRLは公開すべき情報であるため、HTTPS で通信を暗号化する必要がなく、CRLの完全性は認証局の署名により確保されています。署名の検証は以下のコマンドで確認できます。
openssl crl -inform PEM -text -noout -in CA/private-ca.crl -CAfile CA/private-ca.crt verify OK (略)
サーバー証明書の発行
構築したプライベート認証局でサーバー証明書を発行してみましょう。
サーバー証明書発行用の設定ファイルの作成
プライベート認証局の設定ファイルは、認証局用の証明書を発行する設定になっていますので、サーバー証明書発行用の設定ファイル「server.cnf」を作成します。プライベート認証局の設定ファイルと共通で使える部分も多いため「private-ca.cnf」をインクルードして異なる部分のみを設定します。
# default section # プライベート認証局の設定ファイルをインクルードします。 # 説明の都合のため相対パスで指定していますが、絶対パスで指定することをお勧めします。 .include = CA/private-ca.cnf [ CA_default ] # 発行するサーバー証明書の有効期限までの日数を指定します。 default_days = 365 # 発行するサーバー証明書の拡張フィールドに含む情報を設定するセクションを指定します。 x509_extensions = v3_server # v3_server セクションでは発行するサーバー証明書の拡張フィールドに含む情報を指定します。拡張フィールドの値は「,」カンマ区切りで複数指定できます。 # 拡張フィールドの書式や詳細については man x509v3_config を参照してください。 [ v3_server ] # SAN(Subject Alternative Name)拡張の設定です。 # 発行した証明書を使うサイトのドメイン(FQDN)を「DNS:<ドメイン>」の書式で指定します。 subjectAltName = DNS:www.example.com # 発行するのはサーバー証明書ですので認証局の証明書ではないことを示す「CA:false」を指定します。重要な拡張であるため先頭に「critical」を指定します。 basicConstraints = critical,CA:false # 証明書の署名に使われた鍵を一意に特定する識別子(これを「AKID」と略します)を含めるか否かを指定します。 authorityKeyIdentifier = keyid:always # CRL配布ポイントを「URL:<CRL配布ポイントのURL>」の書式で指定します。 crlDistributionPoints = URI:http://crl.apar.jp/private-ca.crl # 認証局のアクセス情報として認証局の証明書の場所を「caIssuers;URI:<URL>」の書式で指定します。 authorityInfoAccess = caIssuers;URI:http://crl.apar.jp/private-ca.crt # 発行した証明書に含まれる公開鍵の用途を指定します。 # 今回発行するのはサーバー証明書のため「serverAuth」のみ指定していますが「clientAuth」(クライアント認証用)も合わせて設定することが多いようです。 extendedKeyUsage = serverAuth # 発行する証明書の用途を指定します。重要な拡張であるため先頭に「critical」を指定します。 keyUsage = critical,digitalSignature,keyEncipherment # 「hash」を指定することによりRFC5280セクション4.2.1.2で指定されている処理が実行されます。(「hash」を指定することが推奨されています) subjectKeyIdentifier = hash
作成した設定ファイル「server.cnf」は、説明の都合のためプライベート認証局用のディレクトリの直下に配置してください。
mv -i server.cnf CA/
秘密鍵とCSRの生成
確認用の Webサーバー(test.apar.jp)で、サーバー証明書用の秘密鍵を作成してCSR(証明書の署名リクエスト)を生成します。
秘密鍵を作成します。
openssl ecparam -name prime256v1 -genkey -out server.key
コモンネーム(CN)にサイトのドメイン(FQDN)を指定してCSRを生成します。
openssl req -new \ -key server.key \ -subj /CN=test.apar.jp \ -out server.csr
生成したCSRをプライベート認証局に渡してサーバー証明書の発行を依頼します。
サーバー証明書の発行
プライベート認証局では、依頼されたCSRのコモンネーム(CN)に設定されている値を確認します。サーバー証明書のCSRであれば、発行された証明書を使うサイトのドメインが設定されているはずです。
openssl req -text -noout -in server.csr
Certificate Request:
Data:
Version: 1 (0x0)
Subject: CN = test.apar.jp (←コモンネーム)
(略)
コモンネーム(CN)に設定されている値を、サーバー証明書発行用の設定ファイル「server.cnf」の「subjectAltName」拡張に設定します。
vi CA/server.cnf subjectAltName = DNS:www.example.com ↓ subjectAltName = DNS:test.apar.jp
【補足】
CSRのコモンネームに指定したサイトのドメインは、証明書を発行する時に証明書のコモンネームにコピーされます。昔はサイトのドメインを識別するためにこのコモンネームが使われていましたが、現在コモンネームは使用されていないため、拡張フィールドの SAN(Subject Alternative Name)拡張にサイトのドメインを指定する必要があります。CSRにSAN拡張を含めて生成してしまい、それを証明書にコピーする方法もありますが、その場合は、設定ファイルの「copy_extensions」オプションの値を「copy」に設定する必要があることに注意してください。
-config
オプションでサーバー証明書発行用の設定ファイル「server.cnf」を指定してサーバー証明書を発行します。
openssl ca \ -config CA/server.cnf \ -in server.csr \ -out server.crt
意図通りにサーバー証明書が発行されていることを確認しておきましょう。
openssl x509 -text -noout -in server.crt Certificate: Data: Version: 3 (0x2) Serial Number: ca:ad:24:0b:ca:cd:6b:f1:94:51:e0:c6:96:20:ba:41 Signature Algorithm: sha256WithRSAEncryption Issuer: C = JP, O = Apar Blog, CN = Apar Blog Private CA Validity Not Before: Apr 27 04:27:05 2024 GMT Not After : Apr 27 04:27:05 2025 GMT Subject: CN = test.apar.jp (略) X509v3 extensions: X509v3 Subject Alternative Name: DNS:test.apar.jp X509v3 Basic Constraints: critical CA:FALSE X509v3 Authority Key Identifier: 8E:B6:D8:71:F8:4B:9D:1D:68:72:E6:FF:1E:DD:30:7A:45:9D:B2:17 X509v3 CRL Distribution Points: Full Name: URI:http://crl.apar.jp/private-ca.crl X509v3 Extended Key Usage: TLS Web Server Authentication X509v3 Key Usage: critical Digital Signature, Key Encipherment X509v3 Subject Key Identifier: 5F:39:90:E9:28:FF:FB:8C:24:41:89:3B:C3:A1:16:EB:C4:BB:AF:1B Signature Algorithm: sha256WithRSAEncryption (略)
発行したサーバー証明書を、確認用のWebサーバーに渡してTLS通信ができるように設定してください。
プライベート認証局の証明書のインストール
クライアントPCに、プライベート認証局の証明書をインストールして正しく動作するか確認してみましょう。
今回は、OSに依存しないルートトラストストア(信頼できる認証局の証明書などを保管する場所)を持つWebブラウザFirefoxを使って確認します。
Firefoxの設定「プライバシーとセキュリティ」画面の「証明書を表示...」をクリックします。
「認証局証明書」を選択し「読み込む..」をクリックして、プライベート認証局の証明書「private-ca.crt」を選択します。
「この認証局によるウェブサイトの識別を信頼する」にチェックを入れて「OK」をクリックします。
プライベート認証局の証明書が表示されていることを確認して「OK」をクリックすればインストール完了です。
確認用のWebサーバーに接続して証明書の検証エラーが表示されなければOKです。
クライアント証明書の発行
続いて、構築したプライベート認証局でクライアント証明書を発行する手順です。
この記事では、秘密鍵の作成からクライアント証明書の発行までをすべてプライベート認証局で行う手順になっていますが、発行する証明書の秘密鍵を認証局で作成することが望ましくないケースもあります。このあたりは組織のポリシーに合わせて行なってください。
クライアント証明書発行用の設定ファイルの作成
クライアント証明書発行用の設定ファイル「client.cnf」を作成します。こちらもプライベート認証局の設定ファイルと共通で使える部分も多いため「private-ca.cnf」をインクルードして異なる部分のみを設定します。
# default section # プライベート認証局の設定ファイルをインクルードします。 # 説明の都合のため相対パスで指定していますが、絶対パスで指定することをお勧めします。 .include = CA/private-ca.cnf [ CA_default ] # 発行するクライアント証明書の有効期限までの日数を指定します。 default_days = 3650 # 発行するクライアント証明書の拡張フィールドに含む情報を設定するセクションを指定します。 x509_extensions = v3_client # v3_client セクションでは発行するクライアント証明書の拡張フィールドに含む情報を指定します。拡張フィールドの値は「,」カンマ区切りで複数指定できます。 # 拡張フィールドの書式や詳細については man x509v3_config を参照してください。 [ v3_client ] # 発行するのはクライアント証明書ですので認証局の証明書ではないことを示す「CA:false」を指定します。重要な拡張であるため先頭に「critical」を指定します。 basicConstraints = critical,CA:false # 証明書の署名に使われた鍵を一意に特定する識別子(これを「AKID」と略します)を含めるか否かを指定します。 authorityKeyIdentifier = keyid:always # CRL配布ポイントを「URL:<CRL配布ポイントのURL>」の書式で指定します。 crlDistributionPoints = URI:http://crl.apar.jp/private-ca.crl # 認証局のアクセス情報として認証局の証明書の場所を「caIssuers;URI:<URL>」の書式で指定します。 authorityInfoAccess = caIssuers;URI:http://crl.apar.jp/private-ca.crt # 発行した証明書に含まれる公開鍵の用途を指定します。クライアント証明書のため「clientAuth」のみを指定します。 extendedKeyUsage = clientAuth # 発行する証明書の用途を指定します。重要な拡張であるため先頭に「critical」を指定します。 keyUsage = critical,digitalSignature # 「hash」を指定することによりRFC5280セクション4.2.1.2で指定されている処理が実行されます。(「hash」を指定することが推奨されています) subjectKeyIdentifier = hash
作成した設定ファイル「client.cnf」は、説明の都合のためプライベート認証局用のディレクトリの直下に配置してください。
mv -i client.cnf CA/
秘密鍵とCSRの生成
秘密鍵を作成します。
openssl ecparam -name prime256v1 -genkey -out client001.key
コモンネーム(CN)にクライアントを一意に識別できる名前を指定してCSRを生成します。コモンネームはクライアント証明書の情報としてWebブラウザなどに表示されますので、分かりやすい名前を設定しておきましょう。
openssl req -new \ -key client001.key \ -subj "/CN=Apar Blog client001" \ -out client001.csr
クライアント証明書の発行
-config
オプションでクライアント証明書発行用の設定ファイル「client.cnf」を指定してクライアント証明書を発行します。
openssl ca \ -config CA/client.cnf \ -in client001.csr \ -out client001.crt
意図通りにクライアント証明書が発行されていることを確認します。
openssl x509 -text -noout -in client001.crt Certificate: Data: Version: 3 (0x2) Serial Number: 93:4a:b8:8b:75:36:a3:e5:47:18:80:99:4a:83:9c:85 Signature Algorithm: sha256WithRSAEncryption Issuer: C = JP, O = Apar Blog, CN = Apar Blog Private CA Validity Not Before: May 1 23:02:03 2024 GMT Not After : Apr 29 23:02:03 2034 GMT Subject: CN = Apar Blog client001 (略) X509v3 extensions: X509v3 Basic Constraints: critical CA:FALSE X509v3 Authority Key Identifier: CB:BF:ED:F6:DB:51:C5:04:EF:BC:ED:E5:17:BA:A6:3D:B2:98:25:84 X509v3 CRL Distribution Points: Full Name: URI:http://crl.apar.jp/private-ca.crl Authority Information Access: CA Issuers - URI:http://crl.apar.jp/private-ca.crt X509v3 Extended Key Usage: TLS Web Client Authentication X509v3 Key Usage: critical Digital Signature X509v3 Subject Key Identifier: D4:0D:5F:1E:D8:B0:5D:16:65:96:F8:FC:4F:BB:07:A2:FC:D3:42:02 Signature Algorithm: sha256WithRSAEncryption (略)
証明書をクライアントPCにインストールできるようにするため、発行したクライアント証明書と秘密鍵をPKCS#12形式に変換します。
openssl pkcs12 -export \ -legacy \ -in client001.crt \ -inkey client001.key \ -out client001.p12
【補足】 -legacy オプションについて
現時点(2024年5月)では、OpenSSL3.0以上で変換したPKCS#12ファイルは macOS のキーチェーンにインポートすることができません。おそらくですが、PKCS#12変換時に使われる暗号化アルゴリズムがOpenSSL3.0で変更されたのが原因のようです。そのため、macOS にクライアント証明書をインストールする場合は -legacy オプションをつけて、従来の暗号化アルゴリズムでPKCS#12形式に変換する必要があります。
クライアント証明書のインストール
クライアントPCに、クライアント証明書をインストールします。クライアントPC上でPKCS#12ファイルをダブルクリックすればクライアント証明書のインストールが開始しますので、表示された手順に従ってインストールしてください。
クライアント証明書認証の設定
確認用のWebサーバーにクライアント証明書認証の設定を行い動作を確認してみましょう。
以下は Apache httpd 2.4 でのクライアント証明書認証の設定例です。
<VirtualHost *:443> ServerName test.apar.jp DocumentRoot "/var/www/html" <Directory "/var/www/html"> Options FollowSymLinks AllowOverride None Require all granted </Directory> SSLEngine on SSLCertificateFile /etc/pki/tls/certs/server.crt SSLCertificateKeyFile /etc/pki/tls/private/server.key # クライアント証明書認証の設定例 SSLCACertificateFile /var/www/html/private-ca.crt SSLVerifyClient require SSLVerifyDepth 1 SSLCARevocationCheck leaf SSLCARevocationFile /var/www/html/private-ca.crl LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" \"%{SSL_CLIENT_S_DN_CN}x\"" clientauth ErrorLog logs/test.apar.jp_error_log CustomLog logs/test.apar.jp_access_log clientauth </VirtualHost>
【設定方法】
SSLCACertificateFile /var/www/html/private-ca.crt
クライアント証明書認証を発行した認証局の証明書を指定します。今回はプライベート認証局の証明書を指定します。
SSLVerifyClient require
このディレクティブに「require」を指定することでクライアント証明書による認証を必須にします。
SSLVerifyDepth 1
検証に必要な認証局の証明書の最大数を設定します。今回はプライベート認証局の証明書のみを指定していますので「1」を指定します。パブリック認証局では一般的にルート認証局から認証を受けた中間認証局が証明書を発行しますので、少なくともルート認証局と中間認証局の証明書が検証に必要になるため「2」以上を指定することになります。
SSLCARevocationCheck leaf
このディレクティブに「leaf」を指定することでクライアント証明書に対するCRL(証明書失効リスト)チェックのみを有効にします。「chain」を設定すると認証局の証明書も含め証明書チェーンすべてにCRLチェックが有効になります。
SSLCARevocationFile /var/www/html/private-ca.crl
CRL(証明書失効リスト)を指定します。
また LogFormat に「\"%{SSL_CLIENT_S_DN_CN}x\"」を追加することで、クライアント証明書のコモンネーム(CN)に指定されている値を Apache httpd のアクセスログに出力するように設定しています。このあたりはお好みで設定してください。
動作確認
確認用のWebサーバーにアクセスすると、クライアント証明書が要求されますので、インストールしたクライアント証明書を選択し(自動で選択される場合もあります)「OK」をクリックすれば認証されWebページが表示されるはずです。
発行した証明書の失効
発行した証明書を失効させる場合の手順です。例として先ほど発行したコモンネーム(CN)「Apar Blog client001」のクライアント証明書を失効させてみましょう。
プライベート認証局のテキストデータベースを検索して、失効対象の証明書のシリアル番号を確認します。
grep client001 CA/db/database V 340505224331Z 934AB88B7536A3E5471880994A839C86 unknown /CN=Apar Blog client001
上の例では"934AB〜"から始まるのが失効対象の証明書のシリアル番号です。CA/newcerts/ ディレクトリ以下に「<シリアル番号>.pem」のファイル名で、証明書のコピーが保管されていますので確認してみましょう。
openssl x509 -text -noout -in CA/newcerts/934AB88B7536A3E5471880994A839C86.pem Certificate: Data: Version: 3 (0x2) Serial Number: 93:4a:b8:8b:75:36:a3:e5:47:18:80:99:4a:83:9c:86 Signature Algorithm: sha256WithRSAEncryption Issuer: C = JP, O = Apar Blog, CN = Apar Blog Private CA Validity Not Before: May 7 22:43:31 2024 GMT Not After : May 5 22:43:31 2034 GMT Subject: CN = Apar Blog client001 (略)
以下のコマンドで証明書を失効させます。-revoke
オプションに証明書のコピーのパス、-crl_reason
オプションには失効理由を指定します。指定できる値は次の通りです。
- unspecified(指定しない)
- keyCompromise(鍵が破られた)
- CACompromise(CAのセキュリティが侵害された)
- affiliationChanged(所属が変わった)
- superseded(破棄する)
- cessationOfOperation(運用を停止する)
- certificateHold(証明書を保留する)
- removeFromCRL(CRLから削除)
openssl ca \ -config CA/private-ca.cnf \ -crl_reason superseded \ -revoke CA/newcerts/934AB88B7536A3E5471880994A839C86.pem
テキストデータベースの対象レコードの先頭が失効を示す「R」(Revoked)に変わっていることが確認できると思います。
grep client001 CA/db/database R 340505224331Z 240511011520Z,superseded 934AB88B7536A3E5471880994A839C86 unknown /CN=Apar Blog client001
CRL(証明書失効リスト)を更新します。(コマンドおよびオプションは初回にCRLを作成した時とまったく同じです)
openssl ca -gencrl \ -config CA/private-ca.cnf \ -out CA/private-ca.crl
CRLに失効した証明書のシリアル番号がなどが追加されます。
openssl crl -inform PEM -text -noout -in CA/private-ca.crl (略) Revoked Certificates: Serial Number: 934AB88B7536A3E5471880994A839C86 Revocation Date: May 11 01:15:20 2024 GMT CRL entry extensions: X509v3 CRL Reason Code: Superseded (略)
CRL配布ポイントのCRLを更新します。
また、Webサーバーでクライアント証明書認証を行なっている場合は、CRLチェック用のCRLも更新してください。こうすることで発行したクライアント証明書が失効されたとWebサーバーが判断して認証がNGとなります。
おわりに
この記事を書くのにあたって「プロフェッショナルTLS&PKI 改題第2版」を参考にさせていただきました。タイトル通りTLSとPKIの全体像についてこれまでの歴史も含めて詳細に解説されていますので、技術書としてはもちろんですが読み物としても楽しめる1冊です。
コメント