4、数字签名基础:非对称加密(RSA/ECC)原理、签名生成与验证流程、公钥的存储与保护
聊到 BootROM 镜像校验,数字签名是绕不开的核心。说白了,签名就是给固件贴上一张「防伪标签」。这张标签只有私钥持有者能贴,但任何人都能用公钥去验。今天我们就把它拆开揉碎了讲清楚。
4.1 非对称加密:为什么需要两把钥匙?
对称加密就像一把钥匙开一把锁,加密和解密用同一个密钥。这在通信双方事先约定好密钥的场景下没问题。但 BootROM 场景下,设备在产线烧录时,你没法预知每一台设备将来要校验哪个固件版本。更关键的是——密钥怎么安全地分发给所有设备?
非对称加密解决了这个难题。它用一对密钥:私钥(自己藏好)和公钥(公开分发)。私钥签名的数据,只能用对应的公钥验证;反过来,公钥加密的数据,只能用私钥解密。两个方向,用途不同。
核心区别速览
| 特性 | 对称加密 | 非对称加密 |
|---|---|---|
| 密钥数量 | 1个(共享密钥) | 2个(私钥+公钥) |
| 密钥分发 | 困难,需安全通道 | 公钥可公开,私钥本地保管 |
| 典型算法 | AES, DES | RSA, ECC |
| 计算开销 | 低 | 高(尤其RSA) |
| BootROM常用场景 | 加密镜像体(配合签名) | 签名验证 |
我个人习惯在 BootROM 里只用非对称做签名验证,镜像体本身用对称加密。这样既保证了签名不可伪造,又兼顾了解密速度。
4.2 RSA 与 ECC:两种主流方案怎么选?
RSA 和 ECC 是目前 BootROM 签名最常用的两种算法。它们的数学基础不同,但目标一致:让私钥签名容易,让攻击者伪造签名极难。
4.2.1 RSA:大整数分解难题
RSA 的安全性基于「大整数分解」的困难性。给你两个大素数 p 和 q,乘起来得到 n 很容易;但给你 n,反推出 p 和 q 就非常困难。私钥就是基于 p 和 q 算出来的,公钥则包含 n 和一个公开指数 e。
签名过程:对镜像哈希值做私钥指数运算(模幂)。验证过程:用公钥指数运算还原哈希,再比对。
我踩过的坑
RSA 的密钥长度很关键。1024 位已经被认为不安全,我建议至少用 2048 位。但注意,密钥越长,签名和验证的耗时也越长。在 BootROM 这种资源受限的环境下,我曾经遇到过 4096 位 RSA 验证耗时超过 500ms 的情况——这直接拖慢了启动速度。后来换成了 ECC。
4.2.2 ECC:椭圆曲线离散对数难题
ECC 的安全性基于椭圆曲线上的离散对数问题。简单说,给定曲线上一个点 G 和一个大整数 k,计算 k*G 很容易;但给定 G 和 k*G,反推 k 几乎不可能。
ECC 的优势在于:同等安全强度下,密钥长度远小于 RSA。256 位 ECC 密钥的安全性与 3072 位 RSA 相当。这对 BootROM 这种存储和计算资源都受限的场景非常友好。
| 安全等级 | RSA 密钥长度 | ECC 密钥长度 | 签名长度 |
|---|---|---|---|
| 80 位 | 1024 | 160-223 | RSA: 128 字节 / ECC: 40-56 字节 |
| 112 位 | 2048 | 224-255 | RSA: 256 字节 / ECC: 56-64 字节 |
| 128 位 | 3072 | 256-383 | RSA: 384 字节 / ECC: 64-96 字节 |
| 192 位 | 7680 | 384-511 | RSA: 960 字节 / ECC: 96-128 字节 |
你看,ECC 的签名长度也短很多。在 BootROM 里,签名是附加在镜像尾部的,每省一个字节都是实打实的 Flash 空间。
4.3 签名生成与验证流程
流程其实不复杂。我画了一张图,把签名和验证两条路径放在一起对比,你一看就明白。
流程总结起来就三步:
- 哈希:对固件镜像做一次哈希运算(比如 SHA-256),得到一个固定长度的摘要。
- 签名:用私钥对这个摘要做加密运算,生成数字签名。
- 验证:BootROM 收到镜像后,自己算一遍哈希,再用公钥解密签名得到原始哈希,两者比对。
这里有个细节:签名不是直接加密整个镜像。你想想看,一个固件可能几兆甚至几十兆,用私钥做非对称加密,计算量太大了。所以只加密哈希值,既保证了完整性,又大幅减少了计算开销。
4.4 公钥的存储与保护
公钥虽然可以公开,但在 BootROM 场景下,它的存储方式直接决定了整个信任链的安全。如果公钥被篡改,攻击者就能用自己的私钥签名恶意固件,设备照样会「信任」它。
所以公钥必须存储在不可篡改的区域。常见的做法有几种:
| 存储方式 | 安全性 | 灵活性 | 典型场景 |
|---|---|---|---|
| 硬编码在 BootROM 只读区 | 最高(物理不可改) | 最低(需重新流片) | 一次性烧录的 MCU |
| 存储在 OTP(一次性可编程) | 高(烧录后不可改) | 中等(产线烧录) | 量产设备 |
| 存储在 eFuse | 高(熔断后不可恢复) | 中等(可逐位熔断) | 安全芯片、SoC |
| 存储在 Flash 并加签名保护 | 中等(依赖上层签名) | 高(可更新) | 支持 OTA 的设备 |
我曾经踩过的坑
有一次做安全审计,发现某款设备的公钥竟然存储在外部 SPI Flash 里,而且没有做任何完整性保护。攻击者只要物理接触设备,替换 Flash 里的公钥,就能刷入任意固件。这个漏洞的修复方案是把公钥迁移到芯片内部的 OTP 区域,同时增加硬件防回滚机制。
我个人建议:公钥的根信任必须锚定在硬件不可变区域。BootROM 里硬编码一个根公钥哈希,或者直接存根公钥,然后用这个根公钥去验证次级公钥。这样既保证了根信任的绝对安全,又给密钥更新留了余地。
4.5 避坑指南:签名实现中的常见问题
理论讲完了,说几个实际项目中容易翻车的地方。
- 哈希算法选择:别用 MD5 或 SHA-1,它们已经被证明存在碰撞攻击。至少用 SHA-256。
- 填充方案:RSA 签名必须使用 PSS 填充,不要用 PKCS#1 v1.5。后者有已知的 Bleichenbacher 攻击变种。
- 随机数质量:ECC 签名需要高质量的随机数。如果随机数生成器被预测,私钥可能被恢复。我记得有个知名案例就是某游戏机因为随机数问题导致私钥泄露。
- 防回滚:签名验证通过后,还要检查镜像版本号。否则攻击者可以拿一个旧版本(有已知漏洞)的合法签名镜像来降级攻击。
一个小技巧
在 BootROM 里做签名验证时,我习惯先验证签名,再解密镜像体。这样如果签名不合法,直接拒绝,省了解密的时间和电量。对于电池供电的 IoT 设备,这个顺序能省不少电。
嗯,数字签名的基础就这些。理解了非对称加密的原理、签名流程和公钥保护策略,你就能看懂 BootROM 是如何建立信任根的。下一层,我们会把这些知识串起来,看看整个镜像校验链是怎么设计的。