片上系統(SoC)中受信任的執行環境的可用性為Android設備提供了向Android OS,平台服務甚至第三方應用程序提供硬件支持的強大安全服務的機會。尋求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中,Keystore添加了對稱加密原語,AES和HMAC以及用於硬件支持的密鑰的訪問控制系統。訪問控制是在密鑰生成期間指定的,並在密鑰的生命週期內強制執行。可以將密鑰限制為僅在用戶經過身份驗證之後才可用,並且只能用於指定目的或具有指定的加密參數。有關更多信息,請參見授權標籤和功能頁面。
除了擴大加密原語的範圍外,Android 6.0中的Keystore還添加了以下內容:
- 一種使用控制方案,用於限制密鑰使用,以減輕由於濫用密鑰而造成的安全風險
- 一種訪問控制方案,用於將密鑰限制為指定的用戶,客戶端和定義的時間範圍
在Android 7.0中,Keymaster 2添加了對密鑰證明和版本綁定的支持。密鑰證明提供了公共密鑰證書,其中包含對密鑰及其訪問控制的詳細描述,以使密鑰在安全硬件中的存在及其配置可以進行遠程驗證。
版本綁定將密鑰綁定到操作系統和補丁程序級別的版本。這樣可以確保發現舊系統或TEE軟件漏洞的攻擊者無法將設備回滾到易受攻擊的版本,也無法使用由較新版本創建的密鑰。此外,如果在已升級到較新版本或補丁程序級別的設備上使用具有給定版本和補丁程序級別的密鑰,則在使用該密鑰之前先對其進行升級,並且該密鑰的先前版本無效。隨著設備的升級,按鍵會與設備一起“棘輪”,但是如果將設備還原到先前的版本,則這些按鍵將無法使用。
在Android 8.0中,Keymaster 3從舊式的C結構硬件抽象層(HAL)過渡到根據新的硬件接口定義語言(HIDL)中的定義生成的C ++ HAL接口。作為更改的一部分,許多參數類型發生了變化,儘管類型和方法與舊類型和HAL struct方法具有一一對應的關係。有關更多詳細信息,請參見“功能”頁面。
除了此界面修訂版之外,Android 8.0擴展了Keymaster 2的證明功能以支持ID證明。 ID證明提供了一種有限的和可選的機制,可以對硬件標識符(例如設備序列號,產品名稱和電話ID(IMEI / MEID))進行強有力的證明。要實現此添加,請更改ASN.1認證架構以添加ID認證。 Keymaster的實現需要找到某種安全的方法來檢索相關的數據項,並且需要定義一種機制來安全且永久地禁用該功能。
在Android 9中,更新包括:
- 更新至Keymaster 4
- 支持嵌入式安全元素
- 支持安全密鑰導入
- 支持3DES加密
- 更改版本綁定,以便boot.img和system.img分別設置版本以允許獨立更新
詞彙表
這是Keystore組件及其關係的快速概述。
AndroidKeystore是Android Framework API和應用程序用來訪問Keystore功能的組件。它是對標準Java密碼體系結構API的擴展,由在應用程序自己的處理空間中運行的Java代碼組成。 AndroidKeystore
通過將其轉發到keystore守護程序來滿足應用程序對Keystore行為的請求。
密鑰庫守護程序是一個Android系統守護程序,可通過Binder API訪問所有密鑰庫功能。它負責存儲“密鑰斑點”,其中包含實際的密鑰材料,並對其進行加密,因此密鑰庫可以存儲它,但不能使用它或將其顯示出來。
keymasterd是提供訪問Keymaster TA的HIDL服務器。 (此名稱不是標準化的,僅供參考。)
Keymaster TA (受信任的應用程序)是在安全上下文中運行的軟件,通常在ARM SoC上的TrustZone中運行,可提供所有安全的Keystore操作,可以訪問原始密鑰資料,驗證密鑰上的所有訪問控制條件, 等等。
LockSettingsService是負責用戶身份驗證(包括密碼和指紋)的Android系統組件。它不是Keystore的一部分,而是相關的,因為許多Keystore密鑰操作都需要用戶身份驗證。 LockSettingsService
與Gatekeeper TA和Fingerprint TA交互以獲取身份驗證令牌,該令牌提供給Keystore守護程序,並最終由Keymaster TA應用程序使用。
Gatekeeper TA (受信任的應用程序)是在安全上下文中運行的另一個組件,它負責對用戶密碼進行身份驗證並生成用於向Keymaster TA證明針對特定用戶在特定時間點進行了身份驗證的身份驗證令牌。
指紋TA (受信任的應用程序)是在安全上下文中運行的另一個組件,它負責對用戶指紋進行身份驗證並生成用於向Keymaster TA證明已在特定時間對特定用戶進行身份驗證的身份驗證令牌。
建築學
Android Keystore API和底層的Keymaster HAL提供了一組基本但足夠的加密原語,以允許使用訪問控制的,硬件支持的密鑰來實現協議。
Keymaster HAL是OEM提供的,可動態加載的庫,供Keystore服務用於提供硬件支持的加密服務。為了確保安全,HAL實現不對用戶空間甚至內核空間執行任何敏感操作。敏感操作被委派給通過某些內核接口訪問的安全處理器。產生的架構如下所示:

圖1.訪問Keymaster
在Android設備中,Keymaster HAL的“客戶端”由多個層(例如,應用程序,框架,Keystore守護程序)組成,但是出於本文檔的目的,可以將其忽略。這意味著所描述的Keymaster HAL API是低級的,由平台內部組件使用,並且不向應用程序開發人員公開。更高級別的API在Android Developer網站上進行了描述。
Keymaster HAL的目的不是實現對安全敏感的算法,而只是將對安全世界的請求編組和解組。接線格式是實現定義的。
與以前版本的兼容性
Keymaster 1 HAL與先前發布的HAL完全不兼容,例如Keymaster 0.2和0.3。為了促進與較舊的Keymaster HAL一起啟動的運行Android 5.0及更低版本的設備上的互操作性,Keystore提供了一個適配器,該適配器通過調用現有硬件庫來實現Keymaster 1 HAL。結果不能提供Keymaster 1 HAL中的全部功能。特別是,它僅支持RSA和ECDSA算法,並且在非安全環境中,所有密鑰授權實施都由適配器執行。
Keymaster 2通過刪除get_supported_*
方法並允許finish()
方法接受輸入,進一步簡化了HAL接口。在一次輸入全部可用的情況下,這減少了到TEE的往返次數,並簡化了AEAD解密的實現。
在Android 8.0中,Keymaster 3從舊式的C結構HAL過渡到了由新的硬件接口定義語言(HIDL)中的定義生成的C ++ HAL接口。通過子類化生成的IKeymasterDevice
類並實現純虛擬方法,可以創建一種新型的HAL實現。作為更改的一部分,許多參數類型已更改,儘管類型和方法與舊類型和HAL struct方法一一對應。
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
對key_parameter_t
數組的指針的結構,而是包含KeyParameter
對象的vec
(向量)。返回值在“ generates
”子句中列出,包括密鑰blob的uint8_t
值向量。
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
是一個使用generate子句中列出的返回值的函數。 HAL實現類重寫此generateKey
方法,並調用generate_cb
函數指針,以將操作結果返回給調用者。注意,函數指針的調用是同步的。調用者調用generateKey
, generateKey
調用提供的函數指針,該指針執行完成,將控制權返回generateKey
實現,然後返回給調用者。
有關詳細示例,請參閱hardware/interfaces/keymaster/3.0/default/KeymasterDevice.cpp
。默認實現為具有舊式keymaster0,keymaster1或keymaster2 HALS的設備提供向後兼容性。
訪問控制
密鑰庫訪問控制的最基本規則是,每個應用程序都有自己的名稱空間。但是對於每條規則都有一個例外。密鑰庫具有一些硬編碼映射,這些映射允許某些系統組件訪問某些其他名稱空間。這是一種非常鈍的工具,因為它可以使一個組件完全控制另一個名稱空間。然後是供應商組件作為Keystore客戶端的問題。當前,我們無法為供應商組件(例如WPA請求方)建立名稱空間。
為了容納供應商組件並通用化訪問控製而沒有硬編碼的異常,Keystore 2.0引入了域和selinux名稱空間。
密鑰庫域
使用密鑰庫域,我們可以將名稱空間與uid分離。訪問密鑰庫中的密鑰的客戶端必須指定他們要訪問的域,名稱空間和別名。基於此元組和呼叫者的身份,我們可以確定呼叫者要訪問哪個密鑰以及它是否具有適當的權限。
我們介紹了五個域參數,它們控制如何訪問密鑰。它們控制密鑰描述符的名稱空間參數的語義以及如何執行訪問控制。
-
DOMAIN_APP
:應用程序域涵蓋了舊版行為。 Java Keystore SPI默認情況下使用此域。使用此域時,將忽略命名空間參數,而是使用調用者的uid。對這個域的訪問由SELinux策略中類keystore_key
的keystore標籤控制。 -
DOMAIN_SELINUX
:此域指示名稱空間在SELinux策略中具有標籤。查找名稱空間參數並將其轉換為目標上下文,並對keystore_key
類的調用SELinux上下文執行權限檢查。為給定操作建立許可後,完整的元組將用於鍵查找。 -
DOMAIN_GRANT
:授權域指示名稱空間參數是授權標識符。別名參數將被忽略。創建授予時將執行Selinux檢查。進一步的訪問控制僅檢查呼叫者uid是否與請求的授予的被授予者uid相匹配。 -
DOMAIN_KEY_ID
:此域指示名稱空間參數是唯一的密鑰ID。密鑰本身可能是使用DOMAIN_APP
或DOMAIN_SELINUX
創建的。從domain
和namespace
從密鑰數據庫加載後,將執行許可權檢查,就像通過域,名稱空間和別名元組加載blob一樣。密鑰ID域的基本原理是連續性。當通過別名訪問密鑰時,後續調用可能會在不同的密鑰上進行操作,因為可能已經生成或導入了新密鑰並將其綁定到該別名。但是,密鑰ID永遠不會更改。因此,在使用別名從密鑰庫數據庫中加載一次密鑰後,使用按密鑰ID進行操作的密鑰時,只要密鑰ID仍然存在,就可以確定它是相同的密鑰。此功能未提供給應用程序開發人員,而是在Android Keystore SPI中使用,即使以不安全的方式同時使用時,也可提供更一致的體驗。 -
DOMAIN_BLOB
:Blob域指示調用者自己管理Blob。這用於需要在裝入數據分區之前訪問密鑰庫的客戶端。密鑰Blob包含在密鑰描述符的blob
字段中。
使用SELinux域,我們可以使供應商組件訪問非常特定的密鑰庫名稱空間,這些名稱空間可以由系統組件(例如設置對話框)共享。
SELinux keystore_key的策略
命名空間標籤是使用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。為了從Keystore加載密鑰或將密鑰導入Keystore,使用AndroidKeyStoreLoadStoreParameter
指定名稱空間ID。例如,
import android.security.keystore2.AndroidKeyStoreLoadStoreParameter; import java.security.KeyStore; KeyStore keystore = KeyStore.getInstance("AndroidKeyStore"); keystore.load(new AndroidKeyStoreLoadStoreParameter(102));
要在給定的名稱空間中生成密鑰,必須使用KeyGenParameterSpec.Builder#setNamespace():
給出名稱空間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 |
客戶端通過其數字ID請求SELinux域和所需的虛擬名稱空間(此處為"wifi_keys"
)來請求密鑰。
除此之外,還定義了以下名稱空間。在它們替換特殊規則的地方,下表指示了它們以前對應的UID。
命名空間ID | SEPolicy標籤 | UID | 描述 |
---|---|---|---|
0 | su_key | 不適用 | 超級用戶密鑰。僅用於對userdebug和eng版本進行測試。與用戶版本無關。 |
1個 | shell_key | 不適用 | 可用於外殼程序的命名空間。通常用於測試,但也可以從命令行用於用戶版本。 |
100 | vold_key | 不適用 | 打算由vold使用。 |
101 | odsing_key | 不適用 | 由設備上的簽名守護程序使用。 |
102 | wfi_key | AID_WIFI(1010) | 由Android的Wifi sybsystem(包括wpa_supplicant)使用。 |
訪問向量
SELinux類keystore_key
已經老化了很多,並且某些權限(例如, verify
或sign
已經失去了意義。這是Keystore 2.0將強制執行的新權限集keystore2_key
。
允許 | 意義 |
---|---|
delete | 從密鑰庫中刪除密鑰時檢查。 |
get_info | 檢查何時請求密鑰的元數據。 |
grant | 調用方需要此權限才能在目標上下文中創建對密鑰的授權。 |
manage_blob | 調用方可以在給定的SELinux名稱空間上使用DOMAIN_BLOB ,從而自行管理Blob。這對於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 | 此權限允許將“密鑰庫”重置為出廠默認設置,刪除所有對Android操作系統的功能至關重要的密鑰 |
unlock | 嘗試解鎖身份驗證綁定密鑰的主密鑰需要此權限。 |