1. TLS 协议概述

TLS 做安全传输的基石。这章我们不绕弯子,直接把 TLS 的来历、握手流程和加密套件说清楚。你在项目中肯定迟早要跟它打交道。

1.1 从 SSL 到 TLS:一点历史

说到 TLS,就绕不开它的前身 SSL。我记得刚开始做网络编程那会儿,还在用 SSLv3。后来爆出 POODLE 漏洞,整个行业紧急迁移到 TLS 1.0。嗯,那次升级真是让人头疼——好多老旧设备不兼容。

  • SSL 1.0 (1994) —— 从未公开发布,据说有严重安全问题。
  • SSL 2.0 (1995) —— 第一个商用版,但漏洞太多,现在谁还敢用?
  • SSL 3.0 (1996) —— 相对成熟,但 2014 年被 POODLE 击穿。我建议直接把它 drop 掉。
  • TLS 1.0 (1999) —— 实际是 SSL 3.1,改了个名。现在已弃用。
  • TLS 1.1 (2006) —— 加了 CBC 保护,但不够彻底。
  • TLS 1.2 (2008) —— 目前最广泛使用,支持 AEAD、GCM。很多硬件加速器就是针对它做的。
  • TLS 1.3 (2018) —— 握手从 2-RTT 降到 1-RTT,还砍掉了一堆过时套件。我在项目中遇到过 TLS 1.3 的 0-RTT 模式,延迟确实低,但重放攻击风险也要留意。

避坑指南:我之前有个项目一直用 TLS 1.2,后来发现某些客户端强制协商成 TLS 1.0。查了半天,是因为服务器上残留了 SSLv2 的兼容代码。建议直接禁用所有低于 TLS 1.2 的版本,除非你有很老的设备要兼容。

说白了,TLS 版本演进就是在“安全”和“性能”之间反复博弈。

1.2 TLS 握手流程:到底在干嘛?

握手是 TLS 最核心的环节。客户端和服务器通过四步(或者三步,取决于版本)协商出对称密钥。你想想看,公钥加密太慢,所以只用来交换一个临时密钥。后面的大流量都由对称加密保护。

下面这张 SVG 描述了典型的 TLS 1.2 全握手流程,你可以把它当作对照图来理解接下来的文字。

TLS 1.2 全握手流程 客户端 Client Hello 服务器 Server Hello + Cert + ServerHelloDone 客户端 ClientKeyExchange 服务器 计算主密钥 客户端 ChangeCipherSpec + Finished 服务器 ChangeCipherSpec + Finished 实际流程中,Certificate、ServerKeyExchange(DHE/ECDHE时需要)等消息可合并。

握手具体做这几件事:

  1. Client Hello:客户端发送支持的 TLS 版本、加密套件列表、随机数。
  2. Server Hello + 证书:服务器选定套件和版本,发回证书(含公钥),同时给随机数。
  3. 密钥交换:客户端验证证书后,用服务器公钥加密一个预主密钥(Pre-Master Secret)发过去;或者用 ECDHE 生成临时密钥对。
  4. 双方计算主密钥:然后各自生成会话密钥。
  5. Change Cipher Spec + Finished:告诉对方“我要开始用这个密钥加密了”,并发一段验证密文。对方解密成功就握手完成。

性能关键点:握手延迟主要来自两次网络 RTT(TLS 1.2 全握手)。如果是 ECDHE 密钥交换,还涉及椭圆曲线计算。我在之前做移动端 SDK 时,发现每次握手多花 50ms,用户体验就很明显。后来切了 TLS 1.3 的 1-RTT 握手,效果拔群。

1.3 加密套件:一串神秘字符解码

你见过这种字符串吧:TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256。说白了,它把四样东西打包在一起:密钥交换算法、身份认证算法、对称加密算法、消息认证码(MAC)或 AEAD。

下面这个表格总结了常见套件的组成,方便你对照。

套件示例 密钥交换 认证 对称加密 MAC / AEAD 适用场景
ECDHE-RSA-AES128-GCM-SHA256 ECDHE RSA AES-128-GCM GCM (AEAD) 通用,主流
ECDHE-ECDSA-AES256-GCM-SHA384 ECDHE ECDSA AES-256-GCM GCM 高安全 & 硬件加速支持
RSA-WITH-AES-128-CBC-SHA RSA RSA AES-128-CBC SHA-1 (HMAC) 老设备 (不建议)
CHACHA20-POLY1305 ECDHE RSA 或 ECDSA ChaCha20 Poly1305 (AEAD) 移动端 & 低功耗

讲两点我踩过的坑。

  • 关于 CBC 模式:以前很多套件用 AES-CBC,但遭遇了 Lucky13、BEAST 等攻击。即使打了补丁,效率也不如 GCM。我建议新系统全用 AEAD 套件(GCM 或 ChaCha20-Poly1305)。
  • 关于 ECDHE vs RSA 密钥交换:RSA 密钥交换不支持前向安全性——如果服务器私钥泄露,之前所有会话加密都能被破解。ECDHE 每次握手生成临时密钥,属于“一次一密”。在我的项目里,一律强制要求 ECDHE。
# 查看 OpenSSL 支持的套件(只显示 TLSv1.2 的 ECDHE 套件)
openssl ciphers -v 'ECDHE:!aNULL:!eNULL' | head -10
# 输出示例:
# ECDHE-ECDSA-AES128-GCM-SHA256  TLSv1.2 Kx=ECDH  Au=ECDSA  Enc=AESGCM(128) Mac=AEAD
# ECDHE-RSA-AES128-GCM-SHA256    TLSv1.2 Kx=ECDH  Au=RSA    Enc=AESGCM(128) Mac=AEAD

警告:不要随便把 ALL:COMPLEMENTOFDEFAULT 放进套件列表。我曾经在测试环境图省事,结果协商到了一个 56-bit DES 套件,Wireshark 一点就能解密。生产环境一定要显式指定白名单。

总结下这一节的核心:

  • 了解 TLS 版本历史,避免用老版本。
  • 握手流程的核心是协商密钥,延迟大头在网络 RTT 和公钥运算。
  • 加密套件选择:AEAD + ECDHE 是标配,放弃 CBC 和静态 RSA。