W tym dokumencie opisujemy prawidłowy sposób korzystania z funkcji kryptograficznych Androida i podajemy przykłady ich użycia. Jeśli Twoja aplikacja wymaga większego bezpieczeństwa kluczy, użyj systemu Android Keystore.
Określanie dostawcy tylko w systemie Android Keystore
Jeśli używasz systemu Android Keystore, musisz podać dostawcę.
W innych sytuacjach Android nie gwarantuje jednak konkretnego dostawcy dla danego algorytmu. Określenie dostawcy bez użycia systemu Android Keystore może powodować problemy z kompatybilnością w przyszłych wersjach.
Wybierz rekomendowany algorytm
Jeśli możesz wybrać algorytm (np. gdy nie musisz zachowywać zgodności z systemem innej firmy), zalecamy używanie tych algorytmów:
Kategoria | Rekomendacja |
---|---|
Cipher | AES w trybie CBC lub GCM z 256-bitowymi kluczami (np. AES/GCM/NoPadding ) |
MessageDigest | Rodzina SHA-2 (np. SHA-256 ) |
Mac | HMAC z rodziny SHA-2 (np. HMACSHA256 ) |
Podpis | Rodzina SHA-2 z ECDSA (np. SHA256withECDSA ) |
wykonywać typowe operacje kryptograficzne,
W sekcjach poniżej znajdziesz fragmenty kodu, które pokazują, jak wykonywać w aplikacji typowe operacje kryptograficzne.
Szyfrowanie wiadomości
Kotlin
val plaintext: ByteArray = ... val keygen = KeyGenerator.getInstance("AES") keygen.init(256) val key: SecretKey = keygen.generateKey() val cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING") cipher.init(Cipher.ENCRYPT_MODE, key) val ciphertext: ByteArray = cipher.doFinal(plaintext) val iv: ByteArray = cipher.iv
Java
byte[] plaintext = ...; KeyGenerator keygen = KeyGenerator.getInstance("AES"); keygen.init(256); SecretKey key = keygen.generateKey(); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING"); cipher.init(Cipher.ENCRYPT_MODE, key); byte[] ciphertext = cipher.doFinal(plaintext); byte[] iv = cipher.getIV();
Generowanie skrótu wiadomości
Kotlin
val message: ByteArray = ... val md = MessageDigest.getInstance("SHA-256") val digest: ByteArray = md.digest(message)
Java
byte[] message = ...; MessageDigest md = MessageDigest.getInstance("SHA-256"); byte[] digest = md.digest(message);
Generowanie podpisu cyfrowego
Musisz mieć obiekt PrivateKey
zawierający klucz podpisywania, który możesz wygenerować w czasie działania programu, odczytać z pliku dołączonego do aplikacji lub uzyskać z innego źródła w zależności od potrzeb.
Kotlin
val message: ByteArray = ... val key: PrivateKey = ... val s = Signature.getInstance("SHA256withECDSA") .apply { initSign(key) update(message) } val signature: ByteArray = s.sign()
Java
byte[] message = ...; PrivateKey key = ...; Signature s = Signature.getInstance("SHA256withECDSA"); s.initSign(key); s.update(message); byte[] signature = s.sign();
Weryfikowanie podpisu cyfrowego
Musisz mieć PublicKey
obiekt zawierający klucz publiczny sygnatariusza, który możesz odczytać z pliku dołączonego do aplikacji, wyodrębnić z certyfikatu lub uzyskać z innego źródła w zależności od potrzeb.
Kotlin
val message: ByteArray = ... val signature: ByteArray = ... val key: PublicKey = ... val s = Signature.getInstance("SHA256withECDSA") .apply { initVerify(key) update(message) } val valid: Boolean = s.verify(signature)
Java
byte[] message = ...; byte[] signature = ...; PublicKey key = ...; Signature s = Signature.getInstance("SHA256withECDSA"); s.initVerify(key); s.update(message); boolean valid = s.verify(signature);
Złożoność implementacji
Niektóre szczegóły implementacji kryptografii na Androidzie wydają się nietypowe, ale są obecne ze względu na kwestie zgodności. W tej sekcji omawiamy te, które najprawdopodobniej napotkasz.
Skrót wiadomości OAEP MGF1
Szyfry RSA OAEP są parametryzowane przez 2 różne skróty wiadomości: „główny” skrót i skrót MGF1. Istnieją identyfikatory Cipher
zawierające nazwy skrótów, np. Cipher.getInstance("RSA/ECB/OAEPwithSHA-256andMGF1Padding")
, które określają główny skrót i pozostawiają skrót MGF1 nieokreślony. W przypadku Keystore na Androida do wygenerowania skrótu MGF1 używany jest algorytm SHA-1, a w przypadku innych dostawców usług kryptograficznych na Androida oba skróty są takie same.
Aby mieć większą kontrolę nad skrótami używanymi przez aplikację, poproś o szyfr z OAEPPadding, jak w Cipher.getInstance("RSA/ECB/OAEPPadding")
, i podaj OAEPParameterSpec
do init()
, aby wyraźnie wybrać oba skróty.
Pokazuje to poniższy kod:
Kotlin
val key: Key = ... val cipher = Cipher.getInstance("RSA/ECB/OAEPPadding") .apply { // To use SHA-256 the main digest and SHA-1 as the MGF1 digest init(Cipher.ENCRYPT_MODE, key, OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA1, PSource.PSpecified.DEFAULT)) // To use SHA-256 for both digests init(Cipher.ENCRYPT_MODE, key, OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA256, PSource.PSpecified.DEFAULT)) }
Java
Key key = ...; Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPPadding"); // To use SHA-256 the main digest and SHA-1 as the MGF1 digest cipher.init(Cipher.ENCRYPT_MODE, key, new OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA1, PSource.PSpecified.DEFAULT)); // To use SHA-256 for both digests cipher.init(Cipher.ENCRYPT_MODE, key, new OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA256, PSource.PSpecified.DEFAULT));
Wycofana funkcja
W sekcjach poniżej opisujemy wycofane funkcje. Nie używaj go w aplikacji.
Algorytmy Bouncy Castle
Implementacje wielu algorytmów w Bouncy Castle są wycofane. Dotyczy to tylko przypadków, w których wyraźnie zażądasz dostawcy Bouncy Castle, jak pokazano w tym przykładzie:
Kotlin
Cipher.getInstance("AES/CBC/PKCS7PADDING", "BC") // OR Cipher.getInstance("AES/CBC/PKCS7PADDING", Security.getProvider("BC"))
Java
Cipher.getInstance("AES/CBC/PKCS7PADDING", "BC"); // OR Cipher.getInstance("AES/CBC/PKCS7PADDING", Security.getProvider("BC"));
Jak wspomnieliśmy w sekcji określanie dostawcy tylko za pomocą systemu Android Keystore, odradzamy wysyłanie żądań do konkretnego dostawcy. Jeśli postępujesz zgodnie z tymi wytycznymi, to wycofanie nie będzie Cię dotyczyć.
Szyfry oparte na hasłach bez wektora inicjującego
Szyfry oparte na hasłach (PBE), które wymagają wektora inicjującego (IV), mogą go uzyskać z klucza, jeśli jest on odpowiednio skonstruowany, lub z jawnie przekazanego wektora IV. Jeśli przekażesz klucz PBE, który nie zawiera wektora inicjującego, i nie przekażesz jawnego wektora inicjującego, szyfry PBE na Androidzie będą obecnie zakładać wektor inicjujący o wartości zero.
Podczas korzystania z szyfrów PBE zawsze przekazuj jawny wektor inicjujący, jak pokazano w tym fragmencie kodu:
Kotlin
val key: SecretKey = ... val cipher = Cipher.getInstance("PBEWITHSHA256AND256BITAES-CBC-BC") val iv = ByteArray(16) SecureRandom().nextBytes(iv) cipher.init(Cipher.ENCRYPT_MODE, key, IvParameterSpec(iv))
Java
SecretKey key = ...; Cipher cipher = Cipher.getInstance("PBEWITHSHA256AND256BITAES-CBC-BC"); byte[] iv = new byte[16]; new SecureRandom().nextBytes(iv); cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(iv));
Dostawca kryptowalut
Od Androida 9 (poziom interfejsu API 28) dostawca Crypto Java Cryptography Architecture (JCA) został usunięty. Jeśli aplikacja zażąda instancji dostawcy Crypto, np. wywołując tę metodę, wystąpi błąd NoSuchProviderException
.
Kotlin
SecureRandom.getInstance("SHA1PRNG", "Crypto")
Java
SecureRandom.getInstance("SHA1PRNG", "Crypto");
Biblioteka Jetpack security-crypto
Wszystkie interfejsy API w bibliotece security-crypto
Jetpack zostały wycofane w stabilnej wersji 1.1.0
.
Nie będzie kolejnych wersji tej biblioteki.
Adnotacje o wycofaniu są widoczne, jeśli w pliku build.gradle
modułu aplikacji masz te zależności:
Groovy
dependencies { implementation "androidx.security:security-crypto:1.1.0" // or implementation "androidx.security:security-crypto-ktx:1.1.0" }
Kotlin
dependencies { implementation("androidx.security:security-crypto:1.1.0") // or implementation("androidx.security:security-crypto-ktx:1.1.0") }
Obsługiwane algorytmy
Oto identyfikatory algorytmów JCA obsługiwane na Androidzie:
AlgorithmParameterGenerator
AlgorithmParameters
CertPathBuilder
CertPathValidator
CertStore
CertificateFactory
Cipher
KeyAgreement
KeyFactory
KeyGenerator
KeyManagerFactory
KeyPairGenerator
KeyStore
Mac
MessageDigest
SSLContext
SSLEngine.Supported
SSLSocket.Supported
SecretKeyFactory
SecureRandom
Signature
TrustManagerFactory