4、固件签名与验证:使用设备证书对固件进行数字签名,设备端验签流程

好,咱们进入正题。上一章我们把设备证书的体系搭起来了,那这证书到底怎么用?最核心的一个场景,就是固件签名与验证。

说白了,就是给固件打个「防伪标签」。我经常跟团队里的小朋友讲:没有签名的固件,就像没贴封条的快递——谁都能拆,谁都能往里塞东西。你想想看,这多可怕。

4.1 为什么必须签名?

OTA 升级的本质,是把新固件从云端推到设备上。但网络是不安全的。中间人攻击、固件篡改、回滚攻击……这些威胁真实存在。

我在一个车联网项目里就遇到过:某次测试,攻击者截获了升级包,把里面的 bootloader 换成了恶意版本。幸好我们的验签机制拦住了,不然整批车都得回厂。嗯,从那以后,我对签名这件事就特别较真。

签名能解决三个核心问题:

  • 完整性:固件在传输过程中有没有被改过?
  • 真实性:这个固件是不是官方发布的?
  • 不可否认性:发布方不能抵赖说「这不是我发的」。

核心原则:设备只信任由合法私钥签名的固件。公钥内置于设备信任根中,不可篡改。

4.2 签名流程:在安全环境中完成

签名这件事,必须在离线、安全的环境里做。我个人习惯用一台不联网的专用签名机,所有私钥都放在 HSM(硬件安全模块)里。

流程其实不复杂,我拆成几步讲:

  1. 计算固件哈希:对固件二进制做 SHA-256 或 SHA-384 哈希,得到一个固定长度的摘要。
  2. 私钥签名:用设备证书对应的私钥,对这个哈希值做 RSA 或 ECDSA 签名。
  3. 打包:把原始固件 + 签名值 + 证书链(可选)打包成一个升级包。

这里有个细节:签的是哈希,不是整个固件。为什么?因为非对称签名很慢,固件动不动几十兆,直接签不现实。哈希一下,效率就上来了。

# 伪代码示例:签名过程
hash = SHA256(firmware.bin)
signature = RSA_SIGN(private_key, hash)
update_package = firmware.bin + signature + device_cert.der

我的经验:签名算法推荐 ECDSA 而不是 RSA。同样的安全强度,ECDSA 的签名更短,计算更快。对资源受限的 IoT 设备来说,这很重要。

4.3 验签流程:设备端的生死关卡

设备收到升级包后,验签是第一道门槛。验签没过,后面的解压、刷写统统不执行。

验签流程是这样的:

  1. 提取签名和固件:从升级包里拆出原始固件和签名值。
  2. 获取公钥:从设备的安全存储区读取根证书或设备证书的公钥。
  3. 计算固件哈希:对收到的固件重新计算 SHA-256 哈希。
  4. 验签:用公钥验证签名值是否匹配这个哈希。
  5. 决策:验签通过 → 继续升级;验签失败 → 丢弃升级包,记录日志。
# 伪代码示例:验签过程
firmware, signature = extract(update_package)
hash = SHA256(firmware)
public_key = read_from_secure_storage("device_cert_pub")
if RSA_VERIFY(public_key, hash, signature) == TRUE:
    proceed_with_update()
else:
    reject_update()
    log_error("Signature verification failed")

注意:验签必须在设备的安全执行环境(如 TEE 或 Secure Boot 阶段)中完成。如果在普通操作系统里验签,攻击者可以 hook 验签函数,让它永远返回「通过」。

4.4 证书链验证:不只是验签

光验签还不够。你想想,如果攻击者用自己的证书签名固件,设备用内置的公钥去验,那肯定验不过。但万一设备里的公钥被换了呢?

所以,完整的验证包括证书链验证:

  • 设备内置根证书(自签名的信任锚点)。
  • 升级包里携带设备证书和中间证书。
  • 设备用根证书验证中间证书,再用中间证书验证设备证书,最后用设备证书的公钥验签。

这就像查户口:你不仅要看身份证(设备证书),还要看发证机关(中间 CA)的资质,最终追溯到公安部(根 CA)。

验证层级 验证内容 验证方式
根证书 自验证,信任锚点 预置于设备 ROM / eFuse
中间证书 由根证书签发 用根证书公钥验签
设备证书 由中间证书签发 用中间证书公钥验签
固件签名 由设备证书私钥签名 用设备证书公钥验签

4.5 整体流程可视化

下面这张图把签名和验签的完整流程串起来了。左边是签名端(安全环境),右边是验签端(设备端)。

固件签名与验证流程 签名端(安全环境) 固件二进制 SHA-256 哈希 私钥签名(ECDSA) 打包升级包 验签端(设备端) 接收升级包 拆包:固件 + 签名 公钥验签 + 证书链验证 通过 → 升级 / 失败 → 拒绝 OTA 传输(不安全信道) 关键点:私钥永不离开签名环境,设备只信任内置根证书签发的证书链

4.6 避坑指南

做固件签名验证,有几个坑我踩过,分享给你:

  • 哈希算法别用太弱的:MD5、SHA-1 已经不安全了。最低 SHA-256,我建议 SHA-384。
  • 签名值要防重放:在固件里嵌入版本号和随机数,防止攻击者拿旧版本的合法签名包来攻击。
  • 验签失败的处理要谨慎:不要只是打印一条日志就继续。我见过一个设备,验签失败后竟然还尝试解压——这等于给攻击者留了后门。
  • 公钥存储要防篡改:公钥如果存在可写的 Flash 里,攻击者就能换掉它。必须放在一次性写入的区域,比如 eFuse 或 OTP。

我曾经踩过的坑:有一次,验签逻辑里用了 memcmp 来比较签名值。结果被时序攻击破解了——攻击者通过测量比较时间,逐字节猜出了签名。后来我改用恒定时间比较函数,才堵住这个漏洞。

4.7 小结

固件签名验证,是 OTA 安全的基石。没有它,其他安全措施都是空中楼阁。

记住几个关键点:

  • 签名在安全环境做,私钥永不泄露。
  • 验签在设备端必须早做、快做、做对。
  • 证书链验证不能省,信任必须可追溯。
  • 防重放、防时序攻击、防公钥篡改——一个都不能少。