Android Keystore — це системний механізм Android для безпечного зберігання криптографічних ключів в апаратній ізоляції. Система використовує Trusted Execution Environment (TEE) на пристроях з ARM TrustZone або виділений Secure Element для захисту ключів на рівні чипа. Згідно з Android Open Source Project, Keystore підтримує алгоритми RSA, EC, AES та HMAC з генерацією ключів безпосередньо в захищеному середовищі.
Головне
Android Keystore — це криптографічний провайдер, реалізований в 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), що задаються при генерації. Їх не можна змінити згодом, що запобігає неправильному використанню ключа.
При створенні нового ключа додаток викликає 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 підписує сертифікат з інформацією про характеристики ключа (апаратний/програмний, алгоритм, призначення). Сервер може перевірити цей сертифікат для підтвердження того, що ключ створений в надійному середовищі.
Часті запитання
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 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.
Читайте також