4、签名算法(上):RSA算法原理、密钥生成、签名与验签流程

说到固件签名,RSA 算法是绕不开的基石。我最早接触 RSA 是在做一款 IoT 网关的固件安全升级时,当时被公钥、私钥、模幂运算这些概念搞得晕头转向。后来踩了几个坑,才真正理解它为什么能成为工业界的主流选择。

这一节,咱们就把 RSA 的底裤扒干净。从数学原理到代码实现,一步步走通。

4.1 RSA 算法核心原理

RSA 的安全性建立在「大整数分解」这个数学难题上。说白了就是:两个大质数相乘很容易,但把乘积分解回两个质数,难如登天。

举个例子:13 × 17 = 221,你一秒就能算出来。但给你 221,让你找出 13 和 17,就得花点时间。如果数字大到 2048 位,用目前最快的计算机也要算到宇宙毁灭。

RSA 的核心流程分三步:

  1. 密钥生成:选两个大质数,算出公钥和私钥
  2. 签名:用私钥对固件哈希值加密,生成签名
  3. 验签:用公钥解密签名,比对哈希值

关键点:私钥签名,公钥验签。私钥必须绝对保密,公钥可以公开。

我在项目中见过有人把私钥硬编码在固件里,结果被逆向出来,整个产品线都得召回。嗯,这种坑咱们不能踩。

4.2 密钥生成:手把手走一遍

密钥生成是 RSA 最核心也最容易出错的地方。我习惯用 OpenSSL 来演示,因为它在嵌入式领域最常用。

4.2.1 选择两个大质数 p 和 q

这两个质数要足够大,通常 1024 位以上。实际开发中我们不会自己找质数,而是用工具生成:

# 生成 2048 位 RSA 私钥
openssl genpkey -algorithm RSA -out private_key.pem -pkeyopt rsa_keygen_bits:2048

这条命令背后发生了什么?OpenSSL 会随机选两个 1024 位的质数 p 和 q,然后计算:

  • n = p × q(模数,公钥和私钥都包含它)
  • φ(n) = (p-1)(q-1)(欧拉函数值)
  • e(公钥指数,通常取 65537)
  • d = e⁻¹ mod φ(n)(私钥指数,用扩展欧几里得算法计算)

公钥就是 (n, e),私钥就是 (n, d)。

我的习惯:公钥指数 e 固定用 65537。为什么?因为它是一个费马素数,计算效率高,而且安全性经过充分验证。别自作聪明改成 3 或 17,历史上出过不少漏洞。

4.2.2 提取公钥

# 从私钥中提取公钥
openssl rsa -pubout -in private_key.pem -out public_key.pem

公钥文件里只有 (n, e),没有 p、q、d。所以别人拿到公钥也没法反推出私钥——除非他能分解 n。

4.3 签名流程:私钥加密哈希值

固件签名不是直接加密整个固件,那样太慢了。正确的做法是:

  1. 计算固件的哈希值(比如 SHA-256)
  2. 用私钥对哈希值进行 RSA 加密,得到签名
  3. 将签名附加到固件末尾

用 OpenSSL 签名一条固件:

# 计算固件哈希并签名
openssl dgst -sha256 -sign private_key.pem -out firmware.sig firmware.bin

这条命令做了两件事:先算 SHA-256 哈希,再用私钥加密。输出的 firmware.sig 就是签名数据。

我曾经踩过的坑:签名前一定要确认哈希算法。有一次我用了 SHA-1,结果被客户的安全审计卡住,说 SHA-1 已经不安全了。现在行业标准是 SHA-256 起步,推荐 SHA-384 或 SHA-512。

4.4 验签流程:公钥解密比对

验签是签名的逆过程。设备端拿到固件和签名后:

  1. 用公钥解密签名,得到原始哈希值
  2. 重新计算固件的哈希值
  3. 比对两个哈希值是否一致

OpenSSL 验签命令:

# 验签
openssl dgst -sha256 -verify public_key.pem -signature firmware.sig firmware.bin

如果输出 Verified OK,说明固件未被篡改,且确实来自持有私钥的开发者。

你想想看,如果固件被篡改过,重新计算的哈希值就会变,验签必然失败。这就是 RSA 签名能防篡改的根本原因。

4.5 知识体系总览

下面这张图把 RSA 签名验签的完整流程串起来了:

RSA 签名与验签完整流程 签名方(开发者) 固件文件 (firmware.bin) ① 计算 SHA-256 哈希 哈希值 H = SHA256(firmware) ② 私钥加密哈希 签名 S = RSA_Encrypt(H, 私钥) ③ 附加签名到固件 firmware.sig = firmware + S ④ 分发固件 + 签名 传输 验签方(设备端) 收到 firmware.sig ⑤ 分离固件和签名 得到 firmware 和 S ⑥ 公钥解密签名 H' = RSA_Decrypt(S, 公钥) ⑦ 重新计算固件哈希 H'' = SHA256(firmware) ⑧ 比对 H' == H'' ? 相等 → 验签通过 | 不等 → 拒绝

4.6 实际开发中的注意事项

理论讲完了,说点实战中容易翻车的地方:

问题 后果 解决方案
密钥长度太短 容易被暴力破解 至少 2048 位,推荐 4096 位
私钥泄露 任何人都能伪造签名 硬件安全模块(HSM)存储
哈希算法过时 碰撞攻击风险 使用 SHA-256 或更高
签名未校验 固件被篡改也不自知 Bootloader 中强制验签

我的建议:在嵌入式设备中,公钥要烧写在只读存储器(如 OTP)里,防止被替换。我曾经见过一个产品,公钥存在可擦写的 Flash 中,攻击者直接替换公钥,然后用自己生成的私钥签名恶意固件,整个安全体系形同虚设。

RSA 的数学原理其实不复杂,难的是在实际工程中把每个环节都做对。密钥生成、签名、验签,每一步都有坑。但只要理解了核心逻辑——私钥签名、公钥验签、哈希比对——你就能搭建起可靠的固件安全体系。

下一节咱们继续聊 RSA 的变种和优化,以及在资源受限的 MCU 上如何高效实现验签。

可可精品资料库