2、密码学基础回顾:哈希算法(SHA256)、非对称加密(RSA/ECC)、数字签名原理
做安全启动,绕不开密码学。说白了,安全启动的整个信任链,就是靠这几个基础算法撑起来的。我刚开始接触这块时,总觉得密码学是数学家的事,我们工程师拿来用就行。后来踩了几个坑才明白——不理解原理,你连配置参数都配不对。
这一节,我们快速过一遍三个核心概念:哈希、非对称加密、数字签名。不推公式,只讲工程里怎么用、有什么坑。
2.1 哈希算法:数据的「指纹」
哈希算法,就是把任意长度的数据,压缩成一个固定长度的摘要。这个摘要就像人的指纹——数据变了,指纹一定变。
安全启动里最常用的哈希是 SHA-256。输出 256 位,也就是 32 字节。为什么是 256?因为碰撞概率足够低,低到工程上可以忽略。
SHA-256 的核心特性:
- 单向性:从摘要反推原始数据,计算上不可行
- 抗碰撞性:找到两个不同数据产生相同摘要,几乎不可能
- 雪崩效应:输入改 1 个比特,输出一半以上的比特都会变
我在项目里见过有人用 SHA-1 做固件校验。嗯,SHA-1 已经被破解了,Google 在 2017 年就演示了实际碰撞攻击。所以现在做安全启动,最低标准就是 SHA-256。
看一个简单的计算示例:
// 假设固件镜像的 SHA-256 摘要
原始数据: "Hello, Secure Boot!"
SHA-256: 8e7c5a6c4b3d2f1e0a9b8c7d6e5f4a3b2c1d0e9f8a7b6c5d4e3f2a1b0c9d8e7f
// 改一个字符
原始数据: "Hello, Secure Boot?"
SHA-256: f1e2d3c4b5a69788796a5b4c3d2e1f0a9b8c7d6e5f4a3b2c1d0e9f8a7b6c5d4
看到了吗?只改了一个字符,摘要完全不一样。这就是雪崩效应。
工程建议: 硬件如果支持 SHA-256 加速引擎,一定要用。纯软件算 1MB 固件大概要几毫秒,硬件加速可以降到微秒级。这对启动时间敏感的设备很关键。
2.2 非对称加密:公钥与私钥
非对称加密,就是加密和解密用不同的钥匙。一把公开叫公钥,一把私藏叫私钥。公钥加密的数据,只有私钥能解;私钥签名的数据,公钥能验。
安全启动里,我们主要用它的签名验证能力,而不是加密能力。为什么呢?因为固件通常是明文存储的,我们关心的是「这个固件是不是官方发布的」,而不是「固件内容能不能被看到」。
目前主流的两套方案:RSA 和 ECC。
| 特性 | RSA | ECC |
|---|---|---|
| 安全强度 | 2048 位 ≈ 112 位对称密钥 | 256 位 ≈ 128 位对称密钥 |
| 密钥长度 | 2048 位(256 字节) | 256 位(32 字节) |
| 签名速度 | 慢(私钥操作) | 快 |
| 验签速度 | 快(公钥操作) | 较快 |
| 硬件支持 | 非常广泛 | 逐渐普及 |
我个人习惯,在资源受限的 MCU 上优先选 ECC。密钥短、签名快,省 Flash 也省 RAM。但如果你用的 SoC 只有 RSA 硬件加速器,那就别纠结,RSA-2048 也够用。
避坑指南: 我曾经在一个项目里看到有人把私钥硬编码在固件里,美其名曰「方便测试」。结果产品差点带着私钥出货。记住:私钥永远只存在于安全的签名服务器或 HSM 里,永远不要出现在嵌入式设备上。
2.3 数字签名:完整性与认证的保证
数字签名,就是把哈希和非对称加密结合起来。流程很简单:
- 对固件算 SHA-256,得到摘要
- 用私钥对摘要加密,得到签名
- 把签名附加到固件后面一起发布
- 设备端:算固件摘要,用公钥解密签名,对比两个摘要
为什么不是直接加密整个固件?因为非对称加密慢啊。一个 1MB 的固件用 RSA 加密,可能要算好几秒。而哈希一下,再加密 32 字节的摘要,快得多。
下面这张图展示了完整的签名与验证流程:
流程清楚了,我们再看一个实际工程中的签名验证代码片段:
// 伪代码:设备端验证固件签名
int verify_firmware(const uint8_t* firmware, uint32_t fw_len,
const uint8_t* signature, uint32_t sig_len,
const uint8_t* public_key, uint32_t key_len) {
uint8_t hash[32]; // SHA-256 输出
// 1. 计算固件哈希
sha256_calculate(firmware, fw_len, hash);
// 2. 用公钥解密签名,得到原始摘要
uint8_t decrypted_hash[32];
rsa_decrypt(signature, sig_len, public_key, key_len, decrypted_hash);
// 3. 比较两个摘要
if (memcmp(hash, decrypted_hash, 32) == 0) {
return 0; // 验证通过
} else {
return -1; // 验证失败
}
}
工程细节: 实际项目中,签名通常不是直接对摘要做 RSA 加密,而是使用 PKCS#1 v1.5 或 PSS 填充方案。填充的作用是防止某些数学攻击。我建议新项目直接用 PSS,安全性更高。
2.4 安全启动中的密码学应用
在安全启动里,这些算法是这样配合的:
- BootROM 里硬编码了厂商的公钥哈希(或者直接存公钥)
- 第一级 bootloader 的签名由厂商私钥生成
- BootROM 用公钥验签,通过后才执行第一级 bootloader
- 第一级 bootloader 再验签下一级,形成信任链
这里有个关键点:公钥本身怎么保证不被篡改?通常的做法是把公钥哈希烧死在芯片的一次性可编程(OTP)区域里。BootROM 先验证公钥的哈希,再用公钥验签。这就是信任根(Root of Trust)的概念。
我曾经踩过的坑: 有次做芯片选型,发现某款芯片的 OTP 区域只有 128 字节。RSA-2048 的公钥是 256 字节,根本放不下。后来只能改用 ECC P-256,公钥只有 64 字节(未压缩格式),刚好塞进去。所以选芯片时,一定要先算好 OTP 空间够不够放你的公钥。
2.5 小结
这一节我们快速回顾了三个基础算法:
- SHA-256:给固件算指纹,确保完整性
- RSA / ECC:非对称加密,提供认证能力
- 数字签名:哈希 + 非对称加密的组合,同时保证完整性和来源认证