オレオレ認証局でSSLクライアント認証しようとしたら、色々ハマったから手順をまとめた

表題の通りです。
以前、自前で認証局たててSSL環境作ってクライアント認証しようとしました。
情報はググれば結構見つかって、それらを参照しながら
特に詰まる事無く各証明書作成を完了したんですが、
いざ導入しようとしたら、いろんなエラーに遭遇して上手くいかず、
試行錯誤の末、環境構築に成功し、現在は上手く動作しています。

その頃はブログも書いていなくて、情報もまとめてなかったんですが、
定期的に作業が必要になる度に思い出すのに手間取ったり、
新しく環境構築することになったりし始めたので、
自分なりに以前色々調べて把握したことを元に、手順をまとめてみました。

構築した環境

CentOS5または6でopensslを使って、/etc/pki配下に自前のSSL環境を作り、
サーバのSSL通信およびクライアント認証を導入しました。
CAは10年、サーバ/クライアントは1年毎に証明書更新することにしました。

ベストプラクティス

CA、サーバ、クライアント、それぞれ向けの設定ファイルをちゃんと作成する。
ということです。
そして、実行するコマンド毎に適切な設定ファイルを指定することで、
遭遇したほとんどのエラーは解決できました。

設定ファイル作成

というわけで、設定ファイルを作成しますが、
その前にサーバ/クライアントの証明書などを置くディレクトリを用意します。

  • /etc/pki/Server
  • /etc/pki/Client

を作ったとして、以降進めます。
また、作業は全てrootで行なっている前提です。

まずCA向け

/etc/pki/tls/openssl.cnfがデフォルトで使われる設定ファイルで、これをCA用に修正します。
以下が設定すべき項目です。

[ CA_default ]
dir              = /etc/pki/CA  # <= 相対パスになってたら、絶対パスに直した方が良い
x509_extensions  = v3_ca        # <= v3_caを見るようにする
default_days     = 365          # <= サーバ/クライアントの認証期間
default_crl_days = 3650         # <= CA自体の認証期間はこっち

[ req_distinguished_name ]
countryName_default            = JP
stateOrProvinceName_default    = Kanagawa
localityName_default           = Yokohama
0.organizationName_default     = yourcompany       # <= 何でも良い
organizationalUnitName_default = CA                # <= 何でも良いけどサーバ/クライアントと違う値にすること
commonName_default             = your.domain       # <= CA用の時は何でも良いと思われる
emailAddress_default           = mail@your.domain

[ v3_ca ]
keyUsage   = cRLSign, keyCertSign   # <= CAの場合はこれっぽい
nsCertType = sslCA, emailCA, objCA  # <= CAとして動作させる値を指定

次にサーバ向け

修正した/etc/pki/tls/openssl.cnfを/etc/pki/Server配下にコピーして、
以下の項目を追加/修正します。

[ CA_default ]
x509_extensions  = usr_cert  # <= CA用じゃないのでusr_certを指定

[ req ]
#x509_extensions = v3_ca   # <= コメントアウト
req_extensions   = v3_req  # <= v3_reqを見るようにする

[ req_distinguished_name ]
organizationalUnitName_default = WWW               # <= 何でも良いけどCAと違う値にすること
commonName_default             = *.your.domain     # <= サーバ名と一致させる必要あり。*で曖昧指定できる
emailAddress_default           = mail@your.domain  # <= 何でもよさげ

[ usr_cert ]
keyUsage   = nonRepudiation, digitalSignature, keyEncipherment  # <= CAじゃない場合はこれっぽい
nsCertType = server                                             # <= serverを指定

そしてクライアント向け

作成した/etc/pki/Server/openssl.cnfを/etc/pki/Client配下にコピーして、
以下の項目を修正します。

[ req_distinguished_name ]
organizationalUnitName_default = DEV     # <= 何でも良いけど違う値にすること
commonName_default             = client  # <= 何でもよさげ
emailAddress_default           =         # <= 何でもよさげ

[ usr_cert ]
nsCertType = client, email, objsign  # <= クライアント用途を指定

環境構築

設定ファイルが用意できたら、半分以上終わったようなもんです。
以下、各コマンド実行で聞かれる内容については省略します(他サイトとか見て下さい)が、
パスワード以外は、全部空EnterでOKのはずです。
ファイル名は適当なので読み替えて下さい。特に制限は無いと思います。

CAの各ファイルを作成

$ /etc/pki/tls/misc/CA -newca
$ echo "00" > /etc/pki/CA/crlnumber
$ cd /etc/pki/CA
$ openssl x509 -inform pem -in cacert.pem -outform der -out cacert.der

challengePasswordは、設定するとエラーになるWebブラウザがあった記憶があるので、
空にしておくのが無難だと思います。
crlnumberは、期限切れの証明書などを失効させるのに必要なのですが、
最初は自分で作らなければならないみたいです。
cacert.derは、WebブラウザにCAを登録するためのファイルです。

サーバの秘密鍵を作成

$ cd /etc/pki/Server
$ openssl genrsa -config openssl.cnf -out key.pem -aes256 2048

上記の秘密鍵ファイルの場合、Apacheなどでは起動時にパスワード入力が必要になります。
それを避けたい場合は、さらに以下のコマンドを実行し、生成されたファイルを使います。

$ openssl rsa -in key.pem -out key2.pem

また、MySQLサーバの場合は、こちらのファイルでないとダメでした。

クライアントの秘密鍵を作成

$ cd /etc/pki/Client
$ openssl genrsa -config openssl.cnf -out key.pem -aes256 2048

秘密鍵ファイルのアクセス権を変更

$ chmod 400 /etc/pki/CA/private/cakey.pem
$ chmod 400 /etc/pki/Server/key.pem
$ chmod 400 /etc/pki/Client/key.pem

サーバの証明書を作成(毎年)

$ cd /etc/pki/Server
$ openssl req -new -config openssl.cnf -key key.pem -out csr.pem
$ openssl ca -config openssl.cnf -in csr.pem -out cert.pem

クライアントの証明書を作成(毎年)

$ cd /etc/pki/Client
$ openssl req -new -config openssl.cnf -key key.pem -out csr.pem
$ openssl ca -config openssl.cnf -in csr.pem -out cert.pem
$ openssl pkcs12 -export -in cert.pem -inkey key.pem -out cert.p12

p12形式のファイルが、クライアント証明書としてWebブラウザなどに取り込むファイルです。
ExportPasswordは、設定するとエラーになるWebブラウザがあった記憶があるので、
空にしておくのが無難だと思います。
その場合、Webブラウザなどでパスワードを聞かれたら、空でOKします。

また、以下のようにp12形式のファイルから、証明書と秘密鍵を取り出すこともできます。

$ openssl pkcs12 -in cert.p12 -nocerts -out key.pem -nodes
$ openssl pkcs12 -in cert.p12 -clcerts -nokeys -out cert.pem

例えば、Gitでクライアント認証する場合は、
p12形式ではなく証明書と秘密鍵を個別に指定する必要がありますが、
そういった場合でも、クライアント端末にはcert.p12だけ配布しておけば、
必要な時に上記コマンドによって証明書と秘密鍵を取り出して使うことができます。

期限切れの証明書を失効(毎年)

$ openssl ca -gencrl -revoke /etc/pki/CA/newcerts/?.pem -out /etc/pki/CA/crl.pem

/etc/pki/CA/newcerts配下の証明書の内容をcatなどで確認して、
目的の証明書を指定します。
-outが失効情報のファイルで、例えば、ApacheのSSLCARevocationFileに指定するファイルです。
このファイルが無かったり、空だったりすると、Apacheでエラーになった記憶があるので、
最初の構築時は、証明書を余分に作って失効させておいた方が良いと思います。
/etc/pki/CA配下のcrlnumberやindex.txtを確認すると、失効状況がわかるかと思います。

あとがき

以上、構築時と更新時の自分の行なっている作業をまとめました。
設定ファイルの各項目の設定値は、環境・用途によっては適切な値が違うと思いますので、
各自で調べてみた方が良いかも知れません。
自分でもよくわかってない部分もあり、もしかすると見当違いな部分もあるかも知れませんが、
同じ境遇な方の参考になればと思います。
尚、ご指摘は大歓迎です。