KeyStore (Android) — to implementacja dostawcy kryptograficznego Java Cryptography Architecture (JCA) zintegrowana z Androidem do bezpiecznego przechowywania kluczy z możliwością izolacji sprzętowej. Od Androida 4.3 (API 18) KeyStore obsługuje klucze sprzętowe przez Keymaster HAL, a od Androida 9 (API 28) — StrongBox Keymaster dla kluczy w wydzielonym Secure Element. Według Android Security Documentation, dostawca „AndroidKeyStore” zastępuje standardowe Bouncy Castle lub OpenSSL KeyStore, zapewniając systemową ochronę przed nieautoryzowanym wydobyciem kluczy.
Najważniejsze
KeyStore w Androidzie — to nie osobna aplikacja ani plik, lecz dostawca kryptograficzny implementujący interfejs java.security.KeyStore. Zapewnia on jednolity interfejs API do przechowywania i używania kluczy prywatnych, kluczy symetrycznych i certyfikatów zaufanych centrów (CA). Dostawca jest rejestrowany pod nazwą „AndroidKeyStore” i dostępny przez standardowe KeyStore.getInstance().
Przed Androidem 4.3 operacje kryptograficzne były wykonywane programowo przez Bouncy Castle. Od Androida 4.3 pojawił się Keymaster HAL 1.0, umożliwiający korzystanie z TEE na ARM TrustZone. Android 6.0 (API 23) dodał Keymaster 2.0 z obsługą sprzętowej autoryzacji za pomocą odcisku palca. Android 9 (API 28) wprowadził Keymaster 4.0 i StrongBox Keymaster dla wydzielonego Secure Element.
Każda wersja Keymaster dodaje nowe możliwości i poprawia izolację kluczy. Nowoczesne urządzenia (2022+) muszą obsługiwać Keymaster 4.0 do certyfikacji Google Mobile Services, co gwarantuje obecność TEE dla wszystkich aplikacji Android.
Android KeyStore składa się z trzech poziomów: Java API (KeyStore, KeyPairGenerator), systemowy proces keystore (C++, działa jako system service) i Keymaster HAL (biblioteka w TEE lub Secure Element). Aplikacja wywołuje API, usługa keystore kieruje żądanie do Keymaster, a operacja jest wykonywana w chronionym środowisku.
Wszystkie prywatne klucze są przechowywane w TEE i nie mogą być odczytane z przestrzeni użytkownika. Nawet systemowa usługa keystore nie ma dostępu do surowych kluczy — tylko do uchwytów wskazujących na klucze wewnątrz Keymaster.
Android KeyStore implementuje standardowy interfejs dostawcy usług JCA. Gdy aplikacja wywołuje Cipher.getInstance(„RSA/ECB/PKCS1Padding”, „AndroidKeyStore”), Android Security Provider deleguje operację do Keymaster przez łańcuch: Java → JNI → keystore service → Keymaster HAL.
Dostawca AndroidKeyStore jest rejestrowany automatycznie przy uruchomieniu procesu. Jego priorytet jest wyższy niż Bouncy Castle czy Conscrypt. Dlatego przy wywołaniu KeyStore.getInstance() bez określania dostawcy w większości przypadków zwracany jest AndroidKeyStore. Do jawnego wywołania użyj KeyStore.getInstance(„AndroidKeyStore”).
Każda aplikacja Android ma izolowany kontener w KeyStore. Aplikacje z tym samym UID (shared userId) mogą mieć wspólny dostęp do określonych kluczy, ale standardowa konfiguracja gwarantuje, że aplikacja A nie może odczytać kluczy aplikacji B.
load(null) — inicjalizacja KeyStore. Parametr zawsze null dla AndroidKeyStore. setEntry — zapis klucza z określeniem KeyProtection (purposes, digest, padding). getEntry — pobranie KeyStore.PrivateKeyEntry, SecretKeyEntry lub TrustedCertificateEntry. containsAlias — sprawdzenie istnienia klucza. deleteEntry — usunięcie klucza (nieodwracalnie).
import java.security.KeyStore
import java.security.KeyPairGenerator
import android.security.keystore.KeyGenParameterSpec
import android.security.keystore.KeyProperties
object KeyStoreManager {
private val keyStore by lazy {
KeyStore.getInstance("AndroidKeyStore").apply { load(null) }
}
fun createRsaKey(alias: String) {
val spec = KeyGenParameterSpec.Builder(alias,
KeyProperties.PURPOSE_SIGN or KeyProperties.PURPOSE_VERIFY
).setKeySize(2048)
.setDigests(KeyProperties.DIGEST_SHA256)
.setSignaturePaddings(KeyProperties.SIGN_PADDING_RSA_PKCS1)
.build()
val kpg = KeyPairGenerator.getInstance(
KeyProperties.KEY_ALGORITHM_RSA,
"AndroidKeyStore"
)
kpg.initialize(spec)
kpg.generateKeyPair()
}
}
Android KeyStore obsługuje szeroki zestaw algorytmów kryptograficznych, różniący się w zależności od wersji Keymaster HAL na urządzeniu. Deweloper może uzyskać listę obsługiwanych algorytmów przez KeyGenParameterSpec.Builder przy próbie generacji — niezgodne parametry powodują InvalidAlgorithmParameterException.
RSA (1024–4096 bitów) — do podpisu (PKCS1, PSS z SHA-1/SHA-256/SHA-384/SHA-512) i szyfrowania (OAEP z SHA-1/SHA-256). EC (P-224, P-256, P-384, P-521) — do podpisu ECDSA i uzgadniania kluczy ECDH. X25519 i Ed25519 — od Androida 12 (API 31) do nowoczesnych protokołów kryptograficznych.
Dla kluczy asymetrycznych Zawsze generuj wewnątrz Keymaster, NIGDY nie importuj kluczy prywatnych. Zaimportowane klucze prywatne nie są chronione sprzętowo — są przechowywane w warstwie programowej i podatne na ataki przy kompromitacji AP.
AES (128, 256 bitów) — do szyfrowania symetrycznego w trybach CBC, CTR, GCM. HMAC (SHA-1, SHA-256, SHA-512) — do uwierzytelniania wiadomości. ChaCha20 (Android 12+) — do wydajnego szyfrowania strumieniowego z uwierzytelnianiem Poly1305.
| Algorytm | Keymaster | Przeznaczenie | API |
|---|---|---|---|
| RSA | KM 1.0+ | Podpis, szyfrowanie | 18+ |
| EC | KM 1.0+ | ECDSA, ECDH | 18+ |
| AES | KM 2.0+ | Szyfrowanie symetryczne | 23+ |
| HMAC | KM 2.0+ | Kod uwierzytelniania | 23+ |
| ChaCha20 | KM 3.0+ | Szyfrowanie strumieniowe | 31+ |
| X25519/E25519 | KM 3.0+ | Wymiana kluczy | 31+ |
KeyStore.PrivateKeyEntry — zawiera klucz prywatny (nieeksportowalny) i łańcuch certyfikatów. KeyStore.SecretKeyEntry — dla kluczy symetrycznych. KeyStore.TrustedCertificateEntry — dla zaufanych certyfikatów CA. Klucze publiczne są dostępne do eksportu przez keyStore.getCertificate(alias).publicKey.
Rozpatrzmy pełny scenariusz: generacja klucza AES do szyfrowania danych i generacja klucza EC do podpisu z ochroną biometryczną. Oba klucze są tworzone wewnątrz Android KeyStore z obsługą sprzętową.
Klucz AES jest tworzony przez KeyGenerator z KeyGenParameterSpec. Parametry: PURPOSE_ENCRYPT + PURPOSE_DECRYPT, BLOCK_MODE_GCM (zalecany tryb z uwierzytelnianiem), ENCRYPTION_PADDING_NONE (dla GCM padding nie jest potrzebny).
import javax.crypto.KeyGenerator
import javax.crypto.Cipher
import javax.crypto.spec.GCMParameterSpec
fun generateAndEncrypt(alias: String, plainText: ByteArray): ByteArray {
val spec = KeyGenParameterSpec.Builder(alias,
KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT
).setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.setKeySize(256)
.build()
val kg = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore")
kg.initialize(spec)
kg.generateKey()
val cipher = Cipher.getInstance("AES/GCM/NoPadding")
cipher.init(Cipher.ENCRYPT_MODE, getKeyFromStore(alias))
return cipher.doFinal(plainText)
}
Klucz EC z userAuthenticationRequired=true wymaga uwierzytelnienia użytkownika przed każdą operacją podpisu. W tym celu używa się BiometricPrompt z CryptoObject zawierającym obiekt Signature. Po pomyślnej biometrii Keymaster zezwala na operację.
fun createBiometricSignKey(alias: String) {
val spec = KeyGenParameterSpec.Builder(alias,
KeyProperties.PURPOSE_SIGN
).setAlgorithmParameterSpec(
ECGenParameterSpec("secp256r1")
).setDigests(KeyProperties.DIGEST_SHA256)
.setUserAuthenticationRequired(true)
.setInvalidatedByBiometricEnrollment(true)
.build()
val kpg = KeyPairGenerator.getInstance(
KeyProperties.KEY_ALGORITHM_EC,
"AndroidKeyStore"
)
kpg.initialize(spec)
kpg.generateKeyPair()
}
Android KeyStore zapewnia sprzętowe gwarancje bezpieczeństwa, których programowe KeyStore (JKS, BKS) nie mogą zapewnić. Klucze są chronione na poziomie SoC, a nawet pełna kontrola nad przestrzenią użytkownika Androida nie pozwala na wydobycie klucza prywatnego.
Key Attestation — mechanizm umożliwiający aplikacji (i serwerowi) sprawdzenie, w jakim środowisku został utworzony klucz. Android Keystore podpisuje certyfikat zawierający listę charakterystyk klucza: algorytm, rozmiar, purges, hardware-backed (True/False), origin (GENERATED, IMPORTED). Serwer weryfikuje łańcuch certyfikatów aż do certyfikatu głównego Google.
Jest to krytyczne dla aplikacji finansowych: serwer może wymagać, aby klucz został utworzony w środowisku sprzętowym (Hardware-Backed = True), i odrzucać klucze utworzone w programowym Keystore. Key Attestation zapobiega atakom, w których osoba atakująca podstawia Keystore na emulator.
setInvalidatedByBiometricEnrollment(true) oznacza, że klucz zostanie automatycznie usunięty przez Keymaster przy zmianie lub usunięciu szablonów biometrycznych użytkownika. Jest to ochrona przed atakami, w których osoba atakująca dodaje swój odcisk palca do istniejącego konta. Po dodaniu nowego odcisku stare klucze stają się niedostępne.
Licznik nieudanych prób uwierzytelnienia biometrycznego jest również zarządzany przez Keymaster. Po maxBiometricAttempt (ustawiana przez producenta, zwykle 5) Keymaster blokuje wszystkie operacje z kluczami biometrycznymi na 30 sekund. Po 10 nieudanych próbach — do czasu wprowadzenia hasła urządzenia (sekretny PIN).
Często zadawane pytania
Bouncy Castle (BKS) — programowy KeyStore przechowujący klucze w pliku chronionym hasłem. Android KeyStore używa sprzętowej izolacji TEE/StrongBox. Klucze BKS można wydobyć przy dostępie root, klucze Android KeyStore — nie. BKS nadaje się do certyfikatów CA, Android KeyStore — do kluczy prywatnych.
Tak, jeśli przy generacji określisz PURPOSE_ENCRYPT or PURPOSE_DECRYPT or PURPOSE_SIGN or PURPOSE_VERIFY. Jednak najlepszą praktyką jest tworzenie oddzielnych kluczy dla różnych operacji. Ogranicza to szkody w przypadku kompromitacji jednego z kluczy i jest zgodne z zasadą najmniejszych uprawnień.
Użyj KeyStore.getKeyCharacteristics(alias), dostępnego przez android.security.keystore. Metoda zwraca zestaw flag: FLAG_HARDWARE — klucz w TEE, FLAG_SECURE_ELEMENT — klucz w StrongBox. Jeśli brak flag — klucz jest programowy.
Wszystkie klucze utworzone z setInvalidatedByBiometricEnrollment(true) zostaną automatycznie unieważnione przez Keymaster. Przy próbie użycia aplikacja otrzyma KeyPermanentlyInvalidatedException. Dane zaszyfrowane tymi kluczami zostaną bezpowrotnie utracone.
Klucze sprzętowe (w TEE/StrongBox) nie obsługują kopii zapasowych — są powiązane z konkretnym urządzeniem. Klucze programowe mogą być uwzględnione w kopii zapasowej Google Drive. Do przenoszenia danych między urządzeniami szyfruj dane na serwerze i odszyfrowuj na nowym urządzeniu.
Podsumowanie
Opracujemy aplikację mobilną pod klucz
IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.
Przeczytaj również