本頁面包含 Android KeyStore 加密功能的相關資訊,由基礎 KeyMint (或 Keymaster) 實作提供。
加密編譯基元
Keystore 提供下列類別的作業:
- 建立金鑰,產生只有安全環境才能存取的私密金鑰或密鑰內容。用戶端可以透過下列方式建立金鑰:
- 產生新金鑰
- 匯入未加密的金鑰內容
- 匯入加密金鑰內容
- 金鑰認證:建立非對稱金鑰時,會產生含有金鑰組公開金鑰部分的憑證。這份憑證也可以包含金鑰中繼資料和裝置狀態的相關資訊,所有資訊都會由金鑰簽署,並鏈結回信任的根。
- 加密編譯作業:
- 對稱式加密和解密 (AES、3DES)
- 非對稱式解密 (RSA)
- 非對稱式簽署 (ECDSA、RSA)
- 對稱式簽署和驗證 (HMAC)
- 非對稱金鑰協議 (ECDH)
用途、模式和填補等通訊協定元素,以及存取控管限制,都會在金鑰產生或匯入時指定,並永久繫結至金鑰,確保金鑰不會以任何其他方式使用。
KeyMint 實作必須支援的原語和模式,請參閱 IKeyMintDevice HAL 介面規格。
基礎 KeyMint 實作必須執行隨機號碼產生作業,才能支援金鑰產生作業,以及建立隨機填充或初始化向量 (IV)。為支援這項功能,Android 系統會定期為 KeyMint 實作提供額外的熵。
金鑰存取控管
如果攻擊者可以隨意使用硬體金鑰,那麼即使金鑰無法從裝置中擷取,也無法提供太多安全性 (雖然這比「可以」竊取的金鑰更安全)。因此,Keystore 必須強制執行存取控管。
存取控管機制會定義為標記/值組合的「授權清單」。授權標記是 32 位元整數,值則有多種類型。部分標記可重複使用,以指定多個值。標記是否可重複,取決於 KeyMint (先前稱為 Keymaster) HAL 介面中的指定。
支援的標記值定義在 Tag.aidl 檔案中,每個值都與 TagType 相關聯,後者會指出相關聯值的類型 (例如整數或位元組),以及是否可以重複指定多個支援的值。
KeyMint 建立金鑰時,呼叫端會指定金鑰的授權清單。Keystore 和 KeyMint 會修改這份清單,新增額外限制,而基礎 KeyMint 實作會將最終授權清單編碼至傳回的 keyblob。編碼的授權清單會以加密方式繫結至 keyblob,因此任何修改授權清單 (包括排序) 的嘗試都會導致 keyblob 無效,無法用於加密作業。
硬體與軟體強制執行
並非所有安全硬體實作都包含相同功能。為了支援各種方法,KeyMint 分別區分安全和不安全的存取控管強制執行,或硬體和軟體強制執行。
這會透過 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::ALGORITHMTag::KEY_SIZETag::BLOCK_MODETag::PADDINGTag::CALLER_NONCETag::DIGESTTag::MGF_DIGEST
下列標記可重複,也就是說,多個值可與單一鍵建立關聯:
Tag::BLOCK_MODETag::PADDINGTag::DIGESTTag::MGF_DIGEST
要使用的值會在作業時指定。
目的
金鑰有一組相關聯的用途,以一或多個含有 Tag::PURPOSE 標記的授權項目表示,定義金鑰的使用方式。用途定義於 KeyPurpose.aidl。
請注意,某些用途值組合會造成安全性問題。舉例來說,如果 RSA 金鑰可用於加密和簽署,攻擊者只要能說服系統解密任意資料,就能產生簽章。
金鑰匯入
KeyMint 支援匯入:
- 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 在裝置鎖定時遭到入侵,也無法使用這些金鑰。
本節所述的所有密碼編譯和隨機號碼產生作業均使用 BoringSSL,除非明確提及使用 KeyMint。所有密鑰在不再需要時都會歸零。
UnlockedDeviceRequired 超級金鑰
為了以加密方式保護 UNLOCKED_DEVICE_REQUIRED 金鑰,Keystore 會先「超級加密」金鑰,再將金鑰儲存在資料庫中。在可能的情況下,當裝置鎖定時,系統會保護超級加密金鑰 (超級金鑰),只有在成功解鎖裝置時才能復原。(之所以使用「超級加密」一詞,是因為這層加密是額外套用,而非 KeyMint 已套用至所有金鑰的加密層)。
每個使用者 (包括設定檔) 都有兩個與 UNLOCKED_DEVICE_REQUIRED 相關聯的超級按鍵:
- UnlockedDeviceRequired 對稱超級金鑰。這是 AES-256-GCM 金鑰。當裝置解鎖時,系統會加密匯入、產生或使用的
UNLOCKED_DEVICE_REQUIRED金鑰。 - UnlockedDeviceRequired 非對稱超級金鑰。這是 ECDH P-521 金鑰組。當裝置處於鎖定狀態時,系統會加密匯入或產生的
UNLOCKED_DEVICE_REQUIRED金鑰。詳情請參閱「在裝置鎖定時儲存金鑰」。
產生及保護超級金鑰
建立使用者時,Keystore 會產生使用者的 UnlockedDeviceRequired 超級金鑰,並儲存在資料庫中,由使用者的合成密碼 (間接) 加密:
- 系統伺服器會使用 SP800-108 KDF,從使用者的合成密碼衍生出使用者的 Keystore 密碼。
- 系統伺服器會將使用者的 KeyStore 密碼傳遞至 KeyStore。
- Keystore 會產生使用者的超級金鑰。
- 針對使用者的每個超級金鑰:
- Keystore 會產生隨機鹽。
- Keystore 會使用 HKDF-SHA256,從使用者的 Keystore 密碼和鹽衍生出 AES-256-GCM 金鑰。
- Keystore 會使用這個 AES‑256‑GCM 金鑰,加密超級金鑰的密鑰部分。
- Keystore 會將加密的超級金鑰和其鹽儲存在資料庫中。 如果是非對稱金鑰,金鑰的公開部分也會以未加密的形式儲存。
這項程序可讓系統在得知使用者的合成密碼時 (例如輸入正確的 PIN 碼、解鎖圖案或密碼),解密這些超級金鑰。
Keystore 也會將這些超級金鑰快取到記憶體中,以便對 UNLOCKED_DEVICE_REQUIRED 金鑰執行操作。不過,只有在裝置解鎖時,系統才會嘗試快取這些金鑰的機密部分。當裝置為使用者鎖定時,KeyStore 會盡可能將這些超級金鑰機密部分的快取副本歸零。具體來說,當裝置為使用者鎖定時,Keystore 會選取並套用下列三種保護層級之一,用於使用者的 UnlockedDeviceRequired 超級金鑰:
- 如果使用者只啟用 PIN 碼、解鎖圖案或密碼,Keystore 會將快取超級金鑰的密鑰部分歸零。因此,超級金鑰只能透過資料庫中的加密副本復原,而該副本只能透過 PIN 碼、解鎖圖案或密碼等效方式解密。
- 如果使用者僅啟用第 3 級 (「強」) 生物辨識和 PIN 碼、解鎖圖案或密碼,Keystore 會安排超級金鑰,讓使用者可透過任何已註冊的第 3 級生物辨識 (通常是指紋) 復原,做為 PIN 碼、解鎖圖案或密碼的替代方案。為此,系統會產生新的 AES-256-GCM 金鑰,並使用該金鑰加密超級金鑰的機密部分,然後將 AES-256-GCM 金鑰匯入 KeyMint,做為生物特徵辨識繫結金鑰,要求使用者在過去 15 秒內成功完成生物辨識驗證,並將所有這些金鑰的明文副本歸零。
- 如果使用者已啟用第 1 級 (「便利」) 生物特徵辨識、第 2 級 (「弱」) 生物特徵辨識或主動解鎖信任代理程式,Keystore 會以純文字形式快取超級金鑰。在這種情況下,系統不會為
UNLOCKED_DEVICE_REQUIRED金鑰提供密碼編譯安全性。使用者只要不啟用這些解鎖方法,就能避免這種安全性較低的備用機制。這類別中最常見的解鎖方法是許多裝置上的人臉解鎖,以及使用配對的智慧手錶解鎖。
當裝置為使用者解鎖時,Keystore 會盡可能復原使用者的 UnlockedDeviceRequired 超級金鑰。如果是 PIN 碼、解鎖圖案或密碼等效解鎖,系統會解密儲存在資料庫中的這些金鑰副本。否則,系統會檢查是否儲存了以生物特徵辨識繫結金鑰加密的這些金鑰副本,如果是,則會嘗試解密。只有在使用者於過去 15 秒內成功透過第 3 級生物特徵辨識完成驗證時,這項作業才會成功,並由 KeyMint (而非 Keystore) 強制執行。
在裝置鎖定時儲存金鑰
Keystore 允許使用者在裝置鎖定時匯入及產生 UNLOCKED_DEVICE_REQUIRED 金鑰。這項機制採用混合式加密配置,確保只有在裝置解鎖後才能解密:
- 加密 (在裝置鎖定時匯入或產生
UNLOCKED_DEVICE_REQUIRED金鑰):- Keystore 會產生新的臨時 ECDH P-521 金鑰組。
- Keystore 會在這個臨時金鑰組的私密金鑰和 UnlockedDeviceRequired 非對稱超級金鑰的公開部分之間,執行 ECDH 金鑰協議,藉此產生共用密鑰。
- Keystore 會產生隨機鹽。
- Keystore 會使用 HKDF-SHA256,從共用密鑰和鹽衍生 AES-256-GCM 金鑰。
- Keystore 會使用這個 AES‑256‑GCM 金鑰加密
UNLOCKED_DEVICE_REQUIRED金鑰。 - Keystore 會將加密的
UNLOCKED_DEVICE_REQUIRED金鑰、鹽和臨時金鑰組的公開部分儲存在資料庫中。
- 解密 (在裝置解鎖時使用建立的
UNLOCKED_DEVICE_REQUIRED金鑰):- Keystore 會從資料庫載入加密的
UNLOCKED_DEVICE_REQUIRED金鑰、鹽和臨時金鑰組的公開部分。 - Keystore 會在臨時金鑰組的公開部分和 UnlockedDeviceRequired 非對稱超級金鑰的私密部分之間執行 ECDH 金鑰協議,藉此產生共用密鑰。裝置已解鎖,因此可以使用私密金鑰。
- Keystore 會使用 HKDF-SHA256,從共用密鑰和鹽衍生 AES-256-GCM 金鑰。這個 AES‑256‑GCM 金鑰與加密期間衍生的金鑰相同。
- Keystore 使用 AES-256-GCM 金鑰解密
UNLOCKED_DEVICE_REQUIRED金鑰。 - Keystore 會使用 UnlockedDeviceRequired 對稱超級金鑰,重新加密
UNLOCKED_DEVICE_REQUIRED金鑰。這不會影響金鑰的安全性屬性,但日後存取金鑰的速度會更快。
- Keystore 會從資料庫載入加密的
這項功能可讓應用程式在裝置鎖定時儲存資料,只有在裝置解鎖時才能解密。如要這麼做,應用程式應按照下列步驟操作:
- 在 Keystore 外部產生 AES‑256‑GCM 金鑰。
- 使用 AES‑256‑GCM 金鑰加密資料。
- 使用
setUnlockedDeviceRequired(true)金鑰保護設定,將 AES-256-GCM 金鑰匯入 KeyStore。 - 將金鑰的原始副本歸零。
如要在裝置解鎖時解密資料,請使用匯入 Keystore 的金鑰。
用戶端繫結
用戶端繫結 (金鑰與特定用戶端應用程式的關聯) 是透過選用的用戶端 ID 和一些選用的用戶端資料 (分別為 Tag::APPLICATION_ID 和 Tag::APPLICATION_DATA) 完成。Keystore 會將這些值視為不透明的 blob,只確保在金鑰產生/匯入期間提供的 blob 與每次使用時提供的 blob 相同,且每個位元組都相同。KeyMint 不會傳回用戶端繫結資料。呼叫端必須知道這個名稱,才能使用金鑰。
這項功能不會向應用程式公開。
有效期限
KeyStore 支援依日期限制金鑰使用。金鑰有效起始時間和金鑰到期時間可以與金鑰建立關聯,如果目前日期/時間超出有效範圍,Keystore 會拒絕執行金鑰作業。金鑰有效範圍是以 Tag::ACTIVE_DATETIME、Tag::ORIGINATION_EXPIRE_DATETIME 和 Tag::USAGE_EXPIRE_DATETIME 標記指定。「產生」和「使用」之間的區別,取決於金鑰是用於「產生」新的密文/簽章等,還是「使用」現有的密文/簽章等。請注意,應用程式不會看到這項區別。
Tag::ACTIVE_DATETIME、Tag::ORIGINATION_EXPIRE_DATETIME 和 Tag::USAGE_EXPIRE_DATETIME 標記為選用。如果沒有標記,系統會假設相關金鑰一律可用於解密/驗證訊息。
由於實際時間是由非安全環境提供,因此與到期相關的標記會列在軟體強制執行的清單中。
信任根繫結
Keystore 要求金鑰必須繫結至信任根,這是啟動期間提供給 KeyMint 安全硬體的位元字串,最好由系統啟動載入程式提供。這個位元字串會以加密方式繫結至 KeyMint 管理的每個金鑰。
信任根由公開金鑰的雜湊組成,用於驗證開機映像檔的簽章和裝置的鎖定狀態。如果變更公開金鑰,允許使用不同的系統映像檔,或變更鎖定狀態,則除非還原先前的信任根,並啟動由該金鑰簽署的系統,否則先前系統建立的 KeyMint 保護金鑰都無法使用。目標是讓攻擊者安裝的作業系統無法使用 KeyMint 金鑰,藉此提高軟體強制執行的金鑰存取控管價值。
隨機號碼產生器重新播種
安全硬體會為金鑰內容和初始化向量 (IV) 產生隨機號碼,但硬體隨機號碼產生器不一定完全可靠,因此 KeyMint HAL 提供介面,允許 Keystore 提供額外的熵,並混入產生的隨機號碼。
使用硬體隨機號碼產生器做為主要種子來源。透過外部 API 提供的種子資料,不得做為產生數字的唯一隨機來源。此外,所用的混合運算必須確保只要有任何一個種子來源無法預測,隨機輸出內容就無法預測。