SSL
- インターネットのようなオープンなネットワークを用いて、安全に秘匿通信をおこなうための2者間プロトコル
- 通信相手が本当に意図した相手であることを確認
- 通信途中でメッセージを改ざんされたり、中身を覗き見られたりするのを防ぐ
- 現代暗号のさまざまな要素技術を組み上げることで実現
注意:
SSLは、通信する2者間で相互に相手を認証するオプションももつが、
ここでは、もっともよく用いられているクライアント・サーバモードのみを扱う。
クライアント・サーバモードではクライアントは認証されず、サーバのみが認証される。
(クライアントは自身の公開鍵と秘密鍵ペアをもつ必要はない。)
SSLの歴史
- SSL 1.0 1994年、Netscape。設計レビューの段階で破棄。
- SSL 2.0 1994年、Netscape。ダウングレード攻撃が可能。
- SSL 3.0 1995年、Netscape。問題修正、機能追加。
- TLS 1.0 1997-1999年、IETF。SSL 3.0と同機能。
- TLS 1.1 2006年、IETF。AES暗号を追加。
- TLS 1.2 2008年、IETF。SHA256を追加。PRFをSHA256を使うように変更。
以下、とくに断らない限り、SSL とは SSL 3.0 = TLS 1.0 を意味する。
SSLの概要
SSLは
の2フェーズからなる。
ハンドシェークフェーズにおいてプロテクションパラメータを共有し、
プロテクションパラメータを用いてデータ転送フェーズで安全にメッセージを送受信する。
TLSメッセージヘッダ
type(1) |
major(1) |
minor(1) |
length(2) |
TLS Message |
type:
typedef enum {
content_change_cipher_spec = 20,
content_alert = 21,
content_handshake = 22,
content_application_data = 23
} ContentType;
ハンドシェークフェーズの目的
ハンドシェークフェーズの目的は、送信用と受信用の2組の、
プロテクションパラメータを共有すること。
プロテクションパラメータ
- 暗号スイート識別子
- MAC鍵
- セッション鍵
- IV
- シークエンス番号
ハンドシェークヘッダ
msg_type(1) |
length(3) |
handshake message body |
typedef enum {
hello_request = 0,
client_hello = 1,
server_hello = 2,
certificate = 11,
server_key_exchange = 12,
certificate_request = 13,
server_hello_done = 14,
certificate_verify = 15,
client_key_exchange = 16,
finished = 20
} Handshaketype;
ハンドシェークフェーズにおけるメッセージのやりとり
クライアント |
|
サーバ |
|
client hello (サポート暗号スイート、クライアント乱数) → |
|
|
← server hello (選択した暗号スイート、サーバ乱数) |
|
|
← certificate (サーバ公開鍵証明書) |
|
|
← hello done |
|
サーバ証明書を検証 |
|
|
|
key exchange(事前マスター秘密を共有) → |
|
マスター秘密の導出 |
|
マスター秘密の導出 |
|
change cipher spec → |
|
|
finished 確認データ → |
|
|
change cipher spec ← |
|
|
finished 確認データ' ← |
|
確認データ'の検証 |
|
確認データの検証 |
鍵導出, プロテクションパラメータの完成 |
|
鍵導出, プロテクションパラメータの完成 |
client hello
サポート暗号スイートとクライアント乱数を送信。
フォーマット
major(1) |
minor(2) |
current time(4) |
random bytes(28) |
session_id_length(1) |
session_id |
cipher_suites_length(4) |
cipher_suites |
compression_method_length(1) |
compression_methods |
- |
- |
暗号スイート
認証・鍵交換・暗号化・ハッシュの一連の暗号アルゴリズムを指定するための識別子
- 認証アルゴリズム: サーバから送られた公開鍵証明書を認証
- 鍵交換アルゴリズム: 事前マスター秘密を共有
- 暗号アルゴリズム: データ転送フェーズでメッセージを暗号化
- ハッシュ関数: MACの計算、マスター秘密から各種の鍵を導出
例えば、
- TLS_RSA_WITH_AES_128_CBC_SHA = (RSA, RSA, AES_128_CBC, SHA1)
- TLS_DH_DSS_WITH_AES_128_CBC_SHA = (DSS, DH, AES_128_CBC, SHA)
など。
クライアント乱数
32バイト乱数 Rclient。
ただし、最初の4バイトは現在時刻。
server hello
暗号スイートとサーバ乱数を送信。
フォーマット
major(1) |
minor(2) |
current time(4) |
random_bytes(28) |
session_id_length(1) |
session_id |
cipher_suite(4) |
compression_method(1) |
サーバ乱数
32バイト乱数 Rserver。
ただし、最初の4バイトは現在時刻。
certificate
サーバの公開鍵証明書からルート認証局証明へと至るX509証明書チェーン。
key exchange
サーバ公開鍵証明書の正当性が確認できたら、
鍵交換アルゴリズムを用いて、46バイトの事前マスター秘密 Spreを共有。
- 鍵交換アルゴリズムがRSAのとき
- 公開鍵証明書よりサーバRSA公開鍵を取り出す
- クライアントは事前マスター秘密 Spreをランダムに生成
- SpreをサーバRSA公開鍵で暗号化し、送信
- 鍵交換アルゴリズムがDHのとき
- 公開鍵証明書よりサーバDH鍵を取り出す
- サーバDH鍵を用いて、DH鍵共有プロトコルで事前マスター秘密 Spreを共有
マスター秘密の導出
つぎに、送信者、受信者双方で、
- クライアント乱数Rclient、サーバ乱数Rserver、事前マスター秘密Spre
の3情報から擬似ランダム関数PRFを用いてマスター秘密Sを導出。
S = PRF(Spre, "master secret", Rclient || Rserver)
//PRF (secret : 鍵, label || seed : 入力)
- PRF(secret, label, seed, out_len) :=
- P_MD5(first_half_secret, label || seed, out_len)
- +
- P_SHA1(second_half_secret, label || seed, out_len)
- P_hash (secret, seed, out_len) :=
- HMAC_hash(secret, A(1) || seed) || HMAC_hash(secret, A(2) || seed) || ...
- // out_lenに達するまで連結を繰り返す
ここで
- A(1) = HMAC_hash(secret, seed)
- A(i) = HMAC_hash(secret, A(i-1)) (i > 1)
finished
さらに、ここまでで交換したハンドシェークメッセージのハッシュ値をお互いに交換し検証する。
ただし、ハッシュ値を直接交換するのではなく、マスター秘密Sをシードとしたときの
ハッシュ値における擬似ランダム関数PRFの値を交換。
- verify_data = PRF(S,
- "client finished" (or "server finished")
- MD5(handshake_messages) || SHA1(handshake_messages), 12)
ここではじめて、サーバ認証が成立。
すなわち、クライアントは、通信相手が、受け取った公開鍵証明書の持ち主に相当する、サーバであることを確信できる。
鍵導出
確認データが正しければ、最後に、送信者、受信者双方で、
- クライアント乱数Rclient、サーバ乱数Rserver、マスター秘密S
の3情報から擬似ランダム関数PRFを用いて
- 送信用 MAC鍵
- 受信用 MAC鍵
- 送信用 セッション鍵
- 受信用 セッション鍵
- 送信用 IV
- 受信用 IV
を生成し、プロテクションパラメータを完成。
PRF(S, "key expansion", Rserver || Rclient, 上記に必要な長さ)
データ転送フェーズ
ハンドシェークフェーズで共有したプロテクションパラメータを用いて、
メッセージにMACをつけて暗号化して送受信する。
送信者:
- MAC鍵を用いて、送信メッセージのMACを計算し、
- そのMACをメッセージに付加したものを、セッション鍵を用いて暗号化して送信する。
受信者:
- 受信メッセージを、セッション鍵を用いて復号し、メッセージとMACを取り出す。
- MAC鍵を用いて、メッセージのMACを計算し、それが受け取ったMACと等しいかどうか検証する。
- 等しければ、メッセージを受理する。
フォーマット
type = content_application_data(1) |
major(1) |
minor(1) |
content_len + mac_len + padding_len (2) |
[content] |
[mac] |
mac = Mac(Mac鍵, Sequence Num(8)||type(1)||major(1)|minor(1)||content_len(2) || content(var))
content以降が暗号化される。
※ padding len は padded block に書き込まれている
最終更新:2012年07月11日 16:55