

本文為英文版的機器翻譯版本，如內容有任何歧義或不一致之處，概以英文版為準。

# 使用自訂 UEFI 安全開機金鑰建立 Linux AMI
<a name="create-ami-with-uefi-secure-boot"></a>

這些說明向您展示了如何使用 UEFI 安全開機和自訂私有金鑰來建立 Linux AMI。Amazon Linux 從 AL2023 2023.1 版起開始支援 UEFI 安全開機。如需詳細資訊，請參閱 *Amazon Linux 2023 使用者指南*中的 [AL2023 的 UEFI 安全啟動](https://docs.aws.amazon.com/linux/al2023/ug/uefi-secure-boot.html)。

**重要**  
以下程序**僅適用於進階使用者**。您必須具備充分的 SSL 和 Linux 發行版本開機流程的知識才能使用這些程序。

**先決條件**
+ 將會使用下列工具：
  + OpenSSL - [https://www.openssl.org/](https://www.openssl.org/)
  + efivar - [https://github.com/rhboot/efivar](https://github.com/rhboot/efivar)
  + efitools - [https://git.kernel.org/pub/scm/linux/kernel/git/jejb/efitools.git/](https://git.kernel.org/pub/scm/linux/kernel/git/jejb/efitools.git/)
  + [get-instance-uefi-data](https://docs.aws.amazon.com/cli/latest/reference/ec2/get-instance-uefi-data.html) 命令
+ 您的 Linux 執行個體必須使用支援 UEFI 開機模式的 Linux AMI 啟動，並且有非揮發性資料存在。

沒有 UEFI 安全開機金鑰的新建立執行個體是在 `SetupMode` 中建立，允許您註冊自己的金鑰。某些 AMI 預先設定為具有 UEFI 安全開機功能，您無法變更現有金鑰。如果您想要變更金鑰，您必須根據原始 AMI 建立新 AMI。

您有兩種在變數存放區傳播金鑰的方法，這些方法在接下來的選項 A 和選項 B 中說明。選項 A 說明如何在執行個體中執行此操作，並模仿真實硬體的流程。選項 B 說明如何建立二進位檔案 Blob，其隨後會在建立 AMI 時作為 base64 編碼的檔案傳遞。對於這兩個選項，您必須先建立用於信任鏈的三對金鑰對。

**Topics**
+ [任務 1：建立金鑰對](#uefi-secure-boot-create-three-key-pairs)
+ [任務 2 – 選項 A：從執行個體中將金鑰新增至變數存放區](#uefi-secure-boot-optionA)
+ [任務 2 – 選項 B：建立一個包含預先填寫的變數存放區的二進位 blob](#uefi-secure-boot-optionB)

## 任務 1：建立金鑰對
<a name="uefi-secure-boot-create-three-key-pairs"></a>

UEFI 安全開機基於下列用於信任鏈的三個金鑰資料庫：平台金鑰 (PK)、金鑰交換金鑰 (KEK) 和簽章資料庫 (db)。¹

您需在執行個體上建立每個金鑰。若要以對 UEFI 安全開機標準有效的格式準備公有金鑰，請為每個金鑰建立一個憑證。`DER` 定義 SSL 格式 (格式的二進位編碼)。然後，您可以將每個憑證轉換為 UEFI 簽章清單，該清單為 UEFI 安全開機中所理解的二進位格式。最後，您需使用相關金鑰簽署每個憑證。

**Topics**
+ [準備建立金鑰對](#uefisb-prepare-to-create-key-pairs)
+ [金鑰對 1：建立平台金鑰 (PK)](#uefisb-create-key-pair-1)
+ [金鑰對 2：建立金鑰交換金鑰 (KEK)](#uefisb-create-key-pair-2)
+ [金鑰對 3：建立簽章資料庫 (db)](#uefisb-create-key-pair-3)
+ [使用私有金鑰簽署開機映像 (核心)](#uefi-secure-boot-sign-kernel)

### 準備建立金鑰對
<a name="uefisb-prepare-to-create-key-pairs"></a>

建立金鑰對之前，請建立一個用於金鑰產生的全域唯一識別符 (GUID)。

1. [連線到執行個體。](connect.md)

1. 在 shell 提示中執行下列命令。

   ```
   uuidgen --random > GUID.txt
   ```

### 金鑰對 1：建立平台金鑰 (PK)
<a name="uefisb-create-key-pair-1"></a>

PK 是 UEFI 安全開機執行個體的信任根。私有 PK 用於更新 KEK，其又可接續用於將授權的金鑰新增至簽章資料庫 (db)。

X.509 標準用於建立金鑰對。如需有關標準的資訊，請參閱*維基百科*上的 [X.509](https://en.wikipedia.org/wiki/X.509)。

**建立 PK**

1. 建立金鑰。您必須將變數命名為 `PK`。

   ```
   openssl req -newkey rsa:4096 -nodes -keyout PK.key -new -x509 -sha256 -days 3650 -subj "/CN={{Platform key}}/" -out PK.crt
   ```

   指定了下列參數：
   + `-keyout PK.key` - 私有金鑰檔案。
   + `-days 3650` - 憑證有效的天數。
   + `-out PK.crt` - 用於建立 UEFI 變數的憑證。
   + `CN={{Platform key}}` - 金鑰的通用名稱 (CN)。您可以輸入自己的組織名稱，而不是{{平台金鑰}}。

1. 建立憑證。

   ```
   openssl x509 -outform DER -in PK.crt -out PK.cer
   ```

1. 將憑證轉換至 UEFI 簽章清單。

   ```
   cert-to-efi-sig-list -g "$(< GUID.txt)" PK.crt PK.esl
   ```

1. 使用私有 PK (自我簽署) 簽署 UEFI 簽章清單。

   ```
   sign-efi-sig-list -g "$(< GUID.txt)" -k PK.key -c PK.crt PK PK.esl PK.auth
   ```

### 金鑰對 2：建立金鑰交換金鑰 (KEK)
<a name="uefisb-create-key-pair-2"></a>

私有 KEK 用於將金鑰新增至 db，這是要在系統上開機使用的授權簽章清單。

**建立 KEK**

1. 建立金鑰。

   ```
   openssl req -newkey rsa:4096 -nodes -keyout KEK.key -new -x509 -sha256 -days 3650 -subj "/CN=Key Exchange Key/" -out KEK.crt
   ```

1. 建立憑證。

   ```
   openssl x509 -outform DER -in KEK.crt -out KEK.cer
   ```

1. 將憑證轉換至 UEFI 簽章清單。

   ```
   cert-to-efi-sig-list -g "$(< GUID.txt)" KEK.crt KEK.esl
   ```

1. 使用私有 PK 簽署簽章清單。

   ```
   sign-efi-sig-list -g "$(< GUID.txt)" -k PK.key -c PK.crt KEK KEK.esl KEK.auth
   ```

### 金鑰對 3：建立簽章資料庫 (db)
<a name="uefisb-create-key-pair-3"></a>

db 清單包含授權在系統上開機的授權金鑰。必須使用私有 KEK 才能修改清單。將使用在此步驟中建立的私有金鑰簽署開機映像。

**建立 db**

1. 建立金鑰。

   ```
   openssl req -newkey rsa:4096 -nodes -keyout db.key -new -x509 -sha256 -days 3650 -subj "/CN=Signature Database key/" -out db.crt
   ```

1. 建立憑證。

   ```
   openssl x509 -outform DER -in db.crt -out db.cer
   ```

1. 將憑證轉換至 UEFI 簽章清單。

   ```
   cert-to-efi-sig-list -g "$(< GUID.txt)" db.crt db.esl
   ```

1. 使用私有 KEK 簽署簽章清單。

   ```
   sign-efi-sig-list -g "$(< GUID.txt)" -k KEK.key -c KEK.crt db db.esl db.auth
   ```

### 使用私有金鑰簽署開機映像 (核心)
<a name="uefi-secure-boot-sign-kernel"></a>

若為 Ubuntu 22.04，下列映像需要簽章。

```
/boot/efi/EFI/ubuntu/shimx64.efi
/boot/efi/EFI/ubuntu/mmx64.efi
/boot/efi/EFI/ubuntu/grubx64.efi
/boot/vmlinuz
```

**簽署映像**  
使用下列語法簽署映像。

```
sbsign --key db.key --cert db.crt --output {{/boot/vmlinuz}} {{/boot/vmlinuz}}
```

**注意**  
您必須簽署所有新核心。{{`/boot/vmlinuz`}} 通常會以符號連結到上次安裝的核心。

請參閱您的發行版本說明文件，了解您的開機鏈和所需映像。

¹ 感謝 ArchWiki 社群所做的所有工作。用於建立 PK、建立 KEK、建立 DB 和簽署映像的命令來自 [Creating keys](https://wiki.archlinux.org/title/Unified_Extensible_Firmware_Interface/Secure_Boot#Creating_keys) (建立金鑰)，該文章由 ArchWiki 維護團隊和/或 ArchWiki 貢獻者撰寫。

## 任務 2 – 選項 A：從執行個體中將金鑰新增至變數存放區
<a name="uefi-secure-boot-optionA"></a>

建立[三個金鑰對](#uefi-secure-boot-create-three-key-pairs)後，您即可以連接到執行個體，並透過完成以下步驟從執行個體中將金鑰新增至變數存放區。或者，完成 [任務 2 – 選項 B：建立一個包含預先填寫的變數存放區的二進位 blob](#uefi-secure-boot-optionB) 中的步驟。

**Topics**
+ [步驟 1：啟動將支援 UEFI 安全開機的執行個體](#step1-launch-uefi-sb)
+ [步驟 2：設定執行個體以支援 UEFI 安全開機](#step2-launch-uefi-sb)
+ [步驟 3：從執行個體建立 AMI](#step3-launch-uefi-sb)

### 步驟 1：啟動將支援 UEFI 安全開機的執行個體
<a name="step1-launch-uefi-sb"></a>

當您[啟動執行個體](LaunchingAndUsingInstances.md) (具有下列先決條件) 時，執行個體將準備好設定為支援 UEFI 安全開機。您只能在啟動時在執行個體上啟用對 UEFI 安全開機的支援；以後無法啟用它。

**先決條件**
+ **AMI** - Linux AMI 必須支援 UEFI 開機模式。若要驗證 AMI 是否支援 UEFI 開機模式，AMI 開機模式參數必須為 **uefi**。如需詳細資訊，請參閱[確定 Amazon EC2 AMI 的開機模式參數](ami-boot-mode.md)。

  請注意， AWS 僅提供設定為支援 Graviton 型執行個體類型的 UEFI 的 Linux AMIs。 AWS 目前不提供支援 UEFI 開機模式的 x86\_64 Linux AMIs。您可以設定自有的 AMI，以支援所有架構的 UEFI 開機模式。若要設定支援 UEFI 開機模式的 AMI，必須在自有的 AMI 上執行多個設定步驟。如需詳細資訊，請參閱[設定 Amazon EC2 AMI 的開機模式](set-ami-boot-mode.md)。
+ **執行個體類型** - 所有支援 UEFI 的虛擬化執行個體類型也支援 UEFI 安全開機。裸機執行個體類型不支援 UEFI 安全開機。如需有關支援 UEFI 安全開機的執行個體類型詳細資訊，請參閱[UEFI 開機模式需求](launch-instance-boot-mode.md)。
+ 在發佈 UEFI 安全開機後啟動您的執行個體。只有在 2022 年 5 月 10 日 (發佈 UEFI 安全開機時) 後啟動的執行個體才能支援 UEFI 安全開機。

啟動執行個體後，您可以透過檢查 UEFI 資料是否存在，驗證執行個體是否已準備好設定為支援 UEFI 安全開機 (換句話說，您可以繼續執行[步驟 2](#step2-launch-uefi-sb))。UEFI 資料的存在表示非揮發性資料是持續的。

**驗證執行個體是否準備好執行步驟 2**  
使用 [https://docs.aws.amazon.com/cli/latest/reference/ec2/get-instance-uefi-data.html](https://docs.aws.amazon.com/cli/latest/reference/ec2/get-instance-uefi-data.html) 命令並指定執行個體 ID。

```
aws ec2 get-instance-uefi-data --instance-id {{i-1234567890abcdef0}}
```

如果輸出中存在 UEFI 資料，則執行個體已準備好執行步驟 2。如果輸出為空白，則無法將執行個體設定為支援 UEFI 安全開機。如果您的執行個體在 UEFI 安全開機支援可用之前啟動，則會發生這種情況。啟動新的執行個體，然後再試一次。

### 步驟 2：設定執行個體以支援 UEFI 安全開機
<a name="step2-launch-uefi-sb"></a>

#### 在執行個體上的 UEFI 變數存放區中註冊金鑰對
<a name="step2a-launch-uefi-sb"></a>

**警告**  
您必須在註冊金鑰*之後*簽署開機映像，否則您將無法啟動執行個體。

建立已簽署的 UEFI 簽章清單 (`PK`、`KEK` 以及`db`) 後，必須將他們註冊至 UEFI 韌體。

只有在以下情況下才能寫入 `PK` 變數：
+ 如果 `SetupMode` 變數為 `1`，表示尚未註冊 PK。透過以下命令來檢查這一點：輸出為 `1` 或 `0`。

  ```
  efivar -d -n 8be4df61-93ca-11d2-aa0d-00e098032b8c-SetupMode 
  ```
+ 新 PK 由現有 PK 的私有金鑰簽署。

**在 UEFI 變數存放區中註冊金鑰**  
必須在執行個體上執行以下命令。

如果啟用了設定模式 (值為 `1`)，則可以在執行個體上執行下列命令來註冊金鑰：

```
[ec2-user ~]$ efi-updatevar -f db.auth db
```

```
[ec2-user ~]$ efi-updatevar -f KEK.auth KEK
```

```
[ec2-user ~]$ efi-updatevar -f PK.auth PK
```

**驗證 UEFI 安全開機是否已啟用**  
若要驗證 UEFI 安全開機是否已啟用，請依照 [驗證 Amazon EC2 執行個體是否已啟用 UEFI 安全開機](verify-uefi-secure-boot.md) 中的步驟執行動作。

現在，您可以使用 [https://docs.aws.amazon.com/cli/latest/reference/ec2/get-instance-uefi-data.html](https://docs.aws.amazon.com/cli/latest/reference/ec2/get-instance-uefi-data.html) CLI 命令匯出 UEFI 變數存放區，或者繼續執行下一個步驟．簽署開機映像以重新開機到已啟用 UEFI 安全開機的執行個體。

### 步驟 3：從執行個體建立 AMI
<a name="step3-launch-uefi-sb"></a>

若要從執行個體建立 AMI，您可以使用主控台或 `CreateImage` API、CLI 或 SDK。如需主控台的說明，請參閱 [建立 Amazon EBS 支援的 AMI](creating-an-ami-ebs.md)。如需 API 的說明，請參閱 [CreateImage](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_CreateImage.html)。

**注意**  
`CreateImage` API 會自動將執行個體的 UEFI 變數存放區複製到 AMI。控制台使用 `CreateImage` API。使用此 AMI 啟動執行個體後，執行個體將具有相同的 UEFI 變數存放區。

## 任務 2 – 選項 B：建立一個包含預先填寫的變數存放區的二進位 blob
<a name="uefi-secure-boot-optionB"></a>

建立[三個金鑰對](#uefi-secure-boot-create-three-key-pairs)之後，您可以建立一個包含預先填寫的變數存放區 (包含 UEFI 安全開機金鑰) 的二進位 blob。或者，完成 [任務 2 – 選項 A：從執行個體中將金鑰新增至變數存放區](#uefi-secure-boot-optionA) 中的步驟。

**警告**  
您必須在註冊金鑰*之前*簽署開機映像，否則您將無法啟動執行個體。

**Topics**
+ [步驟 1：建立新的變數存放區或更新現有變數存放區](#uefi-secure-boot-create-or-update-variable)
+ [步驟 2：在建立 AMI 時上傳二進位 blob](#uefi-secure-boot-upload-binary-blob-on-ami-creation)

### 步驟 1：建立新的變數存放區或更新現有變數存放區
<a name="uefi-secure-boot-create-or-update-variable"></a>

您可以使用 python-uefivars 工具，在沒有執行個體執行的情況下*離線*建立變數存放區。該工具可以從您的金鑰建立一個新的變數存放區。指令碼目前支援 EDK2 格式、 AWS 格式和 JSON 表示法，以便使用高階工具進行編輯。

**在沒有執行個體執行的情況下離線建立變數存放區**

1. 在下列連結下載該工具。

   ```
   https://github.com/awslabs/python-uefivars
   ```

1. 通過執行下列命令從您的金鑰建立新的變數存放區。這將在 {{your\_binary\_blob}}.bin 中建立 base64 編碼二進位 blob。該工具還支援透過 `-I` 參數更新二進位 blob。

   ```
   ./uefivars.py -i none -o aws -O {{your_binary_blob}}.bin -P PK.esl -K KEK.esl --db db.esl --dbx dbx.esl
   ```

### 步驟 2：在建立 AMI 時上傳二進位 blob
<a name="uefi-secure-boot-upload-binary-blob-on-ami-creation"></a>

使用 [https://docs.aws.amazon.com/cli/latest/reference/ec2/register-image.html](https://docs.aws.amazon.com/cli/latest/reference/ec2/register-image.html) 傳遞您的 UEFI 變數存放區資料。對於 `--uefi-data` 參數，請指定您的二進位 blob，而對於 `--boot-mode` 參數，則指定 `uefi`。

```
aws ec2 register-image \
    --name uefi_sb_tpm_register_image_test \
    --uefi-data $(cat {{your_binary_blob}}.bin) \
    --block-device-mappings "DeviceName=/dev/sda1,Ebs= {SnapshotId={{snap-0123456789example}},DeleteOnTermination=true}" \
    --architecture x86_64 \
    --root-device-name /dev/sda1 \
    --virtualization-type hvm \
    --ena-support \
    --boot-mode uefi
```