Перейти к содержанию

Ключи в AndroidKeyStore создаются без требования биометрической инвалидации

Описание

Проверка показала, что при генерации ключей в Android KeyStore не устанавливается флаг .setInvalidatedByBiometricEnrollment(true) (доступен с API 24 в KeyGenParameterSpec.Builder).

Когда этот параметр не задан (false — значение по умолчанию), ключ остаётся действительным даже после добавления, изменения или удаления биометрических данных (отпечатков, лица). Это означает, что злоумышленник, получив физический доступ к устройству, может:

  1. зарегистрировать собственный отпечаток/лицо;
  2. пройти Biometric Prompt;
  3. использовать ключ для расшифровки конфиденциальных данных или подписи транзакций.

Google и OWASP рекомендуют включать инвалидацию, чтобы любой изменённый биометрический шаблон инвалидировал существующий ключ и требовал его повторной генерации.

Потенциальные последствия

  • Обход биометрической защиты. Атакующий добавляет свой отпечаток и моментально получает доступ к хранилищу без знания PIN/пароля.
  • Утечка приватных данных (токены, сеансовые ключи, офлайн-БД).
  • Несоответствие требованиям OWASP MASVS (M9) и внутренним политикам компаний-эмитентов, где смена/добавление биометрии должна автоматически отозвать криптоключи.
  • Повышенный риск при устройстве, выданном сотруднику

Рекомендации

  1. Всегда включайте инвалидацию при генерации ключа

    KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(
          alias,
          KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
       .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
       .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
       .setUserAuthenticationRequired(true)                // требуется биометрия/PIN
       .setUserAuthenticationValidityDurationSeconds(0)    // каждый раз
       .setInvalidatedByBiometricEnrollment(true)          // ← критический флаг
       .build();
    
  2. Обрабатывайте KeyPermanentlyInvalidatedException

    При обращении к ключу ловите исключение и регинирируйте ключи после обновления отпечатков:

    try {
       cipher.doFinal(...)
    } catch (e: KeyPermanentlyInvalidatedException) {
       generateKeysAgain()   // повторная генерация
    }
    
  3. Проверяйте свойство isInvalidatedByBiometricEnrollment() через KeyInfo

    После загрузки ключа убедитесь, что флаг действительно установлен; иначе — откажите в операции.

  4. Используйте StrongBox / TEE + BiometricPrompt

    Совмещайте аппаратное хранилище и обязательную аутентификацию, чтобы даже при рут-доступе ключ не экспортировался.

Ссылки

  1. Android Developers – setInvalidatedByBiometricEnrollment
  2. OWASP MASTG – Local Authentication (§ “setInvalidatedByBiometricEnrollment”)
  3. Medium – “Handling Key Invalidation with Biometric Prompt”
  4. Stack Overflow – Why setInvalidatedByBiometricEnrollment matters
  5. KeyPermanentlyInvalidatedException – API doc
К началу