3. UEFI安全启动

大家好,我是你们的讲师。今天咱们来聊聊UEFI安全启动。说实话,这个主题在可信启动链里属于「地基」级别的存在。你想想看,如果连固件层面的信任根都建立不起来,后面再谈内核完整性、文件系统校验,那都是空中楼阁。

我个人习惯把UEFI安全启动理解成「一把锁」——它锁住的是从固件到操作系统加载器之间的整个链条。谁有钥匙?只有被信任的签名者。嗯,这里要注意,这把锁不是防黑客的万能药,但它能挡住绝大多数试图在启动阶段植入恶意代码的攻击。

核心观点:UEFI安全启动的本质是「信任链传递」——从平台密钥(PK)开始,一级一级验证下去,直到操作系统内核被加载。任何一环断裂,启动就会中断。

UEFI固件基础:你每天都在用,但未必真懂

UEFI(统一可扩展固件接口)说白了就是BIOS的现代替代品。我记得2010年左右第一次接触UEFI时,还觉得这东西花里胡哨的,不如传统BIOS简洁。直到有一次在服务器上部署安全启动,传统BIOS压根不支持,我才意识到UEFI不是「升级版」,而是「重构版」。

UEFI固件有几个关键特征:

  • 运行在32位或64位模式——不再是实模式,能直接访问大内存
  • 支持网络协议栈——可以PXE启动、远程诊断
  • 模块化设计——驱动、协议、应用都是独立的EFI文件
  • 安全启动是内置功能——不是补丁,是架构的一部分

我在项目中遇到过最坑的事:某厂商的UEFI固件实现有bug,安全启动开启后,连自己的固件更新工具都跑不了。后来查了三个月,发现是签名验证的哈希算法实现有问题。所以啊,别以为UEFI固件都是标准化的,不同厂商的实现差异能让你怀疑人生。

安全启动流程:从按下电源键到操作系统加载

安全启动的流程,我习惯用「三段式」来记:

  1. 固件验证阶段:UEFI固件验证自己的驱动和EFI应用
  2. 启动管理器阶段:验证启动管理器(如GRUB)的签名
  3. 操作系统加载阶段:验证操作系统加载器(如shim.efi)的签名

为什么会这样设计?你想想看,如果每个阶段都信任上一阶段,那只要第一环被攻破,后面全完蛋。所以UEFI安全启动用的是「链式验证」——每一环都用自己的密钥去验证下一环的签名。

我曾经在调试一个嵌入式设备时,发现安全启动总是失败。折腾了两天,最后发现是GRUB的签名证书过期了。嗯,证书有效期这个坑,我踩过不止一次。

实战技巧:在部署安全启动前,先用sbverify工具手动验证一下EFI文件的签名。命令很简单:sbverify --cert mycert.pem --verbose myfile.efi。如果这一步都过不了,就别指望固件能自动验证通过。

密钥管理:KEK、db、dbx 三兄弟

UEFI安全启动的密钥体系,说白了就是三张表:

密钥类型 全称 作用 谁持有
PK 平台密钥 最高权限,控制KEK的更新 平台所有者
KEK 密钥交换密钥 授权更新db/dbx 操作系统厂商
db 允许签名数据库 存储被信任的签名/证书 固件存储
dbx 禁止签名数据库 存储被撤销的签名/证书 固件存储

我建议你把PK想象成「公司公章」,KEK是「部门章」,db是「白名单」,dbx是「黑名单」。公章可以重新刻(更新PK),但需要物理访问。部门章可以授权人事变动(更新KEK),但必须由公章持有者签字。

这里有个容易搞混的点:PK和KEK不是用来直接验证EFI文件的。真正验证EFI文件签名的是db里的证书。PK和KEK只是用来管理db/dbx的「管理密钥」。

警告:如果你丢失了PK,那就等于失去了对安全启动的控制权。有些主板可以通过清除CMOS来重置PK,但大多数服务器级硬件不行。我曾经见过一个团队把PK存在U盘里,结果U盘坏了……嗯,从那以后我要求所有PK必须离线存储至少三份。

签名与验证机制:不是简单的「签名-验证」

UEFI安全启动用的签名机制,其实是PKCS#7签名格式。说白了,就是把EFI文件的哈希值用私钥加密,然后把签名和证书一起打包成.signed文件。

验证过程是这样的:

  • 固件读取EFI文件,计算它的哈希值
  • 用db里的公钥解密签名,得到原始哈希值
  • 两个哈希值对比,一致则通过

但这里有个细节:UEFI支持多种哈希算法。SHA-1已经不被推荐了,SHA-256是主流。我在项目中遇到过一台老服务器,固件只支持SHA-1,但我们的签名工具默认用SHA-256。结果安全启动死活过不去,最后发现是哈希算法不匹配。

# 用sbsign工具签名EFI文件
sbsign --key mykey.key --cert mycert.pem --output myfile.signed myfile.efi

# 验证签名
sbverify --cert mycert.pem myfile.signed

# 查看签名信息
sbverify --verbose myfile.signed

你可能会问:为什么不直接用RSA加密整个文件?因为EFI文件可能很大(几十MB),RSA加密效率太低。所以先算哈希,再加密哈希,这是业界标准做法。

平台密钥(PK)的生成与部署:最关键的步骤

生成PK,我建议用OpenSSL。别用那些图形化工具,你永远不知道它们背后做了什么。

# 生成PK私钥(RSA-2048位)
openssl req -new -x509 -newkey rsa:2048 -keyout PK.key -out PK.pem \
  -days 3650 -subj "/CN=My Platform Key/"

# 转换为EFI签名列表格式
cert-to-efi-sig-list -g "$(uuidgen)" PK.pem PK.esl

# 签名PK列表(自签名)
sign-efi-sig-list -k PK.key -c PK.pem PK PK.esl PK.auth

部署PK时,我建议分两步走:

  1. 先进入固件设置,把安全启动设为「设置模式」(Setup Mode)
  2. 再用工具写入PK,比如用efi-updatevar或固件自带的工具

我曾经犯过一个低级错误:在部署PK之前,忘了把安全启动切换到设置模式。结果固件拒绝写入,还报了个莫名其妙的错误码。查了两小时文档才发现问题。

避坑指南:部署PK后,建议立即重启并验证安全启动状态。用mokutil --sb-state命令查看当前状态。如果显示SecureBoot enabled,说明部署成功。如果显示SecureBoot disabled,嗯……你可能需要重新检查密钥格式。

知识体系总览

下面这张图,是我自己整理的UEFI安全启动知识体系。你可以把它当成学习路线图:

UEFI安全启动知识体系 UEFI固件基础 模块化设计 / 协议栈 安全启动流程 链式验证 / 三段式 密钥管理 PK / KEK / db / dbx 签名与验证机制 PKCS#7 / 哈希对比 PK生成与部署 OpenSSL / 设置模式 信任链传递:固件 → 启动管理器 → 操作系统 每个环节都依赖上一环节的签名验证,形成完整的信任链 PK是信任根,KEK是授权链,db/dbx是执行策略

这张图把UEFI安全启动的核心知识点串起来了。你从固件基础开始学,然后理解安全启动流程,接着掌握密钥管理,最后落地到签名验证和PK部署。每一步都环环相扣。

好了,这一章的内容就到这里。记住,UEFI安全启动不是「配置完就完事」的东西。你需要定期检查dbx更新(微软会发布新的黑名单),定期轮换PK(虽然很少需要),还要确保固件本身没有漏洞。安全是一个持续的过程,不是一锤子买卖。

可可精品资料库