第4章:代码签名与验证——数字签名原理与S32G实战

好,咱们进入安全启动最核心的一环——代码签名与验证。说白了,就是给你的固件贴上一张「防伪标签」,让芯片只认你签过名的代码。我当年第一次接触这个时,觉得不就是加个签名嘛,后来踩了不少坑才发现,这里面的门道真不少。

4.1 数字签名原理:RSA与ECDSA

数字签名是什么?你可以想象成你写了一份合同,然后在上面盖了个私章。别人拿到合同,用你的公章印泥一比对,就知道是不是你本人签的。在嵌入式世界里,这个「私章」就是私钥,「公章」就是公钥。

目前主流的两大算法:RSAECDSA。我个人的习惯是,如果芯片资源够用,优先选 ECDSA,为什么呢?

特性 RSA ECDSA
密钥长度 2048/4096位 256/384位(等效安全强度)
签名速度 较慢 较快
验签速度 较快 较慢(但够用)
存储占用
典型应用 传统BootROM S32G等现代芯片

你想想看,S32G 的 BootROM 里硬编码了 ECDSA 验签逻辑,你非要用 RSA,那就得自己写软件验签,性能会差很多。我在项目中遇到过一位同事,非要在 S32G 上用 RSA-4096,结果启动时间多了 200 毫秒,被客户投诉了。

核心原理一句话:签名 = 用私钥对哈希值加密;验签 = 用公钥解密哈希值,再比对是否一致。

4.2 S32G签名工具链:CST(Code Signing Tool)

恩智浦官方提供的签名工具叫 CST,全称 Code Signing Tool。嗯,这里要注意,CST 不是图形界面工具,它是命令行工具。我第一次用的时候,找了半天没找到 exe 文件,后来才发现它是个 Java 程序。

CST 的核心工作流程是这样的:

  1. 准备镜像文件:比如你的 u-boot.bin 或 Linux 内核镜像
  2. 计算哈希:CST 会对镜像做 SHA-256 哈希
  3. 生成签名:用你的私钥对哈希值签名
  4. 打包成安全镜像:把原始镜像 + 签名 + 证书链打包成一个 .signed 文件

我建议你先把 CST 解压到 Linux 环境里,然后设置好环境变量:

export CST_PATH=/opt/nxp/cst_v4.0.0
export PATH=$CST_PATH:$PATH

然后验证一下工具是否正常:

java -jar $CST_PATH/cst.jar --version

如果看到版本号输出,恭喜你,环境搭好了。我曾经在 Windows 上折腾了半天,最后发现 CST 对 Windows 支持不太好,老老实实换回了 Ubuntu。

4.3 签名与验签实战

好,咱们直接上手。假设你有一个 u-boot.bin 需要签名。

4.3.1 生成密钥对

首先,你需要一对密钥。CST 自带了一个密钥生成脚本:

cd $CST_PATH/keys
./gen_keys.sh

这个脚本会生成以下文件:

  • srk.pem:超级根密钥(Super Root Key)公钥
  • srk.der:DER 格式的公钥
  • srk_0.pemsrk_3.pem:四个 SRK 表项

警告:私钥文件一定要妥善保管!我见过有人把私钥传到 Git 仓库里,结果整个安全体系直接报废。建议用 HSM(硬件安全模块)或离线存储。

4.3.2 配置签名参数

CST 需要一个配置文件,告诉它你要签什么、用什么密钥。创建一个 sign_u-boot.txt

[Header]
Version = 4.0.0

[Signatures]
Target = u-boot.bin
Output = u-boot.signed
PrivateKey = keys/srk_0.pem
Certificate = keys/srk_0.der

[Authentication]
SRKTable = keys/srk_table.bin
SRKIndex = 0

这里有个坑:SRKIndex 必须和你的私钥对应。如果你用了 srk_1.pem,那 SRKIndex 就要填 1。我刚开始时没注意,签名验签一直失败,查了两天才发现是索引对不上。

4.3.3 执行签名

运行 CST 签名命令:

java -jar $CST_PATH/cst.jar -c sign_u-boot.txt

如果一切顺利,你会看到 u-boot.signed 文件生成。这个文件就是可以烧录到 S32G 的安全镜像。

4.3.4 验签(模拟验证)

CST 也提供了验签功能,你可以先在 PC 上验证一下签名是否正确:

java -jar $CST_PATH/cst.jar -c verify_u-boot.txt

配置文件类似:

[Header]
Version = 4.0.0

[Verification]
Input = u-boot.signed
Certificate = keys/srk_0.der

如果输出 Verification PASS,说明签名没问题。如果失败,别慌,检查一下密钥是否匹配、镜像是否被修改过。

小技巧:我习惯在 CI/CD 流程里加一步验签检查。每次构建完固件,自动跑一遍 CST 验签,确保签名没被破坏。这样能避免「烧录到板子上才发现签名不对」的尴尬。

4.4 避坑指南

最后,分享几个我踩过的坑:

  • 密钥泄露:私钥一旦泄露,你的安全体系就形同虚设。我曾经在客户现场发现,他们把私钥直接放在 TFTP 服务器上,任何人都能下载。嗯,当场就帮他们改了。
  • 证书链不完整:S32G 的 BootROM 会验证完整的证书链。如果你只签了镜像,但没包含中间证书,启动会失败。记得用 cat 命令把证书链拼接好。
  • 哈希算法不匹配:CST 默认用 SHA-256,但有些旧版 BootROM 只支持 SHA-1。S32G 是支持 SHA-256 的,但如果你从其他平台迁移过来,记得检查一下。
  • 烧录位置错误:签名后的镜像要烧录到正确的偏移地址。S32G 的 BootROM 会从固定位置读取签名信息,烧错位置等于没签。

好了,这一章的内容就到这里。代码签名说白了就是「私钥签名、公钥验签」这八个字,但实际落地时,工具链的配置、密钥的管理、证书链的完整性,每一个环节都可能出问题。下一章咱们会讲 S32G 的硬件信任根,看看芯片是怎么从 ROM 代码开始一步步建立信任链的。