Dokument ten opisuje prawidłowe korzystanie z funkcji kryptograficznych Androida i zawiera przykłady ich użycia. Jeśli Twoja aplikacja wymaga większego zabezpieczenia klucza, użyj systemu Keystore na Androidzie.
Określ dostawcę tylko w systemie Keystore Androida
Jeśli używasz systemu Keystore Androida, musisz określić dostawcę.
Jednak w innych przypadkach Android nie gwarantuje konkretnego dostawcy w przypadku danego algorytmu. Określanie dostawcy bez używania systemu Android Keystore może spowodować problemy ze zgodnością w przyszłych wersjach.
Wybieranie zalecanego algorytmu
Jeśli masz swobodę wyboru algorytmu do użycia (np. gdy nie potrzebujesz zgodności z systemem innej firmy), zalecamy użycie tych algorytmów:
Kategoria | Rekomendacja |
---|---|
Mechanizm szyfrowania | AES w trybie CBC lub GCM z 256-bitowymi kluczami (np. AES/GCM/NoPadding ) |
MessageDigest | Rodzina SHA-2 (np. SHA-256 ) |
Mac | Kod HMAC rodziny SHA-2 (np. HMACSHA256 ) |
Podpis | Rodzina SHA-2 z ECDSA (np. SHA256withECDSA ) |
wykonywanie typowych operacji kryptograficznych;
W następnych sekcjach 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 wykonywania, 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ć obiekt PublicKey
zawierający klucz publiczny podpisującego, 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 problemy ze zgodnością. W tej sekcji omawiamy te, które najprawdopodobniej napotkasz.
OAEP MGF1 message digest
Szyfry RSA OAEP są parametryzowane za pomocą 2 różnych ciągów haszowanych wiadomości: „głównego” ciągu haszowanych wiadomości i ciągu MGF1. Istnieją Cipher
identyfikatory, które zawierają nazwy digest, np. Cipher.getInstance("RSA/ECB/OAEPwithSHA-256andMGF1Padding")
, które określają główny digest, a nieokreślony pozostaje digest MGF1. W przypadku Keystore na Androida do skrótu MGF1 używany jest algorytm SHA-1, natomiast w przypadku innych dostawców usług kryptograficznych na Androida oba skróty są takie same.
Aby mieć większą kontrolę nad skrótami, których używa Twoja aplikacja, poproś o szyfrowanie z dodatkiem OAEPPadding, jak w Cipher.getInstance("RSA/ECB/OAEPPadding")
, i przekaż parametr 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 poniższych sekcjach opisujemy wycofane funkcje. Nie używaj go w swojej aplikacji.
Algorytmy Bouncy Castle
Zastosowanie w algorytmach typu „Bouncy Castle” jest wycofane. Dotyczy to tylko przypadków, gdy wyraźnie poprosisz o użycie dostawcy Bouncy Castle, jak 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 o określaniu dostawcy tylko za pomocą systemu Android Keystore, odradzamy używanie konkretnego dostawcy. Jeśli przestrzegasz tych wytycznych, wycofanie tych zasad Cię nie dotyczy.
Szyfry szyfrowania opartego na hasłach bez wektora inicjującego
Szyfry szyfrowania opartego na hasłach (PBE), które wymagają wektora inicjującego (IV), mogą uzyskać go z klucza, jeśli jest on odpowiednio skonstruowany, lub z jawnie przekazanego wektora inicjującego. Jeśli przekażesz klucz PBE, który nie zawiera IV, i nie przekażesz wyraźnego IV, szyfr PBE na Androidzie przyjmie obecnie wartość zero.
Podczas korzystania z szyfrów PBE zawsze przekazuj jawny IV, 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
W Androidzie 9 (poziom interfejsu API 28) dostawca Crypto Java Cryptography Architecture (JCA) został usunięty. Jeśli Twoja aplikacja poprosi o instancję dostawcy usług kryptograficznych, na przykład przez wywołanie tej metody, nastąpi NoSuchProviderException
.
Kotlin
SecureRandom.getInstance("SHA1PRNG", "Crypto")
Java
SecureRandom.getInstance("SHA1PRNG", "Crypto");
Biblioteka kryptograficzna Jetpack
Biblioteka kryptograficzna Jetpack Security została wycofana. Dotyczy to tylko tych zależności, które występują w pliku build.gradle
modułu aplikacji:
Groovy
dependencies { implementation "androidx.security:security-crypto:1.0.0" }
Kotlin
dependencies { implementation("androidx.security:security-crypto:1.0.0") }
Obsługiwane algorytmy
Oto identyfikatory algorytmu 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