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). Приложението има достъп чрез стандартния Java Cryptography Architecture (JCA) API, а системната услуга насочва заявките към Keymaster, работещ в TEE.
Всички криптографски операции с ключове (подпис, дешифриране) се изпълняват вътре в TEE или Secure Element. Ключовете никога не напускат защитената среда — приложението получава само псевдоним (alias) за препраяне към ключа. Това е фундаментална разлика от софтуерните KeyStore, където ключовете потенциално са достъпни в паметта на процеса.
Стандартният JKS (Java KeyStore) или BKS (Bouncy Castle) съхраняват ключовете в паролно защитени файлове. Android Keystore съхранява ключовете в хардуерна изолация, където са защитени дори от root потребителя. JKS е уязвим при директен достъп до файловата система, Android Keystore — не.
Друга разлика: в Android Keystore ключовете имат строги параметри на използване (purpose — само sign/verify/encrypt/decrypt), зададени при генериране. Те не могат да бъдат променени по-късно, което предотвратява неправилната употреба на ключа.
При създаването на нов ключ, приложението извиква 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 за разграничаване на достъпа. Всяко приложение вижда само своите записи, освен ако се използва споделен 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. Това е принудително използване на ключа на хардуерно ниво.
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.
Резюме
Ще разработим мобилно приложение под ключ
IT Sectr създава iOS и Android приложения за стартъпи и бизнеси от 2017 г. Ще ви консултираме и ще предложим най-доброто решение.
Прочетете също