3. 汽车网络安全威胁 landscape

密钥交换,说白了就是两个人在一条完全公开的线路上,商量出一个只有他俩知道的秘密。你可能会想,这怎么可能?

我第一次接触 Diffie-Hellman 时也觉得这像魔术。后来自己动手推了一遍公式,才明白它背后的数学原理其实很优雅。嗯,咱们今天就把这块彻底讲透。

3.1 经典 Diffie-Hellman 协议

Diffie-Hellman(简称 DH)的核心思想,是利用离散对数问题的难解性。双方各自生成一个私钥,然后通过公开的参数计算出公钥并交换,最后各自在本地算出相同的共享密钥。

流程其实很简单:

  1. 双方约定一个大素数 p 和一个生成元 g(公开参数)
  2. Alice 随机选私钥 a,计算 A = g^a mod p,发给 Bob
  3. Bob 随机选私钥 b,计算 B = g^b mod p,发给 Alice
  4. Alice 计算 s = B^a mod p
  5. Bob 计算 s = A^b mod p
  6. 两人得到相同的 s,这就是共享密钥

关键点: 攻击者能拿到 p、g、A、B,但无法在合理时间内算出 a 或 b,也就无法得到 s。这就是离散对数假设的威力。

我在项目中遇到过一个问题:有人直接用 OpenSSL 默认的 DH 参数,结果被 Logjam 攻击打穿了。为什么?因为默认参数可能只有 512 位,太弱了。

避坑指南: 我曾经见过一个团队在生产环境用了 1024 位的 DH 参数,觉得够安全了。但 2015 年以后,1024 位已经被认为不安全。我建议现在至少用 2048 位,最好 3072 位或更高。

3.2 ECDH:椭圆曲线版本的 DH

经典 DH 有个硬伤——性能。大数模幂运算很慢,尤其是在资源受限的设备上。ECDH 就是来解决这个问题的。

ECDH 用椭圆曲线上的点乘运算代替了模幂运算。同样的安全强度下,ECDH 的密钥长度短得多,计算也快得多。

安全强度(比特) RSA/DH 密钥长度 ECDH 密钥长度
80 1024 160
112 2048 224
128 3072 256
256 15360 512

你看,要达到 128 位安全强度,RSA/DH 需要 3072 位,而 ECDH 只要 256 位。差距不是一点半点。

3.3 曲线选择:别踩这些坑

ECDH 的安全性高度依赖曲线的选择。不是所有曲线都一样安全。

我个人习惯优先使用 NIST P-256(secp256r1)或 Curve25519。为什么?

  • P-256: 广泛支持,硬件加速友好,但要注意实现时的侧信道问题
  • Curve25519: 设计更简洁,常数时间实现,没有专利限制,我强烈推荐

千万别用的曲线: secp256k1(比特币用的那条)虽然名气大,但它不是为通用安全通信设计的。还有那些「国产自研」但没经过充分密码分析的曲线,我建议一律避开。

我曾经在一个 IoT 项目里看到有人用了 NIST P-192,理由是「设备性能太差」。我当时就急了——P-192 只有 80 位安全强度,2010 年以后就不该用了。后来我帮他们换成了 Curve25519,性能反而更好。

3.4 前向安全性设计

前向安全性(Forward Secrecy),说白了就是:即使你以后偷到了我的长期私钥,你也解不开以前通信的内容。

为什么重要?你想想看,如果攻击者现在录下了你所有的加密流量,等十年后量子计算机出来了,或者你私钥泄露了,他就能把历史数据全解开。这太可怕了。

实现前向安全性的核心思路:每次会话都生成临时密钥对,会话结束后立即销毁私钥。

DHE/ECDHE 才是正道: 注意看名字里的「E」——Ephemeral(临时)。DHE 和 ECDHE 每次握手都生成新的 DH 密钥对,用完就扔。而普通的 DH 和 ECDH 用的是静态密钥,没有前向安全性。

我建议你在 TLS 配置里强制使用 ECDHE 套件,禁用 DHE(因为 DHE 太慢)和静态 DH。具体来说:

  • 优先:TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
  • 备选:TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
  • 禁用:所有不含 ECDHE 或 DHE 的套件

小技巧: 如果你用 Nginx,可以在 ssl_ciphers 里把 ECDHE 放在最前面。我习惯的配置是:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256,后面再接其他套件。

3.5 完整密钥交换流程(含前向安全性)

咱们把整个流程串起来,看看实际通信中密钥交换是怎么工作的:

ECDHE 密钥交换与安全通信流程 Alice(客户端) Bob(服务器) 生成临时密钥对 (sk_a, pk_a) 生成临时密钥对 (sk_b, pk_b) pk_a(临时公钥) pk_b(临时公钥) 计算共享密钥 s = ECDH(sk_a, pk_b) 计算共享密钥 s = ECDH(sk_b, pk_a) 派生会话密钥 K = KDF(s, 其他参数) 派生会话密钥 K = KDF(s, 其他参数) 销毁临时私钥 sk_a 销毁临时私钥 sk_b 使用会话密钥 K 进行对称加密通信

关键设计原则: 临时私钥 sk_a 和 sk_b 只在内存中存在,会话结束后立即销毁。即使攻击者后来拿到了服务器的长期签名私钥,也无法解密历史会话。这就是前向安全性的精髓。

3.6 实战建议

最后,我总结几条实战中一定要记住的原则:

  • 永远用 ECDHE,不用 DHE 或静态 DH。 性能和安全都更好
  • 曲线首选 Curve25519,其次 P-256。 别碰那些奇奇怪怪的曲线
  • 密钥长度要够。 2024 年的标准:至少 256 位椭圆曲线,或者 3072 位 DH
  • 实现要常数时间。 我见过一个 ECDH 实现因为没做常数时间处理,被时序攻击直接提取出私钥
  • 定期更新参数。 密码学标准在演进,别一套参数用十年

最后一句忠告: 我曾经接手过一个项目,他们自己实现了一套「改进版」DH 协议,号称性能提升 10 倍。结果我一看,他们把随机数生成器换成了一个固定种子——整个协议形同虚设。密码学协议,别自己发明,用标准实现,这是最安全的做法。