User Identity – Part 2

Sometimes it is necessary to validate a user’s identity in order to provide access to sensitive information within an app. The traditional mechanism of a username and password has always been rather clunky on mobile because of small screens and soft keyboards. However with the arrival of hardware such as fingerprint scanners arriving on devices, there are some new much more user-friendly mechanisms that we can use for authentication. In this series we’ll explore how to implement these within our apps.

Previously we looked at how to perform a fingerprint scan:

Before we dive in to the code let’s have a very brief overview of the cryptography that’s at the core of this. We briefly discussed the Cipher in the previous article and this is a mechanism for encrypting data. The cipher is a well defined, public algorithm and it is the key that is used by the cipher to encrypt the data which is the secure part. The simplest kind of key is a symmetric key. With a symmetric key if you pass any given data to the cipher and encrypt using the symmetric key and then pass the encrypted data through the same cipher with the same key, then the original data will be returned. However we’re not actually using the cipher in this way as the FingerprintManager user some Android-specific key generation to provide a secure authentication mechanism.

The way this works is with how we define the key itself. We use an Android-specific key generation and specify that the key requires user authentication before it can be used. When we generate this key it gets stored in a KeyStore, and we receive a copy which we use to initialise the cipher. The cipher is then passed to the FingerprintManager for authentication, and when there is a successful scan the FingerprintManager accesses the associated key from the KeyStore, flags it with the successful user authentication, and only then can it be used to encrypt data using the cipher.

If we try and encrypt using the cipher before the key has been flagged for successful user authentication then “android.security.KeyStoreException: Key user not authenticated” exception will be thrown. Although we should not get the “success” callback until the scan has completed successfully, validating the cipher in this way means that we can have a high degree of confidence that nothing is interfering with our authentication request because the ability to set the required flag on the key is only available to the OS and not any apps running on top.

This is the basic skeleton of our cipher creation. I have deliberately separated out the actual key specification generation (i.e. the bit where we declare that the key requires user authentication) in to UserAuthKeySpecGenerator to show how we can keep this aspect completely separate to our cipher and key generation code. The significance of this will become apparent later in the series. However, we store this in a Map keyed with the name that we’ll also use to store the key within the KeyStore: USER_AUTH_KEY_NAME = "com.stylingandroid.identity.USER_AUTH_KEY"

We then have a utility method to return the correct cipher named getUserAuthCipher() which calls getCipher with the key name as an argument.

Finally we have a method to look up a KeyGenParameterSpec based upon a given key, and a define a generic exception wrapper: KeyToolsException.

First we look up to see if we already have a key of the given name in the KeyStore. If not we create one. We then get the key and get an instance of an AES Cipher with CBC block mode and PKCS7 padding.

Advanced Encryption Standard (AES) is the actual encryption algorithm that will be used. It is a block cipher which means that it will encrypt the data as a series of fixed size blocks. Cipher Block Chaining (CBC) is a mode of operation whereby each block is XORed with the encrypted, previous block before it is encrypted. In essence this add dependencies between the blocks, with each one being dependent on all of those preceding it in the chain. Finally Public Key Cryptography Standard #7 (PKCS7) is a padding method. The blocks must all be of the same size and if the data does not exactly fit in to a number of whole blocks then the last block will need to be padded to produce a complete block. PKCS7 simply defines how the padding bytes will be generated (the actual specifics aren’t important to us, we just need to be consistent).

This gets initialised with the required block and padding modes (CBC and PKCS7 respectively), and the key specification is created in the generate method. This basically creates rules for how we want to generate the key – we give it the name, the purpose of the key, block mode, padding mode, and finally we tell it that user authentication is required before they key can be used. This is actually the important bit because it means that the key will not work until the user has been authenticated.

It’s actually just this class which is specific to what we need for the FingerprintManager authentication – that’s the reason I wanted to keep it separate. Pretty well all of KeyTools is just standard code to generate a Key and Cipher, but it’s only the KeyGenParameterSpec that we create in UserAuthKeySpecGenerator which is specific to our needs.

So, now that we have covered how authentication using FingerprintManager actually works, it would seem that we’re complete. However we can actually implement some different security using many of the techniques that we’ve covered here. n the next article we’ll take a look at how we can put secure content behind the standard Android lock screen.

There is no additional code for this article, but the code we have been looking at is available here.