Storing a password securely is no easy feat but Android provides a solid set of APIs and secure container to make this task a bit easier.
Why store passwords in the first place?
The best way to protect the user credentials is to not store them at all. The user can enter the credentials upon login and you should use them for authentication, for example, against a server, and then discard them from UI and memory. It’s a valid consideration in your security design and should be the the preferred option.
Storing password derivatives
If there is no other way than to store a password, for example if you are not storing the user password but another credential that you are trying to protect, it is always a good idea not to store the password itself but a derivative of it that can be used for authentication, such as a password hash. A hash is a sequence of characters that is generated from the original by applying a cryptographic function such as SHA which is supposed to make it impossible to re-engineer the original password again.
Adding some salt
So you are storing a password hash and a hacker will never be able to reverse hash the original the password, right? Unfortunately not. A trained hacker can tell by the composition and length of the hash, which hash algorithm was used to generate the hash. Then he can launch a brute force attack by going through a list of well-known passwords, applying the hash function, and checkout the output against the original hash from the user. This can be a highly automated process that can check thousands of passwords per seconds.
An additional step to prevent these brute force attack is to add a ‘Salt‘ to the hash function. A Salt is an additional input parameter for the hash function that is considered in the hashing algorithm to randomize the hash output. That way, without knowing the Salt, a hacker can not determine a relationship between a password and the generated hash.
Enforcing complex password rules
If your app does not enforce complex password rules you may as well not go through the effort of storing the password securely. If you allowing passwords that are easy to guess (Password123 comes to mind) or are in the dictionary, changes are somebody is going to hack in.
At our company we set up a new VM a few months ago. I recently logged in and found that after only a couple of months there were over 250,000 attempts to log in to the server. It’s shocking but we need to realize that we are actively under attack by bots and hackers that are making it a business of breaking into your software.
What are secure password rules? It really depends on a number of factors such as the level of confidentiality of the data you are trying to protect and the tolerance of your user base to select, remember and type in complex passwords. Here my boiler-plate suggestion:
- Password length should be 12 characters or more
- At least one capital and one small letter
- At least one number
- At least one special character
- When stripped of numbers and special characters, the password should not be found in a simple dictionary.
Limiting password retries
Unlimited retries for password login attempts opens up your app to brute-force attacks. A hacker can create an automated script to try the most common passwords in order to break in. List of common passwords are widely publicized. Hey, there’s even a list of common passwords on Wikipedia
The remedy is to limit the number of times a person can try a password, e.g. to five times and then lock the account. After that, a user is required to provide additional security credentials to unlock the account such as the answers to security questions.
When transmitting credentials over the network to a server for authentication, it is important that the transmission is secure, meaning that the network communication between app and server is encrypted with a strong encryption algorithm. Otherwise, a third party listening to network traffic could (a man-in-the-middle) can easily extract credentials from the network traffic.
The best way to achieve this is to use 2048- or 4096-bit keys for SSL communication by installing a certificate from a well-known CA on your web server and to lock down all non-secure communication to the server.
Checking for device security
In order to protect access to your app, it may be a good idea to also guide the user in securing the device itself. The Android API KeyguardManager.isDeviceSecure provides an easy way for you to check if the user has secured the device with a password, PIN or pattern. If he has not, it may be a good idea to give the user a friendly reminder to secure the device with one of the options in the Android security settings.
Authenticating the device user
If you want to add an additional layer of security the confirms that the user trying to log in to your app is indeed the device owner, you can use the Android KeyguardManager.createConfirmDeviceCredentialIntent API. If the user has configured the device with a security PIN, password or pattern, this will bring up a system dialog for the user to verify the device credentials and provide you with a pass or fail.
Protecting Randomization Parameters
Randomization parameters are input parameters to cryptographic algorithms that introduce a random element to the functions to avoid that with the same input parameters, the algorithm will generate the same outputs, which would expose the implementation to brute force attacks. Examples for elements that introduce a random are Salt or Initialization Vector.
Since these parameter are critical for a hacker to extract a credential from a cryptographic function, these secondary parameters should also be stored securely. You can either encrypt them with a secure key stored in the key store or store them in a secure preference container. Check out my blog post on storing Secure Preferences for more information.
Android’s KeyStore System
The Android Keystore system is a secure credential storage system that is used by the KeyChain API and the Android Keystore provider. The Keystore provider was introduced in Android 4.3 (API level 18).
The KeyChain API can be used if you want to store credentials that are accessible to other apps. The user can select the credentials via a system provided UI, similar to the permission ‘Allow/Deny’ dialogs.
For the purpose of this tutorial we will assume that the credentials should only be accessible to the app itself.
The Android Keystore Provider
The Android Keystore Provider provides an interface to store credentials that only the app can access. It provides the same security benefits as the KeyChain API but does not require user interaction. This is the option we are going to use as part of this tutorial.
Creating a salted hash
As mentioned before we’d like to avoid that a hacker can derive the actual password from whatever we are storing, so the best way to do this is by generating a salted one-way hash.
You can use the Java.security MessageDigest class to generate a SHA-256 or SHA-512 hash and then convert it to Base64 to store it.
To add the salt, you can the update() method of the messageDigest object.
Creating the secret key for encryption
For generating a secret key for encryption, you can use the Android Key Generator with an algorithm of .KEY_ALGORITHM_AES for AES encryption.
Then you can use the KeyGenParameterSpec.Builder with an alias for you key, a purpose of ENCRYPT/DECRYPT to specify the key size (e.g. 256), block mode (e.g. CBC) and padding (PKCS7) of the AES encryption.
You can then initialize the KeyGenerator with the KeyGenParameterSpec and generate the key.
Retrieving the secret key from the Key Store
In order to retrieve your secret key from the Key Store, you need to get an instance of the KeyStore by specifying the AndroidKeyStore provider.
You can then use the getEntry() method with your key alias you used in the previous step to get a SecretKey object with your key information.
Encrypting the password Hash
In order to encrypt the hash, you will need to get an instance of the Cipher class by passing your cipher algorithm.
You can then intialize the cipher by passing in the mode (ENCRYPT_MODE), your secret key, and an ivParameterSpec with your Initialization Vector.
The encrypted result is a byte array generated from the cipher.doFinal() method. You can then store then encrypted password by converting the byte array into a Base64 string.
Decrypting the password Hash
Dcrypting the password hash works by using the same cipher algorithm, the same IV you used for the encryption.
You get a secret key from the key store as described above. Then you get a Cipher instance using your cipher algorithm. You initialize the cipher with DECRYPT_MODE, your secret key and the IvParameterSpec.
Before using the cipher method to decrypt, you need to Base64 decode the password hash into a byte array.
The result will be a byte array coming from cipher.doFinal() which you can then convert into a String using UTF8 encoding.