简体   繁体   English

在Android密钥库中安全存储密钥

[英]Securely Storing Keys in Android Keystore

I am making an android application that communicates with a server. 我正在制作一个与服务器通信的android应用程序。 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. 如果我在运行时存储它,密钥将在代码中输出,如果我在REST连接期间发送私钥,那么就没有加密的意义,因为黑客可以找到这两个密钥。 Can anyone help me on creating the best possible solution? 任何人都可以帮我创建最好的解决方案吗? THX in advance! THX提前!

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. 您可以将私钥存储在共享首选项中,但使用生成的密钥加密,密钥将存储在Android KeyStore ,这将在存储私钥时提供更高的安全性。

Please see example below in Kotlin. 请参阅下面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 . 它将自动存储在KeyStore因为我们在获取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: 或者你总是可以使用getSecretKey()方法,如果从KeyStore获得的是null通过将最后一行更改为:

return secretKeyEntry.secretKey ?: generateSecretKey()

When SecretKey is obtained you can proceed with encryption: 获得SecretKey ,您可以继续加密:

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 . 这里,方法encrypt将返回一个ByteArray ,您可以将其存储在SharedPreferences NOTE: that you should also store initialization vector (IV). 注意:您还应该存储初始化向量(IV)。 Here it is stored to the iv property. 它存储在iv属性中。

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 . 在这里,您必须将商店初始化向量(IV)传递给GCMParameterSpec

Hope it will helps someone. 希望它会帮助某人。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM