硬件支持的密鑰庫

片上系統 (SoC) 中可信執行環境的可用性為 Android 設備提供了向 Android 操作系統、平台服務甚至第三方應用程序提供硬件支持的強大安全服務的機會。開發商尋求Android的特定擴展應該去android.security.keystore

在 Android 6.0 之前,Android 已經有一個簡單的、硬件支持的加密服務 API,由 Keymaster 硬件抽象層 (HAL) 的 0.2 和 0.3 版提供。 Keystore 提供數字簽名和驗證操作,以及非對稱簽名密鑰對的生成和導入。這已經在許多設備上實現了,但是有許多安全目標無法僅通過簽名 API 輕鬆實現。 Android 6.0 中的 Keystore 擴展了 Keystore API 以提供更廣泛的功能。

在的Android 6.0,密鑰存儲添加對稱密碼原語,AES和HMAC,以及用於硬件支持的密鑰的訪問控制系統。訪問控制在密鑰生成期間指定並在密鑰的生命週期內強制執行。可以將密鑰限制為僅在用戶通過身份驗證後可用,並且僅用於指定的目的或具有指定的加密參數。欲了解更多信息,請參閱授權標籤功能的網頁。

除了擴展加密原語的範圍外,Android 6.0 中的 Keystore 還添加了以下內容:

  • 一種允許限制密鑰使用的使用控制方案,以減輕由於濫用密鑰而導致的安全風險
  • 一種訪問控制方案,可將密鑰限制為指定用戶、客戶端和定義的時間範圍

在 Android 7.0 中,Keymaster 2 添加了對密鑰證明和版本綁定的支持。主要認證提供包括鍵和訪問控制的詳細描述公鑰證書,使該鍵的存在在安全硬件和配置遠程驗證。

版本綁定結合鍵,操作系統和補丁級別版本。這可確保發現舊版本系統或 TEE 軟件弱點的攻擊者無法將設備回滾到易受攻擊的版本並使用新版本創建的密鑰。此外,當在已升級到更新版本或補丁級別的設備上使用具有給定版本和補丁級別的密鑰時,該密鑰先升級後才能使用,並且先前版本的密鑰失效。隨著設備的升級,密鑰與設備一起“棘輪”前進,但任何將設備恢復到先前版本都會導緻密鑰無法使用。

在 Android 8.0 中,Keymaster 3 從舊式 C 結構硬件抽象層 (HAL) 過渡到根據新硬件接口定義語言 (HIDL) 中的定義生成的 C++ HAL 接口。作為更改的一部分,許多參數類型發生了變化,儘管類型和方法與舊類型和 HAL 結構方法具有一一對應的關係。查看功能的更多詳細信息頁面。

除了這個接口版本,安卓8.0擴展鑰匙大師2的認證功能,支持ID認證。 ID 證明提供了一種有限且可選的機制,用於強證明硬件標識符,例如設備序列號、產品名稱和電話 ID (IMEI / MEID)。要實現此添加,請更改 ASN.1 證明模式以添加 ID 證明。 Keymaster 實現需要找到一些安全的方法來檢索相關數據項,並定義一種機制來安全和永久地禁用該功能。

在 Android 9 中,更新包括:

  • 更新到鑰匙大師4
  • 支持嵌入式安全元件
  • 支持安全密鑰導入
  • 支持 3DES 加密
  • 更改版本綁定,使 boot.img 和 system.img 分別設置版本以允許獨立更新

詞彙表

以下是 Keystore 組件及其關係的快速概覽。

AndroidKeystore是Android框架API和組件使用由應用程序訪問的密鑰庫功能。它是作為標準 Java 密碼體系結構 API 的擴展實現的,由在應用程序自己的進程空間中運行的 Java 代碼組成。 AndroidKeystore由他們轉發到密鑰庫守護滿足了密鑰庫行為的應用程序的請求。

密鑰庫守護進程是一個Android系統守護進程,其提供經由訪問所有密鑰存儲功能粘合劑API 。它負責存儲“密鑰塊”,其中包含實際的密鑰材料,加密後 Keystore 可以存儲它,但不能使用它或洩露它。

keymasterd是HIDL服務器,提供了訪問鑰匙之王TA。 (此名稱未標準化,僅用於概念目的。)

鑰匙大師TA(受信任的應用程序)是在ARM的SoC在一個安全的環境中運行,最經常的TrustZone軟件,提供所有安全密鑰存儲操作,先後獲得原始密鑰材料,驗證所有的上鍵訪問控制條件, 等等。

LockSettingsService負責用戶身份驗證,密碼和指紋Android系統的組成部分。它不是 Keystore 的一部分,但相關,因為許多 Keystore 密鑰操作需要用戶身份驗證。 LockSettingsService與關守TA和指紋TA交互以獲得認證令牌,其提供給密鑰庫守護進程,並且最終由鑰匙大師TA應用消耗。

關守TA(可信應用)是在所述安全上下文,它負責認證用戶的密碼,並生成認證令牌用來證明到鑰匙大師TA為認證是在特定時間點為特定的用戶進行運行的另一個組件。

指紋TA(可信應用)是在所述安全上下文,其是負責認證用戶的指紋和產生認證令牌用來證明到鑰匙大師TA為認證是在特定時間點為特定的用戶進行運行的另一個組件。

建築學

Android Keystore API 和底層 Keymaster HAL 提供了一組基本但足夠的加密原語,以允許使用訪問控制、硬件支持的密鑰實現協議。

Keymaster HAL 是 OEM 提供的、可動態加載的庫,Keystore 服務使用它來提供硬件支持的加密服務。為了確保安全,HAL 實現不會在用戶空間甚至內核空間中執行任何敏感操作。敏感操作委託給通過某些內核接口訪問的安全處理器。最終的架構如下所示:

訪問 Keymaster

圖1.訪問鑰匙大師

在 Android 設備中,Keymaster HAL 的“客戶端”由多個層(例如應用程序、框架、Keystore 守護程序)組成,但在本文檔中可以忽略這些。這意味著所描述的 Keymaster HAL API 是低級的,由平台內部組件使用,並且不會向應用程序開發人員公開。更高級別的API上所描述的Android開發者網站

Keymaster HAL 的目的不是實現安全敏感算法,而只是對安全世界的請求進行編組和解組。有線格式是實現定義的。

與以前版本的兼容性

Keymaster 1 HAL 與之前發布的 HAL(例如 Keymaster 0.2 和 0.3)完全不兼容。為了促進運行 Android 5.0 及更早版本並使用舊 Keymaster HAL 啟動的設備的互操作性,Keystore 提供了一個適配器,該適配器通過調用現有硬件庫來實現 Keymaster 1 HAL。結果無法提供 Keymaster 1 HAL 中的全部功能。特別是,它僅支持 RSA 和 ECDSA 算法,並且在非安全世界中,所有密鑰授權執行都由適配器執行。

鑰匙大師2通過去除進一步簡化了HAL接口get_supported_*的方法和允許finish()方法來接受輸入。在輸入一次可用的情況下,這減少了到 TEE 的往返次數,並簡化了 AEAD 解密的實現。

在 Android 8.0 中,Keymaster 3 從舊式 C 結構 HAL 過渡到根據新硬件接口定義語言 (HIDL) 中的定義生成的 C++ HAL 接口。一種新的風格的HAL實現通過繼承產生的創建IKeymasterDevice類並實現純虛方法。作為更改的一部分,許多參數類型已更改,但類型和方法與舊類型和 HAL 結構方法具有一一對應的關係。

HIDL 概述

硬件接口定義語言 (HIDL) 提供了一種獨立於實現語言的機制來指定硬件接口。 HIDL 工具目前支持生成 C++ 和 Java 接口。預計大多數可信執行環境 (TEE) 實現者會發現 C++ 工具更方便,因此本文檔僅討論 C++ 表示。

HIDL 接口由一組方法組成,表示為:

  methodName(INPUT ARGUMENTS) generates (RESULT ARGUMENTS);

有多種預定義類型,HAL 可以定義新的枚舉和結構類型。有關HIDL更多詳細信息,請參閱參考部分

從鑰匙之王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 ”條款,其中包括的向量uint8_t為密鑰blob值。

HIDL編譯器生成的C++虛方法是:

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

其中generate_cb是一個函數指針定義為:

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

也就是說, generate_cb是一個函數,它的產生子句中列出的返回值。該HAL實現類覆蓋此generateKey方法,並調用generate_cb函數指針返回操作的結果給調用者。注意函數指針調用是同步的。呼叫者呼叫generateKeygenerateKey調用提供的函數指針,該單元執行到結束,控制返回給generateKey執行,然後返回到調用者。

有關詳細示例,請參見在默認實現hardware/interfaces/keymaster/3.0/default/KeymasterDevice.cpp 。默認實現為具有舊式 keymaster0、keymaster1 或 keymaster2 HALS 的設備提供向後兼容性。

訪問控制

Keystore 訪問控制最基本的規則是每個應用程序都有自己的命名空間。但是對於每個規則,都有一個例外。 Keystore 有一些硬編碼映射,允許某些系統組件訪問某些其他名稱空間。這是一個非常生硬的工具,因為它讓一個組件完全控制另一個命名空間。然後是供應商組件作為 Keystore 的客戶端的問題。我們目前無法為供應商組件(例如 WPA 請求方)建立命名空間。

為了適應供應商組件並在沒有硬編碼異常的情況下推廣訪問控制,Keystore 2.0 引入了域和 selinux 命名空間。

密鑰庫域

使用 Keystore 域,我們可以將命名空間與 uid 分離。訪問密鑰庫中的密鑰的客戶端必須指定他們想要訪問的域、命名空間和別名。基於這個元組和調用者的身份,我們可以確定調用者想要訪問哪個鍵以及它是否具有適當的權限。

我們介紹了五個域參數,這些參數控制如何訪問密鑰。它們控制密鑰描述符的命名空間參數的語義以及如何執行訪問控制。

  • DOMAIN_APP :應用程序域涵蓋了傳統行為。 Java Keystore SPI 默認使用這個域。使用此域時,將忽略命名空間參數,而使用調用者的 uid。該域名的訪問是由密鑰庫標籤類控制keystore_key在SELinux策略。
  • DOMAIN_SELINUX :此域表示該命名空間在SELinux策略的標籤。命名空間參數看,並翻譯為目標上下文,並為呼叫的SELinux執行權限檢查keystore_key類。為給定操作建立權限後,將使用完整元組進行鍵查找。
  • DOMAIN_GRANT :補助金域表示該命名空間參數是一個授權標識符。別名參數被忽略。在創建授權時執行 Selinux 檢查。進一步的訪問控制只檢查調用者 uid 是否與請求授權的被授予者 uid 匹配。
  • DOMAIN_KEY_ID :此域表示該命名空間參數是一個獨特的密鑰ID。密鑰本身可能已與創建DOMAIN_APPDOMAIN_SELINUX 。在之後進行的權限檢查domainnamespace已經從密鑰數據庫以同樣的方式加載,如果斑是由域命名空間和別名元組加載。密鑰 id 域的基本原理是連續性。當通過別名訪問密鑰時,後續調用可能對不同的密鑰進行操作,因為可能已經生成或導入了新密鑰並綁定到該別名。然而,密鑰 id 永遠不會改變。因此,當使用別名從密鑰庫數據庫加載一次密鑰後,通過密鑰 ID 使用密鑰時,只要密鑰 ID 仍然存在,就可以確定它是相同的密鑰。此功能不會向應用程序開發人員公開,而是在 Android Keystore SPI 中使用,以提供更一致的體驗,即使以不安全的方式同時使用也是如此。
  • DOMAIN_BLOB :團塊域指示調用自身管理斑點。這用於需要在掛載數據分區之前訪問密鑰庫的客戶端。將密鑰塊包含在blob的關鍵描述符的領域。

使用 SELinux 域,我們可以讓供應商組件訪問非常特定的密鑰庫命名空間,這些命名空間可由系統組件(例如設置對話框)共享。

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_keys:keystore2_key { get, use };

設置好新的命名空間後,AndroidKeyStore 幾乎可以照常使用了。唯一的區別是必須指定命名空間 ID。從進入密鑰庫加載和導入密鑰,名稱空間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 個命名空間 ID,以避免衝突。

劃分範圍配置文件
系統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的域和所需的虛擬命名空間,這裡的關鍵"wifi_keys"它的數字ID。

在此之上定義了以下命名空間。在它們替換特殊規則的地方,下表指出了它們用來對應的 UID。

命名空間 ID SE政策標籤用戶標識描述
0 su_key不適用超級用戶密鑰。僅用於測試 userdebug 和 eng 構建。與用戶構建無關。
1 shell_key不適用shell 可用的命名空間。主要用於測試,但也可以從命令行用於用戶構建。
100 vold_key不適用供vold 使用。
101 odsing_key不適用由設備上的簽名守護程序使用。
102 wfi_key AID_WIFI(1010)由 Android 的 Wifi sybsystem 使用,包括 wpa_supplicant。
120 resume_on_reboot_key AID_SYSTEM(1000)由 Android 的系統服務器用於支持重啟時恢復。

訪問向量

SELinux的類keystore_key已經老化了不少和某些權限,如verifysign都失去了意義。下面是一組新的權限,的keystore2_key ,密鑰庫2.0將強制執行。

允許意義
delete從密鑰庫中刪除密鑰時進行檢查。
get_info在請求密鑰的元數據時檢查。
grant調用者需要此權限才能在目標上下文中創建對密鑰的授權。
manage_blob呼叫者可以使用DOMAIN_BLOB給定的SELinux的命名空間,從而通過自身管理的斑點。這對vold特別有用。
rebind此權限控制別名是否可以重新綁定到新密鑰。這是插入所必需的,意味著之前綁定的密鑰將被刪除。它基本上是一個插入權限,但它更好地捕獲了密鑰庫的語義。
req_forced_op擁有此權限的客戶端可以創建不可修剪的操作,並且除非所有操作槽都被不可修剪的操作佔用,否則操作創建永遠不會失敗。
update需要更新密鑰的子組件。
use在創建使用密鑰材料的 Keymint 操作時檢查,例如,用於簽名、加密/解密。
use_dev_id生成設備標識信息(例如設備 ID 證明)時需要。

此外,我們打出了一套在SELinux安全類非關鍵特定密鑰庫的權限keystore2

允許意義
add_auth身份驗證提供程序(例如 Gatekeeper 或 BiometricsManager)需要添加身份驗證令牌。
clear_ns以前是 clear_uid,此權限允許命名空間的非所有者刪除該命名空間中的所有鍵。
list系統需要通過各種屬性枚舉密鑰,例如所有權或身份驗證邊界。調用者枚舉自己的命名空間不需要此權限。這是由覆蓋get_info許可。
lock此權限允許鎖定密鑰庫,即驅逐主密鑰,從而使身份驗證綁定的密鑰變得不可用和不可創建。
reset此權限允許將 Keystore 重置為出廠默認值,刪除所有對 Android 操作系統運行不重要的密鑰
unlock需要此權限才能嘗試解鎖授權綁定密鑰的主密鑰。