3、密码学基础(下):密钥交换协议(如ECDH)、随机数生成、PKI体系

好,我们接着往下聊。上一节我们把对称加密、非对称加密和哈希函数过了一遍。这一节,我们得把剩下的几块硬骨头啃下来:密钥怎么安全地交换?随机数到底有多重要?还有那个听起来很唬人的PKI体系,到底是个啥?

说实话,这三块东西,在OTA系统里,任何一个出问题,整个安全防线就形同虚设。我见过太多项目,加密算法选得挺强,结果密钥交换环节被人截胡了,或者随机数生成器太弱,密钥直接被猜出来。嗯,咱们一个一个说。

3.1 密钥交换协议:怎么把密钥安全地送给对方?

你想想看,如果车端和云端要用AES加密通信,那双方得先有同一个密钥吧?这个密钥怎么给到对方?

  • 直接写在代码里?—— 太危险,反编译就暴露了。
  • 通过网络发送?—— 明文发送等于没加密。
  • 用非对称加密加密后再发送?—— 这倒是个思路,但怎么保证你拿到的公钥是真的?

所以,我们需要一种协议,让双方能在不安全的信道上,协商出一个只有双方知道的共享密钥。这就是密钥交换协议干的事。

3.1.1 Diffie-Hellman (DH) 协议:祖师爷级别的方案

DH协议是1976年提出的,思想非常巧妙。它不直接传输密钥,而是传输一些“半成品”,双方各自用自己的私密信息加工一下,就能得到相同的成品。

简单来说,流程是这样的:

  1. 双方先约定两个公开的数字:一个大素数 p 和一个生成元 g。
  2. 甲方随机选一个私密的数字 a,计算 A = g^a mod p,把 A 发给乙方。
  3. 乙方随机选一个私密的数字 b,计算 B = g^b mod p,把 B 发给甲方。
  4. 甲方拿到 B,计算 K = B^a mod p = (g^b)^a mod p = g^(ab) mod p。
  5. 乙方拿到 A,计算 K = A^b mod p = (g^a)^b mod p = g^(ab) mod p。

你看,双方都算出了同一个 K,但中间人只看到了 p, g, A, B。想从 A 和 B 反推出 a 或 b?这在数学上叫“离散对数问题”,计算量巨大,基本不可行。

核心要点: DH协议保证了“前向安全性”。什么意思?就算攻击者现在记录了你所有的通信数据,并且多年后破解了你设备的私钥,他也无法解密之前记录的会话。因为之前的会话密钥是由临时生成的 a 和 b 决定的,跟长期私钥无关。这在OTA里非常重要,你总不希望几年前升级的包现在被人翻出来破解吧?

3.1.2 ECDH:椭圆曲线版的DH,更高效

传统的DH协议用的是模幂运算,计算量比较大,生成的密钥也长。后来人们发现,把椭圆曲线数学用上去,可以达到同样的安全强度,但密钥更短,计算更快。

这就是ECDH(椭圆曲线Diffie-Hellman)。

原理上,就是把 DH 里的模幂运算,换成了椭圆曲线上的点乘运算。公开参数从 (p, g) 变成了椭圆曲线方程和基点 G。私密数字 a 和 b 变成了标量,公开的 A 和 B 变成了曲线上的点。

我个人习惯在嵌入式OTA项目里优先选择ECDH。为什么?

  • 性能好: 车规级MCU资源有限,ECDH的计算速度比传统DH快一个数量级。
  • 密钥短: 256位的ECDH密钥,安全强度相当于3072位的RSA密钥。省带宽,省存储。
  • 标准支持好: 主流的安全协议(TLS 1.3)都默认使用ECDHE(基于ECDH的临时密钥交换)。

避坑指南: 我曾经在一个项目里,看到有人直接用了标准库的ECDH,但没检查对方传过来的公钥是否在合法的椭圆曲线上。结果被一个叫“无效曲线攻击”的手法给破了。攻击者传一个不在曲线上的点,就能慢慢猜出你的私钥。所以,一定要做公钥有效性验证,这是标准流程,千万别省。

3.2 随机数生成:安全的基石,也是最容易被忽视的

说句实话,很多安全漏洞,根子都在随机数上。你想想看,密钥交换里的私密数字 a 和 b,如果不够随机,攻击者就能猜出来。数字签名里的随机数 k,如果重复了,私钥就直接暴露了。

嵌入式设备上生成真随机数,其实挺难的。因为MCU是一个确定性的系统,你很难找到真正的“熵源”。

3.2.1 真随机数 vs 伪随机数

类型 来源 特点 嵌入式常用方案
真随机数 (TRNG) 物理噪声源(热噪声、时钟抖动、ADC噪声) 不可预测,但生成速度慢,可能受环境影响 MCU内置的TRNG外设、专用安全芯片
伪随机数 (PRNG) 算法(如ChaCha20, AES-CTR) 速度快,可重复,但种子必须足够随机 软件实现的CSPRNG(密码学安全伪随机数生成器)

在OTA系统里,我们通常的做法是:用TRNG采集一小段高质量的熵(比如256位),作为种子,喂给一个CSPRNG。之后所有的随机数需求,都由这个CSPRNG来提供。这样既保证了随机性,又保证了性能。

3.2.2 嵌入式环境下的熵源采集

嗯,这里要注意。很多MCU的TRNG在刚上电时,输出的随机性可能不够好。我建议的做法是:

  1. 上电后延迟采集: 等系统稳定几十毫秒后再去读TRNG。
  2. 混合多个熵源: 不要只依赖一个TRNG。可以把TRNG的输出、ADC采样的最低有效位、Wi-Fi/蓝牙的射频噪声、甚至用户按键的时间间隔,都混在一起,通过一个哈希函数(比如SHA-256)进行“熵提取”。
  3. 健康检测: 定期检查TRNG的输出,看有没有出现全0、全1或者明显的周期性模式。如果发现异常,要能报警并切换到备用熵源。

警告: 千万不要用C标准库的 rand() 函数来生成安全相关的随机数!那个是给游戏或者简单模拟用的,周期极短,很容易被预测。我曾经见过一个产品,用 rand() 生成TLS的随机数,结果被安全团队一分析,几分钟就破解了会话密钥。这简直是自杀式行为。

3.3 PKI体系:谁来证明这把公钥是真的?

好,现在我们有ECDH来协商密钥了。但有个问题:我怎么知道我收到的公钥,真的是云端服务器的公钥,而不是中间人伪造的?

这就需要PKI(公钥基础设施)出场了。说白了,PKI就是一套“发身份证、查身份证”的体系。

3.3.1 PKI的核心角色

  • CA(证书颁发机构): 相当于公安局。它负责验证申请者的身份,然后签发数字证书。CA自己也有一个证书,叫根证书,是自签名的,被广泛信任。
  • 证书: 相当于身份证。里面包含了:持有者的公钥、持有者的身份信息(比如域名、组织名)、CA的签名、有效期、序列号等。
  • RA(注册机构): 相当于派出所。它负责受理证书申请,审核身份,然后把审核结果告诉CA去签发证书。大系统里常用,小系统可以没有。
  • 证书吊销列表 (CRL) / OCSP: 相当于通缉令。当私钥泄露或者员工离职时,需要把对应的证书作废。CRL是一个黑名单列表,OCSP是在线查询协议。

3.3.2 证书链:信任的传递

你想想看,世界上有那么多设备,不可能每个设备都内置所有CA的根证书。所以有了证书链的概念。

比如,云端服务器的证书是由“中级CA1”签发的,“中级CA1”的证书是由“根CA”签发的。车端只内置了“根CA”的证书。

验证过程是这样的:

  1. 云端把它的服务器证书 + 中级CA1的证书,一起发给车端。
  2. 车端用内置的根证书,验证中级CA1证书上的签名。如果通过,说明中级CA1是可信的。
  3. 车端再用中级CA1的公钥,验证服务器证书上的签名。如果通过,说明服务器证书是可信的。
  4. 最后,车端检查服务器证书里的域名、有效期、是否被吊销等。

这一层层往上追溯,直到你信任的根证书,就叫证书链验证。

个人经验: 在OTA系统里,我建议把根证书直接烧死在芯片的只读存储区(比如eFuse或OTP)。这样即使攻击者拿到了文件系统的读写权限,也无法篡改根证书。中级证书可以放在文件系统里,方便更新。但根证书一旦被换,整个信任体系就崩塌了。

3.3.3 OTA场景下的PKI简化

说实话,标准的PKI体系对于车规级OTA来说,有时候太重了。你不可能让每辆车都去在线查询CRL,网络环境不允许。

所以,在实际项目中,我们经常做一些简化:

  • 自建CA: 不依赖公共CA,自己搭建一套CA系统,只给自己的车和云端服务器发证书。
  • 预置证书: 在车辆出厂前,就把车端的设备证书和私钥烧录进去。同时把自建CA的根证书也烧进去。
  • 短有效期证书: 给OTA签名用的代码签名证书,有效期设得很短(比如1年),配合证书更新机制,这样即使泄露,影响范围也有限。CRL就可以不用了。
  • 证书固定: 对于云端服务器,直接在车端代码里“硬编码”服务器的公钥哈希值。这样连证书链验证都省了,但缺点是换服务器公钥时需要升级固件。

我的建议: 对于量产项目,别自己从头造PKI轮子。用成熟的库,比如mbedTLS或者OpenSSL,它们对证书链验证、CRL/OCSP都有完善的支持。你只需要把精力放在密钥的安全存储和熵源采集上。我曾经见过一个团队,自己写了一套证书验证逻辑,结果漏掉了对证书签名算法强度的检查,被人用MD5碰撞攻击给骗过去了。嗯,用现成的、经过审计的库,它不香吗?

好了,密码学基础的三讲就到这里。从对称加密到非对称,从哈希到密钥交换,再到随机数和PKI,这些是构建OTA安全体系的砖瓦。下一节,我们就要用这些砖瓦,去搭建真正的安全启动和安全升级流程了。