Android Keystore — это системный механизм Android для безопасного хранения криптографических ключей в аппаратной изоляции. Система использует Trusted Execution Environment (TEE) на устройствах с ARM TrustZone или выделенный Secure Element для защиты ключей на уровне чипа. Согласно Android Open Source Project, Keystore поддерживает алгоритмы RSA, EC, AES и HMAC с генерацией ключей непосредственно в защищённой среде.
Главное
Android Keystore — это криптографический провайдер (provider), реализованный в Android начиная с API 1 (Android 1.0), но полноценная аппаратная поддержка появилась с Android 4.3 (API 18). Keystore решает задачу безопасного хранения закрытых ключей таким образом, чтобы даже при компрометации операционной системы злоумышленник не мог извлечь ключи в открытом виде.
Архитектура Android Keystore состоит из трёх уровней: прикладной API (java.security.KeyStore), системный сервис (keystore daemon) и аппаратный уровень (Keymaster HAL). Приложение обращается через стандартный API Java Cryptography Architecture (JCA), а системный сервис маршрутизирует запросы к Keymaster, работающему в TEE.
Все криптографические операции с ключами (подпись, расшифровка) выполняются внутри TEE или Secure Element. Ключи никогда не покидают защищённую среду — приложение получает только хэндл (псевдоним) для обращения к ключу. Это фундаментальное отличие от программных KeyStore, где ключи потенциально доступны в памяти процесса.
Стандартный JKS (Java KeyStore) или BKS (Bouncy Castle) хранят ключи в файлах, защищённых паролем. Android Keystore хранит ключи в аппаратной изоляции, где они защищены даже от root-пользователя. JKS уязвим при прямом доступе к файловой системе, Android Keystore — нет.
Другое отличие: в Android Keystore ключи имеют строгие параметры использования (purpose — только sign/verify/encrypt/decrypt), задаваемые при генерации. Их нельзя изменить впоследствии, что предотвращает misuse ключа.
При создании нового ключа приложение вызывает KeyPairGenerator или KeyGenerator с KeyGenParameterSpec, который содержит все параметры будущего ключа. Система передаёт запрос в Keymaster HAL, который генерирует ключ внутри TEE и возвращает хэндл.
Метод KeyGenParameterSpec.Builder принимает обязательные параметры: имя ключа в Keystore, назначение (PURPOSE_SIGN, PURPOSE_ENCRYPT), алгоритм (RSA, EC, AES). Дополнительно: digest (SHA-256), padding (PKCS7), userAuthenticationRequired (биометрия), keyValidityStart/End (временные ограничения).
После задания параметров KeyPairGenerator.generateKeyPair() возвращает KeyPair, где PrivateKey — это объект, делегирующий операции Keymaster. Открытый ключ может быть извлечён, закрытый — нет. Он существует только внутри TEE.
import java.security.KeyPairGenerator
import android.security.keystore.KeyGenParameterSpec
import android.security.keystore.KeyProperties
fun generateKey(alias: String) {
val spec = KeyGenParameterSpec.Builder(alias,
KeyProperties.PURPOSE_SIGN or KeyProperties.PURPOSE_VERIFY
).setDigests(KeyProperties.DIGEST_SHA256)
.setSignaturePaddings(KeyProperties.SIGN_PADDING_RSA_PKCS1)
.setUserAuthenticationRequired(true)
.build()
val kpGen = KeyPairGenerator.getInstance(
KeyProperties.KEY_ALGORITHM_RSA,
"AndroidKeyStore"
)
kpGen.initialize(spec)
kpGen.generateKeyPair()
}
Signature для ECDSA или RSA-PSS создаётся через стандартный API: Signature.getInstance(algorithm).initSign(privateKey). Операция подписания выполняется в TEE: приложение передаёт данные, Keymaster подписывает их аппаратно и возвращает подпись. Ключ и данные не смешиваются в общей памяти.
Для биометрической защиты необходимо перед подписанием аутентифицировать пользователя через BiometricPrompt. Без успешной аутентификации Keymaster не выполняет операцию, возвращая CryptoAuthenticationException.
import java.security.KeyStore
import java.security.Signature
import androidx.biometric.BiometricPrompt
fun signWithBiometric(alias: String) {
val ks = KeyStore.getInstance("AndroidKeyStore")
ks.load(null)
val entry = ks.getEntry(alias, null) as KeyStore.PrivateKeyEntry
val signature = Signature.getInstance("SHA256withRSA")
signature.initSign(entry.privateKey)
// BiometricPrompt с CryptoObject(signature) запрашивает FaceID/PIN
}
Android поддерживает два режима хранения ключей: программный (на устройствах без TEE) и аппаратный (на устройствах с TEE или Secure Element). Режим зависит от возможностей SoC и версии Android.
На устройствах без Trusted Execution Environment (до Android 4.3 или бюджетные SoC) ключи хранятся в зашифрованном виде с использованием мастер-ключа, полученного из пароля экрана блокировки. Этот режим менее безопасен — ключи доступны в памяти процесса при выполнении криптографических операций.
Уровень защиты основан на шифровании файла KeyStore с помощью AES-256-GCM. Ключ шифрования генерируется на основе пароля пользователя или PIN-кода через Scrypt (PBKDF2 с большим числом итераций).
На современных устройствах используется Keymaster 4.x в TEE (ARM TrustZone). Ключи генерируются, хранятся и используются исключительно внутри TrustZone. Даже ядро Linux не имеет доступа к закрытым ключам — только Keymaster HAL может выполнять операции.
Secure Element (например, eSE в Samsung Knox или StrongBox в Google Pixel 3+) — это отдельный чип с собственным процессором и памятью. Он сертифицирован Common Criteria EAL 4+ и обеспечивает максимальный уровень защиты, включая защиту от физического вскрытия.
| Тип | Место хранения | Уровень защиты | Доступно с API |
|---|---|---|---|
| Software | Файл /data/misc/keystore | Средний (AES-256) | API 1+ |
| Keymaster 3 | TEE (TrustZone) | Высокий | API 23+ |
| Keymaster 4 | TEE + Secure I/O | Очень высокий | API 28+ |
| StrongBox | Аппаратный Secure Element | Максимальный | API 28+, опционально |
Android Keystore интегрирован в Java Cryptography Architecture (JCA). Для доступа к провайдеру используется стандартный KeyStore.getInstance("AndroidKeyStore"). API доступен начиная с API 18.
Метод KeyStore.load(null) загружает контейнер KeyStore текущего приложения. Пароль не требуется — Android использует контекст приложения и его UID для разграничения доступа. Каждое приложение видит только свои записи, если не используется shared UID.
Методы setEntry и getEntry работают с KeyStore.PrivateKeyEntry, SecretKeyEntry или TrustedCertificateEntry. Параметр ProtectionParameter — всегда null для Android KeyStore (защита реализована на уровне системы).
import java.security.KeyStore
import java.security.cert.Certificate
import android.security.keystore.KeyProtection
fun storeSecretKey(alias: String, key: SecretKey) {
val ks = KeyStore.getInstance("AndroidKeyStore")
ks.load(null)
val prot = KeyProtection.Builder(
KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT
).setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.setUserAuthenticationRequired(true)
.build()
ks.setEntry(alias, KeyStore.SecretKeyEntry(key), prot)
}
С помощью KeyCharacteristics можно определить, в каком окружении хранится ключ: программном KeyStore, TEE или StrongBox. Метод getKeyCharacteristics() возвращает набор флагов: FLAG_HARDWARE (keymaster), FLAG_SECURE_ELEMENT (StrongBox), FLAG_TRUSTED_USER_PRESENCE_REQUIRED (биометрия).
Android Keystore поддерживает широкий набор криптографических алгоритмов, разделённый на три категории: асимметричные, симметричные и MAC. Поддержка конкретных алгоритмов зависит от версии Keymaster HAL.
RSA (1024–4096 бит) — для подписи (PKCS1, PSS) и шифрования (OAEP, PKCS1). EC (P-224, P-256, P-384, P-521) — для ECDSA-подписи и ECDH-согласования. AES (128, 256 бит) — для симметричного шифрования в режимах CBC, CTR, GCM. HMAC (SHA1, SHA256, SHA512) — для аутентификации сообщений.
Для каждого ключа задаётся setPurposes, ограничивающий возможные операции. Ключ RSA с PURPOSE_SIGN не может быть использован для шифрования, даже если у злоумышленника есть доступ к API. Это key usage enforcement на аппаратном уровне.
Keymaster включает счётчик неудачных попыток биометрической аутентификации. После заданного числа неудач (настраивается через setInvalidatedByBiometricEnrollment) ключ становится недоступным и требуется удаление/перегенерация. При удалении всех биометрических шаблонов все ключи с userAuthenticationRequired=true автоматически инвалидируются.
Также поддерживается Key Attestation (Android 8.1+): по запросу приложения Keymaster подписывает сертификат с информацией о характеристиках ключа (аппаратный/программный, алгоритм, purges). Сервер может проверить этот сертификат для подтверждения того, что ключ создан в надёжном окружении.
Часто задаваемые вопросы
Java KeyStore хранит ключи в файле, защищённом паролем (JKS, BKS). Android Keystore использует аппаратную изоляцию TEE или Secure Element. Java KeyStore уязвим при root-доступе, Android Keystore — нет, так как закрытые ключи никогда не покидают защищённую среду.
Да, через KeyStore.setEntry с KeyProtection. Однако импортированный ключ не будет иметь аппаратной защиты — он будет храниться в программном Keystore, зашифрованном мастер-ключом. Для максимальной безопасности всегда генерируйте ключи внутри Keystore.
Используйте KeyChain.isBoundKeyAlgorithm или проверьте KeyCharacteristics после генерации ключа. Наличие FLAG_HARDWARE в характеристиках означает, что ключ создан в TEE. Также можно проверить android.security.keystore.isHardwareBacked().
При удалении приложения Android удаляет все его ключи из Keystore. Данные необратимо теряются. При повторной установке приложение должно сгенерировать новые ключи. Резервное копирование ключей через TEE невозможно по архитектурным причинам.
На заблокированном устройстве Keymaster не выполняет никаких операций. Ключи с userAuthenticationRequired=true требуют биометрического подтверждения каждый раз. Даже с root-доступом злоумышленник не может вызвать Keymaster напрямую — только через Android Keystore service.
Итоги
Мы разработаем мобильное приложение под ключ
IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.
Читайте также