【问题标题】:Encryption and Decryption with KeyStore private key and iv goes wrong "Some times"使用 KeyStore 私钥和 iv 加密和解密“有时”出错
【发布时间】:2020-04-13 07:38:09
【问题描述】:

我正在像这样使用 KeyStore 的加密和解密。

这就是流程。在我将字符串传递给加密方法后,我同时保存了加密和 iv,以便我以后可以用它检索值。问题是有时,某些加密值无法正确检索......不是全部!所以认为我加密了 10 个项目并将它们保存在某个地方(加密和 iv)。然后,当我想检索其中一个时,无法正确检索!

 init {
        keyStore = KeyStore.getInstance(ANDROID_KEY_STORE)
        keyStore.load(null)
        cipher = Cipher.getInstance("AES/GCM/NoPadding")
    }

  fun encryptData(text: String): Pair<ByteArray, String>? {
        try {
            cipher.init(Cipher.ENCRYPT_MODE, getSecretKet(ALIAS))
            val iv = cipher.iv.toString(Charsets.ISO_8859_1)
            val result = cipher.doFinal(text.toByteArray(Charsets.ISO_8859_1))
            Timber.i("$TAG encrypted data $result")
            Timber.i("$TAG encrypted iv $iv")
            return if (result != null) {
                Pair(result, iv)
            } else {
                null
            }
        } catch (e: Exception) {
            Timber.e("$TAG error encryptData", e)
            return null
        }
    }

fun decryptData(encryptedData: ByteArray, iv: ByteArray): String {
    return try {
        val spec = GCMParameterSpec(128, iv)
        cipher.init(Cipher.DECRYPT_MODE, getSecretKet(ALIAS), spec)
        val result = cipher.doFinal(encryptedData).toString(Charsets.ISO_8859_1)
        Timber.i("$TAG decrypted data $result")
        result
    } catch (e: Exception) {
        Timber.e("$TAG decryptData error may string was not encrypted", e)
        encryptedData.toString()
    }
}

这是用于获取密钥。首先,我认为问题可能出在我的密钥上,所以我以这种方式实现了它,并且这个类是单音的。但是有些时候我重新打开应用程序时,这个密钥是不一样的(我认为,因为 iv 和加密值以及密码都是固定的)。我也使用 Charsets.ISO_8859_1 因为发现这个字符集更好地保留所有字符并减少丢失。 然后我认为可能是保存位置的问题,所以为了测试我只是从带有字符串字段的 Room db 移动到带有字符串的 SharePref。但是问题是一样的,所以现在我很确定这与 savig 存储库无关。

private fun getSecretKet(alias: String): Key {

    if (keyStore.containsAlias(alias)) {
        //Try for existing key
        return keyStore.getKey(alias, null)
    } else {
        //Key is not present, create new one.
        val keyGenerator = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            val kGenerator =
                KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, ANDROID_KEY_STORE)
            val specs = KeyGenParameterSpec
                .Builder(
                    alias,
                    KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT
                )
                .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
                .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
                .build()
            kGenerator.init(specs)
            kGenerator
        } else {
            KeyGenerator.getInstance(ANDROID_KEY_STORE);
        }
        return keyGenerator.generateKey()
    }
}

我认为这在大多数情况下都可以正常工作,但最终我无法获得我上面提到的一些加密数据。有人知道吗?

【问题讨论】:

  • 我对 Kotlin 不是很熟悉,但是像您正在做的那样使用 String 作为二进制数据的容器通常是一个错误。如果您有一个必须转换为字符串的任意字节序列,则为此目的使用适当的编码器。如果您想显示数据以供人工分析,十六进制编码器很有用。否则,请使用标准或 url 安全的 base64 编码器。我不知道这是否是您的问题的原因。
  • 我明白你的意思,好吧,让我用 base64 字符串测试一下。
  • @PresidentJamesMoveonPolk 你的意思是当我将它传递给密码进行加密或将字节转换为 iv 字符串以保存时需要 base64 吗?
  • cipher 需要字节作为输入,并返回字节作为输出。第一个问题:您为什么要尝试将输出转换为字符串?字节数组是保存和交换数据的完美一等公民。
  • @PresidentJamesMoveonPolk 因为我创建了一个存储库来保存这些值并开始该存储库实现了共享首选项,我认为最好将其转换为字符串,以便我们可以轻松创建任何处理字符串的存储库.不,它保存在 Room db 中。这就是原因。

标签: android kotlin encryption keystore


【解决方案1】:

我最终通过更深入地研究 AES 更改实现来解决这个问题。

  1. 最主要的是IV!简而言之,IV 是一种使加密更加复杂的工具。我们创建一个 IV 并将其传递给 cipher,cipher 在多轮和字节块中加密数据和混合字节,并且在每一轮中,它使用逻辑更新 IV 并将其用于下一轮或字节块。因此,如果您在完成加密后从密码中获得 IV,您会发现它发生了变化!
  2. 第二件事是我使用 imelemneted cipher singleTone 但我自己更改了它的创建加密和解密方法。因此,每个用于编码的字符串都会有新的 IV,并且在编码完成后,我将它与加密字符串一起传递给以后解密。

首先我认为它会在@PresidentJamesMovelenPolk 的帮助下通过字符串加密,但我测试了即使使用 ISO_8859_1 也能正常运行。这是最终的实现:

 companion object {
    const val TRANSFORMATION = "AES/GCM/NoPadding"
    const val ANDROID_KEY_STORE = "AndroidKeyStore"
    const val ALIAS = "MyApp"
    const val TAG = "KeyStoreManager"
}

private var keyStore: KeyStore

init {
    keyStore = KeyStore.getInstance(ANDROID_KEY_STORE)
    keyStore.load(null)
}

fun encryptData(text: String): Pair<ByteArray, String>? {
    try {

        val cipher = Cipher.getInstance(TRANSFORMATION)
        cipher.init(Cipher.ENCRYPT_MODE, getSecretKet(ALIAS))
        val iv = cipher.iv

        val result = cipher.doFinal(text.toByteArray(Charsets.ISO_8859_1))
        val resultIv = Base64.encodeToString(iv, Base64.NO_WRAP)
        Timber.i("$TAG encrypted data $result")
        Timber.i("$TAG encrypted iv $iv")
        return if (result != null) {
            Pair(result, resultIv)
        } else {
            null
        }
    } catch (e: Exception) {
        Timber.e("$TAG error encryptData", e)
        return null
    }
}

fun encryptString(text: String): SecuredData? {
    return try {
        val result = encryptData(text)
        if (result != null) {
            SecuredData(result.first.toString(Charsets.ISO_8859_1), result.second)
        } else {
            null
        }
    } catch (e: Exception) {
        Timber.e("$TAG error encryptString", e)
        null
    }
}

/**
 Get pair of encrypted value and iv
 */
fun decryptString(encryptedString: String, iv: String): String {
    return try {
        val result = decryptData(
            encryptedString.toByteArray(Charsets.ISO_8859_1),
            Base64.decode(iv, Base64.NO_WRAP)
        )
        result
    } catch (e: java.lang.Exception) {
        Timber.e("Error in convert to Base64")
        encryptedString
    }
}

fun decryptData(encryptedData: ByteArray, iv: ByteArray): String {
    return try {
        val cipher = Cipher.getInstance(TRANSFORMATION)
        val spec = GCMParameterSpec(128, iv)
        cipher.init(Cipher.DECRYPT_MODE, getSecretKet(ALIAS), spec)
        val result = cipher.doFinal(encryptedData).toString(Charsets.ISO_8859_1)
        Timber.i("$TAG decrypted data $result")
        result
    } catch (e: Exception) {
        Timber.e("$TAG decryptData error may string was not encrypted", e)
        encryptedData.toString()
    }
}

private fun getSecretKet(alias: String): Key {

    if (keyStore.containsAlias(alias)) {
        // Try for existing key
        return keyStore.getKey(alias, null)
    } else {
        // Key is not present, create new one.
        val keyGenerator = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            val kGenerator =
                KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, ANDROID_KEY_STORE)
            val specs = KeyGenParameterSpec
                .Builder(
                    alias,
                    KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT
                )
                .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
                .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
                .build()
            kGenerator.init(specs)
            kGenerator
        } else {
            KeyGenerator.getInstance(ANDROID_KEY_STORE)
        }
        return keyGenerator.generateKey()
    }
}

@Keep
data class SecuredData(val value: String, val iv: String)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-08-02
    • 1970-01-01
    • 1970-01-01
    • 2017-06-02
    • 1970-01-01
    • 2014-07-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多