2. 密码学基础回顾:哈希算法、非对称加密与数字签名

各位同学,咱们今天聊点硬核的。安全启动这玩意儿,说白了就是一套「信任链」的传递。而支撑这条链子的,就是密码学里的三驾马车:哈希、非对称加密和数字签名。

我见过不少工程师,代码写得飞起,但一提到这些数学原理就头大。其实不用怕,咱们不推公式,只讲「它是什么、能干什么、在安全启动里怎么用」。你只要理解了这三样东西,后面看代码和配置就会豁然开朗。

2.1 哈希算法:数据的「指纹」

哈希算法,也叫散列函数。它的作用很简单:给任意长度的数据,算出一个固定长度的摘要。

这个摘要,就像人的指纹。你无法从指纹反推出人的长相,但你可以通过比对指纹来确认是不是同一个人。

核心特性:

  • 单向性:从哈希值无法反推出原始数据。这是铁律。
  • 抗碰撞性:几乎不可能找到两个不同的输入,产生相同的哈希值。
  • 雪崩效应:输入哪怕只改一个比特,输出的哈希值也会面目全非。

在安全启动里,我们最常用的是 SHA-256。它输出 256 位(32 字节)的摘要。为什么是它?因为它的安全性经过了十几年的考验,而且硬件加速支持非常普遍。

我的经验: 我在一个 IoT 项目里,遇到过有人想用 MD5 做固件校验。我直接否了。MD5 的碰撞攻击在 2004 年就已经被公开,现在用 MD5 做安全启动,等于把大门钥匙挂在门框上。记住,安全启动里,哈希算法至少要用 SHA-256。

来看一个简单的例子,用 OpenSSL 计算文件的 SHA-256 哈希值:

# 计算固件镜像的哈希值
openssl dgst -sha256 firmware.bin

# 输出示例:
# SHA256(firmware.bin)= a3f1b2c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5

你想想看,这个 64 位的十六进制字符串,就代表了整个固件。只要固件被篡改过哪怕一个字节,这个字符串就会完全不一样。

2.2 非对称加密:公钥与私钥的「双钥匙」体系

对称加密就像一把钥匙开一把锁,加密和解密用同一个密钥。但问题来了:你怎么安全地把这把钥匙交给对方?

非对称加密解决了这个问题。它使用一对密钥:公钥和私钥。公钥可以公开,私钥必须保密。

两种主要用途:

  • 加密:用公钥加密,只有对应的私钥能解密。
  • 签名:用私钥签名,只有对应的公钥能验证。

在安全启动里,我们主要用第二种——签名和验证。

2.2.1 RSA 与 ECC:两种主流算法

特性 RSA ECC(椭圆曲线)
安全性基础 大整数分解难题 椭圆曲线离散对数难题
密钥长度(同等安全强度) 2048 位 256 位
签名速度 较慢 较快
验证速度 较快 较慢
硬件资源消耗 高 低

我个人习惯,在资源受限的嵌入式设备(比如 Cortex-M 系列)上,优先选择 ECC。为什么?因为 256 位的 ECC 密钥就能达到 2048 位 RSA 的安全等级,而且签名计算更快,对 CPU 和内存的压力小得多。

注意: 我曾经在一个项目里,看到有人用 1024 位的 RSA 密钥。这在今天已经不够安全了。NIST 已经明确要求 2030 年后淘汰 2048 位以下的 RSA。所以,新设计请至少用 2048 位 RSA 或 256 位 ECC。

2.3 数字签名:给固件盖上「防伪章」

数字签名,就是把哈希和非对称加密结合起来。它解决了三个问题:

  • 完整性:固件有没有被篡改?
  • 真实性:这个固件是不是来自声称的发布者?
  • 不可否认性:发布者不能抵赖自己签过这个固件。

流程其实很简单:

  1. 签名方(通常是服务器):计算固件的 SHA-256 哈希值,然后用私钥对这个哈希值进行加密,得到「签名」。
  2. 验证方(通常是设备):收到固件和签名后,用公钥解密签名,得到原始的哈希值。同时自己计算固件的哈希值。两个哈希值比对,一致则通过。

一句话总结: 数字签名 = 哈希 + 非对称加密。哈希保证内容没变,非对称加密保证来源可信。

来看一个完整的签名和验证流程,用 OpenSSL 演示:

# 1. 生成 ECC 私钥(P-256 曲线)
openssl ecparam -genkey -name prime256v1 -out private.pem

# 2. 从私钥导出公钥
openssl ec -in private.pem -pubout -out public.pem

# 3. 对固件进行签名
openssl dgst -sha256 -sign private.pem -out firmware.sig firmware.bin

# 4. 验证签名
openssl dgst -sha256 -verify public.pem -signature firmware.sig firmware.bin

# 验证成功输出:Verified OK

你想想看,设备上只需要存储公钥。公钥是公开的,即使被攻击者拿到,也无法伪造签名。因为签名需要私钥,而私钥牢牢掌握在厂商手里。

2.4 三者的关系:一张图说清楚

下面这张图,展示了哈希、非对称加密和数字签名在安全启动中的协作关系。我建议你多看几遍,把流程刻在脑子里。

数字签名与验证流程 数字签名与验证流程 签名方(服务器) 固件镜像 私钥 SHA-256 哈希 + 私钥加密 数字签名 传输 固件镜像 + 数字签名 → 传输到设备 验证方(设备) 计算固件哈希值 公钥解密签名 比对哈希值 通过 / 拒绝

2.5 避坑指南:我踩过的那些坑

讲了这么多理论,最后分享几个实战中容易翻车的地方。

坑一:哈希值存储位置

我曾经在一个项目里,看到有人把固件的哈希值直接写在固件头里。这等于把密码贴在保险柜门上。正确的做法是:哈希值必须通过签名保护,或者存储在一次性可编程(OTP)存储器里。

坑二:公钥的信任根

公钥本身也需要被信任。如果攻击者替换了设备里的公钥,那他就可以用自己的私钥签名恶意固件。所以,公钥必须通过硬件安全机制(比如 eFuse 或安全元件)来保护,确保不可篡改。

坑三:随机数生成

非对称加密的密钥生成,依赖高质量的随机数。如果随机数生成器被预测,密钥就可能被破解。我建议在嵌入式设备上,使用硬件随机数生成器(TRNG),而不是软件伪随机数生成器。