Securely Storing Keys in Android Keystore
Asked Answered
S

1

8

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

  1. Generated public and private key already exists before hand
  2. Public key is used to encrypt information, and then passed from server to client
  3. App uses private key to decrypt information

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!

Sibilate answered 30/8, 2015 at 16:53 Comment(2)
If the server only has to send information, not receive it, then you might want to consider having the client generate its own private key after installation, store that key in the android keystore, and then send its public key to the server. Distributing a private key is almost always the wrong way to do things, and having every client use the same private key is worse.Archery
https://mcmap.net/q/323663/-android-keystore-in-kotlin chek thisNautch
S
9

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.

Shrill answered 21/5, 2018 at 10:59 Comment(2)
keyStore.getEntry(secretKeyAlias, null) returns null while decrypting for me on Android 9 for some devices, do you know why that could happen? #57994008Envenom
I understand the concept but to save the private key in shared preference, you have to save it one time in android code and for some reason if the user deletes the app shared preference will be lost. In that case their is no private key present now in shared preference. What to do then?Metalanguage

© 2022 - 2024 — McMap. All rights reserved.