Questo documento descrive il modo corretto di utilizzare le funzionalità crittografiche di Android e include alcuni esempi del loro utilizzo. Se la tua app richiede una maggiore sicurezza delle chiavi, utilizza il sistema Android Keystore.
Specifica un fornitore solo con il sistema Android Keystore
Se utilizzi il sistema Android Keystore, devi specificare un provider.
In altre situazioni, invece, Android non garantisce un particolare fornitore per un determinato algoritmo. La specifica di un fornitore senza utilizzare il sistema Android Keystore può causare problemi di compatibilità nelle versioni future.
Scegliere un algoritmo consigliato
Quando hai la libertà di scegliere quale algoritmo utilizzare (ad esempio quando non hai bisogno della 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 sezioni seguenti 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 digest del messaggio
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 runtime, leggere da un file incluso nella tua app o ottenere da un'altra origine 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 avere un oggetto PublicKey
contenente la chiave pubblica del firmatario, che puoi leggere da un file incluso nella tua app, estrarre da un certificato o ottenere da un'altra origine 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
Alcuni dettagli dell'implementazione della crittografia Android sembrano insoliti, ma sono presenti a causa di problemi di compatibilità. Questa sezione descrive quelli che incontrerai più probabilmente.
Digest del messaggio MGF1 OAEP
Le cifrature RSA OAEP sono parametrizzate da due diversi digest dei messaggi: il digest "principale" e il digest MGF1. Esistono Cipher
identificatori che includono nomi
di digest, ad esempio Cipher.getInstance("RSA/ECB/OAEPwithSHA-256andMGF1Padding")
,
che specifica il digest principale e lascia il digest MGF1 non specificato. Per Android
Keystore, SHA-1 viene utilizzato per il digest MGF1, mentre per altri provider
crittografici Android, i due digest sono uguali.
Per avere un maggiore controllo sui digest utilizzati dalla tua app, richiedi una
cifra con OAEPPadding, come in Cipher.getInstance("RSA/ECB/OAEPPadding")
, e
fornisci un OAEPParameterSpec
a init()
per scegliere esplicitamente entrambi i digest.
Ciò è mostrato nel codice seguente:
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à deprecata
Le sezioni seguenti descrivono le funzionalità ritirate. Non utilizzarlo nella tua app.
Algoritmi Bouncy Castle
Le implementazioni di molti algoritmi di Bouncy Castle sono obsolete. Ciò influisce solo sui casi in cui richiedi esplicitamente il provider Bouncy Castle, 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 alla specifica di un fornitore solo con il sistema Android Keystore, è sconsigliato richiedere un fornitore specifico. Se segui questa linea guida, il ritiro non ti riguarda.
Cifratura basata su password senza vettore di inizializzazione
Le cifrature basate su password (PBE) che richiedono un vettore di inizializzazione (IV) possono ottenerlo dalla chiave, se è costruito in modo appropriato, o da un IV passato in modo esplicito. Se passi una chiave PBE che non contiene un IV e non passi un IV esplicito, le cifrature PBE su Android attualmente presuppongono un IV pari a zero.
Quando utilizzi cifrari PBE, passa sempre un IV esplicito, come mostrato nello snippet di codice riportato di seguito:
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 Crypto Java Cryptography Architecture
(JCA) è stato rimosso. Se la tua app richiede un'istanza del
provider Crypto, ad esempio chiamando il seguente metodo, si verifica un
NoSuchProviderException
.
Kotlin
SecureRandom.getInstance("SHA1PRNG", "Crypto")
Java
SecureRandom.getInstance("SHA1PRNG", "Crypto");
Libreria Jetpack security-crypto
Tutte le API nella libreria Jetpack
security-crypto
sono state ritirate nella release stabile della
versione 1.1.0
.
Non verranno rilasciate versioni successive di questa libreria.
Le annotazioni di ritiro sono visibili se nel file build.gradle
del modulo dell'app sono presenti le seguenti dipendenze:
Groovy
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") }
Algoritmi supportati
Questi sono gli identificatori degli algoritmi JCA supportati su Android:
AlgorithmParameterGenerator
AlgorithmParameters
CertPathBuilder
CertPathValidator
CertStore
CertificateFactory
Cipher
KeyAgreement
KeyFactory
KeyGenerator
KeyManagerFactory
KeyPairGenerator
KeyStore
Mac
MessageDigest
SSLContext
SSLEngine.Supported
SSLSocket.Supported
SecretKeyFactory
SecureRandom
Signature
TrustManagerFactory