I am making an android application that communicates with a server. I'm using token based authentication on my server, and to pass information to the client from the server, I am using asymmetric encryption.
This is how the process goes
However, I do not know how to securely store the private key in the keystore. If I store it during runtime, the key will be out in the code, and if I send the private key during the REST connection, then there's no point of having the encryption because a hacker can find both keys. Can anyone help me on creating the best possible solution? THX in advance!
You can store your private key in shared preferences, but encrypted with generated secret key, which will be stored in Android KeyStore
, which will give much more security in storing the private key.
Please see example below in Kotlin. First, you need to generate secret key:
fun generateSecretKey(): SecretKey {
val keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore")
val spec = KeyGenParameterSpec
.Builder(secretKeyAlias, KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.build()
keyGenerator.init(spec)
return keyGenerator.generateKey()
}
It will be automatically stored in the KeyStore
since we're mentioning it as a provider when getting instance of a KeyGenerator
.
Later, when you will need to obtain secret key again you can do it like this:
fun getSecretKey(): SecretKey {
val keyStore = KeyStore.getInstance("AndroidKeyStore").apply { load(null) }
val secretKeyEntry = keyStore.getEntry(secretKeyAlias, null) as KeyStore.SecretKeyEntry
return secretKeyEntry.secretKey
}
Or you can always use getSecretKey()
method, which will generate new one if the obtained from the KeyStore
is null
by changing last line to:
return secretKeyEntry.secretKey ?: generateSecretKey()
When SecretKey
is obtained you can proceed with encryption:
fun encrypt(data: String): ByteArray? {
val cipher = Cipher.getInstance("AES/GCM/NoPadding")
cipher.init(Cipher.ENCRYPT_MODE, getSecretKey())
iv = cipher.iv
return cipher.doFinal(data.toByteArray())
}
Here, method encrypt
will return a ByteArray
that you can store in the SharedPreferences
.
NOTE: that you should also store initialization vector (IV). Here it is stored to the iv
property.
To decrypt stored data, use this method:
fun decrypt(encrypted: ByteArray): String {
val cipher = Cipher.getInstance("AES/GCM/NoPadding")
val spec = GCMParameterSpec(128, iv)
cipher.init(Cipher.DECRYPT_MODE, getSecretKey(), spec)
val decoded = cipher.doFinal(encrypted)
return String(decoded, Charsets.UTF_8)
}
Here, you must pass store initialization vector (IV) to GCMParameterSpec
.
Hope it will helps someone.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With