3、密码学基础回顾:哈希函数、非对称加密、数字签名、证书链

做安全启动,说白了就是跟「信任」打交道。你得让芯片相信,它加载的每一段代码都是你写的,没人动过手脚。而支撑这套信任体系的,就是密码学里的几个老朋友:哈希、非对称加密、数字签名,还有证书链。

我刚开始做嵌入式安全那会儿,总觉得这些概念太理论,离底层太远。直到有一次,我在一个 IoT 项目里因为签名验证少了一步,导致设备被刷入了恶意固件……嗯,从那以后,我再也不敢小看这些基础了。

3.1 哈希函数:数据的「指纹」

哈希函数,你可以把它理解成一个单向的榨汁机。你塞进去一个西瓜(任意长度的数据),它给你榨出一杯固定大小的果汁(固定长度的哈希值)。而且,你没法从果汁里把西瓜还原出来。

在安全启动里,哈希函数主要干三件事:

  • 完整性校验:检查固件有没有被篡改。哪怕只改了一个 bit,哈希值都会变得面目全非。
  • 数据摘要:对一段很长的固件做哈希,得到一个短摘要。签名的时候,我们只签这个摘要,而不是整个固件——效率高得多。
  • 防碰撞:好的哈希算法,几乎不可能找到两个不同的输入,却得到相同的输出。

我常用的哈希算法对比

算法 输出长度 安全性 嵌入式适用性
SHA-1 160 bit 已废弃(有碰撞攻击) 不推荐
SHA-256 256 bit 安全(当前主流) 广泛支持,性能适中
SHA-384 384 bit 安全 资源充裕时使用
SHA-512 512 bit 安全 性能开销较大

避坑指南:我曾经在一个老项目里看到有人还在用 SHA-1 做固件校验。当时觉得「反正也没人攻击我们」,结果后来发现 SHA-1 的碰撞攻击成本已经低到个人电脑就能跑。赶紧换 SHA-256,别犹豫。

3.2 非对称加密:公钥与私钥

非对称加密,就是一对钥匙:一把公开(公钥),一把私藏(私钥)。公钥加密的数据,只有私钥能解开;反过来,私钥签名的数据,公钥能验证。

在安全启动场景里,我们几乎不用非对称加密来做数据加密(太慢了),而是用它来做身份认证和密钥交换。

常见的算法有:

  • RSA:老牌算法,2048 位以上才安全。嵌入式里做签名验证还行,但生成签名很慢。
  • ECC(椭圆曲线):同样的安全强度,密钥比 RSA 短得多。比如 256 位的 ECC 相当于 3072 位的 RSA。我个人更推荐 ECC,尤其是在资源受限的 MCU 上。
  • Ed25519:一种基于 Curve25519 的签名算法,速度快,实现简单,而且抗侧信道攻击。我最近几个项目都在用这个。

注意:非对称加密的私钥是安全启动的「命根子」。私钥一旦泄露,整个信任链就崩塌了。我见过有厂商把私钥硬编码在编译脚本里,结果被逆向工程扒了出来……嗯,那场面相当尴尬。

3.3 数字签名:给固件「盖个章」

数字签名,就是哈希函数 + 非对称加密的组合拳。流程很简单:

  1. 签名方:对固件做哈希,然后用私钥加密这个哈希,得到签名。
  2. 验证方:对收到的固件做同样的哈希,然后用公钥解密签名,得到原始的哈希。两个哈希一比对,一致就通过。

你想想看,只要私钥没丢,任何人想伪造一个合法的固件,都得先破解私钥——这在计算上是不可行的。

在安全启动里,签名验证通常发生在 BootROM 或第一级 bootloader 中。代码量要极小,不能依赖复杂的操作系统。

// 伪代码:签名验证流程(简化版)
hash = SHA256(firmware_image);
decrypted_hash = RSA_Decrypt(signature, public_key);
if (hash == decrypted_hash) {
    // 签名验证通过,启动固件
} else {
    // 签名验证失败,进入错误处理
    halt();
}

我踩过的坑:有一次,我在验证签名时忘了检查哈希长度。攻击者伪造了一个超长的哈希值,导致缓冲区溢出,直接跳过了验证逻辑。从那以后,我写签名验证代码时,一定会先检查输入长度是否合法。

3.4 证书链:信任的传递

单靠一个公钥,你只能信任一个签名者。但在实际产品中,固件可能由多个团队开发,甚至由第三方提供。这时候就需要证书链了。

证书链,说白了就是一层一层的信任传递:

  • 根证书:自签名,是信任的锚点。通常烧死在芯片的 OTP(一次性可编程)存储器里,不可更改。
  • 中间证书:由根证书签发,用于授权不同的部门或产品线。
  • 终端证书:由中间证书签发,直接用于签名固件。

验证时,芯片从终端证书开始,逐级向上验证,直到根证书。只要根证书可信,整条链就可信。

下面这张图展示了典型的证书链结构:

安全启动证书链结构图 根证书(Root CA) 烧死在芯片 OTP 中 中间证书(产品线) 由根证书签发 中间证书(研发部) 由根证书签发 终端证书(固件 A) 签名固件 A 终端证书(固件 B) 签名固件 B 终端证书(固件 C) 签名固件 C 验证路径示例 固件 A → 终端证书1 → 中间证书1 → 根证书(信任锚) 只要根证书可信,整条链上的所有固件都可信

我的经验:证书链不是越长越好。每多一级,验证时间就多一分,代码复杂度也增加。对于大多数嵌入式设备,两级(根证书 + 终端证书)就足够了。三级以上,除非你有复杂的多部门协作需求,否则纯属给自己找麻烦。

3.5 小结:这些基础怎么用在安全启动上?

好了,我们把这几样东西串起来,看看它们在安全启动里是怎么配合的:

  1. 哈希函数:对固件做摘要,确保完整性。
  2. 非对称加密:提供公钥/私钥对,私钥签名,公钥验证。
  3. 数字签名:用私钥签名固件的哈希,防止固件被篡改或伪造。
  4. 证书链:把公钥包装成证书,逐级授权,形成信任链。

说白了,安全启动就是一场「信任的接力赛」。从芯片出厂时烧录的根公钥开始,一级一级验证下去,直到最终加载的操作系统。任何一环断了,系统就拒绝启动。

我见过不少开发者,觉得密码学太复杂,就随便找了个开源库,复制粘贴几行代码完事。结果呢?要么是哈希算法选错了,要么是签名验证没做边界检查,要么是证书链验证跳过了根证书检查。每一个坑,都可能导致设备被攻破。

嗯,基础不牢,地动山摇。把这些基础打扎实了,后面的防回滚策略才能站得住脚。