Wednesday 22 February 2017

Android fingerprint System tutorial

Android fingerprint authentication uses smartphone touch sensor to authenticate the user. Android Marshmallow has introduced a set of API that makes easy to use touch sensor. Before Android Marshmallow the method to access to touch sensor was not standard.
There are several benefits of using Android fingerprint authentication:
  • Fast and easy to use
  • Secure: fingerprint uniquely identifies you
  • Online transaction are safer
There are several steps you have to follow before using Android fingerprint authentication and at the beginning, it could seem really complex but this tutorial will guide you step by step. 
The final results is an Android app that uses fingerprint authentication as shown below:

Getting started with Android fingerprint authentication

As said before, there are several steps to follow to enable fingerprint authentication:
  • Verify that the lock screen is secure, or in other words, it is protected by PIN, password or pattern
  • Verify that at least one fingerprint is registered on the smartphone
  • Get access to Android keystore to store the key used to encrypt/decrypt an object
  • Generate an encryption key and the Cipher
  • Start the authentication process
  • Implement a callback class to handle authentication events
That’s all!!..We will implement these steps.
Before starting, it is important to request the permission to use the touch sensor and the fingerprint authentication. So in the Manifest.xml, we add:
1
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
Now it is time to create our main activity class that handles all the authentication process.

Verify secure lock screen in Android

The first step is verifying the secure lock screen. This can be done KeyguardManager and FingerprintManager. We get an instance of these two managers using getSystemService:
1
2
3
4
5
6
7
// Keyguard Manager
KeyguardManager keyguardManager = (KeyguardManager)
                  getSystemService(KEYGUARD_SERVICE);
// Fingerprint Manager
fingerprintManager = (FingerprintManager)
                 getSystemService(FINGERPRINT_SERVICE);
Now, our authentication app can check if all the secure conditions are satisfied:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
private boolean checkFinger() {
  // Keyguard Manager
  KeyguardManager keyguardManager = (KeyguardManager)
           getSystemService(KEYGUARD_SERVICE);
  // Fingerprint Manager
  fingerprintManager = (FingerprintManager)
         getSystemService(FINGERPRINT_SERVICE);
  try {
   // Check if the fingerprint sensor is present
   if (!fingerprintManager.isHardwareDetected()) {
     // Update the UI with a message
     message.setText("Fingerprint authentication not supported");
     return false;
   }
   if (!fingerprintManager.hasEnrolledFingerprints()) {
     message.setText("No fingerprint configured.");
     return false;
   }
   if (!keyguardManager.isKeyguardSecure()) {
     message.setText("Secure lock screen not enabled");
     return false;
   }
 }
 catch(SecurityException se) {
   se.printStackTrace();
 }
 return true;
}
Notice that, the authentication app verifies that, at least, one fingerprint is registered otherwise the authentication process can not start.
The image below shows a message error when the app does not find a fingerprint registered.
If everything is ok and all the conditions are satisfied, the authentication app generates the key and accesses to the Android store.

Access to Android keystore and generate the key

The next step is accessing to the Android keystore and generate the key to encrypt the data. The app does it in a separate method called generateKey().
1
2
// Get the reference to the key store
keyStore = KeyStore.getInstance("AndroidKeyStore");
then it is necessary to get the reference to the key generator:
1
2
3
// Key generator to generate the key
keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES,
                 "AndroidKeyStore");
and finally, we have to initialize the key generator:
1
2
3
4
5
6
7
8
9
10
11
keyGenerator.init( new
  KeyGenParameterSpec.Builder(KEY_NAME,
  KeyProperties.PURPOSE_ENCRYPT |
  KeyProperties.PURPOSE_DECRYPT)
  .setBlockModes(KeyProperties.BLOCK_MODE_CBC)
  .setUserAuthenticationRequired(true)
  .setEncryptionPaddings(
    KeyProperties.ENCRYPTION_PADDING_PKCS7)
  .build());
 keyGenerator.generateKey();
Notice that we specify the usage of the key: encrypt and decrypt and that the authentication is required to use the key itself. At the end, the app generates the key (last line).
Below, the complete method:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
private void generateKey() throws FingerprintException {
  try {
    // Get the reference to the key store
    keyStore = KeyStore.getInstance("AndroidKeyStore");
    // Key generator to generate the key
    keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES,
      "AndroidKeyStore");
     keyStore.load(null);
     keyGenerator.init( new
       KeyGenParameterSpec.Builder(KEY_NAME,
      KeyProperties.PURPOSE_ENCRYPT |
      KeyProperties.PURPOSE_DECRYPT)
     .setBlockModes(KeyProperties.BLOCK_MODE_CBC)
     .setUserAuthenticationRequired(true)
     .setEncryptionPaddings(
        KeyProperties.ENCRYPTION_PADDING_PKCS7)
    .build());
    keyGenerator.generateKey();
  }
  catch(KeyStoreException
   | NoSuchAlgorithmException
   | NoSuchProviderException
   | InvalidAlgorithmParameterException
   | CertificateException
   | IOException exc) {
    exc.printStackTrace();
    throw new FingerprintException(exc);
 }
}

Create the Android Cipher

Once the key is ready, the last step is creating the Android Cipher that uses the key, we have generated before. The source code is very simple:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
private Cipher generateCipher() throws FingerprintException {
  try {
    Cipher cipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/"
     + KeyProperties.BLOCK_MODE_CBC + "/"
     + KeyProperties.ENCRYPTION_PADDING_PKCS7);
     SecretKey key = (SecretKey) keyStore.getKey(KEY_NAME,
             null);
     cipher.init(Cipher.ENCRYPT_MODE, key);
     return cipher;
  }
  catch (NoSuchAlgorithmException
     | NoSuchPaddingException
     | InvalidKeyException
     | UnrecoverableKeyException
     | KeyStoreException exc) {
      exc.printStackTrace();
      throw new FingerprintException(exc);
  }
}

Build the Android fingerprint app

It is time to assemble all these methods and create the Android fingerprint authentication app. This app is very simple and it has a MainClass that calls the methods shown above and starts the authentication process.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
@Override
protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  message = (TextView) findViewById(R.id.fingerStatus);
  Button btn = (Button) findViewById(R.id.authBtn);
  final FingerprintHandler fph = new FingerprintHandler(message);
  if (!checkFinger()) {
    btn.setEnabled(false);
  }
  else {
    // We are ready to set up the cipher and the key
   try {
     generateKey();
     Cipher cipher = generateCipher();
     cryptoObject =
      new FingerprintManager.CryptoObject(cipher);
  }
  catch(FingerprintException fpe) {
   // Handle exception
   btn.setEnabled(false);
  }
 }
 btn.setOnClickListener(new View.OnClickListener() {
   @Override
   public void onClick(View view) {
    message.setText("Swipe your finger");
    fph.doAuth(fingerprintManager, cryptoObject);
   }
  });
}
There are a few things to notice. First of all, the Android app creates  a CryptoObject that is used in the authentication process. Moreover, the app shows a button and when the user clicks on it the authentication process starts. The button is disabled if the initial conditions described above are not satisfied. The most important thing to notice is  new class called FingerprintHandler. This class is a callback class that receives the authentication process events. Moreover, this class starts the authentication process with doAuth method.

Android fingerprint authentication callback

The last step is creating the callback class so that we can receive event notification and we can know when the authentication succeeded or something went wrong. This class extends FingerprintManager.AuthenticationCallback.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
public class FingerprintHandler extends FingerprintManager.AuthenticationCallback {
  private TextView tv;
  public FingerprintHandler(TextView tv) {
    this.tv = tv;
  }
  @Override
  public void onAuthenticationError(int errorCode, CharSequence errString) {
    super.onAuthenticationError(errorCode, errString);
    tv.setText("Auth error");
  }
  @Override
  public void onAuthenticationHelp(int helpCode, CharSequence helpString) {
    super.onAuthenticationHelp(helpCode, helpString);
  }
  @Override
  public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result) {
    super.onAuthenticationSucceeded(result);
    tv.setText("auth ok");
    tv.setTextColor(tv.getContext().getResources().
                getColor(android.R.color.holo_green_light));
  }
  @Override
  public void onAuthenticationFailed() {
    super.onAuthenticationFailed();
  }
  public void doAuth(FingerprintManager manager,
                     FingerprintManager.CryptoObject obj) {
   CancellationSignal signal = new CancellationSignal();
   try {
    manager.authenticate(obj, signal, 0, this, null);
   }
   catch(SecurityException sce) {}
 }
}

There are some important methods to notice. First of all, the doAuth that starts the authentication process. This method has the CryptoObject, a cancellation signal and the callback listener (this class). The image below shows the app in action:
In this case, the user is authenticated using Android fingerprint authentication.

How to test the app in Android emulator

To test the app, it is possible to use a real device that has a touch sensor. Anyway, it is possible to test the app in the emulator too. Before starting to use the app, you have to configure the fingerprint accessing to the Security menu. When the system asks for fingerprint you have to use the adb command to emulate the finger touch:
1
adb -e emu finger touch id(like 1,2, ecc.)
Finally, when the configuration is done, you get the message shown below:
At the end of this post, hopefully, you gained the knowledge about Android fingerprint api and how to develop an Android fingerprint example app.

3 comments:

  1. Nice tutorial but please change your theme and use code highlighter. you have so many adds on the site that's create a bad expression on users.

    ReplyDelete