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をつけて暗号化して送受信する。

送信者:
  1. MAC鍵を用いて、送信メッセージのMACを計算し、
  2. そのMACをメッセージに付加したものを、セッション鍵を用いて暗号化して送信する。

受信者:
  1. 受信メッセージを、セッション鍵を用いて復号し、メッセージとMACを取り出す。
  2. MAC鍵を用いて、メッセージのMACを計算し、それが受け取ったMACと等しいかどうか検証する。
  3. 等しければ、メッセージを受理する。

フォーマット
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