1. TLS协议基础:TLS握手过程、TLS记录协议、TLS与TCP的关系
好,咱们直接进入正题。TLS 这个协议,说白了就是给 TCP 套了一层加密壳。你想想看,TCP 本身是明文传输的,数据在网络上裸奔,谁都能抓包看到。TLS 就是来解决这个问题的。
我个人习惯把 TLS 分成两层来看:握手层和记录层。握手层负责建立安全连接,记录层负责加密传输数据。这两层配合起来,才是一个完整的 TLS 连接。
核心要点:TLS 不是替代 TCP,而是在 TCP 之上增加安全特性。没有 TCP 的可靠传输,TLS 的加密就无从谈起。
1.1 TLS握手过程
握手过程,我当年刚接触时觉得挺复杂。其实拆开来看,就三步:协商密码套件、身份认证、生成会话密钥。
我记得有一次排查线上问题,发现握手耗时特别长。后来定位到是证书链太长,客户端验证花了太多时间。嗯,这里要注意,握手阶段的性能优化往往被忽略。
标准的 TLS 1.2 握手流程大致如下:
- ClientHello:客户端发送支持的 TLS 版本、密码套件列表、随机数。
- ServerHello:服务端选择密码套件,发送自己的随机数。
- Certificate:服务端发送证书链(包含公钥)。
- ServerKeyExchange:如果需要,发送密钥交换参数(如 DH 参数)。
- ServerHelloDone:服务端说“我发完了”。
- ClientKeyExchange:客户端生成预主密钥,用服务端公钥加密后发送。
- ChangeCipherSpec:双方切换加密模式。
- Finished:双方验证握手消息的完整性。
这里有个坑,我踩过。TLS 1.3 简化了握手,从 2-RTT 降到了 1-RTT。但很多老系统不支持,你升级时得做好兼容性测试。
实战建议:如果客户端和服务端都在内网,延迟低,TLS 1.2 完全够用。如果跨公网,尤其是有海外节点,强烈建议上 TLS 1.3,能省一个 RTT 的时间。
1.2 TLS记录协议
握手完成后,数据就走记录协议了。记录协议是 TLS 的数据传输单元,每个记录包含:类型、版本、长度、负载、MAC(可选)。
你想想看,应用层发过来的数据,TLS 会先分片,然后压缩(可选),加上 MAC,最后加密。加密后的数据再交给 TCP 去传输。
记录协议的结构如下:
| 字段 | 长度 | 说明 |
|---|---|---|
| Content Type | 1 字节 | 20=ChangeCipherSpec, 21=Alert, 22=Handshake, 23=Application Data |
| Protocol Version | 2 字节 | 如 0x0303 表示 TLS 1.2 |
| Length | 2 字节 | 负载长度,最大 16384 字节 |
| Payload | 可变 | 加密后的数据 |
| MAC | 可变 | 消息认证码,用于完整性校验 |
这里有个关键点:TLS 记录的最大长度是 16KB。如果应用层一次发送的数据超过 16KB,TLS 会帮你分片。但分片后的每个记录,到了 TCP 层可能还会被继续拆分。
注意:很多人在做粘包拆包时,只考虑了 TCP 的粘包,忽略了 TLS 记录层也可能产生粘包。实际上,TLS 记录层的数据在 TCP 层面仍然是流式的,你需要自己处理边界。
1.3 TLS与TCP的关系
这个关系,说白了就是层叠关系。TCP 提供可靠传输,TLS 在 TCP 之上提供加密和认证。没有 TCP,TLS 无法保证数据不丢包、不乱序。没有 TLS,TCP 的数据就是明文。
我画了一张图,帮你理解这个关系:
从这张图你能看到,TLS 在 TCP 之上,但 TLS 本身也有自己的分片机制。这就引出了我们这门课的核心问题:粘包拆包。
TCP 是流式协议,没有消息边界。你 send 两次,对方可能一次 recv 就全收到了(粘包),也可能分多次 recv 才能收完(拆包)。TLS 记录协议虽然有自己的长度字段,但它在 TCP 流中也是连续存放的,所以同样面临粘包拆包问题。
我曾经在一个高并发网关项目中,遇到 TLS 连接下数据错乱的问题。排查了半天,发现是粘包处理逻辑只考虑了 TCP 层,没考虑 TLS 记录层。后来在 TLS 记录层也加了长度判断,问题就解决了。
关键结论:TLS 连接下的粘包拆包,本质上是两层问题:TCP 层的流式传输 + TLS 记录层的分片。处理时需要在 TLS 记录层根据长度字段进行拆包,而不是依赖 TCP 的边界。
1.4 小结
这一章我们聊了 TLS 的基础:握手过程、记录协议、以及与 TCP 的关系。核心就一句话:TLS 是 TCP 的安全增强,但两者在数据传输上都是流式的,都需要处理粘包拆包。