本页中包含与 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
- 支持 224 位、256 位、384 位和 521 位密钥,分别使用 NIST P-224、P-256、P-384 和 P-521 曲线
- ECDSA 所需的摘要模式:
- 无摘要(已弃用,将于日后移除)
- SHA-256
- AES
- 支持 128 位和 256 位密钥
- CBC、CTR、ECB 和 GCM。GCM 实现不允许使用少于 96 位的标记,也不允许使用 96 位以外的随机数长度。
- CBC 和 ECB 模式支持填充模式
PaddingMode::NONE
和PaddingMode::PKCS7
。采用“无填充”时,如果输入的不是分块大小的倍数,CBC 或 ECB 模式的加密会失败。
- HMAC SHA-256,其中任意密钥均不得短于 32 个字节。
对于 Keymaster 实现,强烈建议提供 SHA1,以及 SHA2 系列的其他成员(SHA-224、SHA384 和 SHA512)。如果硬件 Keymaster 实现未提供这些内容,则 Keystore 会在软件中提供它们。
此外,为了实现与其他系统的互操作性,还建议提供以下基元:
- 适用于 RSA 的较小密钥大小
- 适用于 RSA 的任意公开指数
密钥访问控制
如果攻击者可以随意使用在任何情况下都无法从设备获取的基于硬件的密钥,那么此类密钥将无法提供太多的安全性(尽管它们比可被窃取的密钥更安全)。因此,密钥库强制执行访问权限控制至关重要。
访问权限控制指的是由“标记/值”对组成的“授权列表”。授权标记是 32 位整数,其值有多种类型。有些标记可重复使用,以指定多个值。某个标记是否可重复使用是在关于该标记的文档中指定的。密钥创建好后,调用程序会指定一个授权列表。Keymaster 实现使用的底层 Keystore 会修改该列表,以指定一些额外的信息(例如,密钥是否有防回滚保护),并且会返回一个“最终”授权列表(编码到返回的密钥 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
:true 或 false。对于 BOOL
类型的标记,如果不存在则被视为“false”,如果存在则被视为“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
标记。向密钥库发出的密钥特性请求将会在 sw_enforced
列表中找到此标记,并且安全硬件不会强制执行过期日期要求。不过,如果尝试在过期日期之后使用该密钥,则会被密钥库拒绝。
如果设备随后进行了升级,采用了不支持过期日期的安全硬件,那么密钥特性请求将会在 hw_enforced
列表中找到 TAG::ORIGINATION_EXPIRE_DATETIME
,并且即使以某种方式破坏或规避密钥库,尝试在过期日期之后使用相应密钥也会失败。
如需详细了解如何确定密钥是否受硬件支持,请参阅密钥认证。
加密消息构建授权
以下标记用于定义使用关联密钥的操作的加密特性: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 实现不会实现用户身份验证,但会依赖于其他实现用户身份验证的可信应用。对于这些应用实现的接口,请参阅 Gatekeeper 页面。
用户身份验证要求是通过两组标记指定的。第一组用于指明哪些用户可以使用相应密钥:
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。要在安全身份验证令牌中提供该 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
由密钥库(而非 Keymaster)强制执行。不过,在 Android 12 及更高版本中,密钥库会在设备处于锁定状态时以加密方式保护 UNLOCKED_DEVICE_REQUIRED
密钥,以确保当设备处于锁定状态时,在大多数情况下,即使密钥库遭到入侵时,也无法使用这些密钥。
为了实现这一点,密钥库会在将所有 UNLOCKED_DEVICE_REQUIRED
密钥存储到其数据库之前对其进行“超级加密”,并尽可能在设备处于锁定状态时保护超级加密密钥(超级密钥),以便只有成功解锁设备才能恢复这些密钥。(之所以使用“超级加密”一词,是因为除了 Keymaster 已对所有密钥应用的加密层之外,还应用了这一加密层。)
每位用户(包括个人资料)都有两个与 UNLOCKED_DEVICE_REQUIRED
关联的超级密钥:
- UnlockedDeviceRequired 对称超级密钥。这是 AES‑256‑GCM 密钥。它用于加密在系统为用户解锁设备时导入或生成的
UNLOCKED_DEVICE_REQUIRED
密钥。 - UnlockedDeviceRequired 非对称超级密钥。这是 ECDH P‐521 密钥对。它用于加密在系统为用户锁定设备时导入或生成的
UNLOCKED_DEVICE_REQUIRED
密钥。使用此非对称密钥加密的密钥会在首次使用时使用对称密钥重新加密(这只能在设备处于未锁定状态时发生)。
密钥库会在用户创建期间生成这些超级密钥,并将其存储在自己的数据库中,而且会使用用户的合成密码对其进行加密。这样一来,就可以使用 PIN 码、图案或密码等效方式来恢复它们。
密钥库还会将这些超级密钥缓存在内存中,以便能够对 UNLOCKED_DEVICE_REQUIRED
密钥执行操作。不过,只有在系统为用户解锁设备时,它才会尝试缓存这些密钥的秘密部分。当系统为用户锁定设备时,密钥库会尽可能将这些超级密钥的秘密部分的缓存副本归零。具体来说,当系统为用户锁定设备时,密钥库会选择并应用以下三种保护级别之一来保护用户的 UnlockedDeviceRequired 超级密钥:
- 如果用户仅启用了 PIN 码、图案或密码,密钥库会将其缓存的超级密钥的秘密部分归零。这样一来,超级密钥将只能通过数据库中的加密副本来恢复,而该副本只能通过 PIN 码、图案或密码等效方式进行解密。
- 如果用户仅启用了第 3 类(“强”)生物识别,并且启用了 PIN 码、图案或密码,密钥库会安排超级密钥可通过用户已注册为 PIN 码、图案或密码等效方式替代方法的任何第 3 类生物识别(通常为指纹)进行恢复。为此,它会生成一个新的 AES‑256‑GCM 密钥,使用该密钥加密超级密钥的秘密部分,将 AES‑256‑GCM 密钥作为生物识别绑定密钥导入到 Keymaster 中(要求在过去 15 秒内成功完成生物识别验证),然后将所有这些密钥的明文副本归零。
- 如果用户启用了第 1 类(“便利”)生物识别、第 2 类(“弱”)生物识别或主动解锁可信代理,密钥库会以明文形式缓存超级密钥。在这种情况下,系统不会为
UNLOCKED_DEVICE_REQUIRED
密钥提供加密保护。用户可以通过不启用这些解锁方法来避免这种不太安全的后备机制。属于这些类别的最常见解锁方法包括许多设备上的人脸解锁,以及使用配对的智能手表解锁。
当系统为用户解锁设备时,密钥库会尽可能恢复用户的 UnlockedDeviceRequired 超级密钥。对于 PIN 码、图案或密码等效解锁方式,它会解密数据库中存储的这些密钥的副本。否则,它会检查是否保存了使用生物识别绑定密钥加密的这些密钥的副本,如果保存了,则尝试解密该副本。仅当用户在过去 15 秒内成功使用第 3 类生物识别技术完成身份验证时,此操作才会成功,此操作由 Keymaster(而非密钥库)强制执行。
客户端绑定
客户端绑定(即将密钥与特定客户端应用相关联)是通过一个可选客户端 ID 和一些可选客户端数据(分别是 TAG::APPLICATION_ID
和 TAG::APPLICATION_DATA
)实现的。密钥库会将这些值视为不透明 Blob,仅用于确保密钥生成/导入期间存在的 Blob 在每次使用相应密钥时都存在,并且每个字节都完全相同。客户端绑定数据不是由 Keymaster 返回的。调用程序必须知道这些数据,才能使用相应密钥。
此功能未提供给应用。
过期日期
Keystore 支持按日期限制密钥的使用。可以将密钥有效期开始日期和过期日期同密钥相关联,这样一来,如果当前日期/时间不在有效期范围内,Keymaster 会拒绝执行密钥操作。密钥有效期范围是使用 TAG::ACTIVE_DATETIME
、TAG::ORIGINATION_EXPIRE_DATETIME
和 TAG::USAGE_EXPIRE_DATETIME
标记指定的。“ORIGINATION”和“USAGE”之间的区别在于使用相应密钥是为了“生成”新的密文/签名/等,还是“使用”现有密文/签名/等。请注意,此区别未提供给应用。
TAG::ACTIVE_DATETIME
、TAG::ORIGINATION_EXPIRE_DATETIME
和 TAG::USAGE_EXPIRE_DATETIME
是可选标记。如果缺少这些标记,相应密钥会被视为可随时用于解密/验证消息。
由于挂钟时间是由非安全域提供的,因此与过期日期相关的标记不可能位于由硬件强制执行的列表中。如果由硬件强制执行过期日期,将需要安全域以某种方式获取可信时间和数据,例如通过具有可信远程时间服务器的质询响应协议。
信任根绑定
Keystore 要求将密钥绑定到一个信任根。信任根是在启动期间提供给 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 次。n这也需要一个跟踪表格(能够容纳至少 4 个密钥),并且也能够安全失败。请注意,应用无法创建按设备启动限制使用次数的密钥。该功能不会在 Keystore 之外提供,而且仅用于系统操作。
此功能未提供给应用。
随机数生成器补种
由于安全硬件会生成随机数(在密钥材料中使用)和初始化矢量 (IV),而且硬件随机数生成器可能并非始终可信,因此 Keymaster HAL 会提供一个接口,以便客户端提供额外的熵(将与生成的随机数混合在一起)。
使用硬件随机数生成器作为主要种子来源。通过外部 API 提供的种子数据不能是生成数字时所用随机数据的唯一来源。此外,如果有任何一个种子来源不可预测,则所使用的混合操作需要确保随机输出不可预测。
此功能未提供给应用,但可供框架使用。框架会定期为安全硬件提供从 Java SecureRandom 实例检索到的其他熵。