在 Android Keystore 中安全地存储密钥

dem*_*god 6 java security encryption android android-keystore

我正在制作一个与服务器通信的 android 应用程序。我在我的服务器上使用基于令牌的身份验证,并将信息从服务器传递给客户端,我使用了非对称加密。

过程是这样的

  1. 生成的公钥和私钥事先已经存在
  2. 公钥用于加密信息,然后从服务器传递给客户端
  3. App使用私钥解密信息

但是,我不知道如何将私钥安全地存储在密钥库中。如果我在运行时存储它,密钥将在代码中,如果我在 REST 连接期间发送私钥,那么加密就没有意义,因为黑客可以找到这两个密钥。任何人都可以帮助我创建最佳解决方案吗?提前谢谢!

Dem*_*god 7

您可以将您的私钥存储在共享首选项中,但使用生成的密钥加密,该密钥将存储在 Android 中KeyStore,这将为存储私钥提供更高的安全性。

请参阅下面的 Kotlin 示例。首先,您需要生成密钥:

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()
}
Run Code Online (Sandbox Code Playgroud)

它将自动存储在 中,KeyStore因为我们在获取 a 的实例时将其称为提供者KeyGenerator

稍后,当您需要再次获取密钥时,您可以这样做:

fun getSecretKey(): SecretKey {
    val keyStore = KeyStore.getInstance("AndroidKeyStore").apply { load(null) }
    val secretKeyEntry = keyStore.getEntry(secretKeyAlias, null) as KeyStore.SecretKeyEntry
    return secretKeyEntry.secretKey
}
Run Code Online (Sandbox Code Playgroud)

或者,您始终可以使用getSecretKey()方法,如果通过将最后一行更改KeyStore为从is获得,它将生成新的方法null

return secretKeyEntry.secretKey ?: generateSecretKey()
Run Code Online (Sandbox Code Playgroud)

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())
}
Run Code Online (Sandbox Code Playgroud)

在这里,方法encrypt将返回一个ByteArray您可以存储在SharedPreferences. 注意:您还应该存储初始化向量 (IV)。在这里,它被存储到iv属性中。

要解密存储的数据,请使用以下方法:

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)
}
Run Code Online (Sandbox Code Playgroud)

在这里,您必须将商店初始化向量 (IV) 传递给GCMParameterSpec.

希望它会帮助某人。

  • keyStore.getEntry(secretKeyAlias, null) 在某些设备的 Android 9 上为我解密时返回 null,您知道为什么会发生这种情况吗?/sf/ask/4059545591/ (2认同)