今更聞けないSSL/HTTPS

More than 5 years have passed since last update.

SSL/HTTPSの仕組みをざっくり理解しながら、

オレオレHTTPSの稼働まで。


0.そもそもSSLって何?


概要

SSLサーバー証明書とはウェブサイトの所有者の情報、送信情報の暗号化に必要な鍵、発行者の署名データを持った電子証明書です。

SSLサーバーには主に二つの役割があります。


  • 証明書に表示されたドメインの所有者であることの証明

  • ブラウザとウェブサーバー間でのSSL暗号化通信の実現

一般的には、第三者サービスがWHOISと企業実在情報を照会して証明書を発行します。証明書を発行する人を認証局といいます。

よし、SSLサーバー証明書をつくればいいんだな。


オレオレSSLとは

"俺自身が認証局になることだ…"

社会的信用はないSSLなので運用には注意してください。


その前に、暗号化ってなんですか?


sample_cipher.rb

# 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です。

誰が暗号化するのか、誰が復号化するのかを考えると、


  1. クライアントが送る情報はクライアント側で暗号化して、

  2. 送信された情報をホスト側で受信してデコードします。

このときクライアントが勝手に暗号化するとホスト側でデコードができませんので、先にホストからクライアントに暗号化のルールを伝えます。


  1. ホストからクライアントに暗号化のルールを伝える

  2. クライアントが送る情報はクライアント側で暗号化して、

  3. 送信された情報をホスト側で受信してデコードします。

そのあたりを注目してRSAの動作を確認します。


sample_rsa.rb

# 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;
:
}

サーバーを再起動させてアクセスすると、

ssl

うまくいきました。