一、UEFI 是什么?
UEFI 是一种规范,定义了平台固件和操作系统之间的接口,从厂商实现的角度出发,我们可以把 UEFI 理解为比 BIOS 更安全、更具扩展性的新固件(固件就是固化在主板 EEPROM 中的子系统,用于处理计算机操作系统加载之前的所有事情),我们跟 UEFI 打交道的界面就是 UEFI 固件的 UI,大概长这个样子(如下图),我们都见过 BIOS UI,只有简单的文本操作界面,为什么 UEFI 能够做到图形化呢?因为 UEFI 固件在启动时可以加载 EFI 设备驱动,有了显卡驱动, UEFI 的界面就可以实现丰富的图形化。
- UEFI 支持从网络启动
- UEFI 能自动识别 USB 和 CD-ROM 中的启动项
- UEFI 启动项等配置可以在操作系统上通过定义的接口进行修改
- UEFI 启动界面支持丰富的图形化
- UEFI 支持安全启动
- UEFI 支持扩展
二、 Secure Boot 和密钥
Secure Boot 是 UEFI 标准的一部分,简单地说,UEFI 通过公私钥加密体系来确保 UEFI 启动过程中加载的所有可执行文件(EFI 驱动程序、EFI 可执行程序、操作系统 bootloader 等)的有效性和合法性,即数字签名存在且是有效的,这从根本上在在固件启动过程中的可能遭受的攻击问题,为了实现这个功能,UEFI 中定义了以下四个类型的安全变量存储密钥、签名和哈希值。
尽管密钥验证看起来类似于 SSL 标准,但是还有些不同,密钥链只验证到密钥数据库中的密钥本身,不会再往后追溯,但 SSL 会一直验证到 CA 根。所有的密钥都存储在 UEFI 安全变量中,每个变量创建后,只有能证明(拥有对应的私钥)自己是创建本安全变量的人才能更新此变量,其他人无法更新。
1. PK (Platform Key 平台密钥)
此处的平台可以理解为某一个主板厂商的某一个型号的主板(所以一般计算机厂商都有 n 中不同的平台),每个主板中改密钥最多只能有一个,它的作用是用来验证 KEK 变量中所有密钥的合法性,大部分的厂商实现中,这个密钥是 X509 数字证书格式(当然存储在 NVRAM 中的密钥是转换后的二进制格式)。平台密钥的拥有者本质上就是平台的拥有者,比如:联想的某个主板中的 PK 拥有者就是联想公司这个主体。如果平台密钥被清除,平台一般会进入到安装模式,同时平台密钥可以被替换成自己生成的密钥。
2. KEK (Key Exchange Key 密钥交换密钥)
KEK 用于对 db 和 dbx 数据库中内容进行存储、更新、删除时进行签名验证,只有有效签名的密钥、签名和哈希值才能写入、删除或更新到数据库中。KEK 可以包含多个,一般计算机出厂时包含两个 KEK,一个是微软的,一个是主板厂商的,这样微软和厂商都可以发布固件更新包对固件进行直接更新。
3. db (Signature Database 签名数据库)
用于在 Secure Boot 打开的情况下对 EFI 二进制文件进行签名验证,db 中可以包含密钥、签名和哈希,在 Secure Boot 模式下,EFI 二进制文件(EFI 驱动,bootloader 等)中的签名信息会跟这个数据库中的数据进行比较,如果满足以下任何一个条件,则说明该二进制文件合法可执行。
- 文件没有签名,但 db 中包含该文件的 SHA-256 哈希值
- 文件已签名并且 db 中包含这个签名信息
- 文件已签名并且 db 中包含给这个文件签名的密钥并且签名是有效的
4. dbx (Forbidden Signatures Database 禁用签名数据库)
这是一个类似黑名单的数据库,只要 EFI 可执行文件的签名、哈希等信息存在于黑名单中,那么这个 EFI 二进制文件将不能被执行。
三、如何执行数字签名和验证数字签名
首先会对该文件使用哈希算法(SHA-256,MD5等)进行消息摘要操作,生成一个固定长度的摘要信息,MD5后的消息摘要长度为 32 (128/8) 字节,SHA-256 哈希后的长度为 64 (256/8) 个字节,然后使用私钥对生成的固定长度的摘要进行不对称加密(只有对应的公钥才能解密),加密后的密文就是数字签名,数字签名可以追加到原始文件的后面,也可以单独用另一个文件保存,随原始文件一起发布。
问题: 为什么不对文件直接进行非对称加密操作,而是对文件哈希后的摘要加密?
因为非对称加密是个 CPU 密集型操作,需要消耗大量算力,当需要加密的文件很大时,这个操作带来的性能问题是难以接受的,而哈希算法的不可逆性和生成的摘要长度短小且固定正好能够用在这个场景中。
四、常见问题
PK 、KEK 能不能被清除或写入?
大部分兼容机都可以被清除和写入自己的 PK,但有些笔记本不行,比如:Surface 的 Secure Boot 无法被关闭,所有密钥都不能被清除。
EFI 可执行文件包括哪些?
包括在 UEFI 启动过程中加载的所有可执行文件、驱动、镜像、操作系统 bootloader 等。
PK KEK 安全变量中存储的密钥格式?
密钥大部分是 X509 数字证书格式,存储到安全变量中的是转换后的二进制格式。
几种类型密钥之间的关系?
UEFI 模式转换关系
如何创建自己的密钥?
在 Windows(安装 git bash) 或 Linux 主机上可以使用以下脚本自己创建对应的密钥。
#!/bin/bash
echo -n "请输入一个通用名,比如公司名称或个人名字: "
read NAME
openssl req -new -x509 -newkey rsa:2048 -subj "/CN=$NAME PK/" -keyout PK.key \
-out PK.crt -days 3650 -nodes -sha256
openssl req -new -x509 -newkey rsa:2048 -subj "/CN=$NAME KEK/" -keyout KEK.key \
-out KEK.crt -days 3650 -nodes -sha256
openssl req -new -x509 -newkey rsa:2048 -subj "/CN=$NAME DB/" -keyout DB.key \
-out DB.crt -days 3650 -nodes -sha256
openssl x509 -in PK.crt -out PK.cer -outform DER
openssl x509 -in KEK.crt -out KEK.cer -outform DER
openssl x509 -in DB.crt -out DB.cer -outform DER
GUID=`python -c 'import uuid; print(str(uuid.uuid1()))'`
echo $GUID > myGUID.txt
cert-to-efi-sig-list -g $GUID PK.crt PK.esl
cert-to-efi-sig-list -g $GUID KEK.crt KEK.esl
cert-to-efi-sig-list -g $GUID DB.crt DB.esl
rm -f noPK.esl
touch noPK.esl
sign-efi-sig-list -t "$(date --date='1 second' +'%Y-%m-%d %H:%M:%S')" \
-k PK.key -c PK.crt PK PK.esl PK.auth
sign-efi-sig-list -t "$(date --date='1 second' +'%Y-%m-%d %H:%M:%S')" \
-k PK.key -c PK.crt PK noPK.esl noPK.auth
sign-efi-sig-list -t "$(date --date='1 second' +'%Y-%m-%d %H:%M:%S')" \
-k PK.key -c PK.crt KEK KEK.esl KEK.auth
sign-efi-sig-list -t "$(date --date='1 second' +'%Y-%m-%d %H:%M:%S')" \
-k KEK.key -c KEK.crt db DB.esl DB.auth
chmod 0600 *.key
echo ""
echo ""
echo "For use with KeyTool, copy the *.auth and *.esl files to a FAT USB"
echo "flash drive or to your EFI System Partition (ESP)."
echo "For use with most UEFIs' built-in key managers, copy the *.cer files."
echo ""
参考文章
UEFI SecureBoot on Debian
Protecting the pre-OS environment with UEFI