Я столкнулся с проблемой, когда я хочу аннулировать SecretKey, когда пользователь добавляет новый отпечаток пальца на свое устройство Android. Я могу сгенерировать ключ, получить биометрическую подсказку без проблем. Когда я добавляю новый отпечаток пальца, ключ не генерирует исключение... он по-прежнему позволяет отображать подсказку.
Я могу удалить все отпечатки пальцев с устройства, и возникает исключение KeyPermanentlyInvalidatedException.
Я использую библиотеку BiometricPrompt AndroidX с моим minSDK, установленным на 28. Я следовал этому руководству: https://developer.android.com/training/sign-in/biometric-auth#crypto
Мой код ниже - я был бы очень признателен, если бы кто-то захотел помочь мне с этой проблемой.
package com.example.dogtime
import android.os.Bundle
import android.security.keystore.KeyGenParameterSpec
import android.security.keystore.KeyPermanentlyInvalidatedException
import android.security.keystore.KeyProperties
import android.widget.Button
import androidx.appcompat.app.AppCompatActivity
import androidx.biometric.BiometricPrompt
import androidx.core.content.ContextCompat
import butterknife.ButterKnife
import java.security.KeyStore
import javax.crypto.Cipher
import javax.crypto.KeyGenerator
import javax.crypto.SecretKey
class MainActivity : AppCompatActivity() {
private lateinit var biometricPrompt: BiometricPrompt
private lateinit var biometricPromptInfo: BiometricPrompt.PromptInfo
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
ButterKnife.bind(this)
setContentView(R.layout.activity_main)
// Set up prompt
biometricPrompt = BiometricPrompt(
this,
ContextCompat.getMainExecutor(baseContext),
object : BiometricPrompt.AuthenticationCallback() {
// no-op
}
)
// Prompt info
biometricPromptInfo = BiometricPrompt.PromptInfo.Builder()
.setTitle("Title")
.setNegativeButtonText("Test")
.build()
// Generates key
findViewById<Button>(R.id.count_btn).setOnClickListener {
generateKey()
}
// Shows biometric prompt
findViewById<Button>(R.id.random_btn).setOnClickListener {
getCrypto()?.let { it1 -> biometricPrompt.authenticate(biometricPromptInfo, it1) }
?: run {
// Key is invalidated - this is never triggered
}
}
}
private fun generateKey() {
val keyGenerator = KeyGenerator
.getInstance(KeyProperties
.KEY_ALGORITHM_AES, "AndroidKeyStore")
keyGenerator.init(getKeyGen())
keyGenerator.generateKey()
}
private fun getCipher() : Cipher {
return Cipher.getInstance(
KeyProperties.KEY_ALGORITHM_AES + "/"
+ KeyProperties.BLOCK_MODE_CBC + "/"
+ KeyProperties.ENCRYPTION_PADDING_PKCS7
)
}
private fun getKey() : SecretKey {
val keyStore = KeyStore.getInstance("AndroidKeyStore")
keyStore.load(null)
return keyStore.getKey("key6", null) as SecretKey
}
private fun getKeyGen() : KeyGenParameterSpec {
return KeyGenParameterSpec.Builder(
"key6",
KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT
).setBlockModes(KeyProperties.BLOCK_MODE_CBC)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
.setUserAuthenticationRequired(true)
.setInvalidatedByBiometricEnrollment(true)
.build()
}
private fun getCrypto() : BiometricPrompt.CryptoObject? {
return try {
val cipher = getCipher()
val key = getKey()
cipher.init(Cipher.ENCRYPT_MODE, key)
BiometricPrompt.CryptoObject(cipher)
} catch(e: KeyPermanentlyInvalidatedException) {
val keyStore = KeyStore.getInstance("AndroidKeyStore")
keyStore.load(null)
keyStore.deleteEntry("key6")
null
}
}
}
Да, у меня есть одна кнопка, которая генерирует ключ, и другая, которая его вытаскивает. Одна вещь, о которой я подумал, это то, что я тестирую эмулятор Android — когда я получу физическое устройство, я вернусь и сообщу, что происходит!
(Отвечая на мой собственный вопрос). Я получил это для работы с вышеуказанным решением на следующих характеристиках устройства.
Раньше я тестировал это с помощью эмулятора, и ключи не становились недействительными. Я не уверен, почему, но тестирование эмулятора не могло эффективно протестировать этот сценарий.
Я думаю, что ваш код должен работать, вы уверены, что вызываете аутентификацию после добавления отпечатка пальца перед вызовом генерации. Вы можете восстановить ключ, когда после его удаления возникает
KeyPermanentlyInvalidatedException
.