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:
- Encryption,
Cipher
Object usecipher.init(Cipher.ENCRYPT_MODE, key)
Initialize;- After fingerprint authentication is successful, the system returns using the callback function
Cipher
Object to encrypt data;- After the fingerprint authentication is successful, the
Cipher
Objectiv
Save 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:
- Decryption,
Cipher
Object usecipher.init(Cipher.DECRYPT_MODE, key, iv)
Initialize;- After fingerprint authentication is successful, the system returns using the callback function
Cipher
Object 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.