第二章:TLS协议基础——握手协议与记录协议核心机制

做水印嵌入之前,得先搞清楚TLS到底在干什么。说白了,TLS就是给数据包穿了一层盔甲,让它既防偷看又防篡改。这层盔甲由两个子协议撑起来——握手协议和记录协议。一个管建立安全通道,一个管往通道里塞数据。

我个人习惯把TLS握手比作「第一次见面的暗号对答」。你想想看,两个陌生人要加密通信,总得先互相确认身份、商量好怎么加密吧?握手协议干的就是这件事。而记录协议呢,就是商量好以后,把每条消息都打包成一个标准信封,加个封条(MAC)再发出去。今天咱们就掰开揉碎了聊清楚这两个机制。

2.1 握手协议——从零开始搭一座安全桥

握手协议是TLS中最复杂的部分。它包含好几个子消息,依次交换后,客户端和服务器才能拿到对称密钥。我在项目中遇到过不少握手失败的情况,后面会提到几个坑。

标准的TLS 1.2握手流程大致如下:

  1. ClientHello:客户端发一个问候,告诉服务器自己支持的TLS版本、密码套件列表(比如哪些加密算法、哪些密钥交换算法)、以及一个随机数。
  2. ServerHello:服务器从客户端列表中挑一个密码套件,返回自己的随机数,并确认TLS版本。
  3. 证书(Certificate):服务器把自己的公钥证书发给客户端。
  4. ServerKeyExchange(可选):如果密码套件需要额外参数(比如DHE密钥交换),服务器把参数发过去。
  5. ServerHelloDone:服务器说“我的消息发完了”。
  6. ClientKeyExchange:客户端根据服务器公钥生成一个前主密钥(pre-master secret),加密后发过去。
  7. ChangeCipherSpec:客户端通知服务器“接下来我要用刚才协商好的密钥加密了”。
  8. Finished:客户端发一个加密的握手消息摘要,服务器验证是否匹配。
  9. 服务器端同样发送ChangeCipherSpec和Finished。

核心要点:整个握手过程中,对称密钥由客户端生成的pre-master secret、以及双方交换的随机数共同派生出来。这个密钥只用于本次会话,每次连接都不一样。

嗯,这里要注意:TLS 1.3简化了很多,只用了1-RTT(一次往返)就完成握手,但咱们做水印嵌入时,经常会跟TLS 1.2打交道,因为很多老旧系统还在用1.2。我曾经在一台银行前置机上排查握手超时问题,发现对方服务器只支持TLS 1.0……那又是一段不堪回首的经历。

2.2 记录协议——封装、加密、传输一条龙

握手完成后,应用层的数据(比如HTTP请求)就交给记录协议处理了。记录协议做的其实就三件事:

  • 分片:如果上层数据太大,切成不超过2^14字节的片段。
  • 压缩(TLS 1.2可选,但实际基本不用,因为压缩有安全漏洞CRIME,建议别开)。
  • 加密+MAC:先计算消息验证码(MAC),然后加密整个(数据+MAC),最后加上记录头。

记录头的结构很简单,只有5个字节:

字段长度说明
内容类型1字节比如0x17代表应用数据,0x14代表ChangeCipherSpec
协议版本2字节比如TLS 1.2是0x0303
长度2字节后面加密载荷的长度(不包括头)

避坑提醒:我曾经在调试中见过一个诡异的“记录协议长度字段错误”导致TLS解密失败。原因是中间代理把记录拆成了多个TCP段,但接收方没正确重组。写水印嵌入代码时,一定要保证记录数据包的完整性,最好在一个记录里完成水印嵌入,别跨记录。

为了让你看得更明白,我画了一张TLS握手与记录协议的整体工作流程SVG图,把核心消息和加密关系串起来:

TLS握手与记录协议协同工作流程 客户端 服务器 ① ClientHello ② ServerHello + 证书 + ServerHelloDone ③ ClientKeyExchange (pre-master) 密钥派生(双方) ④ ChangeCipherSpec + Finished ⑤ ChangeCipherSpec + Finished 记录协议阶段 应用数据 → 分片 → 加密 + MAC → 记录头 → 发送 接收方 → 解密 → 验证MAC → 重组 → 递交应用层 加密数据流(双向) 水印嵌入点(加密前/解密后)

小技巧:如果你需要在水印嵌入时保持TLS兼容性,最佳嵌入时机是在记录协议加密前的明文载荷里做修改,或者在解密后、重组前做提取。千万别改记录头或MAC,否则接收方验证会失败。我曾在实验里改了一个字节的MAC字段,结果整个连接断开——TLS非常敏感。

2.3 握手协议 vs 记录协议:一张表说清楚

维度握手协议记录协议
主要目的身份验证 + 密钥协商安全传输应用数据
加密状态开始时未加密,Finished后切换全程使用协商好的对称密钥加密
数据单元握手消息(长度可变)记录(≤2^14 + 头)
关键字段随机数、证书、签名内容类型、版本、MAC、密文
是否可嵌入水印理论上可以,但非常危险,可能破坏证书链推荐在应用数据载荷中操作

从我个人的经验看,绝大多数水印方案都是在记录协议层的明文载荷里做手脚。我之前帮一个视频加密项目做水印,就是在RTMP over TLS的场景里,把水印嵌入到H.264的SEI NALU里,然后在TLS记录协议加密前插入。只要不改变记录的长度和边界,就没问题。嗯,这里有一个点:有些TLS实现会改记录长度字段做填充,你得注意不要越界。

讲到这里,你应该对TLS握手和记录的骨架有感觉了。说白了,握手就是建通道前的安检,记录就是在安检后的传送带上给包裹贴封条(加密+MAC)。我们后面讲协同水印嵌入,就是在“贴封条之前”对包裹内容做一次微小修改,而且要让接收方在“拆封条之后”还能原样提取出水印。这才是这门课的核心挑战。