第4章:代码签名与验证——数字签名原理与S32G实战
好,咱们进入安全启动最核心的一环——代码签名与验证。说白了,就是给你的固件贴上一张「防伪标签」,让芯片只认你签过名的代码。我当年第一次接触这个时,觉得不就是加个签名嘛,后来踩了不少坑才发现,这里面的门道真不少。
4.1 数字签名原理:RSA与ECDSA
数字签名是什么?你可以想象成你写了一份合同,然后在上面盖了个私章。别人拿到合同,用你的公章印泥一比对,就知道是不是你本人签的。在嵌入式世界里,这个「私章」就是私钥,「公章」就是公钥。
目前主流的两大算法:RSA 和 ECDSA。我个人的习惯是,如果芯片资源够用,优先选 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 的核心工作流程是这样的:
- 准备镜像文件:比如你的 u-boot.bin 或 Linux 内核镜像
- 计算哈希:CST 会对镜像做 SHA-256 哈希
- 生成签名:用你的私钥对哈希值签名
- 打包成安全镜像:把原始镜像 + 签名 + 证书链打包成一个 .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.pem到srk_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 代码开始一步步建立信任链的。