SlideShare a Scribd company logo
SSL/TLSの基礎と最新動向
セキュリティキャンプ 2015
2015年8月12日
IIJ 大津 繁樹
更新版資料の置場 http://goo.gl/cX1M17
Github Repo: https://goo.gl/vRLzrj
自己紹介
• 大津 繁樹
• 株式会社 インターネットイニシアティブ
• プロダクト本部 アプリケーション開発部サービス開発2課
• NodeJS Technical Committee メンバー
• (主にTLS/CRYPTO/OpenSSLバインディングを担当)
• IETF httpbis WG で HTTP/2相互接続試験等仕様策定に参画。
• ブログ: http://d.hatena.ne.jp/jovi0608/
はじめに
• TLS(Transport Layer Security)の仕組みについて学んでいただき
ます。
• 後述するようTLSは、様々なセキュリティの要素技術から成り
立っており、その一つ一つが難解で深い技術要素です。
• 残念ながら4時間もの時間があってもその全てを完全に理解す
ることは容易ではありません。
本講義の目的
• TLSの本質は、要素技術をどう組み合わせてどういう手順でセ
キュアな通信を確立するかというところにあります。そのため、
本講義の目的は、TLS技術の基礎
「TLSハンドシェイクの仕組み」
を理解していただくことです
• 聞いているだけの理解より、実際に手を動かしての体験する方
を重視して進めます。
• 課題内容は空白にしています。講義当日にお知らせします。
本講義の内容
• TLSの概要
• TLSを理解する準備
• TLSの話
• まずは TLS_RSA_WITH_AES_128_GCM_SHA256 の時から
• Perfect Forward Secrecy
• TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256を使う
• 最新情報 TLS1.3とはどういうものか? 概要説明
まずやってみる
モジュールのインストール
$npm install seccamp2015-data-reader seccamp2015-tls
Simple TLS Client (simple_tls_client.js)の取得
$git clone https://github.com/shigeki/SecCamp2015-TLS
$cd SecCamp2015-TLS
$node simple_tls_client.js
キーボードを打ってみよう
(注:エラー処理やサニタイジングが十分でないのであくまでテスト用で利用すること)
TLSの概要
インターネットの脅威 盗聴
パスワードやク
レジットカード
番号を盗み見
インターネットの脅威 改ざん
通信途中でデー
タを書き換え
インターネットの脅威 なりすまし
ユーザになりす
まして通信を行
う
インターネットの脅威 否認
そんな通信してま
せんと否認する
インターネットの脅威から守るセキュリ
ティ対策
盗聴
改ざん 成りす
まし
否認
暗号化
完全性
チェック
認証
署名
各レイヤーにおけるセキュリティ通信
WPA
IPsec
TLS,DTLS,SSH
S/MIME, PGP
無線LAN
IP
TCP, UDP
データ
TLSの目的
• TLSプロトコルの最重要なゴールは、通信する2つのアプリケー
ションの間でプライバシーとデータの完全性を提供することです。
RFC5246: The Transport Layer Security (TLS) Protocol Version 1.2
1. Introduction
The primary goal of the TLS protocol is to provide privacy and data
integrity between two communicating applications.
アプリ アプリ
完全性
プライバシー
TLSの簡単な歴史
• SSL 1.0未発表
• 1994年 SSL 2.0
• 1995年 SSL 3.0
• 1996年 IETF TLS WGスタート
• 1999年 TLS 1.0
• 2006年 TLS 1.1
• 2008年 TLS 1.2
• 2013年 TLS 1.3検討スタート
SSLは、旧ネットスケー
プ社の私的プロトコル
TLSと名前を変えて標準化
SSL3.0と基本設計は大き
く変えず、内部バージョ
ンは TLS1.0 =SSL 3.1
現在の利用推奨
様々な拡張仕
様の追加
TLSを理解する準備
TLSの要素技術
X509
証明書
PKI
対称
暗号
暗号
モード
公開鍵
暗号
デジタ
ル署名
メッセージ認証
乱数
生成
TLS
鍵交換
一方向ハッシュ
TLSプロトコルは、これらの要素技術を組み合わせてアプリ間のセキュ
ア通信を確立する手順を決める
TLS要素技術の依存性
X509
証明書
PKI
対称
暗号
暗号
モード
公開鍵
暗号
デジタ
ル署名 メッセージ認証
乱数
生成
鍵交換 一方向ハッシュ
本来はこの一つ一つをきちんと理解してもらうのが必要
TLS要素技術の依存性
X509
証明書
PKI
対称
暗号
暗号
モード
公開鍵
暗号
デジタ
ル署名 メッセージ認証
乱数
生成
鍵交換 一方向ハッシュ
GCM
AES
ECDHE RSA
SHA256
HMAC
本日の講義で扱う技術
説明は概要だけですが、演習で実際の動作を手を動かして体験してもらいます。
要素技術とTLS CipherSuites
TLS_RSA_WITH_AES_128_GCM_SHA256 = {0x00,0x9C}
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256= {0xC0,0x2F};
対称
暗号
暗号
モード
デジタ
ル署名
メッセージ認証
(ハッシュ)
鍵交換TLS _ _ _WITH_ _ 鍵長 _ _
鍵交換・デジタル署名にRSA
対称暗号に128bit鍵長のAES
暗号モードにGCM
ハッシュにSHA256
番号として 0x00,0x9Cを割り当て
鍵交換にECDHE
デジタル署名にRSA
対称暗号に128bit鍵長のAES
暗号モードにGCM
ハッシュにSHA256
番号として 0xC0,0x2Fを割り当て
乱数生成
利用用途:
• 暗号鍵(対称暗号:秘密鍵、公開鍵暗号:鍵ペア)の生成
• 暗号モードの初期ベクトルやNonce(*)の生成
• MAC(メッセージ認証)用鍵
(* Nonce(number used once)一度だけ使い捨て用に使われる数字
求められる機能
無作為性: 偏りがなく等しい数である。
予測不可能性: 次の乱数が予想できない。
制限負可能性: 同じ乱数列を再現できない。
乱数生成
• 実際の利用は、疑似乱数生成。seedが必要。
• OpenSSLでは、seedはLinuxの/dev/urandomを利用。Windows
ではOSのAPIのCryptGenRandomだけでなく画面スクリーンの
ビットマップハッシュも利用。
脆弱性:
• CVE-2008-0166: DebianやUbuntuのOpenSSLに予測可能な乱
数を生成してしまう脆弱性
• SSH公開鍵にブルートフォース攻撃
対称暗号
暗号文
平文
共通鍵 共通鍵
平文
ストリーム暗号:データを逐次暗号化(RC4, Chacha20)
ブロック暗号:データをブロック毎に暗号化(DES, AES)
幾つかの暗号では既に危殆化:
DES: 2005年 NIST FPS46-3規格の廃止(2030年までは許容)
RC4: RFC7455: Prohibiting RC4 Cipher Suites
暗号化 復号化
対称暗号 AES
• 1997年よりプロジェクト開始、2000年選定、2001年仕様発行
• ブロックサイズ 128bit
• 鍵長: 128bits, 192bits, 256bits の3種類
• Intel/AMDのCPUでハードウェア処理のサポート AES-NI
暗号モード
• ブロック暗号は同じデータを同じ鍵で暗号化すると毎回同一の
暗号文になる。
• ブロック長より長いデータを暗号化する場合に暗号モードを利
用して繰り返しを避ける。
• CBC:「(平文 XOR ベクトル) を暗号化」を続ける
• CTR: 「カウンターを暗号化 XOR 平文」を続ける
実際にTLSで利用するには改ざん検知のためのMAC(メッセージ認証)との組み合わせる(AEAD)。
これまで
の主流
これからの主流
に(GCM後述)
AEAD(認証付き暗号)
暗号化しないけど改ざ
ん防止が必要なデータ
(ヘッダ等)
暗号化する平文
AEAD
暗号化
暗号文 認証タグ
共通鍵
初期ベクトル
AEAD(認証付き暗号)
平文
AEAD
復号化
改ざんチェック
暗号化しないけど改ざ
ん防止が必要なデータ
(ヘッダ等)
暗号文 認証タグ
共通鍵
初期ベクトル
GCM
• GCM (Galois Counter Mode: ガロアカウンターモード)
• CTRとGHASHを組み合わせたAEAD
• ハードウェア処理で高速化が可能
• AESと組み合わせて AES-GCMとして利用
一方向ハッシュ
データ 一方向
ハッシュ関数
ハッシュ値
ハッシュ値を比較することでデータの改ざんをチェックすることができる。
一方向ハッシュ
• md5
• SHA-1
• SHA-2(SHA-256など6種)
• SHA-3(SHA3-256など6種)
2018年ぐらいには現実的なコストで
衝突データを探せる見込み(*2)
既に現実的な攻撃手法が存在 (*1)
(*2) Cryptanalysis of SHA-1
https://www.schneier.com/blog/archives/2005/02/cryptanalysis_o.html
(*1) how to Break MD5 and Other Hash Functions
http://merlot.usc.edu/csac-f06/papers/Wang05a.pdf
8/5にNISTより正式公開
メッセージ認証(HMAC)
• 事前に共通鍵を共有
• 共通鍵とデータを組み合わせたハッシュ値を作成
• データの完全性とハッシュ作成者を認証する
データ 一方向
ハッシュ関数
ハッシュ値
共通鍵
公開鍵暗号
512bit RSAの危険性 FREAK https://freakattack.com/
• 解を求めるのが困難な数学的問題を利用して暗号を生成。
• 公開鍵と秘密鍵のペアを生成。公開鍵はさらして大丈夫。
• 公開鍵で暗号化し秘密鍵で復号化。
• RSA
• ECC(楕円曲線暗号)
公開鍵 秘密鍵
暗号化 復号化
鍵交換
• 2者間で安全に鍵を共有する仕組み
• 互いに公開鍵を交換しあい、共有鍵を生成する。
• 通信経路上で共有鍵のやり取りがない
• DH (Diffie-Hellman)
• ECDH(楕円曲線DH)
512bit DH Logjam https://weakdh.org/
デジタル署名
• データの完全性のチェックが可能となる。
• データの送信元の認証が可能となる。
• 公開鍵の信頼性の範囲で否認防止が可能となる。
• RSA
• DSA,ECDSA
公開鍵秘密鍵 データ+デジタル署名
データハッシュ値
を暗号化しデジタ
ル署名を生成
デジタル署名を復号化。
データハッシュ値と比較し
検証する
X509証明書(Certificate)
• 公開鍵と所有者・発行者やその他属性情報をデジタル署名した
データ
• 元々は電子ディレクトリサービス仕様(X500)の一部
• 認証局(CA)が所有者の実体を確認して証明書を発行する(PKI)
X509証明書(Certificate)
演習:
sudo npm install -g seccamp2015-crypto-workshopper
• 乱数生成
• AES-GCM
• 公開鍵暗号
• 一方向ハッシュ
• 鍵交換
• メッセージ認証
• デジタル署名
• X509証明書 (時間がなさそうなので取りやめました)
TLSの話
まずは TLS_RSA_WITH_AES_128_GCM_SHA256 の時から
注:複雑さを避けるためクライアント認証機能は説明しません
TLSデータ表現の仕方 (構造体,メンバー)
struct {
uint8 major;
uint8 minor;
} ProtocolVersion;
0 1
majaor minor
ProtocolVersion version = { 3, 3 }; /* TLS v1.2*/
0 1
0x03 0x03
8bit符号なし整数
(2バイト)
構造体と同じ
構造体名
TLSデータ表現の仕方(配列・エンディアン)
struct {
uint32 gmt_unix_time;
opaque random_bytes[28];
} Random;
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
gmt_unix_time random_bytes
32bit符号なし整数(4バイト)
1970/1/1 0:00(UTC)からの経過秒数
28バイト長の任意のバイト列
[]の中はバイト数を表す。
データを書き込むときは、最上位の
バイトデータをインデックスの小さ
い方から大きい方への順番で書き込
む
(big-endian/network byte order)
Mon Jul 20 2015 01:55:29 GMT+0900 (JST) = 1437324929 sec = 0x55abd681
0 1 2 3
55 ab d6 81
TLSデータ表現の仕方 (可変ベクター)
opaque SessionID<0..32>;
<>は可変ベクター型を表す。
中の数字は、<最少サイズ..最大サイズ>
先頭にサイズを必ず入れ、その後にデータが続く。
先頭のサイズの領域は最大サイズが格納できる量。
左の場合は最大32バイトだから1バイト(255まで表
現可能)分のサイズ領域があればよい。
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2
0x20 SessionID(32バイト)
0 1
0x01 SessionID(1バイト)
0
0x00
SessionIDのサイズが0の場合
SessionIDのサイズが1の場合
SessionIDのサイズが32の場合
TLSデータ表現の仕方 (enum)
uint8 CipherSuite[2];
enum { null(0), (255) } CompressionMethod;
8bit符号なし整数が
2バイト
0 1
0x00 0x40
CipherSuite TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 = { 0x00,0x40 };
CompressionMethodの値は最大255まで(1バイト分)
NULLは0に割り当てられています。
TLSデータ表現の仕方 (条件式)
struct {
ProtocolVersion client_version;
Random random;
SessionID session_id;
CipherSuite cipher_suites<2..2^16-2>;
CompressionMethod compression_methods<1..2^8-1>;
select (extensions_present) {
case false:
struct {};
case true:
Extension extensions<0..2^16-1>;
};
} ClientHello;
条件式
extensionが存在しなければ、空
してれば、0~2^16-1までの可変ベク
ターのデータが入る
TLSデータ表現の仕方 (読んでみよう)
struct {
ExtensionType extension_type;
opaque extension_data<0..2^16-1>;
} Extension;
enum {
signature_algorithms(13), (65535)
} ExtensionType;
TLSデータ表現の仕方 (読んでみよう)
enum {
none(0), md5(1), sha1(2), sha224(3), sha256(4), sha384(5),
sha512(6), (255)
} HashAlgorithm;
enum { anonymous(0), rsa(1), dsa(2), ecdsa(3), (255) }
SignatureAlgorithm;
struct {
HashAlgorithm hash;
SignatureAlgorithm signature;
} SignatureAndHashAlgorithm;
SignatureAndHashAlgorithm
supported_signature_algorithms<2..2^16-2>;
TLSデータ表現の仕方 (読んでみよう)
struct {
uint8 major;
uint8 minor;
} ProtocolVersion;
enum {
change_cipher_spec(20), alert(21), handshake(22),
application_data(23), (255)
} ContentType;
struct {
ContentType type;
ProtocolVersion version;
uint16 length;
opaque fragment[TLSPlaintext.length];
} TLSPlaintext;
TLS1.2の構造
I
P
ヘ
ッ
ダ
T
C
P
ヘ
ッ
ダ
TLS Record Layer
(5バイト)
タイプ
(4種類)
(1byte)
バージョン
(2byte)
長さ
(2byte)
Handshake (タイプ:0x16)
msgタイプ
(10種類)
長さ
(3バイト長)
ハンドシェイクデータ
Alert (タイプ:0x15)
レベル 理由
ChangeCipherSpec (タイプ:0x14)
タイプ
Application Data (タイプ:0x17)
暗号化されたデータ
msgタイプ ハンドシェイクデータの種類
0x00 HelloRequest
0x01 ClientHello
0x02 ServerHello
0x0b Certificate
0x0c ServerKeyExchange
0x0d CertificateRequest
0x0e ServerHelloDone
0x0f CertificateVerify
0x10 ClientKeyExchange
0x14 Finished
TLS Record Layer
データに続いて、次
の4種類のTLSデー
タのいずれかが続く。
TLS Handshakeは、こ
の10種類に分かれる。
TLSハンドシェイクデータの構造
struct {
HandshakeType msg_type;
uint24 length;
select (HandshakeType) {
case hello_request: HelloRequest;
case client_hello: ClientHello;
case server_hello: ServerHello;
case certificate: Certificate;
case server_key_exchange: ServerKeyExchange;
case certificate_request: CertificateRequest;
case server_hello_done: ServerHelloDone;
case certificate_verify: CertificateVerify;
case client_key_exchange: ClientKeyExchange;
case finished: Finished;
} body;
} Handshake;
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) (255)
} HandshakeType;
Handshake (タイプ:0x16)
msgタイプ
(10種類)
長さ
(3バイト長)
ハンドシェイクデータ
msgタイプの種
類でそれぞれの
構造を参照する
TLSハンドシェイク
まずは TLS_RSA_WITH_AES_128_GCM_SHA256 の時から
TLSハンドシェイク(full handshake)
ClientHello
ServerHello
Certificate
ServerHelloDone
ClientKeyExchange
ChangeCipherSpec
Finished
ChangeCipherSpec
Finished
Application Data
Application Data
(赤文字はハンドシェイク)
ClientHelloとServerHello
のやり取りで双方が利用
するTLSバージョンや暗
号化方式などを合意する。
TLSハンドシェイク(resumption)
ClientHello
ServerHello
ChangeCipherSpec
Finished
ChangeCipherSpec
Finished
Application Data
Application Data
(赤文字はハンドシェイク)
SessionIDによるTLSセッ
ションの再開。
鍵交換や証明書送付をス
キップ。
今回は演習の対象外です
TLSハンドシェイクの意味
ClientHello/ServerHello/ServerHelloDone
TLSのための情報交換
バージョン・乱数・暗号方式・拡張情報
Certificate
公開鍵情報の送付
エンドポイントの認証
ClientKeyExchange/ServerKeyExchange
共有鍵交換
ChangeCipherSpec
暗号開始の合図
Finished
ハンドシェイクデータの改ざんチェック
ClientHello
struct {
ProtocolVersion client_version;
Random random;
SessionID session_id;
CipherSuite cipher_suites<2..2^16-2>;
CompressionMethod compression_methods<1..2^8-1>;
select (extensions_present) {
case false:
struct {};
case true:
Extension extensions<0..2^16-1>;
};
} ClientHello;
ClientHello
項目 要素 サイズ 先頭の長さ情報
client_version uint8 major, uint8 minor 2 N/A
random uint32 gmt_unix_time, opaque random_bytes[28] 4 + 28 N/A
session_id opaque SessionID <0..32> 1バイト分
cipher_suites uint8 CipherSuite[2] <2..2^16-2> 2バイト分
compression_met
hods
null(0) <1..2^8-1> 1バイト分
extensions extension_type(65535), extension_data<0..2^16-1> <0..2^16-1> 2バイト分
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
type データ
長
データ type データ
長
データ type データ
長
データ
Extension長
Extensionsデータ例
ClientHello
Record Layer Handshake (ClientHello)
type protocol
version
length
(2byte)
msg
type
length
(3byte)
client
version
random session
id
cipher
suite
compr
ession
Extensi
on
major minor major minor
0x16 0x03 0x01 ?? ?? 0x01 ?? ?? ?? 0x03 0x03 32 byte 可変 可変 可変 可変
Version
0x03,0x00 = SSLv3
0x03,0x01= TLSv1.0
0x03,0x02=TLSv1.1
0x03,0x03=TLSv1.2
クライアントが利用で
きる最高のTLSバー
ジョンを指定、サーバ
がどのバージョンを使
うか選択する
Record Layer Handshake (ClientHello)
type protocol
version
length
(2byte)
msg
type
length
(3byte)
client
version
random session
id
cipher
suite
compression
major minor major minor
0x16 0x03 0x01 0x00
0x2D
0x01 0x00
0x00
0x29
0x03 0x03 32 byte
All 0x00
0x00 0x00,0x02
0x00,0x9c
0x01
0x00
CipherSuite 指定(1種)
TLS1.2用
TLS_RSA_WITH_AES_128_GCM_SHA256:0x00,0x9c
41 byte長45 byte長
最小のClientHello
'160301002D' '0100290303'
crypto.randomBytes(32).toString('hex')
'000002009C0100' 本当はちゃんとし
た乱数を入れる
+
+
+
演習: 最少ClientHelloを作って送る
https://gist.github.com/shigeki/d880ae0b4eef3cfd1acc
var net = require('net');
var crypto = require('crypto');
var recordheader = '160301002D';
var random = crypto.randomBytes(32).toString('hex');
var handshake = '010000290303' + random + '000002009C0100';
var clienthello = new Buffer(recordheader + handshake, 'hex');
var client = net.connect({host: 'tls.koulayer.com', port: 443}, function() {
client.write(clienthello);
});
client.on('data', function(c) {
// Receive ServerHello
console.log(c);
});
ServerHello
struct {
ProtocolVersion server_version;
Random random;
SessionID session_id;
CipherSuite cipher_suite;
CompressionMethod compression_method;
select (extensions_present) {
case false:
struct {};
case true:
Extension extensions<0..2^16-1>;
};
} ServerHello;
複数ではない
複数ではない
ServerHello
項目 要素 サイズ 先頭の長さ情報
server_version uint8 major, uint8 minor 2 N/A
random uint32 gmt_unix_time, opaque random_bytes[28] 4 + 28 N/A
session_id opaque SessionID <0..32> 1
cipher_suite uint8 CipherSuite[2] 2 N/A
compression_met
hod
null(0) 1 N/A
extensions extension_type, extension_data<0..2^16-1> <0..2^16-1> 2バイト分
Record Layer(5bytes) Handshake (ServerHello)
type protocol
version
length
(2bytes)
msg
type
length
(3byte)
server
version
random
32bytes
session id cipher
suite
2bytes
compression
major minor major minor
0x16 0x03 0x03 ? + 4 0x01 ? 0x03 0x03 ? 長さ1byte 0x00,0x9c 長さ2bytes
DataReaderを使う
https://github.com/shigeki/seccamp2015-data-reader
データを読み込むHelperオブジェクト
var DataReader = require('seccamp2015-data-reader').DataReader;
var reader = new DataReader(buffer);
API
• reader.readBytes(n):前回読み込んだ位置からnバイト分のデータをバッファで返す
• reader. readVector(floor, ceiling):可変ベクターのデータを読み込み、バッファで返す。(floor:最小値、
ceiling:最大値)
• reader. peekBytes(from, n): from個目からto個目までのバッファをコピーして返す
• reader. bytesRemaining(): 残っているバイトサイズを返す
演習: Record Headerを見る
function parseRecordHeader(reader) {
var type = reader.readBytes(1).readUInt8(0);
var version = reader.readBytes(2);
var length = reader.readBytes(2).readUIntBE(0, 2);
return {type: type, version: version, length: length};
}
Record Layer
type
(1byte)
protocol
version
length
(2bytes)
major
(1byte)
minor
(1byte)
演習 ServerHelloを見る
https://gist.github.com/shigeki/67c35e12b0834fca3821
function parseServerHello(reader) {
var record_header =
parseRecordHeader(reader);
var type = reader.readBytes(1).readUInt8(0);
var length = reader.readBytes(3).readUIntBE(0,
3);
var version = reader.readBytes(2);
var random = reader.readBytes(32);
var session_id = reader.readVector(0, 32);
var cipher = reader.readBytes(2);
var compression = reader.readBytes(1);
return {
record_header: record_header,
type: type,
length: length,
version: version,
random: random,
session_id: session_id,
cipher: cipher,
compression: compression
};
}
Handshake (ServerHello)
msg
type
length
(3byte)
server
version
random
32bytes
session id cipher
suite
2bytes
compression
major minor
Certificate
opaque ASN.1Cert<2^24-1>;
struct {
ASN.1Cert certificate_list<0..2^24-1>;
} Certificate;
項目 要素 サイズ
certificate_list ASN.1Cert<2^24-1> <0..2^24-1>
最大値のみ
3バイト分
全証明書長 証明書#1長 証明書データ#1 証明書#2長 証明書データ#2
3バイト分
複数の証明書データを送付
最初は必ずサーバ証明書 2つ目以降は中間証明書など
ここから先の演習は状態管理が必要
はじめに使った simple_tls_client.js を使います。
モジュールのインストール
$npm install seccamp2015-data-reader seccamp2015-tls
Simple TLS Client (simple_tls_client.js)の取得
$git clone https://github.com/shigeki/SecCamp2015-TLS
$cd SecCamp2015-TLS
$node simple_tls_client.js
キーボードを打ってみよう
(注:エラー処理やサニタイジングが十分でないのであくまでテスト用で利用すること)
演習 Certificateを見る
https://github.com/shigeki/SecCamp2015-TLS/blob/master/index.js
function parseCertificate(reader, state) {
if (!checkRecordHeader(reader))
return null;
var record_header = parseRecordHeader(reader);
storeHandshakeData(reader, record_header.length,
state);
var type = reader.readBytes(1).readUInt8(0);
var length = reader.readBytes(3).readUIntBE(0, 3);
var cert_reader = new
DataReader(reader.readBytes(length));
var certlist = [];
while(cert_reader.bytesRemaining() > 0) {
var cert = cert_reader.readVector(0, 1 << 24 - 1);
certlist.push(cert);
}
return {
record_header: record_header,
type: type,
length: length,
certlist: certlist
};
}
証明書はリストに格納
3バイト分
ServerKeyExchange
RSA鍵交換では不要。今の時点じゃスキップします。
ECDHEのところで再度説明します。
ServerHelloDone
struct { } ServerHelloDone;
handshake type handshake長
0x0e 0x00 0x00 0x00
ServerHelloの終了の合図
ハンドシェイクヘッダのみ
ClientKeyExchange
struct {
select (KeyExchangeAlgorithm) {
case rsa:
EncryptedPreMasterSecret;
case dhe_dss:
case dhe_rsa:
case dh_dss:
case dh_rsa:
case dh_anon:
ClientDiffieHellmanPublic;
} exchange_keys;
} ClientKeyExchange;
鍵交換のアルゴリズム
によって異なる
RSAによる鍵交換では
暗号化された
PreMasterSecretを送る
DH/DHEによる鍵交換
ではクライアントの公
開鍵を送る。
ClientKeyExchange (RSA鍵交換の場合)
struct {
ProtocolVersion client_version;
opaque random[46];
} PreMasterSecret;
struct {
public-key-encrypted PreMasterSecret pre_master_secret;
} EncryptedPreMasterSecret;
ClientHelloで送付
したバージョン
通常は証明書に記載されているRSA
公開鍵でpre_master_secretを暗号化
エラー処理が脆弱性
がなる場合もある
<0..2^16-1>の可変ベクター
ClientKeyExchange (RSA鍵交換の場合)
Record Layer(5bytes) Handshake(ClientKeyExchange)
type protocol
version
length
(2bytes)
msg
type
length
(3byte)
Encrypted PreMasterSecret
major minor
0x16 0x03 0x03 ? + 4 0x10 ? 長さ2バイト ?
PreMasterSecret
client version random 46bytes
major minor
0x03 0x01
TLSの鍵生成の流れ
pre master secret
(任意のバイト数:鍵交換による)
サーバ・クライアント間の鍵交換方式で生成し、秘密的に共有する
master secret
(48 bytes)
PRF(pre_master_secret, "master secret", client_random+server_random)
keyblock
(任意のバイト数:利用暗号方式による)
PRF(master_secret, "key expansion", server_random+client_random)
client_write_MAC server_write_MAC client_write_key server_write_key client_write_IV server_write_IV
PreMasterSecret/MasterSecret
• TLSで利用するIV(初期ベクトル)、共有鍵、MAC鍵のデータ元
• MasterSecretは48バイト長。PreMasterSecretの長さは鍵交換方式に依
存する。
• MasterSecretは、PreMasterSecret、ClientRandom、ServerRandom、
固定ラベルから生成する。
• Clinet/ServerRandomは全て丸見え。PreMasterSecretは、必ず死守し
て守らないといけない。これが漏えいするとTLSの安全性は全てお
じゃん。
演習:PreMasterSecretの計算
TLS1.2のRSA鍵交換時
function createPreMasterSecretRSAKeyExchange(state) {
var pre_master_secret = (ここを作る)
return pre_master_secret;
}
演習答え:PreMasterSecretの計算
TLS1.2のRSA鍵交換時(48バイト)
function createPreMasterSecretRSAKeyExchange(state) {
var version = new Buffer('0303', 'hex');
var pre_master_secret = Buffer.concat([version, crypto.randomBytes(46)]);
return pre_master_secret;
}
ClientHelloで送った
Version 2バイト分
残り46バイトは乱
数で発生させる
演習: ClientKeyExchangeを作る
var clientkeyexchange_json = {
pre_master_secret: pre_master_secret,
pubkey: require('fs').readFileSync(__dirname + '/pubkey.pem')
};
var clientkeyexchange = createClientKeyExchange(clientkeyexchange_json, state);
function createClientKeyExchange(json, state) {
state.handshake.clientkeyexchange_json = json;
var public_key = json.pubkey;
var pre_master_secret = json.pre_master_secret;
var encrypted = crypto.publicEncrypt({
key: public_key,
padding: require('constants').RSA_PKCS1_PADDING
}, pre_master_secret);
var encrypted_pre_master_secret = writeVector(encrypted, 0, 1 << 16 - 1);
var handshake = createHandshake(handshake_type.clientkeyexchange, encrypted_pre_master_secret);
return createRecord(type.handshake, handshake);
};
公開鍵情報は、本当は Certificate デー
タから抽出する。パーサーが必要なの
で別ファイルから読み込む
公開鍵で pre_master_secretを暗号化
MasterSecretの計算 その1: P_hash
PreMasterSecretのサイズは、鍵交換方式で異なる。
MasterSecretは48バイト必要。
P_hash: データ拡張関数。あるsecretを必要なサイズまで伸長させる。
P_hash(secret, seed) = HMAC_hash(secret, A(1)+seed) + HMAC_hash(secret, A(2)+seed) + …. (必要なサイ
ズまで伸長)
A(0) = seed;
A(i) = HMAC_hash(secret, A(i-1))
+はデータの結合を表す。 HMAC_hashのアルゴリズムは、TLS1.2では SHA256を
使う(それ以前はMD5/SHA-1の組み合わせ)。
演習: TLS1.2の P_hashを作る
var crypto = require('crypto');
var P_hash = require('seccamp2015-tls').P_hash;
var algo = 'sha256';
var secret = 'mysecret';
var seed = (new Buffer(32)).fill(0);
var size = 48;
function MyP_hash(algo, secret, seed, size) {
var ret = new Buffer(size);
(ここを作る)
return ret;
}
var answer = P_hash(algo, secret, seed, size);
var my_answer = MyP_hash(algo, secret, seed, size);
console.log(answer);
console.log(my_answer);
console.log(answer.equals(my_answer));
function MyP_hash(algo, secret, seed, size) {
var ret = new Buffer(size);
var hmac = crypto.createHmac(algo, secret);
hmac.update(seed);
var a = hmac.digest(); // A(1)
var end = 0;
while(size > end) {
hmac = crypto.createHmac(algo, secret);
hmac.update(Buffer.concat([a, seed]));
var b = hmac.digest();
var len = (size - end >= b.length) ? b.length: size - end;
b.copy(ret, end, 0, len);
end += len;
hmac = crypto.createHmac(algo, secret);
hmac.update(a);
a = hmac.digest(); // A(i+1)
}
return ret;
}
演習: 答え
HMAC_hash(secret, A(i)+seed)
サイズ分まで結合する
次のA(i)を計算
A(0)はseed
A(1)を計算
MasterSecretの計算 その2
PRF(PseudoRandom Function)
PRF(secret, label, seed) = P_hash(secret, label + seed)
master_secret = PRF(pre_master_secret, "master secret",
ClientHello.random + ServerHello.random)
[0..47];
= P_hash(pre_master_secret, "master secret" +
ClientHello.random +ServerHello.random)[0..47]
固定ラベル
48バイトまで計算
重要:PRFはTLSで他にも様々な要素の計算に用いられます。
演習: TLS1.2 master_secretを計算する
var crypto = require('crypto');
var PRF12 = require('seccamp2015-tls').PRF12;
var pre_master_secret = crypto.randomBytes(48);
var client_random = crypto.randomBytes(32);
var server_random = crypto.randomBytes(32);
function MyPRF12(secret, label, seed, size) {
return MyP_hash('sha256', secret, Buffer.concat([label, seed]), size);
}
var label = new Buffer("master secret");
var master_secret = PRF12(pre_master_secret, label, Buffer.concat([client_random, server_random]), 48);
var Mymaster_secret = MyPRF12(pre_master_secret, label, Buffer.concat([client_random, server_random]), 48);
console.log(master_secret);
console.log(Mymaster_secret);
console.log(master_secret.equals(Mymaster_secret));
ラベルは
master secret
labelとseedを結合して新
しい seed を生成
master_secretは48バイト
KeyExpansion
• TLSで必要な鍵は、 MAC用、共有鍵用、初期ベクトル(IV)用の3種類。
Client/Serverそれぞれが必要なので6つ必要。
• それぞれで必要なサイズは暗号方式で異なる。
• TLSで必要な鍵は48バイトのMasterSecretでは、サイズが足りない。
• MasterSecretからPRFで必要なサイズのkey_blockに伸長させる。
client_write_MAC server_write_MAC client_write_key server_write_key client_write_IV server_write_IV
master_secret(48バイト)
key_block = PRF(master_secret, "key expansion", server_random + client_random);
注意!:master_secret
の計算時と順番が逆
KeyExpansion(AES-128-GCM)
• client_write_MAC, server_write_MAC: 0バイトx2 (AEADはMAC不要)
• client_write_key, server_write_key: 16バイトx2 (128bits鍵長)
• client_write_IV, server_write_IV: 4バイトx2 (12バイト中の頭、後述)
合計: 40バイト
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9
client_write_key(16bytes) server_write_key(16bytes) client_write
_IV(4bytes)
server_write
_IV(4bytes)
key_block = PRF12(algo, master_secret, "key expansion", ServerHello.random+ClientHello.random, 40);
演習:KeyBlockの計算
function KDF(pre_master_secret, client_random, server_random) {
var master_secret = PRF12(pre_master_secret, "master secret", Buffer.concat([client_random, server_random]), 48);
// 40 bytes key_block for AES-128-GCM
var key_block_reader = new DataReader(
PRF12(master_secret, "key expansion", Buffer.concat([server_random, client_random]), 40));
return {
master_secret: master_secret,
client_write_MAC_key: null,
server_write_MAC_key: null,
client_write_key: key_block_reader.readBytes(16),
server_write_key: key_block_reader.readBytes(16),
client_write_IV: key_block_reader.readBytes(4),
server_write_IV: key_block_reader.readBytes(4)
};
}
ラベルは
key expansion
結合の順番が反対
AES-128-GCM
は40バイト分必要
GCMはMACはいらない
AES-128の秘密鍵は16バイト
初期ベクトルの最初の4バイト分は両者で秘密
共有、残り8バイトはフレームに付加して送る
ChangeCipherSpec
struct {
enum { change_cipher_spec(1), (255) } type;
} ChangeCipherSpec;
送信元が暗号開始を宣言。これを送信した後は暗号通信を行う。
Record Layer ChangeCipherSpec
ContentType Version length
(2byte)major minor
0x14 0x03 0x03 0x00 0x01 0x01
Finished
struct {
opaque verify_data[verify_data_length];
} Finished;
verify_data = PRF(master_secret, finished_label, Hash(handshake_messages))[0..11];
finished_label: クライアントは、"client finished"、サーバは"server finished"
12バイト固定
これまでのハンドシェイクデー
タ(ただし自分は除く)のハッ
シュを計算
TLS1.2では SHA256を使う
Finishedを受信すると、これまで送受信したハンドシェイクデータから計算した値と比較。
ハンドシェイクデータが改ざんされてないことを確認する。
Finishedを作る
var clientfinished = {
master_secret: state.keyblock.master_secret,
handshake_message_list: state.handshake_message_list
};
var clientfinished = createClientFinished(clientfinished, state);
function createClientFinished(json, state) {
state.handshake.clientfinished = json;
// create session hash
var shasum = crypto.createHash('sha256');
shasum.update(Buffer.concat(json.handshake_message_list));
var message_hash = shasum.digest();
var r = PRF12(json.master_secret, "client finished", message_hash, 12);
var handshake = createHandshake(handshake_type.finished, r);
return createRecord(type.handshake, handshake);
}
これまで交換したハンド
シェイクデータのリスト
これまで交換したハン
ドシェイクデータの
ハッシュ値を求める
PRFで12バイト分に
切り詰める
フレーム作
成後暗号化
受信したFinished中の値と比
較して改ざんチェック
データの暗号化struct {
ContentType type;
ProtocolVersion version;
uint16 length;
select (SecurityParameters.cipher_type) {
case stream: GenericStreamCipher;
case block: GenericBlockCipher;
case aead: GenericAEADCipher;
} fragment;
} TLSCiphertext;
レコードフィールド
暗号化した後の長さ
ストリーム暗号、ブ
ロック暗号、AEAD
で構造が変わる。
Record Layer 暗号化されたデータ
ContentType Version length
(2byte)major minor
0x03 0x03 …..
データの暗号化(AEAD)
struct {
opaque nonce_explicit[record_iv_length];
aead-ciphered struct {
opaque content[TLSCompressed.length];
};
} GenericAEADCipher;
AEADEncrypted = AEAD-Encrypt(write_key, nonce, plaintext, additional_data)
struct {
opaque salt[4];
opaque nonce_explicit[8];
} GCMNonce;
additional_data = seq_num + TLSCompressed.type + TLSCompressed.version + TLSCompressed.length;
key blockから生成
した4bytes
毎回生成する乱数
(8bytes 丸見え)
uint16
0から始まる
認証データ
暗号化する前の長さ
TLSレコード層の情報
AEAD(AES-128-GCM)で暗号化されたデータ
Record Header explicit nonce
(8 byte)
暗号化されたデータ tag
(16 bytes)Cont
entTy
pe
Version length
(2byte)
major minor
0x03 0x03
AAD
writeSeq + Record Header
暗号化する平文
AES-128-GCM
暗号化されたデータ tag
write_key
初期ベクトル
writeIV(4bytes) + explicit
nonce(8bytes)
explicit nonce、暗号データ、
tag を合わせた長さに再計算
アプリケーションデータの暗号化
function EncryptAEAD(frame, state) {
var key = state.keyblock.client_write_key;
var iv = Buffer.concat([state.keyblock.client_write_IV.slice(0,4), state.nonce_explicit]);
var record_header = frame.slice(0, 5);
var aad = Buffer.concat([state.write_seq, record_header]);
var ret = Encrypt(frame.slice(5), key, iv, aad);
var encrypted = ret.encrypted;
var tag = ret.tag;
// re-calcuate length with adding nonce_explit and tag length
var length = state.nonce_explicit.length + encrypted.length + tag.length;
record_header.writeUIntBE(length, 3, 2);
// increment write sequence number
incSeq(state.write_seq);
return Buffer.concat([record_header, state.nonce_explicit, encrypted, tag]);
}
record長の再計算
sequence noを増加
初期ベクトルはwrite_IV(4) と
explicit nonceを合わせたもの
秘密鍵漏洩の脅威
• TLS_RSA_WITH_AES_128_GCM_SHA256は、鍵交換・署名にRSA
を利用している。
• 通信経路に pre_master_secret を暗号化して相手に送信。
• 公開鍵・秘密鍵共に長期に固定化されている。
• 暗号強度が低い又は秘密鍵が漏えいしてしまうとどうなるか?
演習: TLSを破る
時間があれば行います。
サーバの秘密鍵をお渡しします。
秘密鍵とハンドシェイクデータから暗号化されたアプリ通信を
解読する。
演習: TLSを破る
• https://github.com/shigeki/SecCamp2015-TLS/tree/master/break_tls
• 秘密鍵のパスワードは口頭でお伝えします。
戻し方
$ openssl rsa -in server_encrypted.key -out server.key
Enter pass phrase for server_encrypted.key:
writing RSA key
秘密鍵と handshake.json のデータから暗号化されたApplication Data
を復号してください。
演習回答:
Record Layer Handshake (ClientHello)
type protocol
version
length
(2byte)
msg
type
length
(3byte)
client
version
random session
id
cipher
suite
compr
ession
Extensi
on
major minor major minor
0x16 0x03 0x01 ?? ?? 0x01 ?? ?? ?? 0x03 0x03 32 byte 可変 可変 可変 可変
5+4+2=11bytes 先から32byte
Record Layer(5bytes) Handshake(ClientKeyExchange)
type protocol
version
length
(2bytes)
msg
type
length
(3byte)
Encrypted PreMasterSecret
major minor
0x16 0x03 0x03 ? + 4 0x10 ? 長さ2バイト ?
Client/Server Randomの取得
5+4+2=11bytes 先から最後まで
暗号化されたpre master secretの取得
秘密鍵で復号化
演習回答:
Record Header explicit nonce
(8 byte)
暗号化されたデータ tag
(16 bytes)Cont
entTy
pe
Version length
(2byte)
major minor
0x03 0x03
sequence noと合わせて AAD
ただし長さは再計算が必要
writeIVと合わせてIV
復号化でセットするタグ値
ここを復号化して平
文をゲットする
演習回答:solve.js
var DataReader = require('seccamp2015-data-reader').DataReader;
var SecCampTLS = require('seccamp2015-tls');
var fs = require('fs'), crypto = require('crypto');
var handshake = require(__dirname + '/handshake.json');
var clienthello = new Buffer(handshake.ClientHello, 'hex');
var serverhello = new Buffer(handshake.ServerHello, 'hex');
var clientkeyexchange = new Buffer(handshake.ClientKeyExchange, 'hex');
var encryptedApplicationData = new Buffer(handshake.EncryptedApplicationData, 'hex');
// obtain handshake parameters
var client_random = clienthello.slice(11, 11+32);
var server_random = serverhello.slice(11, 11+32);
var encrypted_pre_master_secret = clientkeyexchange.slice(11);
// obtain private key
var private_key = fs.readFileSync(__dirname + '/server.key');
JSONデータからハンド
シェイクデータを抽出
ハンドシェイクパラ
メータを抽出
秘密鍵の入手
演習回答:
// decrypt pre master secret
var pre_master_secret = crypto.privateDecrypt(
{key: private_key,
padding: require('constants').RSA_PKCS1_PADDING
}, encrypted_pre_master_secret);
// objtain keyblock
var keyblock = SecCampTLS.KDF(pre_master_secret, client_random,
server_random);
// Calculate Sequence Number
var read_seq = (new Buffer(8)).fill(0);
read_seq[7] = 0x01;
秘密鍵を使って ClientKeyExchangeで暗
号化されたpre master secret の復号化
pre master secretとclient/server random
を使ってkey block を生成
sequence number を生成
演習回答:
var reader = new DataReader(encryptedApplicationData);
// Obtain AEAD parameters
var record_header = reader.readBytes(5);
var length = record_header.readUIntBE(3, 2);
var frame = reader.readBytes(length);
var nonce_explicit = frame.slice(0, 8);
var encrypted = frame.slice(8, frame.length - 16);
var tag = frame.slice(-16);
// Re-Caluclate record header
record_header.writeUIntBE(encrypted.length, 3, 2);
var iv = Buffer.concat([keyblock.client_write_IV, nonce_explicit]);
var aad = Buffer.concat([read_seq, record_header]);
暗号化されたアプリケーションデータ
からAEADパラメータを抽出する
record headerの長さ再計算
演習回答:
// Decrypt Application Data
var decipher = crypto.createDecipheriv('aes-128-gcm',
keyblock.client_write_key, iv);
decipher.setAuthTag(tag);
decipher.setAAD(aad);
var clear = decipher.update(encrypted);
decipher.final();
console.log(clear.toString());
これまで得られたパラメー
タからデータの復号化
平文の取得
Perfect Forward Secrecy
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256を使う
Perfect Forward Secrecy(PFS)
• 前方秘匿性
• セッション毎に一時的な鍵を使う。
• ハンドシェイクを含む全暗号データを取得されているような状況でも、
将来的な秘密鍵漏洩などのリスクに対応する。
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
Ephemeral:一時的な
鍵交換手法
ECDHEのハンドシェイク
ClientHello
+ elliptic_curves(10)
+ ec_point_formats(11)
ServerHello
+ ec_point_formats(11)
Certificate
ServerKeyExchange
ServerHelloDone
ClientKeyExchange
ChangeCipherSpec
Finished
ChangeCipherSpec
Finished
Application Data
(赤文字が追加変更されるところ)
ClientHello拡張を追加。
ServerHello拡張を追加。
楕円曲線名とServerの公
開鍵を署名付きで送付
Clientの公開鍵を送付
ECPointの書式を合意
使える楕円曲線名とECPoint書式を通知
ECDHE ClientHello拡張
struct { NamedCurve elliptic_curve_list<1..2^16-1> }
EllipticCurveList;
enum {
sect163k1 (1), 省略, secp256r1 (23), 省略, (0xFFFF)
} NamedCurve; クライアントがサポートしている楕円曲線のリストをサー
バ側に通知。サーバはリストの中から適切な楕円曲線を選
び ServerKeyExchange内で選択した楕円曲線を通知する
0 1 2 3 4 5 6 7
elliptic_curves(10) リスト長 データ長 secp256r1 (23)
0x00 0x0a 0x00 0x04 0x00 0x02 0x00 0x17
ECDHE Client/Server Hello拡張
enum { uncompressed (0),省略, reserved (248..255)
} ECPointFormat;
struct {
ECPointFormat ec_point_format_list<1..2^8-1>
} ECPointFormatList;
楕円暗号の公開鍵の書式
0 1 2 3 4 5
ec_point_formats(11) リスト長 データ長 uncompressed(0)
0x00 0x0b 0x00 0x02 0x01 0x00
ECDHE ServerKeyExchange
select (KeyExchangeAlgorithm) {
case ec_diffie_hellman:
ServerECDHParams params;
Signature signed_params;
} ServerKeyExchange;
struct {
ECParameters curve_params;
ECPoint public;
} ServerECDHParams;
struct {
SignatureAndHashAlgorithm algorithm;
opaque signature<0..2^16-1>;
} DigitallySigned;
ServerECDHParams Signature
ECParameters ECPoint algorithm
signaturecurve_type named_curve 長さ public key
(Hello拡張指定の書式)
RSA-SHA256
(0x04,0x01)named_curve (3) secp256r1 (23)
signature = sign(algorithm, ClientHello.random + ServerHello.random + ServerECDHParams);
RSA秘密鍵で
ServerECDHParms
とRandomを署名
ECDHE ClientKeyExchange
struct {
select (KeyExchangeAlgorithm) {
case ec_diffie_hellman:
ClientECDiffieHellmanPublic;
} exchange_keys;
} ClientKeyExchange;
struct {
select (PublicValueEncoding) {
case implicit: struct { };
case explicit: ECPoint ecdh_Yc;
} ecdh_public;
} ClientECDiffieHellmanPublic;
ECDHEは explicit
ClientECDHParams
ECPoint
長さ public key
(Hello拡張指定の書式)
ClientKeyExchangeは署
名の必要はない
演習: ECDHE公開鍵の守られ方の違い
• ServerKeyExchange: 公開鍵を署名
• ClientKeyExchange: やりたい放題
どうしてでしょう?
TLS最新情報
TLS1.3(*)とはどういうものか?
(* 注: 2015-7-23時点のものです。最終仕様で変更になる可能性があります。)
TLS1.3(*)の背景
• 2008年 TLS1.2仕様化から7年
• 古い暗号やプロトコルの危殆化に伴う機能の抜本的な見直し
• 常時TLS化を目指し、将来的に安心で強固なセキュリティの必
要性
• モバイル環境の普及やWebアプリの高度化に伴う高パフォーマ
ンス、低レイテンシー化要望の高まり
TLS1.3の目的
1. ハンドシェイクデータをできるだけ暗号化して隠匿する
2. ハンドシェイクレイテンシーの削減
• 再接続の場合は、 0-RTT
• フルハンドシェイクは、 1-RTT
3. ハンドシェイクで交換する項目の見直し
4. レコード層暗号化の見直し(RC4廃止、CBCの不採用)
TLS1.3の構造(*)
I
P
ヘ
ッ
ダ
T
C
P
ヘ
ッ
ダ
TLS Record Layer
(5バイト)
タイプ
(4種類)
(1byte)
バージョン
0x0301に固定
(2byte)
長さ
(2byte)
Handshake (タイプ:0x16)
msgタイプ
(10種類)
長さ ハンドシェイクデータ
Alert (タイプ:0x15)
レベル 理由
Application Data (タイプ:0x17)
暗号化されたデータ
msg
タイプ
ハンドシェイクデータの種類
0x00 reserved(旧HelloRequest廃止)
0x01 ClientHello
0x02 ServerHello
0x04 NewSessionTicket
0x06 HelloRetryRequest
0x07 ServerKeyShare
0x0b Certificate
0x0c reserved(旧ServerKeyExchange廃止)
0x0d CertificateRequest
0x0e ServerConfigration
0x0f CertificateVerify
0x10 reserved(旧ClientKeyExchange廃止)
0x14 Finished
TLS Handshakeは、この12
種類に増加。後方互換のた
め3つは予約。
Early Handshake (タイプ:0x19)
msgタイプ 長さ ハンドシェイクデータ
ChangeCipherSpecを廃止
(* 注: 2015-7-20時点のものです。最終仕様で変更になる可能性があります。)
Extens
ion ID
Extension
0x00d signature_algorithms
TBD early_data
TBD supported_groups
TBD known_configration
TBD pre_shared_key
TBD client_key_shares
TLS1.3 ハンドシェイク (full handshake)
ServerHello
ServerKeyShare
EncryptedExtensions
ServerConfigration
Certificate
Finished
Finished
Application Data
Application Data
(赤文字は暗号化されているデータ)
ClientHello
+ ClientKeyShare
1-RTT
Application Data
ここで即
暗号化
ServerHello
ServerKeyShare
EncryptedExtensions
ServerConfigration
Certificate
Finished
Finished
Application Data
Application Data
(赤文字は暗号化されているデータ)
ClientHello
+ ClientKeyShare
2-RTT
Application Data
TLS1.3 ハンドシェイク (鍵交換ができなかった時)
ClientHello
+ ClientKeyShare
HelloRetryRequest
ここで即
暗号化
TLS1.3 ハンドシェイク (再接続)
ServerHello
+ KnownConfigration
+ EarlyDataIndication
ServerKeyShare
Finished
Finished
Application Data
(赤文字は暗号化されているデータ)
ClientHello
+ ClientKeyShare
+ KnownConfigration
+ EarlyDataIndication
ApplicationData
0-RTT
Application Data
以前のサーバ公開
鍵と組み合わせて
フライイングでア
プリデータ暗号化
して送る
TLS1.3 0-RTTの問題点
0-RTT
ApplicationData
POST / HTTP/1.1
xxxx
0-RTT
ApplicationData
POST / HTTP/1.1
xxxx
クラスタリング
厳密な同期は不可能
Reply
攻撃
Reply攻撃に弱いため、最初のデータは冪等性のあるリク
エストのみに制限しなければならない
更新
更新
TLS1.3 鍵生成(Server view)
Ephemeral Secret Static Secret
xES xSS
Master Secret
Early Data
Traffic Keys
Application
Traffic Keys
Handshake
Traffic Keys
Exporter
Secret
Resumption
Secret
Finished
Secret
Server SecretKey
in KnownConfigration
Client PubKey
in ClientKeyShare
ClientPubKey
in ClientKeyShare
Server SecretKey
DH or ECDH鍵交換
HKDF
DH or ECDH鍵交換
HKDF
HKDF seedHKDF key
HKDF(label + session_hash)
ハンドシェイク暗号化用 アプリデータ暗号化用 Finished用
以前に送付した古い
の。最初なら鍵生成
したもの。
TSL1.3変更点まとめ(*)
• Compression, Renegotiationの廃止
• ChangeCipherSpec廃止
• ClientKeyExchange廃止してClientHelloの拡張フィールドに
• HelloRequest/ServerHello廃止
• Record層のバージョンを 0x03,0x03(TLS1.0)に固定
• ServerConfigrationをクライアントに渡し、0-RTT接続を実現
• ServerKeyShare後即暗号化、証明書データも隠匿
• 乱数から時間の gmt フィールド廃止
(* 注: 2015-7-23時点のものです。最終仕様で変更になる可能性があります。)
TSL1.3変更点まとめ(*)
• 鍵交換は名前付きDH,ECDHで ephemeral利用のみ
• 鍵生成は、HKDF利用に変更。Ephemeral/Staticの2種類のsecret
からsession_hashを付けて各用途別に生成。
• 暗号モードで利用できるのはAEADのみ(GCM, CCM, Poly1305)
• AEADの初期ベクタ廃止
• Record層のLengthをAEADで認証外に
• 他にも色々・・・
(* 注: 2015-7-23時点のものです。最終仕様で変更になる可能性があります。)

More Related Content

SSL/TLSの基礎と最新動向