3. 汽车电子电气架构
OTA升级包签名,说白了就是给升级包贴上一张「防伪标签」。这张标签能证明两件事:第一,包确实是官方发的;第二,包在路上没被人动过手脚。我见过不少团队在早期设计时觉得签名可有可无,结果呢?被中间人攻击搞到全车瘫痪的案例,圈子里可不少。
这一节,我们重点聊三个东西:用什么算法签名、怎么验证签名、以及怎么防止攻击者把旧包拿来重放。
3.1 数字签名算法:RSA 与 ECDSA 的实战选择
目前主流的车载OTA签名算法就两个阵营:RSA 和 ECDSA。它们都能实现签名,但底子不一样。
| 对比维度 | RSA (Rivest-Shamir-Adleman) | ECDSA (Elliptic Curve Digital Signature Algorithm) |
|---|---|---|
| 安全强度 | 2048位以上才安全 | 256位 ≈ RSA 3072位 |
| 签名长度 | 与密钥长度相同(如256字节) | 固定64字节(secp256r1) |
| 签名速度 | 较慢(尤其私钥操作) | 快(约快3-5倍) |
| 验签速度 | 较快 | 稍慢于RSA |
| 车载ECU资源消耗 | 高(大数运算) | 低(椭圆曲线运算) |
| 标准支持 | PKCS#1 v1.5 / PSS | ANSI X9.62 / FIPS 186-4 |
我个人习惯在资源受限的MCU(比如Cortex-M4以下的芯片)上优先选ECDSA。为什么?因为签名短、算得快。你想想看,一个CAN报文才8字节,你一个RSA签名256字节塞进去,光传输就得拆几十帧,太折腾了。
核心建议:
- 网关/域控制器(高性能SoC):RSA 4096 + SHA-256,兼容性好,验签快。
- 普通ECU(低性能MCU):ECDSA secp256r1 + SHA-256,签名短,省Flash。
- HSM(硬件安全模块):优先使用硬件加速的算法,别用软件硬扛。
避坑指南:我曾经在一个项目里看到有人用RSA 1024位签名,理由是「够用」。结果第二年就被学术界攻破了。现在行业底线是RSA 2048,我建议直接上4096。别省那点算力,安全没有「差不多」。
3.2 签名验证流程:从云端到ECU的完整链路
签名验证不是单点动作,而是一条链。我把它拆成四个阶段,每个阶段都有坑。
3.2.1 签名生成(云端)
- 计算摘要:对升级包二进制做SHA-256哈希,得到32字节摘要。
- 签名运算:用私钥对摘要加密(RSA)或生成签名对(ECDSA)。
- 打包:将签名、证书链、元数据一起塞进升级包头部。
// 伪代码:云端签名生成
digest = SHA256(firmware.bin)
signature = RSA_Sign(private_key, digest, PSS)
package = {
firmware: firmware.bin,
header: {
signature: signature,
cert_chain: [root_ca, intermediate, signer],
algorithm: "RSA4096_PSS_SHA256",
timestamp: 1712345678
}
}
3.2.2 证书链验证(车端)
车端拿到包后,第一件事不是验签名,而是验证书。为什么?因为签名本身不防「假证书」攻击。
- 根证书预置:车厂根CA证书必须烧死在HSM或安全存储区,不可更新(除非物理更换)。
- 链式验证:从签名证书逐级向上,直到根证书。任何一级失效,直接拒绝。
- 吊销检查:检查证书是否在CRL(证书吊销列表)中。嗯,这里要注意,CRL的更新本身也需要签名保护。
3.2.3 签名验证(车端)
// 伪代码:车端签名验证
digest = SHA256(received_firmware)
cert = extract_cert(package.header)
if !verify_cert_chain(cert, root_ca):
return REJECT
is_valid = RSA_Verify(cert.public_key, digest, package.header.signature, PSS)
if is_valid:
return ACCEPT
else:
return REJECT
3.2.4 防回滚与防重放
签名验证通过不代表万事大吉。攻击者可以截获一个合法的旧版本包,然后反复下发——这就是重放攻击。
重要:签名只保证包是官方发的,不保证包是最新的。防重放必须靠额外的机制。
3.3 防重放攻击设计:时间戳、序列号与版本号
防重放的核心思路就一句话:让每个包都有唯一身份,且车端能判断「这个包是不是新的」。
我常用的方案有三种,实际项目中往往是组合使用。
| 方案 | 原理 | 优点 | 缺点 |
|---|---|---|---|
| 时间戳 | 签名时嵌入UTC时间,车端检查时间差 | 实现简单,无需状态同步 | 依赖车端时钟准确性(容易被篡改) |
| 单调递增序列号 | 每个包分配唯一序列号,车端记录已处理的最大号 | 绝对防重放,不依赖时钟 | 需要非易失性存储,序列号管理复杂 |
| 固件版本号 | 签名时嵌入版本号,车端只接受高于当前版本的包 | 天然防回滚,与业务逻辑一致 | 版本号可能溢出,需设计回滚例外机制 |
我的实战组合:
- 主防重放:单调递增序列号 + 固件版本号双重校验。
- 辅助校验:时间戳作为软约束(允许±5分钟偏差),防止时钟漂移导致误拒。
- 例外处理:允许售后工具通过安全通道下发「版本回滚授权」,但必须经过二次签名。
3.4 整体签名验证流程(SVG流程图)
下面这张图把从云端签名到车端验证的完整链路串起来了。我建议你把它打印出来贴在工位上,每次设计OTA方案时对照着看。
3.5 实战中的常见坑与对策
做OTA签名验证这么多年,我踩过的坑不少。挑几个典型的说说。
坑1:证书过期导致ECU变砖
我曾经在一个量产项目中发现,某ECU的签名证书只有1年有效期。结果一年后OTA推送时,所有车都验证失败。解决方案:证书有效期至少覆盖整车生命周期(通常10年),或者设计证书轮换机制。
坑2:序列号溢出
有团队用uint16存序列号,结果65535次升级后溢出回0,导致旧包被接受。我建议用uint64,或者用版本号+时间戳组合,避免纯序列号。
坑3:验签性能瓶颈
某ECU用软件RSA 4096验签,耗时超过5秒,导致OTA升级超时。后来切到ECDSA secp256r1,耗时降到200ms。如果硬件支持HSM,一定要用硬件加速。
3.6 小结
OTA升级包签名机制,说白了就是三件事:选对算法、验对流程、防住重放。RSA和ECDSA各有优劣,选型时看你的硬件资源。签名验证链必须从根证书到签名逐级校验,缺一环都不行。防重放攻击靠时间戳、序列号、版本号三件套,别偷懒只用一个。
嗯,这一节的内容就到这里。记住一句话:签名是OTA安全的底线,底线不能破。