Protection of the sensitive date stored on the mobile devices is a hot topic. There are a huge number of apps that provide access to the corporate (confidential) data, banking and payment tools, social networks and many other web-services, where user authorization is required. That it why it is very important for mobile app developers to care about data protection and build solutions with access security and credential protection in mind. For these purposes, Keystone API 18 brought native support for Android encryption algorithms. It added AndroidKeyStore provider, which allows to:
- Generate new private cryptographic key or a pair of keys
- Work with Keystore entries – receive the list of saved keys
- Sign/verify data
- Transfer responsibility for safety of Keystore access to operating system.
In this article, we will show how to use this technology in practice to build secure Android apps with user password encryption and also support earlier Android versions providing password protection by means of other technologies.
Contents:
interface PasswordStorageInterface
Details of PasswordStorageHelper_SDK18_init() implementation
PasswordStorageHelper_SDK18 - setData()
PasswordStorageHelper_SDK18 - getData()
Introduction
For Keystore API 1 you need to manually create Keystore file and make sure that access to it is secure. Usually, for 90% of software, private directory is a safe enough place to store data. However, this is not the case for rooted devices, where said data can be easily accessed.
Additionally, Keystore can be protected with a password. However, while this solution does increase security, it is very inconvenient for the user, as it forces them to enter password each time they want to access Keystore. In any case, there is always the possibility that the password will be cracked and perpetrator will get your Keystore file.
As mentioned above, AndroidKeyStore provider was added in Keystore API 18. As a result, all the hard work on providing security now lies on the system itself. We no longer need to manually protect our storage with a password, the system will do it automatically based on the user’s LockScreen settings (whether it’s a PIN, graphical password, or a fingerprint). If device supports hardware key storage, than it will be used instead of a software one, eliminating the possibility that perpetrators will be able to obtain private keys. When using AndroidKeystore, each application only has access to the keys, used in the context of this application.
The Problem
We have a minSdkVersion 16 application that we need an authorization in order to run. We need to save credentials to automatically log in the next time an app has been launched. In order to do this in a secure way, we will encrypt the password with AndroidKeyStore before saving it in Shared Preferences.
However, since AndroidKeyStore provider was only added since API18, we need to make a separate implementation for password storage for different versions of Android OS.
In this example we will use asymmetric cryptographic algorithm called RSA, where we need to generate and use a pair of keys (a public and a private one). These keys work together: an open key to encrypt data and a private key to decrypt it.
RSA is well suited to encrypt small blocks of data, such as passwords and AES keys. However, when it comes to encrypting large amounts of data, this algorithm is a poor choice because of its performance, while something like AES, for example, is way faster.
Read also:
How to Receive and Handle SMS on Android
Implementation
First, we create a PassowrdStorageHelper class that will provide High level API for working with specific data. Let’s define a specific interface that will implement this class.
interface PasswordStorageInterface
Init() – generates a pair of keys if they don’t exist yet. Keys will be available for further use in KeyStore via an alias specified during generation.
setData() – allows to encrypt data and save the results in SharedPreferences by using the public key from the KeyStore.
getData() – allows to get encrypted password from SharedPreferences by using the private key to decrypt data
remove() – removes decrypted password, saved in SharedPreferences.
In order for PasswordStorageHelper to distinguish between devices that support AndroidKeyStore and those that done, we need to create two classes that implement PasswordStorageInterface.
In the PasswordStorageHelper constructor, we need to create specific class object that implements algorithms for working with protected data depending on your current Android version:
Caution: on certain API 18+ devices an exception can occur when initializing PasswordStorageHelper_SDK18().
In this case, we will use the second implementation of the helper:
Thus, by using the Facade pattern we painlessly split different implementations for working with encrypted data using different versions of Android OS. At the same time, we don’t need to worry about how to correctly initialize helper object – everything happens automatically.
Details of PasswordStorageHelper_SDK18_init() implementation
First, we need to check whether private/public keys are already exist in order to avoid generating them a second time.
We should prepare AlgorithmParameterSpec, this is necessary for further KeyPairGenerator initiation.
Now we can start to actually generate keys:
Along with the software implementation, some devices are also support the hardware key storages. In order to confirm whether a device supports such a storage, we can use the KeyChain.isBoundKeyAlgorithm(Algorithm);
However, Android 6.0 saw a much more extended API for Keystore, which is why this method for checking whether there is a hardware key storage doesn’t work anymore. Now, in order to conduct a check, we need keys to already be in the storage. As a result, we use the following method:
PasswordStorageHelper_SDK18 - setData()
In this method, we need to encrypt passwords with the public key that is already located in the KeyStore, after which we need to save encrypted password in the SharedPreferences.
Encrypting data with a public key.
Saving the result
PasswordStorageHelper_SDK18 - getData()
We do everything similarly to setData(), only in a reverse order – get the encrypted password from SharedPreferences and decrypt it with a private key.
Keystore initialization
Decrypting data with a private key
In this example of the PasswordStorageHelper_SDK16 class implementation, initial password is simply encrypted with the Base64 and saved in SharedPreferences. This approach poses certain risks. Any app with root access will be able to access your saved password and use it for its own purposes. This is why, when confidentiality of credentials is extremely important, you need to find other ways to implement PasswordStorageHelper for Android versions that doesn’t support AndroidKeyStore provider.
You can also take a look at the source code, as well as test this application on your own device.