FULL Guide to Encryption & Decryption in Android (Keystore, Ciphers and more)

YouTube video

In this comprehensive guide, we will explore the process of encrypting and decrypting data in Android using the Android Keystore system. Security is of utmost importance, especially when it comes to handling sensitive data, and understanding encryption and decryption methods is crucial for protecting that data. By the end of this guide, you will have a clear understanding of how to encrypt and decrypt data in Android, as well as the importance of the Android Keystore system.

Overview

Before we dive into the coding process, let’s understand the concept of the Android Keystore system. The Android Keystore system is designed to secure your encryption keys within the Android device. While it cannot prevent an attacker from using your keys if they have root access to your device, it prevents them from extracting the keys and using them elsewhere. The Android Keystore system relies on a separate hardware component called a Trusted Execution Environment (TEE) that is not accessible even with root access to the device. This TEE is responsible for managing the keys and ensuring their security.

Creating a Crypto Manager Class

To begin encrypting and decrypting data in Android, we will create a Crypto Manager class. This class will handle all the necessary functions and variables related to encryption and decryption. Let’s start by obtaining an instance of the Android Keystore:

kotlin
private val keystore = KeyStore.getInstance("AndroidKeyStore").apply {
load(null)
}

With the keystore instance in place, we can now proceed with creating a cipher for encryption and decryption. A cipher specifies how the encryption and decryption should occur. We need to define the encryption algorithm, block mode, padding, and transformation. Here are the values we will use for these parameters:

kotlin
private companion object {
private const val algorithm = KeyProperties.KEY_ALGORITHM_AES
private const val blockMode = KeyProperties.BLOCK_MODE_CBC
private const val padding = KeyProperties.ENCRYPTION_PADDING_PKCS7
private val transformation = "$algorithm/$blockMode/$padding"
}

Next, we will create a function to generate a secret key for encryption and decryption. We will use the KeyGenerator class for this purpose:

kotlin
private fun createKey(): SecretKey {
val keyGenerator = KeyGenerator.getInstance(algorithm).apply {
init(
KeyGenParameterSpec.Builder(
"my_keystore_alias",
KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT
)
.setBlockModes(blockMode)
.setEncryptionPaddings(padding)
.setKeySize(256)
.build()
)
}
return keyGenerator.generateKey()
}

In this function, we specify the alias for our keystore, the purposes of the key (encryption and decryption), block modes, encryption paddings, and key size. Once the key is generated, we can use it for encryption and decryption operations.

To handle existing keys and avoid creating a new key each time, we will create another function to retrieve the key:

kotlin
private fun getKey(): SecretKey? {
val entry = keystore.getEntry("my_keystore_alias", null) as? KeyStore.SecretKeyEntry
return entry?.secretKey
}

This function checks if there is already an existing key in the keystore, and if so, retrieves and returns it. Otherwise, it returns null.

Encrypting and Decrypting Data

With the necessary setup in place, we can now proceed with the actual encryption and decryption process. Let’s start by creating a cipher for encryption:

kotlin
private val encryptCipher = Cipher.getInstance(transformation).apply {
init(Cipher.ENCRYPT_MODE, getKey())
}

In this code, we obtain an instance of the cipher using the transformation we defined earlier. We then initialize the cipher for encryption mode using the secret key obtained from the keystore.

For decryption, we will create another cipher:

kotlin
private val decryptCipher = Cipher.getInstance(transformation).apply {
init(Cipher.DECRYPT_MODE, getKey())
}

Similar to the encryption cipher, we obtain an instance of the cipher and initialize it for decryption mode using the secret key.

Now that we have our encryption and decryption ciphers ready, we can create functions to perform encryption and decryption operations:

“`kotlin
fun encrypt(data: ByteArray): ByteArray {
return encryptCipher.doFinal(data)
}

fun decrypt(data: ByteArray): ByteArray {
return decryptCipher.doFinal(data)
}
“`

These functions take a byte array as input and return the encrypted or decrypted byte array as output, respectively. We use the doFinal() method of the cipher to perform the encryption or decryption operation.

Demo App: Encrypting and Decrypting Strings

To demonstrate the encryption and decryption process, we will build a simple Android app where users can enter a string and encrypt or decrypt it. Here’s a brief overview of the app:

  • Users can enter a string in a text field.
  • They have the option to encrypt or decrypt the string.
  • The encrypted or decrypted string is displayed on the screen.
  • The encrypted string is saved in a separate file and can be decrypted later.

In the app, we will utilize the Crypto Manager class we created earlier to handle the encryption and decryption operations.

To build the app, we need to create the necessary UI components (text field, buttons, etc.) and implement the logic to interact with the Crypto Manager class.

Conclusion

In this guide, we explored the process of encrypting and decrypting data in Android using the Android Keystore system. We learned that while encryption and decryption cannot be entirely prevented by an attacker with root access to the device, the Android Keystore system ensures that the encryption keys are secured within a trusted hardware component. We also created a Crypto Manager class that handles the encryption and decryption operations, allowing us to easily encrypt and decrypt data in our Android apps.

By following the steps outlined in this guide, you now have a solid understanding of encryption and decryption in Android and how to implement it in your own apps. Remember to prioritize security when handling sensitive data and make use of the Android Keystore system for secure key management.

Happy coding!