本页中包含与 Android 6.0 中 Keystore 的功能相关的信息。
Keystore 能够提供以下类别的操作:
生成或导入密钥时,必须指定协议元素(例如,目的、模式和填充,以及访问控制限制),这些元素会永久绑定到相应密钥,以确保无法以任何其他方式使用相应密钥。
除了上面列出的操作外,Keymaster 实现还必须再提供一项服务,即随机数生成服务,但该服务并不作为 API 进行提供。该服务仅供在内部使用,用于生成密钥、初始化矢量 (IV)、随机填充,以及其他需要具有随机性的安全协议元素。
所有实现都必须提供:
KM_PAD_RSA_PSS
)KM_PAD_RSA_PKCS1_1_5_SIGN
)KM_PAD_RSA_OAEP
)KM_PAD_RSA_PKCS1_1_5_ENCRYPT
)KM_PAD_NONE
和 KM_PAD_PKCS7
。采用“无填充”时,如果输入的不是分块大小的倍数,CBC 或 ECB 模式的加密必须失败。
强烈建议(但并不强制要求)提供 SHA1,以及 SHA2 系列的其他成员(SHA-224、SHA384 和 SHA512)。如果硬件 Keymaster 实现未提供这些内容,Keystore 将在软件中提供它们。
此外,为了实现与其他系统的互用性,还建议提供以下基元:
如果攻击者可以随意使用在任何情况下都无法从设备获取的基于硬件的密钥,那么此类密钥将无法提供太多的安全性(尽管它们比可被窃取的密钥更安全)。因此,Keystore 强制执行访问控制至关重要。
访问控制指的是由“标记/值”对组成的“授权列表”。授权标记是 32 位整数,其值有多种类型。有些标记可以重复使用,以指定多个值。某个标记是否可重复使用是在关于该标记的文档中指定的。密钥创建好后,调用程序会指定一个授权列表。Keymaster 实现使用的底层 Keystore 将会修改该列表,以指定一些额外的信息(例如,密钥是否有防回滚保护),并且会返回一个“最终”授权列表(编码到返回的密钥 Blob 中)。如果最终授权列表被修改了,那么任何尝试使用相应密钥进行任何加密操作的行为都必须失败。
枚举 keymaster_authorization_tag_t
中定义了一组可能的标记,这组标记必须永远保持不变(不过可进行扩展)。这些标记的名称带有 KM_TAG_
前缀。标记 ID 的前四位用于指明类型。
可能的类型包括:
KM_ENUM
:很多标记的值都是在枚举中定义的。例如,KM_TAG_PURPOSE
的可能值是在枚举 keymaster_purpose_t
中定义的。
KM_ENUM_REP
:与 KM_ENUM
相同,不过此标记可在授权列表中重复使用。重复使用此标记表明有多个已获授权的值。例如,某个加密密钥可能有 KM_PURPOSE_ENCRYPT
和 KM_PURPOSE_DECRYPT
。
KM_UINT
:32 位未签名整数。例如:KM_TAG_KEY_SIZE
KM_UINT_REP
:与 KM_UINT
相同,不过此标记可在授权列表中重复使用。重复使用此标记表明有多个已获授权的值。
KM_ULONG
:64 位未签名整数。例如:KM_TAG_RSA_PUBLIC_EXPONENT
KM_ULONG_REP
:与 KM_ULONG
相同,不过此标记可在授权列表中重复使用。重复使用此标记表明有多个已获授权的值。
KM_DATE
:日期/时间值,以距 1970 年 1 月 1 日的毫秒数表示。例如:KM_TAG_PRIVKEY_EXPIRE_DATETIME
KM_BOOL
:True 或 False。对于 KM_BOOL
类型的标记,如果不存在则被视为“false”,如果存在则被视为“true”。例如:KM_TAG_ROLLBACK_RESISTANT
KM_BIGNUM
:任意长度的整数,以字节数数组表示(采用大端字节存)。例如:KM_TAG_RSA_PUBLIC_EXPONENT
KM_BYTES
:一系列字节数。例如:KM_TAG_ROOT_OF_TRUST
并非所有安全硬件都将实现相同的功能。为了支持多种方法,Keymaster 1.0 会对安全域和非安全域访问控制强制执行(分别称为硬件强制执行和软件强制执行)加以区分。
实现必须:
用于声明由硬件强制执行的授权的 API 机制位于 keymaster_key_characteristics_t
结构中。它将授权列表划分成两个子列表:hw_enforced
和 sw_enforced
。安全硬件负责根据它可以强制执行的内容在每个子列表中放入适当的值。
此外,Keystore 会实现基于软件强制执行所有授权,无论它们是否由安全硬件强制执行。
让我们以一个不支持密钥过期日期且基于 TrustZone 的实现为例。实现仍可能会创建一个具有过期日期的密钥。该密钥的授权列表将包含具有过期日期的 KM_TAG_ORIGINATION_EXPIRE_DATETIME
标记。向 Keystore 发出的密钥特性请求将会在 sw_enforced
列表中找到此标记,并且安全硬件不会强制执行过期日期要求。不过,如果尝试在过期日期之后使用该密钥,则会被 Keystore 拒绝。
如果设备随后进行了升级,采用了不支持过期日期的安全硬件,那么密钥特性请求将会在 hw_enforced
列表中找到 KM_TAG_ORIGINATION_EXPIRE_DATETIME
,并且即使以某种方式破坏或规避 Keystore,尝试在过期日期之后使用相应密钥也会失败。
以下标记用于定义使用关联密钥的操作的加密特性:KM_TAG_ALGORITHM
、KM_TAG_KEY_SIZE
、KM_TAG_BLOCK_MODE
、KM_TAG_PADDING
、KM_TAG_CALLER_NONCE
和 KM_TAG_DIGEST
KM_TAG_PADDING
、KM_TAG_DIGEST
和 KM_PAD_BLOCK_MODE
可重复使用,这意味着可以将多个值与一个密钥相关联,并且要使用的值将在操作时指定。
密钥有一组关联的目的,这些目的以一个或多个带有 KM_TAG_PURPOSE
标记(用于定义可以如何使用相应密钥)的授权条目表示。这些目的是:
KM_PURPOSE_ENCRYPT
KM_PURPOSE_DECRYPT
KM_PURPOSE_SIGN
KM_PURPOSE_VERIFY
任意密钥都可以具有这些目的任意组合。请注意,有些组合会带来安全问题。例如,如果某个 RSA 密钥可用于加密和签名,那么能够诱使系统解密任意数据的攻击者就可以利用该密钥来生成签名。
Keymaster 仅支持以 X.509 格式导出公钥,并支持:
为了确保导入的密钥可与安全生成的密钥区分开来,相应密钥授权列表中会包含 KM_TAG_ORIGIN
。例如,如果密钥是在安全硬件中生成的,hw_enforced
密钥特性列表中将有值为 KM_ORIGIN_GENERATED
的 KM_TAG_ORIGIN
,如果密钥是导入到安全硬件中的,值将为 KM_ORIGIN_IMPORTED
。
安全的 Keymaster 实现不会实现用户身份验证,但会依赖于其他实现用户身份验证的可信应用。对于必须由这些应用实现的接口,请参阅 Gatekeeper 页面。
用户身份验证要求是通过两组标记指定的。第一组用于指明哪些用户可以使用相应密钥:
KM_TAG_ALL_USERS
表示所有用户都可以使用相应密钥。如果有此标记,则不得有 KM_TAG_USER_ID
和 KM_TAG_SECURE_USER_ID
。
KM_TAG_USER_ID
有一个数字值,用于指定已获授权用户的 ID。请注意,此值是 Android 用户 ID(适用于多用户环境)而非应用 UID,且仅由非安全软件强制执行。如果有此标记,则不得有 KM_TAG_ALL_USERS
。
KM_TAG_SECURE_USER_ID
有一个 64 位数字值,用于指定安全用户 ID。必须在安全身份验证令牌中提供该 ID,才能获得使用相应密钥的授权。在重复使用此标记的情况下,只要在安全身份验证令牌中提供了此标记的任何一个值,即可使用相应密钥。
第二组用于指明是否必须对用户进行身份验证以及何时进行验证。如果不存在以下任一标记,但有 KM_TAG_SECURE_USER_ID
,则表示每次使用相应密钥时均需要经过身份验证。
KM_NO_AUTHENTICATION_REQUIRED
表示无需进行任何用户身份验证,不过仍只有以通过 KM_TAG_USER_ID
指定的用户身份运行的应用可以使用相应密钥。KM_TAG_AUTH_TIMEOUT
是一个数字值,用于指定用户身份验证必须多新(以秒数计)才能授权使用相应密钥。此标记仅适用于私钥/密钥操作。公钥操作不需要进行身份验证。设备重新启动后超时将会失效;设备重新启动后,所有密钥的状态均为“从未经过身份验证”。可以将超时设为一个较大的值,以指明每次设备启动后只需进行一次身份验证(2^32 秒约为 136 年;Android 设备的重新启动时间间隔一般不会超过该值)。
客户端绑定(即将密钥与特定客户端应用相关联)是通过一个可选客户端 ID 和一些可选客户端数据(分别是 KM_TAG_APPLICATION_ID
和 KM_TAG_APPLICATION_DATA
)实现的。Keystore 会将这些值视为不透明 Blob,仅用于确保密钥生成/导入期间存在的 Blob 在每次使用相应密钥时都存在,并且每个字节都完全相同。客户端绑定数据不是由 Keymaster 返回的。调用程序必须知道这些数据,才能使用相应密钥。
此功能未提供给应用。
Keystore 支持按日期限制密钥的使用。可以将密钥有效期开始日期和过期日期同密钥相关联,这样一来,如果当前日期/时间不在有效期范围内,Keymaster 将拒绝执行密钥操作。密钥有效期范围是使用 KM_TAG_ACTIVE_DATETIME
、KM_TAG_ORIGINATION_EXPIRE_DATETIME
和 KM_TAG_USAGE_EXPIRE_DATETIME
标记指定的。“ORIGINATION”和“USAGE”之间的区别在于使用相应密钥是为了“生成”新的密文/签名/等,还是“使用”现有密文/签名/等。请注意,此区别未提供给应用。
KM_TAG_ACTIVE_DATETIME
、KM_TAG_ORIGINATION_EXPIRE_DATETIME
和 KM_TAG_USAGE_EXPIRE_DATETIME
是可选标记。如果缺少这些标记,相应密钥会被视为可随时用于解密/验证消息。
由于挂钟时间是由非安全域提供的,因此与过期日期相关的标记不可能位于由硬件强制执行的列表中。如果由硬件强制执行过期日期,将需要安全域以某种方式获取可信时间和数据,例如通过具有可信远程时间服务器的质询响应协议。
Keystore 要求将密钥绑定到一个信任根。信任根是在启动期间提供给 Keymaster 安全硬件的一个位串(最好由引导加载程序提供)。该位串必须以加密形式绑定到由 Keymaster 管理的每个密钥。
信任根包含一个公钥,该公钥用于验证启动映像上的签名和设备的锁定状态。如果该公钥被更改了(以允许使用不同的系统映像),或锁定状态发生了变化,之前的系统创建的受 Keymaster 保护的所有密钥都将无法再使用,除非之前的信任根已恢复并且通过相应密钥签名的系统已启动。这是为了确保由攻击者安装的操作系统无法使用 Keymaster 密钥,从而提高由软件强制执行的密钥访问控制所发挥的作用。
有些 Keymaster 安全硬件可以将密钥材料存储在内部并返回句柄(而非经过加密的密钥材料)。也可能会存在相应密钥在一些其他非安全域或安全域系统组件可用之前无法使用的其他情况。Keymaster 1.0 HAL 允许调用程序通过 KM_TAG_STANDALONE
标记请求将密钥设为“独立”密钥,这意味着,除了 Blob 和运行中的 Keymaster 系统之外,不需要任何其他资源。要想知道某个密钥是否为独立密钥,可以查看与该密钥关联的标记。目前只为此标记定义了两个值:
KM_BLOB_STANDALONE
KM_BLOB_REQUIRES_FILE_SYSTEM
此功能未提供给应用。
密钥创建好后,可以通过 KM_TAG_MIN_SECONDS_BETWEEN_OPS
指定使用时间间隔上限。如果距离上次使用相应密钥执行操作的时间还没有超过 KM_TAG_MIN_SECONDS_BETWEEN_OPS
秒,TrustZone 实现将拒绝再次使用相应密钥执行加密操作。
要实现使用时间间隔上限,一种非常简单的方法是创建一个用于存放密钥 ID 和上次使用时间戳的表格。该表格可能有大小限制,但必须能够容纳至少 16 个条目。如果该表格已被占满,并且没有任何可以更新或舍弃的条目,那么安全硬件实现必须“安全失败”,最好是拒绝所有受密钥使用时间间隔限制的密钥操作,直到其中一个条目过期为止。可设为所有条目在设备重新启动时过期。
也可以通过 KM_TAG_MAX_USES_PER_BOOT
将密钥限制为每次设备启动后最多使用 n 次。这也需要一个跟踪表格(必须能够容纳至少 4 个密钥),并且也必须能够安全失败。请注意,应用无法创建按设备启动限制使用次数的密钥。该功能不会在 Keystore 之外提供,而且仅用于系统操作。
此功能未提供给应用。
由于安全硬件必须生成随机数(在密钥材料中使用)和初始化矢量 (IV),而且硬件随机数生成器可能并非始终可信,因此 Keymaster HAL 会提供一个接口,以便客户端提供额外的熵(将与生成的随机数混合在一起)。
必须使用硬件随机数生成器作为主要种子来源,并且通过外部 API 提供的种子数据不能是生成数字时所用随机数据的唯一来源。此外,如果有任何一个种子来源不可预测,所使用的混合操作必须要确保随机输出不可预测。
此功能未提供给应用,但可供框架使用。框架会定期为安全硬件提供从 Java SecureRandom 实例获取的其他熵。