4. 数字签名原理:签名生成、验证与哈希的关系
数字签名这东西,说白了就是固件在数字世界里的「手写签名」。你想想看,一份合同上签了名,就能证明是你签的,别人改不了,你也赖不掉。固件签名干的也是同样的事——证明固件来自合法发布者,并且没有被篡改过。
我在做嵌入式安全这行之前,总觉得签名是个很「重」的操作。后来踩过几次坑才明白,理解签名原理,是做好固件安全的第一道门槛。咱们今天就把签名生成、签名验证,还有它跟哈希的关系,掰开揉碎了讲清楚。
4.1 签名生成过程
签名生成,发生在固件发布者这一侧。也就是你编译好固件之后,准备发布之前,要做的那一步。
整个过程可以拆成三步:
- 计算固件哈希:对固件二进制文件做一次哈希运算,得到一个固定长度的摘要。
- 用私钥加密哈希:发布者用自己的私钥,对这个哈希值进行加密操作(实际是签名算法,不是简单加密)。
- 打包固件+签名:把原始固件和签名值打包在一起,形成可发布的固件包。
核心要点:签名不是对整个固件做加密,而是对固件的「指纹」(哈希值)做签名。这样做的好处是效率高——固件可能几十兆,哈希只有几十字节。
我习惯用一个简单的命令行流程来演示。假设你用的是 OpenSSL 工具链:
# 第一步:计算固件哈希
openssl dgst -sha256 firmware.bin | cut -d' ' -f2 > firmware.hash
# 第二步:用私钥签名哈希
openssl pkeyutl -sign \
-inkey private_key.pem \
-in firmware.hash \
-out firmware.sig
# 第三步:打包(实际项目中会用自定义打包工具)
cat firmware.bin firmware.sig > firmware_package.bin
嗯,这里要注意——实际产品中很少直接用 OpenSSL 命令行来做签名。一般都是集成到 CI/CD 流水线里,用 SDK 或者专用签名工具。但原理是一样的。
我的经验:签名时一定要记录签名所用的算法和密钥版本。我曾经接手过一个项目,固件包里有签名但没注明算法版本,结果验证端猜了半天才试出来。后来我强制团队在固件头里加一个「签名算法ID」字段,再也没出过这种问题。
4.2 签名验证过程
签名验证,发生在设备端。设备收到固件包后,要确认这个固件确实是合法的发布者签发的,而且没有被篡改过。
验证流程也是三步:
- 拆包:从固件包中分离出原始固件和签名值。
- 计算固件哈希:对原始固件做同样的哈希运算,得到摘要A。
- 用公钥解密签名:用发布者的公钥解密签名值,得到摘要B。如果A == B,验证通过。
重要提醒:设备端必须安全存储公钥。如果公钥被替换,攻击者就能用自己的私钥签名恶意固件。我见过一个 IoT 设备,公钥直接明文存在 Flash 里,攻击者通过串口读出来就替换了——那签名等于白做。
验证的代码逻辑大概长这样:
// 伪代码:设备端签名验证
bool verify_firmware(uint8_t* firmware, uint32_t fw_len,
uint8_t* signature, uint32_t sig_len,
uint8_t* public_key) {
// 1. 计算固件哈希
uint8_t hash[32];
sha256(firmware, fw_len, hash);
// 2. 用公钥验证签名
// 内部会解密签名得到原始哈希,然后对比
return ecdsa_verify(public_key, hash, signature);
}
你可能会问:为什么验证时不是「解密签名得到哈希,再对比」这么简单?其实底层确实是这么做的,但签名算法(比如 ECDSA)的验证过程还包含一些数学校验,防止伪造签名。这里咱们不深究数学细节,记住「哈希对比」这个核心逻辑就够了。
4.3 签名与哈希的关系
哈希是签名的「地基」。没有哈希,签名就没法高效工作。我画了一张图,帮你理清它们的关系:
从图上你能看到,签名生成和验证都依赖哈希运算。哈希在这里扮演了「桥梁」的角色——它把任意大小的固件,映射成一个固定长度的摘要,然后签名算法只需要处理这个摘要就行。
哈希算法有几个关键特性,决定了它适合做签名的基础:
| 特性 | 说明 | 对签名的意义 |
|---|---|---|
| 单向性 | 从哈希值无法反推出原始数据 | 签名不会泄露固件内容 |
| 抗碰撞性 | 很难找到两个不同数据有相同哈希 | 防止伪造固件碰撞签名 |
| 固定长度 | 无论输入多大,输出长度固定 | 签名值大小固定,便于处理 |
| 雪崩效应 | 输入微小变化,哈希完全改变 | 任何篡改都会被检测到 |
避坑指南:我曾经在一个项目里用了 MD5 做签名哈希,当时觉得「反正签名本身有加密保护,哈希弱一点没关系」。结果被安全审计直接打回——MD5 的抗碰撞性已经被攻破,攻击者可以构造两个不同固件但 MD5 相同的碰撞。后来全部换成 SHA-256,再也没敢偷懒。
最后总结一下签名与哈希的关系,就三句话:
- 哈希是签名的「减负器」:把大固件变成小摘要,签名算法只处理摘要。
- 签名是哈希的「保护罩」:用私钥保护哈希不被篡改,同时证明哈希的来源。
- 两者缺一不可:没有哈希,签名效率太低;没有签名,哈希可以被随意替换。