Android Keystore System简介
Android Keystore System 主要是通过Keychain 机制或者Android Keystore Provider机制来被用户使用的,Keychain机制比较老,需要用户手动选择证书,Android Keystore Provider比较新,4.3(api 18)引入的,不需要用户介入,而且每个app可以创建自己的私密的证书,不对外暴露(前者创建的证书时会不可避免的暴露给所有app),本文主要介绍Android Keystore Provider机制
Android Keystore System 安全性
Android Keystore System可以防止秘钥被提取
防止被提取的方法:
秘钥的加密解密操作都在系统进程,而不再某个app进程
秘钥对和系统硬件环境相绑定,离开这个环境迷秘钥仍然不能用
Android Keystore System可以防止非授权情况下使用秘钥
防止方法:
加密算法:使用目的 (encrypt, decrypt, sign, verify), 填充算法(padding schemes), block modes, digests等。
安全时间:授权成功后的一段时间内,如果再次请求授权,是会被系统默认接受并允许的,但是过了安全时间,再次请求授权系统就不会直接允许了,而是需要再次重新授权。
用户授权:有些时候申请秘钥时,必须经过用户授权。
使用1:签名和验证
首先用KeyPairGenerator
生成非对称(Asymmetric)秘钥对:
/*
* 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();
使用私钥签名:
主要用到Signature
类,其中initSign()
只能使用PrivateKey
作为参数
/*
* 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();
使用公钥验证签名:
还是用到Signature
类,其中initVerify()
可以用Certificate
类作为参数也可以用PublicKey
作为参数:
/*
* 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);
使用秘钥时是否需要用户授权的设置(秘钥的用户授权模式)
默认情况下,当keypair被创建后,使用时是不需要用户授权的,但是如果在创建时用setUserAuthenticationRequired()
设置为true后,则开启用户授权模式,此模式打开后默认每次使用key都要用户授权,其实就是用-1这个默认值传入以下方法作为参数setUserAuthenticationValidityDurationSeconds(-1)
,如果不是-1的话,那就代表安全时间,例如20s:setUserAuthenticationValidityDurationSeconds(20)
那么只要用户做过解锁操作20s内这个key是可以被正常使用的。
KeyPairGenerator kpg = KeyPairGenerator.getInstance(
KeyProperties.KEY_ALGORITHM_EC, "AndroidKeyStore");
kpg.initialize(new KeyGenParameterSpec.Builder(
"newKey",
KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY)
.setDigests(KeyProperties.DIGEST_SHA256,
KeyProperties.DIGEST_SHA512)
.setUserAuthenticationRequired(true)
.setUserAuthenticationValidityDurationSeconds(10)
.build());
KeyPair kp = kpg.generateKeyPair();
开启用户授权模式后需要注意的几点:
安全时间内自动授权(值不为-1)时:
只有锁屏功能打开(isDeviceSecure()返回true)key才能正常使用。
如果锁屏密码被修改,清除,重置过,那么key会变为永久失效状态(不可逆)
每次使用key都要用户授权时(值为-1):
如果使用的是-1作为参数,也就是每次都需要用户授权,那么至少指纹设置里面要有一个已经录入的指纹,否则key也是不能正常使用的。
如果使用的是-1作为参数,也就是每次都需要用户授权,此种情况下,有新指纹录入,或者所有指纹都被清除后,那么key同样也会变为永久失效状态(不可逆)。
注意
1.这个设置仅对secret key 和 private key是有效的,如果用public key时(例如用public key验证签名)是不需要被用户授权的.
2.如果设置值为-1需要每次使用key时都得到用户授权,这种情况下只能通过指纹授权,也就是说指纹识别中的authenticate (FingerprintManager.CryptoObject crypto, CancellationSignal cancel, int flags, FingerprintManager.AuthenticationCallback callback, Handler handler)
方法中的crypto参数不能为null,而是和这个key相关联的:
private boolean initCipher() {
try {
mKeyStore.load(null);
SecretKey key = (SecretKey) mKeyStore.getKey(KEY_NAME, null);
mCipher.init(Cipher.ENCRYPT_MODE, key);
return true;
} catch (KeyPermanentlyInvalidatedException e) {
return false;
} catch (KeyStoreException | CertificateException | UnrecoverableKeyException | IOException
| NoSuchAlgorithmException | InvalidKeyException e) {
throw new RuntimeException("Failed to init Cipher", e);
}
}
crypto=new FingerprintManager.CryptoObject(mCipher)