固件加密与签名:AES-256-GCM 加密实践、ECDSA 签名、密钥管理策略
这一章我们聊聊固件安全里最核心的两个动作——加密和签名。很多人觉得这俩是一回事,其实不然。加密是为了保密,签名是为了防篡改和验身份。我见过不少产品,加密做得很好,但没签名,结果固件被替换成恶意版本,照样跑起来。嗯,咱们得把这两件事都做扎实。
为什么需要加密 + 签名?
你想想看,固件在 Flash 里躺着,或者在 OTA 传输途中,本质上都是暴露在攻击者眼皮底下的。如果只是明文存储,拿个逻辑分析仪就能把二进制读出来。更危险的是,攻击者可以修改固件,植入后门,再刷回去。
所以我们需要两把锁:
- 加密:让固件内容只有合法设备能看懂。
- 签名:让设备能确认固件来自可信源头,且没有被改过。
我个人习惯把加密和签名看作「保险柜 + 封条」的关系。加密是保险柜,签名是封条。封条破了,你知道东西被动过;保险柜锁着,别人拿不到内容。
AES-256-GCM:加密与认证一步到位
加密算法很多,为什么我推荐 AES-256-GCM?因为它同时提供了机密性和完整性认证。GCM 模式在加密的同时会生成一个认证标签(Authentication Tag),解密时如果标签不匹配,说明数据被篡改过。
我在项目中遇到过一件事:早期用 AES-CBC 加密固件,结果攻击者通过比特翻转攻击,修改了密文中的某些位,解密后固件虽然乱码了,但设备没有检测机制,直接跑飞了。换成 GCM 之后,认证标签会直接拒绝任何篡改过的数据,安全多了。
AES-256-GCM 加密流程
加密时我们需要三个输入:密钥、初始向量(IV)、明文。输出是密文和认证标签。IV 不需要保密,但每次加密必须不同,否则同样的密钥和 IV 会产生同样的密文,这就危险了。
// 伪代码示例:AES-256-GCM 加密
function encrypt_firmware(plaintext, key) {
iv = generate_random_iv(12 bytes); // 推荐 12 字节 IV
(ciphertext, tag) = aes_256_gcm_encrypt(plaintext, key, iv);
// 打包:IV + 密文 + 标签
return iv || ciphertext || tag;
}
解密时,先提取 IV,再用密钥解密,最后验证认证标签。标签验证失败,直接拒绝加载固件。
ECDSA 签名:用椭圆曲线给固件打上烙印
加密解决了保密问题,但设备怎么知道固件是谁发的?这就需要数字签名。ECDSA(椭圆曲线数字签名算法)是目前嵌入式领域的主流选择,因为它签名短、计算快、安全性高。
签名过程是这样的:
- 签名者(比如服务器)用私钥对固件的哈希值签名,生成签名数据。
- 设备用公钥验证签名。验证通过,说明固件来自可信源。
这里有个关键点:签名是对哈希值签的,不是对整个固件。固件可能很大,哈希一下能大幅减少计算量。常用的哈希算法是 SHA-256。
ECDSA 签名与验证流程
// 签名端(服务器)
function sign_firmware(firmware_hash, private_key) {
signature = ecdsa_sign(firmware_hash, private_key);
return signature; // 通常为 64 字节(P-256 曲线)
}
// 验证端(设备)
function verify_firmware(firmware_hash, signature, public_key) {
result = ecdsa_verify(firmware_hash, signature, public_key);
return result; // true 或 false
}
密钥管理策略:最薄弱的环节
算法再强,密钥泄露就全完了。密钥管理是固件安全里最容易被忽视的部分。说白了,密钥就是你的命根子。
密钥生命周期管理
| 阶段 | 要求 | 常见问题 |
|---|---|---|
| 生成 | 使用硬件真随机数发生器(TRNG) | 用软件伪随机数,可预测 |
| 存储 | 安全元件或硬件安全模块(HSM) | 明文存储在 Flash 中 |
| 分发 | 安全通道,预置或证书交换 | 通过不安全的网络传输 |
| 使用 | 仅在安全环境中解密,用完即擦 | 密钥常驻内存,可被 dump |
| 销毁 | 物理销毁或安全擦除 | 废弃设备中密钥未清除 |
我个人习惯把密钥分成几层:
- 根密钥:烧死在芯片内部,不可读出。用于加密其他密钥。
- 固件加密密钥:由根密钥派生,用于加密固件。
- 签名私钥:保存在服务器 HSM 中,设备只存对应的公钥。
这样做的好处是,即使固件加密密钥泄露,根密钥还在,可以更换派生密钥。我曾经帮一个客户做安全审计,发现他们把签名私钥直接放在编译服务器上,任何能登录服务器的人都能签名固件。后来我们强制要求私钥必须存储在 HSM 中,签名操作通过 API 调用,私钥永不离开硬件。
整体流程:从加密到签名的端到端设计
下面这张图展示了固件从构建到部署的完整安全流程。你可以看到加密和签名是如何协同工作的。
流程其实不复杂:先对固件做哈希,哈希值拿去签名,固件本身拿去加密。最后把密文、签名、IV、认证标签打包在一起。设备端收到后,先验签名,再解密。签名验证失败,直接丢弃,不解密。
避坑指南:我踩过的几个坑
- 密钥硬编码:我曾经接手一个项目,AES 密钥直接写在源码里,编译进固件。攻击者反编译一下就拿到了。密钥应该从安全存储中读取,或者由硬件派生。
- 签名验证不完整:有些实现只验证签名是否存在,不验证签名是否匹配。攻击者可以附上一个任意签名,设备照样通过。一定要用公钥完整验证。
- 随机数重用:ECDSA 签名需要随机数 k,如果 k 重复,私钥会被算出来。索尼 PS3 就栽在这个坑上。一定要用硬件 TRNG 生成 k。
- 忽略回滚攻击:攻击者可能拿旧版本的固件(有已知漏洞)来刷。签名验证通过,但版本是旧的。所以签名时要带上版本号或时间戳,设备端检查版本是否最新。