3、签名算法基础:哈希算法(SHA256)原理、非对称加密(RSA/ECC)基础、数字签名生成与验证流程

做MCU Boot安全,绕不开签名算法。说白了,签名就是给固件贴个「防伪标签」。你想想看,如果Bootloader不验签名,那随便谁写段代码都能跑在板子上——这在产品量产阶段就是灾难。

我个人习惯把签名拆成三块来理解:哈希、非对称加密、签名流程。咱们一个一个说。

3.1 哈希算法:SHA256 到底在算什么?

哈希算法,也叫散列函数。它的作用是把任意长度的数据,压缩成固定长度的「指纹」。SHA256 的输出是256位,也就是32字节。

我在项目中遇到过一个问题:有同事问「SHA256 是不是加密?」不是。加密可以解密,哈希不行。它是单向的,你从哈希值推不出原始数据。这是它的核心特性。

SHA256 有几个关键性质:

  • 确定性:同样的输入,永远得到同样的输出。
  • 抗碰撞:几乎找不到两个不同的输入,得到相同的哈希值。
  • 雪崩效应:输入改一个比特,输出变化一半左右的比特。

在MCU上跑SHA256,我建议用硬件加速模块。很多Cortex-M系列芯片内置了哈希加速器,算一次256位哈希只需要几千个时钟周期。如果用软件纯算,那可就慢了——尤其在大固件上,几兆字节算下来,Bootloader启动时间会明显变长。

避坑指南:我曾经在某个项目里,为了省成本选了不带硬件哈希的MCU,结果Bootloader启动时间从200ms飙到了1.2s。后来还是换了芯片。所以,选型阶段就把哈希加速器列入必选项。

3.2 非对称加密:RSA 与 ECC 的核心差异

非对称加密,说白了就是两把钥匙:公钥加密,私钥解密;或者私钥签名,公钥验签。在Boot签名场景里,我们只用签名和验签,不用加密。

RSA 和 ECC 是目前最主流的两种算法。它们的数学原理不同,但目标一致:保证私钥持有者才能生成有效签名。

对比项 RSA ECC
数学基础 大整数分解 椭圆曲线离散对数
密钥长度(安全等价) 2048位 256位
签名长度 256字节 64字节
计算速度(签名) 较慢 较快
计算速度(验签) 较快 较慢
MCU资源占用 ROM/RAM需求大 相对较小

我个人更倾向在MCU Bootloader里用ECC。为什么?因为签名验证是在Bootloader里做的,而Bootloader的代码空间通常被限制在几十KB。ECC的密钥和签名都短,代码量也小。RSA 2048位的签名有256字节,在Flash里存一堆公钥也挺占地方。

但ECC也有坑。它的验签比RSA慢。我记得在一个项目里,用NIST P-256曲线验一次签,在72MHz的Cortex-M3上跑了大约80ms。RSA 2048位验签只要不到10ms。所以,如果Bootloader对启动时间极其敏感,RSA反而是更好的选择。

注意:ECC的曲线选择很重要。不要自己发明曲线,用标准曲线(如NIST P-256、secp256r1)。我曾经见过一个团队用了非标准曲线,结果验签偶尔失败,查了三天才发现是曲线参数写错了。

3.3 数字签名生成与验证流程

签名流程其实不复杂。我画个图你就明白了。

数字签名生成与验证流程 数字签名生成与验证流程 签名生成(开发/构建阶段) 固件二进制文件 SHA256 哈希计算 私钥签名(RSA/ECC) 固件 + 签名打包 签名验证(MCU Bootloader) 接收固件 + 签名 SHA256 哈希计算 公钥验签 验证通过 验证失败 传输 关键点: 1. 签名生成在开发/构建环境完成,私钥必须安全保管。 2. Bootloader 只做验签,只存储公钥,不接触私钥。 3. 验签失败直接拒绝启动,防止恶意固件运行。

流程其实就三步:

  1. 计算哈希:对固件做SHA256,得到32字节的摘要。
  2. 签名:用私钥对摘要做签名运算,生成签名数据。
  3. 验签:Bootloader用公钥验证签名是否匹配。

这里有个细节:为什么不对整个固件直接签名?因为非对称加密处理大块数据很慢。先哈希压缩,再签名,效率高得多。

实际项目中的签名流程:我在一个量产项目里,签名流程是这样的——构建服务器编译出固件.bin,然后调用OpenSSL命令行算SHA256,再用私钥做RSA签名,最后把签名追加到固件末尾。Bootloader启动时,先读固件算哈希,再用公钥验签。整个过程在芯片上大约耗时150ms。

3.4 签名验证的常见陷阱

做签名验证,有几个地方容易翻车。我列一下:

  • 哈希计算范围不一致:签名时算了整个固件,验签时却漏了头部几个字节。结果永远验不过。
  • 字节序问题:MCU是小端,PC是大端。签名数据在传输过程中字节序搞反了,验签失败。
  • 公钥存储安全:公钥虽然公开,但如果被篡改,攻击者可以用自己的私钥签名恶意固件。所以公钥要放在只读区域,或者用芯片的OTP(一次性可编程)存储。
  • 签名算法填充方式:RSA签名需要填充(如PKCS#1 v1.5或PSS)。填充方式不匹配,验签必败。
我曾经踩过的坑:有一次,验签总是失败,查了两天。最后发现是SHA256的输入包含了签名本身——先算哈希,再把签名追加进去,然后验签时又算了一遍包含签名的哈希。这属于典型的「先有鸡还是先有蛋」问题。记住:签名永远不参与哈希计算。

3.5 小结

签名算法这块,说白了就是三件事:哈希压缩、私钥签名、公钥验签。SHA256负责生成固定长度的指纹,RSA或ECC负责用密钥对做签名运算。Bootloader只做验签,不存私钥。

我个人建议,新项目优先考虑ECC P-256。密钥短、签名短、代码量小,适合MCU环境。如果启动时间要求苛刻,再考虑RSA。

嗯,这一章就到这里。记住:签名验证是Boot安全的第一道防线,也是最重要的一道。