3. 工具链概览:核心工具介绍与选型

做固件签名,说白了就是给固件打上一个「防伪标签」。你想想看,设备在野外运行,怎么保证它跑的程序就是你自己写的?签名就是干这个的。

市面上能用的工具不少,但各有各的脾气。我这些年折腾下来,踩过不少坑,也积累了一些选型经验。今天就把几个主流工具掰开揉碎讲一讲。

3.1 OpenSSL:瑞士军刀,但有点沉

OpenSSL 几乎是每个嵌入式工程师的标配。它功能太全了,从对称加密、非对称加密、证书管理到签名验签,全都能干。

核心用法:生成密钥对、计算哈希、做 RSA/ECDSA 签名。

# 生成 RSA 私钥(2048 位)
openssl genpkey -algorithm RSA -out private.pem -pkeyopt rsa_keygen_bits:2048

# 提取公钥
openssl pkey -in private.pem -pubout -out public.pem

# 对固件进行签名(先哈希,再签名)
openssl dgst -sha256 -sign private.pem -out firmware.sig firmware.bin

# 验签
openssl dgst -sha256 -verify public.pem -signature firmware.sig firmware.bin

我的经验:OpenSSL 的 CLI 参数设计得不太友好,尤其是 -pkeyopt 这种写法,新手很容易写错。我建议你在脚本里把参数写死,别手敲。

注意:OpenSSL 1.1.1 和 3.x 的某些命令有差异。比如 genpkey 在 1.1.1 里默认生成 PKCS#1 格式,3.x 默认 PKCS#8。如果你在 CI 环境里混用版本,签名验签会失败。我遇到过,排查了一整天。

适用场景:

  • 需要高度定制化的签名流程
  • 已有 OpenSSL 基础设施(如 PKI 体系)
  • 对性能要求不高,但要求兼容性好

3.2 Python Cryptography 库:代码里的签名利器

如果你用 Python 写构建脚本,那 cryptography 库比直接调 OpenSSL CLI 优雅得多。它封装了底层细节,API 更现代,也更安全。

from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import rsa, padding
from cryptography.hazmat.backends import default_backend

# 生成密钥对
private_key = rsa.generate_private_key(
    public_exponent=65537,
    key_size=2048,
    backend=default_backend()
)

# 签名
with open("firmware.bin", "rb") as f:
    firmware_data = f.read()
signature = private_key.sign(
    firmware_data,
    padding.PKCS1v15(),
    hashes.SHA256()
)

# 保存签名
with open("firmware.sig", "wb") as f:
    f.write(signature)

小技巧:用 cryptography 库时,记得把私钥序列化保存,别每次构建都重新生成。否则之前签过名的设备就全废了。

适用场景:

  • 构建流程本身就是 Python 写的
  • 需要把签名逻辑嵌入到 CI/CD 脚本里
  • 对跨平台兼容性有要求(Windows/Linux/macOS)

3.3 signify:轻量级签名,专为固件而生

signify 是 OpenBSD 团队开发的签名工具。它比 OpenSSL 轻得多,设计目标就是「简单、安全、不可误用」。

它的密钥格式是自定的,不兼容 OpenSSL。但正因为格式简单,在资源受限的嵌入式设备上验签非常快。

# 生成密钥对
signify -G -p public.pem -s secret.pem

# 签名固件
signify -S -s secret.pem -m firmware.bin -x firmware.sig

# 验签
signify -V -p public.pem -m firmware.bin -x firmware.sig

我的建议:如果你的设备 MCU 只有几十 KB 的 Flash,验签代码能省则省。signify 的验签代码可以做到 2KB 以内,OpenSSL 的验签库至少 50KB 起步。

适用场景:

  • 资源极度受限的 MCU(如 Cortex-M0)
  • 不需要兼容 PKI 体系
  • 追求极简的签名流程

3.4 sbsign:UEFI Secure Boot 的标配

sbsign 是 sbctl 工具集的一部分,专门用于 UEFI Secure Boot 签名。它操作的是 EFI 可执行文件(.efi),不是裸固件。

# 签名 EFI 文件
sbsign --key db.key --cert db.crt --output bootx64-signed.efi bootx64.efi

# 查看签名信息
sbverify --cert db.crt bootx64-signed.efi

适用场景:

  • x86/ARM 平台的 UEFI 固件签名
  • 需要与 Secure Boot 数据库(PK/KEK/db)交互
  • 企业级设备,要求符合 UEFI 规范

3.5 工具选型对比

我整理了一张表,方便你快速决策:

工具 签名算法 密钥格式 验签代码体积 适用平台 学习曲线
OpenSSL RSA/ECDSA/Ed25519 PEM/DER/PKCS#12 ~50KB+ 通用 陡峭
Python Cryptography RSA/ECDSA/Ed25519 PEM/DER 依赖 Python 运行时 构建主机 中等
signify Ed25519 自定义 ~2KB MCU/嵌入式 平缓
sbsign RSA PEM + X.509 UEFI 内置 x86/ARM UEFI 中等

3.6 我的选型建议

说实话,没有银弹。我一般按这个逻辑来选:

  1. 先看目标硬件:如果是 Cortex-M0 这种小芯片,别犹豫,直接上 signify。验签代码省出来的 Flash 空间,够你塞好几个功能模块了。
  2. 再看构建环境:如果团队用 Python 写构建脚本,那 cryptography 库是首选。别为了签名再装一套 OpenSSL 工具链,维护成本太高。
  3. 最后看合规要求:如果客户要求 UEFI Secure Boot,那 sbsign 是唯一选择。别想着用 OpenSSL 自己拼 EFI 签名结构,我试过,坑太多。

避坑指南:我曾经在一个项目里混用了 OpenSSL 和 signify 的密钥。原因是团队里有人觉得 OpenSSL 生成的私钥更「标准」,想用 signify 验签。结果呢?两个工具用的哈希算法和密钥格式完全不同,根本验不过。最后全部重签,浪费了两天时间。

3.7 工具链关系图

下面这张图展示了各个工具在整个签名流程中的位置和关系:

固件签名工具链关系图 固件签名工具链关系图 固件构建 签名工具 验签/部署 OpenSSL Python Cryptography signify sbsign 选型要点 • 资源受限设备 → signify(验签代码仅 ~2KB) • Python 构建环境 → Python Cryptography 库 • UEFI Secure Boot → sbsign • 通用/定制化需求 → OpenSSL ⚠ 不要混用不同工具的密钥格式,否则验签必败

3.8 小结

工具选型没有标准答案,关键看你的设备资源、构建环境和合规要求。我个人习惯是:先定验签端,再定签名端。因为验签代码是跑在设备上的,一旦定下来就很难改。签名工具换起来就简单多了,大不了改个脚本的事。

嗯,这一章就到这里。记住一点:工具只是手段,安全才是目的。别为了用某个工具而用,想清楚你的场景再选。