UEFI/安全啟動

出自 Arch Linux 中文维基

Secure BootUEFI標準中的一項安全功能,旨在為pre-boot process添加一層保護;通過維護被授權或禁止的在啟動時運行的經過加密簽署的二進制文件列表,它有助於使得核心引導組件(引導管理器、內核、initramfs)不被篡改。

因此,它可以被視為對個人使用環境的securing所做努力的延續或補充,減少其他軟件安全解決方案(如系統加密)無法輕易覆蓋的攻擊面,同時完全不同且不依賴於它們。安全啟動只是作為當前安全實踐的一個組成部分獨立存在,具有自己的一套優缺點

注意: 為了更深入地了解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)。 要成功啟動安裝介質,您需要#禁用安全啟動

安全啟動支持最初是在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 的禁用安全啟動

重新製作安裝鏡像[編輯 | 編輯原始碼]

這篇文章的某些內容需要擴充。

原因: 增加明確的說明。 (在 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了。您應該閱讀其他文章,例如UEFI#從 ISO 創建 UEFI 可啟動 USB,了解如何處理這種情況。

實施安全啟動[編輯 | 編輯原始碼]

為了確保理想的安全啟動設置,有以下幾個特定條件:

  1. UEFI基本被認為是可信的(儘管有一些眾所周知的批評和漏洞[2]),且必須被強密碼保護
  2. 不使用默認的製造商或第三方密鑰,因為它們被證明會大大削弱安全啟動的安全模型[3]
  3. UEFI直接讀取帶有EFISTUB英語EFISTUB的內核鏡像(而不是引導加載程序),包含微碼(如果適用)以及 initramfs。以便在整個啟動過程維持安全啟動建立的信任鏈並減少攻擊面。
  4. 使用完整驅動器加密,這樣在創建內核鏡像和簽署的過程中所涉及的工具與文件就無法被具有機器物理訪問權限的人訪問並篡改。
  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英語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[編輯 | 編輯原始碼]

這篇文章的某些內容需要擴充。

原因: 是否有可能通過用自定義密鑰簽署其啟動程序來啟動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) [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[編輯 | 編輯原始碼]

這篇文章的某些內容需要擴充。

原因: 需要進一步測試。 (在 Talk:UEFI/安全啟動#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[編輯 | 編輯原始碼]
提示:rEFInd引導程序中的refind-install腳本可以簽署rEFInd的EFI二進制文件並與shim和MOK證書一併複製到ESP中。詳情見rEFInd#使用 shim

安裝shim-signedAUR.

重命名當前的引導加載程序grubx64.efi

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

複製shimMokManager到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

複製MOK.cer到一個FAT格式的文件系統(你可以使用EFI 系統分區)。

重新啟動並啟用「安全啟動」。如果shim未能找到在MokList中找到簽署的grubx64.efi證書,它將啟動MokManager(mmx64.efi)。

MokManager中選擇Enroll key from disk,找到MOK.cer並把它添加到MokList。完成之後,選擇Continue boot,你的引導加載程序便會啟動並成功加載任何由你的機器所有者密鑰簽署的二進制文件。

shim與密鑰和GRUB[編輯 | 編輯原始碼]

這篇文章的某些內容需要擴充。

原因: GRUB#UEFI 系統提到了一個叫做grub-mkstandalone的工具,但是這裡並沒有出現。 (在 Talk:UEFI/安全啟動 中討論)

本文或本章節可能需要合併到GRUB

附註: 關於構建自己的EFI二進制文件並選擇模塊或許應該放進GRUB頁面。(在 Talk:UEFI/安全啟動 中討論)

只有GRUB的EFI二進制文件包含所有能夠讀取vmlinuzinitramfs鏡像所在文件系統的模塊,才能在安全啟動模式下成功啟動。

從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)與可信平台模塊英語Trusted Platform Module
  • 「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,移除被複製的shimMokManager文件並重命名為你的引導加載程序默認名。

保護安全啟動[編輯 | 編輯原始碼]

防止有物理訪問權限的人禁用安全啟動的唯一方法是用密碼保護固件設置。

大多數UEFI固件提供這樣的功能,通常列在固件設置中的「安全」部分。

提示與技巧[編輯 | 編輯原始碼]

sbctl[編輯 | 編輯原始碼]

sbctl是一種用戶友好的設置安全啟動和簽署文件方式。

注意: sbctl並不能在所有的硬件上工作。它的實際工作效果取決於製造商。

創建並登記密鑰[編輯 | 編輯原始碼]

在開始之前,進入你的固件設置將安全啟動模式設置為設置模式。每個設備都有所不同。

當你重新登錄後,檢查安全啟動的狀態:

$ sbctl status

你應當看到sbctl並未安裝以及安全啟動已關閉。

接下來創建你的自定義安全啟動密鑰:

# sbctl create-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啟用了systemd-boot-update.service,那麼引導加載程序只會在重啟後升級,導致sbctl的Pacman鈎子因此而不簽署新的文件。 變通的方法是直接在/usr/lib/中小心的簽署引導加載程序,這樣bootctl installupdate將會自動識別並複製.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.EFIBOOTIA32.EFI)、內核與UEFI Sheel,並對它們進行簽署,然後用被簽署的文件重新打包ISO。

安裝libisoburnmtoolssbsigntools

首先解壓出相關的文件以及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文件。

另見[編輯 | 編輯原始碼]