4、TLS握手协议详解:握手流程、密码套件协商、密钥交换算法(RSA/ECDHE)
好,咱们进入正题。TLS握手,说白了就是客户端和服务器在正式传输数据前,先互相打个招呼、验明正身、商量好怎么加密的过程。我做了这么多年安全,见过太多因为握手阶段配置不当导致的漏洞。你想想看,如果握手都不安全,后面的加密传输就是空中楼阁。
4.1 握手流程:从Hello到Finished
标准的TLS 1.2握手,我习惯把它分成四个阶段。咱们一步步拆开看。
核心要点:TLS握手的本质是在不安全的信道上,协商出一套只有通信双方知道的对称密钥。所有公钥操作,都是为了安全地传递这个“种子”。
我简单解释一下这个流程:
- ClientHello:客户端告诉服务器,我支持哪些TLS版本、哪些密码套件、以及一个随机数。
- ServerHello + 证书:服务器选好密码套件,发回自己的证书(里面包含公钥),还有一个服务器随机数。
- ClientKeyExchange:客户端生成预主密钥,用服务器的公钥加密后发过去。这一步是关键。
- ChangeCipherSpec + Finished:双方各自算出主密钥,然后发一条加密的Finished消息,确认握手没被篡改。
我的经验:我曾经在排查一个设备注册慢的问题时,发现是握手阶段证书链太长,每次都要传好几个中间证书。后来我把中间证书在服务端做了合并,握手时间直接降了40%。所以,证书链的长度直接影响握手效率,别忽视。
4.2 密码套件协商:到底选哪个?
密码套件,就是一组算法的组合。我经常看到有人把它写成一长串字符串,比如 TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384。这串东西到底什么意思?咱们拆开看。
| 组成部分 | 示例值 | 作用 |
|---|---|---|
| 密钥交换算法 | ECDHE | 决定如何安全地交换预主密钥 |
| 身份认证算法 | RSA | 决定如何验证服务器身份(证书签名) |
| 对称加密算法 | AES_256_GCM | 决定握手后数据怎么加密 |
| HMAC/PRF算法 | SHA384 | 决定密钥派生和完整性校验 |
握手时,客户端会把自己支持的所有密码套件列出来,服务器从中选一个它也觉得安全的。嗯,这里要注意:服务器选的是它认为最安全且双方都支持的那个,而不是客户端列的第一个。
避坑指南:我曾经见过一个项目,服务器配置里把 TLS_RSA_WITH_AES_128_CBC_SHA 排在最前面,结果客户端虽然支持更好的 ECDHE 套件,但服务器优先选了 RSA 密钥交换。这导致整个连接不具备前向安全性。所以,服务器端的密码套件优先级一定要手动调好,别偷懒用默认顺序。
4.3 密钥交换算法:RSA vs ECDHE
这是握手协议里最核心的部分。说白了,就是客户端怎么把那个“种子”(预主密钥)安全地告诉服务器。目前主流就两种:RSA 和 ECDHE。
4.3.1 RSA密钥交换
RSA的方式很直接:客户端生成一个48字节的预主密钥,然后用服务器的RSA公钥加密,发过去。服务器用自己的私钥解密,拿到预主密钥。
听起来很简单对吧?但有个致命问题:没有前向安全性。
什么意思?如果攻击者现在录下了你所有的加密流量,然后过了几年,他通过某种方式拿到了服务器的私钥。那么,他就可以用这个私钥解密当年录下的所有会话。因为预主密钥是用这个公钥加密的,私钥一泄露,所有历史会话全部完蛋。
我的建议:在设备注册场景中,如果设备生命周期长(比如5-10年),RSA密钥交换绝对不要用。因为你无法保证几年后私钥不会泄露。我参与过的一个物联网平台审计,就发现他们还在用RSA密钥交换,后来全部强制改成了ECDHE。
4.3.2 ECDHE密钥交换
ECDHE(椭圆曲线Diffie-Hellman Ephemeral)就聪明多了。它不直接传输预主密钥,而是通过数学计算,双方各自算出一个相同的密钥。
流程大概是这样的:
- 服务器生成一对临时的ECDHE密钥对(公钥+私钥),把公钥发给客户端。
- 客户端也生成一对临时的ECDHE密钥对,把公钥发给服务器。
- 双方用自己的私钥和对方的公钥,通过椭圆曲线乘法,算出同一个共享密钥。
- 这个共享密钥就是预主密钥。
关键点在于:这个临时密钥对是每次握手新生成的,用完就丢。即使攻击者以后拿到了服务器的长期私钥,他也算不出之前的会话密钥。这就是前向安全性。
| 特性 | RSA密钥交换 | ECDHE密钥交换 |
|---|---|---|
| 前向安全性 | ❌ 无 | ✅ 有 |
| 性能(服务端) | 较快(私钥解密) | 较慢(椭圆曲线运算) |
| 性能(客户端) | 较快(公钥加密) | 较慢(椭圆曲线运算) |
| 私钥泄露影响 | 所有历史会话可被解密 | 仅影响当前会话认证,不影响历史会话 |
| 推荐曲线 | N/A | prime256v1 (P-256) 或 x25519 |
性能取舍:你可能会担心ECDHE性能差。确实,椭圆曲线运算比RSA加解密要慢一些。但在现代硬件上,这个差距已经很小了。我实测过,在ARM Cortex-A53上,ECDHE握手比RSA握手慢大约5-8毫秒。为了前向安全性,这点代价完全值得。
4.4 握手过程中的证书验证
光交换密钥还不够,你得确认你正在跟谁说话。证书验证就是干这个的。
客户端收到服务器的证书后,会做以下几件事:
- 证书链验证:从服务器证书开始,逐级往上验证,直到信任的根证书。每一级的签名都要用上一级的公钥验一遍。
- 有效期检查:证书是否在有效期内?我遇到过设备因为系统时间不准,导致证书验证失败的情况。
- 域名检查:证书里的CN或SAN是否匹配你正在访问的域名?
- 吊销检查:通过CRL或OCSP检查证书是否被吊销。这一步很多设备会跳过,但安全要求高的场景不能省。
避坑指南:我曾经调试过一个设备注册失败的问题,折腾了两天。最后发现是设备上的根证书过期了,但设备没有自动更新根证书的机制。所以,如果你的设备要运行很多年,一定要设计好根证书的更新机制,否则某天突然所有设备都注册不了,那场面...嗯,你懂的。
4.5 握手完成后的密钥派生
握手最后一步,双方用预主密钥、客户端随机数、服务器随机数,通过PRF(伪随机函数)派生出6个密钥:
- 客户端写密钥:客户端加密数据用的对称密钥
- 服务器写密钥:服务器加密数据用的对称密钥
- 客户端写IV:客户端加密用的初始向量
- 服务器写IV:服务器加密用的初始向量
- 客户端写MAC密钥:客户端消息完整性校验密钥
- 服务器写MAC密钥:服务器消息完整性校验密钥
注意,客户端和服务器用的是不同的密钥。这样即使一方的密钥泄露,另一方还能保证安全。这个设计我觉得挺巧妙的。
总结一下:TLS握手协议的核心就是三件事——协商算法、验证身份、安全交换密钥。其中密钥交换算法直接决定了连接是否具备前向安全性。在设备注册场景中,我强烈建议只使用ECDHE系列的密码套件。RSA密钥交换,该让它退休了。