方案分析

安全启动的实现有两种方法,使用自签名安全证书以及使用经认证的第三方 (微软)证书,下表简单对比了这两种方法的优劣。

方案优势劣势
自建签名可控
无感知
兼容 Windows 安全启动
需自建密钥
需修改 BIOS (的证书数据库)
不能复用
第三方签名 (PreLoader.efi & shim.efi)方便
不修改 BIOS 设置
复用
安装简单,但操作对新手不友好
维护麻烦
GRUB 无法链式加载 EFI 二进制文件

第三方签名

这意味着需要使用微软的证书来给引导器签名,或者使用微软签名过的特殊引导器来引导 Bootloader 。

显然我们没法用微软的私钥签名,但是我们可以给我们的引导器套一个微软的壳。

Archwiki1 介绍了两个来自微软的小工具:Preloadershim 。但是,不管用哪个工具都有很大的局限性。

更新系统时繁琐的重签名工作、无法链式启动其他 EFI 工具的致命缺陷让它的功能变得非常的鸡肋。

个人认为这种方法仅适用于需要多设备使用以及不会对 Kernel 和 Bootloader 更新的场景。比如什么电脑都会插的急救盘、自构建的安装盘等。

自建签名

这种方法通用性更强。首次配置较为复杂,但后续的更新维护完全可以实现自动化。只要保留自建签名的私钥,也可以实现在多台设备上复用。

创建自签名证书

  1. 安装 efitools

    几乎所有的以下部分需要你安装的 efitools 包。

  2. 生成所有者标识 UUID

    uuidgen --random> GUID.txt
    
  3. 生成平台密钥 (PK)

    openssl req -newkey rsa:4096 -nodes -keyout PK.key -new -x509 -sha256 -days 3650 -subj "/CN=my Platform Key/" -out PK.crt
    openssl x509 -outform DER -in PK.crt -out PK.cer
    cert-to-efi-sig-list -g "$(< GUID.txt)" PK.crt PK.esl
    sign-efi-sig-list -g "$(< GUID.txt)" -k PK.key -c PK.crt PK PK.esl PK.auth
    
  4. 在“用户模式”下,签名一个空文件以允许删除平台密钥

    sign-efi-sig-list -g "$(< GUID.txt)" -c PK.crt -k PK.key PK /dev/null rm_PK.auth
    
  5. 生成密钥交换密钥 (KEK)

    openssl req -newkey rsa:4096 -nodes -keyout KEK.key -new -x509 -sha256 -days 3650 -subj "/CN=my Key Exchange Key/" -out KEK.crt
    openssl x509 -outform DER -in KEK.crt -out KEK.cer
    cert-to-efi-sig-list -g "$(< GUID.txt)" KEK.crt KEK.esl
    sign-efi-sig-list -g "$(< GUID.txt)" -k PK.key -c PK.crt KEK KEK.esl KEK.auth
    
  6. 生成签名数据库密钥 (db)

    openssl req -newkey rsa:4096 -nodes -keyout db.key -new -x509 -sha256 -days 3650 -subj "/CN=my Signature Database key/" -out db.crt
    openssl x509 -outform DER -in db.crt -out db.cer
    cert-to-efi-sig-list -g "$(< GUID.txt)" db.crt db.esl
    sign-efi-sig-list -g "$(< GUID.txt)" -k KEK.key -c KEK.crt db db.esl db.auth
    

这部分也可以使用辅助脚本2

以上步骤创建了三个自签证书并使用工具将其转化成 EFI 接受的格式。

如果可以自行签发证书或有自建 CA ,也可以使用自己的证书取代之。

操作完成后,一份安全启动需要的自签名证书就完成了。

在固件中注册证书

  1. 清除 BIOS 原有的密钥数据库,使其进入设置模式

这部分需要进入 BIOS 设置。

由于不同平台的操作方式不同,这步请各位自行探索。

  1. 注册密钥。这里有三种方法,各不相同,其中第三种方法最保险,可以通用

    • 使用 sbkeysync3 工具

    可能会卡在最后一步注册 PK 文件的时候报错失败。

    • 使用 BIOS 自带工具

      不是所有 BIOS 都带有这个功能,没有就是没有。自行探索,不再赘述。

    • 使用 KeyTool

      1. 使用自签证书签名 KeyTool 的执行程序,让它在设置模式下能够被引导

        使用 ROOT 权限执行

        sbsign --key db.key --cert db.crt --output ${ESP}/KeyTool-signed.efi /usr/share/efitools/efi/KeyTool.efi
        

        esp 处替换为自己的 BOOT 分区路径。

      2. 将之前制作好的 PK, KEK, db 文件复制到 BOOT 分区中

      3. 重启并引导签名后的 KeyTool

        可以提前添加到启动项也可以使用 Bootloader 的命令行,这里示范使用 GRUB 命令行

        search --no-floppy --set=root --file /EFI/KeyTool-signed.efi
        chainloader /EFI/KeyTool-signed.efi
        boot
        
      4. 根据图形化界面的指引导入各个数据库

签名已有 EFI 引导器和内核

使用 sbsign 签名内核和启动管理器。

用 ROOT 权限执行:

sbsign --key db.key --cert db.crt --output /boot/vmlinuz-linux /boot/vmlinuz-linux
sbsign --key db.key --cert db.crt --output esp/EFI/BOOT/BOOTX64.EFI esp/EFI/BOOT/BOOTX64.EFI

也可以顺便签名一下 EFI 文件夹里所有的 EFI 文件。

部署全自动签名 hook

ArchWiki4 介绍了两种方法,手动配置 pacman hook 和使用自动化程序。

hook 和 sbupdate 二选一即可,GRUB 用户推荐使用 hook 。

合并微软签名

可以将微软的公钥并入自己的数据库中,让自签证书兼容 Windows 和 Windows 启动盘的安全启动。

此处参考 ArchWiki5


完成之后重启电脑。进入系统后可通过 bootctl status 查看安全启动是否设置成功。

至此,安全启动就设置完成了。

参考资料

  1. https://wiki.archlinux.org/index.php/Unified_Extensible_Firmware_Interface/Secure_Boot

  2. https://wiki.archlinux.org/index.php/Unified_Extensible_Firmware_Interface/Secure_Boot#Helper_scripts

  3. https://wiki.archlinux.org/index.php/Unified_Extensible_Firmware_Interface/Secure_Boot#Using_sbkeysync

  4. https://wiki.archlinux.org/index.php/Unified_Extensible_Firmware_Interface/Secure_Boot#Signing_the_kernel_with_a_pacman_hook

  5. https://wiki.archlinux.org/index.php/Unified_Extensible_Firmware_Interface/Secure_Boot#Microsoft_Windows