System Keystore Androida umożliwia przechowywanie kluczy kryptograficznych w kontenerze, co utrudnia ich wyodrębnienie z urządzenia. Gdy klucze znajdują się w magazynie kluczy, można ich używać do operacji kryptograficznych, a materiały klucza nie można wyeksportować. System magazynu kluczy umożliwia też ograniczenie sposobu i czasu korzystania z kluczy, na przykład wymaganie uwierzytelniania użytkownika przed użyciem klucza lub ograniczenie korzystania z kluczy tylko do określonych trybów szyfrowania. Więcej informacji znajdziesz w sekcji Funkcje zabezpieczeń.
System Keystore jest używany przez interfejs API KeyChain
, wprowadzony w Androidzie 4.0 (poziom interfejsu API 14), oraz przez usługę dostawcy Keystore na Androidzie, wprowadzoną w Androidzie 4.3 (poziom interfejsu API 18). Z tego dokumentu dowiesz się, kiedy i jak używać systemu Keystore na Androida.
Funkcje zabezpieczeń
System magazynu kluczy Androida chroni materiał klucza przed nieautoryzowanym użyciem na 2 sposoby. Po pierwsze, zmniejsza ryzyko nieautoryzowanego użycia kluczy z zewnątrz urządzenia z Androidem, zapobiegając wyodrębnianiu kluczy z procesów aplikacji i z urządzenia z Androidem jako całości. Po drugie, system magazynu kluczy zmniejsza ryzyko nieautoryzowanego użycia kluczy na urządzeniu z Androidem, ponieważ aplikacje określają autoryzowane użycia kluczy, a następnie nakładają te ograniczenia poza procesami aplikacji.
Zapobieganie wyodrębnianiu
Materiał klucza w przypadku kluczy w archiwum kluczy Androida jest chroniony przed wyodrębnieniem za pomocą 2 środków bezpieczeństwa:
- Materiał klucza nigdy nie jest uwzględniany w procesie zgłaszania. Gdy aplikacja wykonuje operacje kryptograficzne przy użyciu klucza Keystore na Androidzie, szyfrowany tekst, tekst jawny i wiadomości do podpisania lub weryfikacji są przekazywane do procesu systemowego, który wykonuje operacje kryptograficzne. Jeśli proces aplikacji zostanie naruszony, atakujący może być w stanie używać kluczy aplikacji, ale nie będzie mógł wyodrębnić kluczy (np. do użycia poza urządzeniem z Androidem).
- Materiał klucza może być powiązany z bezpiecznym sprzętem urządzenia z Androidem, takim jak zaufane środowisko wykonawcze (TEE) lub element zabezpieczeń (SE). Gdy ta funkcja jest włączona dla klucza, jego materiał klucza nigdy nie jest odsłaniany poza bezpiecznym sprzętem. Jeśli system operacyjny Android został zhakowany lub atakujący może odczytać pamięć wewnętrzną urządzenia, może on używać na urządzeniu z Androidem kluczy Keystore dowolnej aplikacji, ale nie może ich wyodrębnić z urządzenia. Ta funkcja jest włączona tylko wtedy, gdy bezpieczny sprzęt urządzenia obsługuje określoną kombinację algorytmu klucza, trybów blokowania, schematów wypełniania i sum kontrolnych, z którymi można używać klucza.
Aby sprawdzić, czy funkcja jest włączona w przypadku danego klucza, uzyskaj dla niego
KeyInfo
. Kolejny krok zależy od docelowej wersji pakietu SDK aplikacji:- Jeśli Twoja aplikacja jest kierowana na Androida 10 (poziom interfejsu API 29) lub nowszego, sprawdź zwracaną wartość funkcji
getSecurityLevel()
. Zwracane wartości odpowiadająceKeyProperties.SecurityLevelEnum.TRUSTED_ENVIRONMENT
lubKeyProperties.SecurityLevelEnum.STRONGBOX
wskazują, że klucz znajduje się na bezpiecznym sprzęcie. - Jeśli Twoja aplikacja jest kierowana na Androida 9 (poziom interfejsu API 28) lub niższego, sprawdź zwracaną wartość logiczną funkcji
KeyInfo.isInsideSecurityHardware()
.
- Jeśli Twoja aplikacja jest kierowana na Androida 10 (poziom interfejsu API 29) lub nowszego, sprawdź zwracaną wartość funkcji
Moduł zabezpieczeń sprzętowych
Obsługiwane urządzenia z Androidem 9 (poziom interfejsu API 28) lub nowszym mogą mieć StrongBox Keymaster, czyli implementację Keymastera lub Keymint HAL, która znajduje się w elementach zabezpieczeń w ramach modułu zabezpieczeń sprzętowych. Moduł zabezpieczeń sprzętowych może odnosić się do wielu różnych implementacji przechowywania kluczy, w których nie można ich ujawnić przez kompromis w jądrze Linuksa, np. TEE. StrongBox odnosi się do urządzeń takich jak wbudowane elementy zabezpieczeń (eSE) lub jednostki przetwarzania zabezpieczeń (iSE) na procesorze SoC.
Moduł ten zawiera:
- własny procesor;
- Bezpieczne miejsce na dane
- Prawdziwy generator liczb losowych
- dodatkowe mechanizmy zapobiegające modyfikowaniu pakietów i nieautoryzowanemu instalowaniu aplikacji;
- Bezpieczny minutnik
- Pin do uruchamiania ponownego (lub jego odpowiednik), np. pin do wejść/wyjść ogólnego przeznaczenia (GPIO).
Aby obsługiwać implementacje StrongBox o niskiej mocy, udostępniono podzbiór algorytmów i rozmiarów kluczy:
- RSA 2048
- AES 128 i 256
- ECDSA, ECDH P-256
- HMAC-SHA256 (obsługuje rozmiary kluczy od 8 do 64 bajtów)
- 3DES
- APDU o rozszerzonej długości
- Atestacja klucza
- Pomoc dotycząca poprawki H w przypadku przejścia na wyższą wersję
Podczas generowania lub importowania kluczy za pomocą klasy KeyStore
wskazujesz preferencje dotyczące przechowywania klucza w StrongBox Keymaster, przekazując wartość true
do metody setIsStrongBoxBacked()
.
Chociaż StrongBox jest nieco wolniejszy i ma ograniczone zasoby (co oznacza, że obsługuje mniej równoczesnych operacji) w porównaniu z TEE, zapewnia lepsze zabezpieczenia przed atakami fizycznymi i z kanału bocznego. Jeśli chcesz dać pierwszeństwo wyższym gwarancjom bezpieczeństwa nad wydajnością zasobów aplikacji, zalecamy korzystanie z StrongBox na urządzeniach, na których jest ona dostępna. Jeśli StrongBox jest niedostępny, aplikacja może zawsze skorzystać z TEE do przechowywania kluczowych materiałów.
Autoryzacje użycia klucza
Aby zapobiec nieautoryzowanemu używaniu kluczy na urządzeniu z Androidem, Android Keystore umożliwia aplikacjom określanie autoryzowanych zastosowań kluczy podczas ich generowania lub importowania. Po wygenerowaniu lub zaimportowaniu klucza nie można zmienić jego autoryzacji. Autoryzacje są następnie wymuszane przez Keystore Androida za każdym razem, gdy klucz jest używany. Jest to zaawansowana funkcja zabezpieczeń, która jest przydatna tylko wtedy, gdy Twoje wymagania są takie, że po wygenerowaniu lub imporcie klucza (ale nie przed lub w trakcie generowania) nie dochodzi do nieautoryzowanego użycia klucza.
Obsługiwane uprawnienia do korzystania z klucza można podzielić na te kategorie:
- Szyfrowanie: klucz może być używany tylko z autoryzowanymi algorytmami klucza, operacjami lub celami (szyfrowanie, odszyfrowywanie, podpisywanie, weryfikowanie), schematami wypełniania, trybami blokowania lub ciągami kontrolnymi.
- Czasowy zakres ważności: klucz może być używany tylko w określonym przedziale czasu.
- Uwierzytelnianie użytkownika: klucza można używać tylko wtedy, gdy użytkownik został uwierzytelniony w wystarczająco długim czasie. Zobacz artykuł Wymaganie uwierzytelniania użytkowników przy użyciu klucza.
Jako dodatkowy środek bezpieczeństwa w przypadku kluczy, których materiały klucza znajdują się na bezpiecznym sprzęcie (patrz KeyInfo.isInsideSecurityHardware()
lub, w przypadku aplikacji kierowanych na Androida 10 (poziom interfejsu API 29) lub nowszego, KeyInfo.getSecurityLevel()
), niektóre autoryzacje użycia klucza mogą być wymuszane przez bezpieczny sprzęt, w zależności od urządzenia z Androidem.
Bezpieczny sprzęt zwykle wymusza uwierzytelnianie i autoryzację użytkownika za pomocą kryptografii. Jednak bezpieczny sprzęt zazwyczaj nie egzekwuje autoryzacji interwałów ważności w czasie, ponieważ zwykle nie ma niezależnego, bezpiecznego zegara w czasie rzeczywistym.
Za pomocą KeyInfo.isUserAuthenticationRequirementEnforcedBySecureHardware()
możesz sprawdzić, czy autoryzacja użytkownika klucza jest wymuszana przez bezpieczny sprzęt.
Wybierz, czy chcesz używać katalogu kluczy czy dostawcy klucza Android Keystore
Jeśli chcesz uzyskać dane logowania dla całego systemu, użyj interfejsu API KeyChain
. Gdy aplikacja prosi o używanie danych logowania za pomocą interfejsu API KeyChain
, użytkownicy mogą wybrać w interfejsie użytkownika, do których zainstalowanych danych logowania aplikacja ma mieć dostęp. Dzięki temu kilka aplikacji może używać tego samego zestawu danych logowania za zgodą użytkownika.
Użyj dostawcy Keystore na Androida, aby umożliwić poszczególnej aplikacji przechowywanie własnych danych logowania, do których dostęp ma tylko ta aplikacja.
Dzięki temu aplikacje mogą zarządzać danymi logowania, których mogą używać tylko one, a jednocześnie zapewniać takie same zabezpieczenia, jakie interfejs API KeyChain
zapewnia dla danych logowania w całym systemie.
Ta metoda nie wymaga od użytkownika wyboru danych logowania.
Korzystanie z dostawcy Keystore na Androida
Aby korzystać z tej funkcji, musisz użyć standardowych klas KeyStore
i KeyPairGenerator
lub KeyGenerator
oraz dostawcy AndroidKeyStore
wprowadzonego w Androidzie 4.3 (poziom interfejsu API 18).
AndroidKeyStore
jest zarejestrowany jako typ KeyStore
do użycia z metodą KeyStore.getInstance(type)
oraz jako dostawca do użycia z metodami KeyPairGenerator.getInstance(algorithm, provider)
i KeyGenerator.getInstance(algorithm, provider)
.
Operacje kryptograficzne mogą być czasochłonne, dlatego aplikacje powinny unikać używania interfejsu AndroidKeyStore
w głównym wątku, aby zapewnić płynność działania interfejsu. (Aplikacja StrictMode
może pomóc znaleźć miejsca, w których tak nie jest).
Wygeneruj nowy klucz prywatny lub tajny
Aby wygenerować nowy certyfikat KeyPair
zawierający certyfikat PrivateKey
, musisz podać początkowe atrybuty X.509 certyfikatu. Możesz użyć opcji KeyStore.setKeyEntry()
, aby później zastąpić certyfikat certyfikatem podpisanym przez urząd certyfikacji (CA).
Aby wygenerować parę kluczy, użyj KeyPairGenerator
z KeyGenParameterSpec
:
Kotlin
/* * Generate a new EC key pair entry in the Android Keystore by * using the KeyPairGenerator API. The private key can only be * used for signing or verification and only with SHA-256 or * SHA-512 as the message digest. */ val kpg: KeyPairGenerator = KeyPairGenerator.getInstance( KeyProperties.KEY_ALGORITHM_EC, "AndroidKeyStore" ) val parameterSpec: KeyGenParameterSpec = KeyGenParameterSpec.Builder( alias, KeyProperties.PURPOSE_SIGN or KeyProperties.PURPOSE_VERIFY ).run { setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512) build() } kpg.initialize(parameterSpec) val kp = kpg.generateKeyPair()
Java
/* * Generate a new EC key pair entry in the Android Keystore by * using the KeyPairGenerator API. The private key can only be * used for signing or verification and only with SHA-256 or * SHA-512 as the message digest. */ KeyPairGenerator kpg = KeyPairGenerator.getInstance( KeyProperties.KEY_ALGORITHM_EC, "AndroidKeyStore"); kpg.initialize(new KeyGenParameterSpec.Builder( alias, KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY) .setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512) .build()); KeyPair kp = kpg.generateKeyPair();
Importowanie zaszyfrowanych kluczy do bezpiecznego sprzętu
Android 9 (poziom interfejsu API 28) i nowsze umożliwia bezpieczne importowanie zaszyfrowanych kluczy do magazynu kluczy za pomocą formatu klucza kodowanego ASN.1. Następnie Keymaster odszyfrowuje klucze w magazynie kluczy, dzięki czemu ich zawartość nigdy nie pojawia się w postaci zwykłego tekstu w pamięci hosta urządzenia. Ten proces zapewnia dodatkowe zabezpieczenie klucza odszyfrowywania.
Aby umożliwić bezpieczne importowanie zaszyfrowanych kluczy do magazynu kluczy, wykonaj te czynności:
Wygeneruj parę kluczy, która używa celu
PURPOSE_WRAP_KEY
. Zalecamy też dodanie do tej pary kluczy oświadczenia.Na serwerze lub komputerze, któremu ufasz, wygeneruj wiadomość ASN.1 dla
SecureKeyWrapper
.Opakowanie zawiera ten schemat:
KeyDescription ::= SEQUENCE { keyFormat INTEGER, authorizationList AuthorizationList } SecureKeyWrapper ::= SEQUENCE { wrapperFormatVersion INTEGER, encryptedTransportKey OCTET_STRING, initializationVector OCTET_STRING, keyDescription KeyDescription, secureKey OCTET_STRING, tag OCTET_STRING }
Utwórz obiekt
WrappedKeyEntry
, przekazując wiadomość ASN.1 jako tablicę bajtów.Przekaż obiekt
WrappedKeyEntry
do przeciążenia funkcjisetEntry()
, która przyjmuje obiektKeystore.Entry
.
Praca z rekordami w magazynie kluczy
Dostęp do dostawcy AndroidKeyStore
można uzyskać za pomocą wszystkich standardowych interfejsów API KeyStore
.
Wyświetlanie listy wpisów
Wyświetl listę wpisów w magazynie kluczy, wywołując metodę aliases()
:
Kotlin
/* * Load the Android KeyStore instance using the * AndroidKeyStore provider to list the currently stored entries. */ val ks: KeyStore = KeyStore.getInstance("AndroidKeyStore").apply { load(null) } val aliases: Enumeration<String> = ks.aliases()
Java
/* * Load the Android KeyStore instance using the * AndroidKeyStore provider to list the currently stored entries. */ KeyStore ks = KeyStore.getInstance("AndroidKeyStore"); ks.load(null); Enumeration<String> aliases = ks.aliases();
Podpisywanie i weryfikowanie danych
Podpisz dane, pobierając klucz KeyStore.Entry
ze sklepu z kluczami i korzystając z interfejsów API Signature
, takich jak sign()
:
Kotlin
/* * Use a PrivateKey in the KeyStore to create a signature over * some data. */ val ks: KeyStore = KeyStore.getInstance("AndroidKeyStore").apply { load(null) } val entry: KeyStore.Entry = ks.getEntry(alias, null) if (entry !is KeyStore.PrivateKeyEntry) { Log.w(TAG, "Not an instance of a PrivateKeyEntry") return null } val signature: ByteArray = Signature.getInstance("SHA256withECDSA").run { initSign(entry.privateKey) update(data) sign() }
Java
/* * Use a PrivateKey in the KeyStore to create a signature over * some data. */ KeyStore ks = KeyStore.getInstance("AndroidKeyStore"); ks.load(null); KeyStore.Entry entry = ks.getEntry(alias, null); if (!(entry instanceof PrivateKeyEntry)) { Log.w(TAG, "Not an instance of a PrivateKeyEntry"); return null; } Signature s = Signature.getInstance("SHA256withECDSA"); s.initSign(((PrivateKeyEntry) entry).getPrivateKey()); s.update(data); byte[] signature = s.sign();
Podobnie zweryfikuj dane za pomocą metody verify(byte[])
:
Kotlin
/* * Verify a signature previously made by a private key in the * KeyStore. This uses the X.509 certificate attached to the * private key in the KeyStore to validate a previously * generated signature. */ val ks = KeyStore.getInstance("AndroidKeyStore").apply { load(null) } val entry = ks.getEntry(alias, null) as? KeyStore.PrivateKeyEntry if (entry == null) { Log.w(TAG, "Not an instance of a PrivateKeyEntry") return false } val valid: Boolean = Signature.getInstance("SHA256withECDSA").run { initVerify(entry.certificate) update(data) verify(signature) }
Java
/* * Verify a signature previously made by a private key in the * KeyStore. This uses the X.509 certificate attached to the * private key in the KeyStore to validate a previously * generated signature. */ KeyStore ks = KeyStore.getInstance("AndroidKeyStore"); ks.load(null); KeyStore.Entry entry = ks.getEntry(alias, null); if (!(entry instanceof PrivateKeyEntry)) { Log.w(TAG, "Not an instance of a PrivateKeyEntry"); return false; } Signature s = Signature.getInstance("SHA256withECDSA"); s.initVerify(((PrivateKeyEntry) entry).getCertificate()); s.update(data); boolean valid = s.verify(signature);
Wymaganie uwierzytelniania użytkownika w przypadku użycia klucza
Podczas generowania lub importowania klucza do usługi AndroidKeyStore
możesz określić, że klucz może być używany tylko wtedy, gdy użytkownik został uwierzytelniony. Użytkownik jest uwierzytelniony za pomocą podzbioru danych logowania do ekranu blokady (wzór, kod PIN, hasło, dane biometryczne).
Jest to zaawansowana funkcja zabezpieczeń, która jest przydatna tylko wtedy, gdy wymagasz, aby po wygenerowaniu lub zaimportowaniu klucza (ale nie przed lub w trakcie generowania lub importowania) aplikacja nie mogła ominąć wymagań dotyczących uwierzytelniania użytkownika.
Jeśli klucz może być używany tylko wtedy, gdy użytkownik został uwierzytelniony, możesz wywołać funkcję setUserAuthenticationParameters()
, aby skonfigurować klucz do działania w jednym z tych trybów:
- Autoryzowanie na określony czas
- Wszystkie klucze są autoryzowane do użycia, gdy tylko użytkownik uwierzytelni się za pomocą jednego z określonych zestawów danych logowania.
- autoryzowanie na czas trwania określonej operacji kryptograficznej.
Każda operacja z użyciem określonego klucza musi być autoryzowana przez użytkownika.
Aplikacja rozpoczyna ten proces, wywołując funkcję
authenticate()
w instancjiBiometricPrompt
.
W przypadku każdego utworzonego klucza możesz wybrać obsługę silnych danych logowania biometrycznych, danych logowania na ekranie blokady lub obu tych typów danych. Aby sprawdzić, czy użytkownik skonfigurował poświadczenia, na których opiera się klucz aplikacji, wywołaj funkcję canAuthenticate()
.
Jeśli klucz obsługuje tylko poświadczenia biometryczne, jest domyślnie unieważniany, gdy dodawane są nowe rejestracje biometryczne. Możesz skonfigurować klucz tak, aby pozostał ważny po dodaniu nowych rejestracji biometrycznych. Aby to zrobić, prześlij wartość false
do funkcji setInvalidatedByBiometricEnrollment()
.
Dowiedz się więcej o dodawaniu do aplikacji funkcji uwierzytelniania biometrycznego, w tym o wyświetlaniu okna uwierzytelniania biometrycznego.
Obsługiwane algorytmy
Cipher
KeyGenerator
KeyFactory
KeyStore
(obsługuje te same typy kluczy coKeyGenerator
iKeyPairGenerator
)KeyPairGenerator
Mac
Signature
SecretKeyFactory
Artykuły w blogu
Przeczytaj wpis na blogu Ujednolicenie dostępu do KeyStore w ICS.