3. TLS协议体系结构:记录协议与握手协议
TLS协议栈,说白了就是一套分层设计的“安全通信脚手架”。我刚开始接触的时候也觉得很绕,什么记录层、握手层、警报协议……其实理清脉络就很简单。你想想看,如果把TLS比作一栋房子,那记录协议就是地基,负责搬运和封装数据;握手协议是施工队,负责建立信任关系;而警报协议就是火灾报警器——平时看不见,有事才出声。
3.1 整体架构:协议栈的层次关系
咱们先从上往下看这张层次图。应用层的HTTP、FTP这些数据,先被TLS“套”进来,然后一层层处理,最后交给TCP发送。我个人习惯把这四层协议画成一张垂直的栈图,贴在工位上。
从上图可以看到:应用数据先被握手子层处理(比如协商密钥、交换证书),然后交给记录协议进行分片、加密、添加MAC,最后塞进TCP的字节流里。嗯,这里要注意:记录协议是真正的“搬运工”,其他所有子层的数据最终都得通过它来发送。
3.2 记录协议:TLS的“运输工”
记录协议负责把上层数据拆成固定大小的“记录”,然后挨个处理。每个记录都包含内容类型、版本、长度、负载和MAC。我在项目中遇到过某款老旧设备,硬是把记录长度设为16KB,结果客户端频繁超时——后来才发现是分片策略太粗糙。
3.4.1 记录格式
struct {
ContentType type; // 1字节:握手(22)、警报(21)、应用数据(23)
ProtocolVersion version; // 2字节:03 03 代表TLS 1.2
uint16 length; // 2字节:负载长度,最大2^14字节
opaque fragment[length]; // 变长:实际加密数据
} TLSRecord;
每个记录头只有5个字节。你想想看,这5个字节里的内容类型,决定了接收方怎么解析后续数据。比如type=22,说明这是握手消息;type=23,说明这是加密后的应用数据。
3.4.2 记录协议处理流程
记录协议的处理分三个步骤:接收上层数据后,先分片,然后压缩(可选,TLS 1.3已废除),最后加密+MAC。我用个表格把这些动作讲清楚。
| 步骤 | 输入 | 处理方式 | 输出 |
|---|---|---|---|
| 1. 分片 | 应用层数据(如HTTP请求) | 切分为≤2^14字节的片 | 多个TLSPlaintext |
| 2. 压缩(可选) | TLSPlaintext | Deflate/Null压缩(TLS 1.3强制Null) | TLSCompressed |
| 3. 加密+MAC | TLSCompressed + 序列号 | AES-GCM / ChaCha20-Poly1305等 | TLSCiphertext |
3.3 握手协议:TLS的“施工队”
握手协议是整个TLS的精华。它负责协商密码套件、交换密钥、验证身份。握手完成前,所有数据都是明文或在“即将加密”的状态。我常说:握手阶段就像是双方在互相打量——“你是谁?”“你用什么算法?”“你的证书是真的吗?”
3.3.1 握手流程概览
一个标准的TLS 1.2完整握手包含以下消息(我简化一下,去掉可选扩展):
- ClientHello:客户端发送支持的版本、密码套件列表、随机数
- ServerHello:服务端选择版本、一个密码套件,发回随机数
- Certificate(服务端):发送自己的X.509证书链
- ServerKeyExchange(如果需要):发送DH参数或ECDHE公钥
- ServerHelloDone:服务端说“我发完了”
- ClientKeyExchange:客户端发送自己的公钥或pre-master secret
- ChangeCipherSpec(双方):通知记录层切换加密策略
- Finished(双方):发送握手消息的MAC,验证完整性
嗯,这里要注意:ChangeCipherSpec其实不属于握手协议,它属于变更密码规范协议——但大家习惯把它看作握手的一部分。
3.3.2 密码套件在握手中的作用
握手过程中,密码套件像一个“菜单”。客户端在ClientHello里列出它支持的菜(比如TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256),服务端从里面选一个最合胃口的。选好之后,双方就知道用什么密钥交换、用什么对称加密、用什么哈希。
我把密码套件拆开讲,它包含四个部分:
- 密钥交换算法:如ECDHE、DHE、RSA
- 身份验证算法:如RSA、ECDSA、DSS
- 对称加密算法+模式+密钥长度:如AES_128_GCM、ChaCha20_Poly1305
- PRF和MAC算法:如SHA256、SHA384
举个例子,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256的含义是:
| 组件 | 值 | 说明 |
|---|---|---|
| 密钥交换 | ECDHE | 椭圆曲线Diffie-Hellman临时密钥,提供前向安全性 |
| 身份验证 | RSA | 用RSA签名验证服务端证书 |
| 对称加密 | AES_128_GCM | 128位密钥的AES-GCM模式,支持认证加密 |
| MAC/PRF | SHA256 | 用于密钥派生和消息认证 |
3.4 警报协议:TLS的“故障灯”
警报协议是个低调的角色。它只有两个消息:warning和fatal。fatal警报一旦收到,连接必须立即关闭。我见过不少开发者在代码里把fatal当warning处理——结果数据错乱、会话重建,白白浪费时间。
常见的警报类型:
- close_notify:正常关闭连接通知
- unexpected_message:收到意料之外的消息
- bad_record_mac:MAC校验失败(加密数据被篡改)
- handshake_failure:握手协商失败
- decrypt_error:解密失败(可能是密钥不匹配)
3.5 小结与核心要点
回看这张协议栈图,我想强调三点:
- 记录协议是基石,它负责分片、加密、传输——所有上层消息最终都得经过它。
- 握手协议是核心,它决定了后续通信使用的密码套件和密钥。
- 警报协议是安全边界,它确保一旦发现异常,双方能优雅或强制地中止连接。
下次你抓包看到一堆43字节的握手记录,或者5字节的记录头,就能轻松判断:哦,这是ClientHello,那是ServerHelloDone。理解这些“小零件”,才能真正驾驭TLS。