阅读 101

TLS 1.3 Handshake Protocol (下)

接上文

四. Authentication Messages

正如我们在 section-2 中讨论的,TLS 使用一组通用的消息用于身份验证,密钥确认和握手的正确性:Certificate, CertificateVerify 和 Finished。(PSK binders 也以类似的方式进行密钥确认)。这三条消息总是作为握手消息的最后三条消息。Certificate 和 CertificateVerify 消息如下面描述的那样,只在某些情况才会发送。Finished 的消息总是作为认证块的一部分发送。这些消息使用从 sender_handshake_traffic_secret 派生出来的密钥进行加密。

Authentication 消息的计算统一采用以下的输入方式:

  • 要使用证书和签名密钥
  • 握手上下文由哈希副本中的一段消息集组成
  • Base key 用于计算 MAC 密钥

基于这些输入,消息包含:

  • Certificate:
    用于认证的证书和链中任何支持的证书。请注意,基于证书的 Client 身份验证在 PSK 握手流中不可用(包括 0-RTT)

  • CertificateVerify:
    根据 Transcript-Hash(Handshake Context, Certificate) 的值得出的签名

  • Finished:
    根据 Transcript-Hash(Handshake Context, Certificate, CertificateVerify) 的值得出的 MAC 。使用从 Base key 派生出来的 MAC key 计算的 MAC 值。

对于每个场景,下表定义了握手上下文和 MAC Base Key

   +-----------+-------------------------+-----------------------------+
   | Mode      | Handshake Context       | Base Key                    |
   +-----------+-------------------------+-----------------------------+
   | Server    | ClientHello ... later   | server_handshake_traffic_   |
   |           | of EncryptedExtensions/ | secret                      |
   |           | CertificateRequest      |                             |
   |           |                         |                             |
   | Client    | ClientHello ... later   | client_handshake_traffic_   |
   |           | of server               | secret                      |
   |           | Finished/EndOfEarlyData |                             |
   |           |                         |                             |
   | Post-     | ClientHello ... client  | client_application_traffic_ |
   | Handshake | Finished +              | secret_N                    |
   |           | CertificateRequest      |                             |
   +-----------+-------------------------+-----------------------------+
复制代码

1. The Transcript Hash

TLS 中的许多加密计算都使用了哈希副本。这个值是通过级联每个包含的握手消息的方式进来哈希计算的,它包含握手消息头部携带的握手消息类型和长度字段,但是不包括记录层的头部。例如:

Transcript-Hash(M1, M2, ... Mn) = Hash(M1 || M2 || ... || Mn)
复制代码

作为此一般规则的例外,当 Server 用一条 HelloRetryRequest 消息来响应一条 ClientHello 消息时,ClientHello1 的值替换为包含 Hash(ClientHello1)的握手类型为 "message_hash" 的特殊合成握手消息。例如:

  Transcript-Hash(ClientHello1, HelloRetryRequest, ... Mn) =
      Hash(message_hash ||        /* Handshake type */
           00 00 Hash.length  ||  /* Handshake message length (bytes) */
           Hash(ClientHello1) ||  /* Hash of ClientHello1 */
           HelloRetryRequest  || ... || Mn)
复制代码

设计这种结构的原因是允许 Server 通过在 cookie 中仅存储 ClientHello1 的哈希值来执行无状态 HelloRetryRequest,而不是要求它导出整个中间哈希状态。

具体而言,哈希副本始终取自于下列握手消息序列,从第一个 ClientHello 开始,仅包括已发送的消息:ClientHello, HelloRetryRequest, ClientHello, ServerHello, EncryptedExtensions, server CertificateRequest, server Certificate, server CertificateVerify, server Finished, EndOfEarlyData, client Certificate, client CertificateVerify, client Finished。

通常上,实现方可以下面的方法来实现哈希副本:根据协商的哈希来维持一个动态的哈希副本。请注意,随后的握手后认证不会相互包含,只是通过主握手结束的消息。

2. Certificate

此消息将端点的证书链发给对端。

每当约定的密钥交换方法是用证书进行认证(这包括本文档中除了 PSK 以外定义的所有密钥交换方法)的时候,Server 就必须发送 Certificate 消息。

当且仅当 Server 通过发送 CertificateRequest 消息请求 Client 认证时,Client 必须发送 Certificate 消息。

如果 Server 请求 Client 认证但没有合适的证书可用,则 Client 必须发送不包含证书的证书消息(例如,具有长度为 0 的 "certificate_list" 字段)。Finished 消息必须发送,无论 Certificate 消息是否为空。

Certificate 消息的结构体是:

      enum {
          X509(0),
          RawPublicKey(2),
          (255)
      } CertificateType;

      struct {
          select (certificate_type) {
              case RawPublicKey:
                /* From RFC 7250 ASN.1_subjectPublicKeyInfo */
                opaque ASN1_subjectPublicKeyInfo<1..2^24-1>;

              case X509:
                opaque cert_data<1..2^24-1>;
          };
          Extension extensions<0..2^16-1>;
      } CertificateEntry;

      struct {
          opaque certificate_request_context<0..2^8-1>;
          CertificateEntry certificate_list<0..2^24-1>;
      } Certificate;
复制代码
  • certificate_request_context:
    如果此消息是响应 CertificateRequest 消息的,则该消息中的 certificate_request_context 的值不为 0。否则(在 Server 认证的情况下),该字段应为零长度。

  • certificate_list:
    这是一个 CertificateEntry 结构的序列(链),每个结构包含单个证书和一组扩展。

  • extensions:
    CertificateEntry 的一组扩展值。"Extension" 的格式在 [Section 4.2] 中定义了。有效的扩展包括 OCSP 状态扩展 [RFC6066] 和 SignedCertificateTimestamp [RFC6962] 扩展。未来可以为此消息定义一些新的扩展。Server 的 Certificate 消息中的扩展必须对应于 ClientHello 消息中的扩展。Client 的 Certificate 消息中的扩展必须对应于 Server 的 CertificateRequest 消息中的扩展。如果一个扩展适应用于整个链,它应该被包括在第一个 CertificateEntry 中。

如果没有在 EncryptedExtensions 中协商相应的证书类型扩展名 ("server_certificate_type" 或 "client_certificate_type"),或者协商了 X.509 证书类型,则每个 CertificateEntry 都要包含 DER 编码的 X.509 证书。发件者的证书必须位于列表中的第一个 CertificateEntry 中。之后的每个证书都应该直接证明其前面的证书。由于证书验证要求信任锚独立分发,因此可以从链中省略指定信任锚的证书(前提是已知支持的对等方拥有可省略的证书)。

注意:TLS 1.3 之前的版本,"certificate_list" 排序需要每个证书要证明紧接在其前面的证书,然而,一些实现允许一些灵活性。Server 有时为了过渡的目的而发送当前和已弃用的中间体,而其他的配置不正确,但这些情况仍然可以正确地验证。为了最大程度的兼容性,所有实现应该准备处理可能是无关紧要的证书和 TLS 版本的任意排序,但最终实体证书(排序的顺序)必须是第一个。

如果协商了 RawPublicKey 证书类型,则 certificate_list 必须包含不超过一个CertificateEntry,CertificateEntry 中包含定义在 [RFC7250], Section 3 中的 ASN1_subjectPublicKeyInfo 值。

OpenPGP 证书类型禁止在 TLS 1.3 中使用。

Server 的 certificate_list 必须始终是非空的。如果 Client 没有适当的证书要发送以响应 Server 的身份验证请求,则会发送空的 certificate_list。

(1) OCSP Status and SCT Extensions

[RFC6066][RFC6961] 提供了协商 Server 向 Client 发送 OCSP 响应的扩展。 在 TLS 1.2 及以下版本中,Server 回复空的扩展名以表示对此扩展的协商,并且在 CertificateStatus 消息中携带 OCSP 信息。在 TLS 1.3 中,Server 的 OCSP 信息在包含相关证书的 CertificateEntry 中的扩展中。特别的,来自 Server 的 "status_request" 扩展的主体必须是分别在 [RFC6066][RFC6960] 中定义的 CertificateStatus 结构。

注意:status_request_v2 扩展 [RFC6961] 已经废弃了,TLS 1.3 不能根据它是否存在或者根据它的信息来出来 ClientHello 消息。特别是,禁止在 EncryptedExtensions, CertificateRequest 和 Certificate 消息中发送 status_request_v2 扩展。TLS 1.3 的 Server 必须要能够处理包含它的 ClientHello 消息,因为这条消息可能是由希望在早期协议版本中使用它的 Client 发送的。

Server 可以通过在其 CertificateRequest 消息中发送空的 "status_request" 扩展来请求 Client 使用其证书来做 OCSP 的响应。如果 Client 选择性的发送 OCSP 响应,则其 "status_request" 扩展的主体必须是在 [RFC6966] 中定义的 CertificateStatus 结构。

类似地,[RFC6962] 为 Server 提供了一种机制,用在 TLS 1.2 及更低版本中的,可在 ServerHello 中发送签名证书时间戳 (SCT) 的扩展。 在 TLS 1.3 中,Server 的 SCT 信息在 CertificateEntry 的扩展中。

(2) Server Certificate Selection

以下规则适用于 Server 发送的证书:

  • 证书类型必须是 X.509v3 [RFC5280],除非另有明确协商(例如,[RFC5081]

  • Server 的终端实体证书的公钥(和相关限制)必须与 Client的 "signature_algorithms" 扩展(目前为RSA,ECDSA 或 EdDSA)中的所选认证算法兼容。

  • 证书必须允许密钥用于签名(即,如果存在密钥用法扩展,则必须设置 digitalSignature 位),并在 Client 的"signature_algorithms"/"signature_algorithms_cert" 扩展中指示签名方案。

  • "server_name" [RFC6066] 和 "certificate_authorities" 扩展用于指导证书选择。由于 Server 可能需要存在 "server_name" 扩展名,因此 Client 应该在适用时发送此扩展名。

如果 Server 能够提供证书链,Server 所有的证书都必须由 Client 提供的签名算法签名。自签名的证书或预期为信任锚的证书不会作为链的一部分进行验证,因此可以使用任何算法进行签名。

如果 Server 不能产生只通过所指示的支持的算法签名的证书链,则它应当通过向 Client 发送其选择的证书链来继续握手,该证书链可能会包括 Client 不知道能否支持的算法。这个回退链可以只在 Client 允许的情况下才可以使用已弃用的 SHA-1 哈希算法,其他情况都必须禁止使用 SHA-1 哈希算法。

如果 Client 无法使用提供的证书构造可接受的证书链,那么必须中止握手。中止握手并发送证书相关的 alert 消息(默认的,发送 "unsupported_certificate" alert 消息)

如果 Server 有多张证书,它会根据上述标准(除了其他标准以外,如传输层端点,本地配置和首选项)选择其中的一个证书。

(3) Client Certificate Selection

以下的规则适用于 Client 发送的证书:

  • 证书类型必须是 X.509v3 [RFC5280],除非另有明确协商(例如,[RFC5081]

  • 如果 CertificateRequest 消息中 "certificate_authorities" 扩展不为空,则证书链中的至少一个证书应该由所列出的 CA 之一发布的。

  • 证书必须使用可接受的签名算法签名,如第 4.3.2 节所述。注意,这放宽了在 TLS 的先前版本中发现的证书签名算法的约束。

  • 如果 CertificateRequest 消息包含非空的 "oid_filters" 扩展,则终端实体证书必须匹配 Client 识别的扩展 OID,如第 4.2.5 节中所述。

(4) Receiving a Certificate Message

通常,详细的证书验证程序超出了 TLS 的范围(参见[RFC5280])。 本节提供特定于 TLS 的要求。

如果 Server 提供空的证书消息,则 Client 必须使用 "decode_error" alert 消息中止握手。

如果 Client 没有发送任何证书(即,它发送一个空的证书消息),Server 可以自行决定是否在没有 Client 认证的情况下继续握手,或者使用 "certificate_required" alert 消息中止握手。此外,如果证书链的某些方面是不可接受的(例如,它未由已知的可信 CA 签名),则 Server 可以自行决定是继续握手(考虑 Client 还没有经过身份验证)还是中止握手。

任何端点接收任何需要使用任何签名算法使用 MD5 哈希验证的证书都必须使用 "bad_certificate" alert 消息中止握手。不推荐使用 SHA-1,并且建议任何接收任何使用 SHA-1 哈希使用任何签名算法验证的证书的端点都会使用 "bad_certificate" alert 消息中止握手。为清楚起见,这意味着端点可以接受这些算法用于自签名或信任锚的证书。

建议所有端点尽快转换为 SHA-256 或更好的算法,以保持与当前正在逐步淘汰 SHA-1 支持的实现的互操作性。

请注意,包含一个签名算法的密钥的证书可以使用不同的签名算法进行签名(例如,使用 ECDSA 密钥签名的 RSA 密钥)。

3. Certificate Verify

此消息用于提供端点拥有与其证书对应的私钥的明确证据。CertificateVerify 消息还为到此为止的握手提供完整性。Server 必须在通过证书进行身份验证时发送此消息。每当通过证书进行身份验证时(即,当证书消息非空时),Client 必须发送此消息。发送时,此消息必须在 Certificate 消息之后立即出现,并且紧接在 Finished 消息之前。

这条消息的结构体是:

      struct {
          SignatureScheme algorithm;
          opaque signature<0..2^16-1>;
      } CertificateVerify;
复制代码

algorithm 字段指定使用的签名算法(有关此类型的定义,请参见第 4.2.3 节)。signature 字段是使用该算法的数字签名。签名中涵盖的内容是第 4.4.1 节中描述的哈希输出,即:

      Transcript-Hash(Handshake Context, Certificate)
复制代码

计算数字签名是级联计算的:

  • 由八位字节32(0x20)组成的字符串重复 64 次
  • 上下文字符串
  • 用作分隔符的单个0字节
  • 要签名的内容

设计这个结构目的是为了防止对先前版本的 TLS 的攻击,其中 ServerKeyExchange 格式意味着攻击者可以获得具有所选 32 字节前缀(ClientHello.random)的消息的签名。 最初的 64 字节填充将清除 Server 控制的 ServerHello.random 中的前缀。

Server 签名的上下文字符串是 "TLS 1.3,Server CertificateVerify"。Client 签名的上下文字符串是 "TLS 1.3,Client CertificateVerify"。它用于在不同的上下文中提供签名之间的分离,帮助抵御潜在的跨协议攻击。

例如,如果 hash副本 是 32 字节 01(这个长度对 SHA-256 有意义),Server 的 CertificateVerify 的数字签名所涵盖的内容将是:

      2020202020202020202020202020202020202020202020202020202020202020
      2020202020202020202020202020202020202020202020202020202020202020
      544c5320312e332c207365727665722043657274696669636174655665726966
      79
      00
      0101010101010101010101010101010101010101010101010101010101010101
复制代码

在发送方,用于计算 CertificateVerify 消息的签名字段的过程作为输入:

  • 数字签名算涵盖的内容

  • 与上一条消息中发送的证书对应的私有签名密钥

如果由 Server 发送 CertificateVerify 消息,则签名算法必须是 Client "signature_algorithms" 扩展中提供的,除非在没有不支持的算法的情况下不能生成有效的证书链(除非当前支持的算法都不能生成有效的证书链)。

如果由 Client 发送,则签名中使用的签名算法必须是 CertificateRequest 消息中 "signature_algorithms" 扩展的 supported_signature_algorithms 字段中存在的签名算法之一。

另外,签名算法必须与发送者的终端实体证书中的密钥兼容。无论 RSASSA-PKCS1-v1_5 算法是否出现在 "signature_algorithms" 中,RSA 签名都必须使用 RSASSA-PSS 算法。SHA-1 算法禁止用于 CertificateVerify 消息的任何签名。

本规范中的所有 SHA-1 签名算法仅定义用于旧证书,并且对 CertificateVerify 签名无效。

CertificateVerify 消息的接收者必须验证签名字段。验证过程作为输入:

  • 数字签名所涵盖的内容

  • 在关联的证书消息中找到的最终实体证书中包含的公钥

  • 在 CertificateVerify 消息的签名字段中收到的数字签名

如果验证失败,接收方必须通过 "decrypt_error" 警报终止握手。

4. Finished

Finished 消息是认证块中的最后一条消息。它对提供握手和计算密钥的身份验证起了至关重要的作用。

Finished 消息的收件人必须验证内容是否正确,如果不正确,必须使用 "decrypt_error" alert 消息终止连接。

一旦一方已发送其 Finished 消息并已收到并验证来自其对端的 Finished 消息,它就可以开始通过该连接发送和接收应用数据。有两种设置允许在接收对端的 Finished 之前发送数据:

  1. Section 4.2.10 中所述,Client 可以发送 0-RTT 数据。
  2. Server 可以在第一个 flight 之后就发送数据,但是因为握手还没有完成,所以不能保证对端的身份正确性,以及对端是否还在线。(ClientHello 可能重播)

用于计算 Finished 消息的密钥是使用 HKDF,它是从第 4.4 节中定义的 Base Key 计算而来的(参见第7.1节)。特别的:

   finished_key =
       HKDF-Expand-Label(BaseKey, "finished", "", Hash.length)
复制代码

这条消息的数据结构是:

      struct {
          opaque verify_data[Hash.length];
      } Finished;
复制代码

verify_data 按照如下方法计算:

      verify_data =
          HMAC(finished_key,
               Transcript-Hash(Handshake Context,
                               Certificate*, CertificateVerify*))

      * Only included if present.
复制代码

HMAC [RFC2104] 使用哈希算法进行握手。如上所述,HMAC 输入通常是通过动态的哈希实现的,即,此时仅是握手的哈希。

在以前版本的 TLS 中,verify_data 的长度总是 12 个八位字节。在 TLS 1.3 中,它是用来表示握手的哈希的 HMAC 输出的大小。

注意:警报和任何其他非握手记录类型不是握手消息,并且不包含在哈希计算中

Finished 消息之后的任何记录都必须在适当的 client_application_traffic_secret_N 下加密,如第 7.2 节所述。特别是,这包括 Server 为了响应 Client 的 Certificate 消息和 CertificateVerify 消息而发送的任何 alert。

5. End of Early Data

      struct {} EndOfEarlyData;
复制代码

如果 Server 在 EncryptedExtensions 中发送了 "early_data" 扩展,则 Client 必须在收到 Server 的 Finished 消息后发送 EndOfEarlyData 消息。 如果 Server 没有在 EncryptedExtensions中发送 "early_data" 扩展,那么 Client 绝不能发送 EndOfEarlyData 消息。此消息表示已传输完了所有 0-RTT application_data消息(如果有),并且接下来的记录受到握手流量密钥的保护。Server 不能发送此消息,Client 如果收到了这条消息,那么必须使用 "unexpected_message" alert 消息终止连接。这条消息使用从 client_early_traffic_secret 中派生出来的密钥进行加密保护。

6. Post-Handshake Messages

TLS 还允许在主握手后发送其他的消息。这些消息使用握手内容类型,并使用适当的应用程序流量密钥进行加密。

(1) New Session Ticket Message

在 Server 接收到 Client 的 Finished 消息以后的任何时刻,它都可以发送 NewSessionTicket 消息。此消息在 ticket 值和从恢复主密钥派生出来的 PSK 之间创建了唯一的关联。

Client 在 ClientHello 消息中包含 "pre_shared_key" 扩展,并在扩展中包含 ticket ,那么 Client 就可能在未来的握手中使用 PSK。Server 可能在一个连接中发送多个 ticket,发送时机可能是一个接一个的立即发送,也可能是在某个特定事件以后发送。例如,Server 可能会在握手后身份验证之后发送新的 ticket,以封装其他 Client 身份验证状态。多个 ticket 对于 Client 来说,可用于各种目的,例如:

  • 打开多个并行的 HTTP 连接

  • 通过(例如) Happy Eyeballs [RFC8305] 或相关的技术在接口和地址簇上进行连接竞争

任何 ticket 必须只能使用与用于建立原始连接的 KDF 哈希算法相同的密码套件来恢复会话。

Client 必须只有在新的 SNI 值对原始会话中提供的 Server 证书有效时才能恢复,并且只有在 SNI 值与原始会话中使用的 SNI 值匹配时才应恢复。后者是性能优化:通常,没有理由期望单个证书所涵盖的不同 Server 之间能够相互接受彼此的 ticket;因此,在这种情况下尝试恢复会话将会浪费一次性的 ticket。如果提供了这种指示(外部或通过任何其他方式),则 Client 可能可以使用不同的 SNI 值进行恢复会话。

在恢复会话时,如果向调用的应用程序报告 SNI 值,则实现方必须使用在恢复 ClientHello 中发送的值而不是在先前会话中发送的值。请注意,如果 Server 的实现拒绝了不同 SNI 值的所有 PSK 标识,则这两个值总是相同。

注意:虽然恢复主密钥取决于 Client 的第二次 flight,但是不请求 Client 身份验证的 Server 可以独立计算转录哈希的剩余部分,然后在发送 Finished 消息后立即发送 NewSessionTicket 而不是等待 Client 的 Finished 消息。这可能适用于 Client 需要并行打开多个 TLS 连接并且可以从减少恢复握手的开销中受益的情况。

      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 的生存时间,这个时间是以 ticket 发布时间为网络字节顺序的 32 位无符号整数表示以秒为单位的时间。Server 禁止使用任何大于 604800秒(7 天)的值。值为零表示应立即丢弃 ticket。无论 ticket_lifetime 如何,Client 都不得缓存超过 7 天的 ticket,并且可以根据本地策略提前删除 ticket。Server 可以将 ticket 视为有效的时间段短于 ticket_lifetime 中所述的时间段。

  • ticket_age_add:
    安全的生成的随机 32 位值,用于模糊 Client 在 "pre_shared_key" 扩展中包含的 ticket 的时间。Client 的 ticket age 以模 2 ^ 32 的形式添加此值,以计算出 Client 要传输的值。Server 必须为它发出的每个 ticket 生成一个新值。

  • ticket_nonce:
    每一个 ticket 的值,在本次连接中发出的所有的 ticket 中是唯一的。

  • ticket:
    这个值是被用作 PSK 标识的值。ticket 本身是一个不透明的标签。它可以是数据库查找键,也可以是自加密和自我验证的值。

  • extensions:
    ticket 的一组扩展值。扩展格式在 4.2 节中定义的。Client 必须忽略无法识别的扩展。

当前为 NewSessionTicket 定义的唯一扩展名是 "early_data",表示该 ticket 可用于发送 0-RTT 数据(第4.2.10节)。 它包含以下值:

  • max_early_data_size:
    这个字段表示使用 ticket 时允许 Client 发送的最大 0-RTT 数据量(以字节为单位)。数据量仅计算应用数据有效载荷(即,明文但不填充或内部内容类型字节)。Server 如果接收的数据大小超过了 max_early_data_size 字节的 0-RTT 数据,应该立即使用 "unexpected_message" alert 消息终止连接。请注意,由于缺少加密材料而拒绝 early data 的 Server 将无法区分内容中的填充部分,因此 Client 不应该依赖于能够在 early data 记录中发送大量填充内容。

PSK 关联的 ticket 计算方法如下:

       HKDF-Expand-Label(resumption_master_secret,
                        "resumption", ticket_nonce, Hash.length)
复制代码

因为 ticket_nonce 值对于每个 NewSessionTicket 消息都是不同的,所以每个 ticket 会派生出不同的 PSK。

请注意,原则上可以继续发布新 ticket,该 ticket 无限期地延长生命周期,这个生命周期是最初从初始非 PSK 握手中(最可能与对等证书相关联)派生得到的密钥材料的生命周期。

建议实现方对密钥材料这些加上总寿命时间的限制。这些限制应考虑到对等方证书的生命周期,干预撤销的可能性以及自从对等方在线 CertificateVerify 签名到当前时间的这段时间。

(2) Post-Handshake Authentication

当 Client 发送了 "post_handshake_auth" 扩展(参见第4.2.6节)时,Server 可以在握手完成后随时通过发送 CertificateRequest 消息来请求客户端身份验证。Client 必须使用适当的验证消息进行响应(参见第4.4节)。如果 Client 选择进行身份验证,则必须发送 Certificate,CertificateVerify,Finished 消息。如果 Client 拒绝身份验证,它必须发送一个 Certificate 证书消息,其中不包含证书,然后是 Finished 消息。响应 Server 的所有 Client 消息必须连续出现在线路上,中间不能有其他类型的消息。

在没有发送 "post_handshake_auth" 扩展的情况下接收 CertificateRequest 消息的 Client 必须发送 "unexpected_message" alert 消息。

注意:由于 Client 身份验证可能涉及提示用户,因此 Server 必须做好一些延迟的准备,包括在发送 CertificateRequest 和接收响应之间接收任意数量的其他消息。此外,Client 如果连续接收到了多个 CertificateRequests 消息,Client 可能会以不同于它们的顺序响应它们(certificate_request_context 值允许服务器消除响应的歧义)

(3) Key and Initialization Vector Update

KeyUpdate 握手消息用于表示发送方正在更新其自己的发送加密密钥。任何对等方在发送 Finished 消息后都可以发送此消息。在接收 Finished 消息之前接收 KeyUpdate 消息的,实现方必须使用 "unexpected_message" alert 消息终止连接。发送 KeyUpdate 消息后,如第 7.2 节所描述的计算方法,发送方应使用新一代的密钥发送其所有流量。收到 KeyUpdate 后,接收方必须更新其接收密钥。

      enum {
          update_not_requested(0), update_requested(1), (255)
      } KeyUpdateRequest;

      struct {
          KeyUpdateRequest request_update;
      } KeyUpdate;
复制代码
  • request_update:
    这个字段表示 KeyUpdate 的收件人是否应使用自己的 KeyUpdate 进行响应。 如果实现接收到任何其他的值,则必须使用 "illegal_parameter" alert 消息终止连接。

如果 request_update 字段设置为 "update_requested",则接收方必须在发送其下一个应用数据记录之前发送自己的 KeyUpdate,其中 request_update 设置为 "update_not_requested"。此机制允许任何一方强制更新整个连接,但会导致一个实现方接收多个 KeyUpdates,并且它还是静默的响应单个更新。请注意,实现方可能在发送 KeyUpdate (把 request_update 设置为 "update_requested") 与接收对等方的 KeyUpdate 之间接收任意数量的消息,因为这些消息可能早就已经在传输中了。但是,由于发送和接收密钥是从独立的流量密钥中导出的,因此保留接收流量密钥并不会影响到发送方更改密钥之前发送的数据的前向保密性。

如果实现方独立地发送它们自己的 KeyUpdates,其 request_update 设置为 "update_requested" 并且它们的消息都是传输中,结果是双方都会响应,双方都会更新密钥。

发送方和接收方都必须使用旧密钥加密其 KeyUpdate 消息。另外,在接受使用新密钥加密的任何消息之前,双方必须强制接收带有旧密钥的 KeyUpdate。如果不这样做,可能会引起消息截断攻击。


Reference:

RFC 8446

GitHub Repo:Halfrost-Field

Follow: halfrost · GitHub

Source: halfrost.com/TLS_1.3_Han…

关注下面的标签,发现更多相似文章
评论