在google官方的api中,有三个类作为生物&指纹识别的入口可以供开发者使用
FingerprintManagerCompat:指纹识别,Android M及以上,设备上有指纹模块,不提供UI;
BiometricPrompt:生物识别,Android P及以上,设备上有至少一个生物方式的安全验证模块,系统统一UI(只可以自定义一些文案);
FingerprintManager:指纹识别(已弃用,直接使用FingerprintManagerCompat即可)。
生物识别目前包含指纹识别与人脸识别(一直有传言,但截至Android Q release版仍然没有人脸识别的相关文档),它们与传统的pin验证和密码验证的区别是需要硬件支持。
证书链和根证书(密钥对的证书链的根)的作用是给设备的安全硬件(TEE)提供了一种方法来验证非对称密钥对是否存在于安全硬件中。
Android中判断密钥是否来自安全硬件:
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
PrivateKey key = (PrivateKey) keyStore.getKey(alias, null);
KeyFactory factory = KeyFactory.getInstance(key.getAlgorithm(), "AndroidKeyStore");
KeyInfo keyInfo = factory.getKeySpec(key, KeyInfo.class);
boolean result = keyInfo.isInsideSecureHardware();
假如Android系统不可靠(比如被root),那么isInsideSecureHardware有可能是不可靠的,此时使用此方法来验证密钥对是否来自安全硬件。
证书链和根证书的示例见“使用示例 ——非对称加密&证书链和根证书示例”
证书链和根证书的验证逻辑:
Google认证根密钥签名的根证书:
-----BEGIN CERTIFICATE-----
MIIFYDCCA0igAwIBAgIJAOj6GWMU0voYMA0GCSqGSIb3DQEBCwUAMBsxGTAXBgNV
BAUTEGY5MjAwOWU4NTNiNmIwNDUwHhcNMTYwNTI2MTYyODUyWhcNMjYwNTI0MTYy
ODUyWjAbMRkwFwYDVQQFExBmOTIwMDllODUzYjZiMDQ1MIICIjANBgkqhkiG9w0B
AQEFAAOCAg8AMIICCgKCAgEAr7bHgiuxpwHsK7Qui8xUFmOr75gvMsd/dTEDDJdS
Sxtf6An7xyqpRR90PL2abxM1dEqlXnf2tqw1Ne4Xwl5jlRfdnJLmN0pTy/4lj4/7
tv0Sk3iiKkypnEUtR6WfMgH0QZfKHM1+di+y9TFRtv6y//0rb+T+W8a9nsNL/ggj
nar86461qO0rOs2cXjp3kOG1FEJ5MVmFmBGtnrKpa73XpXyTqRxB/M0n1n/W9nGq
C4FSYa04T6N5RIZGBN2z2MT5IKGbFlbC8UrW0DxW7AYImQQcHtGl/m00QLVWutHQ
oVJYnFPlXTcHYvASLu+RhhsbDmxMgJJ0mcDpvsC4PjvB+TxywElgS70vE0XmLD+O
JtvsBslHZvPBKCOdT0MS+tgSOIfga+z1Z1g7+DVagf7quvmag8jfPioyKvxnK/Eg
sTUVi2ghzq8wm27ud/mIM7AY2qEORR8Go3TVB4HzWQgpZrt3i5MIlCaY504LzSRi
igHCzAPlHws+W0rB5N+er5/2pJKnfBSDiCiFAVtCLOZ7gLiMm0jhO2B6tUXHI/+M
RPjy02i59lINMRRev56GKtcd9qO/0kUJWdZTdA2XoS82ixPvZtXQpUpuL12ab+9E
aDK8Z4RHJYYfCT3Q5vNAXaiWQ+8PTWm2QgBR/bkwSWc+NpUFgNPN9PvQi8WEg5Um
AGMCAwEAAaOBpjCBozAdBgNVHQ4EFgQUNmHhAHyIBQlRi0RsR/8aTMnqTxIwHwYD
VR0jBBgwFoAUNmHhAHyIBQlRi0RsR/8aTMnqTxIwDwYDVR0TAQH/BAUwAwEB/zAO
BgNVHQ8BAf8EBAMCAYYwQAYDVR0fBDkwNzA1oDOgMYYvaHR0cHM6Ly9hbmRyb2lk
Lmdvb2dsZWFwaXMuY29tL2F0dGVzdGF0aW9uL2NybC8wDQYJKoZIhvcNAQELBQAD
ggIBACDIw41L3KlXG0aMiS//cqrG+EShHUGo8HNsw30W1kJtjn6UBwRM6jnmiwfB
Pb8VA91chb2vssAtX2zbTvqBJ9+LBPGCdw/E53Rbf86qhxKaiAHOjpvAy5Y3m00m
qC0w/Zwvju1twb4vhLaJ5NkUJYsUS7rmJKHHBnETLi8GFqiEsqTWpG/6ibYCv7rY
DBJDcR9W62BW9jfIoBQcxUCUJouMPH25lLNcDc1ssqvC2v7iUgI9LeoM1sNovqPm
QUiG9rHli1vXxzCyaMTjwftkJLkf6724DFhuKug2jITV0QkXvaJWF4nUaHOTNA4u
JU9WDvZLI1j83A+/xnAJUucIv/zGJ1AMH2boHqF8CY16LpsYgBt6tKxxWH00XcyD
CdW2KlBCeqbQPcsFmWyWugxdcekhYsAWyoSf818NUsZdBWBaR/OukXrNLfkQ79Iy
ZohZbvabO/X+MVT3rriAoKc8oE2Uws6DF+60PV7/WIPjNvXySdqspImSN78mflxD
qwLqRBYkA3I75qppLGG9rp7UCdRjxMl8ZDBld+7yvHVgt1cVzJx9xnyGCC23Uaic
MDSXYrB4I4WHXPGjxhZuCuPBLTdOLU8YRvMYdEvYebWHMpvwGCF6bAx3JBpIeOQ1
wDB5y0USicV3YgYGmi+NZfhA4URSh77Yd6uuJOJENRaNVTzk
-----END CERTIFICATE-----
具体详见:https://developer.android.com/training/articles/security-key-attestation#verifying
安全方面,主要依靠Android原生的KeyStore来实现
对称加密的一般示例:
try {
//也可以使用其他的加解密算法,官方文档都已列出
KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES);
try {
//配置密钥的用途,加密还是解密(示例代码中的密钥通用于加密和解密)
keyGenerator.init(new KeyGenParameterSpec.Builder(aliasName, KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_CBC)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
//下面这一行是创建安全密钥的关键步骤,使用此设置后,只有用户通过了身份验证才可以使用此时创建的密钥
.setUserAuthenticationRequired(true)
.build());
} catch (InvalidAlgorithmParameterException e) {
e.printStackTrace();
}
//建议在生成密钥之前提前检查是否存在相同aliasName的密钥
SecretKey key = keyGenerator.generateKey();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
SecretKey key = (SecretKey) mKeyStore.getKey(aliasName, null);
Cipher cipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/" + KeyProperties.BLOCK_MODE_CBC + "/" + KeyProperties.ENCRYPTION_PADDING_PKCS7);
//与非KeyStore的方式不同,加密时不可以指定向量,否则会抛异常
cipher.init(Cipher.ENCRYPT_MODE, key);
//缓存加密自动生成的向量(如果需要解密)
byte[] cacheIv = cipher.getIV();
BiometricPrompt.CryptoObject cryptoObject = new BiometricPrompt.CryptoObject(cipher);
mBiometricPrompt.authenticate(cryptoObject
, new CancellationSignal()
, App.getInstance().getMainExecutor()
, new BiometricPrompt.AuthenticationCallback() {
@Override
public void onAuthenticationSucceeded(BiometricPrompt.AuthenticationResult result) {
super.onAuthenticationSucceeded(result);
BiometricPrompt.CryptoObject cryptoObject = result.getCryptoObject();
Cipher cipher = cryptoObject.getCipher();
doEnc(cipher, "Bill".getBytes());
}
} .......);
byte[] resultBytes = cipher.doFinal(bytes);
非对称加密&证书链和根证书示例:
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
//支持Cipher的非对称算法只有RSA,如果使用Signature会有更多选择
//详见:https://developer.android.com/training/articles/keystore
KeyPairGenerator kpGenerator = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_RSA, "AndroidKeyStore");
//为此密钥设定用途,加密&解密(!)
KeyGenParameterSpec.Builder builder = new KeyGenParameterSpec.Builder(alias, KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
.setAlgorithmParameterSpec(new RSAKeyGenParameterSpec(512, RSAKeyGenParameterSpec.F4))
.setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA384, KeyProperties.DIGEST_SHA512)
//使用RSA/ECB/PKCS1Padding
.setBlockModes(KeyProperties.BLOCK_MODE_ECB)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1)
//下面这一行是创建安全密钥的关键步骤,使用此设置后,只有用户通过了身份验证才可以使用此时创建的密钥
.setUserAuthenticationRequired(true);
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M) {
//关键步骤,设置是否为该密钥生成认证证书,以及证书中的质疑值
//此质疑值会包含在x509Certificate.getExtensionValue(Constants.KEY_DESCRIPTION_OID)中(KEY_DESCRIPTION_OID = "1.3.6.1.4.1.11129.2.1.17";)
//质疑值的目的是使依赖方能够验证密钥是根据特定请求创建的,如果不需要验证此值,可以随机设置非空字段
builder.setAttestationChallenge(genChallenge());
}
kpGenerator.initialize(builder.build());
kpGenerator.generateKeyPair();
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
Certificate[] certificates = keyStore.getCertificateChain(alias);
//将证书链发送给服务器,由服务器验证
//示例:https://github.com/google/android-key-attestation/blob/master/server/src/main/java/com/android/example/KeyAttestationExample.java
//其主要逻辑是:先验证整条证书链的派生关系,再通过公用的查询地址(需翻墙)查询每个节点证书的有效性,最后验证根证书是否与Google预设的一致
verifyByServer(certificates);
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
//先加密数据
//虽然在第一步(创建安全密钥)中为此密钥设定了验证后才可使用的要求,但公钥本身的意义就是“所有人可知的”,因此公钥加密不需要验证用户身份,同理利用公钥验签也不需要验证用户身份
//如果强行使用目的为加密的Cipher进行生物验证,那么会抛出“Crypto primitive not backed by AndroidKeyStore provider”异常
Cipher cipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_RSA + "/" + KeyProperties.BLOCK_MODE_ECB + "/" + KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1);
cipher.init(KeyProperties.PURPOSE_ENCRYPT, keyStore.getCertificate(alias).getPublicKey());
byte[] resultEncBytes = cipher.doFinal("Bill".getBytes());
//解密行为(即使用私钥的行为)必须经过验证
cipher.init(KeyProperties.PURPOSE_DECRYPT, keyStore.getKey(alias, null));
BiometricPrompt.CryptoObject cryptoObject = new BiometricPrompt.CryptoObject(cipher);
mBiometricPrompt.authenticate(cryptoObject
, new CancellationSignal()
, App.getInstance().getMainExecutor()
, new BiometricPrompt.AuthenticationCallback() {
@Override
public void onAuthenticationSucceeded(BiometricPrompt.AuthenticationResult result) {
super.onAuthenticationSucceeded(result);
BiometricPrompt.CryptoObject cryptoObject = result.getCryptoObject();
Cipher cipher = cryptoObject.getCipher();
byte[] resultBytes = cipher.doFinal(resultEncBytes);
Log.e(TAG, "result: " + new String(resultBytes));
}
} .......);
SafetyNet+证书链(根正书)
SafetyNet保证了设备的真实性/应用的安全性等,但由于它依赖于Google Play Services,所以这里不再过多描述
SafetyNet请查看https://developer.android.com/training/safetynet
由微信推出的生物认证方案,与Google原生生物识别的区别:
原文(https://github.com/Tencent/soter/wiki/%E5%8E%9F%E7%90%86)
全套的Soter方案涉及三个key(ATTK,App Secure Key(ASK)以及AuthKey),且都是RSA-2048的非对称密钥,使用步骤如下:
https://github.com/Tencent/soter/wiki/%E5%AE%89%E5%85%A8%E6%8E%A5%E5%85%A5
比较一下二者在实现硬件可信性上的限制:
设备厂商的限制 | 设备覆盖范围(在硬件支持的前提下) | 第三方服务器的介入 | |
---|---|---|---|
ATTK | 厂商可能会上传虚假的ATTK-公钥到TAM | 绝大部分Android6.0及以上设备,部分Android5.0/5.1(需要厂商的额外支持,约等于所有可以使用微信指纹支付的手机 | TAM |
根证书 | 厂商可能会创建自己的根证书 | Android 7.0及以上设备,且是GMS设备(如果是非GMS设备,会有前面提到的厂商限制问题 | GMS |