recyclerview viewholder 上的异步加载无法正常工作
Async loading on recyclerview viewholder not working properly
我正在开发一个显示敏感信息的应用程序,所以我必须
加密存储在房间数据库中的所有信息。
经过大量研究,我选择使用 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)
}
所以我必须在列表中显示所有这些信息,所以我正在解密所有信息
在取景器上。问题是当它显示很多项目时太慢了,
所以我决定尝试在取景器内部进行异步解密,令我惊讶的是
我得到了很多 "Unitialized keystore" 异常,让 y 数据加密,
这很奇怪,因为当我上下滚动时,一些观众解密成功
而其他人则不是,这很随机,似乎 viewholder 试图解密更多
不止一次。 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
}
}
}
}
**编辑:
这是我用来生成和获取密钥的代码
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)
}
以下是我看到的问题:
我不确定 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
}
}
这意味着您每次加密时都有一个新密钥?相反,这应该在您尝试 encrypt/decrypt.
之前初始化并准备好
而且,如果这是您唯一生成密钥的地方,那么当您调用这个时,您怎么知道:
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()
并确保在执行任何 encrypt/decrypt 操作之前调用它,我认为应该可以解决您的问题。
我正在开发一个显示敏感信息的应用程序,所以我必须 加密存储在房间数据库中的所有信息。 经过大量研究,我选择使用 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)
}
所以我必须在列表中显示所有这些信息,所以我正在解密所有信息 在取景器上。问题是当它显示很多项目时太慢了, 所以我决定尝试在取景器内部进行异步解密,令我惊讶的是 我得到了很多 "Unitialized keystore" 异常,让 y 数据加密, 这很奇怪,因为当我上下滚动时,一些观众解密成功 而其他人则不是,这很随机,似乎 viewholder 试图解密更多 不止一次。 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
}
}
}
}
**编辑:
这是我用来生成和获取密钥的代码
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)
}
以下是我看到的问题:
我不确定 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
}
}
这意味着您每次加密时都有一个新密钥?相反,这应该在您尝试 encrypt/decrypt.
之前初始化并准备好而且,如果这是您唯一生成密钥的地方,那么当您调用这个时,您怎么知道:
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()
并确保在执行任何 encrypt/decrypt 操作之前调用它,我认为应该可以解决您的问题。