Android从6.0(api = 23)系统开始就支持指纹认证功能,但在Android P (api = 28) 系统官方标记为(@Deprecated)过期,不再推荐使用,并新增BiometricPrompt接口,来做指纹识别。所以在项目开发中我们为了兼容手机版本,就必须要做好版本适配
FingerprintManager manager = (FingerprintManager) getSystemService(Context.FINGERPRINT_SERVICE);
然后调用authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel,int flags, @NonNull AuthenticationCallback callback, @Nullable Handler handler)去验证指纹,看官方文档如下:
/**
* @param crypto object associated with the call or null if none required.
* @param cancel an object that can be used to cancel authentication
* @param flags optional flags; should be 0
* @param callback an object to receive authentication events
* @param handler an optional handler to handle callback events
* @deprecated See {@link BiometricPrompt#authenticate(CancellationSignal, Executor,
* BiometricPrompt.AuthenticationCallback)} and {@link BiometricPrompt#authenticate(
* BiometricPrompt.CryptoObject, CancellationSignal, Executor,
* BiometricPrompt.AuthenticationCallback)}
*/
@Deprecated
@RequiresPermission(anyOf = {USE_BIOMETRIC, USE_FINGERPRINT})
public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel,int flags, @NonNull AuthenticationCallback callback, @Nullable Handler handler) {
authenticate(crypto, cancel, flags, callback, handler, mContext.getUserId());
}
参考CryptoObjectHelper类,获取对象如下:
FingerprintManager.CryptoObject crypto = new CryptoObjectHelper().createCryptoObject();
@RequiresApi(Build.VERSION_CODES.M)
public class CryptoObjectHelper {
// This can be key name you want. Should be unique for the app.
static final String KEY_NAME = "us.mifeng.fingerprint.biometric.CryptoObjectHelper";
// We always use this keystore on Android.
static final String KEYSTORE_NAME = "AndroidKeyStore";
// Should be no need to change these values.
static final String KEY_ALGORITHM = KeyProperties.KEY_ALGORITHM_AES;
static final String BLOCK_MODE = KeyProperties.BLOCK_MODE_CBC;
static final String ENCRYPTION_PADDING = KeyProperties.ENCRYPTION_PADDING_PKCS7;
static final String TRANSFORMATION = KEY_ALGORITHM + File.separator + BLOCK_MODE + File.separator + ENCRYPTION_PADDING;
final KeyStore _keystore;
public CryptoObjectHelper() throws Exception {
_keystore = KeyStore.getInstance(KEYSTORE_NAME);
_keystore.load(null);
}
public FingerprintManager.CryptoObject createCryptoObject() throws Exception {
Cipher cipher = createCipher(true);
return new FingerprintManager.CryptoObject(cipher);
}
private Cipher createCipher(boolean retry) throws Exception {
Key key = getKey();
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
try {
cipher.init(Cipher.ENCRYPT_MODE | Cipher.DECRYPT_MODE, key);
} catch (KeyPermanentlyInvalidatedException e) {
_keystore.deleteEntry(KEY_NAME);
if (retry) {
createCipher(false);
} else {
throw new Exception("Could not create the cipher for fingerprint authentication.", e);
}
}
return cipher;
}
private Key getKey() throws Exception {
Key secretKey;
if (!_keystore.isKeyEntry(KEY_NAME)) {
createKey();
}
secretKey = _keystore.getKey(KEY_NAME, null);
return secretKey;
}
private void createKey() throws Exception {
KeyGenerator keyGen = KeyGenerator.getInstance(KEY_ALGORITHM, KEYSTORE_NAME);
KeyGenParameterSpec keyGenSpec =
new KeyGenParameterSpec.Builder(KEY_NAME, KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(BLOCK_MODE)
.setEncryptionPaddings(ENCRYPTION_PADDING)
.setUserAuthenticationRequired(true)
.build();
keyGen.init(keyGenSpec);
keyGen.generateKey();
}
}
CancellationSignal cancel = new CancellationSignal();
设置取消验证监听回掉
cancel.setOnCancelListener(new CancellationSignal.OnCancelListener() {
@Override
public void onCancel() {
}
});
调用cancel()方法取消验证
cancel.cancel();
FingerprintManager.AuthenticationCallback callback= new FingerprintManager.AuthenticationCallback(){
@Override
public void onAuthenticationError(int errorCode, CharSequence errString) {
super.onAuthenticationError(errorCode, errString);
}
@Override
public void onAuthenticationFailed() {
super.onAuthenticationFailed();
}
@Override
public void onAuthenticationHelp(int helpCode, CharSequence helpString) {
super.onAuthenticationHelp(helpCode, helpString);
}
@Override
public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result) {
super.onAuthenticationSucceeded(result);
}
};
}
对AuthenticationCallback中的方法处理:
onAuthenticationError:当遇到不可恢复的错误并且操作完成时调用。不会再对此对象进行回调,一般是输入次数过多或者是手机硬件出现问题
onAuthenticationFailed:当指纹有效但无法识别时调用。
onAuthenticationHelp:在身份验证过程中遇到可恢复的错误时调用。提供的帮助字符串用于为用户提供出错的指导,例如“传感器脏了,请清理它”。
onAuthenticationSucceeded:指纹识别成功回掉
google在BiometricPrompt 创建中不需要自定义UI了,Google为了统一指纹识别以及后期的生物识别,已经不允许自定义UI了,创建时必须使用BiometricPrompt.Builder
来创建对话框,其中可以自定义title、subtitle、description和一个NegativeButton(也就是cancel键)
BiometricPrompt prompt = new BiometricPrompt
.Builder(this)
.setTitle("Verification Title")
.setDescription("Verify fingerprint to continue")
.setSubtitle("")
.setNegativeButton("Use Password", this.getMainExecutor(), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
}).build();
然后通过BiometricPrompt调用
authenticate( @NonNull CryptoObject crypto, @NonNull CancellationSignal cancel, @NonNull @CallbackExecutor Executor executor, @NonNull AuthenticationCallback callback)
authenticate(@NonNull CancellationSignal cancel, @NonNull @CallbackExecutor Executor executor, @NonNull AuthenticationCallback callback)
方法,实现指纹验证
官方文档:
/** 第一个方法*/
/* @param crypto Object associated with the call
* @param cancel An object that can be used to cancel authentication
* @param executor An executor to handle callback events
* @param callback An object to receive authentication events
*/
@RequiresPermission(USE_BIOMETRIC)
public void authenticate(@NonNull CryptoObject crypto,
@NonNull CancellationSignal cancel,
@NonNull @CallbackExecutor Executor executor,
@NonNull AuthenticationCallback callback) {
if (handlePreAuthenticationErrors(callback, executor)) {
return;
}
mFingerprintManager.authenticate(crypto, cancel, mBundle, executor, mDialogReceiver,
callback);
}
/** 第二个方法*/
/* @param cancel An object that can be used to cancel authentication
* @param executor An executor to handle callback events
* @param callback An object to receive authentication events
*/
@RequiresPermission(USE_BIOMETRIC)
public void authenticate(@NonNull CancellationSignal cancel,
@NonNull @CallbackExecutor Executor executor,
@NonNull AuthenticationCallback callback) {
if (handlePreAuthenticationErrors(callback, executor)) {
return;
}
mFingerprintManager.authenticate(cancel, mBundle, executor, mDialogReceiver, callback);
}
参考参考CryptoObjectHelper2类,获取对象如下:
BiometricPrompt.CryptoObject cryptoObject = new CryptoObjectHelper2().createCryptoObject();
CryptoObjectHelper2类如下:
@RequiresApi(Build.VERSION_CODES.P)
public class CryptoObjectHelper2 {
private static final String KEY_NAME = "BiometricPromptApi28";
public BiometricPrompt.CryptoObject createCryptoObject() throws Exception {
KeyPair keyPair = generateKeyPair(KEY_NAME,true);
String mToBeSignedMessage = new StringBuilder()
.append(Base64.encodeToString(keyPair.getPublic().getEncoded(), Base64.URL_SAFE))
.append(":")
.append(KEY_NAME)
.append(":")
// Generated by the server to protect against replay attack
.append("12345")
.toString();
Signature mSignature = initSignature(KEY_NAME);
return new BiometricPrompt.CryptoObject(mSignature);
}
private Signature initSignature(String keyName) throws Exception {
KeyPair keyPair = getKeyPair(keyName);
if (null != keyPair){
Signature signature = Signature.getInstance("SHA256withECDSA");
signature.initSign(keyPair.getPrivate());
return signature;
}
return null;
}
private KeyPair getKeyPair(String keyName) throws Exception{
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
if (keyStore.containsAlias(keyName)){
PublicKey publicKey = keyStore.getCertificate(keyName).getPublicKey();
PrivateKey privateKey = (PrivateKey) keyStore.getKey(keyName,null);
return new KeyPair(publicKey,privateKey);
}
return null;
}
private KeyPair generateKeyPair(String keyName, boolean invaldateByBiometricEnrollment) throws Exception {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_EC,"AndroidKeyStore");
KeyGenParameterSpec.Builder builder = new KeyGenParameterSpec.Builder(keyName,KeyProperties.PURPOSE_SIGN)
.setAlgorithmParameterSpec(new ECGenParameterSpec("secp256r1"))
.setDigests(KeyProperties.DIGEST_SHA256,
KeyProperties.DIGEST_SHA384,
KeyProperties.DIGEST_SHA512)
.setUserAuthenticationRequired(true)
.setInvalidatedByBiometricEnrollment(invaldateByBiometricEnrollment);
keyPairGenerator.initialize(builder.build());
return keyPairGenerator.generateKeyPair();
}
}