Android 指纹识别

Android从6.0(api = 23)系统开始就支持指纹认证功能,但在Android P (api = 28) 系统官方标记为(@Deprecated)过期,不再推荐使用,并新增BiometricPrompt接口,来做指纹识别。所以在项目开发中我们为了兼容手机版本,就必须要做好版本适配

一、Android 6.0处理

1、创建 FingerprintManager对象

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());
    }
  • CryptoObject crypto:与调用关联的加密对象,如果不需要,则为空,但是这样的话,就意味这app无条件信任认证的结果,这是app被篡改的风险。
  • CancellationSignal cancel:可用于取消指纹验证的对象,当在指纹验证的时候取消指纹扫描,如果不取消,就会一致扫描指纹,直到超时
  • flags:图文标志,标志当前图文,默认是0
  • AuthenticationCallback  callback:指纹验证结果的回掉对象,通过改对象,我们处理指纹是否验证成功
  • Handler  handler:处理回调事件的可选处理程序,

a、CryptoObject对象创建

参考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();
    }
}

b、CancellationSignal 对象创建

CancellationSignal cancel = new CancellationSignal();

设置取消验证监听回掉

cancel.setOnCancelListener(new CancellationSignal.OnCancelListener() {
            @Override
            public void onCancel() {

            }
        });

调用cancel()方法取消验证

cancel.cancel();

c、AuthenticationCallback对象创建回掉

    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:指纹识别成功回掉

二、Android 8.0处理

1、BiometricPrompt对象创建

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);
    }

2、BiometricPrompt.CryptoObject

参考参考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();
    }
    
}

 

你可能感兴趣的:(Android)