硬體支援的 KeyStore

在晶片系統中使用受信任的執行環境 Android 裝置 (SoC) 為 Android 裝置提供了硬體支援, Android 作業系統、平台服務,甚至是 第三方應用程式。想要尋找 Android 專用擴充功能的開發人員應前往 android.security.keystore

在 Android 6.0 之前的版本中,Android 已經有採用硬體支援的簡單加密編譯機制 services API,由 Keymaster Hardware 的 0.2 和 0.3 版提供 抽象層 (HAL)。KeyStore 提供數位簽署和驗證功能 作業,以及產生與匯入非對稱式簽署金鑰組。這是 但是許多裝置上已採用不同方法,但還是有許多安全目標: 無法輕鬆只使用特徵碼 API 達成。Android 6.0 中的 KeyStore 擴充 KeyStore API 來提供更多功能。

在 Android 6.0 中新增 KeyStore 對稱密碼編譯基元 AES 和 HMAC,以及硬體支援金鑰的存取控制系統。存取權 金鑰產生期間會指定控制項,並在生命週期內強制執行 。只有在使用者俱備金鑰的情況下才能使用金鑰 且僅基於特定目的或特定加密編譯使用 參數。詳情請參閱 授權標記函式頁面。

除了擴大加密基元的範圍之外,KeyStore Android 6.0 新增下列項目:

  • 這種使用控製配置允許限制金鑰用途,以減少 因金鑰濫用而遭到入侵的風險
  • 一種存取權控管配置,可限制指定使用者的金鑰。 以及指定的時間範圍

在 Android 7.0 中,Keymaster 2 支援金鑰認證和版本 繫結。金鑰認證 提供包含金鑰詳細說明的公開金鑰憑證 以及存取權控管機制,確保金鑰 存在於安全硬體及其中 以及設定遠端驗證

版本繫結 將金鑰繫結至作業系統和修補程式等級版本。這可以確保 發現舊版本系統的弱點 TEE 軟體無法將裝置復原至有安全漏洞的版本,也無法使用金鑰 可用新版本建立此外,如果金鑰具備特定版本的金鑰 、修補程式等級和修補程式 或修補程式等級時,金鑰會先升級才能使用。 的金鑰版本已失效。升級裝置時,按下鍵盤的「ratchet」鍵 此版本會跟著裝置一起移動,但任何還原版本的裝置 這個版本會導致鍵無法使用。

在 Android 8.0 中,Keymaster 3 從舊式的 C 結構硬體轉換 從定義產生的 C++ HAL 介面的抽象層 (HAL) 新的硬體介面定義語言 (HIDL)。這項異動生效後 許多引數型別已變更 與舊型別和 HAL 結構體方法通訊。詳情請參閱 函式頁面 詳細資料。

除了這個介面修訂版本外,Android 8.0 擴充 Keymaster 2's 支援的認證功能 ID 認證。 ID 認證是一項有限制的選用機制,可以用於強力認證 例如裝置序號、產品名稱和電話 ID (IMEI / MEID)。為了實作這項加法,Android 8.0 已變更 ASN.1 來新增 ID 認證。Keymaster 實作作業需要 尋找幾種安全的方式擷取相關資料項目,並定義其定義 提供安全、永久停用功能的機制

在 Android 9 中,更新包括:

  • 更新為 Keymaster 4
  • 支援嵌入式安全元件
  • 支援安全金鑰匯入功能
  • 支援 3DES 加密
  • 變更版本繫結,讓 boot.img 和 system.img 分別設定版本來允許獨立更新

詞彙解釋

以下是 KeyStore 元件及其關係的快速總覽。

AndroidKeystore 是 Android Framework API 和元件 存取 KeyStore 功能這項擴充功能會做為擴充功能 標準 Java Cryptography Architecture API,並由 會在應用程式本身的程序空間中執行AndroidKeystore 執行應用程式 將 KeyStore 行為要求轉送至 KeyStore Daemon。

「Keystore Daemon」是一種 Android 系統 Daemon 可透過 Binder API 存取所有 KeyStore 功能。負責儲存「金鑰 blob」 包含實際的密鑰內容,因此已加密,因此 KeyStore 可儲存這些金鑰, 不會使用或洩漏

keymasterd 是 HIDL 伺服器, Keymaster TA:(這個名稱並未標準化,僅供參考)。

Keymaster TA (可信任的應用程式) 是在 安全環境,通常位於 ARM SoC 上的 TrustZone,因為提供所有 安全 KeyStore 作業、可存取原始金鑰內容、驗證所有 金鑰的存取權控管條件等等

LockSettingsService 是 Android 系統元件,負責 用於使用者驗證,包括密碼和指紋。不屬於 KeyStore,但非常實用,因為許多 KeyStore 金鑰作業需要使用者操作 驗證。LockSettingsService 與把門人員互動 TA 和指紋 TA 以取得驗證權杖,該權杖提供給 KeyStore Daemon,且最終是由 Keymaster TA 使用 應用程式。

總機人員 TA (信任的應用程式) 是另一個元件 是在安全環境中執行,所以需要對使用者進行驗證 並產生用於證明 Keymaster TA 的驗證權杖 可驗證在資料庫中的某個時間點 讓應用程式從可以最快做出回應的位置 回應使用者要求

指紋 TA (信任的應用程式) 是另一個元件 會在需要驗證使用者的安全環境中執行 指紋並產生用於向 Keymaster 證明的驗證權杖 代表特定使用者在特定時間點完成驗證 讓影片從頭到尾

建築

Android KeyStore API 和基礎 Keymaster HAL 提供一組基本但足夠的密碼編譯基元 使用存取控制、硬體支援金鑰來實作通訊協定。

Keymaster HAL 是由 OEM 提供的動態載入程式庫, 提供硬體支援的加密編譯服務。保留 HAL 實作不會在任何 使用者空間,甚至是核心空間機密作業會委派給 安全處理器可透過某些核心介面存取 產生的架構如下所示:

Keymaster 的存取權

圖 1. Keymaster 的存取權

在 Android 裝置上,「用戶端」Keymaster HAL 的組成部分 多層 (例如應用程式、架構、KeyStore Daemon) 參考範圍這表示先前介紹的 Keymaster HAL API 是低階 API,供平台內部元件使用,不會向應用程式公開 開發人員。如要瞭解更高層級的 API,請造訪 Android 開發人員網站

Keymaster HAL 的用途並不是 ,但目的只在於向安全世界發出假冒和解僱請求。 傳輸格式已定義實作。

與先前版本的相容性 版本

Keymaster 1 HAL 與 先前發布的 HAL (例如Keymaster 0.2 和 0.3。促進 在搭載 Android 5.0 以下版本、並且搭載 Android 5.0 以下版本的裝置上 舊版 Keymaster HALs 呼叫現有硬體程式庫的 Keymaster 1 HAL。結果不能 提供了 Keymaster 1 HAL 的完整功能。我們要用 僅支援 RSA 和 ECDSA 演算法,以及所有金鑰授權 只會由轉接程式在不安全的環境中執行。

Keymaster 2 透過移除 get_supported_* 方法並允許 finish() 接受輸入內容的方法。這樣可以減少 TEE 的往返次數 而且可以一次取用所有輸入內容,並簡化實作 AEAD 解密。

在 Android 8.0 中,Keymaster 3 從舊式的 C 結構升級 HAL 至 C++ HAL 介面,根據新的 硬體介面定義語言 (HIDL)。全新風格 HAL 模型的運作原理,則是將產生的 IKeymasterDevice 類別並實作純虛擬化 方法。在變更過程中,許多引數類型已變更 但類型和方法之間 以及 HAL 結構方法

HIDL 總覽

硬體介面定義語言 (HIDL) 可讓您實作 用於指定硬體介面且與語言無關的機制。HIDL 工具目前支援產生 C++ 和 Java 介面。這是正常情況 大多數受信任的執行環境 (TEE) 實作人員都會找到 C++ ,因此本文件僅討論 C++ 表示法。

HIDL 介麵包含一組方法,如下方所示:

  methodName(INPUT ARGUMENTS) generates (RESULT ARGUMENTS);

有許多預先定義的型別,HAL 可以定義新的列舉和 結構類型如要進一步瞭解 HIDL,請參閱參考資料部分

Keymaster 3 IKeymasterDevice.hal 的其中一個方法範例如下:

generateKey(vec<KeyParameter> keyParams)
        generates(ErrorCode error, vec<uint8_t> keyBlob,
                  KeyCharacteristics keyCharacteristics);

這與 keymaster2 HAL 中的值相同:

keymaster_error_t (*generate_key)(
        const struct keymaster2_device* dev,
        const keymaster_key_param_set_t* params,
        keymaster_key_blob_t* key_blob,
        keymaster_key_characteristics_t* characteristics);

在 HIDL 版本中,dev 引數會遭到移除,因為這是 隱含的params 引數不再是包含 參照 key_parameter_t 物件陣列的指標, vec (向量),包含 KeyParameter 物件。 傳回值會列在「generates」中子句,包括 鍵 blob 的 uint8_t 值向量。

HIDL 編譯器產生的 C++ 虛擬方法為:

Return<void> generateKey(const hidl_vec<KeyParameter>& keyParams,
                         generateKey_cb _hidl_cb) override;

其中 generateKey_cb 是定義如下的函式指標:

std::function<void(ErrorCode error, const hidl_vec<uint8_t>& keyBlob,
                   const KeyCharacteristics& keyCharacteristics)>

也就是說,generateKey_cb 這個函式會採用傳回值的函式 列在 generate 子句中HAL 實作類別會覆寫此設定 generateKey 方法並呼叫 generateKey_cb 函式 指標,將作業結果傳回呼叫端。註記函式 指標呼叫為同步性質。來電者 generateKeygenerateKey 會呼叫提供的 函式指標會執行至完成,並將控制項傳回 generateKey 實作,系統會接著傳回呼叫端。

如需詳細範例,請參閱 hardware/interfaces/keymaster/3.0/default/KeymasterDevice.cpp。 在預設情況下,如果裝置搭載 舊式 keymaster0、keymaster1 或 keymaster2 HALS

存取權控管

KeyStore 存取權控管最基本的規則,就是每個應用程式都具備 在自有命名空間中但每項規則都有例外狀況KeyStore 包含 硬式編碼地圖,允許特定系統元件 命名空間這是一種很豐富的樂器 完整控管其他命名空間既然如此 把這些元件當做用戶端到 KeyStore 使用目前我們無法 供應商元件 (例如 WPA 替代項目) 的命名空間。

為配合廠商元件並概略控管存取權 沒有硬式編碼例外狀況,KeyStore 2.0 導入了網域和 SELinux 命名空間

KeyStore 網域

使用 KeyStore 網域時,我們可以將命名空間與 UID 分離。客戶 存取 KeyStore 中的金鑰時必須指定網域、命名空間和別名 供他們存取的資源根據這個組合和呼叫端的 可以判斷呼叫端想要存取的金鑰,以及是否適當 授予其要求的權限。

我們推出了五個網域參數來控制金鑰的存取方式。 並控制金鑰描述元的命名空間參數語意 存取權控管的執行方式

  • DOMAIN_APP:應用程式網域涵蓋 舊版行為Java KeyStore SPI 預設會使用這個網域。當此情況 如果使用網域,命名空間引數將遭到忽略,而呼叫端的 UID 這個網域的存取權是由金鑰庫的 KeyStore 標籤所控管 SELinux 政策中的 keystore_key 類別。
  • DOMAIN_SELINUX:這個網域表示 命名空間的 SELinux 政策有標籤。命名空間參數 並轉譯成目標情境,然後對應用程式執行權限檢查 keystore_key 類別的呼叫 SELinux 結構定義。當 建立指定作業的權限,使用完整的元組 以及新增鍵查詢
  • DOMAIN_GRANT:授權網域表示 命名空間參數是授權 ID系統會忽略別名參數。 建立授予項目時,系統會執行 SELinux 檢查。進一步控管存取權 僅檢查呼叫端 UID 是否與要求的授予對象 UID 相符。
  • DOMAIN_KEY_ID:這個網域表示 「namespace」參數是不重複的金鑰 ID金鑰本身可能已建立 搭配 DOMAIN_APPDOMAIN_SELINUX。權限 檢查會在 domainnamespace 之後執行 從金鑰資料庫載入的方式與載入 blob 的方式相同 例如網域、命名空間與別名元組金鑰 ID 網域的原因 是連續性透過別名存取金鑰時,後續呼叫可能會在以下裝置上運作: 因為系統可能已產生、匯入及繫結新金鑰 。不過,這個金鑰 ID 一律不會變更。因此,在使用「依金鑰 ID」簽署金鑰時 使用別名從 KeyStore 資料庫載入後, 只要金鑰 ID 仍然存在,就表示它是同一個鍵。這個 未向應用程式開發人員提供任何功能。而是用於 Android KeyStore SPI 即使使用者正在使用應用程式,也能提供更一致的體驗 以不安全的方式並行
  • DOMAIN_BLOB:blob 網域代表 呼叫端自行管理 blob。用於需要 在掛接資料分區前存取 KeyStore。關鍵 blob 是 包含在金鑰描述元的 blob 欄位中。

使用 SELinux 網域,我們就能讓廠商元件存取 可由系統元件共用的特定 KeyStore 命名空間,例如 設定對話方塊

keystore_key 的 SELinux 政策

命名空間標籤是使用 keystore2_key_context 設定 檔案。
這些檔案中的每一行都會將數字命名空間 ID 對應至 SELinux 標籤。 例如:

# wifi_key is a keystore2_key namespace intended to be used by wpa supplicant and
# Settings to share keystore keys.
102            u:object_r:wifi_key:s0

按照這種方式設定新的金鑰命名空間後, 加入適當的政策例如, wpa_supplicant:取得及使用新命名空間中的金鑰,我們會使用 在 hal_wifi_supplicant.te 中新增下列程式碼:

allow hal_wifi_supplicant wifi_key:keystore2_key { get, use };

設定新的命名空間後,AndroidKeyStore 幾乎可以 正常工作。唯一的差別在於必須指定命名空間 ID。適用對象 將金鑰載入及匯入 KeyStore,系統便會指定命名空間 ID 使用 AndroidKeyStoreLoadStoreParameter。例如:

import android.security.keystore2.AndroidKeyStoreLoadStoreParameter;
import java.security.KeyStore;

KeyStore keystore = KeyStore.getInstance("AndroidKeyStore");
keystore.load(new AndroidKeyStoreLoadStoreParameter(102));

如要在指定命名空間中產生金鑰,必須提供命名空間 ID 使用KeyGenParameterSpec.Builder#setNamespace():

import android.security.keystore.KeyGenParameterSpec;
KeyGenParameterSpec.Builder specBuilder = new KeyGenParameterSpec.Builder();
specBuilder.setNamespace(102);

下列內容檔案可用於設定 KeyStore 2.0 SELinux 命名空間每個分區都有 10,000 個不同的命名空間保留範圍 以避免衝突。

分區 範圍 設定檔
系統 0 ... 9,999 人
/system/etc/selinux/keystore2_key_contexts, /plat_keystore2_key_contexts
延伸系統 10,000 ... 19,999
/system_ext/etc/selinux/system_ext_keystore2_key_contexts, /system_ext_keystore2_key_contexts
產品 20,000 ... 29,999
/product/etc/selinux/product_keystore2_key_contexts, /product_keystore2_key_contexts
供應商 30,000 ... 39,999
/vendor/etc/selinux/vendor_keystore2_key_contexts, /vendor_keystore2_key_contexts

用戶端要求 SELinux 網域以及所需的 SELinux 網域,以要求金鑰 虛擬命名空間 (在本例中為 "wifi_key")

在上方,已定義下列命名空間。如果用硬體取代 特殊規則,下表指出它們用來對應 UID 的 UID 。

名稱空間編號 SEPolicy 標籤 UID 說明
0 su_key 超級使用者金鑰。僅用於使用者偵錯版本和 eng 版本。非 與使用者建構相關的應用程式
1 Shell_key 殼層可用的命名空間。主要用於測試,但也可用於 透過指令列建構
100 vold_key 適用於 VPS。
101 odsing_key 供裝置端簽署 Daemon 使用。
102 wifi_key AID_WIFI(1010) 供 Android 的 Wi-Fi Sybsystem 使用,包括 wpa_supplicant。
120 Resume_on_reboot_key AID_SYSTEM(1000) Android 系統伺服器會使用這項資訊,在重新啟動後支援繼續執行。

存取向量

SELinux 類別 keystore_key 已有不少時間, 權限,例如 verifysign 已遺失 代表模型的意義以下是一組新的權限:keystore2_key。 藉此強制執行 KeyStore 2.0 版本

權限 意義
delete 從 KeyStore 移除金鑰時勾選。
get_info 在要求金鑰中繼資料時檢查。
grant 呼叫端需要這項權限,才能為目標中的金鑰建立授予項目 相關資訊
manage_blob 呼叫端可能會在指定的 SELinux 命名空間中使用 DOMAIN_BLOB。 進而自行管理 blob這項功能特別實用 伏特。
rebind 這項權限可控管別名是否能重新繫結至新金鑰。這是 必需用於插入,且表示之前繫結的鍵將為 已刪除。基本上這是一種插入權限,但會擷取語意 更值得信賴
req_forced_op 具備這項權限的用戶端可能會建立無法裁剪的作業,以及 除非所有運算單元皆使用完畢,否則作業建立作業一律不會失敗 作業。
update 必須授予權限,才能更新金鑰的子元件。
use 在使用金鑰內容建立 Keymint 作業時勾選,例如 進行簽署、en/解密
use_dev_id 產生裝置識別資訊 (例如裝置 ID) 時必須提供 認證。

此外,我們在 SELinux 安全性類別 keystore2

權限 意義
add_auth 驗證服務供應商 (例如 Gatekeeper 或 BiometricsManager) 需要 新增驗證權杖
clear_ns 這項權限原本是 clear_uid,可讓命名空間以外的擁有者執行以下動作: 刪除該命名空間中的所有索引鍵
list 系統根據各種屬性列舉金鑰時需要,例如 擁有權或驗證受限性呼叫端不需要這項權限 列舉自己的命名空間而且在這邊 get_info權限。
lock 這項權限允許鎖定 KeyStore,也就是移除主金鑰,例如 驗證繫結金鑰會變得無法使用且無法維護。
reset 這項權限可讓 KeyStore 恢復原廠設定、刪除全部 而不影響 Android 作業系統運作所需的鍵。
unlock 必須具備這項權限,才能嘗試解鎖主金鑰進行驗證 範圍。