3. 密码学基础回顾:哈希函数、非对称加密与数字签名
各位同学,咱们今天聊点硬核的。安全启动的核心,说白了就是一套密码学协议。你如果不把哈希、非对称加密、数字签名这几个东西搞明白,后面看安全启动的代码就跟看天书一样。
我当年刚接触这块时,也踩过不少坑。有一次把哈希值和签名搞混了,结果在验证镜像时死活过不去,查了两天才发现是概念理解错了。所以今天咱们把这几个基础概念彻底捋清楚。
3.1 哈希函数:数据的「指纹」
哈希函数,你可以把它想象成给数据按个手印。不管输入多大,输出都是固定长度。而且只要数据改一个比特,哈希值就完全变了。
在安全启动里,我们最常用的是 SHA-256。它输出 256 位,也就是 32 个字节。
SHA-256 的核心特性:
- 单向性:从哈希值反推原始数据,理论上不可能。我见过有人想暴力破解,算到天荒地老也算不出来。
- 抗碰撞性:找到两个不同数据但哈希值相同,几乎不可能。嗯,这里要注意,理论上存在碰撞,但概率低到可以忽略。
- 雪崩效应:输入改一点点,输出天翻地覆。
举个例子,你写个简单的 C 代码算一下:
#include <stdio.h>
#include <string.h>
#include <openssl/sha.h>
void print_hash(unsigned char *hash) {
for(int i = 0; i < SHA256_DIGEST_LENGTH; i++)
printf("%02x", hash[i]);
printf("\n");
}
int main() {
unsigned char data[] = "Hello, Bootloader!";
unsigned char hash[SHA256_DIGEST_LENGTH];
SHA256(data, strlen((char*)data), hash);
print_hash(hash);
// 改一个字符试试
data[0] = 'h';
SHA256(data, strlen((char*)data), hash);
print_hash(hash);
return 0;
}
输出结果会完全不一样。这就是为什么我们在验证固件完整性时,先算哈希再比对——只要哈希对得上,数据就没被篡改过。
我的经验:在 Bootloader 里实现 SHA-256 时,注意内存对齐。有些 MCU 对非对齐访问会触发异常,我吃过这个亏。
3.2 非对称加密:公钥与私钥
对称加密就像一把钥匙开一把锁,加密解密用同一个密钥。但问题来了——你怎么安全地把密钥传给对方?
非对称加密解决了这个问题。它用一对密钥:公钥公开,私钥自己藏好。公钥加密的数据只能用私钥解密,反过来私钥加密的数据只能用公钥解密。
在安全启动里,我们主要用两种算法:
| 算法 | 密钥长度 | 安全性基础 | 签名速度 | 典型应用 |
|---|---|---|---|---|
| RSA | 2048 / 4096 位 | 大整数分解 | 较慢 | 传统 Bootloader |
| ECC | 256 / 384 位 | 椭圆曲线离散对数 | 较快 | 资源受限设备 |
你想想看,为什么 ECC 用更短的密钥能达到和 RSA 同等的安全强度?因为它的数学基础更复杂。256 位的 ECC 密钥,安全性和 3072 位的 RSA 差不多。
避坑指南:我曾经在一个项目里用了 1024 位的 RSA,后来发现已经被破解了。现在行业标准是至少 2048 位,我建议直接用 4096 位,反正 Bootloader 里签名验证只做一次,慢点没关系。
3.3 数字签名:证明「你是谁」和「数据没被改」
数字签名把哈希和非对称加密结合起来了。流程是这样的:
- 对固件算 SHA-256 哈希值
- 用私钥对哈希值加密,得到签名
- 把签名和固件一起发布
- 验证时,用公钥解密签名得到哈希值 A
- 对收到的固件算哈希值 B
- 比较 A 和 B,一致则通过
说白了,签名就是「用私钥加密的哈希值」。它同时保证了:
- 完整性:哈希值对得上,数据没被改
- 真实性:能用公钥解开,说明是持有私钥的人签的
- 不可否认性:签名者无法抵赖
下面这张图把整个流程串起来了:
3.4 在 Bootloader 中的实际应用
好了,理论讲完了。咱们看看在 Bootloader 里具体怎么用。
典型的流程是这样的:
- 芯片上电,执行 ROM 里的 BootROM 代码
- BootROM 读取 Flash 中的 Bootloader 第一级(SPL)
- 用烧录在芯片 OTP 里的公钥验证 SPL 的签名
- 验证通过后,跳转到 SPL 执行
- SPL 再验证下一级 Bootloader 或 OS 内核
这就是所谓的「信任链」——每一级都验证下一级,环环相扣。
关键点:信任的根(Root of Trust)必须是硬件不可修改的。公钥要么烧在 OTP 里,要么用硬件唯一密钥派生。我见过有人把公钥放在 Flash 里,结果被攻击者替换了——那整个安全体系就形同虚设了。
3.5 常见误区与避坑
最后,我总结几个实际项目中容易踩的坑:
- 哈希碰撞攻击:虽然 SHA-256 很安全,但别用 MD5 或 SHA-1,它们已经被攻破了。
- 密钥管理:私钥一定要离线保存,最好用 HSM。我曾经见过有人把私钥放在 Git 仓库里...后果可想而知。
- 侧信道攻击:RSA 和 ECC 的实现要注意时序攻击。简单的比较操作要用常量时间比较,否则攻击者能通过测量时间推断出密钥。
- 随机数质量:生成密钥对时,随机数生成器一定要够随机。嵌入式设备上,硬件随机数生成器(TRNG)是必须的。
我的建议:如果你刚开始做安全启动,先用 RSA-2048 + SHA-256 的组合。它成熟、工具链完善、文档多。等把流程跑通了,再考虑换成 ECC 来节省空间和提升性能。