4. TLS 协议基础:握手协议、记录协议、密码套件与 TLS 1.2/1.3 对比
各位同学,欢迎来到第四讲。今天我们要啃一块硬骨头——TLS 协议本身。说实话,很多搞了几年安全的工程师,对 TLS 的理解也停留在“哦,就是那个 HTTPS 用的加密协议”。但咱们做硬件密钥和双向认证,必须把它的骨架摸清楚。
我当年第一次调试硬件密钥与服务器的双向认证时,抓包看到一堆 Client Hello、Server Hello、Certificate Verify,完全懵了。后来花了整整一周,把 RFC 5246 和 RFC 8446 翻了个底朝天,才算真正搞明白。今天我把这些经验浓缩一下,带你快速掌握 TLS 的核心。
4.1 TLS 协议栈:两层结构
TLS 协议其实分两层:底层是记录协议,上层是握手协议(以及告警协议、Change Cipher Spec 协议等)。你可以把记录协议想象成“运输卡车”,握手协议就是“车上装的货物”。
核心理解:记录协议负责数据的分片、压缩、加密、完整性校验;握手协议负责身份认证和密钥协商。两者配合,才能完成一次安全的 TLS 连接。
先看记录协议。它做的事情很简单:把上层数据切成不超过 2^14 字节的片段,然后选择是否压缩(现在基本不压缩了),再用当前会话的密钥加密,最后加上 MAC 标签。每个记录都有一个 5 字节的头部,包含内容类型、版本号和长度。
// TLS 记录协议头部结构(5 字节)
struct {
ContentType type; // 1 字节:20=ChangeCipherSpec, 21=Alert, 22=Handshake, 23=Application
ProtocolVersion version; // 2 字节:如 0x0303 表示 TLS 1.2
uint16 length; // 2 字节:负载长度
} RecordHeader;
我个人习惯在抓包时先看记录层头部。如果看到 type=22,说明这是握手消息;type=23 就是应用数据。这个区分在调试双向认证时特别有用——你能一眼看出握手卡在哪一步。
4.2 握手协议:TLS 1.2 的完整流程
握手协议才是重头戏。咱们先看 TLS 1.2 的完整握手流程,因为硬件密钥和双向认证主要基于这个版本。
提示:下面的流程图展示了 TLS 1.2 完整握手(含双向认证)的 10 个核心步骤。建议你对照着看后面的文字说明。
咱们一步步拆解这个流程。我当年第一次看这个图,觉得好复杂,但后来发现其实就三个大阶段:
- Hello 阶段(步骤 1-5):双方打招呼,交换能力(密码套件、随机数、扩展),服务端发送证书并可选地请求客户端证书。
- 密钥交换阶段(步骤 6-8):客户端发送证书(如果被请求)、交换预主密钥、用私钥签名证明身份。
- 完成阶段(步骤 9):双方切换加密套件,发送 Finished 消息验证握手完整性。
注意:在双向认证中,步骤 ④ 和 ⑥ 是必须的。我曾经遇到一个项目,服务端发了 CertificateRequest,但客户端证书里没有包含数字签名密钥用法(Key Usage),结果握手一直失败。抓包看了半天才发现是证书扩展字段的问题。
4.3 密码套件:TLS 的“加密配方”
密码套件(Cipher Suite)是 TLS 握手中协商的核心内容。它定义了四个关键算法:密钥交换算法、认证算法、对称加密算法、消息认证码算法。
举个例子,TLS 1.2 中常见的密码套件:
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
│ │ │ │ │
│ │ │ │ └── 伪随机函数 (PRF) 和 MAC 算法
│ │ │ └──────────── 对称加密算法 (AES-128-GCM)
│ │ └────────────────────── 认证算法 (RSA 签名)
│ └──────────────────────────── 密钥交换算法 (ECDHE)
└─────────────────────────────────── TLS 协议版本
我个人建议,在硬件密钥场景中优先选择 ECDHE 作为密钥交换算法。为什么?因为 ECDHE 支持前向安全性(PFS),即使服务器私钥泄露,之前的会话也无法被解密。RSA 密钥交换就不行。
| 组件 | 常见选项 | 硬件密钥适配建议 |
|---|---|---|
| 密钥交换 | ECDHE, DHE, RSA | 优先 ECDHE(P-256 或 X25519) |
| 认证 | RSA, ECDSA, EdDSA | 硬件密钥通常支持 RSA 2048/4096 和 ECDSA P-256 |
| 对称加密 | AES-GCM, ChaCha20-Poly1305 | AES-GCM 硬件加速常见,ChaCha20 适合无 AES 加速的 IoT 设备 |
| MAC/PRF | SHA256, SHA384 | SHA256 足够,SHA384 用于更高安全等级 |
避坑指南:我曾经在调试一款国产硬件密钥时,发现它只支持 RSA 密钥交换,不支持 ECDHE。结果和现代服务器(只开 ECDHE 套件)握手失败。解决方案是让服务器降级支持 RSA 套件,或者换硬件。所以采购硬件密钥时,一定要确认它支持的密码套件列表。
4.4 TLS 1.3:握手协议的革命
TLS 1.3(RFC 8446)是 TLS 协议的一次重大升级。它把握手从 2-RTT 降到了 1-RTT(甚至 0-RTT),同时砍掉了一堆不安全的算法。
咱们直接对比一下 TLS 1.2 和 1.3 的核心差异:
| 对比维度 | TLS 1.2 | TLS 1.3 |
|---|---|---|
| 握手延迟 | 2-RTT(完整握手) | 1-RTT(完整握手),0-RTT(会话恢复) |
| 支持的密码套件 | 37+ 种(含大量不安全套件) | 5 种(仅 AEAD 套件) |
| 密钥交换 | 支持 RSA、DH、ECDH 等多种 | 仅支持 (EC)DHE,强制前向安全 |
| 握手消息加密 | Finished 消息后才加密 | ServerHello 之后即加密 |
| 会话恢复 | Session ID / Session Ticket | PSK + (EC)DHE 或 PSK-only |
| 证书认证 | 支持双向认证 | 支持双向认证(但实现更复杂) |
你可能会问:TLS 1.3 这么好,为什么硬件密钥场景还在大量用 TLS 1.2?
原因有三:
- 兼容性:很多硬件密钥的固件比较老,只实现了 TLS 1.2 协议栈。升级固件成本高、风险大。
- 双向认证复杂度:TLS 1.3 的 CertificateVerify 消息计算方式变了,而且握手消息加密后,调试难度大增。我见过好几个团队在 TLS 1.3 双向认证上踩坑。
- 0-RTT 的安全风险:0-RTT 存在重放攻击问题,不适合金融级双向认证场景。
我的建议:如果项目从零开始,且硬件支持 TLS 1.3,优先用 1.3。但如果要兼容现有硬件密钥生态,TLS 1.2 仍然是稳妥选择。咱们这门课会同时覆盖两个版本,但重点放在 TLS 1.2 上,因为硬件密钥双向认证的实战场景中,TLS 1.2 占了 80% 以上。
4.5 记录协议:数据加密与完整性保护
握手完成后,应用数据就通过记录协议传输了。TLS 1.2 的记录协议使用MAC-then-Encrypt(先计算 MAC,再加密),而 TLS 1.3 强制使用AEAD(认证加密,如 AES-GCM)。
这里有个历史教训。TLS 1.2 的 MAC-then-Encrypt 方案存在填充预言攻击(Padding Oracle Attack),比如著名的 POODLE 攻击。所以后来大家都转向 AEAD 了。
// TLS 1.2 记录协议加密流程(以 AES-CBC + HMAC-SHA1 为例)
原始数据: [应用数据]
步骤 1: 计算 MAC → [应用数据 | MAC]
步骤 2: 填充 → [应用数据 | MAC | 填充]
步骤 3: 加密 → [IV | 密文]
// TLS 1.3 记录协议加密流程(以 AES-128-GCM 为例)
原始数据: [应用数据]
步骤 1: 直接 AEAD 加密 → [Nonce | 密文 | 认证标签]
在硬件密钥场景中,记录协议有一个关键点:序列号。每个记录都有一个 64 位的序列号,用于防止重放攻击。硬件密钥内部需要维护这个序列号状态,掉电后要能恢复。我见过一个产品,掉电后序列号重置为 0,结果被攻击者重放旧记录,导致业务异常。
注意:如果你在硬件密钥中实现 TLS 协议栈,务必把序列号存储在非易失性存储器中,并且每次写入前做磨损均衡。否则频繁写入会烧坏 Flash。
4.6 小结:TLS 协议的核心要点
好了,这一章的内容不少。咱们回顾一下核心要点:
- TLS 协议分两层:记录协议(加密传输)和握手协议(认证和密钥协商)。
- TLS 1.2 完整握手需要 2-RTT,双向认证增加 CertificateRequest 和 CertificateVerify 两个步骤。
- 密码套件由四个算法组成,硬件密钥场景优先选择 ECDHE + ECDSA + AES-GCM + SHA256。
- TLS 1.3 更快、更安全,但硬件兼容性和双向认证实现复杂度是主要挑战。
- 记录协议的序列号管理在硬件实现中是个容易忽略的坑。