#android #kotlin #android-asynctask #android-viewholder
#Android #kotlin #android-asynctask #android-viewholder
Вопрос:
Я разрабатываю приложение, которое показывает разумную информацию, поэтому мне нужно зашифровать всю информацию, хранящуюся в базе данных Room. После долгих исследований я решил зашифровать с помощью AES, сгенерировав случайный ключ и сохранив его в хранилище ключей (кстати, minSdk: 24).
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. проблема в том, что слишком медленно, когда отображается много элементов,
поэтому я решил попробовать асинхронное шифрование внутри viewholder, и, к моему удивлению,
я получил много исключений «Unitialized keystore», позволяющих зашифровать данные y,
это странно, потому что, когда я прокручиваю вниз и вверх, некоторые viewholders расшифровываютуспешно
, а другие нет, это довольно случайно, и кажется, что 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)
}
И мой код viewholder:
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)
}
Комментарии:
1. У вас много данных, которые вы загружаете одновременно? Вы пробовали использовать подкачку?
Ответ №1:
Вот проблемы, которые я вижу:
Я не уверен, что это за реализация generateKey()
, но если она делает то, что написано на tin, тогда не генерируйте свой ключ каждый раз при шифровании. Как я полагаю, вы делаете здесь:
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()
Я думаю, что извлечение и проверка того, что вы вызвали его, прежде чем выполнять какие-либо действия по шифрованию / дешифрованию, должны решить вашу проблему.
Комментарии:
1. Извините, я тоже забыл показать этот код, я просто отредактировал свой вопрос. Я получаю ключи внутри этого метода callCip непосредственно в cipher.init, вызывая getSecrekKey() в качестве параметра. Я не генерирую много ключей, потому что я проверяю, существует ли он уже в методе GenerateKey ()
2. Да, мой совет остается прежним… Я думаю, что эти методы не синхронизированы с одной и той же блокировкой, и могут легко возникнуть условия гонки, когда состояние несовместимо. Я бы поместил оба этих метода в объект kotlin / singleton и ключи кэша там. Для подтверждения вызовите GenerateKey перед выполнением какой-либо асинхронной параллельной работы, а затем попробуйте.
3. Большое вам спасибо, Лоуренс, я внес изменения и пока не получил никаких исключений, большое спасибо!! Не могли бы вы помочь мне еще в одном? Мой RecyclerView, который показывает эти данные, продолжает зависать, особенно когда я пытаюсь прокручивать вниз / вверх быстрее, я думаю, что асинхронное дешифрование внутри viewholder решит проблему, есть мысли, как сделать мою прокрутку recyclerview более быстрой?
4. Привет, @Shermano — нет проблем. Боюсь, я не очень разбираюсь в Android: D в основном работаю с python, серверными системами kotlin и vuejs: D
5. нет проблем @Laurence, большое спасибо, что помогли мне!!