第4章 常见密码套件详解

密码套件这个名字听起来挺唬人的,说白了就是“一组加密算法的组合”。我刚开始搞TLS配置时,面对一堆像 TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 这样的长串,直接懵了。后来拆开看,其实每个部分都很清晰。

4.1 密码套件的骨架

一个标准的密码套件由四部分组成:密钥交换、身份认证、对称加密、消息认证码(MAC)或AEAD模式。拿最经典的一个例子来拆解:

TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
│      │      │       │        └── 哈希算法(用于PRF和HMAC)
│      │      │       └────────── 对称加密算法及其模式
│      │      └────────────────── 身份认证算法
│      └───────────────────────── 密钥交换算法
└──────────────────────────────── 协议前缀

你想想看,加密通信就像两个人隔着墙说话。密钥交换负责商量一个临时钥匙,身份认证确保对方不是冒充的,对称加密用来快速保密传输内容,哈希算法保证数据没被篡改。缺一个都不行。

组件常见算法示例作用
密钥交换ECDHE、DHE、RSA协商对称密钥(前向安全性依赖这里)
身份认证RSA、ECDSA、DSA验证服务器/客户端身份
对称加密AES_128_GCM、AES_256_GCM、CHACHA20实际数据加密
哈希+MACSHA256、SHA384、POLY1305校验完整性及派生密钥

小经验:我习惯在配置前先看一眼套件名里的 ECDHE 或 DHE,只要有这个前缀,基本就支持前向安全了。没有?那就要小心了。

4.2 TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384

这个套件是目前互联网上最常用的组合之一。拆开看:

  • ECDHE:椭圆曲线临时密钥交换,每次握手生成新的临时密钥对,就算服务端的长期私钥泄露,之前的会话也无法被解密(前向安全性)。
  • RSA:用RSA证书来签名验证身份。ECDHE的临时公钥由RSA证书签名,客户端验证签名确认服务器身份。
  • AES_256_GCM:AES 256位密钥,GCM模式同时提供加密和完整性校验,属于AEAD算法。出错了不会泄露明文。
  • SHA384:用于伪随机函数(PRF)和握手消息的HMAC,比SHA256更安全。

我在项目里曾经遇到过一个案例:客户要求最高安全等级,但又必须兼容老旧Java 6客户端。ECDHE_RSA这套在大多数TLS 1.2实现里都跑得很好,但Java 6默认不支持GCM,得额外安装JCE包。嗯,这里要注意:高安全性往往意味着兼容性要付出代价。

推荐场景:需要前向安全性、对性能有一定要求、客户端较新(TLS 1.2+)的环境。这个套件在Nginx、Apache、OpenSSL 1.0.1+中均支持。

4.3 TLS_AES_128_GCM_SHA256

这是TLS 1.3的标配套件之一。TLS 1.3砍掉了大量不安全的交换和加密选项,密码套件只有五个左右。AES_128_GCM_SHA256 是最推荐的一个:

  • 无显式密钥交换描述:TLS 1.3将密钥交换和身份认证移到了握手扩展中,套件名称只保留对称加密和哈希部分。
  • AES_128_GCM:128位密钥,GCM模式,性能优于256位,且安全强度足够(至少在量子计算普及前)。
  • SHA256:用于HKDF和握手完整性。

我个人特别喜欢TLS 1.3的这个设计——简单、高效、前向安全默认开启。配置TLS 1.3时,我基本只开 TLS_AES_128_GCM_SHA256 和 TLS_AES_256_GCM_SHA384 两个,偶尔加一个 TLS_CHACHA20_POLY1305_SHA256 给移动端备选。

套件TLS版本密钥长度前向安全性能
ECDHE_RSA_AES_256_GCM_SHA3841.2256是中等(DH计算)
AES_128_GCM_SHA2561.3128是(默认)高(硬件加速)
ECDHE_ECDSA_AES_256_GCM_SHA3841.2256是高(椭圆曲线签名)

别踩这个坑:我曾经在迁移到TLS 1.3时,想当然地把所有TLS 1.2的套件都复制到1.3配置里,结果发现服务端根本启动不了。TLS 1.3的密码套件名不带密钥交换前缀,直接写 AES_256_GCM_SHA384 就行,别手滑写上 ECDHE_RSA_...。

4.4 其他常见套件速览

除了上面两个,下面几个也经常遇到:

  • TLS_CHACHA20_POLY1305_SHA256:TLS 1.3套件,移动端首选。软件实现快,没有AES硬件加速时性能优势明显。
  • TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:TLS 1.2套件,用ECC证书代替RSA,签名更快、密钥更短。我给自己搭的内部CA就用ECDSA证书,配合这套,握手快得飞起。
  • TLS_DHE_RSA_WITH_AES_256_CBC_SHA256:老套件了——DHE虽然前向安全,但CBC模式容易受padding oracle攻击,SHA256也不如GCM安全。建议直接禁用。
密码套件选择决策流程 服务端配置的所有密码套件 TLS 1.3 套件(自动前向安全) TLS 1.2 及以下(需额外检查) AEAD模式(GCM/ChaCha) 支持前向安全性 性能与兼容性 最终推荐的组合 AES_128_GCM_SHA256 / AES_256_GCM_SHA384 / CHACHA20_POLY1305 避坑提醒 不要混合配置TLS 1.2的CBC套件和1.3的AEAD套件在同一优先级中 建议1.3套件放前面,1.2兼容套件放后面

4.5 配置时的个人建议

回到实际配置环节,我一般会按以下优先级排序:

  1. TLS 1.3 套件:TLS_AES_128_GCM_SHA256 或 TLS_AES_256_GCM_SHA384,现代浏览器都支持,性能最好。
  2. TLS 1.2 首选:TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,如果你用RSA证书的话;如果用的是ECC证书,换成 TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384。
  3. 移动端兜底:TLS_CHACHA20_POLY1305_SHA256,尤其针对没有AES-NI的ARM设备。
  4. 不推荐的:所有CBC模式套件、RSA密钥交换套件(无前向安全)、RC4、3DES——这些我全部从配置里删掉。

一句话总结:密码套件不是越多越好。你想想看,每次握手时服务器和客户端要从列表里挨个试,list太长反而拖慢握手速度。我个人习惯只留6-8个套件,覆盖TLS 1.3和1.2的主流组合就够了。

4.6 一个真实例子

去年帮一个金融客户优化TLS配置,他们的Nginx配置里还挂着十几年前的老套件,比如 TLS_RSA_WITH_RC4_128_SHA。我查了日志,发现还有少量客户端在用,后来确认全部是老旧POS机。最终决定:在单独的子域名上提供降级配置,主域名只允许现代套件。改造完成后,TLS握手时间从平均120ms降到了45ms。嗯,虽然跟套件数量不完全直接相关,但清掉那些老古董确实让协商路径变短了。

千万不要:不要为了兼容而贪心保留所有套件。我曾经见过一份配置同时开了ECDHE_RSA和DHE_RSA,还有ECDHE_ECDSA,甚至还有已废弃的DHE_DSS——结果导致连接时双方协商出的总是最慢的DHE_DSS(因为某些客户端的偏好问题)。只保留你真正需要的那几类。