2. TLS协议栈与握手流程

TLS 并不是一个单一协议,而是一套分层协作的协议族。理解 TLS,关键不是背握手报文的顺序,而是看清两条主线:一条是握手层负责“谈条件、验身份、算密钥”,另一条是记录层负责“把应用数据切成记录、加密、加完整性保护再发出去”。两者分工明确,又通过握手阶段协商出的密钥与算法参数紧密耦合。

一、协议栈分层:谁负责什么

从下往上看,TLS 位于可靠的传输层(通常是 TCP)之上、应用层(HTTP、SMTP 等)之下。它自身分为两层:

  • 记录层(Record Layer):TLS 的“运输底座”。它接收上层交来的数据,按不超过 2^14 字节(16384 字节)的明文分片上限切分为记录,然后依次执行压缩(现代 TLS 已禁用)、计算 MAC、加密,最后加上 5 字节记录头(内容类型 1 字节、版本 2 字节、长度 2 字节)发出。接收方反向执行解密、校验 MAC、重组。
  • 握手层(Handshake Layer):包含握手协议、警报协议、变更密码规格协议和应用数据协议。握手协议负责协商版本、密码套件、交换密钥材料、认证对端;警报协议传递 close_notify、handshake_failure 等状态;变更密码规格协议在 TLS 1.2 中用于切换后续记录的加密状态。

需要特别强调:握手报文本身也是通过记录层传输的。握手初期,记录层以明文承载握手消息;当双方通过 ChangeCipherSpec 或 TLS 1.3 的密钥派生切换到加密状态后,后续握手消息(如 Finished)就在加密记录中传输。这就是“分层协作”的第一层含义。

二、握手流程:从 ClientHello 到 Finished

以 TLS 1.2 的完整握手(ECDHE 密钥交换)为例,典型报文序列如下:

  1. ClientHello:客户端发送支持的 TLS 版本、密码套件列表、随机数 ClientRandom、扩展(如 SNI、supported_groups、signature_algorithms)。
  2. ServerHello:服务端选定版本与密码套件,返回 ServerRandom,并附带密钥交换参数(如 ECDHE 的 ServerKeyExchange 中的公钥与签名)。
  3. Certificate:服务端发送证书链,供客户端验证身份。
  4. ServerHelloDone:服务端表示握手第一阶段结束。
  5. ClientKeyExchange:客户端生成 ECDHE 私钥,计算共享密钥,发送自己的公钥。
  6. ChangeCipherSpec + Finished:客户端切换加密状态,发送用新密钥保护的 Finished,其中包含对之前所有握手消息的哈希校验。
  7. 服务端 ChangeCipherSpec + Finished:服务端同样切换并回送 Finished。

握手完成后,双方各自持有:主密钥(Master Secret)→ 通过 PRF 派生出客户端写 MAC 密钥、服务端写 MAC 密钥、客户端写加密密钥、服务端写加密密钥、客户端写 IV、服务端写 IV。这些参数正是记录层后续加解密所依赖的“密钥材料”。

三、密钥交换、认证与完整性的协作

三者不是并列关系,而是有明确的因果链:

  • 密钥交换解决“双方如何在不安全信道上得到相同的共享秘密”。ECDHE 利用椭圆曲线离散对数难题,双方各自生成临时私钥,交换公钥后独立计算出相同的共享点,即使长期私钥泄露也不影响本次会话密钥(前向保密)。
  • 认证解决“我怎么确认对面真的是它”。服务端用证书私钥对握手参数签名,客户端用证书链中的公钥验签,并校验证书有效期、域名、吊销状态。认证失败则握手终止,不会进入加密通信。
  • 完整性解决“数据有没有被篡改”。TLS 1.2 使用 HMAC(如 HMAC-SHA256)对记录内容计算 MAC,与密文一起发送;TLS 1.3 改用 AEAD(如 AES-GCM、ChaCha20-Poly1305),加密与认证一次完成。Finished 消息则是对全部握手消息的哈希做完整性校验,防止握手被中间人篡改。

协作要点:密钥交换产生共享秘密 → PRF 派生会话密钥 → 记录层用会话密钥加密并计算 MAC → Finished 用握手哈希验证整个握手过程未被篡改。任何一环失败,TLS 都会以警报终止连接,而不是降级继续。

四、TLS 1.3 的关键变化

TLS 1.3 对上述机制做了大幅精简:

  • 废弃 RSA 密钥交换,只保留 (EC)DHE,强制前向保密。
  • 握手从 2-RTT 缩短为 1-RTT,并支持 0-RTT 会话恢复(但 0-RTT 数据不具备前向保密,需谨慎使用)。
  • 记录层统一使用 AEAD,删除显式 MAC-then-Encrypt 模式。
  • ServerHello 之后立即派生握手密钥,后续握手消息全部加密,证书也不再明文暴露。
  • ChangeCipherSpec 仅作为兼容性占位,不再承担密钥切换语义。

下面这张图展示了 TLS 1.2 完整握手中,握手层与记录层的协作关系,以及密钥交换、认证、完整性三条线索如何汇聚到记录层的加密通道。

TLS 1.2 完整握手:握手层与记录层协作机制 客户端 Client 服务端 Server ClientHello:版本 / 密码套件 / ClientRandom / SNI ServerHello:选定套件 / ServerRandom Certificate + ServerKeyExchange(ECDHE 公钥 + 签名) ClientKeyExchange:客户端 ECDHE 公钥 ChangeCipherSpec + Finished(加密) ChangeCipherSpec + Finished(加密) PRF 密钥派生 Master Secret → 客户端/服务端 写MAC密钥 · 写加密密钥 · 写IV 认证 证书链验签 域名 / 有效期 / 吊销 完整性 HMAC-SHA256 Finished 握手哈希 记录层 Record Layer 分片 ≤ 16384B → 计算 MAC → AEAD 加密 → 加 5 字节记录头 内容类型:handshake / alert / change_cipher_spec / application_data 图例 客户端→服务端 服务端→客户端

五、实践中的观察与判断

在实际排障或验证时,可以用以下方式观察上述协作是否正常:

  • 用 openssl s_client -connect example.com:443 -tls1_2 -msg 可以看到每条握手报文的明文十六进制,确认 ClientHello 中的密码套件与 ServerHello 的选定结果是否匹配。
  • 用 Wireshark 过滤 tls.handshake,观察 Certificate 之后是否出现 ServerKeyExchange;若使用 RSA 密钥交换则没有该报文,这也是判断是否具备前向保密的依据。
  • 若握手在 Certificate 之后立即出现 alert(level=fatal, description=bad_certificate),说明认证环节失败,应检查证书链是否完整、SNI 是否匹配、系统时间是否正确。
  • 若 Finished 校验失败,通常表现为 decrypt_error 或 bad_record_mac,说明密钥派生或握手哈希不一致,常见原因是中间设备篡改了握手报文或双方版本/套件协商不一致。

判断要点:只要握手成功完成,记录层就获得了可用的会话密钥;此后所有应用数据都走加密记录。若应用层出现明文或 MAC 错误,问题通常不在应用代码,而在记录层的密钥状态或中间设备干预。

总结来看,TLS 的协作机制可以概括为:握手层负责“建立信任与密钥”,记录层负责“用密钥保护数据”,密钥交换提供共享秘密,认证确认对端身份,完整性防止篡改。四者环环相扣,任何一环缺失或失败,TLS 都不会降级为明文继续通信,这正是它作为传输安全基石的可靠性来源。