摘要
- 现行 RFC 9846 的 KeyUpdate 只推进一个发送方向的 application traffic secret。更新消息用旧发送密钥保护,之后的记录使用新世代;接收方验证消息后推进对应的接收链。
update_requested产生受限的反向更新义务,却不把连接交给远端。两个方向各有世代,交叉请求可能各推进两次,静默期间的多次请求会合并,本地世代上限与 AEAD 限额仍可导致拒绝或终止。- “换钥完成”至少要拆成四条证据:API 安排了更新、旧密钥下的转换消息发出、对端接受并推进、首条新世代应用记录成功。即使四项齐全,也不能证明旧 secret 已被擦除、身份已重新认证或业务动作获得授权。
旧密钥必须为新密钥作证
在受控测试里,让一个持续传输的 TLS 1.3 服务端主动更新发送密钥。线上首先出现的不是“新密钥已生效”的明文标记,而是一条仍由旧发送密钥保护的 KeyUpdate。只有掌握当前接收 secret 的对端才能验证这条消息;验证成功后,它才有理由计算下一代接收 secret。
发送方一旦送出 KeyUpdate,后续记录全部使用新一代发送密钥。若消息带 update_requested,客户端还要在下一条 Application Data 之前发出自己的 KeyUpdate。客户端的回复同样由客户端旧发送密钥保护,然后客户端到服务端这一方向才进入下一世代。
所以,连接里没有同时翻页的一本日历。服务端发送世代、服务端接收世代、客户端发送世代与客户端接收世代按方向配对,却可能在时间上错开。把它们压成一个 key_epoch=7,会在最需要排障时丢掉因果关系。
2026 年的现行规则
RFC 9846 在 2026 年取代 RFC 8446,但保持 TLS 1.3 的线协议兼容。它继续把 KeyUpdate 定义为 handshake type 24,消息体只能是 update_not_requested(0) 或 update_requested(1)。在发送 Finished 之前收到 KeyUpdate,必须以 unexpected_message 终止;其他数值则触发 illegal_parameter。
KeyUpdate 必须位于密钥变化前的 record 边界。双方都必须先收到由旧密钥验证通过的 KeyUpdate,才可接受新密钥保护的数据。这个顺序防止对手跳过转换桥并制造截断空间。它也是审计里的关键区别:看到能用“下一把钥匙”解开的 ciphertext,不等于已经证明这一世代由真实对端授权。
新 secret 由当前方向的 application traffic secret 通过 HKDF-Expand-Label 与 traffic upd 标签派生,再生成新的 key 与 IV。RFC 9846 要求发送实现不得让 epoch 超过 2^48-1;接收方不能替发送方执行该上限,因为未来规范可能改变它。若响应对端请求会越界,本端不得更新,并应忽略请求标志,最终可能因 AEAD 使用限额而关闭连接。
请求不是暗示,也不是所有权
update_requested 比普通建议更强:正常情况下,接收方必须在自己的下一条应用数据之前回一个 update_not_requested。但规范同时把远端能力限制在一个有限状态机里。
已有请求尚未收到对端后续 KeyUpdate 时,不得再发送新的 requested update。一方静默期间收到多条请求,可以用一次反向更新回答。若两端各自发起的 requested update 在线上交叉,双方还会各自回复,两个方向都可能连续推进两代。这不是异常,而是独立方向并发的合法结果。
因此,本端需要限制更新频率、CPU 工作和连接寿命。有效认证不能自动创造无限资源权。对端有权提交规范内的请求,却无权决定本端线程何时运行、旧 key 存放多久、达到限额后是否继续服务。
新记录证明兼容,不证明删除
计算出下一代后,RFC 9846 说实现应删除上一代 traffic secret 与相关 key。这里的证据只能来自本地:内存分配器、硬件安全模块、kernel offload、crash dump 策略以及真正执行删除的代码路径。
对端收到新世代记录,最多证明双方在这个方向派生出兼容状态。它不能读取远端 heap,不能判断旧 key 是否仍在备份,也不能确认另一个线程是否保留副本。把对端的 KeyUpdate 回复写成“密钥已安全销毁”,就是用互操作消息伪造远程合规证明。
换钥也不是重新认证。证书、service identity、client identity 与应用权限仍来自既有连接。新 record key 可以完整保护一条越权指令;密码学新鲜度并不会修正业务授权错误或已经被控制的 endpoint。
API 成功离线上完成还有距离
OpenSSL 的 SSL_key_update() 只能在初始握手后调用,而且通常要等下一次 read、write 或显式驱动 handshake 才真正执行。应用还必须处理尚未完成的写操作。函数返回成功,只能说明更新被安排,不能当成发包时间。
GnuTLS 直接暴露方向差异:无 peer 标志时刷新本地发送方向,GNUTLS_KU_PEER 同时请求对端;调用像 record send 一样可能返回非阻塞状态。它的文档还把 rekey 与 re-authentication 分开,避免监控系统把两种安全事件混成一件事。
rustls 的 refresh_traffic_keys() 会安排 TLS 1.3 更新,并在常规路径跟踪 cipher suite 的 confidentiality limit。若应用通过 kernel 接口把 record protection 外置,记录计数、刷新和终止责任也随之交给应用。抽象层改变了,责任并未消失。
先识别传输,再解释“换钥”
QUIC 使用 TLS 1.3 完成握手,却明确禁止 TLS KeyUpdate。QUIC v1 通过 Key Phase、packet acknowledgement 与 quic ku 派生推进 packet protection。一个只数 TLS handshake type 24 的仪表盘,会把正常 QUIC 更新报告为“从未换钥”。
DTLS 1.3 则保留 KeyUpdate,但因为 datagram 会丢失和乱序,需要 epoch、ACK 与旧接收 key 保留。回应消息可能与请求交叉,因此回应本身不自动确认先前请求。接近 epoch 上限时,DTLS 还允许忽略会越界的反向请求。
TLS、DTLS 与 QUIC 都能达到“更新保护密钥”的目标,却生成不同证据。事件必须带上 transport 与 version,不能只有一个模糊的 rekey 标签。
最小可信账本
生产记录不应包含 secret 原值,但应保留:连接引用、角色、协议版本、方向、旧/新世代、发起原因、request flag、旧密钥转换消息验证结果、record 边界、待发送应用数据、第一条新世代成功记录、交叉或合并状态、限流动作与终止 alert。
同时把四个时点分开:scheduled、emitted、accepted、new-data-succeeded。packet capture 因为看见的是加密记录,通常无法独自判断 KeyUpdate;受控环境可用 endpoint callback、世代 counter 与受限 key log 对齐,生产环境不应为了图表方便而外泄 key material。
旧 secret 删除另存为本地 assurance 证据。它可以与更新事件关联,却不能由更新事件替代。
来源
- RFC 9846:TLS 1.3 现行规范
- RFC 8446:已被取代的原始 TLS 1.3 规范
- RFC 9001:Using TLS to Secure QUIC
- RFC 9147:DTLS 1.3
- IANA:TLS Parameters
- OpenSSL:SSL_key_update
- GnuTLS:Core API reference
- GnuTLS:TLS 1.3 re-authentication and re-key
- rustls:ConnectionCommon
- rustls:kernel connection API
- Heng Lu:Running-Code Primacy
- Heng Lu:Minimum Initial Specification
会员简报
档案背景详情
使用相应会员等级登录,即可解锁完整简报与来源注释。
仅限 Strategic Circle
Strategic Circle
所有读者均可浏览。加入并登录后可解锁档案简报。
加入 Strategic Circle仅限 Leadership Alliance
Leadership Alliance
符合条件的 IP 资产所有者和管理层可登录查看 Leadership Alliance 简报。
加入 Leadership Alliance
