Crittografia

Questo documento descrive il modo corretto di utilizzare le strutture di crittografia di Android e include alcuni esempi di utilizzo. Se la tua app richiede maggiore sicurezza delle chiavi, utilizza il sistema Android Keystore.

Specifica un provider solo con il sistema Android Keystore

Se utilizzi il sistema Android Keystore, devi specificare un provider.

In altre situazioni, tuttavia, Android non garantisce un determinato provider per un determinato algoritmo. La specifica di un provider senza utilizzare il sistema Android Keystore può causare problemi di compatibilità nelle release future.

Scegliere un algoritmo consigliato

Quando hai la libertà di scegliere quale algoritmo utilizzare (ad esempio quando non richiedi la compatibilità con un sistema di terze parti), ti consigliamo di utilizzare i seguenti algoritmi:

Classe Consiglio
Cipher AES in modalità CBC o GCM con chiavi a 256 bit (ad esempio AES/GCM/NoPadding)
MessageDigest Famiglia SHA-2 (ad esempio SHA-256)
Mac HMAC della famiglia SHA-2 (ad esempio HMACSHA256)
Firma Famiglia SHA-2 con ECDSA (ad esempio SHA256withECDSA)

Eseguire operazioni crittografiche comuni

Le seguenti sezioni includono snippet che mostrano come completare operazioni di crittografia comuni nella tua app.

Criptare un messaggio

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

Generare un riepilogo dei messaggi

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

Generare una firma digitale

Devi avere un oggetto PrivateKey contenente la chiave di firma, che puoi generare in fase di esecuzione, leggere da un file incluso nella tua app o ottenere da un'altra fonte, a seconda delle tue esigenze.

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

Verificare una firma digitale

Devi disporre di un oggetto PublicKey contenente la chiave pubblica del firmatario, che puoi leggere da un file in bundle con la tua app, estrarre da un certificato o ottenere da un'altra fonte a seconda delle tue esigenze.

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

Complessità di implementazione

Esistono alcuni dettagli dell'implementazione della crittografia Android che sembrano insoliti, ma sono presenti a causa di problemi di compatibilità. In questa sezione vengono descritti quelli che incontrerai molto probabilmente.

Digest del messaggio MGF1 OAEP

Le cifre RSA OAEP sono parametrizzate da due diversi digest dei messaggi: il digest "principale" e il digest MGF1. Esistono identificatori Cipher che includono nomi di digest, come Cipher.getInstance("RSA/ECB/OAEPwithSHA-256andMGF1Padding"), che specifica il digest principale e lascia indefinito il digest MGF1. Per Android Keystore, viene utilizzato SHA-1 per il digest MGF1, mentre per altri fornitori di crittografia Android i due digest sono uguali.

Per avere un maggiore controllo sui digest utilizzati dalla tua app, richiedi un'operazione di crittografia con OAEPPadding, come in Cipher.getInstance("RSA/ECB/OAEPPadding"), e fornisci un valore OAEPParameterSpec a init() per scegliere esplicitamente entrambi i digest. Ciò viene mostrato nel codice che segue:

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

Funzionalità ritirate

Le sezioni seguenti descrivono le funzionalità ritirate. Non utilizzarlo nella tua app.

Algoritmi di Bouncy Castle

Le implementazioni di Bouncy Castle di molti algoritmi sono ritirate. Questo riguarda solo i casi in cui richiedi esplicitamente il provider Castello gonfiabile, come mostrato nell'esempio seguente:

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

Come indicato nella sezione relativa a specificare un provider solo con il sistema Android Keystore, sconsigliamo di richiedere un provider specifico. Se segui queste linee guida, il ritiro non ti interessa.

Cifrari di crittografia basati su password senza un vettore di inizializzazione

Gli algoritmi di crittografia con password (PBE) che richiedono un vettore di inizializzazione (IV) possono ottenerlo dalla chiave, se è opportunamente costruita, o da un IV passato esplicitamente. Se passi una chiave PBE che non contiene un IV e non passi un IV esplicito, al momento le crittografie PBE su Android presuppongono un IV pari a zero.

Quando utilizzi i cifrari PBE, passa sempre un IV esplicito, come mostrato nel seguente s snippet di codice:

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

Fornitore di criptovalute

A partire da Android 9 (livello API 28), il provider JCA (Crypto Java Cryptography Architecture) è stato rimosso. Se la tua app richiede un'istanza del fornitore di crittografia, ad esempio chiamando il seguente metodo, si verifica un NoSuchProviderException.

Kotlin

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

Java

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

Libreria di crittografia per la sicurezza di Jetpack

La libreria di crittografia di Jetpack Security è deprecata. Questo riguarda solo i casi in cui hai le seguenti dipendenze nel file build.gradle del modulo dell'app:

Alla moda

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

Kotlin

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

Algoritmi supportati

Questi sono gli identificatori degli algoritmi JCA supportati su Android: