本頁面提供 Android 6.0 以上版本 Keystore 加密功能的相關資訊。
密碼編譯基元
Keystore 提供下列作業類別:
- 金鑰產生
- 匯入及匯出非對稱金鑰 (不含金鑰包裝)
- 匯入原始對稱金鑰 (不包裝金鑰)
- 使用適當的填充模式進行非對稱加密和解密
- 使用摘要和適當的填充模式,進行非對稱式簽署和驗證
- 在適當模式下進行對稱加密和解密,包括 AEAD 模式
- 產生及驗證對稱訊息驗證碼
產生或匯入金鑰時,系統會指定用途、模式和填充等通訊協定元素,並永久綁定至金鑰,確保金鑰無法以任何其他方式使用。存取控制限制
除了上述清單中的服務外,Keymaster 實作也提供另一項服務,但並未公開為 API:隨機數字產生。這項功能會在內部用於產生金鑰、初始化向量 (IV)、隨機填充和其他需要隨機性的安全通訊協定元素。
必要的原始物件
所有 Keymaster 實作項目都提供以下項目:
- 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
- 支援使用 NIST P-224、P-256、P-384 和 P-521 曲線的 224、256、384 和 521 位元金鑰
- ECDSA 的摘要模式:
- 無摘要 (已淘汰,日後將移除)
- SHA-256
- AES
- 支援 128 和 256 位元金鑰
- CBC、CTR、ECB 和 GCM。GCM 實作不允許使用小於 96 位元或 96 位元以外的 Nonce 長度的標記。
- CBC 和 ECB 模式支援填充模式
PaddingMode::NONE
和PaddingMode::PKCS7
。如果沒有填充,如果輸入內容不是區塊大小的倍數,CBC 或 ECB 模式加密作業就會失敗。
- HMAC SHA-256,金鑰大小至少為 32 個位元組。
我們強烈建議在實作 Keymaster 時使用 SHA1 和 SHA2 系列的其他成員 (SHA-224、SHA384 和 SHA512)。如果硬體 Keymaster 實作未提供這些值,Keystore 會在軟體中提供這些值。
建議您使用某些原始類型,以便與其他系統互通:
- 縮小 RSA 金鑰大小
- RSA 的任意公冪
金鑰存取權控管
如果攻擊者可以隨意使用硬體金鑰,那麼從裝置中提取的硬體金鑰就無法提供太多安全性 (雖然比可擷取的金鑰安全)。因此,Keystore 必須強制執行存取權控管機制。
存取控制項的定義為標記/值組合的「授權清單」。授權標記為 32 位元整數,值則有多種類型。部分代碼可重複使用,用於指定多個值。標記是否可重複使用,是由 KeyMint (先前稱為 Keymaster) HAL 介面指定。建立金鑰時,呼叫端會指定授權清單。Keystore 底層的 Keymaster 實作會修改清單,指定一些額外資訊,例如金鑰是否具有復原保護功能,並傳回「最終」授權清單,並將其編碼至傳回的金鑰 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
。
UINT
:32 位元不帶正負號整數。示例:
TAG::KEY_SIZE
UINT_REP
:與 UINT
相同,但標記可在授權清單中重複出現。重複表示有多個授權值。
ULONG
:64 位元不帶正負號整數。示例:
TAG::RSA_PUBLIC_EXPONENT
ULONG_REP
:與 ULONG
相同,但標記可在授權清單中重複出現。重複表示有多個授權值。
DATE
:日期/時間值,以 1970 年 1 月 1 日為基準,以毫秒為單位表示。示例:TAG::PRIVKEY_EXPIRE_DATETIME
BOOL
:是或否。如果沒有 BOOL
類型的標記,系統會假設該標記為「false」,如果有 BOOL
類型的標記,則會假設該標記為「true」。示例:TAG::ROLLBACK_RESISTANT
BIGNUM
:任意長度的整數,以大端序順序表示為位元組陣列。示例:
TAG::RSA_PUBLIC_EXPONENT
BYTES
:位元組序列。示例:
TAG::ROOT_OF_TRUST
硬體與軟體執行
並非所有安全硬體實作都包含相同的功能。為了支援各種方法,Keymaster 會區分安全和非安全的存取控制強制執行機制,或硬體和軟體強制執行機制。
所有實作方式:
- 強制執行所有授權的完全比對 (非強制執行)。 金鑰 Blob 中的授權清單會與金鑰產生期間傳回的授權完全相符,包括排序。任何不相符的項目都會導致錯誤診斷。
- 宣告要強制執行語意值的授權。
用於宣告硬體強制授權的 API 機制位於 keymaster_key_characteristics_t
結構中。將授權清單分為兩個子清單:hw_enforced
和 sw_enforced
。安全硬體會根據可強制執行的內容,在每個位置放入適當的值。
此外,Keystore 會實作軟體式強制執行機制,適用於所有授權,無論是否由安全硬體強制執行。
舉例來說,假設您採用的 TrustZone 實作方式不支援金鑰到期。系統可能仍會建立具有到期日的金鑰。該金鑰的授權清單包含標記 TAG::ORIGINATION_EXPIRE_DATETIME
和到期日。向 KeyStore 提出關鍵特性要求時,會在 sw_enforced
清單中找到這個標記,而安全硬體不會強制執行到期要求。不過,如果在到期後嘗試使用金鑰,Keystore 會拒絕。
如果裝置升級至支援到期日的安全硬體,關鍵特性要求會在 hw_enforced
清單中找到 TAG::ORIGINATION_EXPIRE_DATETIME
,並嘗試在到期日後使用金鑰,即使 KeyStore 遭到破壞或略過,這項嘗試也會失敗。
如要進一步瞭解如何判斷金鑰是否有硬體支援,請參閱「金鑰認證」。
密碼編譯訊息結構授權
下列標記可用於定義使用關聯金鑰的作業的加密編譯特性:TAG::ALGORITHM
、TAG::KEY_SIZE
、TAG::BLOCK_MODE
、TAG::PADDING
、TAG::CALLER_NONCE
和 TAG::DIGEST
TAG::PADDING
、TAG::DIGEST
和 PaddingMode::BLOCK_MODE
可重複使用,也就是說,單一鍵可與多個值建立關聯,而要使用的值則會在運作時指定。
目的
金鑰具有一組相關用途,以標記為 TAG::PURPOSE
的一或多個授權項目表示,這些項目定義了金鑰的使用方式。目的如下:
KeyPurpose::ENCRYPT
KeyPurpose::DECRYPT
KeyPurpose::SIGN
KeyPurpose::VERIFY
任何鍵都可以有這些用途的任意子集。請注意,某些組合會造成安全性問題。舉例來說,RSA 金鑰可用於加密和簽署,因此攻擊者可以說服系統解密任意資料,進而產生簽章。
匯入和匯出
Keymaster 僅支援以 X.509 格式匯出公開金鑰,以及匯入以下項目:
- 採用 DER 編碼 PKCS#8 格式的公開和私密金鑰組,不採用密碼加密
- 對稱金鑰為原始位元組
為確保匯入的金鑰可與安全產生的金鑰區分開,TAG::ORIGIN
會納入適當的金鑰授權清單。舉例來說,如果金鑰是在安全硬體中產生,則金鑰特性的 hw_enforced
清單中會找到值為 KeyOrigin::GENERATED
的 TAG::ORIGIN
,而匯入安全硬體的金鑰則會具有 KeyOrigin::IMPORTED
值。
使用者驗證
安全 Keymaster 實作項目不會實作使用者驗證,而是依賴其他可信任的應用程式來執行驗證。如要瞭解這些應用程式實作的介面,請參閱 「守門員」頁面。
使用者驗證要求會透過兩組標記指定。第一組會指出哪些使用者可以使用金鑰:
TAG::ALL_USERS
表示所有使用者都能使用此鍵。如果存在,則TAG::USER_ID
和TAG::USER_SECURE_ID
不存在。TAG::USER_ID
包含指定授權使用者 ID 的數值。請注意,這是 Android 使用者 ID (適用於多使用者),而非應用程式 UID,且僅由非安全軟體強制執行。如果存在,則TAG::ALL_USERS
不存在。TAG::USER_SECURE_ID
具有 64 位元數值,可指定安全驗證權杖中提供的安全使用者 ID,以便解鎖金鑰使用權。如果重複,只要安全驗證權杖中提供任何值,即可使用該金鑰。
第二組則指出使用者是否需要驗證,以及驗證的時間。如果這兩個標記都沒有,但 TAG::USER_SECURE_ID
存在,則每次使用索引鍵時都需要驗證。
NO_AUTHENTICATION_REQUIRED
表示不需要使用者驗證,但金鑰仍只能由以TAG::USER_ID
指定使用者身分執行的應用程式使用。TAG::AUTH_TIMEOUT
是數值,以秒為單位指定使用者驗證需要多新,才能授權使用金鑰。這項設定僅適用於私密/機密金鑰作業。公開金鑰作業不需要驗證。逾時不會跨越重新啟動;重新啟動後,所有金鑰都不會經過驗證。您可以將逾時時間設為較大的值,表示每次啟動都需要驗證 (2^32 秒約為 136 年,Android 裝置的重新啟動頻率可能會超過這個值)。
需要解鎖裝置
含有 TAG::UNLOCKED_DEVICE_REQUIRED
的鍵只能在裝置解鎖時使用。如需詳細語意說明,請參閱
KeyProtection.Builder#setUnlockedDeviceRequired(boolean)
。
UNLOCKED_DEVICE_REQUIRED
是由 Keystore 強制執行,而非由 Keymaster 強制執行。不過,在 Android 12 以上版本中,Keystore 會在裝置鎖定時以加密方式保護 UNLOCKED_DEVICE_REQUIRED
金鑰,以確保在大多數情況下,即使 Keystore 在裝置鎖定時遭到入侵,也無法使用這些金鑰。
為達成這項目標,Keystore 會在將所有 UNLOCKED_DEVICE_REQUIRED
金鑰儲存到資料庫前進行「超級加密」,並盡可能在裝置鎖定時保護超級加密金鑰 (超級金鑰),以便在裝置解鎖後才能復原。(之所以使用「超級加密」一詞,是因為 Keymaster 已將加密機制套用至所有金鑰,而這層加密機制是「在」套用這些加密機制時一併套用。)
每個使用者 (包括設定檔) 都有兩個與 UNLOCKED_DEVICE_REQUIRED
相關聯的超級金鑰:
- UnlockedDeviceRequired 對稱式超級金鑰。這是 AES-256-GCM 金鑰。它會在裝置為使用者解鎖時,加密匯入或產生的
UNLOCKED_DEVICE_REQUIRED
金鑰。 - UnlockedDeviceRequired 非對稱超級金鑰。這是 ECDH P-521 金鑰組。這項功能會為使用者在裝置鎖定時匯入或產生的
UNLOCKED_DEVICE_REQUIRED
金鑰加密。使用這個非對稱金鑰加密的金鑰,會在首次使用時以對稱式金鑰重新加密 (這項作業只能在裝置解鎖時執行)。
Keystore 會在使用者建立時產生這些超級金鑰,並將這些金鑰儲存在資料庫中,並以使用者綜合密碼加密。這樣一來,您就能使用 PIN 碼、解鎖圖案或密碼來復原這些項目。
Keystore 也會在記憶體中快取這些超級金鑰,讓 Keystore 能夠對 UNLOCKED_DEVICE_REQUIRED
金鑰運作。不過,只有在裝置為使用者解鎖時,系統才會嘗試快取這些金鑰的機密部分。當裝置為使用者上鎖時,Keystore 會盡可能將這些超級金鑰的秘密部分快取副本設為零。具體來說,當裝置為使用者鎖定時,Keystore 會為使用者的 UnlockedDeviceRequired 超級金鑰選取並套用以下三種保護層級之一:
- 如果使用者只啟用 PIN 碼、解鎖圖案或密碼,Keystore 就會將快取超級金鑰的機密部分設為零。這樣一來,超級金鑰只能透過資料庫中的加密副本復原,而該副本只能透過 PIN 碼、圖案或密碼解密。
- 如果使用者只啟用了 第 3 類 (「強式」) 生物特徵辨識和 PIN 碼、解鎖圖案或密碼,Keystore 就會安排使用者註冊的任何第 3 類生物特徵辨識 (通常是指紋) 來復原超級金鑰,做為 PIN 碼、解鎖圖案或密碼的替代方案。為此,它會產生新的 AES‑256‑GCM 金鑰,並使用該金鑰加密超級金鑰的機密部分,然後將 AES‑256‑GCM 金鑰匯入 Keymaster,做為需要在過去 15 秒內成功通過生物特徵辨識驗證的生物特徵辨識綁定金鑰,並將所有這些金鑰的明文副本設為零。
- 如果使用者啟用了第 1 級 (「便利」) 生物特徵辨識、第 2 級 (「弱」) 生物特徵辨識或有效的解鎖信任代理程式,Keystore 就會將超級金鑰快取在明文中。在這種情況下,系統不會提供
UNLOCKED_DEVICE_REQUIRED
金鑰的加密編譯安全性。使用者可以選擇不啟用這些解鎖方法,避免使用安全性較低的備用方法。這類最常見的解鎖方法包括許多裝置上的臉部解鎖功能,以及透過配對的智慧手錶解鎖。
當裝置為使用者解鎖時,Keystore 會盡可能復原使用者的 UnlockedDeviceRequired 超級金鑰。對於 PIN 碼、圖案或密碼等同解鎖功能,系統會解密儲存在資料庫中的這些金鑰副本。否則,系統會檢查是否已儲存這些金鑰的副本,並嘗試解密這些金鑰。只有在使用者在過去 15 秒內成功使用 Class 3 生物特徵辨識驗證 (由 Keymaster (而非 Keystore) 強制執行) 時,此操作才會成功。
用戶端繫結
用戶端繫結 (將金鑰與特定用戶端應用程式建立關聯) 是透過選用的用戶端 ID 和部分選用的用戶端資料 (分別為 TAG::APPLICATION_ID
和 TAG::APPLICATION_DATA
) 完成。Keystore 會將這些值視為不透明的 blob,只確保在每個用途中,鍵產生/匯入期間顯示的 blob 都相同,且每個位元組都相同。Keymaster 不會傳回用戶端繫結資料。呼叫端必須知道這項資訊,才能使用金鑰。
這項功能不會公開給應用程式。
到期日
KeyStore 支援依日期限制金鑰用量。金鑰有效開始日期和金鑰到期日可與金鑰建立關聯,如果目前日期/時間不在有效範圍內,Keymaster 就會拒絕執行金鑰作業。鍵的有效範圍會使用 TAG::ACTIVE_DATETIME
、TAG::ORIGINATION_EXPIRE_DATETIME
和 TAG::USAGE_EXPIRE_DATETIME
標記指定。「產生」和「使用」的差異在於金鑰是否用於「產生」新的密文/簽名/等,或是用於「使用」現有的密文/簽名/等。請注意,這項差異不會公開給應用程式。
TAG::ACTIVE_DATETIME
、TAG::ORIGINATION_EXPIRE_DATETIME
和 TAG::USAGE_EXPIRE_DATETIME
標記為選用項目。如果沒有標記,系統會假設該金鑰一律可用於解密/驗證訊息。
由於時鐘時間是由非安全世界提供,因此不太可能會在硬體強制執行的清單中找到到期日相關的標記。硬體執行到期作業的規定,需要安全世界以某種方式取得可信任的時間和資料,例如透過具有可信任的遠端時間伺服器的挑戰回應通訊協定。
信任根繫結
金鑰庫要求金鑰繫結至信任根,這是在啟動期間提供給 Keymaster 安全硬體的位元字串,最好由引導程式提供。這個位元字串會以密碼學方式繫結至 Keymaster 管理的每個金鑰。
信任根包含用於驗證開機映像檔和裝置鎖定狀態的公開金鑰。如果公開金鑰變更為允許使用其他系統映像檔,或是鎖定狀態變更,則先前系統建立的 Keymaster 保護金鑰將無法使用,除非先前的信任根已還原,且已啟動由該金鑰簽署的系統。目標是讓攻擊者安裝的作業系統無法使用 Keymaster 金鑰,藉此提高軟體強制金鑰存取控管的價值。
獨立金鑰
部分 Keymaster 安全硬體可選擇將金鑰內容儲存在內部,並傳回句柄,而非加密金鑰內容。或者,在某些情況下,可能需要先使用其他不安全或安全的系統元件,才能使用金鑰。Keymaster HAL 可讓呼叫端透過 TAG::STANDALONE
標記要求金鑰為「獨立」的,這表示除了 blob 和執行中的 Keymaster 系統之外,不需要任何資源。您可以檢查與鍵相關聯的標記,瞭解鍵是否為獨立的。目前只定義了兩個值:
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) 產生隨機號碼,且硬體隨機號碼產生器不一定完全可靠,因此 Keymaster HAL 提供介面,讓用戶端可提供額外的資訊熵,並將其混合至產生的隨機號碼中。
使用硬體隨機號碼產生器做為主要的種子來源。透過外部 API 提供的種子資料,並非用於產生號碼的唯一隨機來源。此外,如果任何一個種子來源無法預測,則使用的混合運算必須確保隨機輸出無法預測。
這項功能不會公開給應用程式,而是由架構使用,後者會定期提供額外的隨機性,從 Java SecureRandom 例項擷取,並傳送至安全硬體。