3. TLS 1.2与1.3差异:握手与密钥生成
我记得刚入行那会儿,调TLS握手还挺头疼的。特别是从1.2迁移到1.3,整个流程砍掉了大半。今天我们就来掰扯清楚,这俩版本到底差在哪。
说白了,TLS 1.3最大的改进就是快。它把原本的两次RTT握手,压缩到了一次。你想想看,一个网页上可能有几十个HTTPS请求,每减少一次往返,加载速度都能明显提升。
3.1 TLS 1.2握手步骤
TLS 1.2的完整握手,我一般分成四个阶段。嗯,这里要注意,每一步都涉及证书和密钥交换,开销不小。
- ClientHello:客户端发个招呼,带上支持的TLS版本、密码套件列表、随机数。
- ServerHello + 证书 + ServerKeyExchange:服务器回应,选定密码套件,下发证书和Diffie-Hellman参数。
- ClientKeyExchange + ChangeCipherSpec + Finished:客户端计算预主密钥,发给服务器,然后切换加密,发一个Finished消息验证。
- ChangeCipherSpec + Finished(服务器):服务器也切换加密,发Finished回应。
我曾经踩过一个坑:在TLS 1.2里,如果服务端没处理好Session Ticket的轮换,客户端复用会话时会直接拿旧的预主密钥。后果就是,密钥泄露风险大大增加。
整个过程需要两次网络往返(2-RTT)。不光耗时,还容易出各种配置问题。比如证书链不完整,或者DH参数太弱。
3.2 TLS 1.3简化握手
TLS 1.3就聪明多了。它把很多步骤合并到了第一轮消息里。客户端在ClientHello里就提前算好了密钥交换的公钥,服务器直接返回加密的证书和Finished。
核心变化:
- 握手从2-RTT降到1-RTT
- 移除了不安全的密码套件(比如RSA密钥交换、CBC模式)
- 支持0-RTT快速握手(用于会话恢复)
我个人习惯把TLS 1.3的握手叫做“一发入魂”。客户端一发出去,服务器就能直接回复加密数据了。对于移动端弱网环境,这简直是救命稻草。
| 特性 | TLS 1.2 | TLS 1.3 |
|---|---|---|
| 握手往返次数 | 2-RTT | 1-RTT(0-RTT可选) |
| 证书传输时机 | 第二轮 | 第一轮(加密后) |
| 密码套件数量 | 30+种 | 5种 |
| 密钥交换算法 | RSA、DH、ECDH | 仅ECDHE(PFS必须) |
| ServerHello后是否加密 | 否 | 是 |
为什么TLS 1.3能这么快?说白了,它把“讨价还价”的过程提前了。客户端在第一个消息里,就把自己支持的所有密钥交换参数都带上。服务器直接挑一个最合适的,立刻回复加密的数据。这就叫“不废话,直接干”。
3.3 会话密钥生成的区别
两种版本的会话密钥生成流程,差异也很大。这里直接上对比:
TLS 1.2密钥生成
预主密钥 = 客户端随机数 服务端随机数 (通过RSA或DH协商)
主密钥 = PRF(预主密钥, "master secret", 客户端随机数 || 服务端随机数)
会话密钥 = PRF(主密钥, "key expansion", 服务器随机数 || 客户端随机数)
流程特点:主密钥计算依赖完整的随机数对。密钥扩展过程会生成多个输出(MAC密钥、加密密钥、IV)。
TLS 1.3密钥生成
早期密钥 = HKDF-Extract(salt, 0)
握手密钥 = HKDF-Extract(早期密钥, ECDHE共享秘密)
应用密钥 = HKDF-Expand(握手密钥, 上下文)
简化逻辑:移除了主密钥这个中间层。直接用HKDF基于共享秘密派生。密钥衍生层级更清晰,更安全。
我在项目中遇到过一个问题:TLS 1.2的PRF函数在不同OpenSSL版本里实现有细微差别。后来统一升级到1.3,用HKDF就再也没出过这种兼容性问题。
密钥生成的差异,直接影响到了密码学边界。在TLS 1.2里,如果你用RSA做密钥交换,服务器私钥一旦泄露,所有历史会话都能被解密(没有PFS)。而TLS 1.3强制使用ECDHE,确保了前向安全性。
注意:即使TLS 1.3支持0-RTT,重放攻击的风险依然存在。我建议在关键业务场景(比如支付)中,禁用0-RTT,或者至少结合额外的nonce校验。
核心流程图
下面这张图对比了两种协议的握手流程,以及密钥生成的差异。我刻意把TLS 1.2的“绕路”和TLS 1.3的“直连”画得特别明显。
解释一下这张图:左侧是TLS 1.2的4步握手,每一步都有明显的网络往返。右侧是TLS 1.3的简洁流程,客户端一发Hello,服务器直接回复加密证书,后续就是纯应用数据交互了。密钥派生也从PRF升级到了HKDF,更加直观和安全。
我遇到过不少团队,升级到TLS 1.3后,发现原本因为握手慢导致的超时问题,全都消失了。特别是在CDN边缘节点,效果极其明显。