Keystore 提供了一个更安全的地方来以受控方式创建、存储和使用加密密钥。当硬件支持的密钥存储可用并使用时,密钥材料更安全,不会从设备中提取,并且 Keymaster 会实施难以破坏的限制。
但是,只有在已知密钥库密钥位于硬件支持的存储中时,这才是正确的。在 Keymaster 1 中,应用程序或远程服务器无法可靠地验证是否是这种情况。密钥库守护进程加载了可用的 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
方法
钥匙大师 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 ) | 价值 |
---|---|
tbs证书 | TBSC证书序列 |
签名算法 | 用于签署密钥的算法的 AlgorithmIdentifier: ECDSA 用于 EC 密钥,RSA 用于 RSA 密钥。 |
签名值 | BIT STRING,在 ASN.1 DER 编码的 tbsCertificate 上计算的签名。 |
TBSC证书序列
字段名称(参见RFC 5280 ) | 价值 |
---|---|
version | INTEGER 2(表示 v3 证书) |
serialNumber | INTEGER 1(固定值:所有证书都相同) |
signature | 用于签署密钥的算法的算法标识符: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,其中包含证明 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 中,标签存在意味着真,不存在意味着假。 相同的语义适用于 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), }
键描述字段
keymasterVersion
和attestationChallenge
字段是按位置标识的,而不是按标签标识的,因此编码形式的标签仅指定字段类型。其余字段被隐式标记为架构中指定的。
字段名称 | 类型 | 价值 |
---|---|---|
attestationVersion | 整数 | 证明架构的版本:1、2 或 3。 |
attestationSecurity | 安全级别 | 此证明的安全级别。可以获得硬件支持的密钥的软件证明。如果 Android 系统遭到入侵,则无法信任此类证明。 |
keymasterVersion | 整数 | keymaster 设备的版本: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
3 的 types.hal 和keymaster_defs.h
2 及以下版本的 keymaster_defs.h。通过省略KM_TAG
前缀并将其余部分更改为驼峰式大小写,将 Keymaster 标记名称转换为字段名称,因此Tag::KEY_SIZE
变为keySize
。
RootOfTrust 字段
RootOfTrust
字段是按位置标识的。
字段名称 | 类型 | 价值 |
---|---|---|
verifiedBootKey | OCTET_STRING | 用于验证系统映像的密钥的安全散列。推荐使用 SHA-256。 |
deviceLocked | 布尔值 | 如果引导加载程序被锁定,则为真,这意味着只能刷新签名的映像,并且已完成验证引导检查。 |
verifiedBootState | 验证启动状态 | 已验证启动的状态。 |
verifiedBootHash | OCTET_STRING | 受验证启动保护的所有数据的摘要。对于使用 Verified Boot 的 Android Verified Boot 实现的设备,此值包含VBMeta struct或 Verified Boot 元数据结构的摘要。要了解有关如何计算此值的更多信息,请参阅VBMeta 摘要 |
VerifiedBootState 值
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)。 -
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,以及相应的证书链,被安全地配置到设备中。
身份证明
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 值的其他副本,除了系统中的正常位置,设备所有者可以通过生根来修改:
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 证明支持以下硬件标识符:
- 品牌名称,由 Android 中的
Build.BRAND
返回 - 设备名称,由 Android 中的
Build.DEVICE
返回 - 产品名称,由 Android 中的
Build.PRODUCT
返回 - 制造商名称,由 Android 中的
Build.MANUFACTURER
返回 - 模型名称,由 Android 中的
Build.MODEL
返回 - 序列号
- 所有收音机的 IMEI
- 所有电台的 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。这是为了帮助实施者了解应用程序如何使用该功能。系统组件可能会以不同的方式使用它,这就是为什么本节不应被视为规范的原因。