TLS13 的 Session resumption 和之前版本不同
什么是 Session resumption: 在之前的 TLS 连接之上,基于已经交换过的信息, 快速的建立另外一个TLS 连接的机制。
为什么需要 Session resumption: 加速TLS握手, 更快的准备好安全的数据通道.
常见的使用场景:
- FTP 协议。 在 FTP 协议中,当 control channel 使用 TLS 协议进行通信,通常会要求 Data channel 也进行 TLS 加密通信,并且强制要求 Data channel 使用 Session resumption 机制建立 TLS 连接。
- HTTP 协议中,当我们需要和同一个服务器建立多个 TLS 连接时,我们可以选择在成功的建立一条TLS连接之后,后续连接使用 Session resumption 来加速 TLS 连接建立的速度。
- 其他场景。
概述
正如前面所说, Session resumption 可以加速握手流程,这里我们先看看具体是如何做到加速的?
完整握手流程:
Client Server
---------------------------------------------------------------------
ClientHello
+ key_share -------->
ServerHello
+ key_share
{EncryptedExtensions}
{CertificateRequest*}
{Certificate*}
{CertificateVerify*}
{Finished}
<-------- [Application Data*]
{Certificate*}
{CertificateVerify*}
{Finished} -------->
<-------- [NewSessionTicket]
[Application Data] <-------> [Application Data]
Session resumption 的握手流程:
Client Server
---------------------------------------------------------------------
ClientHello
+ key_share*
+ pre_shared_key -------->
ServerHello
+ pre_shared_key
+ key_share*
{EncryptedExtensions}
{Finished}
<-------- [Application Data*]
{Finished} -------->
[Application Data] <-------> [Application Data]
我们可以看到,在 Session Resumption 的握手流程中,CertificateRequest
, Certificate
, CertrificateVerify
都不用再次发送了。这就很明显了,更少的数据交换,那就需要更少的时间啦。
详细流程
这里,我们需要说明的一点儿是, Session resumption 的前提是必须已经和同一个服务器建立过TLS连接。那么接下来的连接中可以选择是否使用 Session resumption。
Session Resumption 在协议上的实现如下:
- 客户端A和服务器先建立一条 TLS13 的连接,并进行后续的通信。
- 通常情况下, 如果服务器支持 Session Resumption, 它便会给你发送一个
New Session Ticket
数据包。(这个包我们后面详细解析, 这里只需要知道它包含了一个 Ticket, 这个 Ticket 用于Session Resumption) - 接着,客户端B(可以是另外一个客户端B或者同一个客户端A)需要和服务器建立 TLS13 连接进行通信。这里,第一条 TLS 连接是否依然存活已经不再重要, 仅仅需要第一次的 TLS连接成功的被建立,并且收到了来自服务器的
New Session Ticket
数据包即可。 - 客户端B发送
ClientHello
. 这个包的不同点在于: 它需要包含也给pre_shared_key
扩展。 这个扩展中包含了前面从服务器端收到的New Session Ticket
中的 Ticket 信息, 和基于这个 Ticket 计算的一个哈希值。 - 服务器端收到这个
ClientHello
之后,校验pre_shared_key
扩展中的值是否合法。 如果合法,并且服务器选择和当前客户端进行 Session Resumption, 那么它会回复一个ServerHello
, 这个包中包含一个pre_shared_key
扩展,这个扩展中包含它选中的客户端提供的 ticket。 - 客户端在收到服务器端的
ServerHello
之后,发现其中携带pre_shared_key
扩展,对它进行校验,如果检验合法,那么双方便能根据之前TLS连接的信息生成用户当前TLS连接的安全参数(cipher suite, master key 等等)。 - 后续的部分 TLS 握手数据包便可以省略不发,快进到交换
Finished
来表明握手已经成功,接下来可以交换 Application data 了。至此 Session Resumption 成功。
如果在第五步中,服务器收到带有 pre_shared_key 扩展的 ClientHello 之后,不想进行 Session Resumption, 那么回复的 ServerHello 中便不会携带 pre_shared_key 扩展。 那么客户端和服务器便需要想通常那样,进行完整的 TLS 握手流程。
重要的实现细节
在第一次 TLS 握手成功,并且收到来自服务器端的 New Session Ticket
, 客户端需要做如下事情:
- 记录这个数据包中的信息,后续需要使用。
- 在此, 需要计算一个叫做
resumption master secret
的密钥。这个密钥用户计算在Session Resumption 成功之后的 TLS 相关加密参数。
对于想要使用 Session Resumption 方式进行TLS握手的客户端,需要将上面的信息设置到当前客户端上。 在当前客户中,这些信息用于计算附带在 ClientHello
中的 pre_shared_key
扩展。
客户端中的 pre_shared_key
中主要包含一个或多个 OfferedPsks , 每个 OfferedPsks 包含两个信息:
- 一个叫做 PskIdentity 的信息。 这里包含
New Session Ticket
中的 Ticket。服务器端可以根据这个 ticket 来获取上一个 TLS session 的信息,包括上一个 Tls session 中所用到的加密参数等等。还包括一个 ticket 过期时间的信息。 用户判断当前 ticket 是否已经过期。 - 一个叫做 PskBinderEntry 的信息。这里包含 HMAC 的输出,HMAC输入为当前 ClientHello 序列化后的字节数组(排除掉 PskBinderEntry 自己), 使用特定的 binder key 作为 key。 这个 binder key 由
resumption master secret
通过一系列算法生成而来。
服务器端收到来自客户端的 pre_shared_key
扩展之后,检查当前 ticket 的合法性, 从 Ticket 中获取想要恢复的 TLS session 信息。 当决定接受这个扩展之后,回复一个pre_shared_key
扩展给客户端,表明它接受 session resumption.
来自服务器端中的 pre_shared_key
中一个信息: 那就是它所选中的客户端中 OfferedPsks 的索引值。
在客户端和服务器交换了 ClientHello 和 ServerHello 之后,并且得到对方都决定进行 Session resumption 之后,双方都根据自己上一次 TLS session 中计算的 resumption master secret
通过一系列算法来生成用于当前 TLS session 进行加密的 key。这个过程中,不需要额外的TLS数据包的交换。
New Session Ticket
在TLS握手成功后,服务器端可以在任何时刻发送 New Session Ticket 消息给客户端。
这个数据包的作用是: 将当前 ticket 和当前 TLS session 中的 由 resumption master secret 生成的 PSK 关联起来。
它的定义如下:
struct {
uint32 ticket_lifetime;
uint32 ticket_age_add;
opaque ticket_nonce<0..255>;
opaque ticket<1..2^16-1>;
Extension extensions<0..2^16-2>;
} NewSessionTicket;
-
ticket_lifetime: 代表当前 Ticket 的生命周期,单位为秒。最大值为 7天。 如果为零,表示这个 ticket 立即过期。
-
ticket_age_add: 一个随机值,用户混淆客户端发送给服务器端的 ‘Obfucated Ticket Age’ 的值。
-
ticket_nonce: 在计算 PskBinderEntry 的 HMAC 值时,作为 salt。
-
ticket:一个服务器生成的字节数组,内容的含义是实现相关的。 客户端不需要关注这些自己的含义,只需要原样的把它传回给服务器端即可。
-
extension: 略
实践中, 它就像下面这样:
Transport Layer Security
TLSv1.3 Record Layer: Handshake Protocol: New Session Ticket
Opaque Type: Application Data (23)
Version: TLS 1.2 (0x0303)
Length: 252
[Content Type: Handshake (22)]
Handshake Protocol: New Session Ticket
Handshake Type: New Session Ticket (4)
Length: 231
TLS Session Ticket
Session Ticket Lifetime Hint: 604800 seconds (7 days)
Session Ticket Age Add: 2010878192
Session Ticket Nonce Length: 4
Session Ticket Nonce: bae70ebb
Session Ticket Length: 214
Session Ticket: ee7ed0879c8cd275fdce0b5c48…
Extensions Length: 0
Extension: pre_shared_key
这个扩展用于协商使用的 Ticket Id。
就像前文中描述的一样,客户端可以在 ClientHello 中的 pre_shared_key 中包含多个 OfferedPsks, 客户端从中选择一个,然后把它的 index 值通过pre_shared_key 扩展发回客户端,以此来达到协商的目的。
定义如下:
struct {
opaque identity<1..2^16-1>;
uint32 obfuscated_ticket_age;
} PskIdentity;
opaque PskBinderEntry<32..255>;
struct {
PskIdentity identities<7..2^16-1>;
PskBinderEntry binders<33..2^16-1>;
} OfferedPsks;
struct {
select (Handshake.msg_type) {
case client_hello: OfferedPsks;
case server_hello: uint16 selected_identity;
};
} PreSharedKeyExtension;
这里,我们不过多解释了,通过一个例子来看看它的样子。
注意,这个扩展出现在 ClientHello 和 ServerHello 中时,它的定义是不同的!
ClientHello 中的 pre_shared_key 扩展
Extension: pre_shared_key (len=257)
Type: pre_shared_key (41)
Length: 257
Pre-Shared Key extension
Identities Length: 220
PSK Identity (length: 214)
Identity Length: 214
Identity: ee7ed0879…
Obfuscated Ticket Age: 2010878193
PSK Binders length: 33
PSK Binders:
....(HMAC value)
ServerHello 中的 pre_shared_key 扩展:
Extension: pre_shared_key (len=2)
Type: pre_shared_key (41)
Length: 2
Pre-Shared Key extension
Selected Identity: 0
完整的 ClientHello 和 ServerHello
最后,我们包含一个完成的 ClientHello 和 ServerHello 在这里,以供参考:
ClientHello:
TLSv1.3 Record Layer: Handshake Protocol: Client Hello
Content Type: Handshake (22)
Version: TLS 1.0 (0x0301)
Length: 503
Handshake Protocol: Client Hello
Handshake Type: Client Hello (1)
Length: 499
Version: TLS 1.2 (0x0303)
Random: b4d4d566da09c6.....ea
Session ID Length: 32
Session ID: 28ef0f99cb.....5b7653a
Cipher Suites Length: 38
Cipher Suites (19 suites)
Compression Methods Length: 1
Compression Methods (1 method)
Extensions Length: 388
Extension: status_request (len=5)
Type: status_request (5)
Length: 5
Certificate Status Type: OCSP (1)
Responder ID list Length: 0
Request Extensions Length: 0
Extension: supported_groups (len=10)
Type: supported_groups (10)
Length: 10
Supported Groups List Length: 8
Supported Groups (4 groups)
Extension: ec_point_formats (len=2)
Type: ec_point_formats (11)
Length: 2
EC point formats Length: 1
Elliptic curves point formats (1)
Extension: session_ticket (len=0)
Type: session_ticket (35)
Length: 0
Data (0 bytes)
Extension: signature_algorithms (len=26)
Type: signature_algorithms (13)
Length: 26
Signature Hash Algorithms Length: 24
Signature Hash Algorithms (12 algorithms)
Extension: renegotiation_info (len=1)
Type: renegotiation_info (65281)
Length: 1
Renegotiation Info extension
Extension: signed_certificate_timestamp (len=0)
Type: signed_certificate_timestamp (18)
Length: 0
Extension: supported_versions (len=3)
Type: supported_versions (43)
Length: 3
Supported Versions length: 2
Supported Version: TLS 1.3 (0x0304)
Extension: key_share (len=38)
Type: key_share (51)
Length: 38
Key Share extension
Extension: psk_key_exchange_modes (len=2)
Type: psk_key_exchange_modes (45)
Length: 2
PSK Key Exchange Modes Length: 1
PSK Key Exchange Mode: PSK with (EC)DHE key establishment (psk_dhe_ke) (1)
Extension: pre_shared_key (len=257)
Type: pre_shared_key (41)
Length: 257
Pre-Shared Key extension
Identities Length: 220
PSK Identity (length: 214)
Identity Length: 214
Identity: ee7ed087...…
Obfuscated Ticket Age: 2010878193
PSK Binders length: 33
PSK Binders
ServerHello:
TLSv1.3 Record Layer: Handshake Protocol: Server Hello
Content Type: Handshake (22)
Version: TLS 1.2 (0x0303)
Length: 128
Handshake Protocol: Server Hello
Handshake Type: Server Hello (2)
Length: 124
Version: TLS 1.2 (0x0303)
Random: b624.....1
Session ID Length: 32
Session ID: 28ef0f99.....3a
Cipher Suite: TLS_AES_128_GCM_SHA256 (0x1301)
Compression Method: null (0)
Extensions Length: 52
Extension: key_share (len=36)
Type: key_share (51)
Length: 36
Key Share extension
Extension: supported_versions (len=2)
Type: supported_versions (43)
Length: 2
Supported Version: TLS 1.3 (0x0304)
Extension: pre_shared_key (len=2)
Type: pre_shared_key (41)
Length: 2
Pre-Shared Key extension
Selected Identity: 0
Leave a Reply