OpenSSLでオレオレ認証局(CA)とオレオレ証明書を作る
- 2016-05-29
- カテゴリ: Client Side
- タグ: Tips OpenSSL Linux 車輪の再発明
OpenSSLを使って久しぶりにオレオレ認証局(CA)を立ち上げようと思ったら方法を完全に忘れていて苦労したので、整理してメモ。方法を検索すると、CA.plを使ったり使わなかったり、CA.shを使ってみたり、openssl.cnfを編集したりしなかったりと、いろいろ流儀があるようなので、その辺も含めて整理する。なお、環境はMac、OpenSSLは0.9.8系。
公開鍵証明書発行の手順
まずはおさらい。公開鍵証明書を発行する手順は次のようになる。
- 証明書のユーザーは、秘密鍵を生成する。コマンドで言えば、
openssl genrsa -aes128 -out newkey.pem 2048
など。 - 証明書のユーザーは、生成した秘密鍵を使って証明書署名要求(CSR; certificate signing request)を生成する。コマンドで言えば、
openssl req -new -key newkey.pem -out newreq.pem
など。 - 証明書のユーザーは、生成した証明書署名要求を認証局(CA; Certificate Authority)へ送信する。
- 認証局は、受信した証明書署名要求に自らの秘密鍵で署名し、公開鍵証明書を生成する。コマンドで言えば、
openssl x509 -req -CA ca.pem -in newreq.pem -out newcert.pem
やopenssl ca ...
など(詳細は後述)。 - 認証局は、生成した公開鍵証明書を証明書のユーザーに送信する。
「なぜこんなに面倒なのか。CAが秘密鍵と公開鍵証明書のペアを生成してユーザーに送ればいいじゃないか。」と言いたくなるが、これは鍵配送問題に対処するため。秘密鍵を送るためには暗号化通信が必要で、そのためには秘密鍵が必要で、その秘密鍵を送るためには(以下略)となってしまうため、秘密情報であるユーザー・認証局それぞれの秘密鍵はそれぞれのマシンの外に出ない仕組みになっている。ユーザーと認証局の間の通信経路がたとえ傍受されていても秘密鍵は流出しない。
もちろん、認証局を使わずに自己署名のオレオレ証明書を生成するだけだったら、openssl req -new -x509 -keyout newkey.pem -out newcert.pem
などのコマンドで簡単にできる。今回はオレオレ認証局を立てたうえで、その認証局で署名したオレオレ証明書を濫発することが目的なので、面倒だけど認証局を立ち上げる。
認証局の実体
認証局は自身の公開鍵証明書とその秘密鍵を持ち、受信した証明書署名要求の妥当性を確認したうえで、ユーザーの公開鍵証明書を生成する。この作業はopenssl ca
コマンドを使って行うことができるが、このコマンドは証明書署名要求に署名するという基本機能に加えて、各証明書に打刻する通し番号を管理したり、過去に発行した証明書を保存したり、ポリシー違反の証明書署名要求を弾いたり、といった雑用もこなしてくれる。これらの雑用が公開鍵証明書の規格が要求する挙動なのかは知らないが、認証局として継続的に証明書を発行していくためには確かに必要な雑用なので、openssl ca
は単純なバッチコマンドというよりは簡易認証局のワークフローを一通り実装したアプリケーションという位置付けなのだろう、たぶん。man ca
にもminimal CA applicationだって書いてあるし、単純に署名するだけならopenssl x509
コマンドがある。
openssl ca
がそれなりに複雑なので、シリアル番号を記録するファイルとか発行した証明書を保存するディレクトリとかをセットアップする必要がある。これらと自身の公開鍵・秘密鍵とを一つのディレクトリにまとめたものが認証局の実体とほぼ呼べるもので、デフォルトではdemoCAという名前で作成される。これを定期的にバックアップしておけばいざという時でも認証局として証明書の発行を再開できる。シリアルナンバーのリセットとかはしたほうがいいと思うけど。
で、このセットアップが面倒だからCA.plのようなラッパースクリプトが登場したり、セットアップ情報を毎回引数としてopenssl caに渡すのはしんどいからopenssl.cnfのような設定ファイルが登場したりすることになる。
CA.pl
OpenSSLのワークフローを単純化するためのラッパースクリプトがCA.plで、より簡単なインターフェースで一般的なオペレーションを実行できる(see man CA.pl
)。インストール先は環境によるが、/usr/local/etc/openssl/misc
などに保存される。同じインターフェースを提供するsh版のCA.shもあるけど、開発中のOpenSSL 1.1.0系ではCA.shが削除されてCA.plに一本化されているようなので、今後はCA.plを使ったほうがよさそう。
短いスクリプトだから読んでみるとわかるけど、CA.plは難しい処理をしているわけでは全くなくて、基本的には適切な引数をつけて適切なopensslコマンドを呼んでいるだけなので、どちらかと言うとエイリアスに近い。例えば、証明書署名要求を生成するCA.pl -newreq
コマンドと、証明書発行要求に認証局が署名をするCA.pl -sign
コマンドの実質的な処理はそれぞれ一行ずつだ。
} elsif (/^-newreq$/) { # create a certificate request system ("$REQ -new -keyout newkey.pem -out newreq.pem $DAYS");
} elsif (/^(-sign|-signreq)$/) { system ("$CA -policy policy_anything -out newcert.pem " . "-infiles newreq.pem");
新しい認証局をセットアップするCA.pl -newca
コマンドは相対的に複雑だが、それでもやっているのは必要なディレクトリを作成してopensslコマンドを呼び出すだけだ。
# create the directory hierarchy mkdir $CATOP, $DIRMODE; mkdir "${CATOP}/certs", $DIRMODE; mkdir "${CATOP}/crl", $DIRMODE ; mkdir "${CATOP}/newcerts", $DIRMODE; mkdir "${CATOP}/private", $DIRMODE; open OUT, ">${CATOP}/index.txt"; close OUT; open OUT, ">${CATOP}/crlnumber"; print OUT "01\n"; close OUT;
system ("$REQ -new -keyout " . "${CATOP}/private/$CAKEY -out ${CATOP}/$CAREQ"); system ("$CA -create_serial " . "-out ${CATOP}/$CACERT $CADAYS -batch " . "-keyfile ${CATOP}/private/$CAKEY -selfsign " . "-extensions v3_ca " . "-infiles ${CATOP}/$CAREQ ");
CA.plはOpenSSL 1.1.0系でかなりリファクタリングされたものの、やっていることはほとんど変わっていない。正直なところ、この程度の処理だったらわざわざCA.plのコマンドを覚えるよりは直接opensslコマンドを叩くほうが単純だしフレキシブルだなと思う一方で、CA.plを使えばいちいちopensslコマンドのオプションを覚える必要はないし作業の標準化の面でも便利だなとも思う。悩ましい。結論としては、CA.plのソースコードを読んで必要なオプションを把握し、実際の処理はopensslコマンドを直接使うことにして、その履歴をシェルのヒストリーに残しておくぐらいがちょうどいい。
なお、CA.plがopensslコマンドを使うときは基本的に最小限のオプションしか指定していないので、具体的な動作はopenssl.cnfの設定内容に依存する。
openssl.cnf
openssl.cnfは、openssl ca
やopenssl req
などの一部のOpenSSLコマンドが使う設定ファイルで、主に/usr/local/etc/openssl
に配置される。ファイルの文法はman config
で読めて、具体的な設定項目はman ca
やman req
に書いてある。認証局関連ファイルの保存場所を指定したり、証明書の有効期限のデフォルト値を指定したり、openssl req
で生成する秘密鍵の鍵長のデフォルト値を指定したりできる。
認証局関連の設定はファイルのcaセクションとCA_defaultセクションに書いてある。caセクションはdefault_ca = CA_default
と書かれているだけなので、openssl ca
はopenssl.cnfのcaセクションをまずは参照するが、具体的な設定はCA_defaultセクションから読み込む仕組みになっている。なお、openssl ca
の-nameオプションでCA_defaultセクションの代わりに読み込むセクション名を指定でき、-configオプションではopenssl.cnfの代わりに読み込む設定ファイル名を指定できる。
openssl.cnfの難点は、system-wideの設定ファイルなので「複数の認証局を運用したい」「ユーザー固有設定の認証局を運用したい」といったニーズに手軽には対応できないところだ。加えて、前述したように認証局の実体はdemoCAというディレクトリに納められているが、openssl.cnfでそのディレクトリの場所や細かな挙動を指定しなければ機能しないため、結果的に認証局のポータビリティを下げている。自前で設定ファイルを別途用意して、-configオプションや-nameオプションで指定して活用する手もあるが、openssl.cnfやCA_defaultセクションは結構なボリュームがあって、自前の設定ファイルを今後のOpenSSLのアップデートに合わせてメンテナンスし続けていくのはつらい。せめて、(ssh_configと~/.ssh/configの関係にように)system-wide設定をuserが部分的に上書きできるような方法があればいいが、コマンドの実行ごとにオプションで上書きしていく方法しか見つからず、とても使い勝手が悪い。
結論
以上を踏まえて、今回は以下のような方針でオレオレ認証局を立ち上げた。
- openssl.cnfは編集しない。
- 認証局のセットアップはCA.plを使う。ただしCA.plは部分的にカスタマイズする。
- 証明書の生成には、CA.plを参考にして作った自作の簡易シェルスクリプトを使う。
メンテナンスが面倒になるため、openssl.cnfは編集せずにデフォルト値をそのまま使う。幸いにして、認証局ディレクトリのデフォルトの場所はカレントディレクトリ配下(./demoCA)に設定されているため、openssl ca
コマンドを実行する前に専用のディレクトリに移動する運用でなんとかできる。その他の設定項目はデフォルト値のままか、コマンド実行時のオプションで上書きするかで対処する。
認証局の初期セットアップは自力でやるよりCA.plに任せるほうが洗練されているため、./CA.pl -newca
を使う。ただし、オプションが一部気に入らないため、部分的に書き換えて使う。証明書の生成作業もCA.plの書き換えでよかったが、CA.plでもせいぜい2-3行のコマンドを実行しているだけだし、今後のCA.plの更新に追随していくのも面倒くさいため、CA.plの該当箇所を適当に抜粋して自前のシェルスクリプトを作成する。
具体的な手順
CA.plをコピーしてきてカスタマイズする。-newcaの箇所を一部書き換えるだけなので、変更箇所は以下のとおり2箇所のみ、認証局証明書の寿命と鍵長を伸ばしただけ。
--- CA.pl.in 2016-05-03 22:46:41.000000000 +0900 +++ CA.pl 2016-05-29 16:56:37.000000000 +0900 @@ -47,5 +47,5 @@ $SSLEAY_CONFIG=$ENV{"SSLEAY_CONFIG"}; $DAYS="-days 365"; # 1 year -$CADAYS="-days 1095"; # 3 years +$CADAYS="-days 5844"; # 16 years $REQ="$openssl req $SSLEAY_CONFIG"; $CA="$openssl ca $SSLEAY_CONFIG"; @@ -112,5 +112,6 @@ } else { print "Making CA certificate ...\n"; - system ("$REQ -new -keyout " . + system ("$openssl genrsa -aes256 -out ${CATOP}/private/$CAKEY 4096"); + system ("$REQ -new -key " . "${CATOP}/private/$CAKEY -out ${CATOP}/$CAREQ"); system ("$CA -create_serial " .
このCA.plを使って、./CA.pl -newca
で認証局をセットアップする。
ユーザーの秘密鍵・証明書署名要求を生成し、認証局で署名する一連の作業を自動化するため、以下のようなシェルスクリプトを使う。CA.plの-newreqと-signを参考にして少しカスタマイズしただけ。
#!/bin/sh if [ -z "$DAYS" ] ; then DAYS="-days 1461" ; fi # 4 year openssl genrsa -aes256 -out newkey.pem 4096 openssl req -new -key newkey.pem -out newreq.pem $DAYS echo "Request is in newreq.pem, you will now sign the request" openssl ca -policy policy_anything -out newcert.pem $DAYS -infiles newreq.pem cat newcert.pem echo "Signed certificate is in newcert.pem, private key is in newkey.pem"
demoCAが配置されたディレクトリに移動してこのシェルスクリプトを実行すれば、オレオレ認証局で署名されたオレオレ証明書が生成される。以上で作業終わり。
まとめ
これだけ苦労してオレオレ認証局を作っても、オレオレ証明書が使えるのってごく限られた場合だけだから寂しい(><)
関連記事
トラックバック URL
- http://liosk.blog103.fc2.com/tb.php/216-b8832177