| RFC 9846 | TLS | 2026 年 7 月 |
| Rescorla | 标准化进程 | [页] |
本文档规定传输层安全 (TLS)协议 1.3 版。TLS 允许客户端/服务器应用程序 通过互联网进行通信,其设计旨在防止窃听、 篡改和消息伪造。¶
本文档废止 RFC 8446, 该 RFC 规定了 TLS 1.3。本文档废止 RFC 5246(规定 TLS 1.2)以及 RFC 5077、 6961、7627 和 8422,这些 RFC 均与 TLS 1.2 或更早版本有关, 并更新 RFC 5705 和 6066。本文档还规定了 TLS 1.2 实现的新要求。¶
这是一份互联网标准化进程文档。¶
本文档是互联网工程任务组 (IETF)的成果。它代表 IETF 社区的共识。本文档已经 过公开评审,并获互联网工程指导委员会 (IESG)批准发布。有关互联网标准的更多 信息,请参阅 RFC 7841 第 2 节。¶
有关本文档当前状态、任何 勘误以及如何提供反馈的信息,可在 https://www.rfc-editor.org/info/rfc9846 获取。¶
Copyright (c) 2026 IETF Trust and the persons identified as the document authors. All rights reserved.¶
This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info) in effect on the date of publication of this document. Please review these documents carefully, as they describe your rights and restrictions with respect to this document. Code Components extracted from this document must include Revised BSD License text as described in Section 4.e of the Trust Legal Provisions and are provided without warranty as described in the Revised BSD License.¶
This document may contain material from IETF Documents or IETF Contributions published or made publicly available before November 10, 2008. The person(s) controlling the copyright in some of this material may not have granted the IETF Trust the right to allow modifications of such material outside the IETF Standards Process. Without obtaining an adequate license from the person(s) controlling the copyright in such materials, this document may not be modified outside the IETF Standards Process, and derivative works of it may not be created outside the IETF Standards Process, except to format it for publication as an RFC or to translate it into languages other than English.¶
TLS 的主要目标是在两个通信对等方之间提供安全信道; 对底层传输的唯一要求是提供可靠且有序的数据流。具体而言, 安全信道应提供以下属性:¶
身份验证:信道的服务器端始终 经过身份验证;客户端可选择进行 身份验证。身份验证可以通过非对称密码学 (例如 RSA [RSA]、椭圆曲线数字 签名 算法(ECDSA)[DSS] 或 Edwards 曲线 数字签名 算法(EdDSA)[RFC8032])或 对称预共享密钥 (PSK)来实现。¶
机密性:信道建立后通过该信道发送的数据 仅对 端点可见。TLS 不会隐藏其传输数据的长度, 但端点可以填充 TLS 记录以掩盖长度, 并增强对流量分析技术的防护。¶
完整性:信道建立后通过该信道发送的数据无法在不被检测到的情况下 被攻击者修改。¶
即使面对完全控制网络的攻击者,这些属性也应成立, 如 [RFC3552] 中所述。 有关相关安全属性的更完整说明, 请参阅附录 F。¶
TLS 由两个主要组件组成:¶
握手协议(第 4 节),用于验证通信方的身份、 协商密码算法和参数,并建立 共享密钥材料。握手协议旨在 抵御篡改;主动攻击者不应能够迫使 对等方协商出不同于连接未受攻击时 所会协商的参数。¶
记录协议(第 5 节),使用握手协议建立的参数 保护通信对等方之间的流量。 记录协议将流量划分为一系列 记录,每条记录均使用 流量密钥独立进行保护。¶
TLS 与应用协议无关;更高层协议可以 透明地构建于 TLS 之上。然而,TLS 标准并未 规定协议如何利用 TLS 增强安全性;如何 发起 TLS 握手以及如何解释所交换的身份验证 证书,均由运行于 TLS 之上的协议的设计者和 实现者自行决定。使用 TLS 的应用 协议必须规定 TLS 如何与其 应用协议配合,包括握手在何时以及如何 发生,以及如何进行身份验证。[RFC9525] 提供了有关将 TLS 与应用 协议集成的实用指南。¶
本文档定义 TLS 1.3 版。虽然 TLS 1.3 与 以前的版本不直接兼容,但所有 TLS 版本都包含 版本控制机制,使客户端和服务器在双方均支持某个版本时, 能够以互操作方式协商出共同版本。¶
本文档取代并废止以前的 TLS 版本, 包括 1.2 版 [RFC5246]、扩展 主秘密值 扩展 [RFC7627],以及 TLS 1.2 的椭圆曲线 定义 [RFC8422]。 本文档还废止 [RFC5077] 中定义的 TLS 票据机制,并以 第 2.2 节中定义的机制取代它。由于 TLS 1.3 改变了密钥派生方式,因此按照第 7.5 节所述更新 [RFC5705]; 导出器不再区分无上下文和空上下文。本文档还更改了 在线证书状态协议(OCSP)消息的传输方式,因此更新 [RFC6066] 并按照第 4.5.1.1 节所述废止 [RFC6961]。¶
本文档中的关键词“必须”、“不得 ”、“要求”、“须”、 “不得”、“应”、“不应”、“建议”、“不建议”、 “可以”和“可选”, 只有在如本文所示以全大写形式出现时, 才应按照 BCP 14 [RFC2119] [RFC8174] 中的说明进行解释。¶
使用以下术语:¶
TLS 1.3 最初在 [RFC8446] 中规定。本文档是对 TLS 1.3 的小幅更新,保留相同版本号并且 向后兼容。它收紧了一些要求,并在被发现表述不清的部分 更新了文本,同时进行了其他 编辑性改进。此外,它不再将术语 “master”用于秘密值,而是改用“main”,或在不需要术语时 使用更短的名称。本文档进行了以下 具体技术变更:¶
禁止在不同连接之间复用 KeyShare 值。¶
消除 PreSharedKeys 和 HelloRetryRequest 使用哪种哈希函数的歧义。¶
要求不支持恢复的客户端忽略 NewSessionTicket。¶
将超过密钥 使用限制前必须发起密钥更新的要求提升为必须,并明确密钥使用 限制的适用范围。¶
限制允许发送的 KeyUpdate 消息数量。¶
恢复将“close_notify”级别定义为“warning”的文本。¶
明确“user_canceled”相关行为,要求发送 “close_notify”,并且应 忽略“user_canceled”。¶
添加通用“general_error”警报。¶
将 CertificateRequest.extensions 的下限修正为 0 字节。原语法存在错误,因为 可以不发送任何扩展,此时 长度为 0。¶
修正 ClientHello.extensions 的下限。这 是一个计算错误。¶
修正 NewSessionTicket.extensions 的上限。 这是一个计算错误。¶
对非对称密钥交换使用更通用的表述, 其此前仅指(EC)DHE, 以反映基于 KEM 的密钥交换的使用。¶
明确消息记录哈希与 未来规范之间的交互。¶
此外,安全注意事项也进行了一些改进, 尤其是隐私方面。¶
以下列出了 TLS 1.2 与 TLS 1.3 之间的主要功能差异。 该列表并非穷尽无遗, 还存在许多细微差异。¶
支持的对称加密算法列表中已移除所有 被视为旧式的算法。其余算法均为带关联数据的认证加密 (AEAD)算法。密码套件的概念已更改,将身份验证和密钥交换机制 与记录保护算法(包括秘密密钥长度)以及同时用于 密钥派生函数和握手消息 身份验证代码(MAC)的哈希函数分离。¶
新增零往返时间(0-RTT)模式,对于 某些应用数据,可在建立连接时节省一次往返, 代价是牺牲某些安全属性。¶
已移除静态 RSA 和 Diffie-Hellman 密码套件; 现在所有基于公钥的密钥交换机制都提供前向保密性。¶
ServerHello 之后的所有握手消息现在均经过加密。 新引入的 EncryptedExtensions 消息使此前 在 ServerHello 中以明文发送的各种扩展也能获得 机密性保护。¶
密钥派生函数已重新设计。新设计凭借更强的密钥分离 属性,使密码学家能够更轻松地进行分析。 基于 HMAC 的提取并扩展密钥派生函数(HKDF) 被用作底层原语。¶
握手状态机经过大幅重构,使其 更加一致,并移除了 ChangeCipherSpec 等多余消息 (中间盒兼容性需要时除外)。¶
椭圆曲线算法现已纳入基础规范,并 加入了 EdDSA 等新的签名 算法。TLS 1.3 移除了点格式 协商,改为每条曲线仅使用一种点格式。¶
还进行了其他密码学改进,包括将 RSA 填充改为使用 RSA 概率签名方案(RSASSA-PSS),并移除 压缩、数字签名算法(DSA)以及 自定义临时 Diffie-Hellman(DHE)组。¶
TLS 1.2 的版本协商机制已被废弃,改为 在扩展中提供版本列表。这提高了与 错误实现版本协商的现有服务器的兼容性。¶
有服务器端状态和无服务器端状态的会话恢复,以及 早期 TLS 版本中基于 PSK 的密码套件,均由一种新的 PSK 交换机制取代。¶
参考文献已在适当情况下更新为指向新版 RFC(例如 RFC 5280,而不是 RFC 3280)。¶
本文档定义了若干可选择影响 TLS 1.2 实现的变更,包括那些并不同时 支持 TLS 1.3 的实现:¶
安全信道使用的密码参数由 TLS 握手协议生成。客户端和服务器 首次相互通信时使用 TLS 的这一子协议。 握手协议允许对等方协商协议版本、 选择密码算法、相互进行身份验证(客户端身份验证 可选),并建立共享秘密密钥材料。 握手完成后,对等方使用所建立的密钥 保护应用层流量。¶
握手失败或出现其他协议错误时,将触发 连接终止,终止前可以发送警报消息 (第 6 节)。¶
TLS 支持三种基本密钥交换模式:¶
在本文档中,非对称密钥交换包括基于有限域 或椭圆曲线的 Diffie-Hellman。 其他扩展定义了密钥封装机制(KEM)的使用。¶
客户端 服务器
密钥 ^ ClientHello
交换 | + key_share*
| + signature_algorithms*
| + psk_key_exchange_modes*
v + pre_shared_key* -------->
ServerHello ^ 密钥
+ key_share* | 交换
+ pre_shared_key* v
{EncryptedExtensions} ^ 服务器
{CertificateRequest*} v 参数
{Certificate*} ^
{CertificateVerify*} | 身份验证
{Finished} v
<-------- [应用数据*]
^ {Certificate*}
身份 | {CertificateVerify*}
验证 v {Finished} -------->
[应用数据] <-------> [应用数据]
+ 表示在前述消息中发送的
值得注意的扩展。
* 表示并非始终发送的可选或
依情况而定的消息/扩展。
{} 表示使用派生自
[sender]_handshake_traffic_secret 的密钥保护的消息。
[] 表示使用派生自
[sender]_application_traffic_secret_N 的密钥保护的消息。
可以将握手视为分为三个阶段(如 上图所示):¶
密钥交换:建立共享密钥材料并选择 密码参数。此阶段之后的所有内容均被 加密。¶
服务器参数:建立其他握手参数 (是否验证客户端身份、是否支持应用层协议等)。¶
身份验证:验证服务器(以及可选的客户端)的身份, 并提供密钥确认和握手完整性。¶
在密钥交换阶段,客户端发送 ClientHello (第 4.2.2 节)消息,其中包含随机 nonce (ClientHello.random);其提供的协议版本;对称 密码/哈希算法对列表;非对称密钥交换共享值列表(位于 “key_share”(第 4.3.8 节)扩展中)、 预共享密钥标签列表(位于 “pre_shared_key”(第 4.3.11 节) 扩展中),或二者兼有;以及 可能存在的其他扩展。为了中间盒兼容性, 还可能存在其他字段和/或消息。¶
服务器处理 ClientHello,并确定适合 该连接的密码参数。随后以其自己的 ServerHello(第 4.2.3 节)作出响应,该消息指明协商后的连接 参数。ClientHello 和 ServerHello 的组合决定共享密钥。如果使用 非对称密钥建立,则 ServerHello 包含一个“key_share”扩展,其中包含服务器的临时 非对称密钥交换共享值;服务器的共享值必须与 客户端的某个共享值属于同一组。 如果使用 PSK 密钥建立,则 ServerHello 包含 “pre_shared_key”扩展,用于指明选择了客户端提供的哪个 PSK。 请注意,实现可以同时使用非对称密钥交换和 PSK,在这种 情况下将同时提供两个扩展。¶
随后,服务器发送两条消息以建立服务器参数:¶
对不需要用于确定密码参数的 ClientHello 扩展的 响应,但特定于单个证书的扩展除外。 [第 4.4.1 节]¶
如果需要基于证书的客户端身份验证,则包含 对该证书的所需参数。如果不需要客户端身份验证, 则省略此消息。[第 4.4.2 节]¶
最后,客户端与服务器交换身份验证消息。每次需要基于证书的 身份验证时,TLS 都使用同一组消息。 (基于 PSK 的身份验证作为密钥交换的附带结果发生。) 具体而言:¶
端点的证书以及任何逐证书扩展。 如果服务器不使用证书进行身份验证,则服务器省略此消息; 如果服务器未发送 CertificateRequest(从而表明客户端不应 使用证书进行身份验证),则客户端也省略此消息。请注意,如果使用 原始公钥 [RFC7250] 或缓存 信息扩展 [RFC7924],则此消息不会 包含证书,而会包含与 服务器长期密钥对应的其他值。[第 4.5.1 节]¶
使用与 Certificate 消息中的公钥对应的私钥, 对整个握手进行签名。如果端点不通过 证书进行身份验证,则省略此消息。[第 4.5.2 节]¶
针对整个握手的 MAC(消息身份验证码)。 此消息为握手中建立的共享秘密值提供密钥确认, 将端点的身份与所交换的密钥绑定,并且在 PSK 模式下 还会验证握手。[第 4.5.3 节]¶
收到服务器的消息后,客户端以自己的身份验证 消息作出响应,即 Certificate 和 CertificateVerify(若被请求)以及 Finished。¶
至此,握手完成,客户端与服务器 派生记录层交换通过认证加密保护的 应用层数据所需的密钥材料。 除第 2.3 节规定的情况外, 在发送 Finished 消息之前不得发送应用数据。 请注意,虽然服务器可以在收到客户端的身份验证消息之前 发送应用数据,但此时发送的任何数据 显然都被发送给未经身份验证的对等方。¶
尽管 TLS PSK 可以在外部建立, PSK 也可以在先前连接中建立, 随后用于建立新连接(使用 PSK 进行“会话恢复”或“恢复”)。 握手完成后,服务器可以 向客户端发送与从初始握手派生的唯一密钥相对应的 PSK 身份 (参见第 4.7.1 节)。 随后,客户端 可以在未来的握手中使用该 PSK 身份,协商使用 关联的 PSK。如果服务器接受该 PSK,则 新连接的安全上下文在密码学上与原始连接绑定,并使用从 初始握手派生的密钥引导密码状态, 而不进行完整握手。 在 TLS 1.2 及更早版本中,此功能由“会话 ID”和 “会话票据”[RFC5077]提供。这两种 机制均已在 TLS 1.3 中废止。¶
PSK 可以与非对称密钥交换结合使用,从而在使用共享密钥的同时 提供前向保密性;也可以单独使用, 代价是应用数据失去前向保密性。¶
图 3展示了一对握手,其中第一次握手建立 PSK,第二次握手使用它:¶
客户端 服务器
初始握手:
ClientHello
+ key_share -------->
ServerHello
+ key_share
{EncryptedExtensions}
{CertificateRequest*}
{Certificate*}
{CertificateVerify*}
{Finished}
<-------- [应用数据*]
{Certificate*}
{CertificateVerify*}
{Finished} -------->
<-------- [NewSessionTicket]
[应用数据] <-------> [应用数据]
后续握手:
ClientHello
+ key_share*
+ psk_key_exchange_modes
+ pre_shared_key -------->
ServerHello
+ pre_shared_key
+ key_share*
{EncryptedExtensions}
{Finished}
<-------- [应用数据*]
{Finished} -------->
[应用数据] <-------> [应用数据]
由于服务器通过 PSK 进行身份验证,因此不会发送 Certificate 或 CertificateVerify 消息。当客户端通过 PSK 提供恢复时, 它应同时向服务器提供“key_share”扩展, 使服务器能够在需要时拒绝恢复并回退 到完整握手。服务器以“pre_shared_key” 扩展作出响应,以协商使用 PSK 密钥建立,并且可以(如图所示) 以“key_share”扩展作出响应,执行非对称密钥建立, 从而提供前向保密性。¶
在外部配置 PSK 时,还必须配置 PSK 身份以及 要与 PSK 一起使用的 KDF 哈希算法。¶
注意:使用外部配置的预共享秘密值时,一项关键 考虑是在生成密钥时使用足够的熵, 如 [RFC4086] 中所述。从密码或其他 低熵来源派生共享秘密值并不安全。低熵秘密值或密码 容易受到基于 PSK 绑定器的字典攻击。即使与 非对称密钥建立结合使用,规定的 PSK 身份验证也不是一种强健的基于密码的认证密钥交换。具体而言, 它无法阻止能够观察握手的攻击者 对密码/预共享密钥实施暴力破解攻击。¶
当客户端与服务器共享 PSK(从外部获取或 通过先前握手获取)时,TLS 1.3 允许客户端在 第一个消息批次中发送数据(“早期数据”)。客户端使用 PSK 验证 服务器的身份并加密早期数据。¶
如图 4所示,0-RTT 数据只是在第一个消息批次中添加到 1-RTT 握手中。握手的其余部分使用与采用 PSK 恢复的 1-RTT 握手相同的消息。¶
客户端 服务器
ClientHello
+ early_data
+ key_share*
+ psk_key_exchange_modes
+ pre_shared_key
(应用数据*) -------->
ServerHello
+ pre_shared_key
+ key_share*
{EncryptedExtensions}
+ early_data*
{Finished}
<-------- [应用数据*]
(EndOfEarlyData*)
{Finished} -------->
[应用数据] <-------> [应用数据]
+ 表示在前述消息中发送的
值得注意的扩展。
* 表示并非始终发送的可选或
依情况而定的消息/扩展。
() 表示使用派生自
client_early_traffic_secret 的密钥保护的消息。
{} 表示使用派生自
[sender]_handshake_traffic_secret 的密钥保护的消息。
[] 表示使用派生自
[sender]_application_traffic_secret_N 的密钥保护的消息。
重要说明:0-RTT 数据的安全属性弱于 其他类型的 TLS 数据。具体而言:¶
协议不为此类数据提供任何前向保密性保证。 服务器的行为决定适用何种前向保密性保证(如果有) (参见第 8.1 节)。此 行为不会作为协议的一部分告知客户端。 因此,在缺乏有关服务器行为的带外信息时, 客户端应假定此类数据不具备前向保密性。¶
不同连接之间不保证不可重放。 普通 TLS 1.3 1-RTT 数据的重放保护 由服务器的 Random 值提供,但 0-RTT 数据并不依赖 ServerHello,因此其保证较弱。如果数据通过 TLS 客户端 身份验证或在应用协议内部进行身份验证, 这一点尤其重要。同样的警告 适用于 early_exporter_secret 的任何使用。¶
0-RTT 数据无法在同一连接内重复(即服务器不会 在同一连接中处理两次相同数据),攻击者也无法 使 0-RTT 数据看起来像 1-RTT 数据 (因为它们使用不同的密钥进行保护)。附录 F.5 描述了潜在攻击,而第 8 节 描述了服务器可用于限制重放影响的机制。¶
本文档处理外部表示形式中的数据格式。 将使用以下非常基础且定义得较为宽松的表示语法。¶
在以下定义中,此语法的可选组成部分通过将其括在 “[[ ]]”(双方括号)中来表示。¶
所有数据项的表示形式均被明确规定。基本数据 块大小为一个字节(即 8 位)。多字节数据项是 从左到右、从上到下拼接的字节序列。从字节 流中,按以下方式(使用 C 记法)构成多字节项(以下示例中为数值):¶
value = (byte[0] << 8*(n-1)) | (byte[1] << 8*(n-2)) |
... | byte[n-1];
¶
多字节值的这种字节顺序就是常见的网络字节序, 即大端格式。¶
基本数值数据类型是无符号字节(uint8)。所有更大的数值 数据类型均由固定长度的字节序列构成,并按照 第 3.1 节所述方式拼接,且同样 为无符号类型。预定义了以下数值 类型。¶
uint8 uint16[2]; uint8 uint24[3]; uint8 uint32[4]; uint8 uint64[8];¶
此处以及规范其他部分中的所有值均按网络字节 (大端)顺序传输;由十六进制字节 01 02 03 04 表示的 uint32 等同于十进制值 16909060。¶
向量(一维数组)是同构数据元素组成的流。 出于表示目的,本规范将向量称为列表。 向量的大小可以在编写文档时指定,也可以留到 运行时再确定。无论哪种情况,长度声明的都是向量中的 字节数,而不是元素数。定义类型 T 的固定长度向量 新类型 T' 的语法为¶
T T'[n];¶
此处,T' 在数据流中占据 n 个字节,其中 n 是 T 大小的整数倍。编码后的流中不包含向量长度。¶
在以下示例中,Datum 被定义为协议不解释的 三个连续字节,而 Data 是三个连续的 Datum, 总共占用九个字节。¶
opaque Datum[3]; /* 三个未经解释的字节 */ Datum Data[9]; /* 三个连续的 3 字节向量 */¶
变长向量通过使用 <floor..ceiling> 记法指定合法长度的 闭合子范围来定义。编码时, 实际长度在字节流中位于向量内容之前。长度 以数值形式表示,占用足以容纳向量指定的 最大长度(ceiling)所需的字节数。实际长度字段为 零的变长向量称为空向量。¶
T T'<floor..ceiling>;¶
在以下示例中,“mandatory”是一个必须包含 300 到 400 个 opaque 类型字节的向量。它永远不能为空。实际长度字段 占用两个字节,即 uint16,足以表示值 400 (参见第 3.3 节)。类似地,“longer”最多可以表示 800 字节的 数据,即 400 个 uint16 元素,并且它可以为空。其编码将在 向量前添加一个两字节的实际长度字段。编码后向量的长度 必须恰好是单个元素长度的整数倍(例如, 17 字节的 uint16 向量是非法的)。¶
opaque mandatory<300..400>;
/* 长度字段为两个字节,不能为空 */
uint16 longer<0..800>;
/* 零到 400 个 16 位无符号整数 */
¶
还提供一种额外的稀疏数据类型,称为“enum”或 “enumerated”。每个定义都是不同的类型。只有相同类型的枚举值 才能相互赋值或比较。枚举中的每个元素 都必须赋予一个值,如以下 示例所示。由于枚举元素无序,因此可以 按任意顺序为其分配任意唯一值。¶
enum { e1(v1), e2(v2), ... , en(vn) [[, (n)]] } Te;
¶
协议的未来扩展或补充可以定义新值。 除非字段定义另有规定,否则实现需要能够解析并忽略未知值。¶
枚举在字节流中占用的空间等同于其最大 已定义序数值所需的空间。以下定义将使用一个字节 承载 Color 类型的字段。¶
enum { red(3), blue(5), white(7) } Color;
¶
可以选择指定一个没有关联标签的值,以便在不定义多余元素的情况下 强制规定宽度。¶
在以下示例中,Taste 将在数据流中占用两个字节, 但在当前协议版本中只能取值 1、2 或 4。¶
enum { sweet(1), sour(2), bitter(4), (32000) } Taste;
¶
枚举元素的名称作用域位于所定义的类型内。 在第一个示例中,对枚举第二个元素的完全限定引用 应为 Color.blue。如果赋值目标已明确指定, 则不需要这种限定。¶
Color color = Color.blue; /* 过度指定,合法 */ Color color = blue; /* 正确,类型隐含 */¶
分配给枚举值的名称不需要唯一。数值 可以描述同一名称适用的范围。该值包含 范围内的最小值和最大值,并以两个句点 字符分隔。这主要用于预留值空间中的区域。¶
enum { sad(0), meh(1..254), happy(255) } Mood;
¶
为方便起见,可以使用基本类型构造结构类型。每个 声明都会定义一个新的唯一类型。定义所使用的语法 与 C 的语法非常相似。¶
struct {
T1 f1;
T2 f2;
...
Tn fn;
} T;
¶
可以使用标准列表 语法定义定长和变长列表(向量)字段。变体示例(第 3.8 节)中的结构 V1 和 V2 展示了这一点。¶
结构中的字段可以使用类型名称进行限定, 其语法与枚举可用的语法非常相似。例如,T.f2 指 前述声明中的第二个字段。¶
已定义的结构可以根据环境中可用的某些信息 具有不同变体。选择器必须是一个枚举 类型,用于定义该结构可能具有的变体。select 的每个 分支(见下文)都规定该变体字段的类型以及 可选字段标签。表示语言并未规定 在运行时选择变体的机制。¶
struct {
T1 f1;
T2 f2;
....
Tn fn;
select (E) {
case e1: Te1 [[fe1]];
case e2: Te2 [[fe2]];
....
case en: Ten [[fen]];
};
} Tv;
¶
例如:¶
enum { apple(0), orange(1) } VariantTag;
struct {
uint16 number;
opaque string<0..10>; /* 可变长度 */
} V1;
struct {
uint32 number;
opaque string[10]; /* 固定长度 */
} V2;
struct {
VariantTag type;
select (VariantRecord.type) {
case apple: V1;
case orange: V2;
};
} VariantRecord;
¶
握手协议用于协商连接的安全参数。 握手消息被提供给 TLS 记录层,并在其中封装于一个或多个 TLSPlaintext 或 TLSCiphertext 结构内,这些结构按照当前活动连接状态的规定 进行处理和传输。¶
enum {
client_hello(1),
server_hello(2),
new_session_ticket(4),
end_of_early_data(5),
encrypted_extensions(8),
certificate(11),
certificate_request(13),
certificate_verify(15),
finished(20),
key_update(24),
message_hash(254),
(255)
} HandshakeType;
struct {
HandshakeType msg_type; /* 握手类型 */
uint24 length; /* 消息中的剩余字节 */
select (Handshake.msg_type) {
case client_hello: ClientHello;
case server_hello: ServerHello;
case end_of_early_data: EndOfEarlyData;
case encrypted_extensions: EncryptedExtensions;
case certificate_request: CertificateRequest;
case certificate: Certificate;
case certificate_verify: CertificateVerify;
case finished: Finished;
case new_session_ticket: NewSessionTicket;
case key_update: KeyUpdate;
};
} Handshake;
¶
除非由 TLS 扩展修改,否则协议消息必须按照 第 4.1 节中定义并在 第 2 节图示中展示的顺序发送。 收到顺序不符合预期的握手消息的对等方 必须使用“unexpected_message”警报中止握手。¶
新的握手消息类型由 IANA 按照 第 11 节所述方式分配。¶
TLS 中的许多密码学计算都使用消息记录 哈希。此值通过对每个所包含握手消息的拼接结果进行哈希来计算, 其中包括承载握手消息类型和长度字段的握手 消息标头,但不包括记录层标头。即:¶
Transcript-Hash(M1, M2, ... Mn) = Hash(M1 || M2 || ... || Mn)¶
作为这项一般规则的例外,当服务器以 HelloRetryRequest 响应 ClientHello 时,ClientHello1 的值会 替换为一个握手类型为“message_hash”(254)的特殊合成握手消息, 其中包含 Hash(ClientHello1)。即:¶
Transcript-Hash(ClientHello1, HelloRetryRequest, ... Mn) =
Hash(message_hash || /* 握手类型 */
00 00 Hash.length || /* 握手消息长度(字节) */
Hash(ClientHello1) || /* ClientHello1 的哈希 */
HelloRetryRequest || ... || Mn)
¶
这样构造是为了使服务器能够执行 无状态 HelloRetryRequest,只需在 cookie 中存储 ClientHello1 的哈希, 而不必导出整个中间 哈希状态(参见第 4.3.2 节)。¶
当使用“...”指定一系列消息时,消息记录哈希 取自主握手期间发送或接收的握手消息序列, 从指定的起始消息开始,到指定的结束消息为止。 在本文档中,它取自以下握手 消息序列(其中一些可能被省略):ClientHello、HelloRetryRequest、ClientHello、 ServerHello、 EncryptedExtensions、服务器 CertificateRequest、服务器 Certificate、 服务器 CertificateVerify、服务器 Finished、EndOfEarlyData、客户端 Certificate、客户端 CertificateVerify 和客户端 Finished。TLS 扩展可以 添加或移除消息,在这种情况下,消息记录哈希将反映 修改后的序列。¶
一般而言,一旦在 ServerHello 中选定了哈希函数, 实现便可以根据协商的哈希函数维护一个 持续更新的消息记录哈希值来实现消息记录。¶
但请注意,后续的握手后身份验证彼此之间并不 相互包含,只包含截至主 握手结束时的消息。¶
密钥交换消息用于确定客户端和服务器的安全能力, 并建立共享秘密值,其中包括 用于保护其余握手过程和数据的流量密钥。¶
在 TLS 中,密码学协商由客户端在其 ClientHello 中 提供以下四组选项来进行:¶
密码套件列表,用于指明客户端支持的 AEAD 算法/HKDF 哈希 组合。¶
“supported_groups”(第 4.3.7 节)扩展,用于指明客户端支持的 密钥交换组;以及“key_share”(第 4.3.8 节)扩展,其中包含 这些组中部分或全部组的密钥交换共享值。术语 “groups”具有历史原因,源自 TLS 1.3 的原始版本, 该版本不支持非 DHE 密钥建立算法)。¶
“signature_algorithms”(第 4.3.3 节) 扩展,用于指明客户端可以接受的签名 算法。还可以添加“signature_algorithms_cert”扩展 (第 4.3.3 节), 用于指明证书专用的签名算法。¶
“pre_shared_key”(第 4.3.11 节)扩展, 其中包含客户端已知的对称密钥身份列表;以及 “psk_key_exchange_modes”(第 4.3.9 节) 扩展,用于指明可以与 PSK 一起使用的密钥交换模式。¶
如果服务器未选择 PSK,则前三组选项 完全相互独立:服务器独立选择 密码套件、用于密钥建立的非对称密钥交换组和密钥共享值, 以及用于向客户端验证自身身份的签名算法/证书组合。 如果收到的“supported_groups”与服务器支持的组之间不存在交集, 则服务器必须使用 “handshake_failure”或“insufficient_security”警报中止握手。¶
如果服务器选择 PSK,则还必须从客户端 “psk_key_exchange_modes”扩展所指明的列表中选择一种密钥 建立模式(当前为仅使用 PSK,或 PSK 与非对称密钥 交换结合)。请注意,如果 PSK 可以在不使用非对称密钥交换的情况下使用, 则“supported_groups”参数之间不存在交集不一定是致命错误, 不同于上一段讨论的非 PSK 情况。¶
如果服务器选择了非对称密钥交换组,而客户端在初始 ClientHello 中未提供兼容的“key_share”扩展,则服务器必须 使用 HelloRetryRequest(第 4.2.4 节)消息作出响应。¶
如果服务器成功选择参数且不需要 HelloRetryRequest,则按以下方式在 ServerHello 中指明所选参数:¶
如果使用 PSK,则服务器将发送 “pre_shared_key”扩展,以指明所选密钥。¶
使用非对称密钥交换时,服务器还将 提供“key_share”扩展。如果未使用 PSK,则始终使用 非对称密钥交换和基于证书的 身份验证。¶
通过证书进行身份验证时,服务器将发送 Certificate(第 4.5.1 节)和 CertificateVerify (第 4.5.2 节) 消息。在本文档定义的 TLS 1.3 中, 始终使用 PSK 或证书中的一种,但不会同时使用二者。 未来文档可以定义如何将二者结合使用。¶
如果服务器无法协商出受支持的一组参数 (即客户端与服务器参数之间不存在交集), 则必须使用 “handshake_failure”或“insufficient_security”致命警报 中止握手(参见第 6 节)。¶
客户端首次连接服务器时,必须将 ClientHello 作为其第一条 TLS 消息发送。当服务器以 HelloRetryRequest 响应其 ClientHello 时,客户端也会发送 ClientHello。在这种情况下,除以下情况外,客户端必须发送 相同且未经修改的 ClientHello:¶
如果 HelloRetryRequest 中提供了“key_share”扩展, 则将共享值列表替换为只包含来自所指明组的一个 KeyShareEntry 的列表。¶
如果存在“early_data”扩展(第 4.3.10 节), 则将其移除。HelloRetryRequest 之后不允许发送早期数据。¶
如果 HelloRetryRequest 中提供了 “cookie”扩展,则将其包含在内。¶
如果存在“pre_shared_key”扩展,则通过 重新计算“obfuscated_ticket_age”和绑定器值来更新它, 并且可以选择移除 与服务器所指明密码套件不兼容的任何 PSK。¶
未来定义且出现在 HelloRetryRequest 中的扩展 可能允许的其他修改。¶
由于 TLS 1.3 禁止重新协商,如果服务器已协商 TLS 1.3,并在任何其他时刻收到 ClientHello,则必须使用 “unexpected_message”警报终止连接。¶
如果服务器使用早期 TLS 版本建立了 TLS 连接, 并在重新协商期间收到 TLS 1.3 ClientHello,则必须保留 先前的协议版本。特别是,不得 协商 TLS 1.3。¶
此消息的结构:¶
uint16 ProtocolVersion;
opaque Random[32];
uint8 CipherSuite[2]; /* 密码套件选择器 */
struct {
ProtocolVersion legacy_version = 0x0303; /* TLS v1.2 */
Random random;
opaque legacy_session_id<0..32>;
CipherSuite cipher_suites<2..2^16-2>;
opaque legacy_compression_methods<1..2^8-1>;
Extension extensions<7..2^16-1>;
} ClientHello;
¶
在早期 TLS 版本中,此字段用于版本协商, 表示客户端支持的最高版本号。 实践表明,许多服务器未正确实现 版本协商,从而产生“版本不容忍”问题,即 服务器会拒绝本来可以接受、但版本号 高于其支持范围的 ClientHello。 在 TLS 1.3 中,客户端通过 “supported_versions”扩展(第 4.3.1 节)指明其版本偏好, legacy_version 字段必须 设置为 0x0303,即 TLS 1.2 的版本号。 TLS 1.3 ClientHello 的识别条件是 legacy_version 为 0x0303,并且存在 supported_versions 扩展, 其中所指明的最高版本为 0x0304。 (有关向后兼容性的详细信息,请参阅附录 E。) 收到不等于 0x0303 的 legacy_version 值的服务器必须使用 “protocol_version”警报中止握手。¶
TLS 1.3 之前的 TLS 版本支持“会话 恢复”功能,该功能在本版本中已与预共享密钥合并 (参见第 2.2 节)。 已缓存由 TLS 1.3 之前的服务器设置的会话 ID 的客户端 应将此字段设置为该值。在 兼容模式下(参见附录 E.4),此字段必须为非空, 因此未提供 TLS 1.3 之前会话的客户端必须生成 一个新的 32 字节值。该值不必随机,但应当 不可预测,以避免实现固定依赖某个特定值 (也称为僵化)。 否则,它必须设置为零长度列表 (即值为零的单字节长度字段)。¶
客户端支持的对称密码选项列表,具体包括 记录保护算法(包括秘密密钥长度)以及与 HKDF 一起使用的哈希函数, 按客户端偏好从高到低排列。相关值定义于附录 B.4。 如果列表中包含服务器无法识别、不支持或不希望使用的密码套件, 服务器必须忽略这些密码套件,并照常处理 其余密码套件。如果客户端 尝试进行 PSK 密钥建立,则应至少公布一个 指明与该 PSK 关联的哈希函数的密码套件。¶
TLS 1.3 之前的版本支持压缩, 所支持压缩方法的列表通过此字段发送。对于每个 TLS 1.3 ClientHello,此列表必须恰好包含一个字节, 并将其设置为零,对应早期 TLS 版本中的“null”压缩方法。 如果收到此字段具有任何其他值的 TLS 1.3 ClientHello, 服务器必须 使用“illegal_parameter”警报中止握手。请注意,TLS 1.3 服务器可能会收到包含其他压缩方法的 TLS 1.2 或更早版本 ClientHello, 并且如果协商此类早期版本,必须遵循 相应早期 TLS 版本的流程。¶
客户端通过在 extensions 字段中发送数据, 向服务器请求扩展功能。实际的“Extension”格式 定义于第 4.3 节。在 TLS 1.3 中,某些扩展是强制使用的,因为相关功能已移入 扩展,以保持 ClientHello 与早期 TLS 版本的兼容性。 服务器必须忽略无法识别的扩展。¶
所有 TLS 版本都允许在 compression_methods 字段之后选择性地跟随一个 extensions 字段。TLS 1.3 ClientHello 消息始终包含扩展(至少包含“supported_versions”,否则 它们将被解释为 TLS 1.2 ClientHello 消息)。 但是,TLS 1.3 服务器可能会收到来自早期 TLS 版本、 不含 extensions 字段的 ClientHello 消息。 可以通过判断 ClientHello 末尾的 compression_methods 字段之后是否还有字节,来检测是否存在扩展。 请注意,这种检测可选数据的方法与 TLS 通常使用变长字段的方法不同,但 为了与定义扩展之前的 TLS 保持兼容而采用此方法。 TLS 1.3 服务器需要先执行此项检查,并且仅在 存在“supported_versions”扩展时才尝试协商 TLS 1.3。 如果协商早于 1.3 的 TLS 版本,服务器必须检查 消息是否在 legacy_compression_methods 之后不含任何数据, 或者包含一个有效的扩展块且其后不再有数据。 否则,服务器必须使用 “decode_error”警报中止握手。¶
如果客户端使用扩展请求附加功能, 而服务器未提供该功能,则客户端可以中止握手。¶
发送 ClientHello 消息后,客户端等待 ServerHello 或 HelloRetryRequest 消息。如果正在使用早期数据, 客户端可以在等待下一条握手消息期间传输早期应用数据 (第 2.3 节)。¶
如果服务器能够根据 ClientHello 协商出一组可接受的 握手参数,则会发送此消息作为对 ClientHello 消息的响应,以继续进行握手。¶
此消息的结构:¶
struct {
ProtocolVersion legacy_version = 0x0303; /* TLS v1.2 */
Random random;
opaque legacy_session_id_echo<0..32>;
CipherSuite cipher_suite;
uint8 legacy_compression_method = 0;
Extension extensions<6..2^16-1>;
} ServerHello;
¶
在早期 TLS 版本中,此字段用于版本协商, 表示为连接选择的版本号。遗憾的是, 某些中间盒在遇到新值时会失败。 在 TLS 1.3 中,TLS 服务器通过 “supported_versions”扩展(第 4.3.1 节)指明其版本, legacy_version 字段必须 设置为 0x0303,即 TLS 1.2 的版本号。 (有关向后兼容性的详细信息,请参阅附录 E。) 收到 legacy_version 值不等于 0x0303 的 TLS 1.3 Server Hello 的客户端必须 使用“protocol_version”警报中止握手。¶
由安全随机数生成器生成的 32 字节。 有关更多信息,请参阅附录 C。 如果协商 TLS 1.2 或 TLS 1.1,最后 8 个字节必须按下文所述方式覆盖, 但其余字节必须为随机值。 此结构由服务器生成,并且必须独立于 ClientHello.random 生成。¶
客户端 legacy_session_id 字段的内容。 请注意,即使客户端的值对应于服务器选择不恢复的、 已缓存的 TLS 1.3 之前会话,也会回显此字段。 收到与其在 ClientHello 中发送的值不匹配的 legacy_session_id_echo 字段的客户端 必须使用“illegal_parameter”警报中止握手。¶
服务器从 ClientHello.cipher_suites 列表中选择的单个密码套件。收到未曾提供的密码套件的客户端 必须使用 “illegal_parameter”警报中止握手。¶
一个必须 取值为 0 的单字节。如果收到此字段具有任何其他值的 TLS 1.3 ServerHello,客户端必须 使用“illegal_parameter”警报中止握手。¶
扩展列表。ServerHello必须只包含 建立密码学上下文和协商协议版本所需的扩展。 所有 TLS 1.3 ServerHello 消息必须包含 “supported_versions”扩展。当前的 ServerHello 消息还会包含 “pre_shared_key”扩展或“key_share”扩展,或者同时包含二者 (将 PSK 与非对称密钥建立结合使用时)。其他扩展 (参见第 4.3 节) 在 EncryptedExtensions 消息中单独发送。¶
出于与中间盒向后兼容的原因 (参见附录 E.4), HelloRetryRequest 消息使用与 ServerHello 相同的结构,但 Random 被设置为字符串 “HelloRetryRequest”的 SHA-256 特殊值:¶
CF 21 AD 74 E5 9A 61 11 BE 1D 8C 02 1E 65 B8 91 C2 A2 11 16 7A BB 8C 5E 07 9E 09 E2 C8 A8 33 9C¶
收到类型为 server_hello 的消息时,实现 必须先检查 Random 值,如果它与 此值匹配,则按照第 4.2.4 节所述方式处理。¶
TLS 1.3 在服务器的 random 值中嵌入了降级保护机制。响应 ClientHello 时协商 TLS 1.2 或更低版本的 TLS 1.3 服务器必须在其 ServerHello 中以特殊方式设置 Random 值的最后 8 个字节。¶
如果协商 TLS 1.2,TLS 1.3 服务器必须将其 Random 值的最后 8 个字节设置为:¶
44 4F 57 4E 47 52 44 01¶
[RFC8996] 和 附录 E.5 禁止协商低于 TLS 1.2 的版本。但是,不遵循该指南的服务器 实现必须 将其 ServerHello.random 值的最后 8 个字节设置为:¶
44 4F 57 4E 47 52 44 00¶
收到指明 TLS 1.2 或更低版本的 ServerHello 的 TLS 1.3 客户端必须检查最后 8 个字节不等于上述任一值。 如果 ServerHello 指明 TLS 1.1 或更低版本, TLS 1.2 客户端也应检查最后 8 个字节 不等于第二个值。如果发现匹配,客户端必须使用 “illegal_parameter”警报中止握手。除 Finished 交换提供的保护外, 此机制还对降级攻击提供有限保护:由于 TLS 1.2 及更低版本中存在的 ServerKeyExchange 消息 包含对两个随机值的签名,只要使用临时密码, 主动攻击者便无法在不被检测到的情况下修改 随机值。 使用静态 RSA 时,此机制不提供降级保护。¶
注意:这是相对于 [RFC5246] 的一项更改,因此实践中许多 TLS 1.2 客户端 和服务器不会按上述规定运行。¶
使用 TLS 1.2 或更早版本执行重新协商的旧式 TLS 客户端, 如果在重新协商期间收到 TLS 1.3 ServerHello, 必须使用“protocol_version”警报中止握手。 请注意,协商 TLS 1.3 后无法进行重新协商。¶
如果服务器能够找到一组可接受的参数, 但 ClientHello 未包含继续握手所需的足够信息, 则服务器会发送此消息作为对 ClientHello 消息的响应。如第 4.2.3 节所述,HelloRetryRequest 与 ServerHello 消息具有相同格式,并且 legacy_version、legacy_session_id_echo、cipher_suite 和 legacy_compression_method 字段具有相同含义。不过,为方便起见,本文档始终将 “HelloRetryRequest”作为一条独立消息讨论。¶
服务器的扩展必须包含 “supported_versions”。 此外,还应包含客户端生成正确 ClientHello 对所需的 最小扩展集合。除可选的“cookie” (参见第 4.3.2 节)扩展外,HelloRetryRequest不得包含 客户端未在其 ClientHello 中首先提供的任何扩展。¶
收到 HelloRetryRequest 后,客户端必须按照第 4.2.3 节的规定检查 legacy_version、legacy_session_id_echo、cipher_suite 和 legacy_compression_method,然后处理扩展, 首先使用“supported_versions”确定版本。 如果 HelloRetryRequest 不会导致 ClientHello 发生任何变化, 客户端必须使用“illegal_parameter”警报中止握手。 如果客户端在同一连接中收到第二个 HelloRetryRequest (即 ClientHello 本身就是对 HelloRetryRequest 的响应), 则必须使用“unexpected_message”警报中止握手。¶
否则,客户端必须处理 HelloRetryRequest 中的所有扩展,并发送第二个经过更新的 ClientHello。 本规范定义的 HelloRetryRequest 扩展如下:¶
收到未曾提供的密码套件的客户端必须中止握手。 服务器必须确保在收到符合要求的更新后 ClientHello 时, 协商相同的密码套件(如果服务器将选择密码套件作为协商的第一步, 则会自动满足此要求)。收到 ServerHello 后, 客户端必须检查 ServerHello 中提供的密码套件 与 HelloRetryRequest 中的相同,否则使用 “illegal_parameter”警报中止握手。¶
此外,在更新后的 ClientHello 中,客户端不应提供 与所选密码套件所使用哈希函数不同的任何预共享密钥。 这使客户端无需在第二个 ClientHello 中 为多个哈希函数计算部分消息记录哈希。¶
HelloRetryRequest 的“supported_versions” 扩展中的 selected_version 值必须在 ServerHello 中保持不变; 如果该值发生变化,客户端必须使用 “illegal_parameter”警报中止握手。¶
许多 TLS 消息包含采用“标记-长度-值”编码的扩展 结构。¶
struct {
ExtensionType extension_type;
opaque extension_data<0..2^16-1>;
} Extension;
enum {
server_name(0), /* RFC 6066, 9261 */
status_request(5), /* RFC 6066, 9846 */
supported_groups(10), /* RFC 7919, 9846 */
signature_algorithms(13), /* RFC 9846 */
use_srtp(14), /* RFC 5764 */
heartbeat(15), /* RFC 6520 */
application_layer_protocol_negotiation(16), /* RFC 7301 */
client_certificate_type(19), /* RFC 7250 */
server_certificate_type(20), /* RFC 7250 */
padding(21), /* RFC 7685 */
compress_certificate(27), /* RFC 8879 */
record_size_limit(28), /* RFC 8449 */
delegated_credential(34), /* RFC 9345 */
supported_ekt_ciphers(39), /* RFC 8870 */
pre_shared_key(41), /* RFC 9846 */
early_data(42), /* RFC 9846 */
supported_versions(43), /* RFC 9846 */
cookie(44), /* RFC 9846 */
psk_key_exchange_modes(45), /* RFC 9846 */
certificate_authorities(47), /* RFC 9846 */
oid_filters(48), /* RFC 9846 */
post_handshake_auth(49), /* RFC 9846 */
signature_algorithms_cert(50), /* RFC 9846 */
key_share(51), /* RFC 9846 */
transparency_info(52), /* RFC 9162 */
external_id_hash(55), /* RFC 8844 */
external_session_id(56), /* RFC 8844 */
quic_transport_parameters(57), /* RFC 9001 */
ticket_request(58), /* RFC 9149 */
ech_outer_extensions(64768), /* RFC 9849 */
encrypted_client_hello(65037), /* RFC 9849 */
(65535)
} ExtensionType;
¶
注意:此列表仅包含本文撰写时标记为 “建议”且允许用于 TLS 1.3 的扩展。¶
其中:¶
“extension_data”字段的内容通常由使用 TLS 表示语言定义的 扩展专用结构规定。除非另有规定,否则禁止尾随数据。 也就是说,发送方不得 在“extension_data”字段中的结构之后包含数据。 处理扩展时,如果解析结构后仍有剩余数据, 接收方必须使用“decode_error”警报中止握手。 如果接收方未实现某个扩展,或被配置为忽略该扩展, 则此要求不适用。¶
扩展类型列表由 IANA 按照 第 11 节所述方式维护。¶
扩展通常采用请求/响应形式, 但某些扩展只有请求而没有对应的 响应(即指示)。客户端在 ClientHello 消息中发送扩展请求, 服务器则在 ServerHello、EncryptedExtensions、HelloRetryRequest 和 Certificate 消息中发送扩展响应。服务器在 CertificateRequest 消息中发送扩展请求,客户端可以在 Certificate 消息中作出响应。服务器还可以在 NewSessionTicket 中主动发送扩展,但客户端不会直接 对其作出响应。¶
如果远程端点未发送对应的扩展请求,实现不得 发送扩展响应(即在 ServerHello、EncryptedExtensions、HelloRetryRequest 和 Certificate 消息中), 但 HelloRetryRequest 中的“cookie”扩展除外。 收到此类扩展时,端点必须使用 “unsupported_extension”警报中止握手。¶
下表指明给定扩展可以出现在哪些消息中, 使用以下记法:CH(ClientHello)、SH (ServerHello)、EE(EncryptedExtensions)、CT(Certificate)、CR (CertificateRequest)、NST(NewSessionTicket)和 HRR (HelloRetryRequest)。如果实现收到它能够识别、但并未规定可用于当前消息的扩展, 则必须使用“illegal_parameter”警报中止握手。¶
| 扩展 | TLS 1.3 |
|---|---|
| server_name [RFC6066] [RFC9261] | CH, EE, CR |
| status_request [RFC6066] [RFC9846] | CH, CR, CT |
| supported_groups [RFC7919] [RFC9846] | CH, EE |
| signature_algorithms [RFC9846] | CH, CR |
| use_srtp [RFC5764] | CH, EE |
| heartbeat [RFC6520] | CH, EE |
| application_layer_protocol_negotiation [RFC7301] | CH, EE |
| client_certificate_type [RFC7250] | CH, EE |
| server_certificate_type [RFC7250] | CH, EE |
| padding [RFC7685] | CH |
| compress_certificate [RFC8879] | CH, CR |
| record_size_limit [RFC8449] | CH, EE |
| delegated_credential [RFC9345] | CH, CR, CT |
| supported_ekt_ciphers [RFC8870] | CH, EE |
| pre_shared_key [RFC9846] | CH, SH |
| early_data [RFC9846] | CH, EE, NST |
| supported_versions [RFC9846] | CH, SH, HRR |
| cookie [RFC9846] | CH, HRR |
| psk_key_exchange_modes [RFC9846] | CH |
| certificate_authorities [RFC9846] | CH, CR |
| oid_filters [RFC9846] | CR |
| post_handshake_auth [RFC9846] | CH |
| signature_algorithms_cert [RFC9846] | CH, CR |
| key_share [RFC9846] | CH, SH, HRR |
| transparency_info [RFC9162] | CH, CR, CT |
| external_id_hash [RFC8844] | CH, EE |
| external_session_id [RFC8844] | CH, EE |
| quic_transport_parameters [RFC9001] | CH, EE |
| ticket_request [RFC9149] | CH, EE |
| ech_outer_extensions [RFC9849] | CH |
| encrypted_client_hello [RFC9849] | CH, HRR, EE |
注意:此表仅包含本文撰写时标记为 “建议”且允许用于 TLS 1.3 的扩展。¶
存在多个不同类型的扩展时,扩展可以按任意顺序出现, 但“pre_shared_key”(第 4.3.11 节)除外,该扩展必须是 ClientHello 中的最后一个扩展(但可以出现在 ServerHello 扩展块中的任意位置)。 给定扩展块中不得包含多个相同类型的扩展。¶
在 TLS 1.3 中,与 TLS 1.2 不同,即使处于恢复 PSK 模式, 每次握手也会重新协商扩展。但是,0-RTT 参数 是在先前握手中协商的参数;不匹配可能要求 拒绝 0-RTT(参见第 4.3.10 节)。¶
新功能与现有功能之间可能在此协议中发生微妙 (或并不微妙)的交互,从而导致整体安全性 显著降低。设计新扩展时应考虑以下事项:¶
服务器不同意扩展的某些情况属于错误 条件(例如握手无法继续),另一些情况 只是拒绝支持特定功能。通常, 前一种情况应使用错误警报,后一种情况则应在服务器 扩展响应中使用字段表示。¶
扩展应尽可能设计为防止任何通过操纵握手 消息来强制使用(或不使用)某项特定功能的攻击。 无论是否认为该功能会造成安全问题,都应遵循此原则。 通常,将扩展字段包含在 Finished 消息哈希的输入中便已足够, 但当扩展改变握手阶段所发送消息的含义时, 必须格外谨慎。 设计者和实现者应认识到,在握手经过身份验证之前, 主动攻击者可以修改消息,并插入、移除或替换扩展。¶
struct {
select (Handshake.msg_type) {
case client_hello:
ProtocolVersion versions<2..254>;
case server_hello: /* 以及 HelloRetryRequest */
ProtocolVersion selected_version;
};
} SupportedVersions;
¶
客户端使用“supported_versions”扩展指明其支持哪些 TLS 版本,服务器使用该扩展指明其正在使用哪个版本。 该扩展包含按偏好顺序排列的受支持版本列表, 最偏好的版本位于最前。实现本规范的实现必须在 ClientHello 中发送此扩展,其中包含它们准备协商的所有 TLS 版本 (对于本规范,这意味着至少包含 0x0304;但如果允许协商 早期 TLS 版本,则这些版本也必须包含在内)。¶
如果不存在此扩展,则符合本规范且同时支持 TLS 1.2 的服务器 必须按照 [RFC5246] 的规定协商 TLS 1.2 或更早版本, 即使 ClientHello.legacy_version 为 0x0304 或更高版本。 服务器可以在收到 legacy_version 为 0x0304 或更高版本的 ClientHello 时中止握手。¶
如果 ClientHello 中存在此扩展,则服务器不得使用 ClientHello.legacy_version 值进行版本协商,并且必须只使用 “supported_versions”扩展确定客户端偏好。 服务器必须只选择该扩展中存在的 TLS 版本, 并必须忽略该扩展中存在的任何未知版本。 请注意,如果一方支持稀疏版本范围, 此机制可以协商早于 TLS 1.2 的版本。选择支持早期 TLS 版本的 TLS 1.3 实现应支持 TLS 1.2。 服务器必须准备接收包含此扩展、 但版本列表中不含 0x0304 的 ClientHello。¶
协商早于 TLS 1.3 版本的服务器必须设置 ServerHello.version,并且不得发送 “supported_versions”扩展。协商 TLS 1.3 的服务器必须通过发送 包含所选版本值(0x0304)的“supported_versions”扩展作出响应。 它必须将 ServerHello.legacy_version 字段设置为 0x0303(TLS 1.2)。¶
检查 ServerHello.random 以确定服务器握手消息 是 ServerHello 还是 HelloRetryRequest 后,客户端必须在 处理 ServerHello 的其余部分之前检查此扩展。 这将要求客户端解析 ServerHello 以读取扩展。 如果存在此扩展,客户端必须忽略 ServerHello.legacy_version 值,并且必须只使用 “supported_versions”扩展确定所选版本。如果 ServerHello 中的 “supported_versions”扩展包含客户端未提供的版本, 或包含早于 TLS 1.3 的版本,则客户端必须使用 “illegal_parameter”警报中止握手。¶
TLS 1.3 提供两个扩展,用于指明哪些签名 算法可用于数字签名。 “signature_algorithms_cert”扩展适用于证书中的签名; 最初出现在 TLS 1.2 中的“signature_algorithms”扩展, 适用于 CertificateVerify 消息中的签名。 证书中包含的密钥必须具有适合其所搭配签名算法的类型。 这对于 RSA 密钥和 PSS 签名尤其重要, 如下所述。如果不存在“signature_algorithms_cert”扩展, 则“signature_algorithms”扩展也适用于证书中出现的签名。 希望服务器通过证书验证自身身份的客户端必须发送 “signature_algorithms”扩展。如果服务器使用证书进行身份验证, 而客户端未发送“signature_algorithms”扩展,则服务器必须使用 “missing_extension”警报中止握手(参见第 9.2 节)。¶
添加“signature_algorithms_cert”扩展是为了让 证书和 TLS 本身支持不同算法集合的实现 能够清晰地指明其能力。TLS 1.2 实现也应处理 此扩展。在两种情况下使用相同策略的实现 可以省略“signature_algorithms_cert”扩展。¶
这些扩展的“extension_data”字段包含 SignatureSchemeList 值:¶
enum {
/* RSASSA-PKCS1-v1_5 算法 */
rsa_pkcs1_sha256(0x0401),
rsa_pkcs1_sha384(0x0501),
rsa_pkcs1_sha512(0x0601),
/* ECDSA 算法 */
ecdsa_secp256r1_sha256(0x0403),
ecdsa_secp384r1_sha384(0x0503),
ecdsa_secp521r1_sha512(0x0603),
/* 公钥 OID 为 rsaEncryption 的 RSASSA-PSS 算法 */
rsa_pss_rsae_sha256(0x0804),
rsa_pss_rsae_sha384(0x0805),
rsa_pss_rsae_sha512(0x0806),
/* EdDSA 算法 */
ed25519(0x0807),
ed448(0x0808),
/* 公钥 OID 为 RSASSA-PSS 的 RSASSA-PSS 算法 */
rsa_pss_pss_sha256(0x0809),
rsa_pss_pss_sha384(0x080a),
rsa_pss_pss_sha512(0x080b),
/* 旧式算法 */
rsa_pkcs1_sha1(0x0201),
ecdsa_sha1(0x0203),
/* 预留码位 */
private_use(0xFE00..0xFFFF),
(0xFFFF)
} SignatureScheme;
struct {
SignatureScheme supported_signature_algorithms<2..2^16-2>;
} SignatureSchemeList;
¶
注意:此枚举命名为“SignatureScheme”,是因为 TLS 1.2 中已存在 一个“SignatureAlgorithm”类型,而此枚举将其取代。 本文始终使用术语“签名算法”。¶
每个 SignatureScheme 值列出客户端愿意验证的一种 签名算法。相关值按偏好从高到低排列。 请注意,签名算法的输入是任意长度的消息,而不是摘要。 传统上作用于摘要的算法在 TLS 中定义时,应先使用指定的 哈希算法对输入进行哈希,然后照常继续。 上述码位组具有以下含义:¶
指明使用 RSASSA-PKCS1-v1_5 [RFC8017] 以及 [SHS] 中定义的对应哈希算法的签名算法。 这些值仅指证书中出现的签名(参见 第 4.5.1.2 节), 并未定义用于已签名的 TLS 握手消息,不过为了与 TLS 1.2 向后兼容, 它们可以出现在 “signature_algorithms”和“signature_algorithms_cert”中。¶
指明使用 ECDSA [DSS]、 NIST SP 800-186 [ECDP] 中定义的对应曲线, 以及 [SHS] 中定义的对应哈希算法的签名算法。 签名表示为 [RFC4492] 中定义的、采用 DER 编码 [X690] 的 ECDSA-Sig-Value 结构。¶
指明使用 RSASSA-PSS 和 [RFC8017] 中定义的 MGF1 掩码生成函数的签名算法。 MGF1 使用的摘要和被签名的摘要 均为 [SHS] 中定义的对应哈希算法。 盐的长度必须等于 摘要算法输出的长度。如果公钥承载于 X.509 证书中,则必须使用 rsaEncryption OID [RFC3279]。¶
指明使用 [RFC8032] 或其后继规范所定义 EdDSA 的签名算法。请注意,这些算法对应 “PureEdDSA”算法,而不是“prehash”变体。¶
指明使用 RSASSA-PSS 和 [RFC8017] 中定义的 MGF1 掩码生成函数的签名算法。 MGF1 使用的摘要和被签名的摘要 均为 [SHS] 中定义的对应哈希算法。 盐的长度必须等于摘要 算法的长度。如果公钥承载于 X.509 证书中, 则必须使用 RSASSA-PSS OID [RFC5756]。用于证书签名时, 算法参数必须采用 DER 编码。如果对应 公钥的参数存在,则签名中的参数 必须与公钥中的参数相同。¶
指明由于使用已知存在弱点的算法而正在被弃用的算法, 具体为在此上下文中与以下任一算法结合使用的 SHA-1: (1)使用 RSASSA-PKCS1-v1_5 的 RSA,或(2)ECDSA。这些值 仅指证书中出现的签名(参见 第 4.5.1.2 节), 并未定义用于已签名的 TLS 握手消息,不过为了与 TLS 1.2 向后兼容, 它们可以出现在 “signature_algorithms”和“signature_algorithms_cert”中。 端点不应协商这些算法, 但仅为向后兼容目的允许这样做。提供这些值的客户端必须将 它们列为最低优先级(在 SignatureSchemeList 中列于所有其他算法之后)。 除非无法在不使用 SHA-1 签名证书的情况下生成有效证书链, TLS 1.3 服务器不得提供此类证书 (参见第 4.5.1.2 节)。¶
自签名证书或作为信任锚的证书上的签名不会被验证, 因为它们位于认证路径的起点(参见 [RFC5280] 的第 3.2 节)。位于认证路径起点的证书 可以使用未在 “signature_algorithms”和“signature_algorithms_cert”扩展中公布为受支持的 签名算法。¶
请注意,TLS 1.2 对此扩展的定义不同。愿意协商 TLS 1.2 的 TLS 1.3 实现必须在协商该版本时 遵守 [RFC5246] 的要求。具体而言:¶
TLS 1.2 ClientHello可以 省略此扩展。¶
在 TLS 1.2 中,此扩展包含哈希/签名 组合。这些组合以两个八位字节编码,因此 SignatureScheme 值被分配为 与 TLS 1.2 的编码对齐。某些旧式组合仍未分配。 自 TLS 1.3 起,这些算法已被弃用。任何实现不得提供或 协商它们。特别是,不得使用 MD5 [SLOTH]、SHA-224 和 DSA。¶
ECDSA 签名方案与 TLS 1.2 的 ECDSA 哈希/签名组合对齐。 但是,旧语义并不限制签名所用曲线。如果协商 TLS 1.2, 实现必须准备接受使用其在 “supported_groups”扩展中公布的任意曲线的签名。¶
公布支持 RSASSA-PSS(TLS 1.3 中的强制要求)的实现, 即使协商 TLS 1.2,也必须准备接受使用该方案的签名。 在 TLS 1.2 中,RSASSA-PSS 与 RSA 密码套件一起使用。¶
“oid_filters”扩展允许服务器提供其希望客户端证书匹配的 OID/值组合列表。如果服务器提供此扩展,则必须只在 CertificateRequest 消息中发送。¶
struct {
opaque certificate_extension_oid<1..2^8-1>;
opaque certificate_extension_values<0..2^16-1>;
} OIDFilter;
struct {
OIDFilter filters<0..2^16-1>;
} OIDFilterExtension;
¶
证书扩展 OID [RFC5280] 及其允许值的列表, 以 DER 编码 [X690] 格式表示。 某些证书扩展 OID 允许多个值(例如扩展密钥用法)。 如果服务器包含非空 filters 列表,则响应中所含的客户端证书必须 包含客户端所识别的所有指定扩展 OID。 对于客户端识别的每个扩展 OID,所有指定值必须 出现在客户端证书中(但证书可以同时具有其他值)。 但是,客户端必须忽略并跳过任何无法识别的 证书扩展 OID。如果客户端忽略了某些必需的证书扩展 OID, 并提供不满足请求的证书,则服务器可以自行决定 是在不进行客户端身份验证的情况下继续连接, 还是使用“unsupported_certificate”警报中止握手。 任何给定 OID不得在 filters 列表中出现多次。¶
PKIX RFC 定义了多种证书扩展 OID 及其对应的值类型。 根据类型不同,相匹配的证书扩展值不一定按位相等。 预计 TLS 实现将依赖其 PKI 库, 使用证书扩展 OID 执行证书选择。¶
本文档为 [RFC5280] 中定义的两个标准证书扩展规定匹配规则:¶
当请求中断言的所有密钥用法位 也在证书的密钥用法扩展中被断言时, 证书中的密钥用法扩展与请求匹配。¶
当请求中存在的所有密钥用途 OID 也出现在证书的扩展密钥用法扩展中时, 证书中的扩展密钥用法扩展与请求匹配。 特殊的 anyExtendedKeyUsage OID不得用于请求。¶
单独的规范可以为其他证书扩展定义 匹配规则。¶
“post_handshake_auth”扩展用于指明 客户端愿意执行握手后身份验证(第 4.7.2 节)。服务器不得 向未提供此扩展的客户端发送握手后 CertificateRequest。 服务器不得发送此扩展。¶
struct {} PostHandshakeAuth;
¶
“post_handshake_auth”扩展的“extension_data”字段 长度为零。¶
由客户端发送时,“supported_groups”扩展指明 客户端支持用于密钥交换的命名组, 按偏好从高到低排列。¶
注意:在 TLS 1.3 之前的 TLS 版本中,此扩展名为 “elliptic_curves”,并且只包含椭圆曲线组。参见 [RFC8422] 和 [RFC7919]。此扩展还用于协商 ECDSA 曲线。现在签名算法单独协商(参见 第 4.3.3 节)。¶
此扩展的“extension_data”字段包含 “NamedGroupList”值:¶
enum {
/* 椭圆曲线组(ECDHE) */
secp256r1(0x0017), secp384r1(0x0018), secp521r1(0x0019),
x25519(0x001D), x448(0x001E),
/* 有限域组(DHE) */
ffdhe2048(0x0100), ffdhe3072(0x0101), ffdhe4096(0x0102),
ffdhe6144(0x0103), ffdhe8192(0x0104),
/* 预留码位 */
ffdhe_private_use(0x01FC..0x01FF),
ecdhe_private_use(0xFE00..0xFEFF),
(0xFFFF)
} NamedGroup;
struct {
NamedGroup named_group_list<2..2^16-1>;
} NamedGroupList;
¶
指明支持在 NIST SP 800-186 [ECDP] 或 [RFC7748] 中定义的对应命名曲线。 值 0xFE00 至 0xFEFF 预留用于专用用途 [RFC8126]。¶
“named_group_list”中的项目按发送方偏好排序 (最偏好的选择位于最前)。“named_group_list” 不得包含任何重复条目。如果接收方检测到重复条目, 则可以使用致命的“illegal_parameter”警报中止连接。¶
自 TLS 1.3 起,服务器可以向客户端发送 “supported_groups”扩展。客户端不得在握手成功完成之前 根据“supported_groups”中的任何信息采取行动, 但可以使用从成功完成的握手中获知的信息, 更改后续连接中“key_share”扩展所使用的组。 如果服务器有比“key_share”扩展中的组更偏好的组, 但仍愿意接受 ClientHello,则应发送 “supported_groups”以更新客户端对其偏好的认知; 无论客户端当前是否支持,扩展中应包含 服务器支持的所有组。¶
使用 PSK 且该 PSK 允许发送早期数据时 (例如参见附录 B.3.4),客户端可以在第一个消息批次中发送应用数据。 如果客户端选择这样做,则必须同时提供 “pre_shared_key”和“early_data”扩展。¶
此扩展的“extension_data”字段包含 “EarlyDataIndication”值。¶
struct {} Empty;
struct {
select (Handshake.msg_type) {
case new_session_ticket: uint32 max_early_data_size;
case client_hello: Empty;
case encrypted_extensions: Empty;
};
} EarlyDataIndication;
¶
有关 max_early_data_size 字段使用方式的详细信息, 请参阅第 4.7.1 节。¶
0-RTT 数据的参数(版本、对称密码套件、 应用层协议协商(ALPN)[RFC7301] 协议等) 是与所用 PSK 关联的参数。 对于外部配置的 PSK,关联值就是与密钥一同配置的值。 对于通过 NewSessionTicket 消息建立的 PSK, 关联值是在建立该 PSK 的连接中协商的值。 用于加密早期数据的 PSK必须是客户端 “pre_shared_key”扩展中列出的第一个 PSK。¶
对于通过 NewSessionTicket 配置的 PSK,服务器必须验证 所选 PSK 身份的票据期限 (通过从 PskIdentity.obfuscated_ticket_age 中减去 ticket_age_add, 并对 232 取模计算)与票据签发后经过的时间 相差在较小容差范围内(参见第 8 节)。如果不在容差范围内, 服务器应继续握手但拒绝 0-RTT,并且 不应采取任何其他假设此 ClientHello 是新鲜消息的操作。¶
在第一个消息批次中发送的 0-RTT 消息,与在其他批次中发送的 同类型消息具有相同的(加密)内容类型 (handshake 和 application_data),但使用不同密钥保护。 收到服务器的 Finished 消息后,如果服务器接受了早期数据, 客户端将发送 EndOfEarlyData 消息以指明密钥发生变化。 此消息使用 0-RTT 流量密钥加密。¶
收到“early_data”扩展的服务器 必须按以下三种方式之一运行:¶
忽略扩展并返回普通的 1-RTT 响应。 随后,服务器通过尝试使用握手流量密钥 对收到的记录解除保护,跳过早期数据, 并丢弃解除保护失败的记录 (最多达到配置的 max_early_data_size)。 一旦某条记录成功解除保护, 就将其视为客户端第二个消息批次的开始, 服务器随后按照普通的 1-RTT 握手继续。¶
通过以 HelloRetryRequest 响应, 请求客户端发送另一个 ClientHello。 客户端不得在后续 ClientHello 中包含 “early_data”扩展。随后,服务器通过跳过所有外部内容类型为 “application_data”(表示它们已加密)的记录来忽略早期数据, 最多达到配置的 max_early_data_size。¶
在 EncryptedExtensions 中返回其自己的 “early_data”扩展,以指明其打算处理早期数据。 服务器无法只接受早期数据消息的一个子集。 即使服务器发送了接受早期数据的消息, 在服务器生成该消息时,实际早期数据本身可能已在传输途中。¶
为了接受早期数据,服务器必须选择客户端“pre_shared_key”扩展中提供的第一个 密钥。此外,还必须验证以下值与 所选 PSK 关联的值相同:¶
这些要求是使用相关 PSK 执行 1-RTT 握手所需要求的超集。¶
未来扩展必须定义其与 0-RTT 的交互。¶
如果其中任何检查失败,服务器不得以该扩展响应, 并且必须使用上述前两种机制之一丢弃第一个消息批次中的全部数据 (从而回退到 1-RTT 或 2-RTT)。 如果客户端尝试 0-RTT 握手但服务器拒绝, 服务器通常不会拥有 0-RTT 记录保护密钥, 因此必须改用试探性解密 (使用 1-RTT 握手密钥,或者在 HelloRetryRequest 情况下 查找明文 ClientHello)来找到第一条非 0-RTT 消息。¶
如果服务器选择接受“early_data”扩展, 则在处理早期数据记录时必须遵守 为所有记录规定的相同错误处理要求。 具体而言,如果服务器在接受“early_data”扩展后 无法解密某条 0-RTT 记录,则必须按照 第 5.2 节的规定, 使用“bad_record_mac”警报终止连接。¶
如果服务器拒绝“early_data”扩展, 客户端应用可以选择在握手完成后, 重新传输此前作为早期数据发送的应用数据。 请注意,自动重新传输早期数据可能导致 对连接状态作出错误假设。例如, 当协商的连接选择了与早期数据所用协议不同的 ALPN 协议时, 应用可能需要构造不同的消息。类似地,如果 早期数据对连接状态作出任何假设, 则握手完成后重新发送可能是错误的。¶
TLS 实现不应自动重新发送早期数据; 应用更适合判断何时适合重新传输。 除非协商后的连接选择了相同的 ALPN 协议, TLS 实现不得自动重新发送早期数据。¶
服务器接下来发送的两条消息 EncryptedExtensions 和 CertificateRequest 包含来自服务器的信息, 这些信息决定握手的其余部分。这些消息 使用从 server_handshake_traffic_secret 派生的密钥加密。¶
在所有握手中,服务器必须在 ServerHello 消息之后立即发送 EncryptedExtensions 消息。 这是第一条使用从 server_handshake_traffic_secret 派生的密钥加密的消息。¶
EncryptedExtensions 消息包含可以受到保护的扩展, 即不需要用于建立密码学上下文、 但又不与单个证书关联的任何扩展。 客户端必须检查 EncryptedExtensions 中是否存在 任何被禁止的扩展;如果发现任何此类扩展, 则必须使用“illegal_parameter”警报中止握手。¶
此消息的结构:¶
struct {
Extension extensions<0..2^16-1>;
} EncryptedExtensions;
¶
使用证书进行身份验证的服务器可以选择性地 请求客户端提供证书。如果发送此消息,则必须紧随 EncryptedExtensions 之后。¶
此消息的结构:¶
struct {
opaque certificate_request_context<0..2^8-1>;
Extension extensions<0..2^16-1>;
} CertificateRequest;
¶
标识证书请求的 opaque 字符串, 客户端将在其 Certificate 消息中回显该字符串。 certificate_request_context 在此连接范围内必须唯一 (从而防止客户端 CertificateVerify 消息被重放)。 除非用于第 4.7.2 节所述的握手后身份验证交换, 此字段须为零长度。 请求握手后身份验证时,服务器应使上下文对客户端不可预测 (例如随机生成),以防止暂时获得客户端私钥的攻击者 预先计算有效的 CertificateVerify 消息。¶
描述所请求证书参数的扩展列表。 必须指定“signature_algorithms”扩展; 如果为此消息定义了其他扩展,则可以选择将其包含在内。 客户端必须忽略无法识别的扩展。¶
在早期 TLS 版本中,CertificateRequest 消息 携带服务器愿意接受的签名算法和证书颁发机构列表。 在 TLS 1.3 中,前者通过发送“signature_algorithms” 和可选的“signature_algorithms_cert”扩展来表示。 后者通过发送“certificate_authorities”扩展来表示 (参见第 4.3.4 节)。¶
使用恢复 PSK 进行身份验证的服务器不得在主握手中发送 CertificateRequest 消息;但只要客户端已发送 “post_handshake_auth”扩展(参见第 4.3.6 节),服务器可以在握手后身份验证中发送该消息 (参见第 4.7.2 节)。 除非其他规范另有规定, 使用外部 PSK 进行身份验证的服务器 不得在主握手中发送 CertificateRequest 消息, 也不得请求握手后身份验证。 [RFC8773] 提供了允许这样做的扩展, 但其所受分析少于本规范。¶
如第 2 节所述,TLS 通常使用一组通用消息 进行身份验证、密钥确认和握手完整性保护: Certificate、CertificateVerify 和 Finished。 (PSK 绑定器也以类似方式执行密钥确认。) 这三条消息始终作为其握手消息批次中的最后几条消息发送。 Certificate 和 CertificateVerify 消息只在下文定义的特定情况下发送。 Finished 消息始终作为身份验证块的一部分发送。 除握手后身份验证外,这些消息使用从 [sender]_handshake_traffic_secret 派生的密钥加密。¶
身份验证消息的所有计算统一采用以下输入:¶
基于这些输入,各消息包含以下内容:¶
用于身份验证的证书,以及证书链中的任何 支持证书。请注意,基于证书的客户端身份验证 不可用于 PSK 握手流程(包括 0-RTT)。¶
对值 Transcript-Hash(Handshake Context, Certificate) 的签名。¶
使用从基础密钥派生的 MAC 密钥, 对值 Transcript-Hash(Handshake Context, Certificate, CertificateVerify) 计算的 MAC。¶
下表定义每种场景的握手上下文和 MAC 基础密钥:¶
| 模式 | 握手上下文 | 基础密钥 |
|---|---|---|
| 服务器 | ClientHello ... EncryptedExtensions/CertificateRequest 中较晚者 | server_handshake_traffic_secret |
| 客户端 | ClientHello ... 服务器 Finished/EndOfEarlyData 中较晚者 | client_handshake_traffic_secret |
| 握手后 | ClientHello ... 客户端 Finished + CertificateRequest | [sender]_application_traffic_secret_N |
此消息将端点的证书链传递给对等方。¶
只要商定的密钥交换方法使用证书进行身份验证, 服务器必须发送 Certificate 消息 (这包括本文档定义的除 PSK 之外的所有密钥交换方法)。¶
当且仅当服务器通过 CertificateRequest 消息 (第 4.4.2 节) 请求基于证书的客户端身份验证时,客户端必须发送 Certificate 消息。 如果服务器请求基于证书的客户端身份验证, 但没有合适的证书可用,则客户端必须发送不含任何证书的 Certificate 消息(即“certificate_list”字段长度为 0)。 无论 Certificate 消息是否为空,都必须发送 Finished 消息。¶
此消息的结构:¶
enum {
X509(0),
RawPublicKey(2),
(255)
} CertificateType;
struct {
select (certificate_type) {
case RawPublicKey:
/* 来自 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;
¶
如果此消息用于响应 CertificateRequest, 则为该消息中 certificate_request_context 的值。 否则(服务器身份验证情况),此字段须为零长度。¶
CertificateEntry 结构的列表(证书链), 每个结构包含一个证书和扩展列表。¶
CertificateEntry 的扩展值列表。 “Extension”格式定义于第 4.3 节。当前适用于服务器证书的有效扩展包括 OCSP 状态扩展 [RFC6066] 和 SignedCertificateTimestamp 扩展 [RFC6962]; 未来还可以为此消息定义其他扩展。 服务器 Certificate 消息中的扩展必须对应于 ClientHello 消息中的扩展。 客户端 Certificate 消息中的扩展必须对应于服务器 CertificateRequest 消息中的扩展。 如果某个扩展适用于整个证书链,则应将其包含在第一个 CertificateEntry 中。¶
如果对应的证书类型扩展 (“server_certificate_type”或“client_certificate_type”) 未在 EncryptedExtensions 中协商,或者协商的是 X.509 证书类型, 则每个 CertificateEntry 都包含一个采用 DER 编码的 X.509 证书。 发送方的证书必须位于列表中的第一个 CertificateEntry。 随后的每个证书应直接认证紧邻其前的证书。 由于证书验证要求信任锚独立分发, 指定信任锚的证书可以从证书链中省略, 前提是已知受支持的对等方拥有任何被省略的证书。¶
注意:在 TLS 1.3 之前,“certificate_list”的顺序要求 每个证书认证紧邻其前的证书; 但某些实现允许一定灵活性。服务器有时会同时发送 当前和已弃用的中间证书用于过渡,也有服务器只是配置错误, 但这些情况仍可能得到正确验证。 为实现最大兼容性,所有实现应准备处理来自任何 TLS 版本的 潜在多余证书和任意顺序, 但最终实体证书除外,该证书必须位于最前。¶
如果协商 RawPublicKey 证书类型,则 certificate_list必须最多包含一个 CertificateEntry, 其中包含 [RFC7250] 的第 3 节中定义的 ASN1_subjectPublicKeyInfo 值。¶
OpenPGP 证书类型 [RFC6091]不得 与 TLS 1.3 一起使用。¶
服务器的 certificate_list必须 始终为非空。如果客户端没有合适的证书 可用于响应服务器的身份验证请求, 则会发送空的 certificate_list。¶
[RFC6066] 和 [RFC6961] 提供扩展,用于协商服务器向客户端 发送 OCSP 响应。在 TLS 1.2 及更低版本中, 服务器使用空扩展作出响应,以指明已协商此扩展, OCSP 信息则承载于 CertificateStatus 消息中。 在 TLS 1.3 中,服务器的 OCSP 信息承载于 包含关联证书的 CertificateEntry 中的扩展。 具体而言,服务器“status_request”扩展的主体必须是 [RFC6066] 中定义的 CertificateStatus 结构,并按照 [RFC6960] 的规定解释。¶
注意:status_request_v2 扩展 [RFC6961] 已被弃用。 TLS 1.3 服务器在处理 ClientHello 消息时, 不得根据此扩展是否存在或其中的信息采取行动; 特别是,不得在 EncryptedExtensions、 CertificateRequest 或 Certificate 消息中发送 status_request_v2 扩展。 TLS 1.3 服务器必须能够处理包含此扩展的 ClientHello 消息,因为希望在早期协议版本中使用它的客户端 可以发送该扩展。¶
服务器可以通过在 CertificateRequest 消息中发送空的“status_request”扩展, 请求客户端随其证书一同提供 OCSP 响应。 如果客户端选择发送 OCSP 响应,其“status_request”扩展的主体 必须是 [RFC6066] 中定义的 CertificateStatus 结构。¶
类似地,[RFC6962] 提供一种机制, 使服务器能够在 TLS 1.2 及更低版本中 将签名证书时间戳(SCT)作为 ServerHello 中的扩展发送。 在 TLS 1.3 中,服务器的 SCT 信息承载于 CertificateEntry 中的扩展。¶
以下规则适用于客户端或服务器发送的证书:¶
最终实体证书必须允许使用该密钥, 通过对等方“signature_algorithms”扩展中指明的签名方案进行签名 (参见第 4.3.3 节)。 也就是说,如果存在密钥用法扩展,则 digitalSignature 位必须设置, 并且公钥(连同相关限制)必须与某个受支持的 签名方案兼容。¶
如果对等方发送“certificate_authorities” 扩展,则证书链中至少一个证书应由所列 CA 之一签发。¶
以下规则还适用于客户端发送的证书:¶
以下规则还适用于服务器发送的证书:¶
如果发送方能够提供完全由对等方公布的签名算法签名的证书链, 则发送方提供的所有证书必须使用此类算法签名 (参见第 4.3.3 节)。 自签名证书或预期作为信任锚的证书不作为证书链的一部分进行验证, 因此可以使用任意算法签名。¶
如果发送方是服务器,并且服务器无法生成 仅使用所指明受支持算法签名的证书链, 则应通过发送其自行选择的证书链继续握手, 该证书链可以包含未知是否受客户端支持的算法。 除非客户端明确公布愿意接受 SHA-1, 此回退证书链不得使用已弃用的 SHA-1 哈希。¶
如果发送方是客户端,客户端可以使用上述回退证书链, 或以匿名方式继续握手。¶
如果接收方无法使用所提供证书构造可接受的证书链, 并决定中止握手,则必须使用适当的证书相关警报 中止握手(默认为“unsupported_certificate”; 有关更多信息,请参阅第 6.2 节)。¶
如果发送方拥有多个证书, 则根据上述标准以及其他标准(例如传输层端点、 本地配置和偏好)选择其中一个。¶
一般而言,详细的证书验证流程不在 TLS 的范围内 (参见 [RFC5280])。 本节提供 TLS 专用要求。¶
如果服务器提供空的 Certificate 消息, 客户端必须使用“decode_error”警报中止握手。¶
如果客户端未发送任何证书(即发送空的 Certificate 消息), 服务器可以自行决定是在不进行客户端身份验证的情况下 继续握手,还是使用“certificate_required”警报中止握手。 此外,如果证书链的某些方面不可接受 (例如未由已知且受信任的 CA 签名),服务器可以自行决定 是继续握手(将客户端视为未经身份验证),还是中止握手。¶
任何端点收到需要使用采用 MD5 哈希的任何签名算法进行验证的证书时, 必须使用“bad_certificate”警报中止握手。 SHA-1 已被弃用,建议任何端点在收到需要使用 采用 SHA-1 哈希的任何签名算法进行验证的证书时, 使用“bad_certificate”警报中止握手。 为清楚起见,这意味着端点可以为自签名证书或信任锚 接受这些算法。¶
建议所有端点尽快迁移到 SHA-256 或更强算法,以便与当前正在逐步淘汰 SHA-1 支持的实现 保持互操作性。¶
请注意,包含某种签名算法密钥的证书 可以使用不同的签名算法签名 (例如,由 ECDSA 密钥签名的 RSA 密钥)。¶
此消息用于明确证明端点 拥有与其证书对应的私钥。 CertificateVerify 消息还为截至此处的握手 提供完整性保护。服务器通过证书进行身份验证时必须发送此消息。 客户端通过证书进行身份验证时(即 Certificate 消息非空时)必须发送此消息。发送此消息时,它必须紧接 Certificate 消息之后,并紧邻 Finished 消息之前。¶
此消息的结构:¶
struct {
SignatureScheme algorithm;
opaque signature<0..2^16-1>;
} CertificateVerify;
¶
algorithm 字段指定所使用的签名算法(此类型的 定义参见第 4.3.3 节)。 signature 是使用该算法生成的数字签名。 签名所覆盖的内容是第 4.1 节所述的哈希输出,即:¶
Transcript-Hash(Handshake Context, Certificate)¶
随后,对以下内容的拼接结果计算数字签名:¶
此结构旨在防止针对早期 TLS 版本的一种攻击; 在该攻击中,ServerKeyExchange 格式意味着 攻击者可以获得具有所选 32 字节 前缀(ClientHello.random)的消息签名。初始的 64 字节填充会清除该前缀, 同时也清除由服务器控制的 ServerHello.random。¶
服务器签名的上下文字符串为 "TLS 1.3, server CertificateVerify"。 客户端签名的上下文字符串为 "TLS 1.3, client CertificateVerify"。 该字符串用于分隔在不同上下文中生成的签名, 有助于防范潜在的跨协议攻击。¶
例如,如果消息记录哈希由 32 个值为 01 的字节组成(此长度适用于 SHA-256),则服务器 CertificateVerify 的数字签名所覆盖的内容为:¶
2020202020202020202020202020202020202020202020202020202020202020 2020202020202020202020202020202020202020202020202020202020202020 544c5320312e332c207365727665722043657274696669636174655665726966 79 00 0101010101010101010101010101010101010101010101010101010101010101¶
在发送方,计算 CertificateVerify 消息的 signature 字段时采用以下输入:¶
如果 CertificateVerify 消息由服务器发送,则签名 算法必须是客户端 "signature_algorithms" 扩展中提供的算法之一, 除非不使用不受支持的算法便无法生成有效证书链 (参见第 4.3.3 节)。¶
如果由客户端发送,则签名中使用的签名算法 必须是 CertificateRequest 消息中 "signature_algorithms" 扩展的 supported_signature_algorithms 字段中存在的算法之一。¶
此外,签名算法必须与发送方最终实体证书中的密钥 兼容。CertificateVerify 消息的任何签名中不得使用 SHA-1 算法。 本规范中的所有 SHA-1 签名算法仅定义用于 旧式证书,对 CertificateVerify 签名无效。¶
CertificateVerify 消息的接收方必须验证 signature 字段。 验证过程采用以下输入:¶
如果验证失败,接收方必须使用 "decrypt_error" 警报 终止握手。¶
Finished 消息是身份验证 块中的最后一条消息。它对于验证握手 以及所计算的密钥至关重要。¶
Finished 消息的接收方必须 验证其内容是否正确;如果不正确,则必须使用 "decrypt_error" 警报终止连接。¶
一方发送其 Finished 消息,并收到且 验证了对等方的 Finished 消息后,便可以开始通过连接 发送和接收应用数据。在以下两种 情况下,允许在收到对等方的 Finished 之前 发送数据:¶
客户端按照第 4.3.10 节所述方式发送 0-RTT 数据。¶
服务器可以在发送其第一个消息批次后 发送数据,但由于握手尚未完成, 它无法确认对等方的身份或活跃性(即 ClientHello 可能已被重放)。¶
用于计算 Finished 消息的密钥通过 HKDF, 从第 4.5 节定义的基础密钥计算得出 (参见第 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*))
¶
仅在存在时包含。¶
HMAC [RFC2104] 使用握手所用的哈希算法。 如上所述,HMAC 输入通常可以通过持续更新的 哈希来实现,即此时的握手哈希。¶
在早期 TLS 版本中,verify_data 始终为 12 个八位字节 长。在 TLS 1.3 中,其大小等于握手所用哈希函数的 HMAC 输出大小。¶
注意:警报以及任何其他非握手记录类型都不是 握手消息,因此不包含在哈希计算中。¶
Finished 消息之后的任何记录必须使用 第 7.2 节所述的适当应用流量密钥加密。 特别是,这包括服务器为响应客户端 Certificate 和 CertificateVerify 消息而发送的任何警报。¶
struct {} EndOfEarlyData;
¶
如果服务器在 EncryptedExtensions 中发送了 "early_data" 扩展, 客户端在收到服务器 Finished 后必须发送 EndOfEarlyData 消息。如果服务器未在 EncryptedExtensions 中 发送 "early_data" 扩展,则客户端不得发送 EndOfEarlyData 消息。此消息指明所有 0-RTT application_data 消息(如果有)均已传输, 并且后续记录使用握手流量密钥进行保护。 服务器不得发送此消息,收到此消息的客户端 必须使用 "unexpected_message" 警报终止连接。 此消息使用从 client_early_traffic_secret 派生的密钥加密。¶
TLS 还允许在主握手之后发送其他消息。 这些消息使用握手内容类型,并使用 适当的应用流量密钥加密。¶
如果客户端问候中包含合适的 "psk_key_exchange_modes" 扩展, 服务器在收到客户端 Finished 消息后, 可以随时可以发送 NewSessionTicket 消息。 NewSessionTicket 消息在票据值与 从恢复秘密值派生的秘密 PSK 之间建立唯一关联 (参见第 7 节)。¶
客户端可以将票据值包含在未来握手的 ClientHello 的 "pre_shared_key" 扩展中, 从而在未来握手中使用此 PSK (第 4.3.11 节)。 收到 NewSessionTicket 消息但不支持恢复的客户端 必须静默忽略此消息。 恢复可以在原始连接仍处于打开状态时进行。 服务器可以在单个连接上发送多个票据, 既可以连续立即发送,也可以在特定事件之后发送 (参见附录 C.4)。 例如,服务器可以在握手后身份验证之后发送新票据, 从而封装额外的客户端身份验证状态。 多个票据可用于客户端的多种用途,包括:¶
任何票据必须只使用 KDF 哈希算法与建立原始连接时所用算法相同的密码套件进行恢复。¶
只有当新的 SNI 值对于原始会话中提供的服务器 证书有效时,客户端必须进行恢复; 并且只有当 SNI 值与原始会话中使用的值匹配时, 客户端才应进行恢复。后一项是一种性能优化: 通常没有理由认为由同一证书覆盖的不同服务器 能够接受彼此的票据;因此,在这种情况下尝试恢复 会浪费一次性票据。如果提供了此类指示 (从外部或通过任何其他方式),客户端可以使用不同的 SNI 值进行恢复。¶
恢复时,如果向调用应用报告 SNI 值, 实现必须使用恢复 ClientHello 中发送的值, 而不是先前会话中发送的值。请注意,如果服务器实现 拒绝所有具有不同 SNI 值的 PSK 身份,则这两个 值始终相同。¶
注意:虽然恢复秘密值依赖于客户端的第二个 消息批次,但不请求基于证书的客户端身份验证的服务器可以独立计算 消息记录的其余部分,然后在发送其 Finished 后立即发送 NewSessionTicket,而不必等待客户端 Finished。 例如,在预期客户端并行打开多个 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-1>;
} NewSessionTicket;
¶
从票据签发时起,以网络字节序的 32 位无符号整数表示以秒为单位的生存期。 服务器不得使用大于 604800 秒(7 天)的任何值。 值为零表示应立即丢弃票据。 无论 ticket_lifetime 为何,客户端不得在票据签发后 使用票据超过 7 天,并且可以根据本地策略更早删除票据。 服务器可以将票据视为只在 比 ticket_lifetime 所述时间更短的期限内有效。¶
安全生成的随机 32 位值,用于 混淆客户端在 "pre_shared_key" 扩展中包含的票据期限。 客户端侧的票据期限与此值相加并对 232 取模, 从而得到客户端传输的值。 服务器必须为其发送的每个票据生成一个新值。¶
每个票据对应的值,在此连接上签发的所有票据中 唯一。¶
用作 PSK 身份的票据值。 票据本身是一个 opaque 标签。它可以是数据库 查找键,也可以是自行加密并自行验证的值。¶
当前为 NewSessionTicket 定义的唯一扩展是 "early_data",用于指明该票据可用于发送 0-RTT 数据 (第 4.3.10 节)。 它包含以下值:¶
客户端使用此票据时允许发送的 0-RTT 数据最大量,以字节为单位。只计算应用数据载荷 (即明文,但不包括填充或内部内容类型字节)。 收到超过 max_early_data_size 字节 0-RTT 数据的服务器 应使用 "unexpected_message" 警报终止连接。 请注意,由于缺少密码材料而拒绝早期数据的服务器 无法区分填充与内容,因此客户端不应依赖于能够在早期数据 记录中发送大量填充。¶
与票据关联的 PSK 按以下方式计算:¶
HKDF-Expand-Label(resumption_secret,
"resumption", ticket_nonce, Hash.length)
¶
由于每条 NewSessionTicket 消息的 ticket_nonce 值各不相同, 因此会为每个票据派生不同的 PSK。¶
请注意,原则上可以继续签发新票据, 从而无限延长最初从一次初始非 PSK 握手 派生的密钥材料的生存期 (该材料很可能与对等方的证书绑定)。 建议实现限制此类密钥材料的总生存期; 这些限制应考虑对等方证书的生存期、 期间发生撤销的可能性, 以及距离对等方在线 CertificateVerify 签名已经过去的时间。¶
客户端发送 "post_handshake_auth" 扩展后(参见 第 4.3.6 节), 服务器可以在握手完成后的任何时间, 通过发送 CertificateRequest 消息请求基于证书的客户端身份验证。 客户端必须使用适当的身份验证消息作出响应 (参见第 4.5 节)。 如果客户端选择进行身份验证,则必须发送 Certificate、CertificateVerify 和 Finished。 如果客户端拒绝,则必须发送 不含任何证书的 Certificate 消息,随后发送 Finished。 客户端针对给定响应发送的所有消息 必须在线路上连续出现,中间不得插入 其他类型的消息或其他响应中的消息。¶
未发送 "post_handshake_auth" 扩展的客户端 如果收到握手后 CertificateRequest 消息,必须发送 "unexpected_message" 致命警报。¶
注意:由于基于证书的客户端身份验证可能需要 提示用户,服务器必须为一定程度的延迟做好准备, 包括在发送 CertificateRequest 与收到 响应之间收到任意数量的其他消息。 此外,短时间内连续收到多个 CertificateRequest 的客户端 可以按不同于接收顺序的顺序作出响应 (certificate_request_context 值使服务器能够 区分这些响应)。¶
KeyUpdate 握手消息用于指明发送方 正在更新其发送密码密钥。任一对等方均可在 发送 Finished 消息后发送此消息。 在收到 Finished 消息之前收到 KeyUpdate 消息的实现 必须使用 "unexpected_message" 警报终止连接。 发送 KeyUpdate 消息后,发送方须使用 按照第 7.2 节所述方式计算的下一代密钥 发送其所有流量。 收到 KeyUpdate 后,接收方必须更新其 接收密钥。¶
enum {
update_not_requested(0), update_requested(1), (255)
} KeyUpdateRequest;
struct {
KeyUpdateRequest request_update;
} KeyUpdate;
¶
指明 KeyUpdate 的接收方是否应使用其自己的 KeyUpdate 作出响应。如果实现收到任何其他值,则必须使用 "illegal_parameter" 警报 终止连接。¶
如果 request_update 字段设置为 "update_requested", 则接收方必须在发送其下一条应用数据记录之前, 发送自己的 KeyUpdate,并将 request_update 设置为 "update_not_requested"。此机制允许任一方强制更新整个连接, 但当实现处于静默状态时收到多个 KeyUpdate, 只会以一次更新作出响应。在从对等方收到后续 KeyUpdate 之前, 发送方不得再次发送将 request_update 设置为 "update_requested" 的 KeyUpdate。¶
请注意,在发送将 request_update 设置为 "update_requested" 的 KeyUpdate 与收到对等方的 KeyUpdate 之间,实现可能收到任意数量的消息, 包括无关的 KeyUpdate,因为这些消息可能已在传输途中。 但是,由于发送密钥和接收密钥从相互独立的流量秘密值派生, 保留接收流量秘密值不会威胁 发送方更改密钥之前所发送数据的前向保密性。¶
如果实现各自独立发送将 request_update 设置为 "update_requested" 的 KeyUpdate,并且这些消息在传输途中交叉, 则双方还会各自发送响应, 结果是双方均递增两代密钥。¶
发送方和接收方必须使用旧密钥加密 其 KeyUpdate 消息。此外,双方必须强制确保 在接受任何使用新密钥加密的消息之前, 先收到使用旧密钥加密的 KeyUpdate。 否则可能会允许消息截断攻击。¶
对于 AES-128 所使用的 128 位密钥,重新生成密钥 264 次后,在给定连接内出现密钥复用的概率很高。 请注意,即使密钥重复,IV 也会独立生成, 因此密钥/IV 同时发生冲突的概率要低得多。 为提供额外的安全余量,发送实现不得允许 纪元——也就是密钥更新次数—— 超过 248-1。为了允许将来更改此值 ——例如用于密钥长度超过 128 位的密码—— 接收实现不得强制执行此规则。 如果发送实现收到 request_update 设置为 "update_requested" 的 KeyUpdate,而发送自己的 KeyUpdate 会使其超过这些限制,则不得发送, 并且应改为忽略 "update_requested" 标志。 这可能导致在达到第 5.5 节中的限制时, 最终需要终止连接。¶
TLS 记录协议接收待传输的消息,将 数据分割成易于管理的块,对记录进行保护,并传输 结果。收到的数据会经过验证、解密和重新组装, 然后传递给更高层客户端。¶
TLS 记录具有类型,因此可以在同一记录层上 多路复用多个更高层协议。本文档规定 四种内容类型:handshake、application_data、alert 和 change_cipher_spec。 change_cipher_spec 记录仅用于兼容性目的 (参见附录 E.4)。¶
实现可能会在第一条 ClientHello 消息已发送或接收之后, 且在收到对等方的 Finished 消息之前的任何时间, 收到一条类型为 change_cipher_spec、仅由单字节值 0x01 组成的未加密记录,并且必须直接丢弃该记录,而不作 进一步处理。请注意,此记录可能出现在握手过程中 实现正在等待受保护记录的位置, 因此必须在尝试解除记录保护之前检测到 这种情况。收到任何其他 change_cipher_spec 值, 或收到受保护的 change_cipher_spec 记录的实现必须使用 “unexpected_message”警报中止握手。如果实现检测到 在第一条 ClientHello 消息之前或对等方的 Finished 消息之后收到 change_cipher_spec 记录,则必须将其视为 意外的记录类型(不过无状态服务器可能无法区分这些情况与允许的情况)。¶
除非通过某个扩展进行了协商,否则实现不得发送本文档中 未定义的记录类型。如果 TLS 实现 收到意外的记录类型,则必须使用 “unexpected_message”警报终止连接。新的记录内容类型值 由 IANA 按照第 11 节所述方式在 TLS ContentType 注册表中分配。¶
记录层将信息块分割成 TLSPlaintext 记录,每块携带 214 字节或更少的数据。根据底层 ContentType 的不同,消息边界的处理方式也不同。 任何未来的内容类型必须规定适当的规则。 请注意,这些规则比 TLS 1.2 中强制执行的规则更严格。¶
握手消息可以合并到单个 TLSPlaintext 记录中,也可以分割到多个记录中,前提是:¶
握手消息不得与其他记录 类型交错。也就是说,如果一条握手消息被分割到两个或多个 记录中,则这些记录之间不得存在任何其他记录。¶
握手消息不得跨越密钥 更改。实现必须验证 密钥更改前紧邻的所有消息均与记录边界对齐; 否则,必须使用 “unexpected_message”警报终止连接。 由于 ClientHello、EndOfEarlyData、ServerHello、Finished 和 KeyUpdate 消息可能紧邻密钥更改之前,因此实现必须 发送与记录边界对齐的这些消息。¶
实现不得发送长度为零的 Handshake 类型片段,即使这些片段包含填充。¶
警报消息(第 6 节)不得跨记录分割, 并且多条警报消息不得合并到单个 TLSPlaintext 记录中。换言之,Alert 类型的记录必须恰好包含一条消息。¶
应用数据消息包含对 TLS 不透明的数据。 应用数据消息始终受到保护。可以发送长度为零的 应用数据片段(即内容长度为零且类型为 application_data 的 TLSInnerPlaintext 记录),因为它们可能 可用作对抗流量分析的措施。应用数据片段 可以分割到多个记录中,也可以合并到单个记录中。¶
enum {
invalid(0),
change_cipher_spec(20),
alert(21),
handshake(22),
application_data(23),
(255)
} ContentType;
struct {
ContentType type;
ProtocolVersion legacy_record_version;
uint16 length;
opaque fragment[TLSPlaintext.length];
} TLSPlaintext;
¶
用于处理所含片段的更高层协议。¶
除初始 ClientHello (即并非在 HelloRetryRequest 之后生成的 ClientHello)之外, TLS 1.3 实现生成的所有记录都必须将此字段设置为 0x0303; 对于初始 ClientHello,出于兼容性目的, 此字段也可以为 0x0301。 此字段已被弃用,并且在所有情况下都必须忽略。 早期 TLS 版本在某些情况下会在此字段中使用其他值。¶
后续 TLSPlaintext.fragment 的长度(以字节为单位)。 该长度不得超过 214 字节。 收到超过此长度的记录的端点必须使用 “record_overflow”警报终止连接。¶
正在传输的数据。此值是透明的,并被视为 一个独立的数据块,由 type 字段指定的 更高层协议进行处理。¶
本文档描述使用版本值 0x0304 的 TLS 1.3。 此版本值是历史遗留的,源于 TLS 1.0 使用 0x0301, SSL 3.0 使用 0x0300。为最大限度提高向后 兼容性,包含初始 ClientHello 的记录应使用版本 0x0301(表示 TLS 1.0),而包含第二个 ClientHello 或 ServerHello 的记录必须使用版本 0x0303(表示 TLS 1.2)。 协商早期 TLS 版本时,端点遵循附录 E中提供的流程和要求。¶
在记录保护尚未启用时,TLSPlaintext 结构会直接写入线路。一旦记录保护 开始,TLSPlaintext 记录便按照下一节所述方式 受到保护并发送。请注意,应用数据 记录不得在不受保护的情况下写入线路 (有关详细信息,请参阅第 2 节)。¶
记录保护函数将 TLSPlaintext 结构转换为 TLSCiphertext 结构。解除保护函数则执行相反的过程。在 TLS 1.3 中, 与早期 TLS 版本不同,所有密码均建模为 “带关联数据的认证加密”(AEAD)[RFC5116]。 AEAD 函数提供统一的加密和身份验证 操作,将明文转换为经身份验证的密文,并可还原。 每条加密记录均由明文标头和随后的加密主体组成, 加密主体本身包含类型和可选填充。¶
struct {
opaque content[TLSPlaintext.length];
ContentType type;
uint8 zeros[length_of_padding];
} TLSInnerPlaintext;
struct {
ContentType opaque_type = application_data; /* 23 */
ProtocolVersion legacy_record_version = 0x0303; /* TLS v1.2 */
uint16 length;
opaque encrypted_record[TLSCiphertext.length];
} TLSCiphertext;
¶
TLSPlaintext.fragment 值,其中包含握手消息或 警报消息的字节编码,或者待发送应用数据的 原始字节。¶
包含记录内容类型的 TLSPlaintext.type 值。¶
在明文中的 type 字段之后,可以出现任意长度的 零值字节序列。只要总大小仍在记录大小限制以内, 这便使发送方有机会按所选大小填充任何 TLS 记录。 有关更多详细信息,请参阅 第 5.4 节。¶
TLSCiphertext 记录的外部 opaque_type 字段始终 设置为值 23(application_data),以便在外部与 习惯解析早期 TLS 版本的中间盒兼容。 解密后,可在 TLSInnerPlaintext.type 中找到记录的 实际内容类型。¶
legacy_record_version 字段始终为 0x0303。 TLS 1.3 TLSCiphertext 直到 TLS 1.3 协商完成后才会生成, 因此不存在可能收到其他值的历史兼容性问题。 请注意,握手协议(包括 ClientHello 和 ServerHello 消息) 会验证协议版本,因此此值是冗余的。¶
后续 TLSCiphertext.encrypted_record 的长度 (以字节为单位),它等于内容和填充的长度之和, 再加上内部内容类型所占的一个字节, 以及 AEAD 算法添加的任何扩展。 该长度不得超过 214 + 256 字节。 收到超过此长度的记录的端点必须 使用“record_overflow”警报终止连接。¶
序列化 TLSInnerPlaintext 结构经 AEAD 加密后的形式。¶
AEAD 算法接收单个密钥、nonce、明文以及 要包含在身份验证检查中的“附加 数据”作为输入,如 [RFC5116] 第 2.1 节所述。 密钥为 client_write_key 或 server_write_key, nonce 从序列号和 client_write_iv 或 server_write_iv 派生(参见第 5.3 节), 附加数据输入则为记录标头。即:¶
additional_data = TLSCiphertext.opaque_type ||
TLSCiphertext.legacy_record_version ||
TLSCiphertext.length
¶
AEAD 算法的明文输入为编码后的 TLSInnerPlaintext 结构。 流量密钥的派生定义于第 7.3 节。¶
AEAD 输出由 AEAD 加密操作产生的密文组成。 由于包含 TLSInnerPlaintext.type 和发送方提供的任何填充, 明文长度大于对应的 TLSPlaintext.length。 AEAD 输出的长度通常会大于明文, 但增加量会因 AEAD 算法而异。由于密码可能 包含填充,因此开销可能随明文长度不同而变化。 用符号表示为:¶
AEADEncrypted =
AEAD-Encrypt(write_key, nonce, additional_data, plaintext)
¶
TLSCiphertext 的 encrypted_record 字段设置为 AEADEncrypted。¶
为进行解密和验证,密码接收密钥、nonce、 附加数据和 AEADEncrypted 值作为输入。输出要么是明文, 要么是表示解密失败的错误。不进行单独的 完整性检查。用符号表示为:¶
plaintext of encrypted_record =
AEAD-Decrypt(peer_write_key, nonce, additional_data, AEADEncrypted)
¶
如果解密失败,接收方必须使用 “bad_record_mac”警报终止连接。¶
TLS 1.3 中使用的 AEAD 算法不得产生 超过 255 个八位字节的扩展。端点收到对等方发送的、 TLSCiphertext.length 大于 214 + 256 个八位字节的记录时,必须 使用“record_overflow”警报终止连接。 此限制源于 TLSInnerPlaintext 的最大长度: 214 个八位字节,加上 ContentType 的 1 个八位字节, 再加上 AEAD 最大扩展的 255 个八位字节。¶
读取和写入记录分别维护一个 64 位序列号。 每读取或写入一条记录后,相应序列号递增一。 每个序列号在连接开始时以及每次密钥更改时均设置为零; 使用特定流量密钥传输的第一条记录必须使用 序列号 0。¶
由于序列号大小为 64 位,因此不应 发生回绕。如果 TLS 实现需要 使序列号回绕,则必须重新生成密钥 (第 4.7.3 节) 或终止连接。¶
每种 AEAD 算法都会为逐记录 nonce 指定一个可能的输入长度范围,从 N_MIN 字节到 N_MAX 字节 [RFC5116]。 TLS 逐记录 nonce 的长度(iv_length)设置为 8 字节与 AEAD 算法的 N_MIN 两者中的较大值 (参见 [RFC5116] 的第 4 节)。 N_MAX 小于 8 字节的 AEAD 算法不得与 TLS 一起使用。 AEAD 构造的逐记录 nonce 按以下方式形成:¶
将 64 位记录序列号按网络字节序编码, 并在左侧填充零,直至达到 iv_length。¶
将填充后的序列号与静态 client_write_iv 或 server_write_iv(取决于角色) 进行 XOR 运算。¶
所得值(长度为 iv_length)用作逐记录 nonce。¶
注意:此构造与 TLS 1.2 中的构造不同, 后者规定了部分显式的 nonce。¶
所有加密的 TLS 记录都可以进行填充,以增大 TLSCiphertext 的大小。这使发送方能够向观察者隐藏 流量大小。¶
生成 TLSCiphertext 记录时,实现可以选择进行填充。 未填充记录就是填充长度为零的记录。 填充是在加密前附加到 ContentType 字段之后的一串零值字节。实现必须在加密前 将所有填充八位字节设置为零。¶
如果发送方希望,应用数据记录可以包含长度为零的 TLSInnerPlaintext.content。这允许在活动是否存在 可能属于敏感信息的场景中,生成大小看似合理的掩护 流量。实现不得发送 TLSInnerPlaintext.content 长度为零的 Handshake 和 Alert 记录; 如果收到此类消息,接收实现必须使用 “unexpected_message”警报终止连接。¶
发送的填充由记录保护机制自动验证; 成功解密 TLSCiphertext.encrypted_record 后, 接收实现从字段末尾向 开头扫描,直到找到一个非零八位字节。 此非零八位字节就是消息的内容类型。 选择此填充方案是因为它允许以任意大小 (从零到 TLS 记录大小限制)填充任何加密的 TLS 记录,而无需引入新的内容类型。该设计还 强制要求所有填充八位字节均为零,从而可以快速检测 填充错误。¶
实现必须将扫描限制在 AEAD 解密返回的明文中。如果接收实现未在 明文中找到非零八位字节,则必须使用 “unexpected_message”警报终止连接。¶
填充的存在不会改变总体记录大小限制: 完整编码后的 TLSInnerPlaintext不得超过 214 + 1 个八位字节。如果最大片段长度被缩小 ——例如通过 [RFC8449] 中的 record_size_limit 扩展——, 则缩小后的限制适用于完整明文, 包括内容类型和填充。¶
选择用于指明何时填充以及填充多少的填充策略 是一个复杂主题,超出了本规范的范围。如果构建于 TLS 之上的 应用层协议具有自己的填充,则可能更适合 在应用层填充应用数据 TLS 记录。 不过,加密 Handshake 或 Alert 记录的填充仍必须 在 TLS 层处理。后续文档可以定义 填充选择算法,或者通过 TLS 扩展或其他方式定义 填充策略请求机制。¶
在给定密钥集下可安全加密的明文数量存在 密码学限制。[AEAD-LIMITS] 在假设底层 原语(AES 或 ChaCha20)不存在弱点的情况下分析了这些限制。 实现必须在达到这些限制之前, 关闭连接或按照第 4.7.3 节所述方式执行密钥更新。 请注意,无法对早期数据执行 KeyUpdate; 因此,实现发送早期数据时不得超过这些限制。 接收实现不应强制执行这些限制, 因为未来的分析可能产生更新后的值。¶
对于 AES-GCM,在给定密钥集下最多可以加密 224.5 条完整大小的记录(约 2400 万条), 同时为认证加密(AE)安全性保留约 2-57 的安全余量。 对于 ChaCha20/Poly1305,在达到安全限制之前, 记录序列号便会回绕。¶
TLS 提供 Alert 内容类型,用于指明关闭信息 和错误。与其他消息一样,警报消息会按照 当前连接状态的规定进行加密。¶
警报消息传递警报的描述以及一个旧式字段, 该字段在早期 TLS 版本中用于传递消息的严重级别。 警报分为 两类:关闭警报和错误警报。在 TLS 1.3 中, 严重程度由所发送警报的类型隐式决定, 因而可以安全地忽略“level”字段。“close_notify”警报 用于指明连接某一方向的有序关闭。 收到此类警报时,TLS 实现应 向应用指明数据结束。¶
错误警报指明连接的异常关闭 (参见第 6.2 节)。收到错误警报时, TLS 实现应向应用指明错误,并且 不得允许在该连接上继续发送或接收任何数据。 服务器和客户端必须遗忘失败连接中建立的秘密值和 密钥,但与会话票据关联的 PSK 除外; 如果可能,这些 PSK应被丢弃。¶
第 6.2 节中列出的所有警报必须以 AlertLevel=fatal 发送,并且无论消息中的 AlertLevel 为何, 收到时都必须将其视为错误警报。 未知 Alert 类型必须被视为错误警报。¶
注意:TLS 定义了两个通用警报(参见第 6 节),用于 消息解析失败的情况。收到无法按照语法 解析的消息(例如长度超出消息边界, 或包含超出范围的长度)的对等方必须使用 “decode_error”警报终止连接。收到语法正确 但语义无效的消息(例如值为 p - 1 的 DHE 共享值,或无效的 enum)的对等方必须使用“illegal_parameter”警报终止连接。¶
enum { warning(1), fatal(2), (255) } AlertLevel;
enum {
close_notify(0),
unexpected_message(10),
bad_record_mac(20),
record_overflow(22),
handshake_failure(40),
bad_certificate(42),
unsupported_certificate(43),
certificate_revoked(44),
certificate_expired(45),
certificate_unknown(46),
illegal_parameter(47),
unknown_ca(48),
access_denied(49),
decode_error(50),
decrypt_error(51),
protocol_version(70),
insufficient_security(71),
internal_error(80),
inappropriate_fallback(86),
user_canceled(90),
missing_extension(109),
unsupported_extension(110),
unrecognized_name(112),
bad_certificate_status_response(113),
unknown_psk_identity(115),
certificate_required(116),
general_error(117),
no_application_protocol(120),
(255)
} AlertDescription;
struct {
AlertLevel level;
AlertDescription description;
} Alert;
¶
为避免截断攻击,客户端和服务器必须共同知晓连接正在 结束。¶
此警报通知接收方,发送方不会再通过此连接 发送任何消息。收到关闭警报后接收的任何数据必须被忽略。 此警报必须以 AlertLevel=warning 发送。¶
此警报通知接收方,发送方正在因与协议失败无关的 某种原因取消握手。如果用户在握手完成后 取消操作,则发送“close_notify”以直接关闭 连接更为合适。此警报后必须跟随一个“close_notify”。 此警报通常使用 AlertLevel=warning。接收实现应 继续从对等方读取数据,直到收到“close_notify”, 不过它们可以记录日志或以其他方式记录这些数据。¶
任一方可以通过发送“close_notify”警报, 发起关闭连接的写入方向。 如果在“close_notify”之前收到传输层关闭, 接收方将无法知道发送的所有数据是否都已收到。¶
除非已经发送某个错误警报,否则每一方在关闭连接的写入方向之前 必须发送“close_notify”警报。 这不会对连接的读取方向产生任何影响。请注意,这是相对于 TLS 1.3 之前版本的一项更改;在那些版本中,实现收到 “close_notify”后必须丢弃待处理的写入, 并立即发送自己的“close_notify”警报。 之前的要求可能导致读取方向发生截断。 双方在关闭连接的读取方向之前都不必 等待收到“close_notify”警报,不过这样做会引入截断的可能性。¶
应用协议可以选择在收到“close_notify”后刷新其 发送缓冲区并立即发送“close_notify”,但这样会允许攻击者 通过延迟“close_notify”,或延迟在传输层交付应用的数据包, 影响对等方收到的数据。 这些问题可以在应用层解决,例如忽略 发送“close_notify”后收到的数据包。¶
如果使用 TLS 的应用协议允许在 TLS 连接关闭后, 仍通过底层传输承载任何数据,则 TLS 实现在向应用层指明数据结束之前必须收到“close_notify”警报。 不应将本标准的任何部分理解为规定 TLS 使用配置文件 管理其数据传输的方式,包括何时打开或关闭连接。¶
注意:这里假设关闭连接的写入方向时, 会在销毁传输之前可靠地交付待处理数据。¶
TLS 中的错误处理非常简单。当检测到 错误时,检测到错误的一方向其 对等方发送消息。发送或收到致命警报消息时, 双方必须立即关闭连接。¶
实现遇到致命错误条件时, 应发送适当的致命警报,并且必须关闭连接, 不再发送或接收任何其他数据。在本规范中, 当使用“终止连接”和“中止握手”而未指定具体警报时, 表示实现应发送以下描述中指明的警报。 “使用 X 警报终止连接”和 “使用 X 警报中止握手”表示,如果实现发送任何警报, 则必须发送警报 X。本节下文定义的所有 警报以及所有未知警报,自 TLS 1.3 起都被普遍视为致命警报 (参见第 6 节)。 实现应提供一种方法,以便记录 警报的发送和接收。¶
定义了以下错误警报:¶
收到了不适当的消息(例如错误的握手消息、 过早发送的 应用数据等)。在正确实现之间的通信中, 不应观察到此警报。¶
收到无法解除保护的记录时返回此警报。 由于 AEAD 算法将解密和验证合并, 并且为了避免侧信道攻击, 所有解除保护失败都使用此警报。 除非消息在网络中损坏,否则在 正确实现之间的通信中不应观察到此警报。¶
收到长度超过 214 + 256 字节的 TLSCiphertext 记录,或者某条记录解密后得到 超过 214 字节(或其他协商限制)的 TLSPlaintext 记录。 除非消息在网络中损坏,否则在 正确实现之间的通信中不应观察到此警报。¶
收到“handshake_failure”警报消息表示, 发送方无法在现有选项中协商出一组可接受的 安全参数。¶
证书已损坏、包含无法正确验证的签名等。¶
证书属于不受支持的类型。¶
证书已被其签名者撤销。¶
证书已过期或当前无效。¶
处理证书时出现其他未指定的问题, 导致证书不可接受。¶
握手中的某个字段不正确,或与 其他字段不一致。此警报用于符合 正式协议语法、但在其他方面不正确的错误。¶
收到了有效证书链或部分证书链, 但由于无法找到 CA 证书, 或无法将其与已知信任锚匹配,因此未接受该证书。¶
收到了有效证书或 PSK,但应用访问控制后, 发送方决定不继续协商。¶
由于某个字段超出规定范围, 或消息长度不正确,无法解码消息。 此警报用于消息不符合 正式协议语法的错误。 除非消息在网络中损坏,否则在 正确实现之间的通信中不应观察到此警报。¶
握手层(而非记录层)的密码学操作失败, 包括无法正确验证签名、验证 Finished 消息 或验证 PSK 绑定器。¶
当协商失败的具体原因是服务器需要的参数 比客户端支持的参数更安全时,返回此警报, 而不是“handshake_failure”。¶
出现与对等方或协议正确性无关的内部错误 (例如内存分配失败),导致无法继续。¶
端点收到的握手消息中缺少 针对所提供 TLS 版本或其他协商参数 必须发送的扩展时,发送此警报。¶
端点收到的握手消息中,ServerHello、 HelloRetryRequest、EncryptedExtensions 或 Certificate 包含 未先在对应 ClientHello 或 CertificateRequest 中提供的扩展时, 发送此警报。¶
客户端通过“server_name”扩展提供的名称 未标识任何现有服务器时,由服务器发送此警报 (参见 [RFC6066])。¶
服务器通过“status_request”扩展提供 无效或不可接受的 OCSP 响应时,由客户端发送此警报 (参见 [RFC6066])。¶
希望进行 PSK 密钥建立,但客户端未提供 可接受的 PSK 身份时,由服务器发送此警报。 发送此警报是可选的;服务器也可以选择发送 “decrypt_error”警报,仅用于指明 PSK 身份无效。¶
服务器需要客户端证书, 但客户端未提供时,由服务器发送此警报。¶
在没有更具体的错误可用, 或发送方希望隐藏具体错误代码时, 发送此警报以指明错误条件。存在更具体错误时,实现应使用 更具体的错误。¶
客户端 “application_layer_protocol_negotiation”扩展只公布 服务器不支持的协议时,由服务器发送此警报 (参见 [RFC7301])。¶
TLS 握手建立一个或多个输入秘密值, 这些秘密值会组合起来生成实际使用的密钥材料, 具体如下所述。密钥派生过程同时纳入输入秘密值 和握手消息记录。请注意,由于握手 消息记录包含 Hello 消息中的随机值, 因此任何给定握手都将具有不同的流量秘密值, 即使使用相同的输入秘密值也是如此, 例如同一 PSK 用于多个连接时。¶
密钥派生过程使用为 HKDF [RFC5869] 定义的 HKDF-Extract 和 HKDF-Expand 函数,以及下文定义的函数:¶
HKDF-Expand-Label(Secret, Label, Context, Length) =
HKDF-Expand(Secret, HkdfLabel, Length)
¶
其中 HkdfLabel 规定如下:¶
struct {
uint16 length = Length;
opaque label<7..255> = "tls13 " + Label;
opaque context<0..255> = Context;
} HkdfLabel;
Derive-Secret(Secret, Label, Messages) =
HKDF-Expand-Label(Secret, Label,
Transcript-Hash(Messages), Hash.length)
¶
Transcript-Hash 和 HKDF 使用的 Hash 函数是密码套件的 哈希算法。 Hash.length 是其输出长度,以字节为单位。Messages 是所指明 握手消息的拼接结果,包括握手消息类型 和长度字段,但不包括记录层标头。请注意, 在某些情况下,会向 HKDF-Expand-Label 传递 长度为零的 Context(以“”表示)。 本文档规定的标签均为 ASCII 字符串, 且不包含尾随 NUL 字节。¶
使用“HKDF-Expand-Label”的任何 TLS 扩展, 都使用与其所用 TLS 版本关联的 HkdfLabel 定义。 与本规范一起使用时,这意味着 使用上文定义的 HkdfLabel;与 DTLS [RFC9147] 一起使用时, 则意味着使用 [RFC9147] 的第 5.9 节中定义的版本。¶
注意:使用常见哈希函数时,长度超过 12 个字符的任何标签 都需要额外迭代一次哈希函数进行计算。 本规范中的所有标签均已选择为不超过 此限制。¶
密钥通过 HKDF-Extract 和 Derive-Secret 函数, 从两个输入秘密值派生。添加新秘密值的一般模式 是使用 HKDF-Extract,将 Salt 设为当前秘密状态,将输入密钥材料(IKM)设为要添加的 新秘密值。在此 TLS 1.3 版本中,两个 输入秘密值为:¶
这将生成下图所示的密钥调度 (图 5)。 在此图中,采用以下格式约定:¶
HKDF-Extract 绘制为从上方接收 Salt 参数, 从左侧接收 IKM 参数,其输出位于下方, 输出名称位于右侧。¶
Derive-Secret 的 Secret 参数由入向箭头指明。 例如,Early Secret 是用于生成 client_early_traffic_secret 的 Secret。¶
“0”表示由 Hash.length 个值为零的字节组成的字符串。¶
注意:即使这些值被称为“main”秘密值, 密钥派生标签仍使用字符串“master”。这种不一致 是在保留兼容性的同时重命名这些值所造成的结果。¶
注意:此图并未显示所有叶子密钥,例如单独的 AEAD 密钥和 IV 密钥,而只显示从握手中派生的 第一组秘密值。¶
0
|
v
PSK --> HKDF-Extract = 早期秘密值
|
+-----> Derive-Secret(.,
| "ext binder" |
| "res binder",
| "")
| = binder_key
|
+-----> Derive-Secret(., "c e traffic",
| ClientHello)
| = client_early_traffic_secret
|
+-----> Derive-Secret(., "e exp master",
| ClientHello)
| = early_exporter_secret
v
Derive-Secret(., "derived", "")
|
非对称 v
共享秘密值 --> HKDF-Extract = 握手秘密值
|
+-----> Derive-Secret(., "c hs traffic",
| ClientHello...ServerHello)
| = client_handshake_traffic_secret
|
+-----> Derive-Secret(., "s hs traffic",
| ClientHello...ServerHello)
| = server_handshake_traffic_secret
v
Derive-Secret(., "derived", "")
|
v
0 --> HKDF-Extract = 主秘密值
|
+-----> Derive-Secret(., "c ap traffic",
| ClientHello...server Finished)
| = client_application_traffic_secret_0
|
+-----> Derive-Secret(., "s ap traffic",
| ClientHello...server Finished)
| = server_application_traffic_secret_0
|
+-----> Derive-Secret(., "exp master",
| ClientHello...server Finished)
| = exporter_secret
|
+-----> Derive-Secret(., "res master",
ClientHello...client Finished)
= resumption_secret
这里的一般模式是,图左侧所示的秘密值 只是没有上下文的原始熵,而右侧的 秘密值包含握手上下文,因此 无需附加上下文即可用于派生工作密钥。 请注意,即使使用相同秘密值,对 Derive-Secret 的不同 调用也可以采用不同的 Messages 参数。 在 0-RTT 交换中,Derive-Secret 使用四个不同的消息记录调用; 在仅使用 1-RTT 的交换中, 则使用三个不同的消息记录调用。¶
如果某个秘密值不可用,则使用由 Hash.length 个值为零的字节组成的 0 值。请注意,这并不表示跳过 轮次,因此如果未使用 PSK,Early Secret 仍然是 HKDF-Extract(0, 0)。计算 binder_key 时, 外部 PSK(在 TLS 之外配置的 PSK)使用标签 “ext binder”,恢复 PSK(作为先前握手恢复秘密值配置的 PSK) 使用“res binder”。不同标签可防止 将一种 PSK 替换为另一种。¶
根据服务器最终选择的 PSK,可能存在多个 Early Secret 值。客户端需要为每个可能的 PSK 计算 一个值;如果未选择任何 PSK,则还需要 计算与零 PSK 对应的 Early Secret。¶
从某个给定秘密值派生的所有值 计算完成后,该秘密值应被清除。¶
握手完成后,任一方都可以使用 第 4.7.3 节中定义的 KeyUpdate 握手消息更新其发送流量密钥。下一代流量密钥通过以下方式计算: 按本节所述,从 client_/server_application_traffic_secret_N 生成 client_/server_application_traffic_secret_N+1, 然后按照第 7.3 节所述重新派生流量密钥。¶
下一代 application_traffic_secret 按以下方式计算:¶
application_traffic_secret_N+1 =
HKDF-Expand-Label(application_traffic_secret_N,
"traffic upd", "", Hash.length)
¶
计算出 client_/server_application_traffic_secret_N+1 及其关联流量密钥后,实现应删除 client_/server_application_traffic_secret_N 及其关联流量密钥。¶
流量密钥材料由以下输入值生成:¶
流量密钥材料通过以下方式从输入流量秘密值生成:¶
[sender]_write_key = HKDF-Expand-Label(Secret, "key", "", key_length) [sender]_write_iv = HKDF-Expand-Label(Secret, "iv", "", iv_length)¶
[sender] 表示发送方。每种数据类别的 Secret 值 如下表所示。¶
| 数据类型 | 秘密值 |
|---|---|
| 0-RTT 应用数据和 EndOfEarlyData | client_early_traffic_secret |
| 初始握手 | [sender]_handshake_traffic_secret |
| 握手后消息和应用数据 | [sender]_application_traffic_secret_N |
警报使用发送时的当前发送密钥发送 (如果尚未建立此类密钥,则以明文发送)。 每当底层 Secret 发生变化时,都会重新计算所有流量密钥材料 (例如从握手密钥切换到应用数据密钥, 或执行密钥更新时)。¶
[RFC5705] 使用 TLS 伪随机函数(PRF)定义了 TLS 密钥材料导出器。 本文档以 HKDF 替代 PRF,因此 需要一种新构造。导出器接口保持不变。¶
导出器值按以下方式计算:¶
TLS-Exporter(label, context_value, key_length) =
HKDF-Expand-Label(Derive-Secret(Secret, label, ""),
"exporter", Hash(context_value), key_length)
¶
其中 Secret 为 early_exporter_secret 或 exporter_secret。除非应用明确指定,否则实现必须使用 exporter_secret。early_exporter_secret 定义用于 0-RTT 数据需要导出器的场景。 建议为早期导出器提供单独接口; 这样可以避免导出器用户在需要常规导出器时 意外使用早期导出器,反之亦然。¶
如果未提供上下文,则 context_value 长度为零。 因此,不提供上下文与提供空上下文计算得到相同的值。 这不同于早期 TLS 版本;在那些版本中,空上下文产生的 输出不同于缺少上下文的情况。截至本文档发布时, 没有任何已分配导出器标签同时在有上下文和无上下文的情况下使用。 未来规范不得定义允许同一标签同时使用空上下文 和无上下文的导出器用法。导出器的新用法应在所有导出器计算中提供 上下文,不过该值可以为空。¶
如第 2.3 节和附录 F.5所述,TLS 不为 0-RTT 数据提供固有的 重放保护。需要关注两类潜在威胁:¶
网络攻击者通过简单复制一个 0-RTT 数据 消息批次发起重放攻击。¶
网络攻击者利用客户端重试行为, 设法使服务器收到应用消息的多个副本。 此威胁在一定程度上已经存在,因为重视健壮性的客户端 会通过尝试重试请求来响应网络错误; 但如果客户端在握手完成后自动重新传输被拒绝的 0-RTT 数据, 0-RTT 会增加额外维度,因为攻击者可能使数据 在连接 1 上作为 0-RTT 数据处理, 并在连接 2 上作为 1-RTT 数据处理。¶
第一类攻击可以通过共享状态来防止,从而保证 0-RTT 数据最多只被接受一次。服务器应通过实现本节所述方法之一 或采用等效方式,提供这种级别的重放安全性。 不过可以理解的是,出于运维方面的考虑, 并非所有部署都会维护这种级别的状态。因此在正常运行中, 客户端不会知道服务器实际实现了哪些机制(如果有), 因而必须只发送它们认为 可以安全重放的早期数据。¶
除重放的直接影响外,还存在一类攻击, 即使通常被认为幂等的操作,也可能被大量重放利用 (例如时序攻击、资源限制耗尽等, 如附录 F.5所述)。 可通过确保每个 0-RTT 载荷只能被重放有限次数来缓解这些攻击。 服务器必须确保其任何实例 (无论是机器、线程,还是相关服务基础设施中的任何其他实体) 对同一次 0-RTT 握手最多只接受一次 0-RTT; 这会将重放次数限制为部署中的服务器实例数。 可以通过在本地记录最近收到的 ClientHello 数据并拒绝重复项, 或使用提供相同或更强保证的任何其他方法来实现此保证。 “每个服务器实例最多一次”的保证是最低要求; 可行时,服务器应进一步限制 0-RTT 重放。¶
第二类攻击无法在 TLS 层防止, 并且必须由应用处理(例如参见 [RFC8470],其中提供了 HTTP 与 0-RTT 数据结合使用的指导)。 请注意,客户端实现任何形式重试行为的应用 已经需要实现某种抗重放防御, 包括正确处理通过不同 TLS 连接到达的 重复应用层请求。¶
最简单的抗重放防御形式,是服务器只允许 每个会话票据使用一次。例如,服务器 可以维护所有尚未使用的有效票据的数据库,并在每个 票据使用时将其从数据库中删除。如果提供未知票据, 服务器随后会回退到完整握手。¶
如果票据并非自包含,而是数据库键, 并且对应 PSK 在使用后被删除,则使用 PSK 建立的连接 不仅具有抗重放保护,而且在数据库条目中的所有 PSK 副本 均被删除后还具有前向保密性。 当 PSK 在不使用非对称密钥交换的情况下使用时, 此机制也能增强 PSK 使用的安全性。¶
由于此机制要求在具有多个分布式服务器的环境中 在服务器节点之间共享会话数据库, 与自行加密的票据相比,可能很难实现较高比例的 成功 PSK 0-RTT 连接。 与会话数据库不同,即使没有一致存储, 会话票据仍可成功进行基于 PSK 的会话建立; 不过当允许 0-RTT 时,它们仍需要一致存储, 以对 0-RTT 数据实施抗重放保护, 具体见下一节。¶
另一种抗重放形式是记录从 ClientHello 派生的唯一值 (通常是随机值或 PSK 绑定器),并拒绝重复项。记录所有 ClientHello 会导致状态 无界增长,但服务器可以改为记录给定时间窗口内的 ClientHello, 并使用“obfuscated_ticket_age”确保票据不会在该窗口之外复用。¶
为实现此机制,收到 ClientHello 时,服务器 首先按照第 4.3.11 节所述验证 PSK 绑定器。 随后按下一节所述计算 expected_arrival_time;如果该时间不在记录窗口内, 则拒绝 0-RTT,并回退到 1-RTT 握手。¶
如果 expected_arrival_time 位于窗口内, 服务器就检查是否已记录匹配的 ClientHello。如果找到, 则使用“illegal_parameter”警报中止握手, 或接受 PSK 但拒绝 0-RTT。如果未找到匹配的 ClientHello, 则接受 0-RTT,并在 expected_arrival_time 仍位于窗口内期间存储该 ClientHello。 服务器也可以实现存在误报的数据存储,例如 Bloom 过滤器;在这种情况下,对于表面上的重放, 服务器必须拒绝 0-RTT,但不得中止握手。¶
服务器必须只从 ClientHello 中经过验证的部分 派生存储键。如果 ClientHello 包含多个 PSK 身份, 攻击者可以为偏好较低的身份创建具有不同绑定器值的多个 ClientHello, 并假设服务器不会验证该绑定器 (如第 4.3.11 节所建议)。 也就是说,如果客户端发送 PSK A 和 B,但服务器偏好 A, 攻击者可以更改 B 的绑定器,而不影响 A 的绑定器。如果 B 的绑定器是存储键的一部分, 则此 ClientHello 不会显示为重复项, 从而导致该 ClientHello 被接受,并可能 引发诸如重放缓存污染之类的副作用; 不过任何 0-RTT 数据都无法解密, 因为它将使用不同的密钥。 如果使用经过验证的绑定器或 ClientHello.random 作为存储键, 则无法实施此攻击。¶
由于此机制不需要存储所有尚未使用的票据, 因而可能更容易在具有高恢复率和高 0-RTT 使用率的 分布式系统中实现;代价是,由于难以可靠地 存储和检索收到的 ClientHello 消息, 抗重放防御可能较弱。 在许多此类系统中,对所有收到的 ClientHello 进行全局一致存储并不现实。 在这种情况下,最佳抗重放保护是 让单个存储区域对给定票据具有权威性, 并在任何其他区域拒绝该票据的 0-RTT。 这种方法可以防止攻击者进行简单重放, 因为只有一个区域会接受 0-RTT 数据。 较弱的设计是为每个区域实现独立存储, 但允许任何区域接受 0-RTT。 此方法将重放次数限制为每个区域一次。 当然,无论采用哪种设计,应用消息重复仍然可能发生。¶
实现刚启动时,只要其记录窗口的任何部分 与启动时间重叠,就应 拒绝 0-RTT。否则,它们可能接受 最初在该时间段内发送的重放消息。¶
注意:如果客户端时钟运行速度远快于服务器时钟, 则收到的 ClientHello 可能位于窗口未来一侧之外; 在这种情况下,它可能被接受用于 1-RTT,从而导致客户端重试, 随后又在稍后变得可接受用于 0-RTT。 这是第 8 节所述第二类攻击的另一种变体。¶
由于 ClientHello 指明客户端发送它的时间, 因此可以高效判断 ClientHello 是否很可能在合理的近期发送, 并且只对此类 ClientHello 接受 0-RTT; 否则回退到 1-RTT 握手。 这对于第 8.2 节所述的 ClientHello 存储机制是必要的, 因为否则服务器需要存储无限数量的 ClientHello; 对于自包含的一次性票据,这也是一种有用的优化, 因为它可以高效拒绝无法用于 0-RTT 的 ClientHello。¶
为实现此机制,服务器需要存储 服务器生成会话票据的时间,并加上客户端与服务器之间 往返时间的估计值。即:¶
adjusted_creation_time = creation_time + estimated_RTT
¶
此值可以编码到票据中,从而无需 为每个尚未使用的票据保留状态。服务器可以通过从客户端 “pre_shared_key”扩展中的“obfuscated_ticket_age”参数减去票据的 “ticket_age_add”值,确定客户端所认为的票据期限。 服务器可以按以下方式确定 ClientHello 的 expected_arrival_time:¶
expected_arrival_time = adjusted_creation_time + clients_ticket_age¶
收到新的 ClientHello 时, 将 expected_arrival_time 与服务器当前墙上时钟时间进行比较; 如果二者相差超过某个数值,则拒绝 0-RTT, 不过仍可允许 1-RTT 握手完成。¶
存在若干潜在误差来源,可能导致 expected_arrival_time 与测量时间不匹配。 客户端和服务器时钟速率的变化通常可能很小, 不过绝对时间可能相差很大。 网络传播延迟最有可能导致 合法经过时间值不匹配。NewSessionTicket 和 ClientHello 消息都可能被重新传输, 因而产生延迟,而这种延迟可能被 TCP 隐藏。 对于互联网上的客户端,这意味着窗口 大约需要十秒,以考虑时钟误差和测量变化; 其他部署场景可能具有不同需求。 时钟偏差分布并不对称,因此最佳折衷方案 可能需要采用不对称的允许误差范围。¶
请注意,仅靠新鲜度检查不足以防止重放, 因为它无法检测误差窗口内发生的重放; 根据带宽和系统容量, 现实环境中该窗口内可能包含数十亿次重放。 此外,新鲜度检查只在收到 ClientHello 时执行, 而不会在收到后续早期应用数据记录时执行。 接受早期数据后,记录可能继续以流式方式 发送到服务器,持续更长时间。¶
在没有应用配置文件标准另有规定的情况下:¶
符合 TLS 的应用必须 实现 TLS_AES_128_GCM_SHA256 [GCM] 密码套件,并且应实现 TLS_AES_256_GCM_SHA384 [GCM] 和 TLS_CHACHA20_POLY1305_SHA256 [RFC8439] 密码套件 (参见附录 B.4)。¶
符合 TLS 的应用必须支持使用 rsa_pkcs1_sha256(用于证书)、rsa_pss_rsae_sha256(用于 CertificateVerify 和证书)以及 ecdsa_secp256r1_sha256 的数字签名。符合 TLS 的应用必须支持使用 secp256r1(NIST P-256)进行密钥交换, 并且应支持使用 X25519 [RFC7748] 进行密钥交换。¶
在没有应用配置文件标准另有规定的情况下, 符合 TLS 的应用必须实现以下 TLS 扩展:¶
提供适用功能时,所有实现必须发送并使用这些 扩展:¶
所有 ClientHello、ServerHello 和 HelloRetryRequest 消息都要求包含“supported_versions”。¶
证书身份验证要求使用 “signature_algorithms”。¶
使用 DHE 或 ECDHE 密钥交换的 ClientHello 消息要求包含“supported_groups”。¶
DHE 或 ECDHE 密钥交换要求使用 “key_share”。¶
PSK 密钥协商要求使用 “pre_shared_key”。¶
PSK 密钥协商要求使用 “psk_key_exchange_modes”。¶
如果 ClientHello 包含 主体中含 0x0304 的“supported_versions”扩展, 则认为客户端正尝试使用本规范进行协商。 此类 ClientHello 消息必须满足以下要求:¶
如果不包含“pre_shared_key”扩展,则必须同时包含 “signature_algorithms”扩展和“supported_groups”扩展。¶
如果包含“supported_groups”扩展,则必须同时包含 “key_share”扩展,反之亦然。允许空的 KeyShare.client_shares 列表。¶
服务器收到不符合这些 要求的 ClientHello 时,必须使用“missing_extension” 警报中止握手。¶
此外,所有实现必须支持 能够使用“server_name”扩展的应用使用该扩展。 服务器可以要求客户端发送有效的“server_name”扩展。 要求此扩展的服务器应在收到缺少“server_name”扩展的 ClientHello 时,使用“missing_extension”警报终止连接。¶
本节描述 TLS 端点和中间盒必须遵循的不变量。 这些要求也适用于早期 TLS 版本。¶
TLS 的设计目标是能够安全且兼容地扩展。 较新的客户端或服务器在与较新的对等方通信时, 应协商双方共同支持且最为偏好的参数。 TLS 握手提供降级保护:在不终止 TLS 的情况下, 在较新客户端和较新服务器之间转发流量的中间盒 应无法影响握手(参见附录 F.1)。 与此同时,不同部署的更新速率不同, 因而较新的客户端或服务器可以继续支持旧参数, 从而与旧端点互操作。¶
为使此机制正常工作,实现必须正确处理 可扩展字段:¶
发送 ClientHello 的客户端必须 支持其中公布的所有非 GREASE [RFC8701] 参数。 否则,服务器可能选择其中一个参数,导致 无法互操作。¶
收到 ClientHello 的服务器必须 正确忽略所有无法识别的密码套件、扩展和其他参数。 否则,它可能无法与较新的客户端互操作。 在 TLS 1.3 中,收到 CertificateRequest 或 NewSessionTicket 的客户端 也必须忽略所有无法识别的扩展。¶
终止 TLS 连接的中间盒必须充当符合要求的 TLS 服务器(面向原始客户端),包括拥有 客户端愿意接受的证书;同时还必须充当符合要求的 TLS 客户端 (面向原始服务器),包括验证原始服务器的证书。 特别是,它必须生成自己的 ClientHello, 其中只包含它理解的参数,并且必须生成新的 ServerHello 随机值,而不是转发端点的值。¶
请注意,TLS 的协议要求和安全分析只分别适用于 两个连接。安全部署 TLS 终止器需要 额外的安全考虑,这超出了本文档的范围。¶
转发其不理解的 ClientHello 参数的中间盒 不得处理该 ClientHello 之后的任何消息。 它必须原样转发所有后续流量。 否则,它可能无法与较新的客户端和服务器互操作。¶
被转发的 ClientHello 可能包含中间盒不支持的功能公告, 因此响应可能包含中间盒无法识别的未来 TLS 扩展。 这些扩展可以任意更改 ClientHello 之后的任何消息。 特别是,ServerHello 中发送的值可能发生变化, ServerHello 格式可能发生变化, TLSCiphertext 格式也可能发生变化。¶
TLS 1.3 的设计受到广泛部署但不符合要求的 TLS 中间盒限制(参见附录 E.4); 但这并不会放宽这些不变量。 这些中间盒仍然不符合要求。¶
本文档使用了若干最初在 [RFC4346] 中创建,并在 [RFC8446] 和 [RFC8447] 中更新的注册表。[RFC8446]、[RFC8447] 与本文档之间的变更在第 11.1 节中说明。 IANA 已将对这些 RFC 的引用替换为对本文档的引用。注册表及其 分配策略如下:¶
TLS Cipher Suites 注册表:首字节位于 0-254(十进制)范围内的值通过“需要规范”分配 [RFC8126]。 首字节为 255(十进制)的值预留用于专用用途 [RFC8126]。¶
IANA 已将附录 B.4中列出的密码套件添加到 注册表中。“Value”和“Description”列取自该表。 对于这些密码套件,“DTLS-OK”和“Recommended”列均标记为“Y”。¶
TLS Alerts 注册表:未来值通过“标准 操作”分配 [RFC8126]。IANA 已使用 附录 B.2中的值填充此注册表。对于所有这些值, “DTLS-OK”列均标记为“Y”。 标记为“_RESERVED”的值带有 说明其先前用途的注释。¶
TLS HandshakeType 注册表:未来值通过 “标准操作”分配 [RFC8126]。IANA 已更新此注册表, 将条目 4 从“NewSessionTicket”重命名为“new_session_ticket”, 并使用附录 B.3中的值填充此注册表。 对于所有这些值,“DTLS-OK”列均标记为“Y”。 标记为“_RESERVED”的值带有说明其先前或 临时用途的注释。¶
本文档还使用了最初在 [RFC4366] 中创建的 TLS ExtensionType Values 注册表。IANA 已更新该注册表以引用本文档。 对该注册表的变更如下:¶
IANA 已按以下方式更新注册策略:¶
首字节位于 0-254(十进制)范围内的值 通过“需要规范”分配 [RFC8126]。 首字节为 255(十进制)的值预留用于专用用途 [RFC8126]。¶
IANA 已更新此注册表,以包含 “key_share”、“pre_shared_key”、“psk_key_exchange_modes”、 “early_data”、“cookie”、“supported_versions”、 “certificate_authorities”、“oid_filters”、“post_handshake_auth”和“signature_algorithms_cert” 扩展,其值采用本文档中定义的值,“Recommended”值为“Y”。¶
IANA 已更新此注册表,添加一个“TLS 1.3”列,其中列出扩展可以出现的消息。此列 最初根据第 4.3 节中的表格填充; 未在其中列出的任何扩展均标记为“-”,以指明 TLS 1.3 不使用该扩展。¶
本文档更新了 TLS Certificate Types 注册表中的两个条目, 该注册表最初在 [RFC6091] 中创建,并在 [RFC8447] 中更新。IANA 已 将值 1 的条目更新为名称“OpenPGP_RESERVED”、 “Recommended”值“N”,以及注释“用于 TLS 1.3 之前的 TLS 版本。”IANA 已将值 0 的条目更新为名称 “X509”、“Recommended”值“Y”,以及注释“在 TLS 1.3 之前为 X.509”。¶
本文档更新了 TLS Certificate Status Types 注册表中的一个条目,该注册表最初在 [RFC6961] 中创建。IANA 已 将值 2 的条目更新为名称“ocsp_multi_RESERVED”,并添加注释“用于 TLS 1.3 之前的 TLS 版本”。¶
本文档更新了 TLS Supported Groups 注册表中的两个条目(该注册表由 [RFC4492] 以不同名称创建;目前由 [RFC8422] 维护,并由 [RFC7919] 和 [RFC8447] 更新)。值 29 和 30(x25519 和 x448)的条目已更新为同时 引用本文档。¶
此外,本文档定义了两个由 IANA 维护的新注册表:¶
TLS SignatureScheme 注册表:首字节位于 0-253(十进制)范围内的值通过“需要规范”分配 [RFC8126]。 首字节为 254 或 255(十进制)的值预留用于专用用途 [RFC8126]。首字节 位于 0-6 范围内,或第二字节位于 0-3 范围内且当前尚未分配的值, 预留用于向后兼容。 此注册表具有“Recommended”列。 注册表最初使用第 4.3.3 节中描述的值填充。以下值标记为 “Recommended”:ecdsa_secp256r1_sha256、ecdsa_secp384r1_sha384、 rsa_pss_rsae_sha256、rsa_pss_rsae_sha384、rsa_pss_rsae_sha512、 rsa_pss_pss_sha256、rsa_pss_pss_sha384、rsa_pss_pss_sha512 和 ed25519。¶
TLS PskKeyExchangeMode 注册表:位于 0-253(十进制)范围内的值通过“需要规范”分配 [RFC8126]。值 254 和 255 (十进制)预留用于专用用途 [RFC8126]。 此注册表具有“Recommended”列。注册表最初 使用 psk_ke(0)和 psk_dhe_ke(1)填充。二者均标记为 “Recommended”。¶
TLS ExtensionType Values、TLS Cipher Suites、TLS Supported Groups、TLS Exporter Labels、TLS HashAlgorithm、TLS Certificate Types、 TLSPskKeyExchangeMode 和 TLS SignatureScheme 注册表均具有 “Recommended”列。有关“Recommended”列值和设置的更多信息, 请参阅 [RFC9847] 第 3 节。¶
IANA 已将 IANA 注册表中对 [RFC8446] 的所有引用 更新为对本文档的引用。¶
IANA 已更新 TLS SignatureScheme 和 PskKeyExchangeMode 注册表的 “Recommended”列流程,使其与 [RFC9847] 保持一致。¶
IANA 已将 TLS ExtensionType Values 注册表中的 “extended_master_secret”值重命名为“extended_main_secret”。¶
IANA 已在 TLS Alerts 注册表中为“general_error” 警报创建一个值,其值采用第 6 节中给出的值。¶
IANA 已为 TLS ExtensionType Values 注册表中的 status_request 和 supported_groups 条目添加对本文档的引用。¶
IANA 已将 cached_info 在“TLS 1.3”列中的 TLS ExtensionType 值 更新为“CH, EE”。¶
本附录概述客户端和服务器握手的合法状态转换。 状态名称(全部使用大写字母,例如 START)没有正式含义,仅为便于 理解而提供。只在某些情况下执行的操作 以 [] 表示。记法“K_{send,recv} = foo”表示“将发送/接收 密钥设置为给定密钥”。¶
START <----+
发送 ClientHello | | 接收 HelloRetryRequest
[K_send = 早期数据] | |
v |
+-> WAIT_SH ----+
| | 接收 ServerHello
| | K_recv = 握手
可以 | V
发送 | WAIT_EE
早期 | | 接收 EncryptedExtensions
数据 | +---------+-------+
| 使用 | | 使用证书
| PSK | v
| | WAIT_CERT_CR
| | 接收 | | 接收 CertificateRequest
| | Certificate | v
| | | WAIT_CERT
| | | | 接收 Certificate
| | v v
| | WAIT_CV
| | | 接收 CertificateVerify
| +-> WAIT_FINISHED <-+
| | 接收 Finished
+-> | [发送 EndOfEarlyData]
| K_send = 握手
| [发送 Certificate [+ CertificateVerify]]
从此处后 | 发送 Finished
可发送应用数据 --> | K_send = K_recv = 应用
v
CONNECTED
¶
请注意,按照上图所示的转换,客户端可能会以明文 或使用早期数据密钥发送由 ServerHello 之后的消息引起的警报。 如果客户端需要发送此类警报,则在可能的情况下应 先将密钥更新为握手密钥。¶
START <-----+
接收 ClientHello | | 发送 HelloRetryRequest
v |
RECVD_CH ----+
| 选择参数
v
NEGOTIATED
| 发送 ServerHello
| K_send = 握手
| 发送 EncryptedExtensions
| [发送 CertificateRequest]
可发送 | [发送 Certificate + CertificateVerify]
应用数据 | 发送 Finished
从此处 --> | K_send = 应用
+--------+--------+
无 0-RTT | | 0-RTT
| |
K_recv = 握手 | | K_recv = 早期数据
[跳过解密错误] | +------> WAIT_EOED --+
| | 接收 | | 接收 EndOfEarlyData
| | 早期数据 | | K_recv = 握手
| +------------+ |
| |
+-> WAIT_FLIGHT2 <--------+
|
+--------+--------+
无身份验证 | | 基于证书的客户端身份验证
| |
| v
| WAIT_CERT
| 接收 | | 接收 Certificate
| 空的 | v
| Certificate | WAIT_CV
| | | 接收
| v | CertificateVerify
+-> WAIT_FINISHED <---+
| 接收 Finished
| K_recv = 应用
v
CONNECTED
¶
本附录提供规范性协议类型以及常量的定义。 列为“_RESERVED”的值曾用于早期 TLS 版本, 此处为完整起见而列出。TLS 1.3 实现不得发送这些值, 但可能会从旧版 TLS 实现接收到这些值。¶
enum {
invalid(0),
change_cipher_spec(20),
alert(21),
handshake(22),
application_data(23),
(255)
} ContentType;
struct {
ContentType type;
ProtocolVersion legacy_record_version;
uint16 length;
opaque fragment[TLSPlaintext.length];
} TLSPlaintext;
struct {
opaque content[TLSPlaintext.length];
ContentType type;
uint8 zeros[length_of_padding];
} TLSInnerPlaintext;
struct {
ContentType opaque_type = application_data; /* 23 */
ProtocolVersion legacy_record_version = 0x0303; /* TLS v1.2 */
uint16 length;
opaque encrypted_record[TLSCiphertext.length];
} TLSCiphertext;
¶
enum { warning(1), fatal(2), (255) } AlertLevel;
enum {
close_notify(0),
unexpected_message(10),
bad_record_mac(20),
decryption_failed_RESERVED(21),
record_overflow(22),
decompression_failure_RESERVED(30),
handshake_failure(40),
no_certificate_RESERVED(41),
bad_certificate(42),
unsupported_certificate(43),
certificate_revoked(44),
certificate_expired(45),
certificate_unknown(46),
illegal_parameter(47),
unknown_ca(48),
access_denied(49),
decode_error(50),
decrypt_error(51),
export_restriction_RESERVED(60),
protocol_version(70),
insufficient_security(71),
internal_error(80),
inappropriate_fallback(86),
user_canceled(90),
no_renegotiation_RESERVED(100),
missing_extension(109),
unsupported_extension(110),
certificate_unobtainable_RESERVED(111),
unrecognized_name(112),
bad_certificate_status_response(113),
bad_certificate_hash_value_RESERVED(114),
unknown_psk_identity(115),
certificate_required(116),
general_error(117),
no_application_protocol(120),
(255)
} AlertDescription;
struct {
AlertLevel level;
AlertDescription description;
} Alert;
¶
enum {
hello_request_RESERVED(0),
client_hello(1),
server_hello(2),
hello_verify_request_RESERVED(3),
new_session_ticket(4),
end_of_early_data(5),
hello_retry_request_RESERVED(6),
encrypted_extensions(8),
certificate(11),
server_key_exchange_RESERVED(12),
certificate_request(13),
server_hello_done_RESERVED(14),
certificate_verify(15),
client_key_exchange_RESERVED(16),
finished(20),
certificate_url_RESERVED(21),
certificate_status_RESERVED(22),
supplemental_data_RESERVED(23),
key_update(24),
message_hash(254),
(255)
} HandshakeType;
struct {
HandshakeType msg_type; /* 握手类型 */
uint24 length; /* 消息中的剩余字节 */
select (Handshake.msg_type) {
case client_hello: ClientHello;
case server_hello: ServerHello;
case end_of_early_data: EndOfEarlyData;
case encrypted_extensions: EncryptedExtensions;
case certificate_request: CertificateRequest;
case certificate: Certificate;
case certificate_verify: CertificateVerify;
case finished: Finished;
case new_session_ticket: NewSessionTicket;
case key_update: KeyUpdate;
};
} Handshake;
¶
uint16 ProtocolVersion;
opaque Random[32];
uint8 CipherSuite[2]; /* 密码套件选择器 */
struct {
ProtocolVersion legacy_version = 0x0303; /* TLS v1.2 */
Random random;
opaque legacy_session_id<0..32>;
CipherSuite cipher_suites<2..2^16-2>;
opaque legacy_compression_methods<1..2^8-1>;
Extension extensions<7..2^16-1>;
} ClientHello;
struct {
ProtocolVersion legacy_version = 0x0303; /* TLS v1.2 */
Random random;
opaque legacy_session_id_echo<0..32>;
CipherSuite cipher_suite;
uint8 legacy_compression_method = 0;
Extension extensions<6..2^16-1>;
} ServerHello;
struct {
ExtensionType extension_type;
opaque extension_data<0..2^16-1>;
} Extension;
enum {
server_name(0), /* RFC 6066, 9261 */
status_request(5), /* RFC 6066, 9846 */
supported_groups(10), /* RFC 7919, 9846 */
signature_algorithms(13), /* RFC 9846 */
use_srtp(14), /* RFC 5764 */
heartbeat(15), /* RFC 6520 */
application_layer_protocol_negotiation(16), /* RFC 7301 */
client_certificate_type(19), /* RFC 7250 */
server_certificate_type(20), /* RFC 7250 */
padding(21), /* RFC 7685 */
compress_certificate(27), /* RFC 8879 */
record_size_limit(28), /* RFC 8449 */
delegated_credential(34), /* RFC 9345 */
supported_ekt_ciphers(39), /* RFC 8870 */
pre_shared_key(41), /* RFC 9846 */
early_data(42), /* RFC 9846 */
supported_versions(43), /* RFC 9846 */
cookie(44), /* RFC 9846 */
psk_key_exchange_modes(45), /* RFC 9846 */
certificate_authorities(47), /* RFC 9846 */
oid_filters(48), /* RFC 9846 */
post_handshake_auth(49), /* RFC 9846 */
signature_algorithms_cert(50), /* RFC 9846 */
key_share(51), /* RFC 9846 */
transparency_info(52), /* RFC 9162 */
external_id_hash(55), /* RFC 8844 */
external_session_id(56), /* RFC 8844 */
quic_transport_parameters(57), /* RFC 9001 */
ticket_request(58), /* RFC 9149 */
ech_outer_extensions(64768), /* RFC 9849 */
encrypted_client_hello(65037), /* RFC 9849 */
(65535)
} ExtensionType;
struct {
NamedGroup group;
opaque key_exchange<1..2^16-1>;
} KeyShareEntry;
struct {
KeyShareEntry client_shares<0..2^16-1>;
} KeyShareClientHello;
struct {
NamedGroup selected_group;
} KeyShareHelloRetryRequest;
struct {
KeyShareEntry server_share;
} KeyShareServerHello;
struct {
uint8 legacy_form = 4;
opaque X[coordinate_length];
opaque Y[coordinate_length];
} UncompressedPointRepresentation;
enum { psk_ke(0), psk_dhe_ke(1), (255) } PskKeyExchangeMode;
struct {
PskKeyExchangeMode ke_modes<1..255>;
} PskKeyExchangeModes;
struct {} Empty;
struct {
select (Handshake.msg_type) {
case new_session_ticket: uint32 max_early_data_size;
case client_hello: Empty;
case encrypted_extensions: Empty;
};
} EarlyDataIndication;
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;
¶
struct {
select (Handshake.msg_type) {
case client_hello:
ProtocolVersion versions<2..254>;
case server_hello: /* 以及 HelloRetryRequest */
ProtocolVersion selected_version;
};
} SupportedVersions;
¶
enum {
/* RSASSA-PKCS1-v1_5 算法 */
rsa_pkcs1_sha256(0x0401),
rsa_pkcs1_sha384(0x0501),
rsa_pkcs1_sha512(0x0601),
/* ECDSA 算法 */
ecdsa_secp256r1_sha256(0x0403),
ecdsa_secp384r1_sha384(0x0503),
ecdsa_secp521r1_sha512(0x0603),
/* 公钥 OID 为 rsaEncryption 的 RSASSA-PSS 算法 */
rsa_pss_rsae_sha256(0x0804),
rsa_pss_rsae_sha384(0x0805),
rsa_pss_rsae_sha512(0x0806),
/* EdDSA 算法 */
ed25519(0x0807),
ed448(0x0808),
/* 公钥 OID 为 RSASSA-PSS 的 RSASSA-PSS 算法 */
rsa_pss_pss_sha256(0x0809),
rsa_pss_pss_sha384(0x080a),
rsa_pss_pss_sha512(0x080b),
/* 旧式算法 */
rsa_pkcs1_sha1(0x0201),
ecdsa_sha1(0x0203),
/* 预留码位 */
obsolete_RESERVED(0x0000..0x0200),
dsa_sha1_RESERVED(0x0202),
obsolete_RESERVED(0x0204..0x0400),
dsa_sha256_RESERVED(0x0402),
obsolete_RESERVED(0x0404..0x0500),
dsa_sha384_RESERVED(0x0502),
obsolete_RESERVED(0x0504..0x0600),
dsa_sha512_RESERVED(0x0602),
obsolete_RESERVED(0x0604..0x06FF),
private_use(0xFE00..0xFFFF),
(0xFFFF)
} SignatureScheme;
struct {
SignatureScheme supported_signature_algorithms<2..2^16-2>;
} SignatureSchemeList;
¶
enum {
unallocated_RESERVED(0x0000),
/* 椭圆曲线组(ECDHE) */
obsolete_RESERVED(0x0001..0x0016),
secp256r1(0x0017), secp384r1(0x0018), secp521r1(0x0019),
obsolete_RESERVED(0x001A..0x001C),
x25519(0x001D), x448(0x001E),
/* 有限域组(DHE) */
ffdhe2048(0x0100), ffdhe3072(0x0101), ffdhe4096(0x0102),
ffdhe6144(0x0103), ffdhe8192(0x0104),
/* 预留码位 */
ffdhe_private_use(0x01FC..0x01FF),
ecdhe_private_use(0xFE00..0xFEFF),
obsolete_RESERVED(0xFF01..0xFF02),
(0xFFFF)
} NamedGroup;
struct {
NamedGroup named_group_list<2..2^16-1>;
} NamedGroupList;
¶
“obsolete_RESERVED”范围内的值曾用于早期 TLS 版本,TLS 1.3 实现不得提供或协商这些值。 这些废弃曲线存在各种已知或理论上的弱点, 或者使用率极低;在某些情况下,其使用仅源于 无意的服务器配置问题。它们不再被认为适合 一般用途,应假定其可能不安全。 此处规定的曲线集合足以与当前部署且配置正确的 所有 TLS 实现互操作。¶
opaque DistinguishedName<1..2^16-1>;
struct {
DistinguishedName authorities<3..2^16-1>;
} CertificateAuthoritiesExtension;
struct {
opaque certificate_extension_oid<1..2^8-1>;
opaque certificate_extension_values<0..2^16-1>;
} OIDFilter;
struct {
OIDFilter filters<0..2^16-1>;
} OIDFilterExtension;
struct {} PostHandshakeAuth;
struct {
Extension extensions<0..2^16-1>;
} EncryptedExtensions;
struct {
opaque certificate_request_context<0..2^8-1>;
Extension extensions<0..2^16-1>;
} CertificateRequest;
¶
enum {
X509(0),
OpenPGP_RESERVED(1),
RawPublicKey(2),
(255)
} CertificateType;
struct {
select (certificate_type) {
case RawPublicKey:
/* 来自 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;
struct {
SignatureScheme algorithm;
opaque signature<0..2^16-1>;
} CertificateVerify;
struct {
opaque verify_data[Hash.length];
} Finished;
¶
密码套件定义与 HKDF 一起使用的 AEAD 算法和哈希 算法组合。 密码套件名称遵循以下命名约定:¶
CipherSuite TLS_AEAD_HASH = VALUE;¶
| 组成部分 | 内容 |
|---|---|
| TLS | 字符串“TLS” |
| AEAD | 用于记录 保护的 AEAD 算法 |
| HASH | 与 HKDF 和 Transcript-Hash 一起使用的哈希算法 |
| VALUE | 分配给此 密码套件的双字节 ID |
本规范定义以下与 TLS 1.3 一起使用的密码套件。¶
| 描述 | 值 |
|---|---|
| TLS_AES_128_GCM_SHA256 | {0x13,0x01} |
| TLS_AES_256_GCM_SHA384 | {0x13,0x02} |
| TLS_CHACHA20_POLY1305_SHA256 | {0x13,0x03} |
| TLS_AES_128_CCM_SHA256 | {0x13,0x04} |
| TLS_AES_128_CCM_8_SHA256 | {0x13,0x05} |
对应的 AEAD 算法 AEAD_AES_128_GCM、AEAD_AES_256_GCM 和 AEAD_AES_128_CCM 定义于 [RFC5116]。 AEAD_CHACHA20_POLY1305 定义于 [RFC8439]。AEAD_AES_128_CCM_8 定义于 [RFC6655]。对应的 哈希算法定义于 [SHS]。¶
尽管 TLS 1.3 与早期 TLS 版本使用相同的密码套件空间, 但 TLS 1.3 密码套件的定义方式不同,仅规定 对称密码,因此不能用于 TLS 1.2。同样, TLS 1.2 及更低版本的密码套件不能用于 TLS 1.3。¶
TLS 协议无法防止许多常见的安全错误。本附录 提供若干建议以帮助实现者。 [RFC8448] 提供 TLS 1.3 握手的测试向量。¶
TLS 需要密码学安全的伪随机数生成器 (CSPRNG)。 大多数操作系统都提供性能良好且安全性适当的 CSPRNG, 也可以从密码学库中获取。 建议优先使用现有 CSPRNG 实现,而不是自行创建新实现。 目前已有许多足够安全的密码学库, 且使用许可条件友好。如果这些库仍不能满足要求, [RFC4086] 提供了随机值生成指南。¶
TLS 在以下方面使用随机值:(1)ClientHello 和 ServerHello 中 公开 Random 值等公共协议字段;(2)生成 密钥材料。对于正常工作的 CSPRNG, 这不会构成安全问题,因为无法根据其输出确定 CSPRNG 状态。但是,对于存在缺陷的 CSPRNG, 攻击者可能利用公开输出来确定 CSPRNG 内部状态,从而预测密钥材料, 如 [CHECKOWAY] 和 [DSA-1571-1] 中所述。¶
实现可以通过使用不同的 CSPRNG 分别生成公共值和 私有值,为抵御 此类攻击提供额外安全性。¶
[RFC8937] 描述了 安全协议实现使用长期私钥和确定性签名函数 增强其(伪)随机数生成器的方法。 这可以改善存在缺陷或以其他方式遭到破坏的 随机数生成器所产生的随机性。¶
实现负责验证证书的完整性, 并且通常应支持证书撤销消息;参见第 4.5.1.1 节。如果应用配置文件没有给出具体 指示,则应始终验证证书,以确保其由受信任的证书颁发机构 (CA)正确签名。选择和添加信任锚时应非常谨慎。 用户应能够查看有关证书和信任锚的信息。 应用还应强制执行最小和最大密钥大小。例如, 包含弱于 2048 位 RSA 或 224 位 ECDSA 的密钥或签名的认证路径,不适合用于安全应用。¶
请注意,在某些协议中,客户端和服务器模式使用相同 证书是一种常见做法。对此场景尚未进行 广泛分析,高层协议有责任确保 在这种情况下高层语义不存在歧义。¶
实现经验表明,早期 TLS 规范的某些部分 不易理解,并已成为互操作性和安全问题的来源。 本文档已澄清其中许多领域,但本附录仍简要列出 实现者需要特别注意的最重要事项。¶
TLS 协议问题:¶
是否正确处理被分割到 多条 TLS 记录中的握手消息(参见第 5.1 节)?是否正确处理 ClientHello 被分割为多个小片段等边界情况? 是否会分割超过最大片段大小的握手消息? 尤其是,Certificate 和 CertificateRequest 握手消息可能大到需要分割。 可以使用 [RFC8879] 中定义的证书压缩 降低发生分割的风险。¶
是否已确保在所有支持 TLS 1.3 或更高版本的 可能配置中,完全移除对 SSL、RC4、EXPORT 密码 和 MD5(通过“signature_algorithms”扩展)的所有支持, 并且尝试使用这些废弃功能时会正确失败 (参见附录 E)?¶
是否正确处理 ClientHello 中的 TLS 扩展, 包括未知扩展?¶
服务器请求客户端证书但没有 合适证书可用时,是否正确发送空的 Certificate 消息,而不是省略整个消息(参见 第 4.5.1 节)?¶
处理 AEAD-Decrypt 产生的明文片段, 并从末尾扫描 ContentType 时, 如果对等方发送了全部为零的畸形明文, 是否能避免扫描越过明文开头?¶
是否正确忽略 ClientHello 中无法识别的密码套件 (第 4.2.2 节)、问候扩展 (第 4.3 节)、命名组 (第 4.3.7 节)、密钥共享值 (第 4.3.8 节)、 支持的版本(第 4.3.1 节) 和签名算法(第 4.3.3 节)?¶
作为服务器,对于支持兼容的非对称密钥交换组, 但未在“key_share”扩展中预先提供该组的客户端, 是否会发送 HelloRetryRequest?作为客户端,是否能正确处理 服务器发来的 HelloRetryRequest?¶
密码学细节:¶
TLS 客户端是否检查服务器发送的 Diffie-Hellman 参数是否可接受(参见第 4.3.8.1 节)?¶
生成 Diffie-Hellman 私有值、ECDSA“k”参数 和其他安全关键值时,是否使用强健且最重要的是正确播种的随机数 生成器(参见附录 C.1)? 建议实现按照 [RFC6979] 的规定实现“确定性 ECDSA”。 请注意,确定性 ECDSA 和 EdDSA 等纯确定性椭圆曲线密码学 (ECC)签名,在易于物理接触的物联网(IoT)设备中, 可能容易受到某些侧信道攻击和故障注入攻击。¶
是否将 Diffie-Hellman 公钥值和共享秘密值 用零填充至组大小(参见第 4.3.8.1 节和第 7.4.1 节)?¶
客户端不应在多个连接中复用同一票据。 复用票据会使被动观察者能够关联不同连接。 签发票据的服务器应至少提供与客户端可能使用的 连接数相同数量的票据;例如,使用 HTTP/1.1 [RFC9112] 的 Web 浏览器可能会向 服务器打开六个连接。服务器应 在每个连接上签发新票据。这可确保客户端在创建新连接时 始终能够使用新票据。¶
向服务器提供票据还会使服务器能够关联 不同连接。这与是否复用票据无关。 客户端应用不应跨本应互不关联的连接提供票据。 例如,[FETCH] 定义了网络分区键, 用于分隔 Web 浏览器中的缓存查找。¶
建议选择外部身份标签时, 不要使其提供有关用户身份的额外信息。 例如,如果标签包含电子邮件地址, 则被动攻击者可以轻易据此识别用户, 而客户端的 Certificate 是加密的。可通过多种方式 避免这种风险,包括:(1)使用随机身份标签; (2)使用服务器已知的密钥预先加密身份;或(3) 使用加密客户端问候 [RFC9849]。¶
如果外部 PSK 身份用于多个连接, 外部观察者通常能够跨连接跟踪 客户端和/或服务器。 使用加密客户端问候 [RFC9849] 可以缓解此风险;轮换或加密 PSK 身份的 TLS 外部机制 也可以缓解此风险。¶
早期 TLS 版本提供了基于匿名 Diffie-Hellman、 明确不进行身份验证的密码套件。这些模式已在 TLS 1.3 中弃用。 不过,仍可通过多种方法协商不提供 可验证服务器身份验证的参数,包括:¶
单独使用任一技术都容易受到中间人攻击, 因而不适合一般用途。不过,也可以通过 带外验证服务器公钥、首次使用时信任, 或信道绑定等机制,将此类连接与外部身份验证机制 绑定(不过 [RFC5929] 中描述的信道绑定并未针对 TLS 1.3 定义)。如果未使用此类机制, 则连接无法抵御主动中间人攻击;在缺少明确配置 或具体应用配置文件的情况下,应用不得以这种方式使用 TLS。¶
为与本文档使用的名称保持一致, [RFC5246] 中的以下术语被重命名:¶
[RFC5246] 第 8.1 节中计算的 master secret 被重命名为 main secret。在公式和结构中,它被称为 main_secret, 而不是 master_secret。不过,为保持兼容性, PRF 函数的 label 参数保持不变。¶
premaster secret 被重命名为 preliminary secret。 在公式和结构中,它被称为 preliminary_secret,而不是 pre_master_secret。¶
[RFC5246] 第 7.4.7.1 节中定义的 PreMasterSecret 和 EncryptedPreMasterSecret 结构分别重命名为 PreliminarySecret 和 EncryptedPreliminarySecret。¶
相应地,[RFC7627] 中定义的扩展被重命名为 “Extended Main Secret”扩展。该扩展码位被重命名为 “extended_main_secret”。为保持兼容性,[RFC7627] 第 4 节中 PRF 函数的 label 参数保持不变。¶
TLS 协议提供一种内置机制,用于可能支持不同 TLS 版本的 端点之间进行版本协商。¶
TLS 1.x 和 SSL 3.0 使用兼容的 ClientHello 消息。只要 ClientHello 格式 保持兼容,并且客户端和服务器至少共同支持一个协议版本,服务器也可以处理 尝试使用未来 TLS 版本的客户端。¶
早期 TLS 版本将记录层版本号 (TLSPlaintext.legacy_record_version 和 TLSCiphertext.legacy_record_version)用于多种用途。 自 TLS 1.3 起,此字段已被弃用。所有实现必须忽略 TLSPlaintext.legacy_record_version 的值。 TLSCiphertext.legacy_record_version 的值包含在解除保护所使用的 附加数据中,但除此之外可以忽略, 或可以验证其是否与固定常量值匹配。 版本协商仅使用握手版本 (ClientHello.legacy_version 和 ServerHello.legacy_version,以及 ClientHello、HelloRetryRequest 和 ServerHello 的“supported_versions”扩展)。 为最大限度提高与旧端点的互操作性,协商使用 TLS 1.0-1.2 的实现 应将 ServerHello 及其后的所有记录中的记录层 版本号设置为协商后的版本。¶
为了最大限度兼容先前的非标准行为和错误配置的部署, 所有实现应支持基于本文档中预期方式验证认证路径, 即使在处理早期 TLS 版本的握手时也是如此 (参见第 4.5.1.2 节)。¶
TLS 1.2 及更早版本支持“扩展主秘密值”[RFC7627]扩展, 该扩展将大部分握手消息记录纳入秘密值和派生密钥的计算。 请注意,此扩展已在附录 D中重命名。由于 TLS 1.3 始终将截至服务器 Finished 的消息记录纳入哈希, 同时支持 TLS 1.3 和早期版本的实现应在使用 TLS 1.3 时, 在其 API 中指明使用了扩展主秘密值扩展。¶
希望与不支持 TLS 1.3 的服务器协商的 TLS 1.3 客户端, 会发送普通的 TLS 1.3 ClientHello,其中 ClientHello.legacy_version 为 0x0303(TLS 1.2), 但“supported_versions”扩展中包含正确的版本。 如果服务器不支持 TLS 1.3,则会以包含较旧版本号的 ServerHello 响应。 如果客户端同意使用此版本,则协商将按照该协议版本的相应流程继续。 使用票据进行恢复的客户端应使用先前协商的版本发起连接。¶
请注意,0-RTT 数据与旧版服务器不兼容,并且在不知道 服务器支持 TLS 1.3 的情况下不应发送。 参见附录 E.3。¶
如果服务器选择的版本不受客户端支持(或不可接受), 客户端必须使用“protocol_version”警报中止握手。¶
已知某些旧式服务器实现未正确实现 TLS 规范,并且在遇到其不了解的 TLS 扩展或版本时可能中止连接。 与有缺陷服务器的互操作性是一个复杂主题,超出了本文档的范围。 可能需要进行多次连接尝试才能协商出 向后兼容的连接;不过,这种做法容易受到 降级攻击,因此不建议使用。¶
TLS 服务器也可能收到指明的版本号低于其最高支持版本的 ClientHello。如果存在“supported_versions”扩展, 服务器必须按照第 4.3.1 节所述,使用该扩展进行协商。 如果不存在“supported_versions”扩展, 服务器必须在 ClientHello.legacy_version 与 TLS 1.2 中选择较低者。例如,如果服务器支持 TLS 1.0、1.1 和 1.2, 而 legacy_version 为 TLS 1.0,则服务器将使用 TLS 1.0 ServerHello 继续。 如果不存在“supported_versions”扩展,并且服务器仅支持 高于 ClientHello.legacy_version 的版本,则服务器必须 使用“protocol_version”警报中止握手。¶
请注意,早期 TLS 版本并未在所有情况下明确规定记录层 版本号的值(TLSPlaintext.legacy_record_version)。服务器 会在此字段中收到各种 TLS 1.x 版本,但其值 必须始终被忽略。¶
0-RTT 数据与旧版服务器不兼容。旧版服务器会用较旧的 ServerHello 响应 ClientHello,但无法正确跳过 0-RTT 数据,并且无法完成握手。当客户端尝试使用 0-RTT 时, 这可能会造成问题,尤其是在多服务器部署中。例如, 某个部署可能逐步部署 TLS 1.3,其中部分服务器 实现 TLS 1.3,部分服务器实现 TLS 1.2;或者某个 TLS 1.3 部署 可能被降级到 TLS 1.2。¶
尝试发送 0-RTT 数据的客户端在收到 TLS 1.2 或更早版本的 ServerHello 时必须使连接失败。 随后可以禁用 0-RTT 并重试连接。 为避免降级攻击,客户端不应禁用 TLS 1.3,而只应禁用 0-RTT。¶
为避免此错误条件,多服务器部署在启用 0-RTT 之前, 应确保已统一且稳定地部署不使用 0-RTT 的 TLS 1.3。¶
现场测量 [Ben17a] [Ben17b] [Res17a] [Res17b] 发现,当 TLS 客户端/服务器双方协商 TLS 1.3 时, 大量中间盒会出现错误行为。实现可以通过让 TLS 1.3 握手 看起来更像 TLS 1.2 握手,提高通过这些中间盒 建立连接的成功率:¶
客户端始终在 ClientHello 中提供非空会话 ID, 如第 4.2.2 节的 legacy_session_id 部分所述。¶
如果不提供早期数据,客户端会在其第二个消息批次之前 立即发送一条虚假的 change_cipher_spec 记录 (参见第 5 节的第三段)。 该记录可以位于第二个 ClientHello 之前,也可以位于加密的 握手消息批次之前。如果提供早期数据,则此记录 紧跟在第一个 ClientHello 之后。¶
服务器在其第一条握手消息之后立即发送一条虚假的 change_cipher_spec 记录。该记录可以位于 ServerHello 或 HelloRetryRequest 之后。¶
综合使用这些变更后,TLS 1.3 握手会类似于 TLS 1.2 会话恢复,从而提高通过中间盒成功建立连接的概率。 此“兼容模式”部分经过协商:客户端可以选择是否提供会话 ID, 而服务器必须回显该值。任一方都可以在握手期间的任何时间发送 change_cipher_spec,因为对等方必须忽略该记录; 但如果客户端发送非空会话 ID,服务器必须按照本附录所述 发送 change_cipher_spec。¶
协商使用旧版 TLS 的实现,在可用时应优先使用 具有前向保密性的 AEAD 密码套件。¶
出于 [RFC7465] 中所述原因,RC4 密码套件的安全性被认为不足。无论出于何种原因, 实现不得为任何 TLS 版本提供或协商 RC4 密码套件。¶
旧版 TLS 允许使用强度非常低的密码。 无论出于何种原因,强度低于 112 位的密码不得为任何 TLS 版本提供或协商。¶
出于 [RFC6176]、 [RFC7568] 和 [RFC8996] 中列出的原因,SSL 2.0 [SSL2]、SSL 3.0 [RFC6101]、TLS 1.0 [RFC2246] 和 TLS 1.1 [RFC4346] 的安全性被认为不足, 因而无论出于何种原因都不得协商这些版本。¶
实现不得发送兼容 SSL 2.0 版本的 CLIENT-HELLO。 实现不得使用兼容 SSL 2.0 版本的 CLIENT-HELLO 协商 TLS 1.3 或更高版本。不建议实现接受兼容 SSL 2.0 版本的 CLIENT-HELLO 来协商旧版 TLS。¶
实现必须为 ClientHello.legacy_version 和 ServerHello.legacy_version 使用 0x0303。收到其他版本号的 Hello 消息时, 实现必须按照第 4.2.2 节和第 4.2.3 节所述方式中止握手。¶
实现不得发送版本低于 0x0300 的任何记录。 实现不应接受版本低于 0x0300 的任何记录 (不过如果完全忽略记录版本号,可能会无意中接受)。¶
实现不得使用 [RFC6066] 第 7 节中定义的截断 HMAC 扩展,因为它不适用于 AEAD 算法,并且已被证明在某些场景中不安全。¶
对 TLS 进行完整安全分析超出了本文档的范围。 本附录非正式地描述所需属性, 并引用研究文献中提供更正式定义的 更详细工作。¶
我们分别讨论握手属性和记录层属性。¶
TLS 握手是一种认证密钥交换(AKE)协议, 旨在同时提供单向身份验证(仅服务器)和 双向身份验证(客户端和服务器)功能。握手完成时, 双方分别输出其所认知的以下值:¶
一组“会话密钥”(从主秘密值派生的各种秘密值), 可从中派生一组工作密钥。请注意,使用早期数据时, 也会从早期秘密值派生秘密值。如下文所述, 这些秘密值具有比从主秘密值派生的秘密值略弱的属性。¶
一组密码学参数(算法等)。¶
通信双方的身份。¶
我们假设攻击者是主动网络攻击者,这意味着其完全控制 双方通信所使用的网络 [RFC3552]。 即使在这些条件下,握手也应提供下列属性。 请注意,这些属性不一定彼此独立,但反映了 协议使用者的需求。¶
共享会话密钥应仅为通信双方所知, 而不应为攻击者所知(参见 [CK01],定义 1,第 2 部分)。 请注意,在单向身份验证连接中,攻击者可以与服务器建立 自己的会话密钥,但这些会话密钥与 客户端建立的会话密钥不同。¶
客户端所认知的对等方身份应反映 服务器身份。如果客户端经过身份验证,则服务器所认知的 对等方身份应与客户端身份匹配。¶
任意两个不同的握手都应生成不同且互不相关的 会话密钥。一次握手生成的各个会话密钥也应彼此不同 且相互独立。¶
双方使用的密码学参数应相同, 并且应与对等方在不存在攻击时进行通信所会使用的参数相同 (参见 [BBFGKZ16],定义 8 和 9)。¶
如果长期密钥材料(本例中为
基于证书身份验证模式中的签名密钥,或采用非对称密钥交换模式的
PSK 中的外部/恢复 PSK)在握手完成后泄露,
只要会话密钥本身(以及可用于重建会话密钥的所有材料)
已被清除,就不会危及会话密钥的安全性
(参见 [DOW92])。
特别是,与密钥共享值、共享秘密值以及 TLS 密钥调度中派生的密钥
对应的私钥,也都需要清除,
但 binder_key、resumption_secret 和从
resumption_secret 派生的 PSK 除外。
在“psk_ke”PskKeyExchangeMode 中使用 PSK 时,不满足前向保密性属性。
未能清除原本应为临时或连接专用的密钥或秘密值,
实际上会创建必须加以保护的额外长期密钥。
这些长期密钥泄露(即使发生在握手完成后)
也可能导致连接流量失去保护。¶
在使用证书进行双向身份验证的连接中, 某一参与方的长期秘密值泄露,不应破坏该参与方在给定连接中 对其对等方的身份验证(参见 [HGFS15])。例如,如果客户端的签名密钥 泄露,则后续握手中不应能够向该客户端冒充任意服务器。¶
服务器身份(证书)应受到保护,避免被动 攻击者获知。客户端身份(证书)应同时受到保护,避免 被动和主动攻击者获知。对于不提供机密性的密码套件, 此属性不成立;虽然本规范未定义任何此类密码套件, 其他文档可能会定义。¶
非正式地说,TLS 1.3 的基于签名模式通过 非对称密钥交换建立唯一、秘密的共享密钥,并由服务器对 握手消息记录的签名进行身份验证,同时通过 MAC 将该密钥 与服务器身份绑定。如果客户端使用证书进行身份验证, 它也会对握手消息记录进行签名,并提供一个与双方身份绑定的 MAC。 [SIGMA] 描述了此类密钥交换协议的设计和 分析。如果每个连接都使用新的非对称密钥, 则输出密钥具有前向保密性。¶
外部 PSK 和恢复 PSK 从一个长期共享秘密值引导出 每个连接唯一的一组短期会话密钥。 此秘密值可能在先前握手中建立。如果使用 PSK 与非对称密钥建立,这些会话密钥也具有前向保密性。 恢复 PSK 的设计使连接 N 计算出的、用于形成 连接 N+1 的恢复秘密值,与连接 N 使用的流量密钥相分离, 从而在连接之间提供前向保密性。 此外,如果在同一连接上建立多个票据, 它们会与不同密钥关联,因此与某个票据关联的 PSK 泄露, 不会导致使用其他票据所关联 PSK 建立的连接泄露。 如果票据存储在数据库中(从而可以删除), 而不是自行加密,则此属性尤其有意义。¶
前向保密性限制密钥在一个时间方向上的泄露影响 (时间 T2 的密钥泄露不会危及时间 T1 的某些密钥, 其中 T1 < T2)。通过重新执行非对称密钥建立, 可以在另一个方向上获得保护(时间 T1 的泄露不会危及 时间 T2 的密钥)。如果长期身份验证密钥已泄露, 使用非对称密钥建立的完整握手可防御被动攻击者。 如果 resumption_secret 已泄露,使用非对称密钥建立的恢复握手 可防御被动攻击者,而使用非对称密钥建立的完整握手 可防御主动攻击者。如果流量秘密值已泄露, 任何使用非对称密钥建立的握手都可防御主动攻击者。 使用 [RFC7624] 中的术语, 不重新执行非对称密钥建立的前向保密性,无法阻止攻击者进行静态 密钥外泄。在 application_traffic_secret_N 外泄后, 攻击者例如可以被动窃听连接上发送的所有未来数据, 包括使用 application_traffic_secret_N+1、 application_traffic_secret_N+2 等加密的数据。 频繁重新执行非对称密钥建立,会迫使攻击者进行动态密钥外泄 (或内容外泄)。¶
PSK 绑定器值在 PSK 与当前握手之间形成绑定, 并在建立该 PSK 的会话(如果通过 NewSessionTicket 消息建立)与当前会话之间形成绑定。 此绑定会传递性地包含原始握手消息记录, 因为该消息记录会被摘要进生成恢复秘密值的各个值中。 这要求用于生成恢复秘密值的 KDF 和用于计算绑定器的 MAC 都具有抗碰撞性。 有关更多信息,请参阅附录 F.1.1。 注意:绑定器不覆盖其他 PSK 的绑定器值, 不过 Finished MAC 会包含这些值。¶
注意:本规范目前不允许服务器在不基于证书的握手 (例如 PSK)中发送 CertificateRequest 消息。 如果未来放宽此限制, 客户端签名将不会直接覆盖服务器证书。 但是,如果 PSK 通过 NewSessionTicket 建立, 客户端签名将通过 PSK 绑定器传递性地覆盖服务器证书。 [PSK-FINISHED] 描述了针对未与服务器证书绑定的构造的具体攻击 (另请参阅 [Kraw16])。 当客户端可能与两个不同端点共享同一 PSK/密钥 ID 对时,使用基于证书的客户端身份验证并不安全。 除非其他规范另有规定,否则实现不得 将外部 PSK 与客户端或服务器的基于证书身份验证结合使用。 [RFC8773] 提供了允许这样做的扩展, 但其所受分析程度不及本规范。¶
如果使用导出器,它会生成唯一且秘密的值 (因为这些值由唯一会话密钥生成)。 使用不同标签和上下文计算的导出器在计算上相互独立, 因而无法从一个导出器计算另一个导出器, 也无法从导出值计算会话秘密值。 注意:导出器可以生成任意长度的值; 如果导出器要用作信道绑定,则导出值必须足够大, 以提供抗碰撞性。TLS 1.3 提供的导出器 分别从与早期流量密钥和应用流量密钥相同的握手上下文派生, 因而具有类似的安全属性。请注意,它们 不包含客户端证书;未来希望与客户端证书绑定的应用 可能需要定义包含完整握手消息记录的新导出器。¶
对于所有握手模式,Finished MAC(以及存在时的签名) 都可防止降级攻击。此外,按照第 4.2.3 节所述, 在随机 nonce 中使用某些字节可以检测向早期 TLS 版本的降级。 有关 TLS 1.3 和降级的更多详细信息,请参阅 [BBFGKZ16]。¶
客户端与服务器一旦交换足够的信息来建立共享密钥, 握手的其余部分就会被加密,从而即使计算出的共享密钥 尚未经过身份验证,也可抵御被动攻击者。 由于服务器先于客户端进行身份验证, 客户端可以确保:如果它向服务器进行身份验证, 只会向经过身份验证的服务器披露自身身份。 请注意,实现必须在握手期间使用所提供的记录填充机制, 以避免因长度泄露身份信息。 客户端提供的 PSK 身份未加密, 服务器选择的身份也未加密。¶
TLS 1.3 中的密钥派生使用 [RFC5869] 中定义的 HKDF, 以及它的两个组成部分 HKDF-Extract 和 HKDF-Expand。 HKDF 构造的完整设计理由见 [Kraw10], 其在 TLS 1.3 中使用方式的设计理由见 [KW16]。 在本文档中,每次应用 HKDF-Extract 后, 都会调用一次或多次 HKDF-Expand。 应始终遵循此顺序(包括本文档未来的修订版); 特别是,不应在中间没有 HKDF-Expand 的情况下, 将一次 HKDF-Extract 的输出用作另一次 HKDF-Extract 的输入。 只要通过密钥和/或标签进行区分, 就允许对某些相同输入多次应用 HKDF-Expand。¶
请注意,HKDF-Expand 实现了输入和输出长度均可变的 伪随机函数(PRF)。在本文档的某些 HKDF 用法中 (例如生成导出器和 resumption_secret), HKDF-Expand 的应用必须具有抗碰撞性;也就是说, 应无法找到两个不同的 HKDF-Expand 输入,使其输出相同值。 这要求底层哈希函数具有抗碰撞性, 并且 HKDF-Expand 的输出长度至少为 256 位 (或达到哈希函数防止找到碰撞所需的长度)。¶
无论是在握手期间,还是在握手后身份验证中, 向服务器发送基于证书身份验证数据的客户端,都无法确定 服务器随后是否认为客户端已经过身份验证。 如果客户端需要确定服务器认为连接是单向身份验证 还是双向身份验证,则必须由应用层提供此信息。 有关详细信息,请参阅 [CHHSV17]。 此外,[Kraw16] 对握手后身份验证的分析表明, 由握手后阶段所发送证书标识的客户端拥有流量密钥。 因此,该参与方要么是参与原始握手的客户端, 要么是原始客户端向其委托流量密钥的一方 (假设流量密钥尚未泄露)。¶
0-RTT 运行模式通常提供与 1-RTT 数据类似的 安全属性,但有两个例外:0-RTT 加密密钥不提供完整的前向保密性; 服务器若不保留可能数量过多的状态, 就无法保证握手的唯一性(不可重放性)。 有关限制重放风险的机制,请参阅第 8 节。¶
exporter_secret 和 early_exporter_secret 被派生为独立于流量密钥,因此不会 威胁使用这些密钥加密的流量安全性。 不过,由于这些秘密值可用于计算任意导出器值, 因而应尽快清除。 如果导出器标签的完整集合已知,则实现应 为所有这些标签预先计算导出器计算中的内部 Derive-Secret 阶段, 然后清除 [early_]exporter_secret; 一旦确定不再需要某个内部值,也应立即清除该值。¶
记录层依赖握手生成强健的流量秘密值, 可用于派生双向加密密钥和 nonce。 假设这一点成立,并且密钥用于加密的数据量不超过 第 5.5 节所述限制, 则记录层应提供以下保证:¶
攻击者不应能够确定给定记录的明文 内容。¶
攻击者不应能够构造一条不同于现有记录、 但会被接收方接受的新记录。¶
攻击者不应能够使接收方接受一条其已经接受过的 记录,也不应能够使接收方在尚未处理记录 N 的情况下 接受记录 N+1。¶
对于具有给定外部长度的记录,攻击者不应能够 确定记录中有多少是内容,有多少是填充。¶
如果已使用第 4.7.3 节所述的流量密钥更新机制, 并且删除了上一代密钥,则攻陷端点的攻击者 不应能够解密使用旧密钥加密的流量。¶
非正式地说,TLS 1.3 通过使用强密钥对明文进行 AEAD 保护来提供这些属性。AEAD 加密 [RFC5116] 为数据提供机密性和完整性。 不可重放性通过为每条记录使用单独的 nonce 来提供, 该 nonce 从记录序列号派生(第 5.3 节), 并且双方独立维护序列号;因此, 乱序交付的记录会导致 AEAD 解除保护失败。 为防止不同用户使用同一密钥重复加密相同明文时 发生大规模密码分析(HTTP 中经常出现这种情况), nonce 通过将序列号与一个随流量密钥一起派生的、 每连接唯一的秘密初始化向量混合而形成。 有关此构造的分析,请参阅 [BT16]。¶
TLS 1.3 中的密钥更新技术(参见第 7.2 节) 遵循 [REKEY] 中讨论的串行生成器构造, 该文表明密钥更新可使密钥支持比不进行更新时更多次数的加密。 这依赖 HKDF-Expand-Label 函数作为伪随机函数(PRF)的安全性。 此外,只要此函数确实是单向的, 就无法计算密钥更改之前的流量密钥(前向保密性)。¶
对于连接的某个流量秘密值泄露后通过该连接传输的数据, TLS 不提供安全性。也就是说,对于流量秘密值, TLS 不提供泄露后安全性/未来保密性/后向保密性。 实际上,获知某个流量秘密值的攻击者可以计算该连接上 所有未来流量秘密值。希望获得此类保证的系统 需要执行新的握手,并使用非对称密钥交换建立新连接。¶
TLS 容易受到多种基于观察加密数据包长度和时序的 流量分析攻击 [CLINIC] [HCJC16]。 当可能需要区分的消息集合较小时, 这种攻击尤其容易实施,例如视频服务器托管固定内容集合的情况; 但即使在更复杂的场景中,也仍能提供可利用的信息。¶
TLS 不提供针对这种攻击形式的特定防御, 但包含一种供应用使用的填充机制: AEAD 函数所保护的明文由内容加可变长度填充组成, 从而允许应用生成任意长度的加密记录, 以及只含填充的掩护流量,以隐藏传输期间与静默期间的差异。 由于填充与实际内容一起加密, 攻击者无法直接确定填充长度, 但可能通过记录处理期间暴露的时序信道间接测量 (例如观察处理一条记录所需的时间, 或缓慢送入记录以判断哪些记录会触发服务器响应)。 通常,不知道如何消除所有这些信道, 因为即使使用恒定时间的填充移除函数, 也很可能将内容传递给依赖数据的函数。 至少,一个完全恒定时间的服务器或客户端 需要与应用层协议实现密切协作, 包括使该高层协议也以恒定时间运行。¶
注意:强健的流量分析防御可能会因 延迟数据包传输和增加流量而导致性能下降。¶
总体而言,TLS 没有针对侧信道攻击 (即通过时序等辅助信道攻击通信)的特定防御, 而是将此问题留给相关密码学原语的实现处理。 不过,TLS 的某些功能旨在使编写抗侧信道代码更加容易:¶
与使用复合“先计算 MAC 再加密”结构的早期 TLS 版本不同, TLS 1.3 仅使用 AEAD 算法, 从而允许实现使用这些原语的自包含恒定时间实现。¶
TLS 对所有解密错误统一使用“bad_record_mac”警报, 旨在防止攻击者逐步获知消息各部分的信息。 在此类错误发生时终止连接还可提供额外防护; 新连接将具有不同的密码学材料, 从而阻止需要多次尝试的密码学原语攻击。¶
侧信道信息泄露可能发生在 TLS 之上的层中, 即应用协议以及使用这些协议的应用中。 抵御侧信道攻击取决于应用和应用协议分别确保 机密信息不会无意泄露。¶
可重放的 0-RTT 数据会给使用 TLS 的应用带来多种安全威胁, 除非这些应用经过专门设计,能在重放情况下保持安全 (最低限度意味着操作是幂等的, 但许多情况下还可能需要更强的条件,例如恒定时间响应)。 潜在攻击包括:¶
重复执行会造成副作用的操作 (例如购买商品或转账),从而损害网站或用户。¶
攻击者可以存储并重放 0-RTT 消息, 从而相对于其他消息重新排列顺序 (例如将删除操作移动到创建操作之后)。¶
放大由缓存等副作用造成的现有信息泄露。 攻击者可以将 0-RTT 消息重放到尚未缓存某个目标资源的 缓存节点,以了解该消息内容的信息, 随后使用单独连接检查该资源是否已被加入缓存。 只要 0-RTT 消息仍可重放,就可以对不同缓存节点重复执行此操作。¶
如果数据可以被大量重放,还会出现其他攻击, 例如反复测量密码学操作的速度。 此外,攻击者可能使速率限制系统过载。 有关这些攻击的进一步说明,请参阅 [Mac17]。¶
最终,服务器有责任保护自身免受使用 0-RTT 数据复制的攻击。 第 8 节中描述的机制 旨在防止 TLS 层重放,但不能完全防止 收到客户端数据的多个副本。 当服务器没有关于客户端的任何信息时, TLS 1.3 会回退到 1-RTT 握手,例如服务器位于不共享状态的 不同集群中,或票据已按照 第 8.1 节所述被删除。 如果应用层协议在这种情况下重新传输数据, 攻击者就可能通过同时向原始集群 (该集群会立即处理数据)和另一个会回退到 1-RTT、 并在应用层重放后处理数据的集群发送 ClientHello, 来诱导消息重复。 此攻击的规模受客户端愿意重试事务的程度限制, 因而只能造成有限数量的重复, 且每个副本在服务器看来都是一个新连接。¶
如果正确实现, 第 8.1 节和第 8.2 节所述机制 可防止具有一致状态的任意集群多次接受 被重放的 ClientHello 及其关联 0-RTT 数据; 对于将单个票据的 0-RTT 使用限制在一个集群的服务器, 给定 ClientHello 及其关联 0-RTT 数据只会被接受一次。 不过,如果状态并非完全一致, 攻击者可能在复制窗口期间使多个数据副本被接受。 由于客户端不知道服务器行为的具体细节, 因此不得在早期数据中发送不适合重放、 且不愿意在多个 1-RTT 连接中重试的消息。¶
应用协议不得在没有定义其用法的配置文件时 使用 0-RTT 数据。该配置文件需要标识哪些消息或 交互可以安全地与 0-RTT 一起使用, 以及当服务器拒绝 0-RTT 并回退到 1-RTT 时应如何处理。¶
此外,为避免误用,除非应用明确请求, TLS 实现不得启用 0-RTT(无论发送还是接受); 如果服务器拒绝 0-RTT 数据, 除非应用作出指示,否则不得自动重新发送。 服务器端应用可能希望对某些类型的应用流量 实现特殊的 0-RTT 数据处理 (例如中止连接、请求在应用层重新发送数据, 或延迟处理直到握手完成)。 为使应用能够实现此类处理, TLS 实现必须提供一种方式,让应用确定 握手是否已经完成。¶
由于实现会通过中止握手来响应无效的 PSK 绑定器, 攻击者可能能够验证给定 PSK 身份是否有效。 具体而言,如果服务器同时接受外部 PSK 握手和基于证书的握手, 有效的 PSK 身份会导致握手失败, 而无效身份则会被直接跳过并导致证书握手成功。 仅支持 PSK 握手的服务器可以通过同样处理以下两种情况, 抵御此类攻击:不存在有效 PSK 身份; 或存在身份,但其绑定器无效。¶
TLS 中的外部 PSK 被设计为仅由一个客户端 和一个服务器知晓。不过,如 [RFC9257] 所述, 存在由两个以上实体共享 PSK 的使用场景。 在此类场景中,除被攻陷的组成员可以冒充 任何其他成员这一预期安全弱点外, 恶意非成员还可以在诚实组成员之间重新路由握手, 使它们以非预期方式建立连接 [Selfie]。 [RFC9257] 提供了 外部 PSK 使用建议,包括使用 [RFC9258] 中定义的外部 PSK 导入器, 以防止此类恶意消息重新路由。¶
当 TLS 1.3 使用不包含有效身份的自签名证书 (例如 DTLS-SRTP [RFC5763]) 或原始公钥 [RFC7250] 进行对等方身份验证时,可能容易受到 错误绑定攻击 [MM24]。 可以通过使用“external_id_hash”扩展 [RFC8844] 缓解此风险; 如果仅验证服务器身份,也可以由服务器验证 “server_name”扩展是否与其预期身份匹配。¶
虽然 TLS 1.3 不使用 RSA 密钥传输, 因而不会直接受到 Bleichenbacher 类攻击 [Blei98], 但如果 TLS 1.3 服务器还在早期 TLS 版本环境中支持静态 RSA, 则可能在 TLS 1.3 连接中冒充服务器 [JSS15]。 TLS 1.3 实现可以通过在所有 TLS 版本中禁用静态 RSA 支持 来防止此攻击。原则上,实现还可以 使用不同 keyUsage 位,将静态 RSA 解密证书 与 RSA 签名证书分开; 但此技术依赖客户端拒绝接受 使用未设置 digitalSignature 位的证书中密钥所生成的签名, 而许多客户端并不强制执行此限制。¶
Martin Abadi 加利福尼亚大学圣克鲁兹分校 abadi@cs.ucsc.edu Christopher Allen (TLS 1.0 联合编辑) Alacrity Ventures ChristopherA@AlacrityManagement.com Nimrod Aviram 特拉维夫大学 nimrod.aviram@gmail.com Richard Barnes 思科 rlb@ipv.sx Steven M. Bellovin 哥伦比亚大学 smb@cs.columbia.edu David Benjamin Google davidben@google.com Benjamin Beurdouche INRIA 与微软研究院 benjamin.beurdouche@ens.fr Karthikeyan Bhargavan (RFC 7627 编辑) INRIA karthikeyan.bhargavan@inria.fr Simon Blake-Wilson (RFC 4492 联合作者) BCI sblakewilson@bcisse.com Nelson Bolyard (RFC 4492 联合作者) Sun Microsystems, Inc. nelson@bolyard.com Ran Canetti IBM canetti@watson.ibm.com Matt Caswell OpenSSL matt@openssl.org Stephen Checkoway 伊利诺伊大学芝加哥分校 sfc@uic.edu Pete Chown Skygate Technology Ltd pc@skygate.co.uk Katriel Cohn-Gordon 牛津大学 me@katriel.co.uk Cas Cremers 牛津大学 cas.cremers@cs.ox.ac.uk Antoine Delignat-Lavaud (RFC 7627 联合作者) INRIA antdl@microsoft.com Tim Dierks (TLS 1.0 联合作者、TLS 1.1 和 1.2 联合编辑) 独立人士 tim@dierks.org Roelof DuToit 赛门铁克公司 roelof_dutoit@symantec.com Taher Elgamal Securify taher@securify.com Pasi Eronen 诺基亚 pasi.eronen@nokia.com Cedric Fournet 微软 fournet@microsoft.com Anil Gangolli anil@busybuddha.org David M. Garrett dave@nulldereference.com Illya Gerasymchuk 独立人士 illya@iluxonchik.me Alessandro Ghedini Cloudflare Inc. alessandro@cloudflare.com Daniel Kahn Gillmor 美国公民自由联盟 dkg@fifthhorseman.net Matthew Green 约翰斯·霍普金斯大学 mgreen@cs.jhu.edu Jens Guballa ETAS jens.guballa@etas.com Felix Guenther 达姆施塔特工业大学 mail@felixguenther.info Vipul Gupta (RFC 4492 联合作者) Sun Microsystems Laboratories vipul.gupta@sun.com Chris Hawk (RFC 4492 联合作者) Corriente Networks LLC chris@corriente.net Kipp Hickman Alfred Hoenes David Hopwood 独立顾问 david.hopwood@blueyonder.co.uk Marko Horvat MPI-SWS mhorvat@mpi-sws.org Jonathan Hoyland 伦敦大学皇家霍洛威学院 jonathan.hoyland@gmail.com Subodh Iyengar Facebook subodh@fb.com Benjamin Kaduk Akamai Technologies kaduk@mit.edu Hubert Kario Red Hat Inc. hkario@redhat.com Phil Karlton (SSL 3.0 联合作者) Leon Klingele 独立人士 mail@leonklingele.de Paul Kocher (SSL 3.0 联合作者) Cryptography Research paul@cryptography.com Hugo Krawczyk IBM hugokraw@us.ibm.com Adam Langley (RFC 7627 联合作者) Google agl@google.com Olivier Levillain ANSSI olivier.levillain@ssi.gouv.fr Xiaoyin Liu 北卡罗来纳大学教堂山分校 xiaoyin.l@outlook.com Ilari Liusvaara 独立人士 ilariliusvaara@welho.com Atul Luykx 鲁汶天主教大学 atul.luykx@kuleuven.be Colm MacCarthaigh 亚马逊云科技 colm@allcosts.net Carl Mehner USAA carl.mehner@usaa.com Jan Mikkelsen Transactionware janm@transactionware.com Bodo Moeller (RFC 4492 联合作者) Google bodo@acm.org Kyle Nekritz Facebook knekritz@fb.com Erik Nygren Akamai Technologies erik+ietf@nygren.org Magnus Nystrom 微软 mnystrom@microsoft.com Kazuho Oku DeNA Co., Ltd. kazuhooku@gmail.com Kenny Paterson 伦敦大学皇家霍洛威学院 kenny.paterson@rhul.ac.uk Christopher Patton 佛罗里达大学 cjpatton@ufl.edu Alfredo Pironti (RFC 7627 联合作者) INRIA alfredo.pironti@inria.fr Andrei Popov 微软 andrei.popov@microsoft.com John Preuß Mattsson 爱立信 john.mattsson@ericsson.com Marsh Ray (RFC 7627 联合作者) 微软 maray@microsoft.com Robert Relyea 网景通信公司 relyea@netscape.com Kyle Rose Akamai Technologies krose@krose.org Jim Roskind 亚马逊 jroskind@amazon.com Michael Sabin Joe Salowey Tableau Software joe@salowey.net Rich Salz Akamai rsalz@akamai.com David Schinazi Apple Inc. dschinazi@apple.com Sam Scott 伦敦大学皇家霍洛威学院 me@samjs.co.uk Mohit Sethi 阿尔托大学 mohit@iki.fi Thomas Shrimpton 佛罗里达大学 teshrim@ufl.edu Dan Simon Microsoft, Inc. dansimon@microsoft.com Brian Smith 独立人士 brian@briansmith.org Ben Smyth Ampersand www.bensmyth.com Brian Sniffen Akamai Technologies ietf@bts.evenmere.org Nick Sullivan Cloudflare Inc. nick@cloudflare.com Bjoern Tackmann 加利福尼亚大学圣迭戈分校 btackmann@eng.ucsd.edu Tim Taubert Mozilla ttaubert@mozilla.com Martin Thomson Mozilla mt@mozilla.com Hannes Tschofenig Arm Limited Hannes.Tschofenig@arm.com Sean Turner sn3rd sean@sn3rd.com Steven Valdez Google svaldez@google.com Filippo Valsorda Cloudflare Inc. filippo@cloudflare.com Thyla van der Merwe 伦敦大学皇家霍洛威学院 tjvdmerwe@gmail.com Victor Vasiliev Google vasilvv@google.com Loganaden Velvindron cyberstorm.mu logan@cyberstorm.mu Hoeteck Wee 巴黎高等师范学院 hoeteck@alum.mit.edu Tom Weinstein David Wong NCC Group david.wong@nccgroup.trust Christopher A. Wood Apple Inc. cawood@apple.com Tim Wright 沃达丰 timothy.wright@vodafone.com Peter Wu 独立人士 peter@lekensteyn.nl Kazu Yamamoto 互联网倡议日本公司 kazu@iij.ad.jp¶