TLS13 Session resumption

TLS13 的 Session resumption 和之前版本不同

什么是 Session resumption: 在之前的 TLS 连接之上,基于已经交换过的信息, 快速的建立另外一个TLS 连接的机制。

为什么需要 Session resumption: 加速TLS握手, 更快的准备好安全的数据通道.

常见的使用场景:

  1. FTP 协议。 在 FTP 协议中,当 control channel 使用 TLS 协议进行通信,通常会要求 Data channel 也进行 TLS 加密通信,并且强制要求 Data channel 使用 Session resumption 机制建立 TLS 连接。
  2. HTTP 协议中,当我们需要和同一个服务器建立多个 TLS 连接时,我们可以选择在成功的建立一条TLS连接之后,后续连接使用 Session resumption 来加速 TLS 连接建立的速度。
  3. 其他场景。

概述


正如前面所说, 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 在协议上的实现如下:

  1. 客户端A和服务器先建立一条 TLS13 的连接,并进行后续的通信。
  2. 通常情况下, 如果服务器支持 Session Resumption, 它便会给你发送一个 New Session Ticket 数据包。(这个包我们后面详细解析, 这里只需要知道它包含了一个 Ticket, 这个 Ticket 用于Session Resumption)
  3. 接着,客户端B(可以是另外一个客户端B或者同一个客户端A)需要和服务器建立 TLS13 连接进行通信。这里,第一条 TLS 连接是否依然存活已经不再重要, 仅仅需要第一次的 TLS连接成功的被建立,并且收到了来自服务器的 New Session Ticket 数据包即可。
  4. 客户端B发送 ClientHello. 这个包的不同点在于: 它需要包含也给 pre_shared_key扩展。 这个扩展中包含了前面从服务器端收到的 New Session Ticket 中的 Ticket 信息, 和基于这个 Ticket 计算的一个哈希值。
  5. 服务器端收到这个 ClientHello 之后,校验 pre_shared_key扩展中的值是否合法。 如果合法,并且服务器选择和当前客户端进行 Session Resumption, 那么它会回复一个 ServerHello, 这个包中包含一个pre_shared_key 扩展,这个扩展中包含它选中的客户端提供的 ticket。
  6. 客户端在收到服务器端的ServerHello 之后,发现其中携带pre_shared_key扩展,对它进行校验,如果检验合法,那么双方便能根据之前TLS连接的信息生成用户当前TLS连接的安全参数(cipher suite, master key 等等)。
  7. 后续的部分 TLS 握手数据包便可以省略不发,快进到交换 Finished 来表明握手已经成功,接下来可以交换 Application data 了。至此 Session Resumption 成功。

如果在第五步中,服务器收到带有 pre_shared_key 扩展的 ClientHello 之后,不想进行 Session Resumption, 那么回复的 ServerHello 中便不会携带 pre_shared_key 扩展。 那么客户端和服务器便需要想通常那样,进行完整的 TLS 握手流程。


重要的实现细节


在第一次 TLS 握手成功,并且收到来自服务器端的 New Session Ticket , 客户端需要做如下事情:

  1. 记录这个数据包中的信息,后续需要使用。
  2. 在此, 需要计算一个叫做 resumption master secret 的密钥。这个密钥用户计算在Session Resumption 成功之后的 TLS 相关加密参数。

对于想要使用 Session Resumption 方式进行TLS握手的客户端,需要将上面的信息设置到当前客户端上。 在当前客户中,这些信息用于计算附带在 ClientHello 中的 pre_shared_key扩展。

客户端中的 pre_shared_key 中主要包含一个或多个 OfferedPsks , 每个 OfferedPsks 包含两个信息:

  1. 一个叫做 PskIdentity 的信息。 这里包含 New Session Ticket 中的 Ticket。服务器端可以根据这个 ticket 来获取上一个 TLS session 的信息,包括上一个 Tls session 中所用到的加密参数等等。还包括一个 ticket 过期时间的信息。 用户判断当前 ticket 是否已经过期。
  2. 一个叫做 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


Comments

Leave a Reply

Your email address will not be published. Required fields are marked *