Keystore 提供了一个更安全的位置,让您能够以可控方式创建、存储和使用加密密钥。如果有硬件支持的密钥存储区可用,则使用该存储区比从设备中提取密钥材料更安全,并且 Keymaster 强制执行的限制会更难以打破。
不过,仅当已知 Keystore 密钥位于由硬件支持的存储区中时,才能够实现这一点。在 Keymaster 1 中,应用或远程服务器无法可靠地验证是否属于这种情况。Keystore 守护程序加载可用的 Keymaster 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
类型
Keymaster 2 及更低版本
typedef struct { keymaster_blob_t* entries; size_t entry_count; } keymaster_cert_chain_t;
AttestKey
方法
Keymaster 3
attestKey(vec<uint8_t> keyToAttest, vec<KeyParameter> attestParams) generates(ErrorCode error, vec<vec<uint8_t>> certChain);
Keymaster 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_ID
和Tag::APPLICATION_DATA
。如果在密钥生成过程中指定了后两个参数,则它们是解密密钥 Blob 所必需的参数。certChain
是输出参数,会返回一个证书数组。条目 0 是认证证书,表示它会对keyToAttest
中的密钥进行认证,并且包含认证扩展。
attestKey
方法被视为对经过认证的密钥进行的公钥操作,因为它可以随时调用,不需要满足授权限制条件。例如,如果经过认证的密钥需要进行用户身份验证才能使用,则该方法可以在不进行用户身份验证的情况下生成认证。
认证证书
认证证书是标准的 X.509 证书,具有可选的认证扩展,其中包含对经过认证的密钥的描述。证书采用出厂配置的认证密钥进行签名,该密钥使用的算法与正在进行认证的密钥相同(例如,RSA 密钥使用 RSA 算法,EC 密钥使用 EC 算法)。
认证证书中包含下表所列的字段,且不得包含任何其他字段。某些字段会指定固定的字段值。CTS 测试会验证证书内容是否与所定义的内容完全一致。
证书序列
字段名称(请参见 RFC 5280) | 值 |
---|---|
tbsCertificate | TBSCertificate 序列 |
signatureAlgorithm | 用于签署密钥的算法的 AlgorithmIdentifier: ECDSA 用于 EC 密钥,RSA 用于 RSA 密钥。 |
signatureValue | BIT STRING,在 ASN.1 DER 编码的 tbsCertificate 上计算的签名。 |
TBSCertificate 序列
字段名称(请参见 RFC 5280) | 值 |
---|---|
version |
INTEGER 2(表示 v3 证书) |
serialNumber |
INTEGER 1(固定值:所有证书上的值都相同) |
signature |
用于签署密钥的算法的 AlgorithmIdentifier:ECDSA 用于 EC 密钥,RSA 用于 RSA 密钥。 |
issuer |
与批量认证密钥的主题字段相同。 |
validity |
两个日期的序列,包含 Tag::ACTIVE_DATETIME 和 Tag::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::SIGN 或 KeyPurpose::VERIFY 用途,则设置该值。所有其他位均未设置。 |
extensions/CRL Distribution Points |
值待定 |
extensions/"attestation" |
OID 为 1.3.6.1.4.1.11129.2.1.17;内容在下文的“认证扩展”部分中定义。与所有 X.509 证书扩展一样,内容表示为一个 OCTET_STRING,其中包含认证序列的 DER 编码。 |
认证扩展
attestation
扩展包含对与密钥相关联的 Keymaster 授权的完整描述,其所采用的结构直接对应于 Android 和 Keymaster HAL 中使用的授权列表。授权列表中的各个标记分别由一个 ASN.1 SEQUENCE
条目表示,通过 Keymaster 标记编号进行显式标记,而类型描述符(4 个高值位)则采用掩码处理。
例如,在 Keymaster 3 中,Tag::PURPOSE
在 types.hal 中被定义为 ENUM_REP | 1
。对于认证扩展,ENUM_REP
值会被移除,但留下标记 1
。(对于 Keymaster 2 及更低版本,KM_TAG_PURPOSE
在 keymaster_defs.h 中定义。)
根据下表所示,值通过一种简单的方式转换为 ASN.1 类型:
Keymaster 类型 | ASN.1 类型 |
---|---|
ENUM |
整数 |
ENUM_REP |
整数集 |
UINT |
整数 |
UINT_REP |
整数集 |
ULONG |
整数 |
ULONG_REP |
整数集 |
DATE |
整数(自 1970 年 1 月 1 日 GMT 00:00:00 以来的毫秒数) |
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), }
KeyDescription 字段
keymasterVersion
和 attestationChallenge
字段通过位置(而非标记)进行标识,因此编码形式的标记仅指定字段类型。其余字段采用隐式标记,如架构中所指定。
字段名称 | 类型 | 值 |
---|---|---|
attestationVersion |
整数 | 认证架构的版本:1、2 或 3。 |
attestationSecurity |
SecurityLevel | 此认证的安全级别。可以对由硬件支持的密钥实现软件认证。如果 Android 系统遭到入侵,则此类认证不可信。 |
keymasterVersion |
整数 | Keymaster 设备的版本,例如 0、1、2、3 或 4。 |
keymasterSecurity |
SecurityLevel | Keymaster 实现的安全级别。 |
attestationChallenge |
OCTET_STRING | Tag::ATTESTATION_CHALLENGE 的值,根据认证请求指定。 |
uniqueId |
OCTET_STRING | 可选的唯一 ID,如果密钥具有 Tag::INCLUDE_UNIQUE_ID ,则存在该值 |
softwareEnforced |
AuthorizationList | 可选值,不会被 TEE 强制执行的 Keymaster 授权(如果存在)。 |
teeEnforced |
AuthorizationList | 可选值,会被 TEE 强制执行的 Keymaster 授权(如果存在)。 |
AuthorizationList 字段
AuthorizationList
字段均为可选字段并通过 Keymaster 标记值来标识,类型位则采用掩码处理。此类字段使用显式标记,因此还包含一个可指明其 ASN.1 类型的标记,这样更易于解析。
如需详细了解各个字段的值,如果是 Keymaster 3,请参阅 types.hal
,如果是 Keymaster 2 及更低版本,请参阅 keymaster_defs.h
。通过省略 KM_TAG
前缀并将其余部分更改为驼峰式大小写形式,Keymaster 标记名称被转换为了字段名称,因此 Tag::KEY_SIZE
变成了 keySize
。
RootOfTrust 字段
RootOfTrust
字段通过位置进行标识。
字段名称 | 类型 | 值 |
---|---|---|
verifiedBootKey |
OCTET_STRING | 用于验证系统映像的密钥的安全哈希。建议使用 SHA-256。 |
deviceLocked |
BOOLEAN | 如果引导加载程序已锁定,则该值为 true,这表示只能刷入已签名的映像,并表示验证启动检查已完成。 |
verifiedBootState |
VerifiedBootState | 启动时验证的状态。 |
verifiedBootHash |
OCTET_STRING | 受启动时验证保护的所有数据的摘要。对于使用 Android 启动时验证功能实现(针对启动时验证功能)的设备,此值包含 VBMeta 结构或启动时验证元数据结构的摘要。如需详细了解如何计算此值,请参阅 VBMeta 摘要 |
VerifiedBootState 值
verifiedBootState
的值具有以下含义:
值 | 含义 |
---|---|
Verified |
表示实现了从引导加载程序到已验证分区的完整信任链,其中包括引导加载程序、启动分区和所有已验证的分区。 在此状态下, verifiedBootKey 值是嵌入证书的哈希值,表示不可更改的证书已烧录到 ROM 中。 |
SelfSigned |
表示已使用嵌入的证书验证启动分区,并且签名有效。在允许启动过程继续之前,引导加载程序会显示一条警告以及公钥的指纹。
在此状态下, verifiedBootKey 值是自签名证书的哈希值。 |
Unverified |
表示可以随意修改设备。设备完整性由用户进行带外验证。在允许启动过程继续之前,引导加载程序会向用户显示一条警告。 在此状态下, verifiedBootKey 值为空。 |
Failed |
表示设备验证失败。实际上,并没有任何认证证书包含此值,因为引导加载程序在此状态下会停止运行。此处包含该值是为了实现完整性。 |
SecurityLevel 值
securityLevel
的值具有以下含义:
值 | 含义 |
---|---|
Software |
创建或管理相关元素(认证或密钥)的代码已在 Android 系统中实现,如果 Android 系统遭到入侵,该代码可能会被更改。 |
TrustedEnvironment |
创建或管理相关元素(认证或密钥)的代码已在可信执行环境 (TEE) 中实现。如果 TEE 遭到入侵,该代码可能会被更改;不过,TEE 对远程入侵具有很强的抵抗力,并且可以通过直接硬件攻击来适度抵御入侵。 |
StrongBox |
创建或管理相关元素(认证或密钥)的代码已在专用硬件安全模块中实现。如果硬件安全模块遭到入侵,该代码可能会被更改;不过,它对远程入侵具有很强的抵抗力,并且可以通过直接硬件攻击来强力抵御入侵。 |
唯一 ID
唯一 ID 是一个用于标识设备的 128 位值,但只能在限定时间段内使用。该值通过以下公式计算得出:
HMAC_SHA256(T || C || R, HBK)
其中:
T
是“时间计数器值”,通过将Tag::CREATION_DATETIME
的值除以 2592000000 再舍去所有余数计算得出。T
每 30 天变化一次 (2592000000 = 30 * 24 * 60 * 60 * 1000)。C
是Tag::APPLICATION_ID
的值- 如果 attest_key 调用的 attest_params 参数中存在
Tag::RESET_SINCE_ID_ROTATION
,则R
为 1;如果不存在该标记,则为 0。 HBK
是可信执行环境已知的绑定到硬件的唯一密钥,可信执行环境绝不会显示该密钥。该密钥包含至少 128 位的熵,并且对各个设备都是唯一的(根据 128 位的熵,概率上的唯一性是可接受的)。HBK 应该通过 HMAC 或 AES_CMAC 从融合密钥材料中派生。
将 HMAC_SHA256 输出截断为 128 位。
认证密钥和证书
两个密钥、一个 RSA 和一个 ECDSA 以及相应的证书链均已安全地部署到设备中。
ID 认证
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 中。请勿存储这些 ID 值的其他副本(存储在系统中的正常位置除外),设备所有者可以通过获取 root 权限修改这些值:
S = D || HMAC(HBK, D)
其中:
D = HMAC(HBK, ID1) || HMAC(HBK, ID2) || ... || HMAC(HBK, IDn)
HMAC
是具有适当安全哈希的 HMAC 结构(建议使用 SHA-256)HBK
是不用于任何其他用途的绑定到硬件的密钥ID1...IDn
是原始 ID 值;特定值与特定索引之间的关联取决于实现,因为不同设备将具有不同数量的标识符||
表示串联
由于 HMAC 输出的大小是固定的,因此不需要标头或其他结构就能够查找单个 ID 哈希或 D 结构的 HMAC。实现不仅需要检查所收到的值以执行认证,还需要验证 S。验证 S 的方法是从 S 中提取 D,计算 HMAC(HBK、D),然后将计算结果与 S 中的值进行比较,从而确认没有任何单个 ID 遭到修改/损坏。此外,在实现过程中,对所有单个 ID 元素进行比较的时间都必须是固定不变的,并且必须对 S 进行验证。不论所提供的 ID 有多少,也不论测试的任何部分是否正确匹配,比较时间都必须是固定不变的。
硬件标识符
ID 认证支持以下硬件标识符:
- 品牌名称,由 Android 中的
Build.BRAND
返回 - 设备名称,由 Android 中的
Build.DEVICE
返回 - 产品名称,由 Android 中的
Build.PRODUCT
返回 - 制造商名称,由 Android 中的
Build.MANUFACTURER
返回 - 型号名称,由 Android 中的
Build.MODEL
返回 - 序列号
- 所有无线装置的 IMEI
- 所有无线装置的 MEID
为了支持设备 ID 认证,设备需要证明这些标识符。运行 Android 的所有设备都具有前 6 种标识符,它们是此功能正常发挥作用所必需的元素。如果设备具有任何集成的手机无线装置,那么还必须支持对无线装置的 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。我们提供 Java API 是为了帮助实现人员了解应用如何使用该功能。系统组件可能会以各种不同方式使用该功能,这就是我们不将本部分作为规范的重要原因。