目录
一、Android 密钥库系统
利用
Android
密钥库系统,您可以在容器中存储加密密钥,从而提高从设备中提取密
钥的难度。在密钥进入密钥库后,可以将它们用于加密操作,而密钥材料仍不可导出。此外,
它提供了密钥使用的时间和方式限制措施,例如要求进行用户身份验证才能使用密钥,或者
限制为只能在某些加密模式中使用。如需了解详细信息,请参阅
安全功能
部分。
密钥库系统由
KeyChain
API
以及在
Android 4.3
(
API
级别
18
)中引入的
Android
密钥
库提供程序功能使用。本文说明了何时以及如何使用
Android
密钥库提供程序。
安全功能
Android
密钥库系统可以保护密钥材料免遭未经授权的使用。首先,
Android
密钥库可以
防止从应用进程和
Android
设备中整体提取密钥材料,从而避免了在
Android
设备之外以
未经授权的方式使用密钥材料。其次,
Android
密钥库可以让应用指定密钥的授权使用方式,
并在应用进程之外强制实施这些限制,从而避免了在
Android
设备上以未经授权的方式使
用密钥材料。
提取防范
Android
密钥库密钥使用两项安全措施来避免密钥材料被提取:
密钥材料永不进入应用进程。通过
Android
密钥库密钥执行加密操作时,应用会将待签
署或验证的明文、密文和消息馈送到执行加密操作的系统进程。如果应用进程受攻击,攻击
者也许能使用应用密钥,但无法提取密钥材料(例如,在
Android
设备以外使用)。
您可以将
密钥材料绑定至
Android
设备的安全硬件
,例如可信执行环境
(TEE)
和安全
元素
(SE)
。为密钥启用此功能时,其密钥材料永远不会暴露于安全硬件之外。如果
Android
操作系统受到攻击或者攻击者可以读取设备内部存储空间,攻击者也许能在
Android
设备
上使用应用的
Android
密钥库,但无法从设备上提取这些数据。只有设备的安全硬件支持
密钥算法、区块模式、填充方案和密钥有权使用的摘要的特定组合时,才可启用此功能。要
检 查 是 否 为 密 钥 启 用 了 此 功 能 , 请 获 取 密 钥 的
KeyInfo
并 检
查
KeyInfo.isInsideSecurityHardware()
的返回值。
二、密钥使用授权
为了避免在
Android
设备上以未经授权的方式使用密钥材料,在生成或导入密钥时
Android
密钥库会让应用指定密钥的授权使用方式。一旦生成或导入密钥,其授权将无法更
改。然后,每次使用密钥时,都会由
Android
密钥库强制执行授权。这是一项高级安全功
能,通常仅用于有以下要求的情形:在
生成
/
导入密钥后
(而不是之前或当中),应用进程受
到攻击不会导致密钥以未经授权的方式使用。
支持的密钥使用授权可归为以下几个类别:
加密:授权密钥算法、运算或目的(
加密、解密、签署、验证
)、填充方案、区块模式以
及可与密钥搭配使用的摘要;
时间有效性间隔:密钥获得使用授权的时间间隔;
用户身份验证:密钥只能在用户最近进行身份验证时使用。请参阅
要求进行用户身份验
证才能使用密钥
。
作 为 一 项 额 外 的 安 全 措 施 , 对 于 密 钥 材 料 位 于 安 全 硬 件 内 部 的 密 钥 ( 请 参
阅
KeyInfo.isInsideSecurityHardware()
),某些密钥使用授权可能由安全硬件实施,具体取决于
Android
设备。加密和用户身份验证授权可能由安全硬件实施。由于安全硬件一般不具备独
立的安全实时时钟,时间有效性间隔授权不可能由其实施。
您可以使用
KeyInfo.isUserAuthenticationRequirementEnforcedBySecureHardware()
查询密钥的
用户身份验证授权是否由安全硬件实施。
选择密钥链或
Android
密钥库提供程序
在需要系统级凭据时请使用
KeyChain
API
。在应用通过
KeyChain
API
请求使用任何凭据时,
用户需要通过系统提供的
UI
选择应用可以访问已安装的哪些凭据。因此,在用户同意的情
况下多个应用可以使用同一套凭据。
使用
Android
密钥库提供程序让各个应用存储自己的凭据,并且只允许应用自身访问。这
样,应用可以管理仅能由自己使用的凭据,同时又可以提供等同于
KeyChain
API
为系统级凭
据提供的安全优势。这一方法不需要用户选择凭据。
使用
Android
密钥库提供程序
要使用此功能,请使用标准的
KeyStore
和
KeyPairGenerator
或
KeyGenerator
类,以及在
Android 4.3
(
API
级别
18
)中引入的
AndroidKeyStore
提供程序。
AndroidKeyStore
注 册 为
KeyStore
类 型 以 用 于
KeyStore.getInstance(type)
方 法 , 而 在 用
于
KeyPairGenerator.getInstance(algorithm, provider)
和
KeyGenerator.getInstance(algorithm,
provider)
方法时注册为提供程序。
三、生成新私钥
生成新的
PrivateKey
要求您同时指定自签署证书具备的初始
X.509
属性。之后,您可以使
用
KeyStore.setKeyEntry
将证书替换为由证书颁发机构
(CA)
签署的证书。
要生成密钥,请使用
KeyPairGenerator
和
KeyPairGeneratorSpec
:
/*
* 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();
生成新密钥
要生成密钥,请使用
KeyGenerator
和
KeyGenParameterSpec
。
使用密钥库条目
AndroidKeyStore
提供程序的使用通过所有的标准
KeyStore
API
加以实现。
列出条目
通过调用
aliases()
方法列出密钥库中的条目:
/*
* Load the Android KeyStore instance using the 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()
)签署数据:
/*
* 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[])
方法验证数据:
/*
* 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);