Implementation of Symmetric Algorithm in Android

Hossein Kheirollahpour
2 min readNov 10, 2023

--

In these algorithms, a single key is generated, enabling us to perform encryption on the Android client. Later, decryption must also be performed using the same key. Our goal is to maximize the security of our key. (Sample project Github link , Main article link)

Let’s move on to implementation…

  1. Our chosen algorithm is AES, recognized as one of the most complex and secure algorithms in the IT world. Additionally, we generate this key using AndroidKeyStore. As you may know, KeyStore is a secure enclave that significantly restricts access to the generated key:
KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore")

2. For this key, we choose a name. Additionally, we assign our goal of encryption and decryption to this key:

KeyGenParameterSpec.Builder = KeyGenParameterSpec.Builder(
"sample_symmetric_key_alias",
KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT
)

3. We set the size of this key, along with block mode and the encryption padding type:

setKeySize(KEY_SIZE)
setBlockModes(KeyProperties.BLOCK_MODE_GCM)
setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)

4. This key should only be accessible through biometrics. Therefore, when performing encryption or decryption, the user must have an active fingerprint. We ensure the user’s ownership of the device through the system OS’s fingerprint dialog prompt:

setUserAuthenticationRequired(true)

5. In each encryption operation, a random text is generated to prevent possible attacks:

setRandomizedEncryptionRequired(true)

6. The validity period of the key after successful biometric verification:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
setUserAuthenticationParameters(10, KeyProperties.AUTH_BIOMETRIC_STRONG)
} else {
setUserAuthenticationValidityDurationSeconds(10)
}

7. If a new fingerprint is added to the device, this key becomes invalid:

setInvalidatedByBiometricEnrollment(true)

8. During decryption, the screen must be locked, and if the device supports StrongBox, the key generation process must be performed inside it (having two key creation spaces: TEE and StrongBox):

val hasStrongBox = context.packageManager.hasSystemFeature(PackageManager.FEATURE_STRONGBOX_KEYSTORE)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
setUnlockedDeviceRequired(true)
if(hasStrongBox) setIsStrongBoxBacked(true)
}
  • In the next step, we use the following code for encryption:
val plainText = ... // user input text
val plainBytes = plainText.toByteArray(StandardCharsets.UTF_8)
val key = keyStoreManager.getKeyWithAlias(KEY_ALIAS_SYMMETRIC)
val cipher = Cipher.getInstance(AES_GCM_NOPADDING)
cipher.init(Cipher.ENCRYPT_MODE, key)
val initailVector = cipher.iv
val encryptedBytes = cipher.doFinal(plainBytes)
  • We also use the following code to decrypt:
val encryptedBytes = ... // encrypted byte array
val key = keyStoreManager.getKeyWithAlias(KEY_ALIAS_SYMMETRIC)
val spec = GCMParameterSpec(AUTHENTICATION_TAG_SIZE, iv)
val cipher = Cipher.getInstance(AES_GCM_NOPADDING)
cipher.init(Cipher.DECRYPT_MODE, key, spec)
val decryptedBytes = cipher.doFinal(encryptedBytes)
val palinText = String(decryptedBytes, StandardCharsets.UTF_8)

In conclusion, we've learned how to generate a secure symmetric key with various security features. Additionally, we've covered the process of encrypting and decrypting using that key. If you have further questions or if there's anything specific you'd like to explore or clarify, feel free to ask!

--

--