第4章 UEFI安全启动实战:在物理机上启用Secure Boot、自定义签名、常见问题排查

好,咱们进入实战环节。前面几章讲了理论,这一章我带你亲手在物理机上折腾一遍 Secure Boot。说实话,光看文档不动手,你永远记不住那些坑在哪。我自己第一次配的时候,差点把一台测试机的 BIOS 搞废掉——嗯,后来学乖了,每次改之前都先备份固件设置。

4.1 物理机启用Secure Boot的标准流程

先明确一点:不是所有主板都叫 UEFI。你得先确认你的机器用的是 UEFI 模式,而不是传统的 Legacy BIOS。怎么查?开机进 Setup 界面,或者用 efibootmgr 看一眼。

核心前提: 操作系统必须安装在 GPT 分区表上,且 ESP(EFI System Partition)分区必须存在。没有 ESP?Secure Boot 想都别想。

步骤一:进入固件设置

开机时按 Del / F2 / F12(各家厂商不一样)。找到 Secure Boot 选项,通常在「Security」或「Boot」菜单下。我见过有些主板把它藏在「Authentication」子菜单里,找得我直冒汗。

步骤二:切换为 Standard 或 Custom 模式

大部分主板默认是 Standard(标准)模式,使用厂商预置的证书。如果你想用自己的签名,必须切换到 Custom(自定义)模式。切换后,系统会提示你清除已有的 Secure Boot 数据库。

注意: 清除数据库后,如果你没有提前准备好自己的证书,系统将无法启动任何未签名的操作系统。我曾经手滑清空过,结果只能拿 U 盘进救援模式重新导入证书。

步骤三:启用 Secure Boot

把 Secure Boot 从 Disabled 改为 Enabled。保存退出,重启。如果一切正常,你会看到系统顺利进入桌面。如果卡在开机 logo 处,或者出现「Invalid signature detected」的提示——别慌,后面有排查方法。

4.2 自定义签名:从生成密钥到导入数据库

为什么要自定义签名?说白了,就是你想完全掌控自己的机器。比如你编译了一个自定义内核,或者用了第三方的驱动模块,厂商的证书不认它。这时候,你得自己当 CA(证书颁发机构)。

4.2.1 生成你自己的密钥对

我习惯用 OpenSSL 来生成。下面这套命令,我在不下十台机器上跑过,稳得很。

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

# 生成 Key Exchange Key (KEK)
openssl req -new -x509 -newkey rsa:2048 -subj "/CN=My KEK/" \
    -keyout KEK.key -out KEK.crt -days 3650 -nodes

# 生成 Signature Database (db) 证书
openssl req -new -x509 -newkey rsa:2048 -subj "/CN=My Signature Database/" \
    -keyout db.key -out db.crt -days 3650 -nodes

这里有个细节:PK 是最高权限,KEK 次之,db 最底层。你想想看,如果 PK 丢了,整台机器就废了——因为只有 PK 才能更新 PK 自己。所以,私钥一定要备份到离线存储里。

4.2.2 将证书转换为 EFI Signature List (ESL) 格式

UEFI 固件不认识 .crt 文件,它只认 ESL 格式。用 cert-to-efi-sig-list 工具转换:

cert-to-efi-sig-list -g "$(uuidgen)" PK.crt PK.esl
cert-to-efi-sig-list -g "$(uuidgen)" KEK.crt KEK.esl
cert-to-efi-sig-list -g "$(uuidgen)" db.crt db.esl

那个 -g 参数后面跟的 UUID,是用来标识这个签名列表的。你可以随便生成一个,但别重复。

4.2.3 签名并导入数据库

导入 PK 之前,得先用旧的 PK 签名新的 PK 文件。但如果你是从零开始(清空状态),这一步可以跳过。直接使用 efi-updatevar 或固件自带的工具导入。

# 使用 efi-updatevar 导入(需要在内核中启用 CONFIG_EFI_VARS)
efi-updatevar -f PK.esl PK
efi-updatevar -f KEK.esl KEK
efi-updatevar -f db.esl db

小技巧: 如果你不想装 efi-updatevar,也可以把 ESL 文件放到 FAT32 格式的 U 盘里,然后在固件设置界面手动导入。大多数专业级主板(比如 Supermicro、ASUS 的 WS 系列)都支持这个功能。

4.3 签名你的内核和引导加载器

数据库导入完了,接下来要签名实际要启动的文件。以 GRUB 和 Linux 内核为例:

# 签名 GRUB
sbsign --key db.key --cert db.crt --output /boot/efi/EFI/BOOT/grubx64.efi.signed \
    /boot/efi/EFI/BOOT/grubx64.efi

# 签名内核
sbsign --key db.key --cert db.crt --output /boot/vmlinuz-linux.signed \
    /boot/vmlinuz-linux

签名后,把原始文件替换成签名版本。注意,有些发行版(比如 Ubuntu)会自动处理签名,你只需要装好 shim-signed 包就行。但如果你用的是 Arch 或 Gentoo,嗯,自己动手吧。

4.4 常见问题排查

这部分是我最想跟你聊的。因为理论再漂亮,实战中总会出幺蛾子。我列几个我踩过的坑:

现象 可能原因 解决办法
开机显示「Invalid signature」 内核或 GRUB 未签名,或签名证书不在 db 中 检查签名状态:sbverify --cert db.crt /boot/vmlinuz
导入 PK 后直接变砖 PK 证书与 KEK/db 不匹配,或 PK 私钥丢失 用编程器刷回原始固件,或者短接 CMOS 跳线(部分主板支持)
Windows 能启动,Linux 不行 Linux 引导加载器使用了未签名的 shim 安装 shim-signed,并确保 shim 的证书在 db 中
更新固件后 Secure Boot 被重置 固件更新清空了自定义证书 更新前导出证书,更新后重新导入

4.4.1 排查工具

我常用的三板斧:

  • sbverify:检查一个 EFI 文件是否被签名,以及用的是哪张证书。
  • efi-readvar:查看当前固件中的 PK、KEK、db 内容。
  • mokutil:管理 Machine Owner Key(MOK),适合在 shim 环境下调试。

举个例子,有一次我同事说他的内核签名了但还是启动不了。我让他跑了一下 sbverify --cert db.crt vmlinuz,结果发现签名用的证书跟 db 里存的不一样——他生成密钥时把 CN 写错了。这种问题,光看日志根本看不出来。

4.5 知识体系总览

下面这张图,把 Secure Boot 的整个信任链画清楚了。你对照着看,应该能理解为什么 PK 丢了就全完了。

UEFI Secure Boot 信任链与签名流程 UEFI Secure Boot 信任链 Platform Key (PK) Key Exchange Key (KEK) Signature Database (db) 签名后的 .efi / 内核 信任传递规则 PK 签名 KEK KEK 签名 db db 签名启动文件 环环相扣,缺一不可 ⚠ 私钥丢失 = 变砖 务必离线备份 PK 私钥

4.6 避坑指南

最后,我把自己这些年攒下来的教训列成清单,你照着做,能省不少时间:

  • 备份原始固件设置:在改 Secure Boot 之前,进固件界面把所有设置截图或拍照。有些主板支持导出配置文件,一定要用。
  • 先测试,再量产:别在生产服务器上直接搞。找一台同型号的测试机,先跑一遍完整流程。我见过有人在生产环境导入 PK 后直接黑屏,最后只能返厂。
  • 签名 shim 而不是 GRUB:如果你用 shim,只需要签名 shim 本身。shim 会验证 GRUB,GRUB 再验证内核。这样你更新 GRUB 时不用重新签名。
  • 注意固件更新:厂商推送固件更新时,有时会重置 Secure Boot 数据库。更新前导出证书,更新后重新导入。我吃过这个亏,那次差点重装系统。

我的个人习惯: 每次改完 Secure Boot 设置,我都会在机器上贴一张标签,写上「Secure Boot: Custom / PK 指纹: xxxx」。这样下次别人接手时,不会一头雾水。

好了,这一章的内容就到这。你照着上面的步骤走一遍,应该能在物理机上跑通 Secure Boot。如果卡住了,回头看看排查表,八成能找到答案。