Android Keystore — dostawca kryptograficzny, który generuje i przechowuje klucze szyfrowania w izolowanym środowisku wykonawczym (TEE), niedostępnym nawet dla systemu operacyjnego. Według AOSP Security Documentation (2025), Keystore jest używany w ponad 80% aplikacji Android z top 100 Google Play do ochrony tokenów i szyfrowania danych. Zrozumienie Android Keystore jest krytycznie ważne dla bezpiecznego przechowywania kluczy na Androidzie.
Najważniejsze
Android Keystore — systemowy komponent platformy Android, który udostępnia API do generowania, przechowywania i używania kluczy kryptograficznych w chronionym środowisku. W przeciwieństwie do programowych bibliotek kryptograficznych (Bouncy Castle, Conscrypt), Keystore gwarantuje, że klucze prywatne nigdy nie opuszczają izolowanego obszaru wykonawczego.
Keystore pojawił się w Android 4.3 (API 18) jako programowy dostawca z obsługą RSA. Od Android 6.0 (API 23), Keystore otrzymał wsparcie sprzętowe poprzez Keymaster Hardware Abstraction Layer (HAL), który deleguje operacje kryptograficzne do Trusted Execution Environment (TEE) na kompatybilnych urządzeniach. Według Android Compatibility Definition Document (2025), wszystkie urządzenia z Android 9+ są zobowiązane do obsługi sprzętowego Keystore przez TEE lub StrongBox.
Klucze w Keystore są identyfikowane przez alias — ciąg znaków przekazywany podczas tworzenia lub ładowania klucza. Keystore nie pozwala na uzyskanie surowego materiału klucza: metody getEncoded() zwracają null dla kluczy utworzonych w Keystore. To fundamentalna różnica w porównaniu z programowymi kluczami — atakujący nie może wyodrębnić klucza prywatnego nawet przy pełnej kontroli nad urządzeniem.
Keystore jest zintegrowany z innymi mechanizmami bezpieczeństwa Android: uwierzytelnianiem biometrycznym (BiometricPrompt), szyfrowaniem na poziomie plików (File-Based Encryption) oraz funkcjami weryfikacji SafetyNet / Play Integrity. Klucze mogą być skonfigurowane do automatycznego usuwania w określonych warunkach: po usunięciu kodu PIN, po dodaniu nowego odcisku palca lub po wygaśnięciu okresu ważności.
Architektura Android Keystore obejmuje trzy poziomy implementacji różniące się stopniem ochrony sprzętowej. Poziom zależy od możliwości sprzętowych urządzenia.
TEE (Trusted Execution Environment) — izolowany obszar działający równolegle z głównym systemem operacyjnym na tym samym procesorze. TEE wykorzystuje technologię ARM TrustZone, która dzieli fizyczne rdzenie procesora na dwa wirtualne: Normal World (Android) i Secure World (TEE). Kod w Secure World ma dostęp do pamięci i urządzeń peryferyjnych niedostępnych z Normal World.
Gdy aplikacja wywołuje operację kryptograficzną przez Keystore, żądanie jest przekazywane przez Keymaster HAL do TEE, gdzie operacja jest wykonywana sprzętowo. Wynik wraca do aplikacji, ale klucz prywatny pozostaje w chronionej pamięci TEE. TEE jest certyfikowany na zgodność z GlobalPlatform TEE Protection Profile i jest obowiązkowym wymaganiem dla Android 9+ na urządzeniach z procesorami obsługującymi TrustZone.
TEE obsługuje algorytmy AES/GCM (128, 256 bitów), RSA (2048, 4096 bitów), EC (P-256, P-384, P-521) i HMAC-SHA256. Wydajność TEE jest niższa niż programowej kryptografii (o 20–40%), ale dla typowych operacji (podpis JWT, deszyfrowanie klucza sesji) opóźnienie nie przekracza 10–50 ms.
StrongBox — dedykowany układ bezpieczeństwa, fizycznie oddzielony od głównego procesora. W przeciwieństwie do TEE, który współdzieli czas procesora z Androidem, StrongBox ma własny CPU, pamięć operacyjną, True Random Number Generator (TRNG) i chronione przechowywanie (One-Time Programmable memory). StrongBox jest certyfikowany na Common Criteria EAL 4+ i Secure IC Protection Profile.
StrongBox jest dostępny na urządzeniach z Android 9+ pod warunkiem obecności odpowiedniego układu (np. Titan M w Google Pixel, Knox w Samsung Galaxy). Deweloper włącza StrongBox przez flagę setIsStrongBoxBacked(true) w KeyGenParameterSpec. W przypadku braku wsparcia sprzętowego flaga jest ignorowana, a Keystore przełącza się na TEE.
Ograniczenia StrongBox: obsługuje ograniczony zestaw algorytmów (AES-256, EC P-256, HMAC-SHA256), kolejka operacji — nie więcej niż jedna jednocześnie, liczba operacji — ograniczona zasobami układu. StrongBox nie jest przeznaczony do scenariuszy o wysokim obciążeniu — używaj TEE dla częstych operacji, a StrongBox tylko dla kluczy krytycznych (klucze główne szyfrowania, klucze podpisu).
Software-based Keystore — implementacja programowa używana na urządzeniach bez wsparcia sprzętowego TEE lub StrongBox. Klucze są przechowywane w zaszyfrowanej postaci w systemie plików, ale klucz prywatny może być tymczasowo odszyfrowany w pamięci operacyjnej. Programowy Keystore jest mniej bezpieczny — atakujący z dostępem root może przechwycić klucz w pamięci.
Od Android 12 (API 31), Google wymaga sprzętowego wsparcia Keystore dla wszystkich nowych urządzeń. Urządzenia z Android 9–11 mogą mieć programowy Keystore w modelach budżetowych. Deweloper może sprawdzić poziom ochrony przez KeyStore.getKeyCharacteristics() — atrybut SECURITY_LEVEL_TRUSTED_ENVIRONMENT lub SECURITY_LEVEL_STRONGBOX potwierdza ochronę sprzętową.
Android Keystore obsługuje szeroki zestaw algorytmów kryptograficznych podzielonych na kategorie w zależności od typu klucza. Wybór algorytmu wpływa na wydajność, kompatybilność i poziom bezpieczeństwa.
AES (Advanced Encryption Standard) — szyfrowanie symetryczne do ochrony danych na urządzeniu. Zalecany tryb: AES/GCM/NoPadding (256 bitów). GCM zapewnia uwierzytelnione szyfrowanie (AEAD) — weryfikację integralności zaszyfrowanych danych. Rozmiar IV (Initialization Vector): 12 bajtów dla GCM. Nie używaj AES/ECB — nie zapewnia on odpowiedniej ochrony.
RSA (Rivest–Shamir–Adleman) — szyfrowanie asymetryczne do ochrony kluczy sesji i podpisu cyfrowego. Zalecany rozmiar: 2048 lub 4096 bitów. Tryby: RSA/ECB/PKCS1Padding (szyfrowanie) i RSA/ECB/PKCS1Sign (podpis). RSA 1024 jest uznawany za przestarzały i nie jest zalecany dla nowych aplikacji (NIST SP 800-131A Rev. 2).
EC (Elliptic Curve) — kryptografia asymetryczna na krzywych eliptycznych do podpisu i wymiany kluczy. Obsługiwane krzywe: secp256r1 (P-256, obowiązkowa), secp384r1 (P-384) i secp521r1 (P-521). EC zapewnia porównywalne z RSA bezpieczeństwo przy znacznie mniejszym rozmiarze klucza. P-256 jest zalecana dla większości scenariuszy: jest obsługiwana przez wszystkie urządzenia i zapewnia 128-bitowy poziom bezpieczeństwa.
HMAC (Hash-based Message Authentication Code) — symetryczne uwierzytelnianie wiadomości. Obsługiwane funkcje skrótu: SHA-256, SHA-384, SHA-512. HMAC jest używany do weryfikacji integralności i autentyczności danych, na przykład do weryfikacji żądań webhook lub sprawdzania integralności konfiguracji.
Wszystkie algorytmy mogą być powiązane z uwierzytelnianiem biometrycznym przez KeyGenParameterSpec.Builder.setUserAuthenticationRequired(true). Na Android 11+ dostępna jest flaga setUserAuthenticationParameters() z określeniem limitu czasu (w sekundach), w którym klucz jest dostępny po uwierzytelnieniu biometrycznym bez ponownego żądania.
Rozważmy praktyczne przykłady pracy z Android Keystore w Kotlin: generowanie klucza AES, szyfrowanie danych i tworzenie pary asymetrycznej do podpisu.
Przykład tworzy 256-bitowy klucz AES/GCM z powiązaniem do uwierzytelniania biometrycznego. Klucz nie jest dostępny do eksportu przez getEncoded().
import android.security.keystore.KeyGenParameterSpec
import android.security.keystore.KeyProperties
import java.security.KeyStore
private val keyStore = KeyStore.getInstance("AndroidKeyStore").apply { load(null) }
fun generateAesKey(alias: String) {
val spec = KeyGenParameterSpec.Builder(
alias,
KeyProperties.PURPOSE_ENCRYPT or
KeyProperties.PURPOSE_DECRYPT
)
.setKeySize(256)
.setBlockModes(KeyProperties.KEY_BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.setUserAuthenticationRequired(true)
.setInvalidatedByBiometricEnrollment(true)
.build()
val generator = KeyGenerator.getInstance(
KeyProperties.KEY_ALGORITHM_AES,
"AndroidKeyStore"
)
generator.init(spec)
generator.generateKey()
}
Przykład szyfruje dane przy użyciu klucza z Android Keystore. Cipher pobiera klucz po aliasie, inicjalizuje szyfrowanie AES/GCM i zwraca zaszyfrowane dane wraz z IV.
fun encryptData(alias: String, plaintext: ByteArray): ByteArray {
val cipher = Cipher.getInstance("AES/GCM/NoPadding")
val secretKey = keyStore.getKey(alias, null) as SecretKey
cipher.init(Cipher.ENCRYPT_MODE, secretKey)
val iv = cipher.getIV()
val encrypted = cipher.doFinal(plaintext)
// IV + dane zaszyfrowane
return iv + encrypted
}
fun decryptData(alias: String, ciphertextWithIv: ByteArray): ByteArray {
val iv = ciphertextWithIv.copyOfRange(0, 12)
val encrypted = ciphertextWithIv.copyOfRange(12, ciphertextWithIv.size)
val cipher = Cipher.getInstance("AES/GCM/NoPadding")
val secretKey = keyStore.getKey(alias, null) as SecretKey
val spec = GCMParameterSpec(128, iv)
cipher.init(Cipher.DECRYPT_MODE, secretKey, spec)
return cipher.doFinal(encrypted)
}
Przykład tworzy parę kluczy RSA-2048 w Keystore z powiązaniem do StrongBox. Klucz prywatny jest używany do podpisu, klucz publiczny może być eksportowany przez getEncoded().
fun generateRsaKeyPair(alias: String) {
val spec = KeyGenParameterSpec.Builder(
alias,
KeyProperties.PURPOSE_SIGN or
KeyProperties.PURPOSE_VERIFY
)
.setKeySize(2048)
.setSignaturePaddings(
KeyProperties.SIGNATURE_PADDING_RSA_PKCS1
)
.setDigests(KeyProperties.DIGEST_SHA256)
.setIsStrongBoxBacked(true)
.build()
val pair = KeyPairGenerator.getInstance(
KeyProperties.KEY_ALGORITHM_RSA,
"AndroidKeyStore"
).apply { init(spec) }
.generateKeyPair()
// Klucz publiczny można wyeksportować
val publicKey = pair.public // X509EncodedKeySpec
}
Efektywne używanie Android Keystore wymaga przestrzegania zasad, które zapewniają maksymalną ochronę przy zachowaniu wydajności.
Używaj KeyGenParameterSpec z minimalnie niezbędnymi parametrami: określaj tylko te purpose, block modes i paddings, które są rzeczywiście używane. Nadmiarowe parametry (np. PURPOSE_ENCRYPT dla klucza używanego tylko do podpisu) tworzą dodatkowe wektory ataku. Android zaleca jawne określanie digest dla podpisu — SHA256 to minimalny dopuszczalny poziom (SHA1 jest przestarzały).
Powiązuj klucze z biometrią dla operacji krytycznych: setUserAuthenticationRequired(true) gwarantuje, że klucz może być użyty tylko po uwierzytelnieniu biometrycznym. Na Android 11+ używaj setUserAuthenticationParameters() z limitem czasu (zalecane 30–60 sekund), aby nie wymagać biometrii przy każdej operacji w ramach jednej sesji. setInvalidatedByBiometricEnrollment(true) automatycznie usuwa klucz przy dodaniu nowego odcisku palca lub twarzy — zapobiega to dostępowi przy użyciu starych danych biometrycznych.
Sprawdzaj poziom bezpieczeństwa na etapie inicjalizacji: używaj KeyStore.getKeyCharacteristics() do określenia SECURITY_LEVEL. Jeśli urządzenie obsługuje tylko programowy Keystore (SECURITY_LEVEL_SOFTWARE), podejmij decyzję: albo zrezygnuj z funkcjonalności, albo użyj dodatkowego szyfrowania (np. owinięcie klucza hasłem użytkownika). Nie polegaj na StrongBox, jeśli nie jest gwarantowany — zawsze określaj flagę setIsStrongBoxBacked(true) i sprawdzaj wynik przez getKeyCharacteristics.
Aktualizuj klucze zgodnie z harmonogramem: klucze kryptograficzne mają zalecany okres życia. NIST SP 800-57 zaleca zmianę kluczy AES co 1–2 lata, par RSA/EC co 2–3 lata. Zaimplementuj mechanizm rotacji kluczy: przy uruchomieniu aplikacji sprawdzaj datę utworzenia klucza (KeyGenParameterSpec.Builder.setKeyValidityStart/End) i generuj nowy klucz po wygaśnięciu okresu. Stare dane zaszyfrowane starym kluczem powinny być odszyfrowane i ponownie zaszyfrowane nowym.
Nie używaj Keystore do dużych danych: Keystore jest przeznaczony do przechowywania kluczy (kilkaset bajtów), a nie do szyfrowania dużych plików. Do szyfrowania danych używaj schematu: wygeneruj losowy klucz AES (DEK — Data Encryption Key), zaszyfruj dane tym kluczem, a DEK zaszyfruj kluczem Keystore (KEK — Key Encryption Key). Android EncryptedSharedPreferences używa właśnie tego schematu: klucz główny w Keystore, dane — AES-256 GCM.
Często zadawane pytania
Nie, Android Keystore jest zaprojektowany tak, aby klucz prywatny nigdy nie opuszczał TEE ani StrongBox. Metoda getEncoded() zwraca null dla kluczy utworzonych w Keystore. Klucz można używać tylko przez Cipher, Signature lub Mac API — surowy materiał jest niedostępny.
TEE (TrustZone) — wirtualna izolacja na tym samym procesorze, wykorzystuje współdzielenie czasu. StrongBox — oddzielny układ z własnym CPU i pamięcią. StrongBox jest bezpieczniejszy (Common Criteria EAL 4+), ale wolniejszy i obsługuje mniej algorytmów. TEE nadaje się do częstych operacji, StrongBox — do kluczy krytycznych.
Użyj KeyStore.getKeyCharacteristics() po wygenerowaniu klucza z flagą setIsStrongBoxBacked(true). Atrybut SECURITY_LEVEL_STRONGBOX potwierdza wsparcie sprzętowe. Jeśli urządzenie nie obsługuje StrongBox, Keystore przełącza się na TEE bez błędu — należy jawnie sprawdzić poziom bezpieczeństwa.
Klucze w Keystore są automatycznie usuwane po usunięciu aplikacji z urządzenia. Na Android 10+ klucze mogą zostać zachowane, jeśli aplikacja ma flagę allowBackup=true w manifeście, ale będą niedostępne po ponownej instalacji. Zaleca się generowanie kluczy od nowa przy czystej instalacji.
Nie, Android Keystore jest powiązany ze sprzętem konkretnego urządzenia. Klucz wygenerowany w TEE jednego urządzenia nie może zostać przeniesiony na inne. Do szyfrowania międzyplatformowego używaj schematu: Keystore chroni klucz na urządzeniu, a klucze sesji są przesyłane przez zabezpieczone API z użyciem szyfrowania asymetrycznego.
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ż