En este documento, se describe la manera adecuada de usar los recursos criptográficos de Android y se incluyen algunos ejemplos. Si tu app requiere una clave de seguridad mayor, usa el sistema de almacén de claves de Android.
Especifica un proveedor solo con el sistema Android Keystore
Si usas el sistema Android Keystore, debes especificar un proveedor.
Sin embargo, en otras situaciones, Android no garantiza un proveedor en particular para un algoritmo determinado. Especificar un proveedor sin utilizar el sistema de Android Keystore podría provocar problemas de compatibilidad en versiones futuras.
Elige un algoritmo recomendado
Si tienes la libertad de elegir qué algoritmo usar (por ejemplo, si no requieres compatibilidad con un sistema de terceros), te recomendamos que uses los siguientes algoritmos:
Clase | Recomendación |
---|---|
Cifrado | AES en modo CBC o GCM con claves de 256 bits (como AES/GCM/NoPadding ) |
MessageDigest | Familia SHA-2 (por ejemplo, SHA-256 ) |
Mac | HMAC de la familia SHA-2 (por ejemplo, HMACSHA256 ) |
Firma | Familia SHA-2 con ECDSA (por ejemplo, SHA256withECDSA ) |
Ejecuta operaciones criptográficas comunes
En las siguientes secciones, se incluyen fragmentos que demuestran cómo puedes realizar operaciones criptográficas comunes en tu app.
Encripta un mensaje
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();
Genera un resumen del mensaje
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);
Genera una firma digital
Debes tener un objeto PrivateKey
que contenga la clave de firma, que puedes generar en el entorno de ejecución, leer desde un archivo incluido en tu app u obtener de alguna otra fuente según tus necesidades.
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();
Verifica una firma digital
Debes tener un objeto PublicKey
que contenga la clave pública del firmante, que puedes leer de un archivo incluido en tu app, extraer de un certificado, o bien obtener de alguna otra fuente según tus necesidades.
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);
Complejidades de implementación
Hay algunos detalles de la implementación de la criptografía de Android que parecen poco comunes, pero que se incluyen debido a problemas de compatibilidad. En esta sección, se analizan los que probablemente encontrarás.
Resumen del mensaje de OAEP MGF1
Los cifrados de RSA OAEP se parametrizan a través de dos resúmenes de mensajes diferentes: el resumen "principal" y el resumen de MGF1. Hay identificadores Cipher
que incluyen nombres de resumen, como Cipher.getInstance("RSA/ECB/OAEPwithSHA-256andMGF1Padding")
, que especifica el resumen principal y deja el resumen de MGF1 sin especificar. En el caso del almacén de claves de Android, se usa SHA-1 para el resumen de MGF1, mientras que, para otros proveedores de criptografía de Android, los dos resúmenes son iguales.
Para tener más control sobre los resúmenes que utiliza tu app, solicita un algoritmo de cifrado con OAEPPadding, como en Cipher.getInstance("RSA/ECB/OAEPPadding")
, y proporciona una OAEPParameterSpec
a init()
a fin de elegir de manera explícita ambos resúmenes.
Esto se muestra en el siguiente código:
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));
Funcionalidad obsoleta
En las siguientes secciones, se describe la funcionalidad obsoleta. No la uses en tu app.
Algoritmos de Bouncy Castle
Las implementaciones de Bouncy Castle de muchos algoritmos son obsoletas. Esto solo afecta a casos en los que solicitas explícitamente el proveedor de Bouncy Castle, como se muestra en el siguiente ejemplo:
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"));
Como se indicó en la sección Cómo especificar un proveedor solo con el sistema de Android Keystore, no te recomendamos que solicites un proveedor específico. Si sigues ese lineamiento, esta baja no te afectará.
Cifrados de encriptación basados en contraseñas sin un vector de inicialización
Los algoritmos de cifrado de encriptación basados en contraseñas (PBE) que requieren un vector de inicialización (IV) pueden obtenerlo de la clave, si están construidos adecuadamente, o a partir de un IV que se pase de manera explícita. Si pasas una clave de PBE que no contiene un IV y no pasas un IV explícito, los cifrados de PBE en Android del momento supondrán un IV de cero.
Cuando uses cifrados de PBE, siempre pasa un IV explícito, como se muestra en el siguiente fragmento de código:
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));
Proveedor de criptografía
A partir de Android 9 (nivel de API 28), se quitó el proveedor de criptografía de arquitectura de criptografía de Java (JCA). Si tu app solicita una instancia del proveedor de criptografía, por ejemplo, cuando llama al método siguiente, se produce un NoSuchProviderException
.
Kotlin
SecureRandom.getInstance("SHA1PRNG", "Crypto")
Java
SecureRandom.getInstance("SHA1PRNG", "Crypto");
Biblioteca criptográfica de seguridad de Jetpack
La biblioteca criptográfica de seguridad de Jetpack dejó de estar disponible. Esto solo afecta a casos en los que tienes las siguientes dependencias en el archivo build.gradle
del módulo de tu app:
Groovy
dependencies { implementation "androidx.security:security-crypto:1.0.0" }
Kotlin
dependencies { implementation("androidx.security:security-crypto:1.0.0") }
Algoritmos compatibles
Estos son los identificadores de algoritmo de JCA compatibles con 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