El sistema Android Keystore te permite almacenar claves criptográficas en un contenedor para que resulte más difícil extraerlas del dispositivo. Una vez que las claves se encuentran en el almacén de claves, se pueden usar para realizar operaciones criptográficas con el material de claves restante no exportable. Además, mediante prestaciones permite restringir el momento y la manera en que se pueden usar las claves; por ejemplo, la solicitud de autenticación del usuario para el uso de claves o la restricción de estas para su uso exclusivo en determinados modos criptográficos. Para obtener más información, consulta la sección Funciones de seguridad.
La API de KeyChain
, que se introdujo en Android 4.0 (API nivel 14); la función del proveedor de Android Keystore, que se introdujo en Android 4.3 (API nivel 18); y la biblioteca de seguridad, disponible como parte de Jetpack, usan el sistema de almacén de claves. En este documento, se explica cómo y cuándo debe usarse el proveedor del almacén de claves d e Android.
Funciones de seguridad
El sistema Android Keystore protege el material de claves contra usos no autorizados. En primer lugar, evita la extracción del material de claves de los procesos de la aplicación y del dispositivo Android en su totalidad a fin de reducir el uso no autorizado de claves fuera del dispositivo. En segundo lugar, permite que las apps especifiquen usos autorizados de sus claves y aplica estas restricciones fuera de los procesos de las apps para reducir el uso no autorizado de material de claves en el dispositivo.
Prevención de extracción
El material de claves de las claves de Android Keystore está protegido contra la extracción mediante dos medidas de seguridad:
- El material de claves nunca ingresa al proceso de la aplicación. Cuando una aplicación realiza operaciones criptográficas usando una clave de Android Keystore, el texto sin formato, el texto cifrado y los mensajes en segundo plano que se firmarán o verificarán se envían a un proceso del sistema que se ocupa de las operaciones criptográficas. Si el proceso de la app está comprometido, el atacante puede usar las claves de la app. Sin embargo, no podrá extraer el material de claves (por ejemplo, para usarlo fuera del dispositivo Android).
- El material de claves puede estar vinculado al hardware seguro (p. ej., entorno de ejecución seguro [TEE] y elemento seguro [SE]) del dispositivo Android. Cuando se habilita esta función para una clave, su material de clave nunca se expone fuera del hardware seguro. Si el SO Android se ve comprometido o un atacante puede leer el almacenamiento interno del dispositivo, este puede usar las claves de Android Keystore de cualquier app del dispositivo Android, pero no podrá extraerlas de este. Esta función se habilita únicamente si el hardware seguro del dispositivo admite la combinación específica de algoritmo de la clave, modos de bloqueo, esquemas de relleno y resúmenes con la que se autoriza el uso de la clave. Si quieres averiguar si la función está habilitada para una clave, obtén
KeyInfo
para la clave e inspecciona el valor de retorno deKeyInfo.isInsideSecurityHardware()
.
Módulo de seguridad de hardware
Los dispositivos compatibles que ejecutan Android 9 (nivel de API 28) o versiones posteriores pueden contar con un StrongBox Keymaster, una implementación del Keymaster HAL que reside en un módulo de seguridad de hardware. El módulo contiene lo siguiente:
- Su propia CPU
- Almacenamiento seguro
- Un generador de números aleatorios reales
- Mecanismos adicionales de resistencia contra alteraciones de paquetes e instalaciones de prueba no autorizadas de apps
Cuando se verifican las claves almacenadas en el StrongBox Keymaster, el sistema corrobora la integridad de una clave con el entorno de ejecución seguro (TEE).
Para admitir implementaciones de StrongBox de bajo consumo, se admiten un subconjunto de algoritmos y tamaños de clave:
- RSA 2048
- AES 128 y 256
- ECDSA P-256
- HMAC-SHA256 (admite tamaños de clave de 8 a 64 bytes)
- Triple DES 168
Cuando generas o importas claves con la clase KeyStore
, indicas una preferencia por almacenar la clave en el StrongBox Keymaster al pasar true
al método setIsStrongBoxBacked()
.
Autorizaciones de uso de las claves
Para mitigar el uso no autorizado de claves en el dispositivo Android, Android Keystore permite que las apps especifiquen usos autorizados de sus claves al generarlas o importarlas. Una vez que se genera o se importa una clave, no se pueden cambiar sus autorizaciones. Luego, Android Keystore aplica las autorizaciones cada vez que se use la clave. Esta es una función de seguridad avanzada que generalmente es útil solo si tus requisitos establecen que el compromiso del proceso de tu aplicación después de la generación o importación de la clave (pero no antes de esta ni durante su transcurso) no pueda permitir el uso no autorizado de la clave.
Las autorizaciones de uso de claves compatibles pertenecen a las siguientes categorías:
- Criptografía: Corresponde a algoritmos, operaciones o propósitos de clave autorizados (encriptación, desencriptación, firma y verificación), esquemas de relleno, modos de bloqueo, resúmenes con los que se puede usar la clave.
- Intervalo de validez temporal: Corresponde a un intervalo durante el cual está autorizado el uso de la clave.
- Autenticación del usuario: La clave solo se puede usar si se autenticó al usuario en un período lo suficientemente reciente. Consulta la sección Solicita la autenticación del usuario para el uso de la clave.
Como medida de seguridad adicional, en el caso de las claves cuyo material de claves se encuentre dentro de hardware seguro (consulta KeyInfo.isInsideSecurityHardware()
), se pueden aplicar algunas autorizaciones de uso de claves mediante hardware seguro, según el dispositivo Android.
Es probable que las autorizaciones criptográfica y de autenticación del usuario se apliquen mediante el hardware seguro.
Por el contrario, es poco probable que las autorizaciones de intervalo de validez temporal se apliquen mediante el hardware seguro, ya que normalmente éste no cuenta con un reloj independiente y seguro que funcione en tiempo real.
Puedes averiguar si la autorización de autenticación del usuario de una clave se aplica mediante el hardware seguro por medio del método KeyInfo.isUserAuthenticationRequirementEnforcedBySecureHardware()
.
Elige entre Keychain o el proveedor del almacén de claves de de Android
Usa la API de KeyChain
cuando quieras credenciales para todo el sistema. Cuando una app solicita el uso de cualquier credencial a través de la API de KeyChain
, los usuarios pueden elegir, mediante una IU proporcionada por el sistema, la credencial instalada a la que puede acceder una app. Esto permite que varias apps usen el mismo conjunto de credenciales con el consentimiento del usuario.
Usa el proveedor del almacén de claves de Android para permitir que una app individual almacene sus propias credenciales, a las cuales solo la app puede acceder.
Esto permite que las apps administren credenciales que solo ellas pueden usar y entrega los mismos beneficios de seguridad que ofrece la API de KeyChain
para las credenciales en todo el sistema. Este método no requiere la interacción del usuario para seleccionar las credenciales.
Usa el proveedor del almacén de claves de Android
Para usar esta función, se emplean las clases KeyStore
, KeyPairGenerator
o KeyGenerator
estándares junto con el proveedor de AndroidKeyStore
incluido en Android 4.3 (API nivel 18).
AndroidKeyStore
está registrado como un tipo de KeyStore
cuando se lo usa con el método KeyStore.getInstance(type)
y como proveedor cuando se lo usa con los métodos KeyPairGenerator.getInstance(algorithm, provider)
y KeyGenerator.getInstance(algorithm, provider)
.
Genera una nueva clave privada
Si quieres generar una clave PrivateKey
nueva, también debes especificar los atributos iniciales X.509 que tendrá el certificado autofirmado.
La biblioteca de Seguridad proporciona una implementación predeterminada para generar una clave simétrica válida, como se muestra en el siguiente fragmento:
Kotlin
// Although you can define your own key generation parameter specification, it's // recommended that you use the value specified here. val keyGenParameterSpec = MasterKeys.AES256_GCM_SPEC val masterKeyAlias = MasterKeys.getOrCreate(keyGenParameterSpec)
Java
// Although you can define your own key generation parameter specification, it's // recommended that you use the value specified here. KeyGenParameterSpec keyGenParameterSpec = MasterKeys.AES256_GCM_SPEC; String masterKeyAlias = MasterKeys.getOrCreate(keyGenParameterSpec);
Puedes usar KeyStore.setKeyEntry
para reemplazar el certificado más tarde con un certificado firmado por una autoridad certificada (CA).
Para generar la clave, usa un KeyPairGenerator
con KeyPairGeneratorSpec
.
Kotlin
/* * Generate a new EC key pair entry in the Android Keystore by * using the KeyPairGenerator API. The private key can only be * used for signing or verification and only with SHA-256 or * SHA-512 as the message digest. */ val kpg: KeyPairGenerator = KeyPairGenerator.getInstance( KeyProperties.KEY_ALGORITHM_EC, "AndroidKeyStore" ) val parameterSpec: KeyGenParameterSpec = KeyGenParameterSpec.Builder( alias, KeyProperties.PURPOSE_SIGN or KeyProperties.PURPOSE_VERIFY ).run { setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512) build() } kpg.initialize(parameterSpec) val kp = kpg.generateKeyPair()
Java
/* * Generate a new EC key pair entry in the Android Keystore by * using the KeyPairGenerator API. The private key can only be * used for signing or verification and only with SHA-256 or * SHA-512 as the message digest. */ KeyPairGenerator kpg = KeyPairGenerator.getInstance( KeyProperties.KEY_ALGORITHM_EC, "AndroidKeyStore"); kpg.initialize(new KeyGenParameterSpec.Builder( alias, KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY) .setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512) .build()); KeyPair kp = kpg.generateKeyPair();
Genera una clave secreta nueva
Para generar la clave, sigue el mismo proceso que para generar una clave privada nueva. Usa la biblioteca de Seguridad en cada caso.
Importa claves encriptadas de manera más segura
Android 9 (nivel de API 28) y versiones posteriores permiten importar claves encriptadas de forma segura al almacén de claves mediante un formato de clave con codificación ASN.1. El Keymaster luego descifra las claves en el almacén de claves, para que el contenido de estas nunca aparezca como texto sin formato en la memoria host del dispositivo. Ese proceso proporciona seguridad adicional en la desencriptación de claves.
Para admitir la importación segura de claves encriptadas al almacén de claves, completa los siguientes pasos:
Genera un par de claves que use el propósito
PURPOSE_WRAP_KEY
. Te recomendamos que también agregues una certificación a este par de claves.En un servidor o una máquina que sea confiable, genera el mensaje ASN.1 que debería contener el elemento
SecureKeyWrapper
.El contenedor incluye el siguiente esquema:
KeyDescription ::= SEQUENCE { keyFormat INTEGER, authorizationList AuthorizationList } SecureKeyWrapper ::= SEQUENCE { wrapperFormatVersion INTEGER, encryptedTransportKey OCTET_STRING, initializationVector OCTET_STRING, keyDescription KeyDescription, secureKey OCTET_STRING, tag OCTET_STRING }
Crea un objeto
WrappedKeyEntry
y pasa el mensaje ASN.1 como un arreglo de bytes.Pasa este objeto
WrappedKeyEntry
a la sobrecarga desetEntry()
que acepta un objetoKeystore.Entry
.
Trabaja con entradas de Keystore
El uso del proveedor de AndroidKeyStore
abarca todas las API de KeyStore
estándares.
Enumera las entradas
Para enumerar entradas en el almacén de claves, llama al método aliases()
:
Kotlin
/* * Load the Android KeyStore instance using the * "AndroidKeyStore" provider to list out what entries are * currently stored. */ val ks: KeyStore = KeyStore.getInstance("AndroidKeyStore").apply { load(null) } val aliases: Enumeration<String> = ks.aliases()
Java
/* * Load the Android KeyStore instance using the * "AndroidKeyStore" provider to list out what entries are * currently stored. */ KeyStore ks = KeyStore.getInstance("AndroidKeyStore"); ks.load(null); Enumeration<String> aliases = ks.aliases();
Firma y verifica los datos
Obtén la KeyStore.Entry
del almacén de claves y usa las API de Signature
, como sign()
, para firmar datos:
Kotlin
/* * Use a PrivateKey in the KeyStore to create a signature over * some data. */ val ks: KeyStore = KeyStore.getInstance("AndroidKeyStore").apply { load(null) } val entry: KeyStore.Entry = ks.getEntry(alias, null) if (entry !is KeyStore.PrivateKeyEntry) { Log.w(TAG, "Not an instance of a PrivateKeyEntry") return null } val signature: ByteArray = Signature.getInstance("SHA256withECDSA").run { initSign(entry.privateKey) update(data) sign() }
Java
/* * Use a PrivateKey in the KeyStore to create a signature over * some data. */ KeyStore ks = KeyStore.getInstance("AndroidKeyStore"); ks.load(null); KeyStore.Entry entry = ks.getEntry(alias, null); if (!(entry instanceof PrivateKeyEntry)) { Log.w(TAG, "Not an instance of a PrivateKeyEntry"); return null; } Signature s = Signature.getInstance("SHA256withECDSA"); s.initSign(((PrivateKeyEntry) entry).getPrivateKey()); s.update(data); byte[] signature = s.sign();
También puedes verificar datos con el método verify(byte[])
:
Kotlin
/* * Verify a signature previously made by a PrivateKey in our * KeyStore. This uses the X.509 certificate attached to our * private key in the KeyStore to validate a previously * generated signature. */ val ks = KeyStore.getInstance("AndroidKeyStore").apply { load(null) } val entry = ks.getEntry(alias, null) as? KeyStore.PrivateKeyEntry if (entry == null) { Log.w(TAG, "Not an instance of a PrivateKeyEntry") return false } val valid: Boolean = Signature.getInstance("SHA256withECDSA").run { initVerify(entry.certificate) update(data) verify(signature) }
Java
/* * Verify a signature previously made by a PrivateKey in our * KeyStore. This uses the X.509 certificate attached to our * private key in the KeyStore to validate a previously * generated signature. */ KeyStore ks = KeyStore.getInstance("AndroidKeyStore"); ks.load(null); KeyStore.Entry entry = ks.getEntry(alias, null); if (!(entry instanceof PrivateKeyEntry)) { Log.w(TAG, "Not an instance of a PrivateKeyEntry"); return false; } Signature s = Signature.getInstance("SHA256withECDSA"); s.initVerify(((PrivateKeyEntry) entry).getCertificate()); s.update(data); boolean valid = s.verify(signature);
Solicita la autenticación del usuario para el uso de la clave
Al generar una clave o importarla a AndroidKeyStore
, puedes especificar que solo se autorice el uso de la clave si se ha autenticado el usuario. El usuario se autentica mediante un subconjunto de sus credenciales seguras de pantalla bloqueada (patrón/PIN/contraseña y huella digital).
Esta es una función de seguridad avanzada que generalmente es útil solo si tus requisitos indican que el compromiso del proceso de tu aplicación después de la generación o importación de la clave (pero no antes de esta ni durante su transcurso) no puede omitir el requisito por el cual se debe autenticar el usuario para que este pueda usar la clave.
Cuando se autoriza el uso de una clave solo si se ha autenticado el usuario, esta se configura para funcionar en uno de dos modos:
- La autenticación del usuario permite el uso de claves durante un tiempo determinado. El uso de todas las claves en este modo se autoriza una vez que el usuario desbloquea la pantalla bloqueada segura o confirma la credencial de la pantalla bloqueada segura mediante el flujo
KeyguardManager.createConfirmDeviceCredentialIntent
. El período de validez de la autorización es específico para cada clave y se especifica con el métodosetUserAuthenticationValidityDurationSeconds
durante la generación o importación de la clave. Esas claves solo se pueden generar o importar si la pantalla bloqueada segura está habilitada (consultaKeyguardManager.isDeviceSecure()
). Las claves se invalidan de forma permanente cuando se inhabilita la pantalla bloqueada segura (se restablece en "Ninguno", "Deslizar" o cualquier otro modo que no autentique al usuario) o se fuerza su restablecimiento (p. ej., por parte de un administrador de dispositivos). - La autenticación del usuario autoriza una operación criptográfica específica asociada con una clave. En este modo, el usuario debe autorizar de manera individual cada operación que incluya esa clave.
Actualmente, el único medio para realizar una autorización como esta es la autenticación mediante huella dactilar:
FingerprintManager.authenticate
. Estas claves solo se pueden generar o importar si hay al menos una huella digital registrada (consultaFingerprintManager.hasEnrolledFingerprints
). Además, se invalidan de forma permanente cuando se registra una huella digital nueva o se anulan todas.
Algoritmos compatibles
Cifrado
Algoritmo | Compatibilidad (niveles de API) | Notas |
---|---|---|
AES/CBC/NoPadding | 23 y posteriores | |
AES/CBC/PKCS7Padding | 23 y posteriores | |
AES/CTR/NoPadding | 23 y posteriores | |
AES/ECB/NoPadding | 23 y posteriores | |
AES/ECB/PKCS7Padding | 23 y posteriores | |
AES/GCM/NoPadding | 23 y posteriores | Solo se admiten IV de 12 bytes de extensión. |
RSA/ECB/NoPadding | 18+ | |
RSA/ECB/PKCS1Padding | 18+ | |
RSA/ECB/OAEPWithSHA-1AndMGF1Padding | 23 y posteriores | |
RSA/ECB/OAEPWithSHA-224AndMGF1Padding | 23 y posteriores | |
RSA/ECB/OAEPWithSHA-256AndMGF1Padding | 23 y posteriores | |
RSA/ECB/OAEPWithSHA-384AndMGF1Padding | 23 y posteriores | |
RSA/ECB/OAEPWithSHA-512AndMGF1Padding | 23 y posteriores | |
RSA/ECB/OAEPPadding | 23 y posteriores |
KeyGenerator
Algoritmo | Compatibilidad (niveles de API) | Notas |
---|---|---|
AES | 23 y posteriores | Tamaños admitidos: 128, 192, 256 |
HmacSHA1 | 23 y posteriores |
|
HmacSHA224 | 23 y posteriores |
|
HmacSHA256 | 23 y posteriores |
|
HmacSHA384 | 23 y posteriores |
|
HmacSHA512 | 23 y posteriores |
|
KeyFactory
Algoritmo | Compatibilidad (niveles de API) | Notas |
---|---|---|
EC | 23 y posteriores | Especificaciones de claves admitidas: KeyInfo (solo clave privada), ECPublicKeySpec (solo clave pública) y X509EncodedKeySpec (solo clave pública) |
RSA | 23 y posteriores | Especificaciones de claves admitidas: KeyInfo (solo clave privada), RSAPublicKeySpec (solo clave pública) y X509EncodedKeySpec (solo clave pública) |
KeyStore
KeyStore admite los mismos tipos de claves queKeyPairGenerator
y KeyGenerator
.KeyPairGenerator
Algoritmo | Compatibilidad (niveles de API) | Notas |
---|---|---|
DSA | 19–22 | |
EC | 23 y posteriores |
En los niveles de API previos al 23, las claves EC pueden generarse por medio del método KeyPairGenerator del algoritmo "RSA" con la inicialización de |
RSA | 18+ |
|
Mac
Algoritmo | Compatibilidad (niveles de API) | Notas |
---|---|---|
HmacSHA1 | 23 y posteriores | |
HmacSHA224 | 23 y posteriores | |
HmacSHA256 | 23 y posteriores | |
HmacSHA384 | 23 y posteriores | |
HmacSHA512 | 23 y posteriores |
Firma
Algoritmo | Compatibilidad (niveles de API) | Notas |
---|---|---|
MD5withRSA | 18+ | |
NONEwithECDSA | 23 y posteriores | |
NONEwithRSA | 18+ | |
SHA1withDSA | 19–22 | |
SHA1withECDSA | 19 y posteriores | |
SHA1withRSA | 18+ | |
SHA1withRSA/PSS | 23 y posteriores | |
SHA224withDSA | 20–22 | |
SHA224withECDSA | 20 y posteriores | |
SHA224withRSA | 20 y posteriores | |
SHA224withRSA/PSS | 23 y posteriores | |
SHA256withDSA | 19–22 | |
SHA256withECDSA | 19 y posteriores | |
SHA256withRSA | 18+ | |
SHA256withRSA/PSS | 23 y posteriores | |
SHA384withDSA | 19–22 | |
SHA384withECDSA | 19 y posteriores | |
SHA384withRSA | 18+ | |
SHA384withRSA/PSS | 23 y posteriores | |
SHA512withDSA | 19–22 | |
SHA512withECDSA | 19 y posteriores | |
SHA512withRSA | 18+ | |
SHA512withRSA/PSS | 23 y posteriores |
SecretKeyFactory
Algoritmo | Compatibilidad (niveles de API) | Notas |
---|---|---|
AES | 23 y posteriores | Especificaciones de claves admitidas: KeyInfo |
HmacSHA1 | 23 y posteriores | Especificaciones de claves admitidas: KeyInfo |
HmacSHA224 | 23 y posteriores | Especificaciones de claves admitidas: KeyInfo |
HmacSHA256 | 23 y posteriores | Especificaciones de claves admitidas: KeyInfo |
HmacSHA384 | 23 y posteriores | Especificaciones de claves admitidas: KeyInfo |
HmacSHA512 | 23 y posteriores | Especificaciones de claves admitidas: KeyInfo |
Artículos del blog
Consulta la entrada de blog Cómo unificar el acceso al almacén de claves en ICS.