Recyclerview Viewholder上的异步加载无法正常工作

时间:2019-04-17 21:20:08

标签: android kotlin android-asynctask android-viewholder

我正在开发一个显示明智信息的应用程序,因此我必须 加密存储在会议室数据库中的所有信息。 经过大量研究,我选择使用AES加密,生成一个随机密钥 并存储在KeyStore上(minSdk:24 btw)。

private fun encript(plain: String): String? {
    return try {
        generateKey()
        encData(plain)
    } catch (e: Throwable) {
        if (!BuildConfig.DEBUG){
            Crashlytics.logException(e)
        }
        null
    }
}

private fun encData(plain: String): String? {
    val sKey = getSecretKey()
    iv = ByteArray(12)
    secRng = SecureRandom()
    secRng.nextBytes(iv)
    val cipher = Cipher.getInstance(AES_MODE)
    val parameterSpec = GCMParameterSpec(128, iv)
    cipher.init(Cipher.ENCRYPT_MODE, sKey, parameterSpec)
    cipText = cipher.doFinal(plain.toByteArray())
    return encBuffer()
}

private fun encBuffer(): String? {
    val byteBuffer = ByteBuffer.allocate(4 + iv.size + cipText.size)
    byteBuffer.putInt(iv.size)
    byteBuffer.put(iv)
    byteBuffer.put(cipText)
    val cipherMessage = byteBuffer.array()
    //clean()
    return Base64.encodeToString(cipherMessage, Base64.DEFAULT)
}

所以我必须在列表上显示所有这些信息,所以我解密所有信息 在观察者上。问题是当它显示很多项目时太慢了, 所以我决定在Viewholder中尝试异步解密,这让我感到惊讶 我遇到了很多“统一密钥库”异常,让y数据加密, 这很奇怪,因为当我上下滚动时,一些视图持有者成功解密了 而其他人则不是,这是很随机的,似乎是观察者试图解密更多 不止一次。 PS:出于安全原因,我无法在SharedPrerences上缓存解密的数据

fun decription(encStr: String): String? {
    return try {
        dec(encStr)
    } catch (e: Throwable) {
        Log.d("cripto", "Here, Trowing Unitialized Keystore")
        if (!BuildConfig.DEBUG){
            Crashlytics.logException(e)
        }
        null
    }
}

private fun dec(encStr: String): String {
    val byteBuffer = ByteBuffer.wrap(Base64.decode(encStr, Base64.DEFAULT))
    val ivLength = byteBuffer.int
    if (ivLength < 12 || ivLength >= 16) { // check input parameter
        throw IllegalArgumentException("invalid iv length")
    }
    val iv = ByteArray(ivLength)
    byteBuffer.get(iv)
    val cipherText = ByteArray(byteBuffer.remaining())
    byteBuffer.get(cipherText)
    return callCip(cipherText, iv)

}

private fun callCip(cipText: ByteArray, iv: ByteArray): String {
    val cipher = Cipher.getInstance("AES/GCM/NoPadding")
    cipher.init(Cipher.DECRYPT_MODE, getSecretKey(), GCMParameterSpec(128, iv))
    val plainText = cipher.doFinal(cipText)
    return String(plainText)
}

还有我的观看者代码:

    doAsync {

            var name = ""
            var lname = ""
            var patRecord = ""

            if (value?.patient != null){
                name = PatSecHelper.nToPat(value.patient?.name?.trim() ?: "")
                lname = PatSecHelper.nToPat(value.patient?.lastName?.trim() ?: "")
                patRecord = PatSecHelper.nToPat(value.patient?.patientRecord?.trim() ?: "")

            }

            onComplete {
                if (value?.patient == null){
                    view.textViewPatientId.visibility = View.GONE
                }else{
                    if (name == "" || lname == "") {
                        view.textViewPatient.text = "Error..."
                    }else{
                        view.textViewPatient.text = "$name $lname"
                    }
                    if (patRecord == ""){
                        view.textViewPatientId.text = "Error..."
                    }else{
                        view.textViewPatientId.text = patRecord
                    }

                }

            }
        }

**编辑:

这是我用于genarete和获取密钥的代码

    private fun generateKey(){
    keyStore = KeyStore.getInstance(AndroidKeyStore)
    keyStore.load(null)

    if (!keyStore.containsAlias(KEY_ALIAS)) {
        val keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, AndroidKeyStore)
        keyGenerator.init(
                KeyGenParameterSpec.Builder(KEY_ALIAS,
                        KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT)
                        .setBlockModes(KeyProperties.BLOCK_MODE_GCM).setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
                        .setRandomizedEncryptionRequired(false)
                        .build())
        keyGenerator.generateKey()
    }
}

@Throws(Exception::class)
private fun getSecretKey(): java.security.Key {
    keyStore = KeyStore.getInstance(AndroidKeyStore)
    keyStore.load(null)
    return keyStore.getKey(KEY_ALIAS, null)
}

1 个答案:

答案 0 :(得分:1)

这是我看到的问题:

我不确定generateKey()的实现是什么,但是如果它实现了它的提示,那么不要在每次加密时都生成密钥。我相信您正在这里:

private fun encript(plain: String): String? {
    return try {
        generateKey() // <- why are you doing this every time? are you storing a unique key with everything you encrypt?
        encData(plain)
    } catch (e: Throwable) {
        if (!BuildConfig.DEBUG) {
            Crashlytics.logException(e)
        }
        null
    }
}

这意味着您每次加密某些内容时都有一个新密钥吗?而是应该先初始化并准备好,然后再尝试加密/解密。

而且,如果这是您唯一生成密钥的位置,那么在调用此键时如何知道呢?

private fun callCip(cipText: ByteArray, iv: ByteArray): String {
    val cipher = Cipher.getInstance("AES/GCM/NoPadding")
    cipher.init(Cipher.DECRYPT_MODE, getSecretKey(), GCMParameterSpec(128, iv)) // <- Are you sure you have called encrypt before callin this?
    val plainText = cipher.doFinal(cipText)
    return String(plainText)
}

您生成了要获取的密钥?拔出generateKey()并确保在执行任何加密/解密操作之前已调用它,应该会解决您的问题。