TLS13 Session resumption
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 SecurityTLSv1.3 Record Layer: Handshake Protocol: New Session TicketOpaque Type: Application Data (23)Version: TLS 1.2 (0x0303)Length: 252[Content Type: Handshake (22)]Handshake Protocol: New Session TicketHandshake Type: New Session Ticket (4)Length: 231TLS Session TicketSession Ticket Lifetime Hint: 604800 seconds (7 days)Session Ticket Age Add: 2010878192Session Ticket Nonce Length: 4Session Ticket Nonce: bae70ebbSession Ticket Length: 214Session 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: 257Pre-Shared Key extensionIdentities Length: 220PSK Identity (length: 214)Identity Length: 214Identity: ee7ed0879…Obfuscated Ticket Age: 2010878193PSK Binders length: 33PSK Binders:....(HMAC value)
ServerHello 中的 pre_shared_key 扩展:
Extension: pre_shared_key (len=2)Type: pre_shared_key (41)Length: 2Pre-Shared Key extensionSelected 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 HelloContent Type: Handshake (22)Version: TLS 1.2 (0x0303)Length: 128Handshake Protocol: Server HelloHandshake Type: Server Hello (2)Length: 124Version: TLS 1.2 (0x0303)Random: b624.....1Session ID Length: 32Session ID: 28ef0f99.....3aCipher Suite: TLS_AES_128_GCM_SHA256 (0x1301)Compression Method: null (0)Extensions Length: 52Extension: key_share (len=36)Type: key_share (51)Length: 36Key Share extensionExtension: supported_versions (len=2)Type: supported_versions (43)Length: 2Supported Version: TLS 1.3 (0x0304)Extension: pre_shared_key (len=2)Type: pre_shared_key (41)Length: 2Pre-Shared Key extensionSelected Identity: 0