1 Star 0 Fork 11

coder_lw / wiki

forked from deepinwiki / wiki 
加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
UEFI 安全启动.md 13.09 KB
一键复制 编辑 原始数据 按行查看 历史

UEFI 安全启动

前言

UEFI 是BIOS 的新版本,他有更多现代功能。有一项就是支持安全启动。所谓安全启动是根据密钥来验证启动文件是否有受认可的签名,然后才允许启动。

这将防止针对启动的安全攻击。如果启动被替换为一个非安全的代码,那么之后的任何防护都是毫无意义的。

厂商默认会内置支持 windows 的安全密钥,但是对于广大的第三方启动系统,如linux ,grub,pe 盘之类的,就不那么友好了。

解决的思路是:

  1. 替换 bios 内置的安全密钥
  2. 机器用户证书
  3. 关闭安全启动

这里,重点介绍第一种方法。

架构

公钥体系:

  1. 私钥 : 自己保管
  2. 公钥 : 公开发布
  3. 散列 : 对文件演算,得到一个简短却很难重复的字符串
  4. 对称加密 : 解密加密都是一个密钥

公钥和私钥是一对匹配的密钥,由公钥加密的内容,私钥可以解密,反之成立。私钥就是保密的密钥,公钥就是公开发布给第三方的密钥。然后就能进行:

  1. 签名:对文件散列(哈希),用私钥加密散列值,附加到原文件(或存储到独立的文件中)
  2. 加密:用(别人的)公钥加密数据,传递出去。
    1. 实际上很少用私钥加密,私钥的用途都是签名。一般公钥负责加密,私钥负责解密。也就是要和某人相互加密通信,那么你需要拥有对方的公钥,对方需要拥有你的公钥。
  3. 解密:用私钥解密第三方用公钥加密的数据。
  4. 复合加密解密: 不直接用公钥加密,而是选择一个高效率的对称加密算法加密数据。加密的算法,和相关参数,再用公钥加密。解密是逆过程,这样即保证了安全性,又大大提高效能,也是主流的做法。
  5. 证书:由公证方验证私钥的持有者身份,相关的身份信息和公钥用公证方私钥签名。进而当第三方信赖公证方,即可信赖证书携带的公钥。从而解决了公钥分发中出现的伪造问题(中间人攻击)。这也是主流的做法,使用证书而不直接使用随意分发的公钥。
    1. 一般系统会内置一些可靠的公证方(公钥管理组织)的证书(内置公钥),因而可以验证其签发的第三方证书。
    2. 证书验证体系是个树状结构,上级签发机构可以签发多个下级签发机构,从而降低密钥泄露后的影响范围,也可以让下级证书处于撤销状态。

UEFI 安全验证机制

  1. 证书
    1. 平台密钥 PK
    2. 键交换密钥 KEK
    3. 内核签名密钥 db
  2. 安全变量
    1. PK :一个顶级签发机构
    2. KEK :一个或多个中层签发机构
    3. db :签名数据库
    4. dbx :黑名单数据库

PK 就是主板厂家自己的密钥。

KEK 是微软这些系统商家的密钥。用PK签名KEK,就允许了他的下级证书生效。

db 是微软代理商(OEM)的签名密钥。

dbx 是一些失效或者泄露的密钥的黑名单。

也就是,比如你购买一个 windows 光盘,他的启动映像是被微软签名的。微软和主板厂家有py交易,所以主板会内置微软的证书。因而系统可以正常启动。

如果你购买一台品牌电脑,他的windows 系统是经过 OEM 供应商修改的,捆版一些软件之类的,这时候微软不可能替它签名,不过 OEM 厂家和微软也有py交易。OEM的证书会被微软签名。因而也能正常启动。

linux 和微软没关系,和主板厂家也没关系,所以它只能关闭安全启动。

但是,如果我们替换了 PK,下面想怎么干就怎么干了。

这就是下面我们要做的。

替换安全密钥

保存原始变量

主板一般有保存到 U 盘的功能。也就是保存为四个文件(请更名):

  1. db -> old_db.esl
  2. dbx -> old_dbx.esl
  3. KEK -> old_KEK.esl
  4. PK -> old_PK.esl

或者进入系统(efitools包):

efi-readvar -v PK -o old_PK.esl

同样方法保存四个变量即可。

创建证书

证书格式有很多种,主流的是 X.509 格式。

openssl req -new -x509 -newkey rsa:2048 -subj "/CN=htqx's kernel-signing key/" -keyout db.key -out db.crt -days 3650 -nodes -sha256

其中:

  1. db.key 是私钥
  2. db.crt 是证书(含公钥)
  3. -days 是有效期
  4. -sha256 散列算法
  5. "/CN=是说明/"
  6. rsa:2048 是公钥私钥算法
  7. -x509 是证书格式
  8. req 是待(上级)签发证书
  9. -nodes 是 no des 的意思,就是不对密钥文件加密(否则需要你输入密码)
  10. openssl 是一个强大的密码学工具

同样的方法,依次创建:

  1. PK.key 和 PK.crt
  2. KEK.key 和 KEK.crt
  3. db.key 和 db.crt

签发证书

要让证书逻辑上组成树状结构(上下级),就需要对其进行签发。

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

sign-efi-sig-list -k PK.key -c PK.crt PK PK.esl PK.auth

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

sign-efi-sig-list -a -k PK.key -c PK.crt KEK KEK.esl KEK.auth

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

sign-efi-sig-list -a -k KEK.key -c KEK.crt db db.esl db.auth

# dbx
sign-efi-sig-list -k KEK.key -c KEK.crt dbx old_dbx.esl old_dbx.auth

解释:

  1. UEFI 对证书文件有一定的格式要求,这里先转换成证书认可的格式 .esl,然后生成提交格式 .auth
  2. PK 只有一个密钥,所以是覆盖模式
  3. KEK 可以有多个,所以用了添加模式 -a 参数(add)。

有些 BIOS 可能不支持添加模式,这时候需要手动创建捆绑包:

cat old_KEK.esl KEK.esl > compound_KEK.esl

sign-efi-sig-list -k PK.key -c PK.crt KEK compound_KEK.esl compound_KEK.auth

独立证书

有些BIOS 支持添加独立证书(也就不用转换为 UEFI专用格式了)。独立证书制作只是简单的转换为 DER 标准格式:

证书规范(内容):

  1. x509

编码规范:

  1. BER : 宽松
  2. CER : 中等
  3. DER : 严格
    1. .pem : 文本编码
    2. .cer/.der : 二进制编码

证书后缀名含义:

  1. .crt :文本格式证书(默认)
  2. .cer :二进制格式证书(正式)
openssl x509 -outform DER -in PK.crt -out PK.cer

更新安全变量

重启进入 BIOS 设置:

  1. 清除安全启动 key(clear secure boot keys),进入管理模式。管理模式可以修改安全变量。

这时有些BIOS 可以添加密钥。

或进入系统(安装组合密钥):

efi-updatevar -e -f old_dbx.esl dbx
efi-updatevar -e -f compound_db.esl db
efi-updatevar -e -f compound_KEK.esl KEK

若失败可以回退:

efi-updatevar -e -f old_dbx.esl dbx
efi-updatevar -e -f old_db.esl db
efi-updatevar -e -f old_KEK.esl KEK

另一种修改方法(附加密钥):

efi-updatevar -a -c db.crt db
efi-updatevar -a -c KEK.crt KEK

最后设置 PK(注意顺序的重要性),进入正常模式:

efi-updatevar -f PK.auth PK

机器用户证书(推荐方案)

BIOS 支持用户证书,甚至是启动时临时插入,即第五个变量:

  1. MOK :机器用户密钥(Machine Owner Key)

微软曾经给 linux 签发过一个 shim 启动器,但这个启动器本身并不支持启动任意程序(这样不安全),但支持 MOK ,即用户自行添加的密钥验证。

shim 启动器的常规文件名(名字可以改):

  1. shimx64.efi

mok管理器:

  1. mmx64.efi
  2. MokManager.efi

当使用 shim 启动时,它会识别经过签名的下级启动器(grubx64.efi 等),如果下级启动器的签名不在 MOK,它会启动一个 mok 管理界面,提示用户插入证书。(ventoy U盘多系统引导就是采用类似技术)

系统中管理 MOK(mokutil包):

# 添加用户证书 (这个证书必须是x509 v3版)
mokutil -i /boot/efi/EFI/refind/keys/refind_local.cer

shim 引导

为了让 shimx64.efi 启动,需要在引导创建一个启动项(efibootmgr包):

lsblk # 查看 efi 盘路径在哪里(以efi盘为根目录)
efibootmgr -c -L shim -d /dev/nvme0n1 -p 1 -l /efi/shim/shimx64.efi

efibootmgt -v # 查看详情

说明:

  1. -c 新建启动项
  2. -L 名称
  3. -d 磁盘
  4. -p 分区
  5. -l 在efi盘中的准确路径
  6. -g GPT分区类型

然后将 grubx64.efi 和 mokmanager.efi 之类的工具放在同一个目录, shim 引导器就会自动加载。

注意:安全启动是一个完整的链条,grub 也要签名,grub 的模块也要签名,linux 内核也要签名,linux 内核模块也要签名, linux 镜像里面的内核模块也要签名。

生成安全内核

内核编译选项(选中):

  1. EFI stub support :支持 EFI 引导
  2. Module signature verification : 模块签名
    1. Require modules to be validly signed :验证签名
    2. Automatically sign all modules:自动签名
  3. /etc/efikeys/MOK.pem :默认私钥和公钥的路径

手动签名模块:

/lib/modules/${KV_FULL}/build/scripts/sign-file sha256 /etc/efikeys/MOK.key /etc/efikeys/MOK.der /lib/modules/${KV_FULL}/${libdir}/${modulename}.${KV_OBJ}

意思是在内核模块目录中找到签名工具,添加私钥和公钥,签名模块文件。

其中 ${KV_FULL} 这些变量替换为你实际的路径名字。

手动签名内核本身(sbsigntool包):

sbsign --key /etc/efikeys/MOK.key --cert /etc/efikeys/MOK.crt /boot/vmlinuz --output /boot/vmlinuz

意思是使用私钥和公钥(用你自己的来替换路径),签名 /boot/vmlinuz 的内核文件,输出签名后的结果。

grub 配置安全启动

首先对 grub 自身(grubx64.efi)进行前面。然后grub 配置为支持安全启动,这时候它就要求加载的文件(配置,模块之类的需要验证)。grub使用的验证技术并不是 x509,而是 pgp,可以安装开源软件 gpg 来创建相关密钥。

# 转换密钥格式
# 将公钥和私钥转换为 p12 格式
openssl pkcs12 --export -in db.crt -inkey db.key -out db.p12

# monkeysphere 包
# 转换私钥格式
cat db.key | PEM2OPENPGP_USAGE_FLAGS=authenticate,sign,certify,encrypt pem2openpgp "htqx <htqx@xxx.com>" | gpg --import db.pgp

# 导出备用, 注意 htqx 这个用户名可能会有重复,可以用指纹代替
gpg --export htqx > htqx.pgp # 证书
gpg -u htqx --sign htqx.pgp # grub 默认要求对证书进行签名
# 输出 htqx.pgp.gpg (使用这个)

gpg --export-secret-keys htqx > htqx.pgp.key # 私钥

重新安装 grub 引导文件,配置安全检查:

# 安装 grub 引导
 sudo grub-install -k htqx.pgp --modules=tpm --target=x86_64-efi --efi-directory=/boot/efi

注意:有些版本的 grub 内置了一个shim 启动选项,只要开启密钥的时候,它会安装shim 和mokmanager。

#  签名grub
sbsign --key /etc/efikeys/MOK.key --cert /etc/efikeys/MOK.crt /boot/efi/EFI/gentoo/grubx64.efi --output /boot/efi/EFI/shim/grubx64.efi

# 签名 grub 模块
for i in `find /boot -name "*.cfg" -or -name "*.lst" -or \
  -name "*.mod" -or -name "vmlinuz*" -or -name "initramfs*" -or \
  -name "grubenv" | grep -Ev "\.sig$"`;
do
# -u 指定密钥用户
# --batch 批量
# --detach-sign 分离式签名,产生 *.sig
  gpg --local-user htqx --batch --detach-sign $i
done
#

注意,gpg 密钥保存在用户目录 ~/.gungp/ 下,所以如果不是管理员账户,那么就要将密钥导入到管理员 root 账户内。才能使用脚本。

启动顺序

  1. shimx64.efi : 需要创建启动项
  2. grubx64.efi : 需要对其签名
    1. 需要对 grub 相关模块和配置文件用 gpg 签名。
    2. refind : 另一种常见引导管理器(自签名:/boot/efi/EFI/refind/keys/refind_local.cer)
  3. vmlinuz : 需要对内核签名
  4. 内核模块 : 需要对内核模块签名

重点:

  1. 创建 shim 菜单,和安装好 shim
  2. 创建 x509 v3 证书(二进制格式),并把其加入 mok
  3. 用 x509 密钥签名 grubx64.efi , vmlinuz 内核
  4. 创建 pgp 证书,并对其签名(然后使用签名的版本),grub 安装的时候要添加 pgp 证书
  5. 用 pgp 密钥签名 grub 相关文件

总结: 麻烦且用处不大,除非无法关闭安全启动,或者系统对安全启动做了集成。

参考

  1. gentoo 安全启动:https://wiki.gentoo.org/wiki/User:Sakaki/Sakaki%27s_EFI_Install_Guide/Configuring_Secure_Boot_under_OpenRC
  2. 在不调整Secure Boot设置的情况下安装Gentoo GNU/Linux:https://zhuanlan.zhihu.com/p/30528435
  3. pem2openpgp : http://manpages.ubuntu.com/manpages/xenial/en/man1/pem2openpgp.1.html#:~:text=pem2openpgp%20is%20a%20low-level%20utility%20for%20transforming%20raw%2C,material%2C%20%20so%20they%20should%20be%20handled%20carefully.
  4. 使用 mokutil 管理验证密钥: https://docs.windriver.com/bundle/Wind_River_Linux_Security_Features_Guide_CD/page/jje1494884826334.html
  5. GRUB 在启用 shim 和安全启动的情况下无法加载:https://forum.manjaro.org/t/grub-fails-to-load-with-shim-and-secure-boot-enabled/62522
  6. 强化 GNU GRUB: https://libreboot.org/docs/gnulinux/grub_hardening.html
  7. 在 GRUB 中使用数字签名: https://www.gnu.org/software/grub/manual/grub/html_node/Using-digital-signatures.html#Using-digital-signatures
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/coder_lw/wiki.git
git@gitee.com:coder_lw/wiki.git
coder_lw
wiki
wiki
master

搜索帮助