密码学基础(上):对称加密、非对称加密、哈希函数在固件更新中的应用

各位同学,咱们今天聊聊固件更新里最核心的东西——密码学。说实话,我刚开始做嵌入式安全那会儿,觉得密码学就是一堆数学公式,离实际工程很远。直到有一次,我负责的一个IoT设备因为固件签名没做好,被黑客注入了恶意固件,整批产品差点召回。从那以后,我再也不敢小看这些基础算法了。

这一章,咱们就聚焦三个最常用的工具:AES(对称加密)、RSA/ECC(非对称加密)、SHA-256(哈希函数)。它们在固件更新里各司其职,缺一不可。

1. 对称加密:AES在固件加密传输中的应用

对称加密,说白了就是加密和解密用同一把钥匙。你想想看,就像你家门锁,用一把钥匙锁上,也用同一把钥匙打开。在固件更新场景里,我们经常用AES来加密固件镜像,防止传输过程中被窃听或篡改。

我个人习惯用AES-128-GCM模式。为什么选GCM?因为它不光加密,还能做完整性校验。我在项目中遇到过,有人只用了AES-CBC模式,结果固件被篡改了都不知道——因为CBC模式不提供认证。嗯,这里要注意,GCM模式会额外生成一个认证标签(tag),你解密的时候必须验证这个tag,否则等于没防篡改。

来看一个典型的固件加密流程:

// 伪代码:固件加密
uint8_t key[16] = {0x2b, 0x7e, 0x15, 0x16, ...}; // 128位密钥
uint8_t iv[12] = {0x00, 0x01, 0x02, ...};         // 12字节随机数
uint8_t firmware[] = {...};                       // 原始固件
uint8_t ciphertext[sizeof(firmware)];
uint8_t tag[16];

aes_gcm_encrypt(key, iv, firmware, ciphertext, tag);
// 将 ciphertext + iv + tag 打包发送给设备

设备端收到后,先解密,再验证tag。如果tag不匹配,直接拒绝更新。我曾经见过一个产品,解密后没验证tag,结果攻击者把固件里的某个字节改了,设备照样跑起来——后果很严重。

避坑指南:密钥管理是老大难。千万别把AES密钥硬编码在固件里!我建议用硬件安全模块(HSM)或安全元件(SE)来存储密钥。如果MCU没有这些,至少用OTP(一次性可编程)区域来存。

2. 非对称加密:RSA/ECC在固件签名验证中的应用

对称加密有个硬伤——密钥分发。你想想,如果我把AES密钥也通过同样的网络发给设备,那攻击者截获了怎么办?所以,固件更新里真正用来做身份认证的,是非对称加密。

非对称加密,就是一对钥匙:公钥和私钥。私钥你藏好,公钥公开。我用私钥给固件签名,设备用公钥验证签名。这样,就算公钥被攻击者拿到,他也伪造不了签名。

我个人更倾向于ECC(椭圆曲线密码学),而不是RSA。为什么?ECC在同等安全强度下,密钥更短、计算更快。比如,256位的ECC相当于3072位的RSA。在嵌入式设备上,这点性能差异非常明显。我记得有一次,在一个只有几十KB RAM的MCU上跑RSA-2048签名验证,耗时超过2秒,换成ECC-256后,降到200毫秒以内。

固件签名验证的典型流程:

// 签名端(服务器)
uint8_t private_key[] = {...};  // 私钥
uint8_t firmware_hash[32];      // 固件的SHA-256哈希
sha256(firmware, firmware_hash);
ecdsa_sign(private_key, firmware_hash, signature);

// 验证端(设备)
uint8_t public_key[] = {...};   // 公钥(烧录在设备中)
uint8_t firmware_hash[32];
sha256(received_firmware, firmware_hash);
if (ecdsa_verify(public_key, firmware_hash, signature) == SUCCESS) {
    // 签名验证通过,可以更新
} else {
    // 签名无效,拒绝更新
}
小技巧:公钥一定要在设备出厂前就烧录好,并且用硬件保护起来。我见过有人把公钥放在文件系统里,结果被攻击者替换了——那整个签名体系就形同虚设了。

3. 哈希函数:SHA-256在固件完整性校验中的应用

哈希函数,说白了就是给数据算一个「指纹」。不管你的固件有多大,SHA-256都能算出一个256位的固定长度哈希值。只要固件里有一个比特变了,哈希值就会完全不一样。

在固件更新里,SHA-256有三个典型用途:

  1. 完整性校验:下载完固件后,算一下哈希,跟服务器给的哈希对比。不一样?说明传输过程中出错了,或者被人动过手脚。
  2. 签名的基础:非对称签名不是直接对固件签名,而是对固件的哈希签名。因为固件可能很大,直接签名太慢。
  3. 版本防回滚:把固件版本号也参与哈希计算,防止攻击者把固件回滚到有漏洞的旧版本。

我曾经在一个项目中,只做了签名验证,没做哈希完整性校验。结果有一次,固件在传输过程中因为网络丢包损坏了几个字节,签名验证居然通过了(因为签名只验证了哈希,而哈希恰好没变?不,其实是因为签名验证本身没问题,但固件损坏了,设备刷进去就变砖了)。从那以后,我坚持「签名+哈希」双重校验。

来看一个完整的固件更新校验流程:

// 设备端校验流程
1. 接收固件镜像和元数据(哈希值、签名、版本号)
2. 计算接收到的固件的SHA-256哈希
3. 对比计算出的哈希与元数据中的哈希
   - 不一致 → 丢弃,报错
   - 一致 → 继续
4. 用公钥验证签名
   - 验证失败 → 丢弃,报错
   - 验证通过 → 继续
5. 检查版本号是否大于当前版本
   - 否 → 拒绝更新(防回滚)
   - 是 → 执行更新
核心要点:哈希函数是单向的,你无法从哈希值反推出原始数据。所以,即使攻击者拿到了固件的SHA-256值,他也无法伪造一个「碰撞」的恶意固件。这就是为什么哈希是固件安全的第一道防线。

4. 三种算法如何协同工作

你可能会问:这三种算法到底怎么配合?我画个简单的流程图给你看:

步骤 服务器端 设备端
1 用SHA-256计算固件哈希 接收加密固件+签名+哈希
2 用私钥对哈希签名 用AES密钥解密固件
3 用AES密钥加密固件 计算解密后固件的SHA-256
4 打包发送:加密固件+签名+哈希 对比哈希,验证签名
5 全部通过 → 执行更新

你看,AES负责加密传输,防止固件内容泄露;RSA/ECC负责身份认证,确保固件来自合法来源;SHA-256负责完整性校验,确保固件没有被篡改。三者缺一不可。

再次提醒:密钥生命周期管理非常重要。私钥泄露了,整个产品线的安全就完了。我建议定期轮换密钥,并且使用硬件安全模块来生成和存储密钥。别问我怎么知道的——都是血泪教训。

好了,这一章的内容就到这里。下一章咱们会深入讲数字证书和PKI体系,看看如何用证书链来管理公钥信任。到时候我会分享一个我踩过的坑——关于证书过期导致设备无法更新的真实案例。