TLS 1.3 作为最新版本的传输层安全协议,在握手效率和安全性方面做了重大改进。其中,密钥更新(Key Update)机制是一个重要的安全特性,允许在不重新握手的情况下更新加密密钥,从而延长加密会话的生命周期,同时保持前向安全性。

什么是密钥更新机制?

密钥更新(Key Update)是 TLS 1.3 协议定义的 POST-HANDSHAKE 消息之一。在 TLS 1.3 中,握手完成后的加密通信使用两类密钥:

  • 应用流量密钥(Application Traffic Keys):用于加密实际的应用数据
  • 握手流量密钥(Handshake Traffic Keys):仅用于加密握手消息

传统的 TLS 1.2 想要更新密钥必须重新握手,这会增加延迟。TLS 1.3 引入了 KeyUpdate 消息,允许双方在不中断连接的情况下动态更新应用流量密钥。

为什么需要密钥更新?

1. 限制单密钥加密数据量

现代加密算法在加密大量数据后,理论上存在被破解的风险(尽管概率极低)。定期更新密钥可以:

  • 减少每个密钥加密的数据量
  • 降低潜在的安全风险
  • 满足某些合规要求

2. 保持前向安全性

TLS 1.3 的密钥派生使用 HKDF,从原始握手密钥派生出应用流量密钥。即使某个会话密钥被破解,之前的通信仍然是安全的。密钥更新机制让这一特性在长会话中持续生效。

3. 减少握手开销

相比完整的重新握手,KeyUpdate 消息只包含几个字节,延迟几乎可以忽略不计。

KeyUpdate 消息格式

TLS 1.3 的 KeyUpdate 消息结构简洁高效:

1
2
3
4
5
6
7
8
9
struct {
    KeyUpdateRequest request;
} KeyUpdate;

enum {
    update_not_requested(0),
    update_requested(1),
    (255)
} KeyUpdateRequest;
  • update_not_requested(0):我已更新密钥,但不希望对方也更新
  • update_requested(1):我已更新密钥,并请求对方也更新

工作流程详解

密钥更新的完整流程如下:

1. 发起更新

通信一方(可以是客户端或服务器)决定更新密钥时,发送 KeyUpdate 消息:

1
发送方 → KeyUpdate(update_requested) → 接收方

2. 派生新密钥

发送方使用 HKDF 从当前密钥状态派生新的密钥:

1
2
3
4
5
6
HKDF-Expand-Label(
    secret,
    "traffic upd",
    "",
    hash_length
) → new_application_traffic_key

3. 切换密钥

发送方立即开始使用新的密钥加密后续数据。接收方收到 KeyUpdate 消息后:

  1. 派生自己的新密钥
  2. 切换到新密钥解密集收到的数据

4. 响应更新

如果收到 update_requested,接收方也需要发送自己的 KeyUpdate 消息作为响应。

实际验证

查看 TLS 1.3 连接

使用 OpenSSL 可以查看 TLS 1.3 连接详情:

1
2
$ echo | openssl s_client -connect cloudflare.com:443 -tls1_3 2>&1 | grep -E "Protocol|Cipher"
New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384

TLS 1.3 默认使用 TLS_AES_256_GCM_SHA384TLS_CHACHA20_POLY1305_SHA256 密码套件。

TLS 1.3 与 TLS 1.2 密钥更新对比

特性 TLS 1.2 TLS 1.3
密钥更新方式 重新握手 KeyUpdate 消息
开销 高(完整握手) 极低(仅几字节)
延迟 1-RTT 0-RTT
前向安全性 需重新握手 自动保持

实际应用配置

Nginx 配置

Nginx 通过 ssl_protocolsssl_ciphers 启用 TLS 1.3,并自动支持密钥更新:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
server {
    listen 443 ssl http2;
    server_name example.com;

    ssl_certificate /path/to/cert.pem;
    ssl_certificate_key /path/to/key.pem;

    # 启用 TLS 1.3
    ssl_protocols TLSv1.3;

    # TLS 1.3 密码套件(按优先级排序)
    ssl_prefer_server_ciphers on;
}

Nginx 会在内部自动处理密钥更新,无需额外配置。

查看握手后的密钥状态

可以通过 Wireshark 捕获 TLS 1.3 流量,观察 KeyUpdate 消息:

  1. 捕获 TLS 1.3 连接流量
  2. 过滤 tls.record.content_type == 24(KeyUpdate)
  3. 观察密钥更新消息的发送

安全性考虑

密钥更新不能替代完整握手

虽然 KeyUpdate 机制提供了高效的密钥轮换,但以下情况仍需要完整重新握手:

  • 证书即将过期
  • 需要验证客户端身份(客户端证书)
  • 安全策略要求定期重新验证

计数器溢出风险

TLS 1.3 使用 64 位计数器跟踪加密数据块。虽然实际使用中几乎不可能达到上限,但设计系统时应考虑这一限制。

与 0-RTT 的关系

0-RTT 早期数据(Early Data)与密钥更新是独立的机制。0-RTT 用于首次握手时的快速数据传输,而 KeyUpdate 用于会话中的密钥轮换。

总结

TLS 1.3 的密钥更新机制是一个设计精巧的安全特性:

  • 高效:仅需几字节消息,无需完整握手
  • 安全:保持前向安全性,降低单密钥加密数据量
  • 透明:对应用层透明,无需额外开发

在实际部署中,只要使用支持 TLS 1.3 的服务器(如 Nginx、Apache、OpenSSL 1.1.1+),密钥更新机制会自动生效,为 TLS 连接提供持续的安全保护。


参考来源