UEFI/安全启动

来自 Arch Linux 中文维基

Secure BootUEFI标准中的一项安全功能,旨在为pre-boot process添加一层保护;通过维护被授权或禁止的在启动时运行的经过加密签名的二进制文件列表,它有助于使得核心引导组件(引导管理器、内核、initramfs)不被篡改。

因此,它可以被视为对个人使用环境的securing所做努力的延续或补充,减少其他软件安全解决方案(如系统加密英语dm-crypt/Encrypting an entire system)无法轻易覆盖英语Dm-crypt/Encrypting an entire system#Encrypted boot partition (GRUB)的攻击面,同时完全不同且不依赖于它们。安全启动只是作为当前安全实践的一个组成部分独立存在,具有自己的一套优缺点

注意: 为了更深入地了解Linux中的安全启动。见Rodsbooks' Secure Bootother online resources。文章重点介绍了如何在 Arch Linux 中设置安全启动。

检查安全启动状态[编辑 | 编辑源代码]

在启动操作系统之前[编辑 | 编辑源代码]

此时,必须查看固件设置。如果机器已启动并正在运行,则在大多数情况下必须重新启动。

您可以在引导过程中通过按特殊键来访问固件配置。使用的特殊键取决于固件。它通常是EscF2Del或者Fn键中的一个。有时在启动过程中特殊键的名称会显示一小段时间。主板说明书往往有关于此的记载。如果你想要按下特殊键,请在启动机器后立即按下该键,甚至是在屏幕实际显示任何内容之前。 进入固件设置后,请注意不要擅自更改任何设置。 通常在每个设置的底部都会有导航说明和设置的简短帮助。设置本身可能由几个页面组成,您必须导航到正确的位置。有一些安全启动设置可能简单地表示为安全启动选项,可以将其设置为打开或关闭 .

在启动操作系统之后[编辑 | 编辑源代码]

使用systemd检查系统上的安全启动状态的一种简单方法是使用systemd-boot

注意: 没有必要使用systemd-boot作为你的引导管理器来运行这个命令,它更类似于其他*ctl systemd实用程序(localectl、timedatectl……)并且不会影响你的配置。
$ bootctl status
System:
     Firmware: UEFI 2.70 (American Megatrends 5.15)
  Secure Boot: enabled
   Setup Mode: user
 Boot into FW: supported
...

在这里,我们看到安全启动已启用并强制执行。Secure Boot的其他值包括disabled,Setup Mode的其他值包括setup[1]

检查机器是否使用安全启动的另一种方法是使用以下命令:

$ od --address-radix=n --format=u1 /sys/firmware/efi/efivars/SecureBoot-*

如果启用了安全启动,此命令将返回1作为五个列表中的最后一个整数,例如:

6  0  0  0  1

但是请注意,如果使用了功能缺失的引导加载程序,内核可能不知道安全引导(即使它在固件中启用)。 这可以通过在系统启动后不久检查内核消息来验证:

# dmesg | grep -i secure
[    0.013442] Secure boot disabled
[    0.013442] Secure boot could not be determined

能启用的内核会打印Secure boot enabled

引导安装介质[编辑 | 编辑源代码]

注意: 官方安装镜像不支持安全启动(FS#53864)。 要成功启动安装介质,您需要disable Secure Boot

安全启动支持最初是在archlinux-2013.07.01-dual.iso中添加的,后来在archlinux-2016.06.01-dual.iso中被删除。 当时 prebootloader被替换为efitools,即使后者使用未签名的 EFI 二进制文件。 从那以后,官方安装介质中就不再支持 Secure Boot。

Archboot镜像提供了一种在安装介质中使用安全启动的方式。

禁用安全启动[编辑 | 编辑源代码]

可以通过 UEFI 固件接口禁用安全启动功能。如何访问固件配置请访问#在启动操作系统之前.

如果使用热键不起作用并且您可以启动 Windows,您可以通过以下方式强制重新启动到固件配置(对于 Windows 10):设置 > 更新和安全 > 恢复 > 高级启动(立即重新启动)> 故障排除 > 高级选项 > UEFI 固件设置 > 重启

请注意,某些主板(Packard Bell 笔记本电脑就是这种情况)只有在您设置了管理员密码(之后可以删除)时才允许禁用安全启动。 另请参阅 Rod Smith 的禁用安全启动

重新制作安装镜像[编辑 | 编辑源代码]

Tango-view-fullscreen.png这篇文章的某些内容需要扩充。Tango-view-fullscreen.png

原因: 增加明确的说明。 (在 Talk:UEFI/安全启动 中讨论)

一个人或许想要像上文所说的那样重新制作安装镜像英语Remastering the Install ISO。举个例子,#PreLoader中的已签名EFI程序PreLoader.efi#PreLoader中的HashTool.efi可在此采用。另一个选项就是可以借用其他支持安全启动的GNU+Linux发行版中的BOOTx64.EFI(shim)以及grubx64.efi并根据需要修改GRUB配置。在这种情况下,上述发行版安装介质中的安全启动认证链应以 grubx64.efi为终点(Ubuntu的例子),这样GRUB就会从archiso启动未签名的内核和initramfs。

注意,到目前为止,本文假定你可以访问机器的ESP。但是当安装一台以前从未有操作系统的机器时,就没有ESP了。您应该阅读其他文章,例如Unified Extensible Firmware Interface#Create UEFI bootable USB from ISO,了解如何处理这种情况。

实施安全启动[编辑 | 编辑源代码]

为了确保理想的安全启动设置,有以下几个特定条件:

  1. UEFI基本被认为是可信的(尽管有一些众所周知的批评和漏洞[2]),且必须被强密码保护
  2. 不使用默认的制造商或第三方密钥,因为它们被证明会大大削弱安全启动的安全模型[3]
  3. UEFI直接读取带有EFISTUB英语EFISTUB的内核镜像(而不是引导加载程序),包含微码(如果适用)以及 initramfs。以便在整个启动过程维持安全启动建立的信任链并减少攻击面。
  4. 使用完整驱动器加密英语dm-crypt/Encrypting an entire system,这样在创建内核镜像和签名的过程中所涉及的工具与文件就无法被具有机器物理访问权限的人访问并篡改。
  5. 可以使用TPM(可信平台模块)英语Trusted Platform Module获得进一步的提升,但因为相关工具与支持使这很难实施。

#使用自己的密钥中描述了一个简单且完全自力更生的设置,而#使用已签名的引导加载程序使用了第三方签名的中间工具。

使用自己的密钥[编辑 | 编辑源代码]

警告: 使用自己的密钥替换平台的密钥会瘫痪某些机器(比如笔记本)上的硬件,甚至无法进入UEFI/BIOS设置恢复。这是因为某些硬件的固件(OpROMs)在启动时使用微软的密钥进行签名。

实现安全启动需要下列密钥:

平台密钥 Platform Key (PK):最高级的密钥。
密钥交换密钥 Key Exchange Key (KEK):用于签署签名数据库和禁止的签名数据库更新的密钥。
签名数据库 Signature Database (db):包含密钥和/或被允许的EFI二进制文件的散列值。
禁止的签名数据库 Forbidden Signatures Database (dbx):包含密钥和/或被禁止的EFI二进制文件的散列值。

详细解释请阅读所有UEFI密钥的含义

为了使用安全启动,你至少需要PKKEKdb密钥。 虽然你可以添加多个KEK,db和dbx证书,但只允许一个平台密钥(PK)。

一旦安全启动处于“用户模式”,密钥只能通过用更高级别的密钥签署(使用sign-efi-sig-list)来更新。平台密钥可以自己签名。

安装 efitools[编辑 | 编辑源代码]

执行接下来的大部分内容需求安装efitools

备份当前变量[编辑 | 编辑源代码]

在创建新的密钥和修改EFI变量之前,建议备份当前的变量,以便在发生错误时可以恢复。

运行以下命令来备份所有四个主要的安全启动变量。

$ efi-readvar -v PK -o old_PK.esl
$ efi-readvar -v KEK -o old_KEK.esl
$ efi-readvar -v db -o old_db.esl
$ efi-readvar -v dbx -o old_dbx.esl

如果你在新电脑或主板上执行这些命令,你提取的变量很可能是微软提供的。

创建密钥[编辑 | 编辑源代码]

手动流程[编辑 | 编辑源代码]

要生成密钥,请执行以下步骤。

你将需要多种格式的私钥和证书。

.key
PEM格式的私钥,用于签名EFI二进制和EFI签名列表。
.crt
PEM格式的证书,用于sbsign(1)sbvarsign(1)sign-efi-sig-list(1)
.cer
DER格式的证书,用于固件。
.esl
EFI签名列表的证书,用于sbvarsign(1)efi-updatevar(1)KeyTool与固件。
.auth
EFI签名列表中的证书,带有认证头(即签名的证书更新文件),用于efi-updatevar(1)sbkeysyncKeyTool与固件。

创建一个GUID用于自我识别:

$ uuidgen --random > GUID.txt

平台密钥:

$ 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

签署一个空白文件用来在“用户模式”中移除平台密钥:

$ sign-efi-sig-list -g "$(< GUID.txt)" -c PK.crt -k PK.key PK /dev/null rm_PK.auth

密钥交换密钥:

$ 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

签名数据库密钥:

$ 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
辅助脚本[编辑 | 编辑源代码]

关于这个主题的参考页[4]的作者提供了一个辅助/快捷脚本(需要python)。一个轻度修改的版本也被打包为sbkeysAUR

为了使用它,请在安全位置创建一个文件夹(比如/etc/efi-keys/,如果你接下来计划统一内核镜像的创建与签名自动化过程)并且运行:

# mkdir /etc/efi-keys
# cd !$
# curl -L -O https://www.rodsbooks.com/efi-bootloaders/mkkeys.sh
# chmod +x mkkeys.sh
# ./mkkeys.sh
<输入一个通用名,以嵌入至密钥中,比如 "Secure Boot">

这将产生所需的不同格式的文件。

更新密钥[编辑 | 编辑源代码]

一旦安全启动进入“用户模式”,任何对KEK、db以及dbx的改变都需要更高级的密钥签署。

比如,如果你想要更新你的db密钥:

  1. 创建新的密钥
  2. 转换其为EFI签名列表,
  3. 签署EFI签名列表,
  4. 登记已签署的证书更新文件。
$ cert-to-efi-sig-list -g "$(< GUID.txt)" new_db.crt new_db.esl
$ sign-efi-sig-list -g "$(< GUID.txt)" -k KEK.key -c KEK.crt db new_db.esl new_db.auth

如果你想添加另一个密钥至签名数据库中且不替换你的db密钥,你需要使用选项-a(请看sign-efi-sig-list(1)):

$ sign-efi-sig-list -a -g "$(< GUID.txt)" -k KEK.key -c KEK.crt db new_db.esl new_db.auth

new_db.auth被创建,登记它

签署EFI二进制文件[编辑 | 编辑源代码]

安全启动激活时(比如在“用户模式”中),只有已被签署的EFI二进制文件(比如应用程序、驱动统一的内核镜像英语unified kernel image)可以被启动。

使用sbsigntools手动操作[编辑 | 编辑源代码]

安装sbsigntools来使用sbsign(1)签署EFI二进制文件。

提示:
  • 为了检查一个二进制文件是否被签署并列出它的签名,使用sbverify --list /path/to/binary
  • rEFInd启动管理器的refind-install脚本可以签署rEFInd的EFI二进制文件并把它们与db证书一起拷贝到ESP中。 请参考rEFInd#使用你自己的密钥
注意: 如果不带--output使用sbsign的话,生成文件将会是filename.signed。详情请看sbsign(1)

使用sbsign来签署你的内核与启动管理器,比如:

# 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
警告: 只对内核进行签名并不能保护initramfs不被篡改。查看统一的内核镜像英语Unified kernel image以了解如何生成一个组合镜像并通过sbsign手动签署。
使用pacman钩子签署内核[编辑 | 编辑源代码]

你也可使用mkinitcpio的pacman钩子在安装与更新时签署一个内核。

复制/usr/share/libalpm/hooks/90-mkinitcpio-install.hook/etc/pacman.d/hooks/90-mkinitcpio-install.hook以及复制 /usr/share/libalpm/scripts/mkinitcpio-install/usr/local/share/libalpm/scripts/mkinitcpio-install

/etc/pacman.d/hooks/90-mkinitcpio-install.hook里,将:

Exec = /usr/share/libalpm/scripts/mkinitcpio-install

替换为:

Exec = /usr/local/share/libalpm/scripts/mkinitcpio-install

/usr/local/share/libalpm/scripts/mkinitcpio-install里,将:

install -Dm644 "${line}" "/boot/vmlinuz-${pkgbase}"

替换为:

sbsign --key /path/to/db.key --cert /path/to/db.crt --output "/boot/vmlinuz-${pkgbase}" "${line}"

如果你正在使用systemd-boot,有一个专用的pacman钩子会半自动的做这个任务。

用sbupdate全自动生成和签署统一的内核[编辑 | 编辑源代码]

sbupdate是一个专门为在Arch Linux上自动生成和签署统一的内核镜像而制作的工具。它通过Pacman#钩子处理内核的安装、移除和更新。

安装sbupdate-gitAUR并按照项目主页上的说明进行配置。[5]

提示:如果正在使用systemd-boot,设置OUT_DIR="EFI/Linux"可以让你的已签署的内核镜像直接被识别而不需要必须的配置。请看systemd-boot(7) § FILESSystemd-boot#增加启动选项

一旦完成配置,只需要以root运行sbupdate以完成首次镜像生成。

注意: sbupdate的输出经常包含错误比如warning: data remaining[26413568 vs 26423180]: gaps between PE/COFF sections?。那些是无害的且可以被安全的无视掉。[6]

将固件置于 "设置模式 "下[编辑 | 编辑源代码]

当平台密钥被删除时,安全启动处于设置模式。要将固件置于设置模式,请进入固件设置工具,并找到删除或清除证书的选项。#在启动操作系统之前中介绍了如何进入设置工具。

在固件中登记密钥[编辑 | 编辑源代码]

使用下列某种方式登记'dbKEKPK证书。

提示:如果dbx(禁止的签名数据库)是空白的,在下面的说明中,它可以被安全地排除在外。
警告: 登记平台密钥会使安全启动进入“用户模式”,也就意味着会离开“设置模式”,所以登记应该完成在所有流程的最后。
使用sbkeysync[编辑 | 编辑源代码]

安装sbsigntools。按照以下目录结构创建文件夹/etc/secureboot/keys -

/etc/secureboot/keys
├── db
├── dbx
├── KEK
└── PK

比如,这样用:

# mkdir -p /etc/secureboot/keys/{db,dbx,KEK,PK}

接下来复制之前生成的每一个.auth文件到它们所对应的位置(比如,把PK.auth放进/etc/secureboot/keys/PK,以此类推)。

检查sbkeysync会对你系统的UEFI密钥库做什么改变。

# sbkeysync --pk --dry-run --verbose

最后,使用sbkeysync来登记你的密钥。

# sbkeysync --verbose
# sbkeysync --verbose --pk


提示:
  • 如果sbkeysync返回了写错误,那么在执行sbkeysync前先运行chattr -i /sys/firmware/efi/efivars/{PK,KEK,db}*来暂时改变文件属性,使EFI密钥可以写入efivars目录中。详情见chattr(1)
  • 如果为PK.auth得到了一个permission denied错误,你可以使用命令efi-updatevar -f /etc/secureboot/keys/PK/PK.auth PK登记密钥。
  • 如果都失败了,这可能是因为固件对BIOS设置上锁了。对于戴尔和联想的系统,你或许需要重置你的BIOS密码:Sysfs固件认证文档

对于联想系统:

# cat > /sys/class/firmware-attributes/thinklmi/authentication/Admin/current_password 
my-super-secret-password
^D

对于戴尔系统:

# cat > /sys/class/firmware-attributes/dell-wmi-sysman/authentication/Admin/current_password 
my-super-secret-password
^D
然后再次尝试。

下一次启动时,UEFI应该会切回用户模式并且执行安全启动策略。

使用固件设置工具[编辑 | 编辑源代码]

复制所有的*.cer*.esl*.auth文件(除了noPK.auth)到一个FAT格式的文件系统(你可以使用EFI 系统分区)。

警告: 不要noPK.auth文件复制到你个人电脑的EFI 系统分区(ESP)!如果你这样做了,假如有人偷了你的电脑,这个人可以删除UEFI安全启动固件中的个人平台密钥,再一次打开你电脑上的 "设置模式",然后用他们自己的平台密钥替换你的安全启动密钥(PK、KEK、db、dbx),这将完全违背UEFI安全启动的目的。 应当只有你自己可以替换平台密钥,只有你自己可以访问noPK.auth文件。因此把noPK.auth文件保存在一个只有你自己能访问的安全的地方。这些地方对于noPK.auth文件来说都可以是安全的:
  • 当你使用KeyTool时,可以使用带有EFI 系统分区的外部U盘。要注意,KeyTool只能读取未加密存储中的文件。
  • 当你使用sbkeysync时,运用静态数据加密

个人电脑的EFI 系统分区一定要按照UEFI的规范进行加密,否则就可以在另一台电脑上被挂载且被读取(如果你的电脑被偷了,硬盘可以被取出来挂载到另一台电脑上)。复制noPK.auth到你电脑的ESP后再删除也是不可取的,因为在FAT32格式的EFI 系统分区上被删除的文件可以通过像是PhotoRec这样的工具被还原

启动固件设置工具,登记dbKEKPK证书。 固件会有各种不同的界面,请看使用固件的设置工具更换密钥,以了解如何登记密钥。

如果使用的工具支持,最好使用.auth.esl而不是.cer

使用KeyTool[编辑 | 编辑源代码]

KeyTool.efi包含在efitools包中,复制它到ESP。为了能在登记密钥后使用它,请用sbsign对它签名。

# sbsign --key db.key --cert db.crt --output esp/KeyTool-signed.efi /usr/share/efitools/efi/KeyTool.efi

使用固件设置工具,引导加载程序或者UEFI Shell启动KeyTool-signed.efi,然后登记密钥。

参见使用KeyTool更换密钥了解KeyTool的菜单选项。

与其他操作系统进行双重启动[编辑 | 编辑源代码]

微软 Windows[编辑 | 编辑源代码]

Tango-view-fullscreen.png这篇文章的某些内容需要扩充。Tango-view-fullscreen.png

原因: 是否有可能通过用自定义密钥签署其启动程序来启动Windows? (在 Talk:UEFI/安全启动#使用自定义密钥启动Windows 中讨论)

通常情况下,启用安全引导下,仅使用私人自定义密钥签名微软引导程序(EFI/Microsoft/Boot/bootmgfw.efi)而没有在UEFI安全启动变量中登记“Microsoft Windows Production PCA 2011”不可能启动Windows:

  • 如果bootmgfw.efi同时包含来自“Microsoft Windows Production PCA 2011”你自己的安全启动数据库密钥(也就是说有两个签名),那么UEFI固件实现(比如INSYDE Corp. 4096.01 (UEFI Version 2.31, Version F.70, Release Date: 07/18/2016, BIOS Revision 15.112, Firmware Revision: 29.66))将不会启动bootmgfw.efi,并且会抛出一个安全违规错误(Selected boot image did not authenticate. Press ENTER to continue.)。像是这样的UEFI固件实现可能只能读取第一个签名而忽略第二个。只有第二个签名的证书被注册到UEFI安全启动变量中,所以安全启动验证失败。
  • 如果从bootmgfw.efi中剥离/移除“Microsoft Windows Production PCA 2011”证书,而只在文件中添加了你自己的安全启动数据库密钥的签名,虽然UEFI会启动该文件但Windows将启动一个恢复/修复环境。Windows会抱怨说:Windows的安装被破坏了(因为bootmgfw.efi中缺失了“Microsoft Windows Production PCA 2011”证书)。

所以为了实现双系统

  • 你必须把bootmgfw.efi的散列值添加到DB变量中允许的散列值列表中,而且每次Windows更新改变bootmgfw.efi时,你都必须更新DB变量。这非常繁琐,容易出错,而且不被微软支持,例如,Bitlocker在这种设置下将无法正常工作(Bitlocker每次解密Windows分区时都会要求你提供恢复密码)。
  • 或者你必须将微软的证书添加到UEFI安全启动的变量中,微软有两个DB证书和一个KEK证书

使用微软的GUID(77fa9abd-0359-4d32-bd60-28f4e78f784b),根据微软的多个DER格式的DB证书创建多个EFI签名列表。然后为了方便,把它们合并进一个文件中:

$ sbsiglist --owner 77fa9abd-0359-4d32-bd60-28f4e78f784b --type x509 --output MS_Win_db.esl MicWinProPCA2011_2011-10-19.crt
$ sbsiglist --owner 77fa9abd-0359-4d32-bd60-28f4e78f784b --type x509 --output MS_UEFI_db.esl MicCorUEFCA2011_2011-06-27.crt
$ cat MS_Win_db.esl MS_UEFI_db.esl > MS_db.esl

可选操作(为了严格符合的微软UEFI安全启动要求):使用微软的GUID(77fa9abd-0359-4d32-bd60-28f4e78f784b),根据微软的KEK格式的DB证书创建EFI签名列表:

 $ sbsiglist --owner 77fa9abd-0359-4d32-bd60-28f4e78f784b --type x509 --output MS_Win_KEK.esl MicCorKEKCA2011_2011-06-24.crt

使用你自己的KEK签署一个DB变量更新。使用带有-a选项的sign-efi-sig-list添加而不是替换一个DB证书:

$ sign-efi-sig-list -a -g 77fa9abd-0359-4d32-bd60-28f4e78f784b -k KEK.key -c KEK.crt db MS_db.esl add_MS_db.auth

可选操作(为了严格符合微软的UEFI安全启动要求):使用你自己的PK签署一个KEK变量更新。使用带有-a选项的sign-efi-sig-list添加而不是替换一个KEK证书:

$ sign-efi-sig-list -a -g 77fa9abd-0359-4d32-bd60-28f4e78f784b -k PK.key -c PK.crt KEK MS_Win_KEK.esl add_MS_Win_KEK.auth

按照#在固件中登记密钥来登记add_MS_db.auth,为了严格符合微软的UEFI安全启动要求,将add_MS_Win_KEK.auth放入UEFI安全启动数据库的变量中。

使用已签名的引导加载程序[编辑 | 编辑源代码]

使用签名的引导加载程序意味着使用由微软的密钥签名的引导加载程序。有两个已知的签名的引导加载程序:PreLoader和shim。它们的目的是连锁加载其他EFI二进制文件(通常是引导加载程序)。由于微软不会签署一个自动启动任何未签署的二进制文件的引导加载程序,PreLoader和shim使用一个叫做机器所有者密钥列表的白名单,缩写为MokList。如果二进制文件的SHA256散列值(Preloader和shim)或签署二进制文件的密钥(shim)在MokList中,它们就执行它,如果没有,他们就启动一个密钥管理工具,允许注册散列值或密钥。

警告: 微软所谓的“Secured-core PCs”在出厂时并没有登记微软第三方UEFI CA证书(Microsoft Corporation UEFI CA 2011)enrolled.[7]。唯一登记的DB证书是Microsoft Windows Production PCA 2011证书,该证书专门用于签署Windows的引导加载程序。

微软第三方UEFI CA证书的登记需要在固件设置中启用,以启动用该证书签署的EFI二进制文件和OpROM。

PreLoader[编辑 | 编辑源代码]

运行时,PreLoader尝试启动loader.efi。如果loader.efi的散列值不在MokList中,PreLoader将会启动HashTool.efi。在HashTool中,你必须登记你想要启动的EFI二进制的散列值,也就是你的引导加载程序loader.efi)以及内核的散列值。

注意: 每次你更新任何二进制文件(如引导加载程序或内核)时,你都需要登记它们的新散列值。
提示:rEFInd加载管理程序的refind-install脚本可以复制rEFInd与PreLoader的EFI二进制文件到ESP。请看REFInd#使用_PreLoader
设置PreLoader[编辑 | 编辑源代码]
注意: efitools包中的PreLoader.efiHashTool.efi没有被签署,所以它们的作用是有限的。你可以从preloader-signedAUR获取被签署的PreLoader.efiHashTool.efi,或者手动下载它们

安装preloader-signedAURHashTool.efi然后复制PreLoader.efiHashTool.efi引导加载程序目录中。对于systemd-boot,使用:

# cp /usr/share/preloader-signed/{PreLoader,HashTool}.efi esp/EFI/systemd

现在复制一份引导加载程序的二进制文件并重命名为loader.efi。对于systemd-boot,使用:

# cp esp/EFI/systemd/systemd-bootx64.efi esp/EFI/systemd/loader.efi

最终,创建一个新的NVRAM引导项来启动PreLoader.efi

# efibootmgr --unicode --disk /dev/sdX --part Y --create --label "PreLoader" --loader /EFI/systemd/PreLoader.efi

X替换为驱动器字符并把Y替换为EFI 系统分区的分区号。

这个启动项应当添加进启动列表中并作为第一项启动,用efibootmgr命令检查,必要时调整启动顺序。

回撤[编辑 | 编辑源代码]

如果启动这个自定义NVRAM启动项时出现问题,复制HashTool.efiloader.efi到UEFI系统自动启动的默认加载程序位置:

# cp /usr/share/preloader-signed/HashTool.efi esp/EFI/BOOT/
# cp esp/EFI/systemd/systemd-bootx64.efi esp/EFI/BOOT/loader.efi

复制一份PreLoader.efi并重命名:

# cp /usr/share/preloader-signed/PreLoader.efi esp/EFI/BOOT/BOOTx64.EFI

对于特别顽固的UEFI实现,可以将PreLoader.efi复制到Windows系统使用的默认加载程序位置:

# mkdir -p esp/EFI/Microsoft/Boot
# cp /usr/share/preloader-signed/PreLoader.efi esp/EFI/Microsoft/Boot/bootmgfw.efi
注意: 如果与Windows双启动,请先备份原始的bootmgfw.efi,因为替换它可能会导致Windows更新的问题。

如前所述,将HashTool.efiloader.efi复制到'esp/EFI/Microsoft/Boot/

当系统在启用安全启动下启动时,按照上述步骤登记loader.efi/vmlinuz-linux(或任何使用的内核镜像)。

如何在启动时使用?[编辑 | 编辑源代码]

当出现一条消息说:Failed to Start loader... I will now execute HashTool.。要使用HashTool来登记loader.efivmlinuz.efi的散列值,请遵循以下步骤。这些步骤假定了一个重制的archiso安装介质的项目。你得到的确切的项目取决于你的引导加载程序的设置。

  • 选择 OK
  • 在HashTool的主菜单,选择Enroll Hash,选择\loader.efi然后选Yes确认。同样,再选择Enroll Hasharchiso来进入archiso的目录,然后选择vmlinuz.efi,用Yes确认。接下来选择Exit返回至启动设备选单。
  • 在启动设备选单中选择Arch Linux archiso x86_64 UEFI CD
移除PreLoader[编辑 | 编辑源代码]
注意: 既然你要删除东西,那么最好先备份。

卸载preloader-signedAUR然后

并简单地删除复制的文件并恢复配置。 对于systemd-boot使用:

# rm esp/EFI/systemd/{PreLoader,HashTool}.efi
# rm esp/EFI/systemd/loader.efi
# efibootmgr --unicode --bootnum N --delete-bootnum
# bootctl update

这里N指的是用来启动PreLoader.efi所创建的NVRAM启动项。 如有必要,用efibootmgr命令检查并调整启动顺序。

注意: 上述命令涵盖了最简单的情况;如果你创建、复制、重命名或编辑了更多的文件,那么你也必须处理它们。如果PreLoader是你的操作引导项,你还需要#禁用安全启动
删除已登记的散列值[编辑 | 编辑源代码]

在MOK数据库中登记的每一条散列值都会吃掉NVRAM的一小块空间。你可能想删除无用的散列值,以释放空间并防止过时的程序启动。

安装efitools然后复制KeyTool.efi

# cp /usr/share/efitools/efi/KeyTool.efi esp/EFI/systemd/KeyTool.efi

引导启动至Preloader,你会看到KeyTool引导项。然后你就可以在MOK数据库中编辑散列值。

shim[编辑 | 编辑源代码]

Tango-view-fullscreen.png这篇文章的某些内容需要扩充。Tango-view-fullscreen.png

原因: Testing needed. (在 Talk:UEFI/安全启动#shim 中讨论)

When run, shim tries to launch grubx64.efi. If MokList does not contain the hash of grubx64.efi or the key it is signed with, shim will launch MokManager (mmx64.efi). In MokManager you must enroll the hash of the EFI binaries you want to launch (your boot loader (grubx64.efi) and kernel) or enroll the key they are signed with.

注意:
  • If you use #shim with hash, each time you update any of the binaries (e.g. boot loader or kernel) you will need to enroll their new hash.
  • Since version 15.3, shim will not launch EFI binaries without a valid .sbat section. Run objdump -j .sbat -s /path/to/binary.efi to verify if an EFI binary has it. See the SBAT documentation for details.
  • It might be worth mentioning that if you are not actually interested in the security brought by Secure Boot and are only enabling it to meet the requirements posed by Windows 11, you may want to consider disabling the validation process in shim with mokutil --disable-validation. In that case you will not need to sign grub (sbat probably still needed) or the kernel images and at the same time be able to boot Windows with chainloader in grub.
设置shim[编辑 | 编辑源代码]
提示:The rEFInd boot manager's refind-install script can sign rEFInd EFI binaries and copy them along with shim and the MOK certificates to the ESP. See rEFInd#Using shim for instructions.

Install shim-signedAUR.

Rename your current boot loader to grubx64.efi

# mv esp/EFI/BOOT/BOOTx64.EFI esp/EFI/BOOT/grubx64.efi

Copy shim and MokManager to your boot loader directory on ESP; use previous filename of your boot loader as as the filename for shimx64.efi:

注意:
  • Make sure you do NOT copy fbx64.efi (which is under the same directory) unless you actually have a valid bootx64.csv to use. Otherwise shim will NOT execute grubx64.efi but will appear to fail to work and just reset the machine.
# cp /usr/share/shim-signed/shimx64.efi esp/EFI/BOOT/BOOTx64.EFI
# cp /usr/share/shim-signed/mmx64.efi esp/EFI/BOOT/

Finally, create a new NVRAM entry to boot BOOTx64.EFI:

# efibootmgr --unicode --disk /dev/sdX --part Y --create --label "Shim" --loader /EFI/BOOT/BOOTx64.EFI

shim can authenticate binaries by Machine Owner Key or hash stored in MokList.

Machine Owner Key (MOK)
A key that a user generates and uses to sign EFI binaries.
hash
A SHA256 hash of an EFI binary.

Using hash is simpler, but each time you update your boot loader or kernel you will need to add their hashes in MokManager. With MOK you only need to add the key once, but you will have to sign the boot loader and kernel each time it updates.

shim与散列值[编辑 | 编辑源代码]

If shim does not find the SHA256 hash of grubx64.efi in MokList it will launch MokManager (mmx64.efi).

In MokManager select Enroll hash from disk, find grubx64.efi and add it to MokList. Repeat the steps and add your kernel vmlinuz-linux. When done select Continue boot and your boot loader will launch and it will be capable launching the kernel.

shim与密钥[编辑 | 编辑源代码]

Install sbsigntools.

You will need:

.key
PEM format private key for EFI binary signing.
.crt
PEM format certificate for sbsign.
.cer
DER format certificate for MokManager.

Create a Machine Owner Key:

$ openssl req -newkey rsa:4096 -nodes -keyout MOK.key -new -x509 -sha256 -days 3650 -subj "/CN=my Machine Owner Key/" -out MOK.crt
$ openssl x509 -outform DER -in MOK.crt -out MOK.cer

Sign your boot loader (named grubx64.efi) and kernel:

# sbsign --key MOK.key --cert MOK.crt --output /boot/vmlinuz-linux /boot/vmlinuz-linux
# sbsign --key MOK.key --cert MOK.crt --output esp/EFI/BOOT/grubx64.efi esp/EFI/BOOT/grubx64.efi

You will need to do this each time they are updated. You can automate the kernel signing with a pacman hook, e.g.:

/etc/pacman.d/hooks/999-sign_kernel_for_secureboot.hook
[Trigger]
Operation = Install
Operation = Upgrade
Type = Package
Target = linux
Target = linux-lts
Target = linux-hardened
Target = linux-zen

[Action]
Description = Signing kernel with Machine Owner Key for Secure Boot
When = PostTransaction
Exec = /usr/bin/find /boot/ -maxdepth 1 -name 'vmlinuz-*' -exec /usr/bin/sh -c 'if ! /usr/bin/sbverify --list {} 2>/dev/null | /usr/bin/grep -q "signature certificates"; then /usr/bin/sbsign --key MOK.key --cert MOK.crt --output {} {}; fi' ;
Depends = sbsigntools
Depends = findutils
Depends = grep

Copy MOK.cer to a FAT formatted file system (you can use EFI system partition).

Reboot and enable Secure Boot. If shim does not find the certificate grubx64.efi is signed with in MokList it will launch MokManager (mmx64.efi).

In MokManager select Enroll key from disk, find MOK.cer and add it to MokList. When done select Continue boot and your boot loader will launch and it will be capable launching any binary signed with your Machine Owner Key.

shim与密钥和GRUB[编辑 | 编辑源代码]

Tango-view-fullscreen.png这篇文章的某些内容需要扩充。Tango-view-fullscreen.png

原因: GRUB#UEFI systems mentions a tool called grub-mkstandalone that is not present here. (在 Talk:UEFI/安全启动 中讨论)

Merge-arrows-2.png本文或本章节可能需要合并到GRUBMerge-arrows-2.png

附注: The details about building your own EFI binary and choosing the modules should probably be in GRUB.(在 Talk:UEFI/安全启动 中讨论)

GRUB can only be successfully booted in Secure Boot mode if its EFI binary includes all of the modules necessary to read the filesystem containing the vmlinuz and initramfs images.

Since GRUB version 2.06.r261.g2f4430cc0, loading modules in Secure Boot Mode via insmod is no longer allowed, as this would violate the expectation to not sideload arbitrary code. If the GRUB modules are not embedded in the EFI binary, and GRUB tries to sideload/insmod them, GRUB will fail to boot with the message:

error: prohibited by secure boot policy

Ubuntu, according to its official build script, embeds the following GRUB modules in its signed GRUB EFI binary grubx64.efi:

  • the "basic" modules, necessary for booting from a CD or from a simple-partitioned disk: all_video, boot, btrfs, cat, chain, configfile, echo, efifwsetup, efinet, ext2, fat, font, gettext, gfxmenu, gfxterm, gfxterm_background, gzio, halt, help, hfsplus, iso9660, jpeg, keystatus, loadenv, loopback, linux, ls, lsefi, lsefimmap, lsefisystab, lssal, memdisk, minicmd, normal, ntfs, part_apple, part_msdos, part_gpt, password_pbkdf2, png, probe, reboot, regexp, search, search_fs_uuid, search_fs_file, search_label, sleep, smbios, squash4, test, true, video, xfs, zfs, zfscrypt, zfsinfo
  • the "platform-specific" modules for x86_64-efi architecture, necessary for e.g.:
    • play: to play sounds during boot
    • cpuid: to the CPU at boot
    • linuxefi: to support UEFI booting
    • tpm: to support Measured Boot / Trusted Platform Modules
  • the "advanced" modules, consisting of modules:
    • cryptodisk: to boot from plain-mode encrypted disks
    • gcry_algorithm: to support particular hashing and encryption algorithms
    • luks: to boot from LUKS-encrypted disks:
    • lvm: to boot from LVM logical volume disks
    • mdraid09, mdraid1x, raid5rec, raid6rec: to boot from RAID virtual disks

You must construct your list of GRUB modules in the form of a shell variable that we denote as GRUB_MODULES. You can use the latest Ubuntu script as a starting point, and trim away modules that are not necessary on your system. Omitting modules will make the boot process relatively faster, and save some space on the ESP partition.

You also need a Secure Boot Advanced Targeting (SBAT) file/section included in the EFI binary, to improve the security; if GRUB is launched from the UEFI shim loader. This SBAT file/section contains metadata about the GRUB binary (version, maintainer, developer, upstream URL) and makes it easier for shim to block certain GRUB versions from being loaded if they have security vulnerabilities[8][9], as explained in the UEFI shim bootloader secure boot life-cycle improvements document from shim.

The first-stage UEFI bootloader shim will fail to launch grubx64.efi if the SBAT section from grubx64.efi is missing!

If GRUB is installed, a sample SBAT .csv file is provided under /usr/share/grub/sbat.csv.

Reinstall GRUB using the provided /usr/share/grub/sbat.csv file and all the needed GRUB_MODULES and sign it:

# grub-install --target=x86_64-efi --efi-directory=esp --modules=${GRUB_MODULES} --sbat /usr/share/grub/sbat.csv
# sbsign --key MOK.key --cert MOK.crt --output esp/EFI/GRUB/grubx64.efi esp/EFI/GRUB/grubx64.efi
# cp esp/GRUB/grubx64.efi esp/boot/grubx64.efi

Reboot, select the key in MokManager, and Secure Boot should be working.

移除shim[编辑 | 编辑源代码]

Uninstall shim-signedAUR, remove the copied shim and MokManager files and rename back your boot loader.

Protecting Secure Boot[编辑 | 编辑源代码]

The only way to prevent anyone with physical access to disable Secure Boot is to protect the firmware settings with a password.

Most UEFI firmwares provide such a feature, usually listed under the "Security" section in the firmware settings.

Tips and tricks[编辑 | 编辑源代码]

sbctl[编辑 | 编辑源代码]

sbctl is a user-friendly way of setting up secure boot and signing files.

注意: sbctl does not work with all hardware. How well it will work depends on the manufacturer.

Creating and enrolling keys[编辑 | 编辑源代码]

Before starting, go to your firmware settings and set secure boot mode to Setup mode. This is different for each device.

Once you log back in, check the secure boot status:

$ sbctl status

You should see that sbctl is not installed and secure boot is disabled.

Then create your custom secure boot keys:

# sbctl create-keys

Enroll your keys, with Microsoft's keys, to the UEFI:

# sbctl enroll-keys -m
警告: Some firmware is signed and verified with Microsoft's keys when secure boot is enabled. Not validating devices could brick them. To enroll your keys without enrolling Microsoft's, run: # sbctl enroll-keys. Only do this if you know what you are doing.

Check the secure boot status again:

$ sbctl status

sbctl should be installed now, but secure boot will not work until the boot files have been signed with the keys you just created.

Signing[编辑 | 编辑源代码]

Check what files need to be signed for secure boot to work:

# sbctl verify

Now sign all the unsigned files. Usually the kernel and the boot loader need to be signed. For example:

# sbctl sign -s /boot/vmlinuz-linux
# sbctl sign -s /boot/EFI/BOOT/BOOTX64.EFI

The files that need to be signed will depend on your system's layout, kernel and boot loader.

Now you are done! Reboot your system and turn secure boot back on in the firmware settings. If the boot loader and OS load, secure boot should be working. Check with:

$ sbctl status

Automatic signing with the pacman hook[编辑 | 编辑源代码]

sbctl comes with a pacman hook that automatically signs all new files whenever the Linux kernel, systemd or the boot loader is updated.

提示:If you use Systemd-boot and systemd-boot-update.service, the boot loader is only updated after a reboot, and the sbctl pacman hook will therefore not sign the new file. As a workaround, it can be useful to sign the boot loader directly in /usr/lib/, as bootctl install and update will automatically recognize and copy .efi.signed files to the ESP if present, instead of the normal .efi file. See bootctl(1).
# sbctl sign -s -o /usr/lib/systemd/boot/efi/systemd-bootx64.efi.signed /usr/lib/systemd/boot/efi/systemd-bootx64.efi

Sign the official ISO with custom keys[编辑 | 编辑源代码]

Support for Secure Boot using custom keys can be added to the official ISO by simply extracting the boot loader (BOOTx64.EFI and BOOTIA32.EFI), kernel, UEFI shell, signing them and then repacking the ISO with the signed files.

Install libisoburn, mtools and sbsigntools.

First extract the relevant files and El Torito boot images:

$ osirrox -indev archlinux-YYYY.MM.DD-x86_64.iso \
	-extract_boot_images ./ \
	-extract /EFI/BOOT/BOOTx64.EFI BOOTx64.EFI \
	-extract /EFI/BOOT/BOOTIA32.EFI BOOTIA32.EFI \
	-extract /shellx64.efi shellx64.efi \
	-extract /shellia32.efi shellia32.efi \
	-extract /arch/boot/x86_64/vmlinuz-linux vmlinuz-linux

xorrisofs(1) option -rational-rock as used by mkarchiso makes the files on ISO 9660 read-only which persists after extracting them. Make the files writable so that they can be modified:

$ chmod +w BOOTx64.EFI BOOTIA32.EFI shellx64.efi shellia32.efi vmlinuz-linux

Sign the files using an enrolled db key and certificate:

$ sbsign --key db.key --cert db.crt --output BOOTx64.EFI BOOTx64.EFI
$ sbsign --key db.key --cert db.crt --output BOOTIA32.EFI BOOTIA32.EFI
$ sbsign --key db.key --cert db.crt --output shellx64.efi shellx64.efi
$ sbsign --key db.key --cert db.crt --output shellia32.efi shellia32.efi
$ sbsign --key db.key --cert db.crt --output vmlinuz-linux vmlinuz-linux

Copy the boot loader and UEFI shell to eltorito_img2_uefi.img. It will be used as the EFI system partition and will be listed as an El Torito UEFI boot image. The size of eltorito_img2_uefi.img is fixed, but there is 1 MiB free space added by mkarchiso for the purposes of rounding/alignment and to account for reserved sectors, so the size increase from the signatures should not be an issue.

$ mcopy -D oO -i eltorito_img2_uefi.img BOOTx64.EFI BOOTIA32.EFI ::/EFI/BOOT/
$ mcopy -D oO -i eltorito_img2_uefi.img shellx64.efi shellia32.efi ::/

Repack the ISO using the modified El Torito UEFI boot image and add the signed boot loader files, UEFI shell and kernel to ISO 9660:

$ xorriso -indev archlinux-YYYY.MM.DD-x86_64.iso \
	-outdev archlinux-YYYY.MM.DD-x86_64-Secure_Boot.iso \
	-boot_image any replay \
	-append_partition 2 0xef eltorito_img2_uefi.img \
	-map BOOTx64.EFI /EFI/BOOT/BOOTx64.EFI \
	-map BOOTIA32.EFI /EFI/BOOT/BOOTIA32.EFI \
	-map shellx64.efi /shellx64.efi \
	-map shellia32.efi /shellia32.efi \
	-map vmlinuz-linux /arch/boot/x86_64/vmlinuz-linux

Boot the resulting archlinux-YYYY.MM.DD-x86_64-Secure_Boot.iso.

See also[编辑 | 编辑源代码]