preface

Recently, when checking the SampleProject, I felt that this login is a bit troublesome, always need to enter the password, now many apps can fingerprint login, this must support a wave; And I haven’t tried fingerprinting in all these years, which is a no-no. You’re never too old to learn.

Fingerprint Login Process

Fingerprint login is not a simple call to the fingerprint API and login account, what is there to say? Someone might ask. However, this is not the case, at the beginning I was also very naive to think it is so simple, but after looking for a lot of articles on the Internet, I found that most of the articles are only detailed description of how to call the API fingerprint identification, as for how to login, how to achieve fingerprint login business is rarely mentioned, so, Here I will give you a clear realization of the fingerprint login process to achieve.

The function of fingerprint identification in fingerprint login

Logins are the logic of our application. What do we need him to do when we interweave fingerprint recognition into it?

In fact, we can put the fingerprint login simple understanding Does not use the password to log in, in order to have no password login, we certainly need the user login information saved to the local, then we consider above all is the local data security problem, here, fingerprint identification is the local data for us to provide the functions of encryption and decryption.

Fingerprint login process

As shown in the figure above, before using the fingerprint login function, you need to enable fingerprint login. This step is to obtain the data required for login and encrypt it for storage. After that, you need to obtain the encrypted data for fingerprint login and decrypt it for login.

Problems faced by fingerprint identification

Before implementing the feature, we need to know that Google has only been supporting fingerprint recognition since Android 6.0, so you may need to integrate SDKS from different handset manufacturers if you want to cover devices under 6.0. Of course, there are very few devices under 6.0 now. And my project is just a self-use DEMO, not to do those complicated things, if necessary, you can understand by yourself;

Second, since Android 6.0, Google has added FingerprintManager for fingerprint recognition, followed by FingerprintManagerCompat for compatibility. Android 9.0 added the BiometricPrompt API for BiometricPrompt and added the @deprecated flag to FingerprintManager, so version compatibility was also a consideration when implementing it.

Fingerprint login implementation

From above, we understand the process of fingerprint login and the development of fingerprint identification and the problems to pay attention to, and then we start to implement.

Integration of fingerprint recognition

First, we integrate fingerprint recognition.

Android 6.0 already has an API for fingerprint recognition, but obviously not all phones will support it, so let’s first determine if fingerprint recognition is supported on current phones.

/** [build.version_codes.m] the above fingerprint managed object */
private val fingerprintManager: FingerprintManagerCompat by lazy {
    FingerprintManagerCompat.from(activity)
}

/** Check the fingerprint recognition support */
fun checkBiometric(a): Int {
    // Get screen lock management
    val km = context.getSystemService(KeyguardManager::class.java)
    return when {
        !fingerprintManager.isHardwareDetected -> {
            // Fingerprints are not supportedBiometricInterface.ERROR_HW_UNAVAILABLE } ! km.isKeyguardSecure -> {// Screen lock is not setBiometricInterface.ERROR_NO_DEVICE_CREDENTIAL } ! fingerprintManager.hasEnrolledFingerprints() -> {// No valid fingerprint is registered
            BiometricInterface.ERROR_NO_BIOMETRICS
        }
        else- > {// Support fingerprint identification
            BiometricInterface.HW_AVAILABLE
        }
    }
}
Copy the code

After confirming that the phone supports fingerprint recognition, we can call the API to pull up fingerprint recognition.

/** [build.version_codes.m] the above fingerprint managed object */
private val fingerprintManager: FingerprintManagerCompat by lazy {
    FingerprintManagerCompat.from(activity)
}

/** [build.version_codes.m] above pull fingerprint authentication */
fun authenticateM(a) {
    fingerprintManager.authenticate(
    	crypto, / / packaging the Cipher object FingerprintManagerCompat CryptoObject object, used for encryption
    	flags, // Optional flag, 0 is recommended
        cancel, CancellationSignal object, used to cancel fingerprint authentication. Null is not recommended but can be null
        callback, // Authentication callback interface
        handler // Callback Handler, usually null)}Copy the code

The above is simple fingerprint authentication code for Android 6.0 and above, but FingerprintManagerCompat does not provide pop-ups, so we need to add pop-ups on top of that.

/** [build.version_codes.m] above pull fingerprint authentication */
fun authenticateM(a) {
    val cancellationSignal = CancellationSignal()
    cancellationSignal.setOnCancelListener {
        Cancel the callback
    }
    val dialog = BiometricDialog.create()
    dialog.setOnCancelListener {
        // Cancel fingerprint authentication
        cancellationSignal.cancel()
    }
    dialog.show()
    fingerprintManager.authenticate(
    	crypto, / / packaging the Cipher object FingerprintManagerCompat CryptoObject object, used for encryption
    	flags, // Optional flag, 0 is recommended
        cancellationSignal, CancellationSignal object, used to cancel fingerprint authentication. Null is not recommended but can be null
        object: FingerprintManagerCompat.AuthenticationCallback() {
            override fun onAuthenticationSucceeded(result: FingerprintManagerCompat.AuthenticationResult?). {
                // The authentication succeeded
                dialog.dismiss()
            }
            override fun onAuthenticationHelp(helpCode: Int, helpString: CharSequence?). {
                // Authentication prompt
                dialog.setHint(helpString)
            }
            override fun onAuthenticationFailed(a) {
                // Authentication failed
                dialog.dismiss()
            }
            override fun onAuthenticationError(errorCode: Int, errString: CharSequence?). {
                // Authentication is abnormal
                dialog.dismiss()
            }
        }, // Authentication callback interface
        null // Callback Handler, usually null)}Copy the code

This completes our authentication function, and the new BiometricPrompt API for Android 9.0 already provides pop-ups, so we don’t need to implement them manually.

/** [build.version_codes.q] above pull fingerprint authentication */
fun authenticateQ(a) {
    val cancellationSignal = CancellationSignal()
    cancellationSignal.setOnCancelListener {
        Cancel the callback
    }
    // Generate an authentication object
    val prompt = with(BiometricPrompt.Builder(activity)) {
        setTitle(title)
        setSubtitle(subTitle)
        setDescription(hint)
        setNegativeButton(negative, activity.mainExecutor, { dialog, _ ->
			Cancel the callbackdialog? .dismiss() cancellationSignal.cancel() }) build() } prompt.authenticate( crypto,/ / packaging the Cipher object BiometricPrompt CryptoObject object, used for encryption
    	cancellationSignal, CancellationSignal object, used to cancel fingerprint authentication, cannot be null
        executor, // The Executor callback can not be null. Use activity.mainExecutor instead
        callback // Authentication callback)}Copy the code

So how do you get crypto? This is relevant in business scenarios, because Cipher objects are retrieved in different ways when used for encryption and decryption.

Enable the fingerprint login function

As mentioned above, fingerprint login function should first provide enabling fingerprint login to save the data required for login. Since Android does not provide relevant API separately, we use login interface to verify the correctness of password.

Therefore, the user is prompted to enter the password. After confirming the password, the login interface is invoked to verify that the password is correct. After confirming the password, the password is temporarily cached and fingerprint authentication is enabled.

The fingerprint authentication process has been described above, but here we focus on obtaining Cipher objects.

/** Gets a Cipher object */
fun loadCipher(a): Cipher {
    val keyStore = KeyStore.getInstance("AndroidKeyStore")
    keyStore.load(null)
    // keyAlias is the alias of the key, which can be defined by yourself. The encryption and decryption must be consistent
    if(! keyStore.containsAlias(keyAlias)) {// Does not contain change alias, new generation
        // Key generator
        val keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore")
        val builder = KeyGenParameterSpec.Builder(
            keyAlias,
            KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT)
        .setBlockModes(KeyProperties.BLOCK_MODE_CBC)
        .setUserAuthenticationRequired(false)
        .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
        keyGenerator.init(builder.build())
        keyGenerator.generateKey()
    }
    // Get the key based on the alias
    val key = keyStore.getKey(keyAlias, null)
    val cipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/"
                                    + KeyProperties.BLOCK_MODE_CBC + "/"
                                    + KeyProperties.ENCRYPTION_PADDING_PKCS7)
    // Enable encryption during login and use cipher. ENCRYPT_MODE to initialize
   	cipher.init(Cipher.ENCRYPT_MODE, key)
    return cipher
}
Copy the code

After obtaining a Cipher object, invoke fingerprint authentication. Different versions of CryptoObject have different objects. Create a Cipher object and pass it in

/** Fingerprint authentication callback succeeded */
override fun onAuthenticationSucceeded(result: AuthenticationResult?). {
    // The authentication succeeds, and a Cipher object is obtained
    valcipher = result? .cryptoObject? .cipher ? :throw RuntimeException("cipher is null!")
    // Use cipher to encrypt and save the login information
    val encryptInfo = cipher.doFinal(loginInfo.toByteArray()).toHexString()
    // Save encryptInfo locally
    // Save the encryption vector locally
    save(encryptInfo)
    save(cipher.iv.toHexString())
}
Copy the code

This completes the fingerprint login.

There are three points to note:

  1. Encryption,CipherObject usecipher.init(Cipher.ENCRYPT_MODE, key)Initialize;
  2. After fingerprint authentication is successful, the system returns using the callback functionCipherObject to encrypt data;
  3. After the fingerprint authentication is successful, theCipherObjectivSave it.

Fingerprint login function

After fingerprint login is enabled above, we can have fingerprint login on the login page.

After entering the login page, the user can automatically pull up fingerprint login or click fingerprint login to pull up fingerprint login.

/** Gets a Cipher object */
fun loadCipher(a): Cipher {
    val keyStore = KeyStore.getInstance("AndroidKeyStore")
    keyStore.load(null)
    // keyAlias is the alias of the key, which can be defined by yourself. The encryption and decryption must be consistent
    if(! keyStore.containsAlias(keyAlias)) {// Does not contain change alias, new generation
        // Key generator
        val keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore")
        val builder = KeyGenParameterSpec.Builder(
            keyAlias,
            KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT)
        .setBlockModes(KeyProperties.BLOCK_MODE_CBC)
        .setUserAuthenticationRequired(false)
        .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
        keyGenerator.init(builder.build())
        keyGenerator.generateKey()
    }
    // Get the key based on the alias
    val key = keyStore.getKey(keyAlias, null)
    val cipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/"
                                    + KeyProperties.BLOCK_MODE_CBC + "/"
                                    + KeyProperties.ENCRYPTION_PADDING_PKCS7)
    // Get the encrypted vector data saved when fingerprint login is enabled
    val ivBytes = get(IV_BYTES).toHexByteArray()
    val iv = IvParameterSpec(ivBytes)
    // Use fingerprint login and cipher.decrypt_mode and iv for initialization
   	cipher.init(Cipher.DECRYPT_MODE, key, iv)
    return cipher
}
Copy the code

Pull up fingerprint authentication as above.

/** Fingerprint authentication callback succeeded */
override fun onAuthenticationSucceeded(result: AuthenticationResult?). {
    // The authentication succeeds, and a Cipher object is obtained
    valcipher = result? .cryptoObject? .cipher ? :throw RuntimeException("cipher is null!")
    // Use cipher to decrypt the login information
    val logintInfo = cipher.doFinal(get(encryptInfo).toHexByteArray()
    // Use loginInfo to login
    login(loginInfo)
}
Copy the code

This completes the fingerprint login.

There are two points to note:

  1. Decryption,CipherObject usecipher.init(Cipher.DECRYPT_MODE, key, iv)Initialize;
  2. After fingerprint authentication is successful, the system returns using the callback functionCipherObject decrypts data.

conclusion

Have you learned how to integrate fingerprint login? We need to remember that fingerprint login is a function similar to remembering passwords. In this process, fingerprint authentication is used to encrypt and decrypt login information. Cipher objects are used, and the initialization modes of Cipher objects are different. The IvParameterSpec encryption vector generated during encryption is required for decryption. However, the Cipher object initialized by us cannot be used directly. It can only be used for encryption and decryption after fingerprint authentication.

Want my source code? You can have it all if you want. Go get it! I put all the source code there! >> SampleProject <<

Thank you for your patience to watch, I am WangJie0822, an ordinary program ape, welcome to pay attention.

Author: WangJie0822

Link: www.wangjie0822.top/posts/bef2e…

Source: WangJie0822

Copyright belongs to the author. Commercial reprint please contact the author for authorization, non-commercial reprint please indicate the source.