密鑰和 ID 證明

金鑰庫提供了一個更安全的地方,以受控的方式建立、儲存和使用加密金鑰。當硬體支援的密鑰儲存可用並使用時,密鑰材料可以更安全地防止從設備中提取,並且 Keymaster 會強制實施難以破壞的限制。

然而,只有當已知密鑰庫密鑰位於硬體支援的儲存中時,這才是正確的。在 Keymaster 1 中,應用程式或遠端伺服器無法可靠​​地驗證情況是否如此。金鑰庫守護程式會載入可用的金鑰主控 HAL,並相信 HAL 關於金鑰硬體支援的任何說法。

為了解決這個問題,Keymaster 在 Android 7.0 (Keymaster 2) 中引入了密鑰認證,在 Android 8.0 (Keymaster 3) 中引入了 ID 認證。

密鑰證明旨在提供一種方法來強有力地確定非對稱密鑰對是否受硬體支援、密鑰的屬性是什麼以及對其使用應用了哪些約束。

ID 證明允許裝置提供其硬體識別碼的證明,例如序號或 IMEI。

金鑰認證

為了支援密鑰證明,Android 7.1 向 HAL 引入了一組標籤、類型和方法。

標籤

  • Tag::ATTESTATION_CHALLENGE
  • Tag::INCLUDE_UNIQUE_ID
  • Tag::RESET_SINCE_ID_ROTATION

類型

鑰匙大師 2 以下

typedef struct {
    keymaster_blob_t* entries;
    size_t entry_count;
} keymaster_cert_chain_t;

AttestKey方法

鑰匙大師3

    attestKey(vec<uint8_t> keyToAttest, vec<KeyParameter> attestParams)
        generates(ErrorCode error, vec<vec<uint8_t>> certChain);

鑰匙大師 2 以下

keymaster_error_t (*attest_key)(const struct keymaster2_device* dev,
        const keymaster_key_blob_t* key_to_attest,
        const keymaster_key_param_set_t* attest_params,
        keymaster_cert_chain_t* cert_chain);
  • dev是 keymaster 裝置結構。
  • keyToAttest是從generateKey傳回的金鑰blob,將為其建立證明。
  • attestParams是證明所需的任何參數的列表。這包括Tag::ATTESTATION_CHALLENGE和可能的Tag::RESET_SINCE_ID_ROTATION ,以及Tag::APPLICATION_IDTag::APPLICATION_DATA 。如果在金鑰產生期間指定了後兩者,則需要解密金鑰 blob。
  • certChain是輸出參數,它傳回憑證陣列。條目 0 是證明證書,這意味著它驗證來自keyToAttest金鑰並包含證明擴充。

attestKey方法被視為對已證明密鑰的公鑰操作,因為它可以隨時調用,並且不需要滿足授權約束。例如,如果證明金鑰需要使用者認證才能使用,則可以在不進行使用者認證的情況下產生證明。

認證證書

證明證書是標準 X.509 證書,具有可選的證明擴展,其中包含已證明密鑰的描述。該證書是使用經過認證的證明密鑰進行簽署的。證明密鑰可以使用與被證明的密鑰不同的演算法。

認證證書包含下表中的字段,並且不能包含任何其他字段。某些欄位指定固定欄位值。 CTS 測試驗證證書內容是否完全符合定義。

證書序列

欄位名稱(參見RFC 5280價值
tbs證書TBS 證書序列
簽名演算法用於簽署金鑰的演算法的AlgorithmIdentifier:
ECDSA 用於 EC 金鑰,RSA 用於 RSA 金鑰。
簽名值位元字串,根據 ASN.1 DER 編碼的 tbsCertificate 計算的簽章。

TBS 證書序列

欄位名稱(參見RFC 5280價值
version INTEGER 2(表示 v3 憑證)
serialNumber INTEGER 1(固定值:所有憑證都相同)
signature用於簽署金鑰的演算法的 AlgorithmIdentifier:ECDSA 表示 EC 金鑰,RSA 表示 RSA 金鑰。
issuer與批量證明密鑰的主題欄位相同。
validity兩個日期的序列,包含Tag::ACTIVE_DATETIMETag::USAGE_EXPIRE_DATETIME的值。這些值自 1970 年 1 月 1 日起以毫秒為單位。有關證書中的正確日期表示,請參閱RFC 5280
如果Tag::ACTIVE_DATETIME不存在,則使用Tag::CREATION_DATETIME的值。如果Tag::USAGE_EXPIRE_DATETIME不存在,則使用批次證明密鑰憑證的到期日期。
subject CN =“Android Keystore Key”(固定值:所有憑證都相同)
subjectPublicKeyInfo包含經過驗證的公鑰的SubjectPublicKeyInfo。
extensions/Key Usage digitalSignature:設定金鑰是否有KeyPurpose::SIGNKeyPurpose::VERIFY用途。所有其他位元均未設定。
extensions/CRL Distribution Points價值待定
extensions/"attestation" OID為1.3.6.1.4.1.11129.2.1.17;內容在下面的證明擴展部分中定義。與所有 X.509 憑證擴充一樣,內容表示為 OCTET_STRING,其中包含證明 SEQUENCE 的 DER 編碼。

認證延期

attestation擴充包含與金鑰關聯的 keymaster 授權的完整描述,其結構直接對應於 Android 和 keymaster HAL 中使用的授權清單。授權清單中的每個標籤都由一個 ASN.1 SEQUENCE條目表示,明確地以 keymaster 標籤號碼標記,但類型描述符(四個高位)被封鎖。

例如,在 Keymaster 3 中, Tag::PURPOSE在 types.hal 中定義為ENUM_REP | 1 .對於證明擴展, ENUM_REP值被刪除,留下標籤1 。 (對於 Keymaster 2 及更低版本, KM_TAG_PURPOSE在 keymaster_defs.h 中定義。)

根據此表,值以簡單的方式轉換為 ASN.1 類型:

鑰匙大師型ASN.1類型
ENUM整數
ENUM_REP整數集
UINT整數
UINT_REP整數集
ULONG整數
ULONG_REP整數集
DATE INTEGER(自 1970 年 1 月 1 日 00:00:00 GMT 起的毫秒數)
BOOL NULL(在 keymaster 中,標籤存在表示 true,不存在表示 false。
相同的語意適用於 ASN.1 編碼)
BIGNUM目前未使用,因此未定義映射
BYTES OCTET_STRING

模式

證明擴充內容由以下 ASN.1 模式描述。

KeyDescription ::= SEQUENCE {
  attestationVersion         INTEGER, # KM2 value is 1. KM3 value is 2. KM4 value is 3.
  attestationSecurityLevel   SecurityLevel,
  keymasterVersion           INTEGER,
  keymasterSecurityLevel     SecurityLevel,
  attestationChallenge       OCTET_STRING,
  uniqueId                   OCTET_STRING,
  softwareEnforced           AuthorizationList,
  teeEnforced                AuthorizationList,
}

SecurityLevel ::= ENUMERATED {
  Software                   (0),
  TrustedEnvironment         (1),
  StrongBox                  (2),
}

AuthorizationList ::= SEQUENCE {
  purpose                     [1] EXPLICIT SET OF INTEGER OPTIONAL,
  algorithm                   [2] EXPLICIT INTEGER OPTIONAL,
  keySize                     [3] EXPLICIT INTEGER OPTIONAL.
  digest                      [5] EXPLICIT SET OF INTEGER OPTIONAL,
  padding                     [6] EXPLICIT SET OF INTEGER OPTIONAL,
  ecCurve                     [10] EXPLICIT INTEGER OPTIONAL,
  rsaPublicExponent           [200] EXPLICIT INTEGER OPTIONAL,
  rollbackResistance          [303] EXPLICIT NULL OPTIONAL, # KM4
  activeDateTime              [400] EXPLICIT INTEGER OPTIONAL
  originationExpireDateTime   [401] EXPLICIT INTEGER OPTIONAL
  usageExpireDateTime         [402] EXPLICIT INTEGER OPTIONAL
  noAuthRequired              [503] EXPLICIT NULL OPTIONAL,
  userAuthType                [504] EXPLICIT INTEGER OPTIONAL,
  authTimeout                 [505] EXPLICIT INTEGER OPTIONAL,
  allowWhileOnBody            [506] EXPLICIT NULL OPTIONAL,
  trustedUserPresenceRequired [507] EXPLICIT NULL OPTIONAL, # KM4
  trustedConfirmationRequired [508] EXPLICIT NULL OPTIONAL, # KM4
  unlockedDeviceRequired      [509] EXPLICIT NULL OPTIONAL, # KM4
  allApplications             [600] EXPLICIT NULL OPTIONAL,
  applicationId               [601] EXPLICIT OCTET_STRING OPTIONAL,
  creationDateTime            [701] EXPLICIT INTEGER OPTIONAL,
  origin                      [702] EXPLICIT INTEGER OPTIONAL,
  rollbackResistant           [703] EXPLICIT NULL OPTIONAL, # KM2 and KM3 only.
  rootOfTrust                 [704] EXPLICIT RootOfTrust OPTIONAL,
  osVersion                   [705] EXPLICIT INTEGER OPTIONAL,
  osPatchLevel                [706] EXPLICIT INTEGER OPTIONAL,
  attestationApplicationId    [709] EXPLICIT OCTET_STRING OPTIONAL, # KM3
  attestationIdBrand          [710] EXPLICIT OCTET_STRING OPTIONAL, # KM3
  attestationIdDevice         [711] EXPLICIT OCTET_STRING OPTIONAL, # KM3
  attestationIdProduct        [712] EXPLICIT OCTET_STRING OPTIONAL, # KM3
  attestationIdSerial         [713] EXPLICIT OCTET_STRING OPTIONAL, # KM3
  attestationIdImei           [714] EXPLICIT OCTET_STRING OPTIONAL, # KM3
  attestationIdMeid           [715] EXPLICIT OCTET_STRING OPTIONAL, # KM3
  attestationIdManufacturer   [716] EXPLICIT OCTET_STRING OPTIONAL, # KM3
  attestationIdModel          [717] EXPLICIT OCTET_STRING OPTIONAL, # KM3
  vendorPatchLevel            [718] EXPLICIT INTEGER OPTIONAL, # KM4
  bootPatchLevel              [719] EXPLICIT INTEGER OPTIONAL, # KM4
}

RootOfTrust ::= SEQUENCE {
  verifiedBootKey            OCTET_STRING,
  deviceLocked               BOOLEAN,
  verifiedBootState          VerifiedBootState,
  verifiedBootHash           OCTET_STRING, # KM4
}

VerifiedBootState ::= ENUMERATED {
  Verified                   (0),
  SelfSigned                 (1),
  Unverified                 (2),
  Failed                     (3),
}

關鍵描述字段

keymasterVersionattestationChallenge欄位是按位置標識的,而不是按標籤標識的,因此編碼形式中的標籤僅指定欄位類型。其餘欄位依照架構中的指定隱式標記。

欄位名稱類型價值
attestationVersion整數證明架構的版本:1、2 或 3。
attestationSecurity安全等級此證明的安全等級。可以獲得硬體支援的密鑰的軟體證明。如果 Android 系統受到損害,此類證明就無法信任。
keymasterVersion整數密鑰管理設備的版本:0、1、2、3 或 4。
keymasterSecurity安全等級keymaster 實現的安全等級。
attestationChallenge OCTET_STRING Tag::ATTESTATION_CHALLENGE的值,指定給證明請求。
uniqueId OCTET_STRING可選的唯一 ID,如果密鑰具有Tag::INCLUDE_UNIQUE_ID則存在
softwareEnforced授權清單TEE 不強制執行的可選金鑰管理員授權(如果有)。
teeEnforced授權清單由 TEE 強制執行的可選 Keymaster 授權(如果有)。

授權清單字段

AuthorizationList欄位都是可選的,由 keymaster 標籤值標識,類型位元被屏蔽。使用明確標記,因此欄位也包含指示其 ASN.1 類型的標記,以便於解析。

有關每個欄位值的詳細信息,請參閱types.hal (適用於 Keymaster 3)和keymaster_defs.h (適用於 Keymaster 2 及更低版本)。透過省略KM_TAG前綴並將其餘部分變更為駝峰式大小寫,Keymaster 標記名稱已轉換為欄位名稱,因此Tag::KEY_SIZE變成keySize

RootOfTrust 字段

RootOfTrust欄位依位置進行標識。

欄位名稱類型價值
verifiedBootKey OCTET_STRING用於驗證系統映像的金鑰的安全雜湊值。推薦 SHA-256。
deviceLocked布林值如果引導程式被鎖定,則為 true,這表示只能刷新已簽署的映像,並且已完成驗證的引導檢查。
verifiedBootState驗證啟動狀態已驗證啟動的狀態。
verifiedBootHash OCTET_STRING受驗證啟動保護的所有資料的摘要。對於使用驗證啟動的 Android 驗證啟動實作的設備,此值包含VBMeta struct或驗證啟動元資料結構的摘要。要了解有關如何計算此值的更多信息,請參閱VBMeta Digest

已驗證的啟動狀態值

verifiedBootState的值具有以下意義:

價值意義
Verified表示從開機載入程式延伸到已驗證分割區的完整信任鏈,包括開機載入程式、開機分割區和所有已驗證分割區。
在此狀態下, verifiedBootKey值是嵌入式憑證的雜湊值,這表示燒錄到 ROM 中的不可變更的憑證。
此狀態與已驗證的啟動流程文件中記錄的綠色啟動狀態相對應。
SelfSigned表示啟動分區已使用嵌入式憑證進行驗證,且簽章有效。在允許引導過程繼續之前,引導程式會顯示警告和公鑰指紋。
在此狀態下, verifiedBootKey值是自簽章憑證的雜湊值。
此狀態與已驗證的啟動流程文件中記錄的黃色啟動狀態相對應。
Unverified表示設備可以自由修改。設備完整性由使用者進行帶外驗證。引導程式在允許引導程序繼續之前向使用者顯示警告。
在此狀態下, verifiedBootKey值為空。
此狀態與已驗證的啟動流程文件中記錄的橘色啟動狀態相對應。
Failed表示設備驗證失敗。實際上沒有任何證明證書包含此值,因為在這種狀態下引導程式會停止。為了完整起見,將其包含在此處。
此狀態與已驗證的啟動流程文件中記錄的紅色啟動狀態相對應。

安全等級值

securityLevel的值具有以下意義:

價值意義
Software建立或管理相關元素(證明或金鑰)的程式碼在 Android 系統中實現,如果該系統遭到破壞,則可能會被變更。
TrustedEnvironment建立或管理相關元素(證明或金鑰)的程式碼是在可信任執行環境 (TEE) 中實現的。如果 TEE 受到損害,它可能會被更改,但 TEE 對遠程損害有很強的抵抗力,對直接硬體攻擊的損害有一定的抵抗力。
StrongBox建立或管理相關元素(證明或金鑰)的程式碼在專用硬體安全模組中實作。如果硬體安全模組遭到破壞,它可能會被更改,但它對遠端破壞具有很強的抵抗力,並且對直接硬體攻擊的破壞具有很強的抵抗力。

唯一身份

唯一 ID 是一個 128 位元值,用於識別設備,但僅在有限的時間內有效。該值的計算方式為:

HMAC_SHA256(T || C || R, HBK)

在哪裡:

  • T是“時間計數器值”,透過將Tag::CREATION_DATETIME的值除以 2592000000 並刪除任何餘數來計算。 T每 30 天更改一次 (2592000000 = 30 * 24 * 60 * 60 * 1000)。
  • CTag::APPLICATION_ID的值
  • 如果 attest_key 呼叫的 attest_params 參數中存在Tag::RESET_SINCE_ID_ROTATION ,則R為 1;如果標籤不存在,則 R 為 0。
  • HBK是可信賴執行環境已知的唯一硬體綁定秘密,但從未被透露。該秘密至少包含 128 位元熵,對於各個設備來說是唯一的(考慮到 128 位元熵,機率唯一性是可以接受的)。 HBK 應透過 HMAC 或 AES_CMAC 從融合金鑰材料中派生。

將 HMAC_SHA256 輸出截斷為 128 位元。

證明密鑰和憑證

兩個金鑰(一個 RSA 和一個 ECDSA)以及相應的憑證鏈被安全地配置到設備中。

Android 12 引入了遠端金鑰配置,Android 13 要求裝置實現它。遠端金鑰配置為現場設備提供每個應用程式的 ECDSA P256 證明證書。這些證書的有效期比工廠提供的證書短。

多個 IMEI

Android 14 在 Android 金鑰證明記錄中新增了對多個 IMEI 的支援。 OEM 可以透過為第二個 IMEI 新增 KeyMint 標籤來實現此功能。具有多個蜂窩無線電的設備變得越來越普遍,OEM 現在可以支援具有兩個 IMEI 的設備。

OEM 需要擁有輔助 IMEI(如果其設備上存在),並提供給 KeyMint 實現,以便這些實現能夠以與證明第一個 IMEI 相同的方式來證明它

身份證明

Android 8.0 為具有 Keymaster 3 的裝置提供了對 ID 證明的可選支援。ID 證明允許裝置提供其硬體識別碼的證明,例如序號或 IMEI。雖然是一個可選功能,但強烈建議所有 Keymaster 3 實作都提供對其的支持,因為能夠證明設備的身份使真正的零接觸遠端配置等用例更加安全(因為遠端可以確定它正在與正確的設備通信,而不是欺騙其身份的設備)。

ID 證明的工作原理是建立裝置硬體識別碼的副本,在裝置出廠前只有可信任執行環境 (TEE) 可以存取這些副本。使用者可以解鎖裝置的引導程式並更改系統軟體和 Android 框架報告的識別碼。 TEE 持有的識別符副本無法以這種方式進行操作,從而確保設備 ID 證明僅證明設備的原始硬體標識符,從而阻止欺騙嘗試。

ID 證明的主要API 表面建構在Keymaster 2 引入的現有金鑰證明機制之上。當請求keymaster 持有的金鑰的證明憑證時,呼叫者可以要求將裝置的硬體識別碼包含在證明憑證的元數據中。如果金鑰保存在 TEE 中,憑證將連結回已知的信任根。此類憑證的接收者可以驗證憑證及其內容(包括硬體識別碼)是否由 TEE 編寫。當要求在證明證書中包含硬體識別碼時,TEE 僅證明其儲存中保存的識別碼(如工廠車間中填充的那樣)。

儲存特性

保存裝置識別碼的儲存需要具有以下屬性:

  • 從設備的原始識別碼派生的值在設備出廠前複製到儲存中。
  • destroyAttestationIds()方法可以永久銷毀該識別碼派生資料的副本。永久破壞意味著資料已完全刪除,因此恢復出廠設定或在設備上執行的任何其他程式都無法恢復資料。這對於用戶已解鎖引導程式並更改系統軟體並修改 Android 框架返回的標識符的裝置尤其重要。
  • RMA 設施應能產生硬體識別碼派生資料的新副本。這樣,透過RMA的設備就可以再次進行ID認證。 RMA 設施使用的機制必須受到保護,以便使用者無法自行呼叫它,因為這將允許他們獲得欺騙 ID 的證明。
  • 除了 TEE 中的 Keymaster 可信任應用程式之外,沒有任何程式碼能夠讀取儲存中保存的識別碼派生資料。
  • 儲存是防篡改的:如果儲存的內容已被修改,TEE 會將其視為內容副本已被銷毀,並拒絕所有 ID 證明嘗試。這是透過對儲存進行簽署或 MAC 操作來實現的,如下所述
  • 儲存不保存原始識別碼。由於 ID 證明涉及質詢,因此呼叫者始終提供要證明的標識符。 TEE 只需驗證這些值是否與其原始的值相符。儲存原始值的安全雜湊值而不是值可以實現此驗證。

建造

要建立具有上面列出的屬性的實現,請將ID 派生值儲存在以下構造S 中。除了系統中的正常位置(裝置擁有者可以透過root 修改這些位置)之外,不要儲存ID 值的其他副本:

S = D || HMAC(HBK, D)

在哪裡:

  • D = HMAC(HBK, ID 1 ) || HMAC(HBK, ID 2 ) || ... || HMAC(HBK, ID n )
  • HMAC是具有適當安全雜湊的 HMAC 構造(建議 SHA-256)
  • HBK是硬體綁定金鑰,不用於任何其他目的
  • ID 1 ...ID n為原始 ID 值;特定值與特定索引的關聯取決於實現,因為不同的設備將具有不同數量的標識符
  • ||代表連接

由於HMAC 輸出的大小是固定的,因此不需要標頭或其他結構即可找到單一ID 哈希值或D 的HMAC。除了檢查提供的值以執行證明之外,實作還需要透過從S 中提取D來驗證 S ,計算 HMAC(HBK, D) 並將其與 S 中的值進行比較,以驗證沒有單獨的 ID 被修改/損壞。此外,實作必須對所有單獨的 ID 元素和 S 的驗證使用恆定時間比較。無論提供的 ID 數量以及測試的任何部分的正確匹配如何,比較時間都必須恆定。

硬體標識符

ID證明支援以下硬體識別碼:

  1. 品牌名稱,由 Android 中的Build.BRAND返回
  2. 裝置名稱,由 Android 中的Build.DEVICE返回
  3. 產品名稱,由 Android 中的Build.PRODUCT返回
  4. 製造商名稱,由 Android 中的Build.MANUFACTURER返回
  5. 模型名稱,由 Android 中的Build.MODEL返回
  6. 序號
  7. 所有收音機的 IMEI
  8. 所有無線電的 MEID

為了支援設備 ID 證明,設備對這些識別碼進行證明。所有運行 Android 的裝置都有前六個,它們是此功能發揮作用所必需的。如果設備具有任何整合式蜂窩無線電,則設備還必須支援無線電的 IMEI 和/或 MEID 認證。

透過執行金鑰證明並在請求中包含要證明的裝置識別碼來請求 ID 證明。標識符被標記為:

  • ATTESTATION_ID_BRAND
  • ATTESTATION_ID_DEVICE
  • ATTESTATION_ID_PRODUCT
  • ATTESTATION_ID_MANUFACTURER
  • ATTESTATION_ID_MODEL
  • ATTESTATION_ID_SERIAL
  • ATTESTATION_ID_IMEI
  • ATTESTATION_ID_MEID

要證明的識別碼是 UTF-8 編碼的位元組字串。此格式也適用於數字識別碼。每個要證明的識別碼都表示為 UTF-8 編碼的字串。

如果裝置不支援 ID 證明(或先前呼叫了destroyAttestationIds()且裝置無法再證明其 ID),則包含一個或多個這些標籤的任何金鑰證明請求都會失敗,並顯示ErrorCode::CANNOT_ATTEST_IDS

如果裝置支援 ID 證明,且金鑰證明請求中已包含一個或多個上述標籤,則 TEE 會驗證每個標籤提供的識別碼與其硬體識別碼副本相符。如果一個或多個識別碼不匹配,則整個證明將失敗並顯示ErrorCode::CANNOT_ATTEST_IDS 。多次提供同一標籤是有效的。例如,在證明 IMEI 時,這可能很有用:設備可能具有多個具有多個 IMEI 的無線電。如果每個ATTESTATION_ID_IMEI提供的值與設備的無線電之一匹配,則證明請求有效。這同樣適用於所有其他標籤。

如果證明成功,則使用上面的架構將經過證明的 ID 添加到已頒發的證明證書的證明擴展(OID 1.3.6.1.4.1.11129.2.1.17) 中。 Keymaster 2 證明架構的變更以粗體顯示,並附有註解。

Java API

本節僅供參考。 Keymaster 實作者既不實作也不使用 Java API。提供此資訊是為了幫助實施者了解應用程式如何使用該功能。系統元件可能會以不同的方式使用它,這就是為什麼本節不被視為規範是至關重要的。