3. 密码学基础(上):对称加密(AES)、非对称加密(RSA/ECC)、哈希函数(SHA-256)

做安全启动,说白了就是解决三个问题:你是谁?你从哪里来?你要到哪里去?——开个玩笑。但认真讲,MCU 的启动链安全,核心就靠三种密码学工具:对称加密、非对称加密和哈希函数。

我刚开始接触 MCU 安全时,总觉得这些算法离嵌入式很远。直到有一次,我在一个 IoT 网关项目里,因为没选对哈希算法,导致启动时间多了 200ms,被客户怼了一顿。从那以后,我对这些基础算法再也不敢马虎了。

这一章,咱们就把这三类工具掰开揉碎,讲清楚它们各自在 MCU-Boot 里扮演什么角色。

3.1 对称加密:AES

对称加密,就是加密和解密用同一把钥匙。你想想看,就像你家大门,用一把钥匙锁上,也用同一把钥匙打开。在 MCU 启动场景里,我们常用它来加密固件镜像,防止别人直接读 Flash 就把代码抄走了。

3.1.1 AES 的核心参数

AES(Advanced Encryption Standard)是目前最主流的对称加密算法。在 MCU 上,我们最关心这几个参数:

参数 常见值 说明
密钥长度 128 / 192 / 256 bit MCU-Boot 常用 AES-128,平衡安全与速度
分组大小 128 bit(固定) AES 每次加密 16 字节
工作模式 ECB / CBC / CTR / GCM MCU 上推荐 GCM 或 CTR,避免 ECB 的弱点
填充方式 PKCS7 / NoPadding 流模式不需要填充
注意:ECB 模式在固件加密中非常危险。我曾经见过一个产品,用 ECB 加密固件,结果因为固件头部有固定模式,攻击者直接通过密文比对就推断出了密钥的某些特征。千万别图省事用 ECB。

3.1.2 MCU 上的 AES 实现

大多数现代 MCU 都内置了 AES 硬件加速器。我个人习惯是:能用硬件就别用软件。硬件 AES 不仅快,还能防侧信道攻击。

下面是一个典型的硬件 AES 初始化代码(以 STM32 为例):

// 初始化 AES 硬件外设
HAL_AES_Init(&haes);

// 设置密钥(128位)
uint8_t key[16] = {0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6,
                   0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C};

// 设置初始向量(CBC模式需要)
uint8_t iv[16] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
                  0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F};

// 加密一块数据
HAL_AES_Encrypt(&haes, plaintext, 16, ciphertext, HAL_AES_MODE_CBC);
经验之谈:在 Bootloader 里,我建议把 AES 密钥存储在 MCU 的 OTP(一次性可编程)区域,或者使用内部唯一 ID 派生。千万别把密钥明文写在代码里,否则别人反编译一下就拿到了。

3.2 非对称加密:RSA 与 ECC

非对称加密,就是加密和解密用不同的钥匙。一把公钥公开,一把私钥保密。在安全启动里,我们通常用私钥签名固件,用公钥验证签名。这样即使公钥被拿到,也无法伪造固件。

3.2.1 RSA:经典但沉重

RSA 的安全性基于大整数分解的难度。在 MCU 上,RSA 的主要问题是:计算量大,密钥长。

  • 密钥长度:通常 2048 位起步,1024 位已被认为不安全
  • 签名验证:比 ECC 慢 5-10 倍
  • 内存占用:大数运算需要较多 RAM

我记得有一次在 Cortex-M4 上做 RSA-2048 签名验证,一次验证花了将近 800ms。对于启动时间要求 500ms 以内的产品,这显然不行。

3.2.2 ECC:轻量级选手

ECC(椭圆曲线密码学)在相同安全强度下,密钥更短,计算更快。比如:

安全等级 RSA 密钥长度 ECC 密钥长度
80 bit 1024 160
112 bit 2048 224
128 bit 3072 256
192 bit 7680 384

在 MCU-Boot 里,我个人强烈推荐使用 ECC-256(secp256r1)。它能在安全性和性能之间取得很好的平衡。

核心结论:对于 MCU 安全启动,签名验证用 ECC,固件加密用 AES。这是目前工业界的主流做法。

3.3 哈希函数:SHA-256

哈希函数,就是把任意长度的数据,压缩成一个固定长度的摘要。而且这个摘要是不可逆的——你没法从摘要反推出原始数据。

在安全启动里,哈希函数有三个关键用途:

  1. 完整性校验:计算固件的哈希值,和存储的参考值对比,看固件有没有被篡改
  2. 数字签名的基础:先对固件做哈希,再对哈希值做签名,而不是直接对固件签名
  3. 密钥派生:从主密钥派生出多个子密钥

3.3.1 SHA-256 的特点

  • 输出长度:256 位(32 字节)
  • 安全强度:128 位(抗碰撞)
  • 硬件支持:很多 MCU 内置 SHA-256 硬件加速器

你想想看,为什么我们不直接对固件签名?因为固件可能很大(几 MB),非对称签名算法处理大块数据很慢。先做哈希,把固件压缩成 32 字节,再对这 32 字节签名,效率就高多了。

3.3.2 哈希链在安全启动中的应用

在 MCU-Boot 里,我们经常用哈希链来验证多级启动。比如:

BootROM → Bootloader → 应用固件
   ↓           ↓            ↓
  验证BL哈希   验证APP哈希   运行

每一级启动前,先计算下一级固件的 SHA-256 值,和签名中提取的哈希值比对。一致才跳转执行。

避坑指南:我曾经在一个项目里,因为哈希计算时没有考虑对齐问题,导致 Bootloader 和 App 的哈希值始终对不上。后来发现是内存对齐导致最后几个字节被漏算了。记住:哈希计算一定要保证输入数据的完整性,特别是边界情况。

3.4 三种工具如何协同工作

好了,现在我们把三种工具串起来,看看在安全启动里它们是怎么配合的:

安全启动密码学流程 固件镜像 SHA-256 哈希 ECC 私钥签名 Flash 存储 Bootloader 验证 ECC 公钥验签 + SHA-256 比对 验证通过 → 跳转执行 说明: 固件在开发阶段用 SHA-256 计算摘要,再用 ECC 私钥签名。Bootloader 在启动时用 ECC 公钥验签,并重新计算 SHA-256 比对。
总结一下:
  • AES:加密固件,防止泄露
  • ECC/RSA:签名验证,防止篡改
  • SHA-256:完整性校验,压缩数据
三者缺一不可,共同构成安全启动的密码学基石。

嗯,这一章的内容就到这儿。记住:密码学不是数学题,而是工程实践。选对算法、配好参数、用好硬件加速,你的 MCU-Boot 才能既安全又高效。