Authentication sucks. Bad security too.
A story about why the old fashioned way of authentication sucks and how we can do better without neglecting our users’ security.
A real world example
Imagine it is the end of November. You are currently sitting in a packed tram staring out of the window while little snowflakes dance in the wind. You notice all the people already getting their xmas-presents and suddenly a thought strikes you: Damn, you think, remembering that your best pal has his 24th birthday coming up in only two days time. You think about ordering the funny card game which became so popular on Kickstarter.
First thing you’re doing is pulling your phone out of your pocket and entering your 8 character long password to unlock it (because you’re that sort of IT guy who thinks this pattern thing isn’t secure enough to save all those neat cat pictures on your phone from prying eyes). After that you visit your favourite shopping website where you, of course, enter your second, 10 character long, password to log into your store account in order to get those completely worthless loyalty points from which you once got a bacon-shaped hand warmer which stopped working after the first use.
You’re searching for the game, find out that it is in stock and instantly get nervous because the site tells you that it’s only shipped today when you manage to order it within the next three minutes. After adding the item to your shopping cart you proceed to checkout where you decide to pay with PayPal. Again, you’re prompted to enter a password. Since this PayPal thing has to do with money — your money — you chose an extra secure 26 character long generated password with at least one special character which, of course, you can’t remember. But for those cases you have this tiny password manager app on your phone which is protected by your 16 character long master password.
Under this extreme pressure you’re typing your password wrong the first three times and for security reasons the app locks down for the next 30 seconds. You start to sweat and realise that this is your last chance. At best you have 60 seconds left to order the card game to arrive on time for your friend’s birthday.
30 seconds later you enter your master password again — and success! You scroll down to find the right entry, copy it to the clipboard, switch from the app to the shopping website and click the obligatory ‘Checkout with PayPal’ button. Then nothing. You’re phone just switched from 4G to EDGE. A few seconds later a tiny dinosaur appears. You’re officially offline. You pull to refresh and luckily the website starts loading again. You finally check out and in horror you read the following line: Estimated delivery: the day after tomorrow.
And that’s only because you needed to authenticate three times in three different apps in three minutes. But the good news is: Android is here to save you. At least the next time.
A real world problem
Authentication is not only annoying, time consuming and comes up way too often, it is also a terrible user experience. Authenticating all the time has become a real pain and because of that almost 50% of all Android users don’t have a secure lockscreen enabled.¹ That is definitely way too much!
In the meantime Android 6.0 has finally introduced a ‘smart’ credential storage, aka the Android Keystore, which is deeply integrated into the operating system. It lets you securely generate and store app-private keys (who would have thought that?) which can be used for en-/decryption and for signing / verification purposes. The cool thing about that is that those generated keys are never exposed to the kernel when the device is equipped with a “Trusted Execution Environment”. A so called TEE is a secure area inside the main processor of a smartphone which runs code isolated from other processes. That means even if the device gets compromised or hacked those keys can’t be easily extracted. Already 80–90% of all modern Android phones out there are equipped with a TEE (mostly because it’s often used to play DRM protected material) and it even is a requirement for Google’s Android Nougat certification — so every new phone running Android Nougat will come with a TEE installed.
“So what has all this got to do with authentication?”, you may ask. First of all you can say that authentication is pretty useless if it is not backed by a good protection. If you protect your banking-app with a clear-text password stored inside the SharedPreferences, then the whole authentication is kind of useless when the system gets rooted by the user or even compromised. So a secure environment is vital. And that’s what the Keystore was made for.
The cool thing with the new Keystore is the fact that you can configure the keys to require user authentication first in order to be used. And that’s pretty awesome. As an example you could use the Keystore to securely store the users login credentials inside your app and require user authentication for them to be used. There’s also a neat feature which lets you specify a timeframe (e.g. the last 30 seconds) in which the user had to authenticate. When the user has a secure lockscreen enabled and pulls out his phone in order to check his account balance, he can just walk right into your app without being penetrated by a second authentication screen inside your app. That’s because he just authenticated on system level when he unlocked his phone. Got it? Awesome!
A real world solution
Okay, enough theory — it’s time to show you some code!
Creating a key
Let’s get our hands dirty with some real world code and create a symmetric key which is protected by the Android Keystore — this key will be used later to en-/decrypt a simple text message:
Pretty straight forward, eh? It’s important that you pass all possible key purposes to the constructor of the KeyGenParameterSpec.Builder because the Keystore will reject all undeclared operations later (wouldn’t be too cool to have a key for encrypting but none for decrypting).
Now we have successfully created a key with the alias “myAwesomeSecretKey01” — great. Now we can start to encrypt something:
Whoa! As a result we get a byte array which contains our encrypted message. In order to decrypt everything again we just need to change the last three lines of code to:
…and that’s it! You just created a secure key inside the Keystore (probably the most secure place inside your Android system) and used it to encrypt and decrypt a simple text message. And again you may ask yourself “What’s with the authentication part?”. Good question. Right now our key can be used without user authentication but as it is app-private only our app can access it.
Adding authentication to your key is pretty straight forward — you simply append the following two lines to your builder when generating the key:
- setUserAuthenticationRequired(true) → requires user authentication for the key to be used
- setUserAuthenticationValidityDurationSeconds(30) → sets the interval in which the user needs to authenticate. When not specified or when setting the seconds to 0, authentication is required every single time.
Putting it all together, our builder would look like this:
Before generating a key which requires authentication you will need to check if the user has already set up a secure lockscreen (without one it’s just impossible to authenticate). You could simply check this using the isKeyguardSecure() method of the KeyguardManager.
When adding authentication you’ll have to handle some additional exceptions when using the key — e.g. the Cipher.init(…) method could throw a UserNotAuthenticatedException if the user has not authenticated during the last 30 seconds. In order to handle that we just add a catch clause in which we cancel the current operation and ask the user to authenticate before trying again:
Luckily Android provides us with a system intent to launch a system style authentication screen which can be customised by passing a title and a message:
In that screen the user has the possibility to authenticate via his pattern, code, password or his fingerprint (when at least one is enrolled). The result is then delivered in the onActivityResult() method of the calling activity:
When everything went fine and the user authenticated successfully we can try do repeat our encryption / decryption operation. This time a UserNotAuthenticatedException should not be thrown.
Since the information of the secure lockscreen is used to encrypt the keys which require authentication the keys get permanently invalidated when the user changes the lockscreen from a secure lockscreen to a non-secure one. In this case a KeyPermanentlyInvalidatedException will be thrown and you have to create the key again.
By using the Keystore authentication just got easier because we don’t even need to show a separate authentication screen in our app when the user recently authenticated (e.g. by unlocking the phone). Also, authentication just got stronger because it’s now tied to app secrets and the credential verification happens inside dedicated hardware.
Fingerprints make authenticating even easier
Great news! You can also use fingerprints to ‘unlock’ the Keystore when user authentication is required. The advantages are obvious:
- Authentication process is much faster
- Consistent authentication flow that fits the app design
- Users just love that ‘future-tech-startrek’ authentication style 😎
In fact the users love this feature so much that the lockscreen adoption on Nexus devices with a fingerprint reader went from ~50% to over 90%.²
The FingerprintManager is the system-level service which coordinates all access to the fingerprint hardware. In order to get access to the fingerprint hardware you have to declare the normal permission android.permission.USE_FINGERPRINT inside your Manifest file. The manager class offers the following three public methods:
- isHardwareDetected() → returns true if a fingerprint scanner is present
- hasEnrolledFingerprint() → returns true if the user has at least one fingerprint enrolled
- authenticate(…) → warms up the fingerprint hardware and starts scanning for a fingerprint
When the user has not enrolled a fingerprint yet, you could prompt him to enroll one now. Otherwise we can proceed to call the authenticate() method which takes five parameters in total:
The FingerprintManager.CryptoObject is just a wrapper class for the crypto objects supported by the FingerprintManager. It’s constructor takes an Signature, Cipher or Mac object. This can also be null if you don’t want to use a key from the Keystore and just check for user presence via fingerprint.
The CancellationSignal is used to cancel a ongoing fingerprint scanning process. Just call cancelSignal.cancel() if you want to cancel the current operation.
The FingerprintManager.AuthenticationCallback is needed to listen to all kinds of fingerprint events and a optional Handler can be provided to handle the appropriate callback events.
Finally the fingerprint hardware warms up and starts the fingerprint scanning process — the events will be delivered to the FingerprintManager.AuthenticationCallback which looks like this:
The onAuthenticationSucceeded() method is called when a fingerprint was recognised and the FingerprintManager.AuthenticationResult contains our CryptoObject with the now unlocked Signature / Cipher or Mac object. From there you can go on and use it for your desired cryptographic operation, e.g. decrypting our secret message from before. The three other methods inform you about issues which can arise during the fingerprint scanning process. It completes when either onAuthenticationSucceeded() or onAuthenticationError() is called. That’s basically it — for now you just need a design which displays the current status and possibly the error / help strings.
Unfortunately the above design is just a design. Right now Google does not provide some kind of FingerprintDialog which you can simply include into your existing project — you have to build the layout on your own. It also doesn’t need to be a dialog. Just keep in mind to always use the material fingerprint icon in your UI so the user knows that he can also use his finger to authenticate in your app.
A real world demonstration
If I aroused your interest for the redesigned Android Keystore framework I have achieved my goal of this article 🙂 For a real world demo you can check out my sample app on GitHub: https://github.com/flschweiger/SafeApp
It implements the basic Keystore functionality in one Activity (just for learning purposes) and shows how simple and useful this can be. But please beware! This is just a demo application and some exceptions / edge cases aren’t handled properly in order to keep the code as simple as possible — so please don’t use any of it in your real world app without knowing what you are doing. Cheers!
Found this article interesting and you are into Android development? Then follow me on Twitter to stay up-to-date 😉
¹, ² from the Google I/O 16 session “What’s new in Android security (M & N edition): https://youtu.be/XZzLjllizYs