4. 安全启动的签名机制:数字签名原理、Authenticode签名、EFI签名工具
安全启动的核心,说白了就是「谁签的名,谁负责」。
UEFI固件在加载一个EFI程序之前,会先检查它的数字签名。签名合法,才放行。签名不对,直接拒掉。这个机制听起来简单,但实际落地时坑不少。我这些年排查过的安全启动故障,至少有一半都跟签名有关——要么是签名工具用错了,要么是证书链没配全。
4.1 数字签名原理:你不需要懂全部数学,但得懂流程
数字签名不是加密,它解决的是「谁写的」和「被改过没」这两个问题。
流程分三步:
- 哈希摘要:对原始文件(比如bootmgfw.efi)做一次哈希运算,得到一个固定长度的摘要。
- 私钥加密:用签名者的私钥对这个摘要加密,生成签名数据。
- 打包嵌入:把签名数据、签名者证书、可能的中间CA证书一起塞进文件里。
验证时,固件用公钥解密签名,拿到原始摘要,再对文件重新算一次哈希。两个摘要一致,说明文件没被篡改,签名者身份也确认了。
关键点:公钥必须提前被固件信任。安全启动的信任链,就是从固件内置的「允许签名数据库」(DB)开始的。
我在项目中遇到过一个问题:明明签名验证通过了,但启动还是失败。后来发现是固件里DB证书过期了。嗯,证书有效期也是个容易忽略的点。
4.2 Authenticode签名:PE文件的专属签名格式
Windows下的EFI文件,本质上是PE格式。微软为PE文件定制了一套签名规范,叫Authenticode。
Authenticode签名不是简单地把签名数据塞进文件尾部,它有一套严格的嵌入规则:
- 签名数据放在PE文件的
Certificate Table目录指向的位置。 - 签名计算时,需要跳过PE头里的
CheckSum字段和Certificate Table目录本身。 - 支持嵌套签名(多个签名同时存在),但安全启动只认第一个有效的。
| 签名类型 | 适用场景 | 验证方式 |
|---|---|---|
| Embedded签名 | 单个EFI文件 | 固件直接解析PE头,提取签名 |
| Catalog签名 | 驱动包、多个文件 | 通过安全编录(.cat)文件间接验证 |
我的经验:如果你自己编译了一个EFI应用,想用安全启动加载它,最简单的方式就是用Embedded签名。Catalog签名适合批量分发场景,但配置起来麻烦不少。
4.3 EFI签名工具:SignTool vs sbsign
签名工具分两大阵营:Windows生态的SignTool,和Linux/开源生态的sbsign。两个我都用过,各有各的脾气。
4.3.1 SignTool(Windows SDK自带)
SignTool是微软官方工具,支持Authenticode签名。基本用法:
signtool sign /fd SHA256 /a /s MY /n "MyCompany" /t http://timestamp.digicert.com bootmgfw.efi
参数说明:
/fd SHA256:指定哈希算法。安全启动强制要求SHA256,别用SHA1。/a:自动选择最佳签名证书。/s MY:从当前用户的MY证书存储区找证书。/n:按证书主题名称筛选。/t:添加时间戳。这个很重要——证书过期后,时间戳能证明签名是在有效期内做的。
避坑指南:我曾经因为忘了加/fd SHA256,默认用了SHA1,结果固件直接报「签名算法不支持」。SHA1在安全启动里已经被废弃了,千万别用。
4.3.2 sbsign(Linux下的efitools)
如果你在Linux环境下开发UEFI应用,sbsign是首选。它来自efitools包,用法更简洁:
sbsign --key mykey.key --cert mycert.pem --output signed.efi unsigned.efi
参数说明:
--key:私钥文件(PEM格式)。--cert:签名证书(PEM格式)。--output:输出签名后的文件。
sbsign生成的签名格式与Authenticode兼容,固件能直接识别。我习惯在CI/CD流水线里用sbsign,因为它不需要图形界面,脚本化非常方便。
| 特性 | SignTool | sbsign |
|---|---|---|
| 平台 | Windows | Linux |
| 签名格式 | Authenticode | Authenticode兼容 |
| 时间戳支持 | 原生支持 | 需额外工具 |
| 证书管理 | Windows证书存储 | 文件系统PEM |
| 自动化友好度 | 中等 | 高 |
4.4 签名验证流程:固件到底怎么查?
固件验证签名的顺序,我画了张图帮你理解:
流程看起来不复杂,但实际调试时,固件不会告诉你「证书链断了」还是「哈希不匹配」。它只会抛一个笼统的错误码,比如SECURITY_VIOLATION。这时候就得靠经验去猜了。
我的调试习惯:先用sbverify(efitools里的另一个工具)在操作系统里验证签名是否合法。如果sbverify报错,那固件肯定也过不了。省得反复重启机器。
4.5 常见签名故障与排查思路
- 签名算法不匹配:固件只认SHA256,你用了SHA1。用SignTool时务必加
/fd SHA256。 - 证书链不完整:签名证书的签发者(CA)不在固件的DB里。要么把CA证书导入DB,要么用自签名证书并直接导入DB。
- 证书过期:如果签名时没加时间戳,证书过期后固件会拒掉。加时间戳能解决这个问题。
- 文件被二次签名:有些工具会在已有签名上再盖一个签名,导致固件解析混乱。签名前先检查文件是否已有签名。
我曾经踩过的坑:有一次客户反馈说「同一个EFI文件,在A机器上能启动,在B机器上就报安全启动错误」。排查了半天,发现B机器的固件DB里缺少中间CA证书。所以,证书链的完整性不光取决于签名方,还取决于目标机器的固件配置。
签名机制是安全启动的基石。理解它,你才能知道固件在「想什么」。下一层,我们会深入DB/DBX的配置,看看怎么管理这些信任证书。