Integrate fingerprint authentication into your Android apps

Ozge Oztekin
Commencis
Published in
5 min readJun 12, 2017

This story will help you to understand how fingerprint scan is working and how to use fingerprint authentication in your application.

You can take a look at Google’s sample fingerprint dialog before coding.

Initial setup

  1. First , add your permission to manifest file.
<uses-permission android:name="android.permission.USE_FINGERPRINT"/>

Since this permission is categorized as a normal permission , there is no need to add it as runtime permission.

2. Create your own fingerprint UI. Android gives you a default popup design which can be seen below. If you want to redesign your own screen , that’s okay. Just create your dialog and then call authenticate() method to start listening fingerprint scan. I’ll explain how this method works later.

3. Initialize KeyStore and generate key

A secret key should be created before authentication process. Generated key will be stored securely on device by using KeyStore instance and used for initializing cipher object a little later. First , gain access to keystore.

final KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);

Generate a secret key after getting KeyGenerator instance. Arguments below are using to specify type of the key. KEY_STORE_ALIAS is container for key to be saved.

final KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES,
"AndroidKeyStore");
keyGenerator.init(new
KeyGenParameterSpec.Builder(KEY_STORE_ALIAS,
KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_CBC)
.setUserAuthenticationRequired(true)
.setEncryptionPaddings(
KeyProperties.ENCRYPTION_PADDING_PKCS7)
.build());
keyGenerator.generateKey();

Before every use of the generated key , setUserAuthenticationRequired(true) method is used to decide whether this key is authorized to be used only if user has been authenticated. That is applied to secret key or private key operations.

Keyguard Manager

This class provides access to your lock screen.

KeyguardManager keyguardManager = (KeyguardManager)
.getSystemService(Context.KEYGUARD_SERVICE);
keyguardManager.isKeyguardSecure();

Returns whether the keyguard is secured by a PIN, pattern, password or a SIM card is currently locked.

Fingerprint Manager

FingerprintManager is fundamental of using fingerprint scan. Most of the fingerprint related methods can be found under this class.

FingerprintManager fingerprintManager = (FingerprintManager)
.getSystemService(Context.FINGERPRINT_SERVICE);

After getting instance of FingerprintManager , you will be able to use some methods of this class such as isHardwareDetected(), hasEnrolledFingerprints() and authenticate(…) .

  • fingerprintManager.isHardwareDetected()

Checks whether fingerprint sensor does exist or is available.

  • fingerprintManager.hasEnrolledFingerprints()

Checks if there is at least one enrolled fingerprint on device.

Tries to authenticate cypto object. The fingerprint hardware starts listening for scan after that call. Before calling this method , appropriate parameters should be prepared.

Parameter 1: CryptoObject

Before creating new instance of CryptoObject , you need to initialize Cipher object as follows

try {    final Cipher cipher = Cipher.getInstance(
KeyProperties.KEY_ALGORITHM_AES + "/"
+ KeyProperties.BLOCK_MODE_CBC + "/"
+ KeyProperties.ENCRYPTION_PADDING_PKCS7);
final SecretKey key;
final KeyStore keyStore = KeyStore.getInstance(ANDROID_KEY_STORE);
keyStore.load(null);
key = (SecretKey) keyStore.getKey(KEY_STORE_ALIAS, null);
cipher.init(Cipher.ENCRYPT_MODE, key);
return cipher;
} catch (KeyPermanentlyInvalidatedException e) {
return false;
} catch (KeyStoreException | CertificateException | UnrecoverableKeyException | IOException
| NoSuchAlgorithmException | InvalidKeyException | NoSuchPaddingException e) {
throw new RuntimeException("Failed to init Cipher", e);
}

This initialization of Cipher object will be used to create CryptoObject instance. While initializing cipher , generated and stored key in keystore container is used. Successfully initialized cipher means that previously stored key is not invalidated and it is still available for use.

Passing CryptoObject as an argument

if (initCipher()) {
cancellationSignal = new CancellationSignal();
fingerprintManager.authenticate(new FingerprintManager.CryptoObject(cipher),
cancellationSignal,
0, // flags
this, // authentication callback
null); // handler
}

After cipher object is initialized , crypto object is created by using this cipher instance. Generated crypto object should be passed as a parameter for authenticate() method. That method has some useful callbacks which I’ll talk about later.

Parameter 2: CancellationSignal

private void stopListeningAuthentication() {
if (cancellationSignal != null) {
cancellationSignal.cancel();
cancellationSignal = null;
}

CancellationSignal is used to cancel scan in progress. By calling cancel method and setting this object to null , you are able to stop listening fingerprint scan anytime. (error comes or user presses cancel button… etc.)

Parameter 3: Flags

It represents optional flags and can be set to zero.

Parameter 4: Authentication Callback

A class instance extends from FingerprintManager.AuthenticationCallback to receive authentication results.

Parameter 5: Handler

It’s an optional parameter. If it is set optionally , FingerprintManager will use Looper class from this handler for its inner MyHandler class.

KeyPermanentlyInvalidatedException

This exception is thrown when fingerprint enrollment has changed on device while initializing cipher. This exception occurs for keys which are to be used only if the user has been authenticated.

Keys are permanently invalidated once a new fingerprint is enrolled on device or all fingerprints on device are removed. Also, if secure lock screen is disabled or forcibly reset , keys are permanently and irreversibly invalidated.

If you need to authenticate user despite this exception , you must call initCipher() method again with a valid key.

You should generate a new key before initializing cipher because the old one is invalidated.

Authentication Callbacks

After a helper class extends from FingerprintManager.AuthenticationCallback is created, the following callbacks are called depending on result of authenticate(CryptoObject, CancellationSignal, int, AuthenticationCallback, Handler) method.

/**
* Called when an unrecoverable error has been encountered and the operation is complete.
* No further callbacks will be made on this object.
*
@param errorCode An integer identifying the error message
*
@param errString A human-readable error string that can be shown in UI
*/
public void onAuthenticationError(int errorCode, CharSequence errString) { }
/**
* Called when a recoverable error has been encountered during authentication. The help
* string is provided to give the user guidance for what went wrong, such as
* "Sensor dirty, please clean it."
*
@param helpCode An integer identifying the error message
*
@param helpString A human-readable string that can be shown in UI
*/
public void onAuthenticationHelp(int helpCode, CharSequence helpString) { }
/**
* Called when a fingerprint is recognized.
*
@param result An object containing authentication-related data
*/
public void onAuthenticationSucceeded(AuthenticationResult result) { }
/**
* Called when a fingerprint is valid but not recognized.
*/
public void onAuthenticationFailed() { }

You can find defined help and error codes by following this link .

Don’t worry, be happy ! :)

It seems a little frightening at the beginning because of using encryption process. However , adding fingerprint to your application is really attractive and user friendly. I think it’s never too late to add fingerprint into your app. After proceed step by step , development becomes easier. It is worth a shot!

Thanks for reading and see you next time.

--

--