本頁包含 Android Keystore 的加密功能相關資訊,這些功能由底層的 KeyMint (或 Keymaster) 實作提供。
密碼編譯基元
金鑰儲存區提供下列類別的作業:
- 建立金鑰,產生私密或密鑰內容,且只有安全環境可以存取。用戶端可以透過下列方式建立金鑰:
- 產生新金鑰
- 匯入未加密的金鑰內容
- 匯入加密金鑰內容
- 金鑰認證:建立非對稱金鑰時,系統會產生包含金鑰組公開金鑰部分的憑證。這項憑證也可以選擇性地保存金鑰的中繼資料和裝置狀態資訊,所有資訊都會由可追溯至信任根的金鑰鏈簽署。
- 密碼編譯作業:
- 對稱式加密和解密 (AES、3DES)
- 非對稱式解密 (RSA)
- 非對稱式簽署 (ECDSA、RSA)
- 對稱簽署和驗證 (HMAC)
- 非對稱金鑰協議 (ECDH)
請注意,Keystore 和 KeyMint 不會處理非對稱金鑰的公開金鑰作業。
產生或匯入金鑰時,系統會指定通訊協定元素 (例如用途、模式和填補) 和存取控制限制,並永久繫結至金鑰,確保金鑰無法用於其他用途。
除了上述清單外,KeyMint (先前為 Keymaster) 實作項目還提供一項服務,但未以 API 形式公開:隨機數字產生。這項功能會在內部用於產生金鑰、初始化向量 (IV)、隨機填補內容,以及需要隨機性的安全通訊協定其他元素。
必要原始物件
所有 KeyMint 實作項目都提供下列功能:
- RSA
- 支援 2048、3072 和 4096 位元金鑰
- 支援公開指數 F4 (2^16+1)
- RSA 簽署的填補模式:
- RSASSA-PSS (
PaddingMode::RSA_PSS
) - RSASSA-PKCS1-v1_5 (
PaddingMode::RSA_PKCS1_1_5_SIGN
)
- RSASSA-PSS (
- RSA 簽署的摘要模式:
- SHA-256
- RSA 加密/解密的填補模式:
- 無填充物
- RSAES-OAEP (
PaddingMode::RSA_OAEP
) - RSAES-PKCS1-v1_5 (
PaddingMode::RSA_PKCS1_1_5_ENCRYPT
)
- ECDSA
- 支援 224、256、384 和 521 位元金鑰,分別使用 NIST P-224、P-256、P-384 和 P-521 曲線
- ECDSA 的摘要模式:
- 沒有摘要 (已淘汰,日後將移除)
- SHA-256
- AES
- 支援 128 和 256 位元金鑰
- CBC、CTR、ECB 和 GCM。GCM 實作方式不允許使用小於 96 位元的標記,或長度不是 96 位元的隨機數。
- CBC 和 ECB 模式支援
PaddingMode::NONE
和PaddingMode::PKCS7
填補模式。如果沒有填補,且輸入內容不是區塊大小的倍數,CBC 或 ECB 模式加密就會失敗。
- HMAC SHA-256,金鑰大小至少為 32 個位元組。
強烈建議您在 KeyMint 實作項目中使用 SHA1 和 SHA2 系列的其他成員 (SHA-224、SHA384 和 SHA512)。如果硬體 KeyMint 實作項目未提供這些金鑰,金鑰儲存區會以軟體形式提供。
建議使用部分基本型別,以便與其他系統互通:
- RSA 的金鑰大小較小
- RSA 的任意公用指數
金鑰存取權控管
如果攻擊者可以隨意使用硬體型金鑰,即使這些金鑰永遠無法從裝置中擷取,也無法提供多少安全性 (但這類金鑰比「可以」外洩的金鑰更安全)。因此,Keystore 必須強制執行存取權控管。
存取控制項定義為標記/值配對的「授權清單」。授權標記是 32 位元整數,值則為各種型別。部分標記可以重複,以指定多個值。標記是否可以重複,是在 KeyMint HAL 介面中指定。建立金鑰時,呼叫端會指定授權清單。KeyMint 實作項目 (Keystore 底層) 會修改清單,指定一些額外資訊,例如金鑰是否具有回溯保護機制,並傳回「最終」授權清單,編碼至傳回的金鑰 blob 中。如果最終授權清單經過修改,任何嘗試使用金鑰進行加密編譯作業的行為都會失敗。
如果是 Keymaster 2 和更早的版本,可能標記的集合是在列舉 keymaster_authorization_tag_t
中定義,且永久固定 (但可以擴充)。名稱的前置字串為 KM_TAG
。代碼 ID 的前四個位元用於表示類型。
Keymaster 3 已將 KM_TAG
前置字元變更為 Tag::
。
可能的類型包括:
ENUM
:許多標記的值都是在列舉中定義。舉例來說,TAG::PURPOSE
的可能值是在列舉 keymaster_purpose_t
中定義。
ENUM_REP
:與 ENUM
相同,但授權清單中可以重複標記。重複表示多個授權值。舉例來說,加密金鑰可能會有 KeyPurpose::ENCRYPT
和 KeyPurpose::DECRYPT
。
KeyMint 建立金鑰時,呼叫端會指定金鑰的授權清單。Keystore 和 KeyMint 會修改這份清單,加入額外限制,而底層的 KeyMint 實作會將最終授權清單編碼至傳回的 keyblob。編碼授權清單會以密碼編譯方式繫結至金鑰 Blob,因此任何修改授權清單 (包括排序) 的嘗試都會導致金鑰 Blob 無效,無法用於密碼編譯作業。
硬體與軟體強制執行
並非所有安全硬體實作項目都包含相同的功能。為支援各種方法,Keymaster 會區分安全和不安全的世界存取控制執行,或分別區分硬體和軟體執行。
這會透過 KeyMint API 的 KeyCharacteristics
類型 securityLevel
欄位公開。安全硬體會根據可強制執行的項目,負責將授權放在具有適當安全等級的 KeyCharacteristics
中。這項資訊也會顯示在非對稱金鑰的認證記錄中:SecurityLevel::TRUSTED_ENVIRONMENT
或 SecurityLevel::STRONGBOX
的金鑰特徵會顯示在 hardwareEnforced
清單中,SecurityLevel::SOFTWARE
或 SecurityLevel::KEYSTORE
的特徵則會顯示在 softwareEnforced
清單中。
舉例來說,安全環境通常不會強制執行金鑰使用時間範圍的限制,因為安全環境無法信任日期和時間資訊的存取權。因此,Android 中的 KeyStore 會強制執行 Tag::ORIGINATION_EXPIRE_DATETIME
等授權,並具有 SecurityLevel::KEYSTORE
。
如要進一步瞭解如何判斷金鑰及其授權是否由硬體支援,請參閱「金鑰認證」。
加密訊息建構授權
下列標記用於定義使用相關聯金鑰的作業加密特徵:
Tag::ALGORITHM
Tag::KEY_SIZE
Tag::BLOCK_MODE
Tag::PADDING
Tag::CALLER_NONCE
Tag::DIGEST
Tag::MGF_DIGEST
下列標記可重複使用,也就是說,單一鍵可與多個值建立關聯:
Tag::BLOCK_MODE
Tag::PADDING
Tag::DIGEST
Tag::MGF_DIGEST
要使用的值會在作業時間指定。
目的
金鑰會有一組相關聯的用途,以一或多個含有 Tag::PURPOSE
標記的授權項目表示,定義金鑰的使用方式。相關用途定義於
KeyPurpose.aidl
。
請注意,某些用途值組合會造成安全性問題。舉例來說,如果 RSA 金鑰可用於加密和簽署,攻擊者就能說服系統解密任意資料,進而產生簽章。
匯入金鑰
Keymaster 僅支援以 X.509 格式匯出公開金鑰,以及匯入下列項目:
- 採用 DER 編碼 PKCS#8 格式的非對稱金鑰配對 (不含以密碼為基礎的加密)
- 以原始位元組表示的對稱金鑰
為確保匯入的金鑰與安全產生的金鑰有所區別,Tag::ORIGIN
會納入適當的金鑰授權清單。舉例來說,如果金鑰是在安全硬體中產生,則會在金鑰特徵的 hw_enforced
清單中找到值為 KeyOrigin::GENERATED
的 Tag::ORIGIN
,而匯入安全硬體的金鑰值為 KeyOrigin::IMPORTED
。
使用者驗證
安全的 KeyMint 實作項目不會實作使用者驗證,而是依賴其他會這麼做的可信任應用程式。如要瞭解這些應用程式實作的介面,請參閱 Gatekeeper 頁面。
使用者驗證規定是透過兩組標記指定。 第一組表示允許使用金鑰的驗證方法:
Tag::USER_SECURE_ID
具有 64 位元的數值,可指定安全驗證權杖中提供的安全使用者 ID,用來解鎖金鑰。如果重複,只要安全驗證權杖中提供任一值,即可使用金鑰。
第二組則表示使用者是否需要驗證身分,以及驗證時間。
如果沒有這兩個標記,但有 Tag::USER_SECURE_ID
,每次使用金鑰時都必須驗證。
Tag::NO_AUTHENTICATION_REQUIRED
表示不需要使用者驗證,但金鑰的存取權仍僅限於擁有者應用程式 (以及授予存取權的任何應用程式)。Tag::AUTH_TIMEOUT
是數值,以秒為單位,指定授權金鑰使用時,使用者驗證必須有多新。逾時不會跨越重新啟動;重新啟動後,所有驗證都會失效。您可以將逾時時間設為較大的值,表示每次啟動都需要驗證一次 (2^32 秒約為 136 年;想必 Android 裝置重新啟動的頻率會比這個時間更頻繁)。
需要解鎖裝置
只有在裝置解鎖時,才能使用 Tag::UNLOCKED_DEVICE_REQUIRED
鍵。如需詳細語意,請參閱
KeyProtection.Builder#setUnlockedDeviceRequired(boolean)
。
UNLOCKED_DEVICE_REQUIRED
是由 KeyStore 強制執行,而非 KeyMint。不過,在 Android 12 以上版本中,KeyStore 會在裝置鎖定時以加密方式保護UNLOCKED_DEVICE_REQUIRED
金鑰,確保即使 KeyStore 在裝置鎖定時遭到入侵,在大多數情況下也無法使用金鑰。
為此,Keystore 會先「超級加密」所有 UNLOCKED_DEVICE_REQUIRED
金鑰,再將其儲存在資料庫中,並盡可能在裝置鎖定時保護超級加密金鑰 (超級金鑰),確保只有成功解鎖裝置才能復原這些金鑰。(之所以使用「超級加密」一詞,是因為除了 KeyMint 已對所有金鑰套用的加密層之外,這個加密層還會額外套用加密機制)。
每個使用者 (包括設定檔) 都有兩個與 UNLOCKED_DEVICE_REQUIRED
相關聯的超級金鑰:
- 需要解鎖裝置的對稱超級金鑰。這是 AES‑256‑GCM 金鑰。裝置解鎖時,這項服務會加密匯入或產生的
UNLOCKED_DEVICE_REQUIRED
金鑰。 - UnlockedDeviceRequired 非對稱超級金鑰。這是 ECDH P-521 金鑰組。裝置為使用者鎖定時,這項技術會加密匯入或產生的
UNLOCKED_DEVICE_REQUIRED
金鑰。以這個非對稱金鑰加密的金鑰,會在首次使用時以對稱金鑰重新加密 (只能在裝置解鎖時進行)。
Keystore 會在建立使用者時產生這些超級金鑰,並以使用者合成密碼加密,然後儲存在資料庫中。因此可以使用 PIN 碼、解鎖圖案或密碼等方式復原。
金鑰儲存區也會在記憶體中快取這些超級金鑰,以便對金鑰執行作業。UNLOCKED_DEVICE_REQUIRED
不過,只有在裝置解鎖供使用者存取時,才會嘗試快取這些金鑰的私密部分。當裝置為使用者鎖定時,KeyStore 會盡可能將這些超級金鑰的私密部分快取副本歸零。具體來說,當裝置為使用者鎖定時,KeyStore 會為使用者的 UnlockedDeviceRequired 超級金鑰選取並套用三種保護層級之一:
- 如果使用者只啟用 PIN 碼、解鎖圖案或密碼,Keystore 會將快取超級金鑰的私密部分歸零。因此,只有透過資料庫中的加密副本才能復原超級金鑰,且必須使用 PIN 碼、解鎖圖案或同等密碼才能解密。
- 如果使用者只啟用第 3 類 (「強」) 生物特徵辨識和 PIN 碼、解鎖圖案或密碼,Keystore 會安排透過使用者註冊的任何第 3 類生物特徵辨識 (通常是指紋) 復原超級金鑰,做為 PIN 碼、解鎖圖案或密碼的替代方式。為此,它會產生新的 AES-256-GCM 金鑰、使用該金鑰加密超級金鑰的私密部分、將 AES-256-GCM 金鑰匯入 KeyMint 做為生物特徵辨識繫結金鑰 (必須在過去 15 秒內成功完成生物特徵辨識驗證),並將所有這些金鑰的明文副本歸零。
- 如果使用者啟用第 1 類 (「便利」) 生物特徵辨識、第 2 類 (「弱」) 生物特徵辨識,或啟用解除鎖定信任代理程式,金鑰儲存區就會以純文字形式快取超級金鑰。在這種情況下,系統不會提供金鑰的加密安全防護。
UNLOCKED_DEVICE_REQUIRED
使用者可以不要啟用這些解鎖方法,避免使用安全性較低的備用方式。這些類別中最常見的解鎖方法是臉部解鎖 (適用於許多裝置) 和使用已配對的智慧手錶解鎖。
為使用者解鎖裝置後,金鑰儲存區會盡可能復原使用者的 UnlockedDeviceRequired 超級金鑰。如果是 PIN 碼、圖案或密碼等效解鎖方式,系統會解密儲存在資料庫中的這些金鑰副本。否則,系統會檢查是否已儲存以生物特徵辨識綁定金鑰加密的金鑰副本,並嘗試解密。只有在使用者於過去 15 秒內,透過 KeyMint (而非 Keystore) 強制執行的第 3 類生物特徵辨識成功完成驗證時,這項作業才會成功。
用戶端繫結
金鑰與特定用戶端應用程式的關聯 (即用戶端繫結) 是透過選用的用戶端 ID 和一些選用的用戶端資料 (分別為 Tag::APPLICATION_ID
和 Tag::APPLICATION_DATA
) 完成。金鑰儲存區會將這些值視為不透明的 Blob,只會確保在金鑰產生/匯入期間呈現的 Blob,在每次使用時都會呈現,且位元組完全相同。KeyMint 未傳回用戶端繫結資料。通話者必須知道這組號碼才能使用通話金鑰。
應用程式無法使用這項功能。
到期日
KeyStore 支援依日期限制金鑰用量。金鑰的有效開始時間和金鑰到期時間可以與金鑰建立關聯,如果目前日期/時間超出有效範圍,Keymaster 會拒絕執行金鑰作業。金鑰有效範圍是使用 Tag::ACTIVE_DATETIME
、Tag::ORIGINATION_EXPIRE_DATETIME
和 Tag::USAGE_EXPIRE_DATETIME
標記指定。「來源」和「用途」的區別在於金鑰是否用於「產生」新的密文/簽章/等,或是「使用」現有的密文/簽章/等。請注意,這項區別不會向應用程式公開。
Tag::ACTIVE_DATETIME
、Tag::ORIGINATION_EXPIRE_DATETIME
和 Tag::USAGE_EXPIRE_DATETIME
標記為選用項目。如果沒有這些標記,系統會假設相關金鑰一律可用於解密/驗證訊息。
由於掛鐘時間是由非安全世界提供,因此與到期相關的標記會列在軟體強制執行的清單中。
信任根繫結
金鑰必須繫結至信任根,也就是啟動期間提供給 KeyMint 安全硬體的位元字串 (最好由開機載入程式提供)。這個位元字串會以密碼編譯方式繫結至 KeyMint 管理的每個金鑰。
信任根包含用於驗證開機映像檔簽章的公開金鑰,以及裝置的鎖定狀態。如果變更公開金鑰以允許使用其他系統映像檔,或變更鎖定狀態,則除非還原先前的信任根,並啟動以該金鑰簽署的系統,否則先前系統建立的任何 KeyMint 保護金鑰都無法使用。我們的目標是提高軟體強制執行的金鑰存取控制項價值,讓攻擊者安裝的作業系統無法使用 KeyMint 金鑰。
獨立鍵
部分 KeyMint 安全硬體可選擇在內部儲存金鑰內容,並傳回控制代碼,而非加密的金鑰內容。或者,可能要等到其他不安全或安全的世界系統元件可用時,才能使用金鑰。呼叫端可透過 KeyMint HAL,使用 TAG::STANDALONE
標記要求金鑰「獨立」,也就是說,除了 Blob 和執行的 KeyMint 系統外,不需要任何資源。您可以檢查與金鑰相關聯的標記,確認金鑰是否為獨立金鑰。目前僅定義了兩個值:
KeyBlobUsageRequirements::STANDALONE
KeyBlobUsageRequirements::REQUIRES_FILE_SYSTEM
應用程式無法使用這項功能。
速度
建立時,可以使用 TAG::MIN_SECONDS_BETWEEN_OPS
指定最高用量速度。如果作業是在 TAG::MIN_SECONDS_BETWEEN_OPS
秒前執行,TrustZone 實作項目會拒絕使用該金鑰執行加密作業。
實作速率限制的簡單方法是建立金鑰 ID 和上次使用時間戳記的表格。這個表格的大小有限,但至少可容納 16 個項目。如果表格已滿,且無法更新或捨棄任何項目,安全硬體實作會「安全失敗」,優先拒絕所有速度受限的金鑰作業,直到其中一個項目過期為止。所有項目在重新啟動後過期是可接受的。
您也可以使用 TAG::MAX_USES_PER_BOOT
將金鑰限制為每次啟動 n 次。這也需要追蹤資料表,其中至少要容納四個鍵,且也要安全失敗。請注意,應用程式無法建立每次啟動的受限金鑰。這項功能不會透過 KeyStore 公開,僅供系統運作使用。
應用程式無法使用這項功能。
隨機號碼產生器重新播種
由於安全硬體會產生金鑰素材和初始化向量 (IV) 的隨機號碼,且硬體隨機號碼產生器不一定完全值得信賴,因此 KeyMint HAL 提供介面,讓用戶端提供額外的亂度,並混入產生的隨機號碼。
使用硬體隨機數產生器做為主要種子來源。 透過外部 API 提供的種子資料,不得做為產生隨機數的唯一來源。此外,所用的混合作業必須確保隨機輸出內容無法預測,即使其中一個種子來源無法預測也一樣。