Use Android Keystore for securely storing and retrieving the data

I would like to brief how we can use Android Keystore to store passwords, or any other sensitive data in it, encrypt the data, and decrypt that data back. We can use the keystore not only to store passwords, but to store any sensitive data, and it does so in such a nice way that it is much more difficult for attackers, or malicious software to get this information from the device.

The Android Keystore system lets you store cryptographic keys in a container to make it more difficult to extract from the device. Once keys are in the keystore, they can be used for cryptographic operations with the key material remaining non-exportable. Moreover, it offers facilities to restrict when and how keys can be used, such as requiring user authentication for key use or restricting keys to be used only in certain cryptographic modes.
— Developer.android.com

An android application which uses keystore can only edit, save, and retrieve its own keys. This is a very simple but powerful concept. The app generates or receives a private and public key pair and stores them in the Android Keystore. The public key will be used to encrypt information, before being stored and the private key is used to decrypt the same information.

Below is the sample how to use Android Keystore to save a password, encrypt it, display the encrypted form and decrypt it.

The encryption/decryption transformation algorithm used for this sample is : “AES/GCM/NoPadding”

Creating new keys

First create an alias with a unique name, which will be used to encrypt/decrypt the data. And make sure this is not an empty one. The alias is the name of the entry in which the generated key will appear in Android KeyStore.

Create an instance of Androids KeyGenerator.

final KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEYALGORITHMAES, "AndroidKeyStore");

The algorithm we are using to generate keygenerator is AES, and we want to save the keys/data in the AndroidKeyStore.

After getting the instance build a KeyGenParameterSpec using the KeyGenParameterSpec.Builder to pass in to the KeyGenerators init method.
KeyGenParameterSpec is the properties for the keys which will be generated. For example, let’s say that if user wanted the key to expire after a certain amount of time, this is where we would specify that.

Below is the KeyGenParameterSpec.

final KeyGenerator keyGenerator =
KeyGenerator .getInstance(KeyProperties.KEYALGORITHMAES, ANDROIDKEYSTORE);
final KeyGenParameterSpec keyGenParameterSpec = new KeyGenParameterSpec.Builder(alias,
KeyProperties.PURPOSEENCRYPT | KeyProperties.PURPOSEDECRYPT)
.setBlockModes(KeyProperties.BLOCKMODEGCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTIONPADDINGNONE)
.build();

In the above snippet we are passing the alias which we are going to use and then we will specify the purpose Encrypt or Decrypt.
The setBlockModes, assures us that only the block modes specified can be used to both encrypt and decrypt the data, if any other type of block mode is used, it will be rejected.
Since “AES/GCM/NoPadding” transformation algorithm is used, inform KeyGenParameterSpec the type of padding that should be used.

Encrypting data

After setting all the setup, do the encryption like below.

final KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEYALGORITHMAES, ANDROIDKEYSTORE);
final KeyGenParameterSpec keyGenParameterSpec = new KeyGenParameterSpec.Builder(alias,
KeyProperties.PURPOSEENCRYPT | KeyProperties.PURPOSEDECRYPT)
.setBlockModes(KeyProperties.BLOCKMODEGCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTIONPADDINGNONE)
.build();
keyGenerator.init(keyGenParameterSpec);
final SecretKey secretKey = keyGenerator.generateKey();
final Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(Cipher.ENCRYPTMODE, secretKey);

Initialize the keyGenerator with the keyGenParameterSpec, then generate the SecretKey.

Use this SecretKey to initialize the Cipher object, which will take care of the actual encryption. Inform the Cipher the type of encryption transformation which will be used and pass the secretKey to use for the encryption.

final KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEYALGORITHMAES, ANDROIDKEYSTORE);
final KeyGenParameterSpec keyGenParameterSpec = new KeyGenParameterSpec.Builder(alias,
KeyProperties.PURPOSEENCRYPT | KeyProperties.PURPOSEDECRYPT)
.setBlockModes(KeyProperties.BLOCKMODEGCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTIONPADDINGNONE)
.build();
keyGenerator.init(keyGenParameterSpec);
final SecretKey secretKey = keyGenerator.generateKey();
final Cipher cipher = Cipher.getInstance(TRANSFORMATION);
cipher.init(Cipher.ENCRYPTMODE, secretKey);
iv = cipher.getIV();
encryption = cipher.doFinal(textToEncrypt.getBytes("UTF-8"));

Next grab a reference to the ciphers initialization vector (IV), which will be used for decryption, and finalize the encryption with doFinal(textToEncrypt). The doFinal method returns a byte array which is the actual encrypted text.

Decrypting the data

Initiating the KeyStore
Before starting decrypting data, create an instance of the KeyStore.

keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
Use the keyStore to get secret key using the alias which previously used when encrypting the data.
Get a SecretKeyEntry from the keyStore to grab the secretKey from.
keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
final KeyStore.SecretKeyEntry secretKeyEntry = (KeyStore.SecretKeyEntry) keyStore.getEntry(alias, null);
final SecretKey secretKey = secretKeyEntry.getSecretKey();

In GCMParameterSpec  specify an authentication tag length (has to be one of these: 128, 120, 112, 104, 96), and pass in the IV which got during the encryption process.
Use this GCMParameterSpec with the above Cipher to initialize the decryption process.

keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
final KeyStore.SecretKeyEntry secretKeyEntry = (KeyStore.SecretKeyEntry) keyStore.getEntry(alias, null);
final SecretKey secretKey = secretKeyEntry.getSecretKey();
final Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
final GCMParameterSpec spec = new GCMParameterSpec(128, encryptionIv);
cipher.init(Cipher.DECRYPTMODE, secretKey, spec);
And like before, to get the decrypted data do
final byte[] decodedData = cipher.doFinal(encryptedData);
To get the unencrypted string representation, we do
final String unencryptedString = new String(decodedData, "UTF-8");

The unencryptedString contains the decrypted actual data.

In order to grab all available aliases create a getAllAliasesInTheKeystore() method inside KeyStoreManager, which returns an array list of all available aliases in the keystore.

private ArrayList getAllAliasesInTheKeystore() throws KeyStoreException {
return Collections.list(keyStore.aliases());
}