В этом документе описывается, как правильно использовать криптографические средства Android, и приводятся примеры их использования. Если вашему приложению требуется более высокий уровень безопасности ключей, используйте систему Android Keystore.
Указывайте поставщика только в системе Android Keystore
Если вы используете систему Android Keystore, то должны указать поставщика.
В других ситуациях Android не гарантирует, что для определенного алгоритма будет использоваться конкретный поставщик. Если вы укажете поставщика, не используя систему Android Keystore, в будущих выпусках могут возникнуть проблемы с совместимостью.
Как выбрать рекомендуемый алгоритм
Если вы можете выбрать алгоритм (например, когда вам не требуется совместимость со сторонней системой), рекомендуем использовать следующие алгоритмы:
| Класс | Рекомендация |
|---|---|
| Шифр | AES в режиме CBC или GCM с 256-битными ключами (например, AES/GCM/NoPadding); |
| MessageDigest | Семейство SHA-2 (например, SHA-256) |
| Mac | HMAC семейства SHA-2 (например, HMACSHA256) |
| Подпись | Семейство SHA-2 с ECDSA (например, SHA256withECDSA) |
Выполнение стандартных криптографических операций.
В следующих разделах приведены фрагменты кода, демонстрирующие, как выполнять распространенные криптографические операции в приложении.
Как зашифровать сообщение
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();
Как создать сводку сообщений
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);
Как создать цифровую подпись
Вам понадобится объект PrivateKey, содержащий ключ подписи. Его можно сгенерировать во время выполнения, прочитать из файла, связанного с приложением, или получить из другого источника в зависимости от ваших потребностей.
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();
Как проверить цифровую подпись
Вам понадобится объект PublicKey, содержащий открытый ключ подписавшего. Его можно считать из файла, входящего в состав приложения, извлечь из сертификата или получить из другого источника.
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);
Сложности реализации
Некоторые детали реализации криптографии в Android могут показаться необычными, но они необходимы для обеспечения совместимости. В этом разделе рассматриваются те из них, с которыми вы, скорее всего, столкнетесь.
Сводка сообщения OAEP MGF1
Шифры RSA OAEP параметризуются двумя разными дайджестами сообщений: основным дайджестом и дайджестом MGF1. Существует Cipher идентификаторов, которые включают названия дайджестов, например Cipher.getInstance("RSA/ECB/OAEPwithSHA-256andMGF1Padding"), который указывает основной дайджест и оставляет дайджест MGF1 неопределенным. Для Android Keystore SHA-1 используется для дайджеста MGF1, а для других поставщиков криптографии Android два дайджеста совпадают.
Чтобы лучше контролировать дайджесты, которые использует ваше приложение, запросите шифр с OAEPPadding, как в Cipher.getInstance("RSA/ECB/OAEPPadding"), и укажите OAEPParameterSpec для init(), чтобы явно выбрать оба дайджеста.
Это показано в коде ниже:
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));
Устаревшие функции
В разделах ниже описаны функции, поддержка которых прекращена. Не используйте его в своем приложении.
Алгоритмы Bouncy Castle
Реализации многих алгоритмов в Bouncy Castle устарели. Это касается только случаев, когда вы явно запрашиваете поставщика Bouncy Castle, как показано в примере ниже.
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"));
Как отмечалось в разделе Указание поставщика только с помощью системы Android Keystore, запрашивать определенного поставщика не рекомендуется. Если вы следуете этой рекомендации, то прекращение поддержки вас не затронет.
Шифры на основе паролей без вектора инициализации
Шифры на основе пароля, для которых требуется вектор инициализации, могут получить его из ключа, если он правильно создан, или из явно переданного вектора инициализации. Если вы передаете ключ PBE, который не содержит IV, и не передаете явный IV, шифры PBE на Android в настоящее время предполагают, что IV равен нулю.
При использовании шифров PBE всегда передавайте явный вектор инициализации, как показано в следующем фрагменте кода:
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));
Поставщик криптографических услуг
Начиная с Android 9 (уровень API 28) поставщик Crypto JCA (Java Cryptography Architecture) больше не поддерживается. Если приложение запрашивает экземпляр поставщика Crypto, например вызывает метод, указанный ниже, возникает ошибка NoSuchProviderException.
Kotlin
SecureRandom.getInstance("SHA1PRNG", "Crypto")
Java
SecureRandom.getInstance("SHA1PRNG", "Crypto");
Библиотека Jetpack Security-Crypto
Поддержка всех API в библиотеке Jetpack security-crypto прекращена в стабильном выпуске версии 1.1.0.
Новые версии этой библиотеки больше не выпускаются.
Аннотации об устаревании видны, если в файле build.gradle модуля вашего приложения есть любая из следующих зависимостей:
Яркий
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") }
Поддерживаемые алгоритмы
Ниже перечислены идентификаторы алгоритмов JCA, которые поддерживаются на устройствах Android.
AlgorithmParameterGeneratorAlgorithmParametersCertPathBuilderCertPathValidatorCertStoreCertificateFactoryCipherKeyAgreementKeyFactoryKeyGeneratorKeyManagerFactoryKeyPairGeneratorKeyStoreMacMessageDigestSSLContextSSLEngine.SupportedSSLSocket.SupportedSecretKeyFactorySecureRandomSignatureTrustManagerFactory