はじめに
Ruby には OpenSSL を扱うためのライブラリ (openssl ライブラリ) が標準添付されていますが、リファレンスマニュアルだけでは使い方が少しわかりづらいので、標準的な使い方をまとめてみました。
以下のような人には役に立つかと思います。
- シェルスクリプトよりも Ruby のスクリプトをよく使う人
- SSL サーバ証明書の作成をよく行うが、それを自動化したい人
- 証明書を操作する Ruby 製のアプリケーションを作成したい人
- Rails で作った Web サービスにクライアント証明書による認証機能を追加したい人
プライベート CA (認証局) 構築
CA の役割については、こちらなどをご覧ください
-
./ca_private_key.pem
に CA の秘密鍵を、./ca.pem
に CA 証明書を作成します - CA の名前は "Example CA" にします
- CA の秘密鍵ファイルには "ca_passphrase" というパスフレーズを設定します
- CA 証明書の有効期限は 1 年間に設定します
require 'openssl'
OpenSSL::Random.seed File.read('/dev/random', 16)
digest = OpenSSL::Digest::SHA1.new
ca_passphrase = 'ca_passphrase' # CA の秘密鍵に設定するパスフレーズ (適切に設定してください)
# CA の情報を設定 (適切に設定してください)
issu = OpenSSL::X509::Name.new
issu.add_entry 'C' , 'JP'
issu.add_entry 'ST', 'Tokyo'
issu.add_entry 'DC', 'Minato-ku'
issu.add_entry 'O' , 'Example, Inc.'
issu.add_entry 'CN', 'Example CA'
# CA の秘密鍵/公開鍵を生成
issu_rsa = OpenSSL::PKey::RSA.generate 2048
# CA の秘密鍵を書き出し
File.open 'ca_private_key.pem', 'wb' do |f|
f.write issu_rsa.export(OpenSSL::Cipher::Cipher.new('aes256'), ca_passphrase)
end
# CA 証明書を作成
issu_cer = OpenSSL::X509::Certificate.new
issu_cer.not_before = Time.now
issu_cer.not_after = Time.now + 1*365*24*60*60 # 有効期限を 1 年後に設定
issu_cer.public_key = issu_rsa.public_key
issu_cer.serial = 1
issu_cer.issuer = issu
issu_cer.subject = issu
ex = OpenSSL::X509::Extension.new 'basicConstraints', OpenSSL::ASN1.Sequence([OpenSSL::ASN1::Boolean(true)])
issu_cer.add_extension ex
issu_cer.sign issu_rsa, digest
File.open 'ca.pem', 'wb' do |f|
f.write issu_cer.to_pem
end
サーバ証明書を作る
サーバ証明書の役割については、こちらなどをご覧ください
-
secure.example.com
用のサーバ証明書を./secure.example.com.crt
に作成します - サーバの秘密鍵を
./secure.example.com.crt.key
に作成します - 証明書の有効期限は 1 年間に設定します
require 'openssl'
OpenSSL::Random.seed File.read('/dev/random', 16)
digest = OpenSSL::Digest::SHA1.new
ca_passphrase = 'ca_passphrase'
domain = 'secure.example.com'
# CA 秘密鍵の再読み込み
issu_rsa = OpenSSL::PKey::RSA.new File.read('ca_private_key.pem'), ca_passphrase
# CA 証明書の再読み込み
issu_cer = OpenSSL::X509::Certificate.new File.read('ca.pem')
issu = issu_cer.issuer
# サーバの情報を設定 (適切に設定してください)
sub = OpenSSL::X509::Name.new
sub.add_entry 'C' , 'JP'
sub.add_entry 'ST', 'Tokyo'
sub.add_entry 'DC', 'Minato-ku'
sub.add_entry 'O' , 'Example, Inc.'
sub.add_entry 'CN', domain
# サーバの秘密鍵/公開鍵を生成
sub_rsa = OpenSSL::PKey::RSA.generate 2048
# サーバの秘密鍵を書き出し
File.open "#{domain}.key", 'wb' do |f|
f.write sub_rsa.export
end
# サーバ証明書を作成
sub_cer = OpenSSL::X509::Certificate.new
sub_cer.not_before = Time.now
sub_cer.not_after = Time.now + 1*365*24*60*60 # 有効期限を 1 年後に設定
sub_cer.public_key = sub_rsa.public_key
sub_cer.serial = 2
sub_cer.issuer = issu
sub_cer.subject = sub
sub_cer.sign issu_rsa, digest
ex = OpenSSL::X509::Extension.new 'basicConstraints', OpenSSL::ASN1.Sequence([OpenSSL::ASN1::Boolean(false)])
sub_cer.add_extension ex
ex = OpenSSL::X509::Extension.new 'nsCertType', 'server'
sub_cer.add_extension ex
sub_cer.sign issu_rsa, digest
File.open "#{domain}.crt", 'wb' do |f|
f.write sub_cer.to_pem
end
クライアント証明書を作る
クライアント証明書の役割については、こちらなどをご覧ください
- クライアント証明書を
./client.pfx
に作成します - 証明書の有効期限は 1 年間に設定します
require 'openssl'
OpenSSL::Random.seed File.read('/dev/random', 16)
digest = OpenSSL::Digest::SHA1.new
ca_passphrase = 'ca_passphrase'
client_passphrase = 'client_passphrase' # クライアント証明書に設定するパスワード (適切に設定してください)
# CA 秘密鍵の再読み込み
issu_rsa = OpenSSL::PKey::RSA.new File.read('ca_private_key.pem'), ca_passphrase
# CA 証明書の再読み込み
issu_cer = OpenSSL::X509::Certificate.new File.read('ca.pem')
issu = issu_cer.issuer
# クライアントの情報を設定 (適切に設定してください)
sub = OpenSSL::X509::Name.new
sub.add_entry 'C' , 'JP'
sub.add_entry 'ST', 'Tokyo'
sub.add_entry 'DC', 'Minato-ku'
sub.add_entry 'O' , 'Example, Inc.'
sub.add_entry 'CN', 'Example Taro'
# クライアント用の秘密鍵/公開鍵を生成
sub_rsa = OpenSSL::PKey::RSA.generate 2048
# クライアント証明書を作成
sub_cer = OpenSSL::X509::Certificate.new
sub_cer.not_before = Time.now
sub_cer.not_after = Time.now + 1*365*24*60*60 # 有効期限を 1 年後に設定
sub_cer.public_key = sub_rsa.public_key
sub_cer.serial = 3
sub_cer.issuer = issu
sub_cer.subject = sub
sub_cer.sign issu_rsa, digest
File.open 'client.pem', 'wb' do |f|
f.write sub_cer.to_pem
end
# クライアント証明書を PKCS#12 形式で作成
sub_pkcs = OpenSSL::PKCS12.create client_passphrase, 'secure.example.com', sub_rsa, sub_cer, [issu_cer]
File.open 'client.pfx', 'wb' do |f|
f.write sub_pkcs.to_der
end
CRL (証明書失効リスト) を作る
CRL の役割については、こちらなどをご覧ください
-
./crl.pem
に CRL を作成します
require 'openssl'
digest = OpenSSL::Digest::SHA1.new
ca_passphrase = 'ca_passphrase'
# CA 秘密鍵の再読み込み
issu_rsa = OpenSSL::PKey::RSA.new File.read('ca_private_key.pem'), ca_passphrase
# CA 証明書の再読み込み
issu_cer = OpenSSL::X509::Certificate.new File.read('ca.pem')
issu = issu_cer.issuer
# CRL を作成する
crl = OpenSSL::X509::CRL.new
crl.issuer = issu
crl.last_update = Time.now
crl.next_update = Time.now + 1*24*60*60 # 次回アップデート予定日時を 1 日後に設定
crl.sign issu_rsa, digest
File.open 'crl.pem', 'wb' do |f|
f.write crl.to_pem
end
CRL に追加する (クライアント証明書を無効にする)
- 「クライアント証明書を作る」で作成したクライアント証明書を CRL に追加します
require 'openssl'
digest = OpenSSL::Digest::SHA1.new
ca_passphrase = 'ca_passphrase'
# CA 秘密鍵の再読み込み
issu_rsa = OpenSSL::PKey::RSA.new File.read('ca_private_key.pem'), ca_passphrase
# CRL の再読み込み
crl = OpenSSL::X509::CRL.new File.read('crl.pem')
# クライアント証明書の再読み込み
sub_cer = OpenSSL::X509::Certificate.new File.read('client.pem')
# CRL の更新
revoked = OpenSSL::X509::Revoked.new
revoked.serial = sub_cer.serial
revoked.time = Time.now
crl.add_revoked revoked
crl.last_update = Time.now
crl.sign issu_rsa, digest
File.open 'crl.pem', 'wb' do |f|
f.write crl.to_pem
end
CRL から取り除く (クライアント証明書を有効にする)
- 「クライアント証明書を作る」で作成したクライアント証明書を CRL から取り除きます
require 'openssl'
digest = OpenSSL::Digest::SHA1.new
ca_passphrase = 'ca_passphrase'
# CA 秘密鍵の再読み込み
issu_rsa = OpenSSL::PKey::RSA.new File.read('ca_private_key.pem'), ca_passphrase
# CRL の再読み込み
crl = OpenSSL::X509::CRL.new File.read('crl.pem')
# クライアント証明書の再読み込み
sub_cer = OpenSSL::X509::Certificate.new File.read('client.pem')
# CRL の更新
sub_cer_serial = sub_cer.serial.to_i
crl.revoked = crl.revoked.reject {|_revoked| _revoked.serial.to_i == sub_cer_serial}
crl.last_update = Time.now
crl.sign issu_rsa, digest
File.open 'crl.pem', 'wb' do |f|
f.write crl.to_pem
end
Apache に設定する
CentOS の場合を例にしています。その他のディストリビューションをお使いの場合は、パスが異なる場合があるのでご注意ください。
ファイルを整理する
# mv ca.pem /etc/pki/CA/
# mv secure.example.com.crt /etc/pki/CA/certs/
# mv secure.example.com.key /etc/pki/CA/private/
# mv crl.pem /etc/pki/CA/
設定ファイルを編集する
設定ファイルに以下を追記してください
# サーバ証明書の設定
SSLCertificateFile /etc/pki/CA/certs/secure.example.com.crt # サーバ証明書
SSLCertificateKeyFile /etc/pki/CA/private/secure.example.com.key # サーバ秘密鍵
# クライアント証明書の設定
SSLCACertificateFile /etc/pki/CA/cacert.pem # CA 証明書
SSLVerifyClient require # クライアント証明書を要求する設定
SSLVerifyDepth 1
# CRL の設定
SSLCARevocationFile /etc/pki/CA/crl.pem
注: プライベート CA により署名されたサーバ証明書なのでブラウザ側でプライベート CA の証明書を信頼する設定を行う必要があります。
Apache を再起動する
# service httpd restart
注: CRL の更新をした場合も Apache を再起動する必要があります
クライアント証明書のインストール
クライアント証明書のインストールはブラウザ毎に行う必要があります
Mac で Google Chrome を使う場合
- 「クライアント証明書を作る」で作成した
client.pfx
をダブルクリックする - パスワードの入力を求められるので "client_passphrase" と入力し、キーチェーンアクセスに登録する
- Web サーバにアクセスすると証明書の選択を求められるので、インストールした証明書を選択する
その他の OS、ブラウザを使う場合
サイボウズさんのサイトによくまとまっているので、こちらを参考に設定を行ってください
終わりに
私自身は何かを自動処理させる場合、シェルスクリプトよりも Ruby のスクリプトで書くことが多いので、今回 Ruby の openssl のライブラリの使い方がわかって非常に楽になりました。"シェルスクリプトより Ruby!"という方は是非ご活用ください!!