Android Keystore System

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)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值