SSL/HTTPSの仕組みをざっくり理解しながら、
オレオレHTTPSの稼働まで。
##0.そもそもSSLって何?
###概要
SSLサーバー証明書とはウェブサイトの所有者の情報、送信情報の暗号化に必要な鍵、発行者の署名データを持った電子証明書です。
SSLサーバーには主に二つの役割があります。
- 証明書に表示されたドメインの所有者であることの証明
- ブラウザとウェブサーバー間でのSSL暗号化通信の実現
一般的には、第三者サービスがWHOISと企業実在情報を照会して証明書を発行します。証明書を発行する人を認証局といいます。
よし、SSLサーバー証明書をつくればいいんだな。
###オレオレSSLとは
"俺自身が認証局になることだ…"
社会的信用はないSSLなので運用には注意してください。
###その前に、暗号化ってなんですか?
# encoding: utf-8
require 'OpenSSL'
def encrypt_data(data, password, salt)
#暗号化方式を選ぶ
cipher = OpenSSL::Cipher::Cipher.new("AES-256-CBC")
cipher.encrypt
cipher.pkcs5_keyivgen(password, salt)
cipher.update(data) + cipher.final
end
def decode_data(data, password, salt)
cipher = OpenSSL::Cipher::Cipher.new("AES-256-CBC")
cipher.decrypt
cipher.pkcs5_keyivgen(password, salt)
cipher.update(data) + cipher.final
end
password = "foobar" # pass_phrase
salt = "8-octets" # 8-octet string
data = "Hello, World" # このデータを暗号化するとします
# 暗号化
encrypted_data = encrypt_data(data, password, salt)
p encrypted_data
# => "\xDEW\xFD\x8A\xB6\x83\xF1\xC1\x96\x15\x81\x02\xE3\x05\x879"
# 復号化
p decode_data( encrypted_data, password, salt )
# => "Hello, World"
パスフレーズとソルトを使って暗号化・復号化しています。
####塩?
ソルトって何という疑問が浮かぶと思います。
平たくいえば、パスフレーズをランダム化するために使います。
例えばいまパスワードは"foobar"ですが、データ流出事故が起きたとします。
-
DBのpasswordの項目に"foobar"と入っている場合
言わずもがなパスワードも流出します -
DBのpasswordの項目に"foobar"を暗号化したもの"x83\xF1\xC1\x96\x15\x81"が入っている場合
"x83\xF1\xC1\x96\x15\x81"が出るまでpass_phraseを入れ続けて一致すれば、元のpasswordが"foobar"が判明する -
DBのpasswordの項目はその時々にソルトを使って暗号化される場合
"x83\xF1\xC1\x96\x15\x81"に一致するパスフレーズが見つかったとしても"foobar"は判明しない
ソルトがわかっていたら?となるので、"foobar"をパスワードとしているユーザーが複数いたらそれぞれにソルトを変える
故にソルトは一意に生成されます
そしてソルトの長さは上の例では8-octet stringでしたが、暗号化方式によってまちまちです。
とりあえずこれだけでも秘密のトークアプリケーションがつくれますね。
####RSA?
暗号化と証明書を同時につくれるナイスな方式がRSAです。
誰が暗号化するのか、誰が復号化するのかを考えると、
- クライアントが送る情報はクライアント側で暗号化して、
- 送信された情報をホスト側で受信してデコードします。
このときクライアントが勝手に暗号化するとホスト側でデコードができませんので、先にホストからクライアントに暗号化のルールを伝えます。
- ホストからクライアントに暗号化のルールを伝える
- クライアントが送る情報はクライアント側で暗号化して、
- 送信された情報をホスト側で受信してデコードします。
そのあたりを注目してRSAの動作を確認します。
# encoding: utf-8
require 'openssl'
include OpenSSL::PKey
### 1.SSL証明書を発行
# RSAを鍵長2048bitで作成します
rsa = RSA.generate(2048)
# 公開鍵を取得します
public_key = rsa.public_key.to_s
# 暗号鍵を取得します 暗号化にパスワードを入力します ここでは'password'にしてます
private_key = rsa.export(OpenSSL::Cipher::Cipher.new('aes256'),'password')
### 2.クライアント側の処理を想定
# 公開鍵をもらいます
# 発行された公開鍵でRSAを作成します
pub = RSA.new(public_key)
# 送信する情報の暗号化
enc_data = pub.public_encrypt("Personal Information")
p enc_data #=> 暗号化されたデータ よくわからない文字列
### 3.ホスト側の処理を想定
# 送信された情報を受信した!
# 暗号鍵でRSAを作成します 暗号化されているのでパスワードも必要です
private = RSA.new(private_key,'password')
# 送信された情報を復号化します
p private.private_decrypt(enc_data) #=> 'Personal Information' 復号化できました
できました。
前段が長くなりましたが、いよいよ本番です。
##1.DES3という暗号化方式でRSA暗号鍵を"server.key"という名前で生成
暗号鍵のパスフレーズを設定します
$ [sudo] openssl genrsa -des3 -out server.key 1024
Enter pass phrase for server.key:
Verifying - Enter pass phrase for server.key:
# genrsa Generation of RSA Private Key. Superceded by genpkey.
# 暗号化方式一覧
# Cipher commands (see the `enc' command for more details)
# aes-128-cbc aes-128-ecb aes-192-cbc # aes-192-ecb
# aes-256-cbc aes-256-ecb base64 # bf
# bf-cbc bf-cfb bf-ecb bf-ofb
# camellia-128-cbc camellia-128-ecb camellia-192-cbc camellia-192-ecb
# camellia-256-cbc camellia-256-ecb cast cast-cbc
# cast5-cbc cast5-cfb cast5-ecb cast5-ofb
# des des-cbc des-cfb des-ecb
# des-ede des-ede-cbc des-ede-cfb des-ede-ofb
# des-ede3 des-ede3-cbc des-ede3-cfb des-ede3-ofb
# des-ofb des3 desx rc2
# rc2-40-cbc rc2-64-cbc rc2-cbc rc2-cfb
# rc2-ecb rc2-ofb rc4 rc4-40
# seed seed-cbc seed-cfb seed-ecb
# seed-ofb zlib
##2.RSA暗号鍵から"server.csr"という名前で証明書を発行
証明書に必要な情報をいろいろ聞かれます。
$ [sudo] openssl req -new -key server.key -out server.csr
Enter pass phrase for server.key: [暗号鍵のパスワード]
Country Name (2 letter code) [XX]:
State or Province Name (full name) []:
Locality Name (eg, city) [Default City]:
Organization Name (eg, company) [Default Company Ltd]:
Organizational Unit Name (eg, section) []:
Common Name (eg, your name or your server's hostname) []:
Email Address []:
Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:
##3.毎回のパスワード入力を避けるため暗号鍵を復号化
$ [sudo] cp server.key server.key.org
$ [sudo] openssl rsa -in server.key.org -out server.key
Enter pass phrase for server.key.org:
writing RSA key
# openssl rsa [-in filename] : ファイルをRSA復号
#
# -in filename
# 入力する証明書要求のファイル名(filename)
# デフォルトは、標準出力
# -out filename
# 出力する証明書要求のファイル名(filename)
# デフォルトは、標準出力
##4.SSL証明書の作成
とりあえず30日の期限にします。
$ openssl x509 -req -days 30 -in server.csr -signkey server.key -out server.crt
# 形式
# openssl req [-new] [-in filename] [-out filename] # [-key filename]
# [-x509] [-days n]
# 機能
# 証明書の署名要求(CSR)の作成
# オプション
# -in filename
# 入力する証明書要求のファイル名(filename)
# デフォルトは、標準出力
# -out filename
# 出力する証明書要求のファイル名(filename)
# デフォルトは、標準出力
# -signkey filename
# 入力する秘密鍵のファイル名(filename)
# -days n
# X.509形式の証明書の有効期限をn日とする
以上でオレオレSSL証明書が発行できました。
##(5.nginxで動作させてみる)
nginxのconfに書いてみる
server {
:
listen 443 default ssl;
ssl on;
ssl_certificate /path/to/server.crt;
ssl_certificate_key /path/to/server.key;
:
}
サーバーを再起動させてアクセスすると、
うまくいきました。