Недействительный SecretKey при добавлении новых биометрических данных

Я столкнулся с проблемой, когда я хочу аннулировать 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
        }
    }
}

Я думаю, что ваш код должен работать, вы уверены, что вызываете аутентификацию после добавления отпечатка пальца перед вызовом генерации. Вы можете восстановить ключ, когда после его удаления возникает KeyPermanentlyInvalidatedException.

Nongthonbam Tonthoi 12.12.2020 06:31

Да, у меня есть одна кнопка, которая генерирует ключ, и другая, которая его вытаскивает. Одна вещь, о которой я подумал, это то, что я тестирую эмулятор Android — когда я получу физическое устройство, я вернусь и сообщу, что происходит!

Connor Jackson 12.12.2020 16:08
4
2
1 025
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

(Отвечая на мой собственный вопрос). Я получил это для работы с вышеуказанным решением на следующих характеристиках устройства.

  • Устройство: Samsung A31, пиксель 2XL
  • Версия Android: 10
  • Биометрический: отпечаток пальца

Раньше я тестировал это с помощью эмулятора, и ключи не становились недействительными. Я не уверен, почему, но тестирование эмулятора не могло эффективно протестировать этот сценарий.

Другие вопросы по теме