Wednesday, September 23, 2020

How to store encryption keys safely in Android 19+?

We have to encrypt our data while saving or sending over the Internet. We have to use shared key algorithms for encryption because they are fast. But what to do with the keys, how to generate and keep them safe?

Developers usually use some of the approaches mentioned below:

1. Generate shared key in the app with shared logic in app and server.

This can be used for encrypting/decrypting data locally and for sending data over Internet. Problem with this approach is, app can be decompiled and logic can reconstructed. Once the logic is reconstructed hacker can keep making the keys as and when required.

2. Get key from Server and use it.

Security of the key depends on how the key is transported to Mobile app. If someone can grab it, then it is compromised and can be used to decrypt data and can even be used to modify it. 

Recommended approach for this is to use HTTPS connection and send key on it. Ideally new key should be used with each request, as this gives very little time to the hacker to identify the key and decrypt the contents.

3. Generate key using random no generator and save it safely.

This can be used for encrypting/decrypting data locally, but where to save the keys. Following post describes how to use Android keystore and store keys safely. This code is tested on devices having OS version 4.4 onwards.

Using Android Keystore to save AES key

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.[1]

Use the Android Keystore provider to let an individual app store its own credentials that only the app itself can access. This provides a way for apps to manage credentials that are usable only by itself while providing the same security benefits that the KeyChain API provides for system-wide credentials. This method requires no user interaction to select the credentials.[1]

For using Keystore we will generate RSA key pair and then use it to encrypt AES key.

private val ALIAS = "Alias"

private val RSA_ALGO = "RSA"
private val KEY_STORE_NAME = "AndroidKeyStore"
private val AES_TRANSFORMATION = "AES/GCM/NoPadding"

private val RSA_TRANSFORMATION = "RSA/ECB/PKCS1Padding"
@Throws(GeneralSecurityException::class)
fun generateRSAKeyPair(context: Context) {
val start = Calendar.getInstance()
val end = Calendar.getInstance()
end.add(Calendar.YEAR, 30)
val spec = KeyPairGeneratorSpec.Builder(context)
.setAlias(ALIAS)
.setSubject(X500Principal("CN=" + ALIAS))
.setSerialNumber(BigInteger.TEN)
.setStartDate(start.time)
.setEndDate(end.time)
.build()

val gen = KeyPairGenerator.getInstance(RSA_ALGO, KEY_STORE_NAME)
gen.initialize(spec)
gen.generateKeyPair()
}

//We can grab the public key using

@Throws(Exception::class)
fun getRsaPublicKey(): PublicKey {
val keyStore = KeyStore.getInstance(KEY_STORE_NAME)
keyStore.load(null)

return keyStore.getCertificate(ALIAS).publicKey
}

//Private key can be retrieved using
@Throws(Exception::class)
fun getRsaPrivateKey(): PrivateKey? {
val keyStore = KeyStore.getInstance(KEY_STORE_NAME)
keyStore.load(null)
return (keyStore.getEntry(ALIAS, null) as? KeyStore.PrivateKeyEntry)?.privateKey
}

//Generate AES Key and IV using
fun generateAesKey(): ByteArray {
    val secureRandom = SecureRandom()
val key = ByteArray(32)
secureRandom.nextBytes(key)
return key
}
fun getIv(): ByteArray {
val secureRandom = SecureRandom()
val iv = ByteArray(12)
secureRandom.nextBytes(iv)
return iv
}
//Encrypt the AES key using RSA public key
fun encryptUsingRsa(plain: ByteArray, publicKey1: PublicKey): String {
val cipher = Cipher.getInstance(RSA_TRANSFORMATION)
cipher.init(Cipher.ENCRYPT_MODE, publicKey1)
val encryptedBytes = cipher.doFinal(plain)

val encrypted = bytesToString(encryptedBytes)
return encrypted
}
//Decrypt the AES key using RSA private key
fun decryptUsingRsa(result: String, privateKey1: PrivateKey?): ByteArray {
if(privateKey1==null) return ByteArray(0)

val cipher1 = Cipher.getInstance(RSA_TRANSFORMATION)
cipher1.init(Cipher.DECRYPT_MODE, privateKey1)
return cipher1.doFinal(stringToBytes(result))
}

fun bytesToString(b: ByteArray): String {
return Base64.encodeToString(b, Base64.NO_WRAP)
}


fun stringToBytes(s: String): ByteArray {
return Base64.decode(s, Base64.NO_WRAP)
}

References:
[1] https://developer.android.com/training/articles/keystore

Android aar deployment in Maven - 2022

Introduction If you are working on android library project, you might be wondering how to publish it on Maven like this . Earl...