利用 Android 密钥库系统,您可以在容器中存储加密密钥,从而提高从设备中提取密钥的难度。在密钥进入密钥库后,可以将它们用于加密操作,而密钥材料仍不可导出。此外,它提供了密钥使用的时间和方式限制措施,例如要求进行用户身份验证才能使用密钥,或者限制为只能在某些加密模式中使用。如需了解详情,请参阅安全功能部分。
密钥库系统由 Android 4.0(API 级别 14)中引入的 KeyChain
API、Android 4.3(API 级别 18)中引入的 Android 密钥库提供程序功能以及作为 Jetpack 的一部分提供的 Security 库使用。本文介绍何时以及如何使用 Android 密钥库提供程序。
安全功能
Android 密钥库系统可以保护密钥材料免遭未经授权的使用。首先,Android 密钥库可以防止从应用进程和 Android 设备中整体提取密钥材料,从而避免了在 Android 设备之外以未经授权的方式使用密钥材料。其次,Android 密钥库可以让应用指定密钥的授权使用方式,并在应用进程之外强制实施这些限制,从而避免了在 Android 设备上以未经授权的方式使用密钥材料。
提取防范
Android 密钥库密钥使用两项安全措施来避免密钥材料被提取:
- 密钥材料永不进入应用进程。通过 Android 密钥库密钥执行加密操作时,应用会在后台将待签署或验证的明文、密文和消息馈送到执行加密操作的系统进程。如果应用进程受到攻击,攻击者也许能使用应用密钥,但无法提取密钥材料(例如,在 Android 设备以外使用)。
- 您可以将密钥材料绑定至 Android 设备的安全硬件,例如可信执行环境 (TEE) 和安全元件 (SE)。为密钥启用此功能时,其密钥材料永远不会暴露于安全硬件之外。如果 Android 操作系统受到攻击或者攻击者可以读取设备内部存储空间,攻击者也许能在 Android 设备上使用任意应用的 Android 密钥库,但无法从设备上提取这些数据。只有设备的安全硬件支持密钥算法、分块模式、填充方案和密钥有权配合使用的摘要的特定组合时,才可启用此功能。要检查是否为密钥启用了此功能,请获取密钥的
KeyInfo
并检查KeyInfo.isInsideSecurityHardware()
的返回值。
硬件安全模块
运行 Android 9(API 级别 28)或更高版本的受支持设备可拥有 StrongBox Keymaster,它是位于硬件安全模块中的 Keymaster HAL 的一种实现。该模块包含以下组成部分:
- 自己的 CPU。
- 安全存储空间。
- 真实随机数生成器。
- 可抵御软件包篡改和未经授权旁加载应用的附加机制。
检查存储在 StrongBox Keymaster 中的密钥时,系统会通过可信执行环境 (TEE) 证实密钥的完整性。
为支持低能耗的 StrongBox 实现,为一部分算法和密钥大小提供了支持:
- RSA 2048
- AES 128 和 256
- ECDSA P-256
- HMAC-SHA256(支持 8-64 字节密钥大小,含首末值)
- Triple DES 168
使用 KeyStore
类生成或导入密钥时,您需要通过将 true
传递给 setIsStrongBoxBacked()
方法,指示在 StrongBox Keymaster 中存储密钥的偏好。
密钥使用授权
为了避免在 Android 设备上以未经授权的方式使用密钥,在生成或导入密钥时,Android 密钥库会让应用指定密钥的授权使用方式。一旦生成或导入密钥,其授权将无法更改。然后,每次使用密钥时,都会由 Android 密钥库强制执行授权。这是一项高级安全功能,通常仅用于有以下要求的情形:在生成/导入密钥后(而不是之前或当中),应用进程受到攻击不会导致密钥以未经授权的方式使用。
支持的密钥使用授权分为以下几类:
- 加密:授权密钥算法、运算或目的(加密、解密、签署、验证)、填充方案、分块模式以及可与密钥搭配使用的摘要;
- 时间有效性间隔:密钥获得使用授权的时间间隔;
- 用户身份验证:密钥只能在用户最近进行身份验证时使用。请参阅要求进行用户身份验证才能使用密钥。
作为一项额外的安全措施,对于密钥材料位于安全硬件内的密钥(请参阅 KeyInfo.isInsideSecurityHardware()
),某些密钥使用授权可能会由安全硬件强制执行,具体取决于 Android 设备。加密和用户身份验证授权可能由安全硬件强制执行。由于安全硬件一般不具备独立的安全实时时钟,时间有效性间隔授权不可能由其强制执行。
您可以使用 KeyInfo.isUserAuthenticationRequirementEnforcedBySecureHardware()
查询密钥的用户身份验证授权是否由安全硬件强制执行。
选择密钥链或 Android 密钥库提供程序
如果您需要系统全局凭据,请使用 KeyChain
API。在应用通过 KeyChain
API 请求使用任何凭据时,用户需要通过系统提供的界面选择应用可以访问已安装的哪些凭据。因此,在用户同意的情况下多个应用可以使用同一套凭据。
使用 Android 密钥库提供程序让各个应用存储自己的凭据,并且只允许应用自身访问。通过这种方式,应用可以管理仅可供自身使用的凭据,同时所提供的安全优势还可媲美 KeyChain
API 为系统全局凭据提供的安全优势。这一方法不需要用户选择凭据。
使用 Android 密钥库提供程序
如需使用此功能,请使用标准的 KeyStore
和 KeyPairGenerator
或 KeyGenerator
类,以及在 Android 4.3(API 级别 18)中引入的 AndroidKeyStore
提供程序。
AndroidKeyStore
在与KeyStore.getInstance(type)
方法搭配使用时注册为 KeyStore
类型,在与 KeyPairGenerator.getInstance(algorithm, provider)
和 KeyGenerator.getInstance(algorithm, provider)
方法搭配使用时注册为提供程序。
生成新私钥
生成新的 PrivateKey
还需要指定自签名证书具备的初始 X.509 属性。
Security 库为生成有效的对称密钥提供了默认实现,如以下代码段所示:
Kotlin
// Although you can define your own key generation parameter specification, it's // recommended that you use the value specified here. val keyGenParameterSpec = MasterKeys.AES256_GCM_SPEC val masterKeyAlias = MasterKeys.getOrCreate(keyGenParameterSpec)
Java
// Although you can define your own key generation parameter specification, it's // recommended that you use the value specified here. KeyGenParameterSpec keyGenParameterSpec = MasterKeys.AES256_GCM_SPEC; String masterKeyAlias = MasterKeys.getOrCreate(keyGenParameterSpec);
或者,您也可以稍后使用 KeyStore.setKeyEntry
将证书替换为由证书授权机构 (CA) 签名的证书。
如需生成密钥,请使用 KeyPairGenerator
和 KeyPairGeneratorSpec
。
Kotlin
/* * Generate a new EC key pair entry in the Android Keystore by * using the KeyPairGenerator API. The private key can only be * used for signing or verification and only with SHA-256 or * SHA-512 as the message digest. */ val kpg: KeyPairGenerator = KeyPairGenerator.getInstance( KeyProperties.KEY_ALGORITHM_EC, "AndroidKeyStore" ) val parameterSpec: KeyGenParameterSpec = KeyGenParameterSpec.Builder( alias, KeyProperties.PURPOSE_SIGN or KeyProperties.PURPOSE_VERIFY ).run { setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512) build() } kpg.initialize(parameterSpec) val kp = kpg.generateKeyPair()
Java
/* * Generate a new EC key pair entry in the Android Keystore by * using the KeyPairGenerator API. The private key can only be * used for signing or verification and only with SHA-256 or * SHA-512 as the message digest. */ KeyPairGenerator kpg = KeyPairGenerator.getInstance( KeyProperties.KEY_ALGORITHM_EC, "AndroidKeyStore"); kpg.initialize(new KeyGenParameterSpec.Builder( alias, KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY) .setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512) .build()); KeyPair kp = kpg.generateKeyPair();
生成新密钥
如需生成密钥,请按照与生成新私钥相同的流程进行操作。在每种情况下,您都需要使用 Security 库。
更安全地导入加密密钥
借助 Android 9(API 级别 28)及更高版本,您能够利用 ASN.1 编码密钥格式将已加密密钥安全导入密钥库。Keymaster 随后会在密钥库中将密钥解密,因此密钥的内容永远不会以明文形式出现在设备的主机内存中。此过程提高了密钥解密的安全性。
如需支持以安全方式将已加密密钥导入密钥库,请完成以下步骤:
生成目的为
PURPOSE_WRAP_KEY
的密钥对。建议也为该密钥对添加认证。在您信任的服务器或机器上,生成
SecureKeyWrapper
应包含的 ASN.1 消息。该封装容器包含以下架构:
KeyDescription ::= SEQUENCE { keyFormat INTEGER, authorizationList AuthorizationList } SecureKeyWrapper ::= SEQUENCE { wrapperFormatVersion INTEGER, encryptedTransportKey OCTET_STRING, initializationVector OCTET_STRING, keyDescription KeyDescription, secureKey OCTET_STRING, tag OCTET_STRING }
创建
WrappedKeyEntry
对象,传入字节数组形式的 ASN.1 消息。将该
WrappedKeyEntry
对象传入接受Keystore.Entry
对象的setEntry()
的重载。
使用密钥库条目
AndroidKeyStore
提供程序的使用通过所有标准 KeyStore
API 加以实现。
列出条目
通过调用 aliases()
方法列出密钥库中的条目:
Kotlin
/* * Load the Android KeyStore instance using the * "AndroidKeyStore" provider to list out what entries are * currently stored. */ val ks: KeyStore = KeyStore.getInstance("AndroidKeyStore").apply { load(null) } val aliases: Enumeration<String> = ks.aliases()
Java
/* * Load the Android KeyStore instance using the * "AndroidKeyStore" provider to list out what entries are * currently stored. */ KeyStore ks = KeyStore.getInstance("AndroidKeyStore"); ks.load(null); Enumeration<String> aliases = ks.aliases();
对数据进行签名和验证
通过从密钥库提取 KeyStore.Entry
并使用 Signature
API(例如 sign()
)来为数据签名:
Kotlin
/* * Use a PrivateKey in the KeyStore to create a signature over * some data. */ val ks: KeyStore = KeyStore.getInstance("AndroidKeyStore").apply { load(null) } val entry: KeyStore.Entry = ks.getEntry(alias, null) if (entry !is KeyStore.PrivateKeyEntry) { Log.w(TAG, "Not an instance of a PrivateKeyEntry") return null } val signature: ByteArray = Signature.getInstance("SHA256withECDSA").run { initSign(entry.privateKey) update(data) sign() }
Java
/* * Use a PrivateKey in the KeyStore to create a signature over * some data. */ KeyStore ks = KeyStore.getInstance("AndroidKeyStore"); ks.load(null); KeyStore.Entry entry = ks.getEntry(alias, null); if (!(entry instanceof PrivateKeyEntry)) { Log.w(TAG, "Not an instance of a PrivateKeyEntry"); return null; } Signature s = Signature.getInstance("SHA256withECDSA"); s.initSign(((PrivateKeyEntry) entry).getPrivateKey()); s.update(data); byte[] signature = s.sign();
类似地,请使用 verify(byte[])
方法验证数据:
Kotlin
/* * Verify a signature previously made by a PrivateKey in our * KeyStore. This uses the X.509 certificate attached to our * private key in the KeyStore to validate a previously * generated signature. */ val ks = KeyStore.getInstance("AndroidKeyStore").apply { load(null) } val entry = ks.getEntry(alias, null) as? KeyStore.PrivateKeyEntry if (entry == null) { Log.w(TAG, "Not an instance of a PrivateKeyEntry") return false } val valid: Boolean = Signature.getInstance("SHA256withECDSA").run { initVerify(entry.certificate) update(data) verify(signature) }
Java
/* * Verify a signature previously made by a PrivateKey in our * KeyStore. This uses the X.509 certificate attached to our * private key in the KeyStore to validate a previously * generated signature. */ KeyStore ks = KeyStore.getInstance("AndroidKeyStore"); ks.load(null); KeyStore.Entry entry = ks.getEntry(alias, null); if (!(entry instanceof PrivateKeyEntry)) { Log.w(TAG, "Not an instance of a PrivateKeyEntry"); return false; } Signature s = Signature.getInstance("SHA256withECDSA"); s.initVerify(((PrivateKeyEntry) entry).getCertificate()); s.update(data); boolean valid = s.verify(signature);
要求进行用户身份验证才能使用密钥
生成密钥或将密钥导入到 AndroidKeyStore
时,您可以指定仅授权经过身份验证的用户使用密钥。用户使用安全锁定屏幕凭据(图案/PIN 码/密码、指纹)的子集进行身份验证。
这是一项高级安全功能,通常仅用于有以下要求的情形:在生成/导入密钥后(而不是之前或当中),应用进程受到攻击不会导致密钥被未经身份验证的用户使用。
如果仅授权经过身份验证的用户使用密钥,可将密钥配置为以下列两种模式之一运行:
- 经过身份验证的用户可以在一段时间内使用密钥。在用户解锁安全锁定屏幕或使用
KeyguardManager.createConfirmDeviceCredentialIntent
流确认其安全锁定屏幕凭据后,即授权其使用此模式中的所有密钥。每个密钥的授权持续时间各不相同,并由setUserAuthenticationValidityDurationSeconds
在密钥生成或导入时指定。此类密钥只有在启用了安全锁定屏幕的情况下才能生成或导入(请参阅KeyguardManager.isDeviceSecure()
)。在安全锁定屏幕停用(重新配置为“无”、“滑动”或不验证用户身份的其他模式)或被强制重置(例如由设备管理员执行)时,这些密钥将永久失效。 - 用户身份验证会授权与某一密钥关联的特定加密操作。在此模式中,涉及此类密钥的每个操作都必须由用户单独授权。目前,此类授权的唯一方式是指纹身份验证:
FingerprintManager.authenticate
。此类密钥只有在至少注册一个指纹的情况下才能生成或导入(请参阅FingerprintManager.hasEnrolledFingerprints
)。一旦注册新指纹或取消注册所有指纹,这些密钥将永久失效。
支持的算法
Cipher
算法 | 提供支持的 API 级别 | 备注 |
---|---|---|
AES/CBC/NoPadding | 23+ | |
AES/CBC/PKCS7Padding | 23+ | |
AES/CTR/NoPadding | 23+ | |
AES/ECB/NoPadding | 23+ | |
AES/ECB/PKCS7Padding | 23+ | |
AES/GCM/NoPadding | 23+ | 仅支持 12 字节长的 IV。 |
RSA/ECB/NoPadding | 18+ | |
RSA/ECB/PKCS1Padding | 18+ | |
RSA/ECB/OAEPWithSHA-1AndMGF1Padding | 23+ | |
RSA/ECB/OAEPWithSHA-224AndMGF1Padding | 23+ | |
RSA/ECB/OAEPWithSHA-256AndMGF1Padding | 23+ | |
RSA/ECB/OAEPWithSHA-384AndMGF1Padding | 23+ | |
RSA/ECB/OAEPWithSHA-512AndMGF1Padding | 23+ | |
RSA/ECB/OAEPPadding | 23+ |
KeyGenerator
算法 | 提供支持的 API 级别 | 备注 |
---|---|---|
AES | 23+ | 支持的大小:128、192、256 |
HmacSHA1 | 23+ |
|
HmacSHA224 | 23+ |
|
HmacSHA256 | 23+ |
|
HmacSHA384 | 23+ |
|
HmacSHA512 | 23+ |
|
KeyFactory
算法 | 提供支持的 API 级别 | 备注 |
---|---|---|
EC | 23+ | 支持的密钥规范:KeyInfo (仅私钥)、ECPublicKeySpec (仅公钥)、X509EncodedKeySpec (仅公钥) |
RSA | 23+ | 支持的密钥规范:KeyInfo (仅私钥)、RSAPublicKeySpec (仅公钥)、X509EncodedKeySpec (仅公钥) |
KeyStore
KeyStore 支持的密钥类型与KeyPairGenerator
和 KeyGenerator
支持的相同。KeyPairGenerator
算法 | 提供支持的 API 级别 | 备注 |
---|---|---|
DSA | 19-22 | |
EC | 23+ |
在 API 级别 23 前,EC 密钥可使用经“RSA”算法初始化的 |
RSA | 18+ |
|
Mac
算法 | 提供支持的 API 级别 | 备注 |
---|---|---|
HmacSHA1 | 23+ | |
HmacSHA224 | 23+ | |
HmacSHA256 | 23+ | |
HmacSHA384 | 23+ | |
HmacSHA512 | 23+ |
Signature
算法 | 提供支持的 API 级别 | 备注 |
---|---|---|
MD5withRSA | 18+ | |
NONEwithECDSA | 23+ | |
NONEwithRSA | 18+ | |
SHA1withDSA | 19-22 | |
SHA1withECDSA | 19+ | |
SHA1withRSA | 18+ | |
SHA1withRSA/PSS | 23+ | |
SHA224withDSA | 20-22 | |
SHA224withECDSA | 20+ | |
SHA224withRSA | 20+ | |
SHA224withRSA/PSS | 23+ | |
SHA256withDSA | 19-22 | |
SHA256withECDSA | 19+ | |
SHA256withRSA | 18+ | |
SHA256withRSA/PSS | 23+ | |
SHA384withDSA | 19-22 | |
SHA384withECDSA | 19+ | |
SHA384withRSA | 18+ | |
SHA384withRSA/PSS | 23+ | |
SHA512withDSA | 19-22 | |
SHA512withECDSA | 19+ | |
SHA512withRSA | 18+ | |
SHA512withRSA/PSS | 23+ |
SecretKeyFactory
算法 | 提供支持的 API 级别 | 备注 |
---|---|---|
AES | 23+ | 支持的密钥规范:KeyInfo |
HmacSHA1 | 23+ | 支持的密钥规范:KeyInfo |
HmacSHA224 | 23+ | 支持的密钥规范:KeyInfo |
HmacSHA256 | 23+ | 支持的密钥规范:KeyInfo |
HmacSHA384 | 23+ | 支持的密钥规范:KeyInfo |
HmacSHA512 | 23+ | 支持的密钥规范:KeyInfo |
博客文章
请参阅博客文章在 ICS 中统一密钥库访问。