Total Pageviews

Tuesday, 8 June 2021

如何在 Linux 系统启用 UEFI 的 Secure Boot

概述

Secure Boot 作为 UEFI 的一个选项,它可以被设置为开启或关闭 ( 有少数的计算机里面, Secure Boot 被设置为开启,却不存在关闭它的选项,但系统主板内置有 Windows 系统的公钥证书签名,使其只能加载 Windows ,其他系统一律不以加载,用户没有选项,不能关闭,还没法换系统,无法适用本博客介绍内容)。 Secure Boot 所需要的公钥证书被保存在计算机的主板的 FLASH 里面(注意不是磁盘里面哦),在主板的一小块 FLASH 里面保存着 PK , KEK, db, dbx 的证书链,所以, 在操作开始前请确保你的计算机 UEFI-BIOS 的 Secure Boot 能够被关闭,否则,发生操作失误,将导致证书不能匹配任何程序代码文件,而使主板拒绝加载任何程序代码文件,就会导致主板变砖 (虽然它不是 BIOS ,但是许多人都这么称呼它为 UEFI-BIOS ,所以,这里也这么称呼其为 UEFI-BIOS) 。

这里说一下 Fedora 是如何做到在 Secure Boot 开启时加载的,它实际上是 Fedora 开源项目向微软公司购买签名达到的, Fedora 被系统的主板识别为和 Windows 一样的系统,这种购买行为在开源社区是不鼓励的,好处是 Fedora 几乎可以在任何可以加载 Windows 系统上被识别加载,即使该计算机硬件不能关闭 Secure Boot。

本人只在 ThinkPad 测试成功,其他机器未曾实验过,不过仅从理论来看,应该也会成功。

工具

以下工具是必不可少的:

  • openssl
  • efitools
  • sbsigntools

如果你是第三方编译好的软件包来安装的,可以忽略此步:

在用源代码编译 efitools 时,需要 Perl 的一个模块,名字为 File Slurp,在 cpan 可以找到。

预先准备

开机进入系统的 UEFI-BIOS 页面,在安全页面里找到 Secure Boot 选项,先关掉 Secure Boot (如果没有关的话),再删除或清空所有证书文件,使机器从 User Mode 转到 Setup Mode 即为正常,如果这两步不能做到,可能影响到下面的步骤。

步骤

概念介绍

这里简要介绍 PK,KEK,db,dbx 和 MOK 的概念。

  • Platform Key (PK) - PK 是用于在硬件平台层和硬件平台拥有者建立起的信任关系 ,规定一个硬件平台只能被一个拥有者所拥有,即 PK 只能存在一个 ,与拥有者相关的公钥被存储在 FLASH 里面的 PK 变量里面,同时,拥有者的私钥可以来对 PK, KEK, db, dbx 进行签名和管理。

  • Key Exchange Key (KEK) - KEK 是用于在硬件平台和操作系统之间建立信任关系 ,KEK 的公钥可以在主板的 FLASH 存在多个不同项,即 KEK 可以存在多个 ,每一项对应一种可以被启动的操作系统,同样,KEK 的私钥可以来对 db, dbx 进行签名和管理。

  • Database (db) - db 是用于对 被许可的 EFI 文件予以加载的数据签名库 ,和 KEK 一样, db 的公钥可以存在很多项 。(在 UEFI 平台里面,操作系统加载文件就是一个 EFI 文件)

  • Database Excluded (dbx) - dbx 是一个 黑名单数据签名库 ,只要谁的 EFI 签名在这,谁就被屏蔽掉, dbx 的公钥也可以存在很多项 。

  • Machine Owner Key (MOK) - MOK 的作用是等价于 db 的,但是它不是标准 Secure Boot 的内容,PreLoader 和 Shim 使用了 MOK 。

建立 PK,KEK,db 密钥对

这里需要 Linux 系统中存在 uuidgen

1
echo $(uuidgen) >guid

如果你不打算启动 Windows 系统,请忽略文中所有提及有关 Windows 系统的内容

如果系统不存在 uuidgen,就随便写个 GUID 到那个 guid 文件里,然后把微软的 Owner GUID 写到另外一个文件里:

1
echo  77fa9abd-0359-4d32-bd60-28f4e78f784b >ms-guid

建立密钥对:

1
openssl req -new -x509 -newkey rsa:2048 -subj "/CN=Platform Key/" -keyout PK.key -out PK.crt -days 3650 -nodes -sha256

上面的 -subj 的参数可以自己设置,参数 -days 是密钥的有效期,这里设置为 10 年,也可以设置为其他,生成私钥 PK.key,公钥 PK.crt

同理,KEK 和 db 亦是如此:

1
2
openssl req -new -x509 -newkey rsa:2048 -subj "/CN=Key Exchange Key/" -keyout KEK.key -out KEK.crt -days 3650 -nodes -sha256
openssl req -new -x509 -newkey rsa:2048 -subj "/CN=Database/" -keyout db.key -out db.crt -days 3650 -nodes -sha256

这里没有建立 dbx 的黑名单,因为暂时用不到这个,也没有黑名单签名文件,MOK 不通用,亦不讨论

建立 EFI List 文件

EFI List 文件其实就是公钥配以平台拥有者的 GUID 的文件:

1
2
3
cert-to-efi-sig-list -g $(cat guid) PK.crt PK.esl
cert-to-efi-sig-list -g $(cat guid) KEK.crt KEK.esl
cert-to-efi-sig-list -g $(cat guid) db.crt db.esl

这样三个公钥就全变成 List 文件了。

为了能够也启动磁盘里的 Windows 系统,下载三个微软的公钥,一个 KEK,两个 db:

1
2
3
curl -L http://go.microsoft.com/fwlink/?LinkID=321185 -o MSKEK.der
curl -L http://go.microsoft.com/fwlink/?LinkID=321192 -o MSdbWPCA.der
curl -L http://go.microsoft.com/fwlink/?LinkID=321194 -o MSdbUDSCA.der

微软的这三个公钥与这里使用的公钥格式不同,需要重新转换:

1
2
3
openssl x509 -in MSKEK.der -inform DER -out MSKEK.crt
openssl x509 -in MSdbWPCA.der -inform DER -out MSdbWPCA.crt
openssl x509 -in MSdbUDSCA.der -inform DER -out MSdbUDSCA.crt

同样需要转换成 List 文件,不过这里的 GUID 最好用微软 Owner GUID:

1
2
3
cert-to-efi-sig-list -g $(cat ms-guid) MSKEK.crt MSKEK.esl
cert-to-efi-sig-list -g $(cat ms-guid) MSdbWPCA.crt MSdbWPCA.esl
cert-to-efi-sig-list -g $(cat ms-guid) MSdbUDSCA.crt MSdbUDSCA.esl

在这里需要注意一下,在 ThinkPad 里面,第二次添加 KEK 时(加参数 -a ),会出现失败,我的解决方法是把那些 List 合并成一个 List 文件,即:

1
2
cat KEK.esl MSKEK.esl >realKEK.esl
cat db.esl MSdbWPCA.esl MSdbUDSCA.esl >realDB.esl

根据 efitools 的文档,这种 cat 操作是可以完成合并的。

给 List 文件签名

1
2
3
sign-efi-sig-list -k PK.key -c PK.crt PK PK.esl PK.esl.signed
sign-efi-sig-list -k PK.key -c PK.crt KEK realKEK.esl realKEK.esl.signed
sign-efi-sig-list -k KEK.key -c KEK.crt db realDB.esl realDB.esl.signed

上述中,平台拥有着的 Key 权限最高,可以对 PK 本身,和 KEK 签名,KEK 是建立操作系统和硬件平台之间的信任关系,它只用于对于 db 进行签名。

给二进制的 EFI 文件签名

这是密钥管理和签名的最后一步,我使用的 grub ,其他的比如 elilo 等等,同理。给 grub 的 EFI 文件签名:

1
sbsign --key db.key --cert db.crt --output grubx64.efi.signed /boot/efi/EFI/slackware64/grub64.efi

可以不用给内核 vmlinuz 签名,毕竟 vmlinuz 不是硬件平台的启动 EFI 文件,不过给内核签名也没什么影响,如果想给内核签名类似于给 grub 签名,用 私钥 db.key 及对应的公钥 db.crt 对其签名,这里用的 db 是那个自己创建的 db ,即没有合并微软 db 的那个,合并了微软 db 的是 realDB 。

写入 efivars

到这里是整个过程的最后一步,首先必须保证系统的 efivarfs 被正确挂载,ls 一下 /sys/firmware/efi/efivars/ 是否存在文件,有的系统默认挂载 efivarfs ,有的则不是(后续步骤需要切换至 root 用户):

1
mount -t efivarfs efivarfs /sys/firmware/efi/efivars/

再把那三个签过名的 List 文件写入主板:

1
2
3
efi-updatevar -f PK.esl.signed PK
efi-updatevar -f realKEK.esl.signed KEK
efi-updatevar -f realDB.esl.signed db

参考 LinuxQuestions 上的解决方案,使用了在 efi-updatevar 添加 -a 参数来表示添加,但是在 ThinkPad 上多次实验第二次皆以 Operation not permitted 的错误而中断,所以,合并 EFI List 文件即只需添加一次,就全部都被加上,无需再用 -a 来添加。上述的问题很难判断是密钥链的逻辑问题,还是硬件的保护机制,不过用合并这种方法,在 Secure Boot 下,Windows 和 Linux 都可以正常启动,看来没问题。

所有操作都完成后,可以运行 efi-readvar 来查看添加结果。只要 efivarfs 仍然被挂载,即使在非 root 用户也可以查看。

相关链接

docs.slackware.com

linuxquesions.org

howtogeek.com

archlinux.org

pcworld.com

from http://hchen90.top/2017/09/02/enablesbonlinux/

No comments:

Post a Comment