Cryptographie

Ce document décrit la bonne utilisation des systèmes cryptographiques d'Android et inclut quelques exemples d'utilisation. Si votre application nécessite une sécurité renforcée des clés, utilisez le système Android Keystore.

Spécifier un fournisseur uniquement si vous utilisez le système Android Keystore

Si vous utilisez le système Android Keystore, vous devez spécifier un fournisseur.

Dans d'autres cas, Android ne garantit toutefois pas de fournisseur particulier pour un algorithme donné. Si vous spécifiez un fournisseur sans utiliser le système Android Keystore, vous risquez de rencontrer des problèmes de compatibilité dans les prochaines versions.

Choisir un algorithme recommandé

Lorsque vous êtes libre de choisir l'algorithme à utiliser (par exemple, lorsque vous n'avez pas à vous soucier de la compatibilité avec un système tiers), nous vous recommandons d'utiliser les algorithmes suivants :

Classe Recommandation
Algorithme de chiffrement AES en mode CBC ou GCM avec des clés 256 bits (comme AES/GCM/NoPadding)
Condensé du message Famille SHA-2 (SHA-256, par exemple)
Mac HMAC de la famille SHA-2 (HMACSHA256, par exemple)
Signature Famille SHA-2 avec ECDSA (SHA256withECDSA, par exemple)

Effectuer des opérations cryptographiques courantes

Les sections suivantes incluent des extraits qui montrent comment effectuer des opérations cryptographiques courantes dans votre application.

Chiffrer un message

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

Générer un condensé du message

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

Générer une signature numérique

Vous devez disposer d'un objet PrivateKey contenant la clé de signature, que vous pouvez générer lors de l'exécution, lire à partir d'un fichier fourni avec votre application ou obtenir auprès d'une autre source selon vos besoins.

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

Valider une signature numérique

Vous devez disposer d'un objet PublicKey contenant la clé publique du signataire, que vous pouvez lire à partir d'un fichier fourni avec votre application, extraire d'un certificat ou obtenir à partir d'une autre source en fonction de vos besoins.

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

Complexité de l'implémentation

Certains détails d'implémentation de la cryptographie Android, qui semblent inhabituels, sont dus à des problèmes de compatibilité. Cette section présente ceux que vous êtes le plus susceptible de rencontrer.

Condensé du message OAEP MGF1

Les algorithmes de chiffrement OAEP RSA sont paramétrés par deux condensés de messages différents : le condensé principal ("main") et le condensé MGF1. Il existe des identifiants Cipher qui incluent des noms de condensé, tels que Cipher.getInstance("RSA/ECB/OAEPwithSHA-256andMGF1Padding"), qui spécifie le condensé principal et ne spécifie pas le condensé MGF1. Pour Android Keystore, SHA-1 est utilisé pour le condensé MGF1, tandis que pour les autres fournisseurs de chiffrement Android, les deux condensés sont identiques.

Pour mieux contrôler les condensés utilisés par votre application, vous devez demander un chiffrement avec OAEPPadding, comme dans Cipher.getInstance("RSA/ECB/OAEPPadding"), et fournir un élément OAEPParameterSpec à init() pour choisir explicitement les deux condensés. Ce processus est illustré dans le code suivant :

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

Fonctionnalité obsolète

Les sections suivantes décrivent des fonctionnalités obsolètes. Ne les utilisez pas dans votre application.

Algorithmes "Bouncy Castle"

Les implémentations Bouncy Castle de nombreux algorithmes sont obsolètes. Cela ne concerne que les cas où vous demandez explicitement le fournisseur Bouncy Castle, comme illustré dans l'exemple suivant :

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

Comme indiqué dans la section expliquant comment spécifier un fournisseur uniquement avec le système Android Keystore, il est déconseillé de demander un fournisseur spécifique. Si vous suivez cette consigne, cette obsolescence ne vous concerne pas.

Chiffrement par mot de passe sans vecteur d'initialisation

Les algorithmes de chiffrement basés sur des mots de passe, également appelés algorithmes de chiffrement PBE (Password-Based Encryption), nécessitent un vecteur d'initialisation. Ils peuvent l'obtenir à partir de la clé, si sa construction est correcte, ou si le vecteur d'initialisation a été explicitement transmis. Lors du transfert d'une clé PBE qui ne contient pas de vecteur d'initialisation et qui n'implique aucun vecteur d'initialisation explicite, les algorithmes de chiffrement PBE sur Android supposent actuellement un vecteur d'initialisation de zéro.

Lorsque vous utilisez des algorithmes de chiffrement PBE, transmettez toujours un vecteur d'initialisation explicite, comme indiqué dans l'extrait de code suivant :

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

Fournisseur Crypto

À partir d'Android 9 (niveau d'API 28), le fournisseur JCA (Java Cryptography Architecture) Crypto a été supprimé. Si votre application demande une instance de ce fournisseur, par exemple en appelant la méthode suivante, une exception NoSuchProviderException se produit.

Kotlin

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

Java

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

Bibliothèque de chiffrement de sécurité Jetpack

La bibliothèque de chiffrement de sécurité Jetpack est obsolète. Cela ne concerne que les cas où vous avez les dépendances suivantes dans le fichier build.gradle de votre module d'application :

Groovy

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

Kotlin

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

Algorithmes compatibles

Voici les identifiants de l'algorithme JCA compatibles avec Android :