UEFI/安全啟動
安全啟動(Secure Boot)是UEFI標準中的一項安全功能,旨在為pre-boot process添加一層保護;通過維護被授權或禁止的在啟動時運行的經過加密簽署的二進制文件列表,它有助於使得核心引導組件(引導管理器、內核、initramfs)不被篡改。
因此,它可以被視為對個人使用環境的securing所做努力的延續或補充,減少其他軟體安全解決方案(如系統加密)無法輕易覆蓋的攻擊面,同時完全不同且不依賴於它們。安全啟動只是作為當前安全實踐的一個組成部分獨立存在,具有自己的一套優缺點。
檢查安全啟動狀態
在啟動作業系統之前
此時,必須查看固件設置。如果機器已啟動並正在運行,則在大多數情況下必須重新啟動。
您可以在引導過程中通過按特殊鍵來訪問固件配置。使用的特殊鍵取決於固件。它通常是Esc、F2、Del或者Fn鍵中的一個。有時在啟動過程中特殊鍵的名稱會顯示一小段時間。主板說明書往往有關於此的記載。如果你想要按下特殊鍵,請在啟動機器後立即按下該鍵,甚至是在屏幕實際顯示任何內容之前。
進入固件設置後,請注意不要擅自更改任何設置。 通常在每個設置的底部都會有導航說明和設置的簡短幫助。設置本身可能由幾個頁面組成,您必須導航到正確的位置。有一些安全啟動設置可能簡單地表示為安全啟動選項,可以將其設置為打開或關閉 .
在啟動作業系統之後
使用systemd檢查系統上的安全啟動狀態的一種簡單方法是使用bootctl(1):
$ bootctl
System:
Firmware: UEFI 2.80 (American Megatrends 5.26)
Firmware Arch: x64
Secure Boot: enabled (user)
TPM2 Support: yes
Measured UKI: yes
Boot into FW: supported
...
在這裡,我們看到安全啟動已啟用並強制執行用戶模式(user mode)。Secure Boot的其他值包括disabled,Setup Mode的其他值包括setup[1]。
檢查機器是否使用安全啟動的另一種方法是使用以下命令:
$ od --address-radix=n --format=u1 /sys/firmware/efi/efivars/SecureBoot-8be4df61-93ca-11d2-aa0d-00e098032b8c
如果啟用了安全啟動,此命令將返回1作為五個列表中的最後一個整數,例如:
6 0 0 0 1
但是請注意,如果使用了功能缺失的引導加載程序,內核可能不知道安全引導(即使它在固件中啟用)。 這可以通過在系統啟動後不久檢查內核消息來驗證:
# journalctl -kg 'secure boot'
kernel: Secure boot disabled kernel: Secure boot could not be determined
能啟用的內核會列印Secure boot enabled。
引導安裝介質
官方安裝鏡像不支持安全啟動(archlinux/archiso#69)。對於安全啟動的支持最早在 archlinux-2013.07.01-dual.iso 中引入,但隨後在 archlinux-2016.06.01-dual.iso 中被移除。當時,prebootloader 被替換為了 efitools包,儘管後者使用的是未簽名的 EFI 二進制文件。自那之後,官方安全介質就再未支持過安全啟動。
為了在開啟安全啟動的系統上引導安裝介質,你需要禁用安全啟動,或者修改鏡像以添加已簽名的引導加載程序(Bootloader)。
Archboot 鏡像提供了一種在安裝介質中使用安全啟動的方式。
禁用安全啟動
可以通過 UEFI 固件接口禁用安全啟動功能。如何訪問固件配置請訪問#在啟動作業系統之前.
如果使用熱鍵不起作用並且您可以啟動 Windows,您可以通過以下方式強制重新啟動到固件配置(對於 Windows 10):設置 > 更新和安全 > 恢復 > 高級啟動(立即重新啟動)> 故障排除 > 高級選項 > UEFI 固件設置 > 重啟。
請注意,某些主板(Packard Bell 筆記本電腦就是這種情況)只有在您設置了管理員密碼(之後可以刪除)時才允許禁用安全啟動。 另請參閱 Rod Smith 的禁用安全啟動。
重新打包安裝鏡像
參見#ISO 重新打包。
編輯安裝介質
如果使用的是 U 盤安裝介質,則可以通過手動編輯介質上的 EFI 系統分區來添加對安全啟動的支持。
插入 U 盤,然後掛載該分區:
# mount /dev/disk/by-label/ARCHISO_EFI /mnt
接著按照使用自己的密鑰中的說明安裝任意已簽名的 Bootloader。例如,若要安裝 #PreLoader:
# mv /mnt/EFI/BOOT/BOOTx64.EFI /mnt/EFI/BOOT/loader.efi # cp /usr/share/preloader-signed/PreLoader.efi /mnt/EFI/BOOT/BOOTx64.EFI # cp /usr/share/preloader-signed/HashTool.efi /mnt/EFI/BOOT/HashTool.efi
實施安全啟動
為了確保理想的安全啟動設置,有以下幾個特定條件:
- UEFI基本被認為是可信的(儘管有一些眾所周知的批評和漏洞[2]),且必須被強密碼保護。
- 不使用默認的製造商或第三方密鑰,因為它們被證明會大大削弱安全啟動的安全模型[3]。
- UEFI直接加載用戶簽名的、兼容EFI boot stub的統一內核鏡像(無需引導加載程序),包含微碼(如果適用)以及 initramfs。以便在整個啟動過程維持安全啟動建立的信任鏈並減少攻擊面。
- 使用完整驅動器加密,這樣在創建內核鏡像和簽署的過程中所涉及的工具與文件就無法被具有機器物理訪問權限的人訪問並篡改。
- 可以使用TPM(可信平台模塊)獲得進一步的提升。
#使用自己的密鑰中描述了一個簡單且完全自力更生的設置,而#使用已簽署的引導加載程序使用了第三方簽署的中間工具。
使用 GRUB 引導加載程序在啟用安全啟動前需要額外的步驟,參見GRUB#啟用安全啟動。
使用自己的密鑰
實現安全啟動需要下列密鑰:
- 平台密鑰 Platform Key (PK):最高級的密鑰。
- 密鑰交換密鑰 Key Exchange Key (KEK):用於簽署簽名資料庫和禁止的簽名資料庫更新的密鑰。
- 簽名資料庫 Signature Database (db):包含密鑰和/或被允許的EFI二進制文件的散列值。
- 禁止的簽名資料庫 Forbidden Signatures Database (dbx):包含密鑰和/或被禁止的EFI二進制文件的散列值。
詳細解釋請閱讀所有UEFI密鑰的含義。
為了使用安全啟動,你至少需要PK,KEK和db密鑰。 雖然你可以添加多個KEK,db和dbx證書,但只允許一個平台密鑰(PK)。
一旦安全啟動處於「用戶模式」,密鑰只能通過用更高級別的密鑰簽署(使用sign-efi-sig-list)來更新。平台密鑰可以自己簽署。
確定 OpROM 簽名
按以下步驟檢查是否擁有任何已簽名的 OpROM。
列出所有帶 OpROM 的設備:
$ find /sys/devices/ -name rom
/sys/devices/pci0000:80/0000:80:01.0/0000:81:00.0/rom ...
以上展示了一個帶有已簽名 EFI ROM 的 Intel DC SSD 示例。對每個設備重複以下步驟。
$ lspci -s 81:00.0
81:00.0 Non-Volatile memory controller: Intel Corporation PCIe Data Center SSD (rev 01)
轉儲(Dump)ROM 內容:
# echo 1 > /sys/devices/pci0000:80/0000:80:01.0/0000:81:00.0/rom # cat /sys/devices/pci0000:80/0000:80:01.0/0000:81:00.0/rom > ~/intel_ssd.rom # echo 0 > /sys/devices/pci0000:80/0000:80:01.0/0000:81:00.0/rom
獲取並編譯 TianoCore 的 EDK II 工具:
$ make -C BaseTools
解析 ROM 文件:
$ ./BaseTools/Source/C/bin/EfiRom -d ~/intel_ssd.rom
Image 1 -- Offset 0x0
ROM header contents
Signature 0xAA55
PCIR offset 0x001C
Signature PCIR
Vendor ID 0x8086
Device ID 0x0953
Length 0x001C
Revision 0x0003
DeviceListOffset 0x00
Class Code 0x000000
Image size 0x6A00
Code revision: 0x0000
MaxRuntimeImageLength 0x00
ConfigUtilityCodeHeaderOffset 0x00
DMTFCLPEntryPointOffset 0x00
Indicator 0x80 (last image)
Code type 0x03 (EFI image)
EFI ROM header contents
EFI Signature 0x0EF1
Compression Type 0x0001 (compressed)
Machine type 0x8664 (X64)
Subsystem 0x000B (EFI boot service driver)
EFI image offset 0x0038 (@0x38)
結果顯示,該 OpROM 在偏移量(Offset)0x0 處的 Image 1 偏移量 0x0038 位置包含一個壓縮的 EFI 鏡像。提取該 EFI 區域:
$ dd if=intel_ssd.rom ibs=1 skip=$(( 0x00 + 0x38 )) of=intel_ssd.rom.efi.compressed
解壓縮該鏡像:
$ ./BaseTools/Source/C/bin/TianoCompress -d --uefi -v ~/intel_ssd.rom.efi.compressed -o ~/intel_ssd.rom.efi
$ file ../intel_ssd.rom.efi
../intel_ssd.rom.efi: PE32+ executable for EFI (boot service driver), x86-64, 6 sections
使用 sbsigntools包 檢查簽名,可以看到它是使用微軟的 DB 證書籤名的:
$ sbverify --list ~/intel_ssd.rom.efi
signature 1 image signature issuers: - /C=US/ST=Washington/L=Redmond/O=Microsoft Corporation/CN=Microsoft Corporation UEFI CA 2011 image signature certificates: - subject: /C=US/ST=Washington/L=Redmond/O=Microsoft Corporation/OU=MOPR/CN=Microsoft Windows UEFI Driver Publisher issuer: /C=US/ST=Washington/L=Redmond/O=Microsoft Corporation/CN=Microsoft Corporation UEFI CA 2011 - subject: /C=US/ST=Washington/L=Redmond/O=Microsoft Corporation/CN=Microsoft Corporation UEFI CA 2011 issuer: /C=US/ST=Washington/L=Redmond/O=Microsoft Corporation/CN=Microsoft Corporation Third Party Marketplace Root
備份當前變量
在創建新的密鑰和修改EFI變量之前,建議備份當前的變量,以便在發生錯誤時可以恢復。
安裝 efitools包 軟體包後,運行以下命令來備份所有四個主要的安全啟動變量。
$ for var in PK KEK db dbx ; do efi-readvar -v $var -o old_${var}.esl ; done
如果你在新電腦或主板上執行這條命令,你提取的變量很可能是微軟提供的。
創建密鑰
手動流程
要生成密鑰,請執行以下步驟。
你將需要多種格式的私鑰和證書。
- .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)、sbkeysync、KeyTool與固件。
創建一個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 no_PK.auth
密鑰交換密鑰:(生成KEK.auth的指令沒有加-a,所以文件只能用來update KEK實現替換。如果要生成用於add添加的KEK.auth,則需要在指令裡添加-a,同理db等。PK只能有一個所以直接替換就好,刪除的話用本頁面介紹的指令生成的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密鑰:
- 創建新的密鑰,
- 轉換其為EFI簽名列表,
- 簽署EFI簽名列表,
- 登記已簽署的證書更新文件。
$ 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二進制文件(比如應用程式、驅動、統一內核鏡像)可以被啟動。
使用sbsigntools手動操作
安裝sbsigntools包來使用sbsign(1)簽署EFI二進制文件。
- 為了檢查一個二進制文件是否被簽署並列出它的簽名,使用
sbverify --list /path/to/binary。 -
rEFInd啟動管理器的
refind-install腳本可以簽署rEFInd的EFI二進制文件並把它們與db證書一起拷貝到ESP中。 請參考rEFInd#使用你自己的密鑰。
使用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
使用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]
OUT_DIR="EFI/Linux"可以讓你的已簽署的內核鏡像直接被識別而不需要必須的配置。請看systemd-boot(7) § FILES與Systemd-boot#增加啟動選項。一旦完成配置,只需要以root運行sbupdate以完成首次鏡像生成。
warning: data remaining[26413568 vs 26423180]: gaps between PE/COFF sections?。那些是無害的且可以被安全的無視掉。[6]
將固件置於 "設置模式 "下
當平台密鑰被刪除時,安全啟動處於設置模式。要將固件置於設置模式,請進入固件設置工具,並找到刪除或清除證書的選項。#在啟動作業系統之前中介紹了如何進入設置工具。
在固件中登記密鑰
使用下列某種方式登記'db、KEK和PK證書。
使用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文件來說都可以是安全的:
個人電腦的EFI 系統分區一定要按照UEFI的規範進行加密,否則就可以在另一台電腦上被掛載且被讀取(如果你的電腦被偷了,硬碟可以被取出來掛載到另一台電腦上)。複製noPK.auth到你電腦的ESP後再刪除也是不可取的,因為在FAT32格式的EFI 系統分區上被刪除的文件可以通過像是PhotoRec這樣的工具被還原。
啟動固件設置工具,登記db、KEK與PK證書。 固件會有各種不同的界面,請看使用固件的設置工具更換密鑰,以了解如何登記密鑰。
如果使用的工具支持,最好使用.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
通常情況下,啟用安全引導下,僅使用私人自定義密鑰簽署微軟引導程序(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證書:
- 為了使Windows作業系統可以被引導,Microsoft Windows Production PCA 2011 證書必須被包含進
DB變量。 - 為了使用第三方二進制,比如UEFI驅動、其他ROM、shim包等,Microsoft Corporation UEFI CA 2011 證書(又叫作微軟第三方UEFI CA證書)必須被包含進
DB變量。 - 為了「通過更新
DBX和潛在更新DB來撤銷不良鏡像對新的Windows簽名鏡像做準備」,Microsoft Corporation KEK CA 2011 證書應當被包含進KEK變量。然而沒有「Microsoft Corporation KEK CA 2011」證書,Windows也可以啟動。
- 為了使Windows作業系統可以被引導,Microsoft Windows Production PCA 2011 證書必須被包含進
使用微軟的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中,它們就執行它,如果沒有,他們就啟動一個密鑰管理工具,允許註冊散列值或密鑰。
微軟第三方UEFI CA證書的登記需要在固件設置中啟用,以啟動用該證書籤署的EFI二進制文件和OpROM。
PreLoader
運行時,PreLoader嘗試啟動loader.efi。如果loader.efi的散列值不在MokList中,PreLoader將會啟動HashTool.efi。在HashTool中,你必須登記你想要啟動的EFI二進制的散列值,也就是你的引導加載程序(loader.efi)以及內核的散列值。
設置PreLoader
PreLoader.efi與HashTool.efi沒有被簽署,所以它們的作用是有限的。你可以從preloader-signedAUR獲取被簽署的PreLoader.efi與HashTool.efi,或者手動下載它們。安裝preloader-signedAUR和HashTool.efi然後複製PreLoader.efi和HashTool.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.efi和loader.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
bootmgfw.efi,因為替換它可能會導致Windows更新的問題。如前所述,將HashTool.efi和loader.efi複製到'esp/EFI/Microsoft/Boot/。
當系統在啟用安全啟動下啟動時,按照上述步驟登記loader.efi和/vmlinuz-linux(或任何使用的內核鏡像)。
如何在啟動時使用?
當出現一條消息說:Failed to Start loader... I will now execute HashTool.。要使用HashTool來登記loader.efi和vmlinuz.efi的散列值,請遵循以下步驟。這些步驟假定了一個重製的archiso安裝介質的項目。你得到的確切的項目取決於你的引導加載程序的設置。
- 選擇 OK
- 在HashTool的主菜單,選擇Enroll Hash,選擇
\loader.efi然後選Yes確認。同樣,再選擇Enroll Hash和archiso來進入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命令檢查並調整啟動順序。
刪除已登記的散列值
在MOK資料庫中登記的每一條散列值都會吃掉NVRAM的一小塊空間。你可能想刪除無用的散列值,以釋放空間並防止過時的程序啟動。
# cp /usr/share/efitools/efi/KeyTool.efi esp/EFI/systemd/KeyTool.efi
引導啟動至Preloader,你會看到KeyTool引導項。然後你就可以在MOK資料庫中編輯散列值。
shim
在運行時,shim會嘗試啟動grubx64.efi。如果MokList沒有包含grubx64.efi的散列值或它所簽署的密鑰,shim將會啟動MokManager(mmx64.efi)。你必須在MokManager裡登記你想要啟動的EFI二進制文件(你的引導加載程序grubx64.efi與內核)的散列值,或者登記你想要簽署的密鑰。
- 如果你使用#shim與散列值,每次你升級任何二進制文件(比如引導加載程序或內核)後都需要重新登記它們的散列值。
- 在版本15.3之後,shim不再啟動沒有合法
.sbat部分的EFI二進制文件。想要驗證一個EFI二進制文件是否具有這個部分,運行objdump -j .sbat -s /path/to/binary.efi。詳情看:SBAT文檔。 - 或許值得注意的是,說實話,如果你對安全啟動帶來的安全性並不感興趣,之所以想要開啟它僅僅是為了滿足Windows 11的要求,你或許可以考慮關閉shim的驗證過程:
mokutil --disable-validation。這種情況下你不必簽署grub(或許sbat仍然需要)以及內核鏡像,且還可以通過chainloader從grub啟動Windows。
設置shim
安裝shim-signedAUR.
重命名當前的引導加載程序為grubx64.efi
# mv esp/EFI/BOOT/BOOTx64.EFI esp/EFI/BOOT/grubx64.efi
複製shim與MokManager到ESP中的啟動引導文件夾,然後將shimx64.efi重命名為引導加載程序之前的文件名:
- 確保你不要複製同一目錄下的fbx64.efi,除非你真的有一個有效的bootx64.csv可以使用。否則shim不會執行grubx64.efi,這將無法正常工作,只會重置機器。
# cp /usr/share/shim-signed/shimx64.efi esp/EFI/BOOT/BOOTx64.EFI # cp /usr/share/shim-signed/mmx64.efi esp/EFI/BOOT/
最終,創建一個新的NVRAM引導項來啟動BOOTx64.EFI:
# efibootmgr --unicode --disk /dev/sdX --part Y --create --label "Shim" --loader /EFI/BOOT/BOOTx64.EFI
shim可以通過機器所有者密鑰或MokList所存儲的散列值驗證二進制文件。
- 機器所有者密鑰 - Machine Owner Key (MOK)
- 一個由用戶生成並被用來簽署EFI二進制文件的密鑰。
- 散列值 - hash
- 一個EFI二進制文件的SHA256散列值。
使用散列值更簡單但是每次升級你的引導加載器或內核後需要添加它們新的散列值進MokManager。而使用MOK你只需要添加密鑰一次,但是每次引導加載器或內核升級後都需要重新簽署。
shim與散列值
如果shim無法找到在MokList中找到grubx64.efi的SHA256散列值,它將會啟動MokManager(mmx64.efi)。
在MokManager中選擇Enroll hash from disk,找到grubx64.efi然後添加它到MokList。重複這一步驟添加你的內核vmlinuz-linux。完成之後,選擇Continue boot,你的引導加載程序便會啟動並成功加載內核。
shim與密鑰
安裝sbsigntools包。
你需要:
- .key
- PEM格式的私有密鑰,用於簽署EFI二進制文件。
- .crt
- PEM格式的證書,用於sbsign。
- .cer
- DER格式的證書,用於MokManager。
創建一個機器所有者密鑰(MOK):
$ 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
簽署你的引導加載程序(名為grubx64.efi)以及內核:
# 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
每次它們更新後你都需要這麼做。你可以使用一個Pacman鉤子自動化簽署過程,比如:
/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
或者使用一個 mkinitcpio鉤子:
/etc/initcpio/post/kernel-sbsign
#!/usr/bin/env bash
kernel="$1"
[[ -n "$kernel" ]] || exit 0
# use already installed kernel if it exists
[[ ! -f "$KERNELDESTINATION" ]] || kernel="$KERNELDESTINATION"
keypairs=(/path/to/db.key /path/to/db.crt)
for (( i=0; i<${#keypairs[@]}; i+=2 )); do
key="${keypairs[$i]}" cert="${keypairs[(( i + 1 ))]}"
if ! sbverify --cert "$cert" "$kernel" &>/dev/null; then
sbsign --key "$key" --cert "$cert" --output "$kernel" "$kernel"
fi
done
複製MOK.cer到一個FAT格式的文件系統(你可以使用EFI 系統分區)。
重新啟動並啟用「安全啟動」。如果shim未能找到在MokList中找到簽署的grubx64.efi證書,它將啟動MokManager(mmx64.efi)。
在MokManager中選擇Enroll key from disk,找到MOK.cer並把它添加到MokList。完成之後,選擇Continue boot,你的引導加載程序便會啟動並成功加載任何由你的機器所有者密鑰簽署的二進制文件。
shim與密鑰和GRUB
只有GRUB的EFI二進制文件包含所有能夠讀取vmlinuz與initramfs鏡像所在文件系統的模塊,才能在安全啟動模式下成功啟動。
從GRUB版本2.06.r261.g2f4430cc0之後,通過insmod在安全啟動下讀取模塊不再被允許,因為這將違反不側載任意代碼的構想。如果GRUB模塊沒有被嵌入進EFI二進制文件,GRUB會嘗試通過insmod側載它們,這會在啟動時報出如下錯誤信息:
error: prohibited by secure boot policy
根據Ubuntu的官方構建腳本,Ubuntu嵌入了下列GRUB模塊在它已簽署的GRUB EFI二進制文件grubx64.efi中。
-
「base」模塊,為了從CD或者單一分區硬碟中啟動所必需含有:
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 -
「platform-specific」模塊,為了x86_64-efi架構所必需含有,例如:
-
play:為了在啟動中播放聲音 -
cpuid:為了啟動中的CPU支持 -
linuxefi:為了支持UEFI啟動 -
tpm:為了支持度量啟動(Measured boot)與可信平台模塊
-
-
「advanced」模塊,包含下列模塊:
-
cryptodisk:為了從plain模式加密的硬碟啟動 -
gcry_algorithm:為了支持特定的散列和加密算法 -
luks:為了從LUKS加密的硬碟啟動 -
lvm:為了從LVM邏輯分卷硬碟啟動 -
mdraid09,mdraid1x,raid5rec,raid6rec:為了從RAID虛擬硬碟中啟動
-
你必須以一個shell變量的形式構建你的GRUB模塊列表,我們將它表示為GRUB_MODULES。你可以使用最新的Ubuntu腳本作為一個出發點,並修剪掉你系統中不需要的模塊。修剪模塊將使啟動過程相對更快,並在ESP分區上節省一些空間。
如果GRUB從UEFI shim加載器啟動,為了提高安全性,你還需要在EFI二進制文件中包含一個安全啟動高級目標(SBAT)文件/部分。這個SBAT文件/部分包含了關於GRUB二進制文件的元數據(版本、維護者、開發者、上游URL),並且使shim更容易阻止某些有安全漏洞的GRUB版本被加載[8][9],正如UEFI shim引導加載程序的安全啟動生命流程的提升文檔中所解釋的那樣。
如果grubx64.efi中的SBAT部分丟失,UEFI shim引導加載程序的第一階段將無法啟動grubx64.efi。
如果安裝了GRUB,/usr/share/grub/sbat.csv中提供了一個SBAT的.csv文件樣本。
使用提供的/usr/share/grub/sbat.csv文件和所有需要的GRUB_MODULES重新安裝GRUB並簽署:
# 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
重新啟動,在MokManager中選擇密鑰,安全啟動就應該可以工作了。
移除shim
卸載shim-signedAUR,移除被複製的shim與MokManager文件並重命名為你的引導加載程序默認名。
保護安全啟動
防止有物理訪問權限的人禁用安全啟動的唯一方法是用密碼保護固件設置。
大多數UEFI固件提供這樣的功能,通常列在固件設置中的「安全」部分。
提示與技巧
sbctl
sbctl包是一種用戶友好的設置安全啟動和簽署文件方式。
創建並登記密鑰
在開始之前,進入你的固件設置將安全啟動模式設置為設置模式。每個設備都有所不同。
當你重新登錄後,檢查安全啟動的狀態:
$ sbctl status
你應當看到sbctl並未安裝,並且「設置模式」(Setup Mode)已經開啟,以及安全啟動已關閉。
若顯示「設置模式」(Setup Mode)關閉,請進入BIOS手動重置進入Setup Mode。
接下來創建你的自定義安全啟動密鑰:
# sbctl create-keys
(在sbctl 0.8以及更新版本之後,執行此命令默認會把密鑰創建在/var/lib/sbctl/keys/文件夾內)
將你的密鑰與微軟的密鑰登記入UEFI中:
# sbctl enroll-keys -m
# sbctl enroll-keys。只有在你清楚了解自己在做什麼的情況下才可以這樣做。
再次檢查安全啟動狀態:
$ sbctl status
sbctl現在應該已經安裝好了,但是在用你剛剛創建的密鑰對啟動文件進行簽署之前,安全啟動將無法工作。
簽署
為了安全啟動工作,檢查需要被簽署的文件:
# sbctl verify
現在簽署所有的未被簽署的文件。通常是內核與引導加載程序需要被簽署。比如:
# sbctl sign -s /boot/vmlinuz-linux # sbctl sign -s /boot/EFI/BOOT/BOOTX64.EFI
需要被簽署的文件將取決與你系統的結構,內核以及引導加載程序。
現在萬事大吉!重新啟動你的系統,並在固件設置中重新打開安全啟動。如果引導加載程序與作業系統被加載,安全啟動應當開始工作了。用下列命令確認:
$ sbctl status
使用pacman鉤子自動簽署
sbctl帶有一個Pacman鉤子可以在內核、systemd或者引導加載程序更新後自動地簽署所有的新文件。
systemd-boot-update.service,那麼引導加載程序只會在重啟後升級,導致sbctl的Pacman鉤子因此而不簽署新的文件。 變通的方法是直接在/usr/lib/中小心的簽署引導加載程序,這樣bootctl install與update將會自動識別並複製.efi.signed(如果有的話)到ESP,而不是普通的.efi文件。詳情見bootctl(1)。
# sbctl sign -s -o /usr/lib/systemd/boot/efi/systemd-bootx64.efi.signed /usr/lib/systemd/boot/efi/systemd-bootx64.efi
用自定義密鑰簽署官方ISO
使用自定義密鑰的安全啟動支持可以添加到官方ISO中,只需提取出引導加載程序(BOOTx64.EFI與BOOTIA32.EFI)、內核與UEFI Shell,並對它們進行簽署,然後用被簽署的文件重新打包ISO。
安裝libisoburn包、mtools包與sbsigntools包。
首先解壓出相關的文件以及El-Torito啟動鏡像:
$ 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
mkarchiso所使用的,xorrisofs(1)的-rational-rock選項,會使ISO 9660中的文件變為只讀,且在解壓後仍然如此。我們需要讓這些文件變為可寫,以便它們可以被修改:
$ chmod +w BOOTx64.EFI BOOTIA32.EFI shellx64.efi shellia32.efi vmlinuz-linux
使用一個已登記的資料庫密鑰和證書籤署這些文件:
$ 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
將引導加載程序與UEFI Shell複製到eltorito_img2_uefi.img中。它將被用作為EFI系統分區,並被列為一個El-Torito UEFI啟動鏡像。eltorito_img2_uefi.img的大小是固定的,但mkarchiso增加了1 MiB的空餘空間,用於對齊以及為後續的扇區保留,所以簽署所帶來的大小增加應該不是問題。
$ 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 ::/
使用修改過的El-Torito UEFI引導映像,重新打包ISO,並在ISO 9660中加入簽署的引導加載程序文件、UEFI Shell和內核。
$ 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
啟動生成的archlinux-YYYY.MM.DD-x86_64-Secure_Boot.iso文件。
另見
- Understanding the UEFI Secure Boot Chain by tianocore
- Wikipedia:Unified Extensible Firmware Interface#Secure boot
- Dealing with Secure Boot by Rod Smith
- Controlling Secure Boot by Rod Smith
- UEFI secure booting (part 2) by Matthew Garrett
- UEFI Secure Boot by James Bottomley
- efitools README
- Will your computer's "Secure Boot" turn out to be "Restricted Boot"? — Free Software Foundation
- Free Software Foundation recommendations for free operating system distributions considering Secure Boot
- Intel's UEFI Secure Boot Tutorial
- Secure Boot, Signed Modules and Signed ELF Binaries
- National Security Agency docs: UEFI Defensive Practices Guidance and unclassified UEFI Secure Boot customization
- sbkeysync & maintaining uefi key databases by Jeremy Kerr
- Secure your boot process: UEFI + Secureboot + EFISTUB + Luks2 + lvm + Arch Linux (2020-07)
- How is hibernation supported, on machines with UEFI Secure Boot? (Security StackExchange)
- Authenticated Boot and Disk Encryption on Linux by Lennart Poettering (2021-09-23)