証明書は認証局 (CA) が公開鍵 (をもとに情報を付加した証明書署名要求 (CSR; certificate signing request)) に署名をしたものです。一般的に証明書は公開鍵を内包しています。
サーバ証明書およびクライアント証明書を実際にコマンド例を示しつつ発行します。
認証局 (CA) を構築
証明書は認証局 (CA) が 証明書署名要求 (CSR; certificate signing request) に署名をしたものです。認証局 (CA) は秘密鍵を持っています。署名にはその秘密鍵を使用します。つまり、署名に必要なものは秘密鍵だけです。認証局の秘密鍵以外の適当な秘密鍵で署名すると形式的には証明書が完成します。しかしながらその適当な秘密鍵の保持者は証明書の信頼性を第三者に一般に保証することができないためオレオレ証明書などと称されます。
- 認証局の秘密鍵を使用する場合
- サーバ証明書の運用が可能
- クライアント証明書の運用が可能
- 認証局のものでない単なる秘密鍵を使用する場合
- サーバ証明書の運用が可能 (オレオレ証明書)
- クライアント証明書の運用が不可能
認証局は自分で構築することができます。これを俗にオレオレ認証局と称します。
- 認証局の秘密鍵を使用する場合
- サーバ証明書の運用が可能
- クライアント証明書の運用が可能
- オレオレ認証局の秘密鍵を使用する場合
- サーバ証明書の運用が可能 (これもオレオレ証明書です。オレオレサーバ証明書)
- クライアント証明書の運用が可能 (オレオレクライアント証明書)
クライアント証明書の運用を考える場合は必ず認証局が必要です。ここではベリサインなどの認証局は使用せず、独自にオレオレ認証局を構築します。オレオレクライアント証明書の運用を考えているということになります。オレオレサーバ証明書の運用のみを考える場合は以降の手順は不要です。
構築コマンド例
root で作業します。なお、作業環境は CentOS 6.6 です。
$ sudo su -l
PKI ディレクトリに移動します。
$ cd /etc/pki/
独自認証局を構築するためのディレクトリが用意されています。中身は空です。
$ ls /etc/pki/CA/
certs crl newcerts private
必要なファイルをコピーしてきます。
$ cp tls/misc/CA CA/
$ cp tls/openssl.cnf CA/
内容を編集します。
- CA
$SSLEAY_CONFIG
が最初に登場する前に一行SSLEAY_CONFIG="-config /etc/pki/CA/openssl.cnf"
追加DAYS
およびCADAYS
の既定値をそれぞれ 100 年 (36500 日) に変更して事実上無期限にするREQ
をREQ="$OPENSSL req $SSLEAY_CONFIG -sha256"
に変更 (sha256 を既定値にする)CA
をCA="$OPENSSL ca $SSLEAY_CONFIG -md sha256"
に変更 (sha256 を既定値にする)
- openssl.cnf
req_distinguished_name
セクションの既定値を変更countryName_default
,stateOrProvinceName_default
,localityName_default
,0.organizationName_default
- default_days を 100 年 (36500 日) に変更して事実上無期限にする
認証局用の秘密鍵を生成して、更にその秘密鍵をもとに作成した公開鍵と国情報などを合わせた CSR を生成します。更に自分の秘密鍵で署名します。/etc/pki/CA/CA
には CATOP=/etc/pki/CA
という一行があります。ルート CA は自分で自分の CSR に署名します。これらの処理はコマンド一つで実行できるようになっています。
$ cd /etc/pki/CA
$ ./CA -newca (最初はそのままエンターです)
作成中に求められるコモンネームは認証局の識別子です。ここでは "MyCA" としました。また、認証局の秘密鍵のパスワードは my_ca_pass
としました。認証局用の CSR を生成するときに認証局秘密鍵のパスワードが早速必要になります。
成果物
- private/cakey.pem (認証局秘密鍵)
- cacert.pem (自分の秘密鍵で署名した認証局証明書)
- newcerts/DB04B363DA63FFA3.pem (内容は cacert.pem と同じ。newcerts/ には今後新規発行した証明書が格納されていきます。証明書ディレクトリ。内容は index.txt で管理されます)
- careq.pem (cacert.pem の発行に使用した CSR)
証明書破棄リスト (CRL)
将来的に証明書を無効にする場合に備えて破棄リストを生成しておきます。
$ cd /etc/pki/CA
$ echo 00 > crlnumber
$ openssl ca -config openssl.cnf -gencrl -out crl.pem
成果物
- crl.pem (証明書破棄リスト)
- crlnumber (内容がインクリメントされて "01" になった)
以上で CA が構築されました。
サーバ証明書の発行
CA を構築していない場合
こちらのページなどを参考にします。一般的にオレオレ証明書の作成といった場合は CA を構築せずに以下のようなコマンドで発行する場合が多いようです。
秘密鍵の作成
$ openssl genrsa -out my-private-key.pem 2048
CSR の作成 (コモンネームなどの情報を入力)
$ openssl req -sha256 -new -key my-private-key.pem -out csr.pem
認証局 CA がないため自分の秘密鍵を流用して署名 (100 年の有効期間)
$ openssl x509 -sha256 -req -days 36500 -in csr.pem -signkey my-private-key.pem -out my-certificate.crt
成果物
- my-certificate.crt (オレオレサーバ証明書)
- my-private-key.pem (サーバに設置する秘密鍵。パスフレーズなし)
それぞれ以下のコマンドで情報確認が可能
秘密鍵
$ openssl rsa -text -in my-private-key.pem
Private-Key: (2048 bit)
...
CSR
$ openssl req -text -in csr.pem
証明書
$ openssl x509 -text -in my-certificate.crt
CA を構築した場合
「CA を構築していない場合」の手順または以下のバッチコマンドで秘密鍵と CSR を準備します。
$ sudo su -l
$ cd /etc/pki/CA
$ ./CA -newreq
パスフレーズは my_server_pass
としました。CN は localhost
としました。成果物は newreq.pem
および newkey.pem
です。次に構築した CA で署名します。
$ sudo su -l
$ cd /etc/pki/CA
$ ./CA -sign
成果物 newcert.pem
が生成されました。同じ内容のファイルが newcerts/DB04B363DA63FFA4.pem
(ファイル名はシリアル番号で環境依存) にも生成されています。シリアル連番 serial や発行証明書のリスト index.txt も更新されました。カレントディレクトリに生成されたままのファイルを移動させて整理します。newcerts のシリアル番号とファイル名を同じにしておきます。
$ mkdir csr
$ mv newreq.pem csr/DB04B363DA63FFA4.pem
$ mv newkey.pem private/DB04B363DA63FFA4.pem
内容としては同じではありますが、使い勝手をよくするために秘密鍵からパスワードを解除して証明書からは余分な文字列を除去して依頼主に渡します。
$ cd /etc/pki/CA
$ mkdir share
$ openssl rsa -in private/DB04B363DA63FFA4.pem -out share/localhost.key
$ mv newcert.pem share/localhost.crt
$ openssl x509 -in share/localhost.crt -out share/localhost.crt
証明書から削除した余分な文字列は上述の通りコマンドで再確認できます。
$ openssl x509 -text -in share/localhost.crt
サーバ証明書の使用
Apache の設定例
必要なソフトウェアをインストールします。
$ sudo yum install httpd mod_ssl
用意したサーバ証明書を設定します。
$ sudo vi /etc/httpd/conf.d/ssl.conf
SSLCertificateFile /etc/pki/CA/share/localhost.crt
SSLCertificateKeyFile /etc/pki/CA/share/localhost.key
サービスを起動します。
$ sudo service httpd start
アクセスしてみましょう。
$ curl https://localhost/ ←オレオレサーバ証明書のためエラー
$ curl https://localhost/ -k ←証明書を強制的に信頼するオプション
$ curl https://localhost/ --cacert /etc/pki/CA/cacert.pem ←指定した認証局証明書を信頼するオプション
上記 curl コマンドの二つ目はブラウザで信頼性のないサーバ証明書を個別に信頼する処理に相当します。三つめは構築した CA の証明書をブラウザにインポートすることに相当します。Firefox の場合は以下のようになります。
Firefox → オプション → 詳細 → 証明書 → 証明書を表示 → 認証局証明書 → インポート → cacert.pem → すべてチェックして OK
証明書をインポートした CA で署名された証明書であればブラウザは信頼のある証明書として扱うようになります。ただし、コモンネーム (CN) が一致しない場合はやはりブラウザに信頼されないことに注意してください。他人のサイトの証明書を勝手に利用したなりすまし防止のためです。
nginx の設定例
必要なソフトウェアをインストールします。
$ sudo yum install epel-release (環境によっては不要)
$ sudo yum install nginx
用意したサーバ証明書を設定します。コメントアウトをすべて解除して以下のように編集します。
$ sudo vi /etc/nginx/conf.d/ssl.conf
ssl_certificate /etc/pki/CA/share/localhost.crt;
ssl_certificate_key /etc/pki/CA/share/localhost.key;
サービスを起動します。
$ sudo service nginx start
アクセスしてみましょう。
$ curl https://localhost/ ←オレオレサーバ証明書のためエラー
$ curl https://localhost/ -k ←証明書を強制的に信頼するオプション
$ curl https://localhost/ --cacert /etc/pki/CA/cacert.pem ←指定した認証局証明書を信頼するオプション
AWS ELB の設定例
Apache や nginx は HTTP で動作させて ELB にサーバ証明書をアップロードすることができます。クライアントは HTTPS 通信を ELB に対して行います。
クライアント証明書の発行
以下のバッチコマンドで秘密鍵と CSR を作成します。あるいはサーバ証明書の発行における「CA を構築していない場合」の手順でも可能です。
$ sudo su -l
$ cd /etc/pki/CA
$ ./CA -newreq
パスフレーズは my_client_pass
としました。CN は myuser
としました。newreq.pem
および newkey.pem
が生成されます。構築した CA で署名します。
$ sudo su -l
$ cd /etc/pki/CA
$ ./CA -sign
成果物 newcert.pem が生成されました。同じ内容のファイルが newcerts/CF1EC9646459BA80.pem (ファイル名はシリアル番号で環境依存) にも生成されています。シリアル連番 serial や発行証明書のリスト index.txt も更新されました。カレントディレクトリに生成されたままのファイルを移動させて整理します。newcerts のシリアル番号とファイル名を同じにしておきます。
$ mkdir csr
$ mv newreq.pem csr/CF1EC9646459BA80.pem
$ mv newkey.pem private/CF1EC9646459BA80.pem
最後にユーザへ配布するために秘密鍵と証明書をパッケージングして .p12
ファイルを作ります。
$ openssl pkcs12 -export -in newcert.pem -inkey private/CF1EC9646459BA80.pem -out myuser.p12
$ rm newcert.pem
まず private/DB04B363DA63FFA6.pem
のパスワードを入力する必要があります。その次に .p12
ファイルを開封してクライアントにインストールする際に必要なパスワードを設定します。my_p12_pass
としました。
成果物
$ mv myuser.p12 share/
クライアント証明書の使用
クライアント証明書を利用した認証は Basic/Digest 認証と対比させて考えると理解しやすいかもしれません。「Digest 認証」や「Basic 認証 + HTTPS」よりもセキュアな認証を実現できます。サーバ証明書の場合は、オレオレ認証局をクライアントに信頼させることで、オレオレ認証局が署名したサーバ証明書の信頼性を保証しました。クライアント証明書の場合は、オレオレ認証局をサーバに信頼させることで、オレオレ認証局が署名したクライアント証明書の信頼性を保証します。「オレオレクライアント証明書をサーバが信頼する」は誤りであることに注意してください。ちなみに「オレオレサーバ証明書をクライアントが信頼する」ことは前述の通り可能ですので混乱しないでください。
Apache の設定例
前述のサーバ証明書の設定をまずは行ってください。そのうえで、クライアント証明書に署名した CA の証明書を設定します。クライアント証明書だけの使用はできないことに注意してください。
$ sudo vi /etc/httpd/conf.d/ssl.conf
以下のように設定を変更します。
#SSLCACertificateFile /etc/pki/tls/certs/ca-bundle.crt
↓
SSLCACertificateFile /etc/pki/CA/cacert.pem
SSLCARevocationFile /etc/pki/CA/crl.pem (証明書破棄リストを利用したい場合です。必須ではありません)
#SSLVerifyClient require
↓
SSLVerifyClient require
#SSLVerifyDepth 10
↓
SSLVerifyDepth 1
クライアント証明書のないユーザはそもそもアクセスできません。ただ、同じ CA が署名したクライアント証明書であれば無作為に許可することは避けたいため CN でも制限をかけます。アクセス制限をかけたいディレクトリおよび許可するユーザ一覧を登録します。
<VirtualHost _default_:443>
...
<Directory "/var/www/html">
SSLRequire %{SSL_CLIENT_S_DN_CN} in {"myuser", "hoge", "fuga"}
</Directory>
...
</VirtualHost>
Welcome ページだけは閲覧できてしまうためファイル内の指示に従い内容をコメントアウトします。
$ sudo vi /etc/httpd/conf.d/welcome.conf
サービスを起動します。
$ sudo service httpd start
クライアント証明書のインストール
ブラウザからアクセスすると以下のようなエラーが出て制限されていることが確認できます。
Firefox
SSL peer was unable to negotiate an acceptable set of security parameters. (エラーコード: ssl_error_handshake_failure_alert) 受信したデータの真正性を検証できなかったため、このページは表示できませんでした。
Chrome
サーバーとの安全な接続を確立できません。サーバー側に問題があるか、サーバーが必要とするクライアント認証証明書を所持していない可能性があります。
クライアント証明書をブラウザにインストールします。Firefox の場合の手順を示します。
Firefox → オプション → 詳細 → 証明書 → 証明書を表示 → あなたの証明書 → インポート → myuser.p12 → my_p12_pass → 証明書と鍵が正常に復元されました
アクセスできるようになったことを確認します。
nginx の設定例
前述のサーバ証明書の設定をまずは行ってください。そのうえで、クライアント証明書に署名した CA の証明書を設定します。クライアント証明書だけの使用はできないことに注意してください。サーバ証明書は必要になります。
$ sudo vi /etc/nginx/conf.d/ssl.conf
以下のように編集します。
server {
listen 443;
...
ssl_certificate /etc/pki/CA/share/localhost.crt;
ssl_certificate_key /etc/pki/CA/share/localhost.key;
...
# ここから追記
ssl_verify_client on;
ssl_client_certificate /etc/pki/CA/cacert.pem;
ssl_crl /etc/pki/CA/crl.pem; (証明書破棄リストを利用したい場合です。必須ではありません)
ssl_verify_depth 1;
if ($ssl_client_s_dn !~ "CN=myuser") {
return 403;
}
# ここまで追記
location / {
...
}
}
前述 Apache の手順を参考に動作検証を行います。
AWS ELB を利用する場合
ELB はサーバ証明書の登録をサポートしています。しかしながら 2015/07/10 現在のところ ELB が信頼する CA の証明書を変更できないため、オレオレ認証局が署名したクライアント証明書を利用することができません。商用の CA にクライアント証明書の発行を依頼する、もしくは ELB では HTTPS を復号化せずに単純に TCP 通信を転送して、ELB 配下の EC2 インスタンスで認証および復号化を行います。前者の場合は SSL アクセラレータという観点からサーバの負荷的には有利にはなりますが、CN による制限が ELB では実現できません。後者の場合は以下のように ELB を設定します。
- ELB および EC2 の SecurityGroup で 443 からの Inbound を許可
- ELB のリスナー設定を TCP(443) → TCP(443) と設定
- ELB のヘルスチェック設定を TCP 443 と設定
curl コマンドで p12 ファイルを利用
クライアント証明書を取り出します。秘密鍵は別ファイルとして取り出したいため -nokeys を付与します。
$ sudo openssl pkcs12 -in myuser.p12 -out myuser.crt -nokeys
秘密鍵を取り出します。証明書は別ファイルとして取り出したため -nocerts を付与します。秘密鍵には DES 暗号をかけたくないため -nodes を付与します。
$ sudo openssl pkcs12 -in myuser.p12 -out myuser.key -nocerts -nodes
クライアント証明書および秘密鍵を用いて curl を実行します。
$ curl https://localhost/ --cert ./myuser.crt --key ./myuser.key
↑信頼のない CA が署名した localhost のサーバ証明書は使用できずエラー
$ curl https://localhost/ --cert ./myuser.crt --key ./myuser.key -k
↑強制的に localhost のサーバ証明書を curl に信頼させて使用
$ curl https://localhost/ --cert ./myuser.crt --key ./myuser.key --cacert /etc/pki/CA/cacert.pem
↑localhost のサーバ証明書に署名した CA を信頼のあるものとして設定
ruby スクリプトで p12 ファイルを利用
#!/bin/env ruby
require 'net/https'
# p12 ファイルを復元
p12 = OpenSSL::PKCS12.new(File.read('/etc/pki/CA/share/myuser.p12'), "my_p12_pass")
# HTTP クライアント
https = Net::HTTP.new('localhost', 443)
# HTTPS を利用
https.use_ssl = true
# クライアント証明書を利用
https.cert = p12.certificate
https.key = p12.key
# localhost のサーバ証明書に署名した CA を信頼のあるものとして設定
https.ca_file = '/etc/pki/CA/cacert.pem'
https.verify_mode = OpenSSL::SSL::VERIFY_PEER
# あるいは強制的に localhost のサーバ証明書を信頼のあるものとして扱う
#https.verify_mode = OpenSSL::SSL::VERIFY_NONE
# HTTPS 接続を行う
https.start {|h|
response = h.get('/')
puts response.body
}
発行したクライアント証明書の無効化
破棄したい証明書に署名した CA に移動します。
$ sudo su -l
$ cd /etc/pki/CA
発行済み証明書の一覧を見て、破棄したい証明書のシリアルを探します。
$ less /etc/pki/CA/index.txt
破棄を実行します。
$ openssl ca -config openssl.cnf -revoke newcerts/A873F5D1CCC7EB36.pem
'V' が 'R' に変更されたことを確認します。
$ less /etc/pki/CA/index.txt
HTTP サーバで参照する証明書破棄リスト (CRL) を作成します。
$ openssl ca -config openssl.cnf -gencrl -out crl.pem
これを Apache や nginx の設定ファイルで参照している場合、サーバに再読み込みさせることによって crl.pem
に記載されたクライアント証明書を用いたアクセスを許可しないようになります。
$ service httpd reload (Apache の場合)
$ service nginx reload (nginx の場合)
crl.pem
を参照している場合は "CN=myuser" のクライアント証明書ではアクセスできないことを、上述のブラウザ, curl, ruby スクリプトなどで確認します。
関連記事
- クロスルート証明書の具体例クロスルート証明書とは、中間証明書を署名したルート CA を更に別のルート CA が署名したものです。中間証明書を署名したルート CA の証明書がインストールされていないクライアントであっても、クロスルート証明書を署名したルート CA の証明書がインストールされていれば、サーバー証明書の信頼性を確認できます。 具体例 openssl コマンドで実際にサーバー証明書を取得しました。 $ openss...
- JDK keytool の基本的な使い方 (openssl との対比)JDK でインストールされる keytool コマンドの利用方法を、openssl コマンドと対比してまとめます。keytool コマンドは、Android Studio のドキュメントでも使用される、Java における一般的なツールです。 秘密鍵、公開鍵 (、自己署名証明書) の生成 keytool は openssl と異なり、秘密鍵と公開鍵の片方のみを生
- 証明書、認証局とは? (PKI や ルートCA について)証明書とは 公開鍵証明書 (単に証明書とも) は以下の2つのセットのことです。信頼を有するある者が、信頼のないある者の公開鍵の信頼性を第三者に保証するために発行します。 デジタル署名: 信頼を有するある者がその秘密鍵で、信頼のないある者の公開鍵 (のハッシュ値) を暗号化した結果。電子署名とも。 信頼のないある者の公開鍵 第三者が証明書を利用する手順は以下の通りです。 公開鍵証明書から、デジタル署...
- 暗号技術まわりの用語の簡単なまとめ暗号方式の分類 暗号化に用いる鍵と復号化に用いる鍵が異なるかどうかで2種類に分類されます。 対称暗号 暗号化と復号化に同じ鍵を用います。共通鍵暗号、共有鍵暗号、秘密鍵暗号ともよばれます。 具体例 DES (Data Encryption Standard): ブルートフォースアタックで現実的な時間内に解読されることが分かっています 3DES: トリプルデス。DESを三段重ねにしたもので処理スピード...