2、TLS协议基础:TLS 1.2与TLS 1.3核心差异、密码套件、握手流程归纳总结

做老旧系统对接时,我发现很多同事对TLS的认知还停留在“装个证书就能用”。

说实话,真正动手去调兼容性的时候,TLS 1.2和1.3的区别才是卡脖子的地方。我接手过一台银行的老旧前置机,只支持TLS 1.2,而新平台强制要求TLS 1.3。两边握手都握不上,日志里全是“handshake failure”。

所以要谈兼容性,TLS协议本身的细节必须吃透。这节我就把核心差异、密码套件和握手流程给你掰开揉碎了讲。

2.1 TLS 1.2与TLS 1.3核心差异

先理清一个概念:TLS 1.3不是1.2的小修小补,它基本是重写的。最直观的感受就是握手次数从2-RTT降到了1-RTT,甚至0-RTT(带PSK时)。

下面这张表我把关键差异全列出来,方便你对照排查问题。

对比项 TLS 1.2 TLS 1.3
握手延迟 2-RTT (完整握手) 1-RTT (完整握手),0-RTT(PSK复用)
密码套件协商 客户端先发支持的套件列表,服务端选一个。容易在中间被降级攻击。 客户端直接发支持的密钥交换+认证算法组合,服务端选一个。协商更安全。
支持的密钥交换 RSA、DH、DHE、ECDH、ECDHE等 只有(EC)DHE,强制前向安全
认证与加密分离? 密码套件里同时定义认证、加密、MAC。常见像TLS_RSA_WITH_AES_128_CBC_SHA。 密码套件只定义AEAD算法(AES-GCM/ChaCha20-Poly1305)+ HKDF。认证用的签名算法单独协商。
会话恢复 Session ID + Session Ticket PSK + Ticket / 0-RTT
签名算法 在ServerKeyExchange里指定,支持RSA、DSA、ECDSA 在CertificateVerify里指定,只支持RSA-PSS、ECDSA、EdDSA
协议版本协商 明文发送版本,可被降级攻击 在ServerHello的随机数中夹带版本(通过“支持版本扩展”),防降级
已废弃/移除 压缩、TLS_FALLBACK_SCSV、CBC模式(但还保留) 完全移除压缩、CBC模式、RC4、3DES、静态RSA/DH

常见坑:我曾经碰到一个项目,TLS 1.3接手后连接一直失败,查了半天发现是老旧服务端不支持 ECDHE 必须用的曲线(比如 secp256r1 大家倒是都支持,但客户端如果只发了 x25519,服务端不认)。TLS 1.3 强制要求至少支持 secp256r1 和 secp384r1,这个细节我建议你记清楚。

2.2 密码套件:从“一大串”到“几个”

TLS 1.2时代,密码套件列表长得吓人。你随便找个浏览器抓个ClientHello,里面能塞几十个。比如:TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256——名称里包含了密钥交换、认证、加密、MAC。

到了TLS 1.3,套件格式变了:TLS_AES_128_GCM_SHA256、TLS_CHACHA20_POLY1305_SHA256、TLS_AES_256_GCM_SHA384。一共就几个,而且全都是AEAD+HKDF。

为什么这么改?说白了,以前的设计让攻击者有很多降级空间。你想想看,如果客户端支持了发了一堆弱密码(比如RC4),服务端恰好选了个最烂的,那就惨了。TLS 1.3直接砍掉所有不安全选项,只留几条路,你选哪个都不会太差。

我个人习惯在做兼容性测试时,重点看这几点:

  • 服务端支持的曲线列表——尤其是老旧设备可能没有x25519,但一定有secp256r1。
  • 签名算法——TLS 1.3不再支持RSA-PKCS1-v1.5签名,要用RSA-PSS。有些旧证书签发出来的签名算法还是老的,得重新颁。
  • PSK/Ticket——如果要走0-RTT,注意重放攻击防护。我不建议在老旧系统对接时一开始就开0-RTT,先走完整握手稳定再说。

经验之谈:很多老旧系统只支持TLS 1.2的几个CBC套件(比如AES_128_CBC_SHA)。如果对方非要你降级,记住CBC模式在TLS 1.2里还有BEAST、Lucky13攻击,虽然大部分实现已经修补了。但我建议尽量迁移到GCM或者ChaCha20,别图省事。

2.3 握手流程归纳总结

下面我把TLS 1.2和TLS 1.3的完整握手流程剥开,每一步都过一遍。顺便画一张对比图,视觉上更直观。

TLS 1.2 完整握手(2-RTT)

  1. ClientHello:客户端发版本、随机数、密码套件列表、扩展(SNI、支持曲线等)。
  2. ServerHello:服务端选版本、随机数、密码套件,发证书(Certificate)、ServerKeyExchange(如果使用DHE/ECDHE,包含公钥和签名)、CertificateRequest(可选)。
  3. ClientKeyExchange:客户端生成预主密钥(PreMasterSecret),用服务端公钥加密发给服务端(RSA)或计算DH共享密钥(DHE)。
  4. ChangeCipherSpec + Finished:双方各自算主密钥,发Finished消息验证握手完整性。
  5. 数据传输。

TLS 1.3 完整握手(1-RTT)

  1. ClientHello:客户端直接发支持的密钥交换参数(Key Share扩展,包含一个或几个DH公钥),同时带密码套件、签名算法、曲线等。不再发完整的套件列表。
  2. ServerHello:服务端选参数,发自己的DH公钥,发证书、CertificateVerify(签名)、Finished。这一步打包了原来1.2的好几个消息。
  3. 客户端:收到后算出共享密钥,发自己的Certificate(如果需要客户端认证)、CertificateVerify、Finished。
  4. 数据传输。

你会发现,TLS 1.3把证书传输和密钥交换合并到同一个往返里了,而且不需要单独的ChangeCipherSpec消息。这里有个细节:TLS 1.3握手消息全部是加密的(除了最初的ClientHello),中间人很难嗅探到证书和签名内容。

为什么要关注握手流程? 当我调试老旧系统对接时,最常遇到的问题就是“握手中断在哪一步”。在抓包(Wireshark)里看几号消息就能定位。比如TLS 1.2如果在ServerHello之后客户端不发ClientKeyExchange,八成是密码套件不匹配。TLS 1.3如果在ServerHello之后客户端不发Finished,去看看签名算法是不是服务端没支持。

流程图:TLS 1.2 vs TLS 1.3 完整握手对比

TLS 1.2 与 TLS 1.3 完整握手流程对比 ClientHello (版本、套件、随机数) ServerHello + 证书 + ServerKeyExchange ClientKeyExchange + ChangeCipherSpec 双向 Finished ClientHello (Key Share + 套件 + 曲线) ServerHello + 证书 + CertificateVerify + Finished 客户端 Finished(可选证书+验证) 数据(应用数据) 说明:TLS 1.3 将 ServerHello + 证书 + CertificateVerify + Finished 合并为一个往返。 客户端只需一次等待即可开启加密通信,而 TLS 1.2 需要两次。 TLS 1.2 (2-RTT) TLS 1.3 (1-RTT)

这张图里你可以看出来,TLS 1.3的握手实际上把服务端响应合并在一条消息里,并且去掉了独立的ChangeCipherSpec。我用绿色代表TLS 1.3的步骤,蓝色代表TLS 1.2。箭头表示消息流动方向。

一个小技巧:如果你用Wireshark抓包,看到ServerHello之后紧接着就是ClientHello密文(TLS 1.3),那就是1.3。如果看到ChangeCipherSpec独立一条记录,那多半是1.2。

小结一下知识点

  • TLS 1.3最大变化是握手延迟砍半、密码套件精简、强制前向安全。
  • 密码套件名称从 Kx_Auth_Enc_MAC 变成了只有 AEAD+HKDF。
  • 握手流程从两步变成一步,减少了一次网络往返。
  • 兼容老旧系统时,重点关注服务端支持的曲线、签名算法、密码套件范围。

当然,这些只是协议层面的基础。实际对接时你还会遇到证书链不全、时间戳问题、SNI不支持、加密库版本太老等各种情况。不过先把这些核心差异装进脑子,后面讲具体案例时你就知道该往哪个方向查了。