3、密码学基础回顾(下):数字签名的工作原理与算法对比

上一节我们聊了哈希和对称加密,这一节咱们把重点放在数字签名上。说实话,数字签名是固件验签的基石。你想想看,如果没法确认固件是谁签发的、有没有被篡改,那整个安全体系就形同虚设。

我刚开始做固件安全时,总觉得数字签名是个很玄乎的东西。后来亲手搭过几套验签流程,才真正理解它背后的逻辑。说白了,数字签名就是现实世界中「签名+封条」的数字版。

3.1 数字签名到底解决了什么问题?

先问个问题:你收到一个固件包,怎么确认它是官方发布的?

两个核心诉求:

  • 身份认证——这个固件确实是设备厂商签的,不是第三方伪造的。
  • 完整性校验——固件在传输或存储过程中没有被篡改过。

数字签名同时解决了这两个问题。我在一个物联网项目里遇到过,客户拿到的固件被中间人替换了,里面植入了挖矿脚本。就是因为没有做签名验签,设备直接刷了进去。嗯,从那以后我再也不敢省略这步了。

3.2 签名与验签流程

数字签名用的是非对称密码体制。签名方持有私钥,验签方持有公钥。流程其实很清晰:

签名流程(发送方)

  1. 对原始固件计算哈希值(比如 SHA-256)。
  2. 用私钥对哈希值进行加密,生成签名。
  3. 将原始固件 + 签名打包发送出去。

验签流程(接收方)

  1. 拆包,得到原始固件和签名。
  2. 对原始固件计算哈希值(用同样的哈希算法)。
  3. 用公钥解密签名,得到解密后的哈希值。
  4. 对比两个哈希值。一致则验签通过,否则拒绝。

核心要点:签名不是对整个固件加密,而是对固件的哈希值加密。这样做效率高得多,尤其固件动辄几十上百 MB 的时候。

下面这张图把整个流程串起来了,我建议你多看几遍,理解清楚每个环节的作用。

数字签名与验签流程图 数字签名与验签流程 签名方(私钥签名) 原始固件 SHA-256 哈希值 H1 私钥加密 数字签名 固件 + 签名 → 发送 网络传输 验签方(公钥验签) 接收固件 + 签名 固件哈希 H1' 公钥解密得 H2 对比 H1' 与 H2 一致 → 验签通过 ✅ 不一致 → 拒绝 ❌ 关键点:签名是对哈希值加密,不是对整个固件加密;验签时对比的是两个哈希值

我的经验:实际工程中,签名和固件通常分开存储。比如固件放在 Flash 的 A 区,签名放在 B 区。Bootloader 启动时先读签名验签,通过后才加载固件。这样做的好处是,即使签名区被破坏,固件本身还能保留。

3.3 RSA 与 ECDSA 算法对比

目前固件签名领域,RSA 和 ECDSA 是两大主流。我两个都用过,各有优劣。下面这张表把关键差异列出来:

对比维度 RSA ECDSA
安全基础 大整数分解难题 椭圆曲线离散对数难题
密钥长度(同等安全强度) 2048 / 3072 / 4096 位 256 / 384 / 521 位
签名速度 较慢(私钥操作) 较快
验签速度 较快(公钥操作) 较慢
签名长度 与密钥长度相同(如 256 字节) 较短(如 64 字节)
硬件资源消耗 较高(大数运算) 较低
成熟度 非常成熟,应用广泛 较新,但已通过主流认证

RSA 的特点

RSA 是我最早接触的签名算法。它的逻辑很直观:私钥加密,公钥解密。验签时公钥运算非常快,这对嵌入式设备来说是个优势。但 RSA 的密钥太长,2048 位的 RSA 公钥要 256 字节,签名也是 256 字节。在存储空间紧张的 MCU 上,这个开销不小。

我记得有个项目,Flash 总共才 512KB,光存几个 RSA 公钥就占了不少地方。后来不得不改用 ECDSA。

ECDSA 的特点

ECDSA 用更短的密钥达到同等安全强度。256 位的 ECDSA 密钥,安全性和 3072 位的 RSA 差不多。签名长度只有 64 字节,省空间。但 ECDSA 的验签比 RSA 慢,而且对随机数质量要求很高。随机数不好,私钥可能泄露。

注意:ECDSA 的随机数生成器如果出问题,后果很严重。我曾经在项目中遇到过,某个芯片的硬件随机数生成器有缺陷,导致 ECDSA 签名重复出现相同的随机数 k。攻击者可以直接算出私钥。后来我们强制要求每次签名前做随机数质量检测,才堵住这个漏洞。

3.4 工程选型建议

选 RSA 还是 ECDSA?我个人的建议是这样的:

  • 如果 MCU 有硬件加速单元(比如支持 RSA 或 ECC 的硬件引擎),优先用硬件支持的算法。硬件加速比软件快一个数量级。
  • 如果存储空间紧张,选 ECDSA。256 位的 ECDSA 公钥只有 32 字节,签名 64 字节,比 RSA 省太多。
  • 如果验签性能是瓶颈,且公钥存储空间充裕,选 RSA。RSA 验签确实快,尤其用公钥指数 65537 的时候。
  • 如果追求兼容性,RSA 更稳妥。几乎所有安全芯片和加密库都支持 RSA。

我的建议:新项目我倾向于 ECDSA。原因很简单——省空间、省带宽、省功耗。而且现在主流加密库(mbedTLS、OpenSSL)对 ECDSA 的支持已经很成熟了。但如果你做的是存量系统升级,原有 RSA 体系已经跑得很稳,没必要强行迁移。

3.5 一个常见的坑:公钥的信任根

最后说一个我踩过的坑。数字签名本身没问题,但公钥怎么安全地放到设备里?如果公钥被替换了,攻击者用自己的私钥签名,设备照样能通过验签。

我曾经在一个项目里,公钥是出厂时预置在 Flash 里的。后来发现,攻击者通过调试接口读出了 Flash 内容,替换了公钥。结果就是,任何固件都能刷进去。

解决方案?公钥必须存储在安全区域。比如:

  • 一次性可编程(OTP)存储器
  • 安全芯片内部的密钥存储区
  • 硬件信任根(如 eFuse)

嗯,这个坑我印象太深了。从那以后,我设计验签方案时,第一件事就是确认公钥的存储方式是否安全。