Криптография

В этом документе описывается правильный способ использования криптографических средств Android и приведены некоторые примеры их использования. Если вашему приложению требуется повышенная безопасность ключей, используйте систему Android Keystore .

Укажите провайдера только с системой Android Keystore

Если вы используете систему Android Keystore, вам необходимо указать поставщика.

Однако в других ситуациях Android не гарантирует конкретного поставщика для данного алгоритма. Указание поставщика без использования системы Android Keystore может вызвать проблемы совместимости в будущих выпусках.

Выберите рекомендуемый алгоритм

Если у вас есть свобода выбора, какой алгоритм использовать (например, когда вам не требуется совместимость со сторонней системой), мы рекомендуем использовать следующие алгоритмы:

Сорт Рекомендация
Шифр AES в режиме CBC или GCM с 256-битными ключами (например, AES/GCM/NoPadding ).
Дайджест сообщений Семейство SHA-2 (например, SHA-256 )
Мак Семейство HMAC SHA-2 (например, HMACSHA256 )
Подпись Семейство SHA-2 с ECDSA (например, SHA256withECDSA )

Выполнение общих криптографических операций

Следующие разделы содержат фрагменты, демонстрирующие, как можно выполнять распространенные криптографические операции в вашем приложении.

Зашифровать сообщение

Котлин

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

Ява

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();

Создать дайджест сообщения

Котлин

val message: ByteArray = ...
val md = MessageDigest.getInstance("SHA-256")
val digest: ByteArray = md.digest(message)

Ява

byte[] message = ...;
MessageDigest md = MessageDigest.getInstance("SHA-256");
byte[] digest = md.digest(message);

Создать цифровую подпись

Вам необходим объект PrivateKey , содержащий ключ подписи, который вы можете сгенерировать во время выполнения, прочитать из файла, связанного с вашим приложением, или получить из другого источника в зависимости от ваших потребностей.

Котлин

val message: ByteArray = ...
val key: PrivateKey = ...
val s = Signature.getInstance("SHA256withECDSA")
        .apply {
            initSign(key)
            update(message)
        }
val signature: ByteArray = s.sign()

Ява

byte[] message = ...;
PrivateKey key = ...;
Signature s = Signature.getInstance("SHA256withECDSA");
s.initSign(key);
s.update(message);
byte[] signature = s.sign();

Проверьте цифровую подпись

Вам необходим объект PublicKey , содержащий открытый ключ подписывающего лица, который вы можете прочитать из файла, прилагаемого к вашему приложению, извлечь из сертификата или получить из другого источника в зависимости от ваших потребностей.

Котлин

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)

Ява

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 для дайджеста MGF1 используется SHA-1, тогда как для других поставщиков криптографии Android эти два дайджеста одинаковы.

Чтобы иметь больший контроль над дайджестами, которые использует ваше приложение, запросите шифр с помощью OAEPPadding, как в Cipher.getInstance("RSA/ECB/OAEPPadding") , и предоставьте OAEPParameterSpec для init() , чтобы явно выбрать оба дайджеста. Это показано в следующем коде:

Котлин

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))
        }

Ява

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, как показано в следующем примере:

Котлин

Cipher.getInstance("AES/CBC/PKCS7PADDING", "BC")
// OR
Cipher.getInstance("AES/CBC/PKCS7PADDING", Security.getProvider("BC"))

Ява

Cipher.getInstance("AES/CBC/PKCS7PADDING", "BC");
// OR
Cipher.getInstance("AES/CBC/PKCS7PADDING", Security.getProvider("BC"));

Как отмечалось в разделе об указании поставщика только в системе Android Keystore , запрашивать конкретного поставщика не рекомендуется. Если вы будете следовать этому правилу, это прекращение поддержки вас не затронет.

Шифры шифрования на основе паролей без вектора инициализации

Шифры с шифрованием на основе пароля (PBE), которым требуется вектор инициализации (IV), могут получить его из ключа, если он сконструирован соответствующим образом, или из явно переданного IV. Если вы передаете ключ PBE, который не содержит IV и не передает явный IV, шифры PBE на Android в настоящее время предполагают, что IV равен нулю.

При использовании шифров PBE всегда передавайте явный IV, как показано в следующем фрагменте кода:

Котлин

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))

Ява

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), поставщик криптографической архитектуры Java (JCA) был удален. Если ваше приложение запрашивает экземпляр поставщика Crypto, например, вызывая следующий метод, возникает исключение NoSuchProviderException .

Котлин

SecureRandom.getInstance("SHA1PRNG", "Crypto")

Ява

SecureRandom.getInstance("SHA1PRNG", "Crypto");

Криптобиблиотека безопасности Jetpack

Криптобиблиотека безопасности Jetpack устарела. Это касается только тех случаев, когда в файле build.gradle вашего модуля приложения есть следующие зависимости:

классный

dependencies {
    implementation "androidx.security:security-crypto:1.0.0"
}

Котлин

dependencies {
    implementation("androidx.security:security-crypto:1.0.0")
}

Поддерживаемые алгоритмы

Это идентификаторы алгоритма JCA, которые поддерживаются на Android: