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:
AlgorithmParameterGeneratorAlgorithmParametersCertPathBuilderCertPathValidatorCertStoreCertificateFactoryCipherKeyAgreementKeyFactoryKeyGeneratorKeyManagerFactoryKeyPairGeneratorKeyStoreMacMessageDigestSSLContextSSLEngine.SupportedSSLSocket.SupportedSecretKeyFactorySecureRandomSignatureTrustManagerFactory