Trabaja con datos de forma más segura Parte de Android Jetpack
La biblioteca de seguridad proporciona una implementación de las prácticas recomendadas de seguridad en relación con la lectura y escritura de datos en reposo, y la creación y verificación de claves.
La biblioteca utiliza el patrón de compilador a fin de proporcionar configuraciones predeterminadas seguras para los siguientes niveles de seguridad:
- Seguridad sólida que equilibra una excelente encriptación y un buen rendimiento. Este nivel de seguridad es apropiado para aplicaciones para consumidores, como apps bancarias y de chat, y para apps empresariales que realizan comprobaciones de revocación de certificados.
- Seguridad máxima. Este nivel de seguridad es adecuado para las apps que requieren un almacén de claves con copia de seguridad en hardware y la presencia del usuario para proporcionar acceso a las claves.
En esta guía, se muestra cómo trabajar con las configuraciones de seguridad recomendadas de la biblioteca de seguridad y se incluye información sobre cómo leer y escribir de manera fácil y segura datos encriptados que se almacenan en archivos y preferencias compartidas.
Administración de claves
La biblioteca de seguridad utiliza un sistema de 2 partes para la administración de claves:
-
Un conjunto de claves que contiene una o más claves para encriptar un archivo o datos de preferencias compartidas. Se almacena el conjunto de claves en
SharedPreferences
. -
Una clave principal (
master
) que encripta todos los conjuntos de claves. Esta se almacena utilizando el sistema del almacén de claves de Android.
Clases incluidas en la biblioteca
La biblioteca de seguridad incluye las siguientes clases a fin de proporcionar datos más seguros en reposo:
EncryptedFile
-
Proporciona implementaciones personalizadas de
FileInputStream
yFileOutputStream
, lo que otorga a tu app operaciones de transmisión de lectura y escritura más seguras.Para proporcionar operaciones seguras de lectura y escritura desde transmisiones de archivos, la biblioteca de seguridad utiliza el tipo primitivo de encriptación autenticada con datos asociados (AEAD). Obtén más información sobre este tipo primitivo en la documentación de la biblioteca Tink, en GitHub.
EncryptedSharedPreferences
-
Envuelve la clase
SharedPreferences
y encripta automáticamente claves y valores utilizando un método de dos esquemas:- Se encriptan las claves mediante un algoritmo de encriptación determinista, de modo que sea posible encriptar la clave y buscarla correctamente.
- Se encriptan los valores con AES-256 GCM de una manera no determinista.
En las siguientes secciones, se muestra cómo usar estas clases para realizar operaciones comunes con archivos y preferencias compartidas.
Cómo incluir la biblioteca en tu proyecto
A fin de usar la biblioteca de seguridad, agrega la siguiente dependencia al archivo build.gradle
del módulo de tu app:
dependencies { implementation "androidx.security:security-crypto:1.0.0-rc04" // For Identity Credential APIs implementation "androidx.security:security-identity-credential:1.0.0-alpha01" }
Cómo leer archivos
En el siguiente fragmento de código, se muestra cómo usar EncryptedFile
para leer el contenido de un archivo de una manera más segura:
Kotlin
// Although you can define your own key generation parameter specification, it's // recommended that you use the value specified here. val mainKey = MasterKey.Builder(applicationContext) .setKeyScheme(MasterKey.KeyScheme.AES256_GCM) .build() val fileToRead = "my_sensitive_data.txt" val encryptedFile = EncryptedFile.Builder( applicationContext, File(DIRECTORY, fileToRead), mainKey, EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB ).build() val inputStream = encryptedFile.openFileInput() val byteArrayOutputStream = ByteArrayOutputStream() var nextByte: Int = inputStream.read() while (nextByte != -1) { byteArrayOutputStream.write(nextByte) nextByte = inputStream.read() } val plaintext: ByteArray = byteArrayOutputStream.toByteArray()
Java
// Although you can define your own key generation parameter specification, it's // recommended that you use the value specified here. Context context = getApplicationContext(); MasterKey mainKey = new MasterKey.Builder(context) .setKeyScheme(MasterKey.KeyScheme.AES256_GCM) .build(); String fileToRead = "my_sensitive_data.txt"; EncryptedFile encryptedFile = new EncryptedFile.Builder(context, new File(DIRECTORY, fileToRead), mainKey, EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB ).build(); InputStream inputStream = encryptedFile.openFileInput(); ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); int nextByte = inputStream.read(); while (nextByte != -1) { byteArrayOutputStream.write(nextByte); nextByte = inputStream.read(); } byte[] plaintext = byteArrayOutputStream.toByteArray();
Cómo escribir archivos
En el siguiente fragmento de código, se muestra cómo usar EncryptedFile
para escribir el contenido de un archivo de una manera más segura:
Kotlin
// Although you can define your own key generation parameter specification, it's // recommended that you use the value specified here. val mainKey = MasterKey.Builder(applicationContext) .setKeyScheme(MasterKey.KeyScheme.AES256_GCM) .build() // Creates a file with this name, or replaces an existing file // that has the same name. Note that the file name cannot contain // path separators. val fileToWrite = "my_sensitive_data.txt" val encryptedFile = EncryptedFile.Builder( applicationContext, File(DIRECTORY, fileToWrite), mainKey, EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB ).build() val fileContent = "MY SUPER-SECRET INFORMATION" .toByteArray(StandardCharsets.UTF_8) encryptedFile.openFileOutput().apply { write(fileContent) flush() close() }
Java
// Although you can define your own key generation parameter specification, it's // recommended that you use the value specified here. Context context = getApplicationContext(); MasterKey mainKey = new MasterKey.Builder(context) .setKeyScheme(MasterKey.KeyScheme.AES256_GCM) .build(); // Creates a file with this name, or replaces an existing file // that has the same name. Note that the file name cannot contain // path separators. String fileToWrite = "my_sensitive_data.txt"; EncryptedFile encryptedFile = new EncryptedFile.Builder(context, new File(DIRECTORY, fileToWrite), mainKey, EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB ).build(); byte[] fileContent = "MY SUPER-SECRET INFORMATION" .getBytes(StandardCharsets.UTF_8); OutputStream outputStream = encryptedFile.openFileOutput(); outputStream.write(fileContent); outputStream.flush(); outputStream.close();
En los casos prácticos que requieran seguridad adicional, completa los siguientes pasos:
- Crea un objeto
KeyGenParameterSpec.Builder
y pasatrue
asetUserAuthenticationRequired()
y un valor mayor que 0 asetUserAuthenticationValidityDurationSeconds()
. Solicita al usuario que ingrese las credenciales mediante
createConfirmDeviceCredentialIntent()
. Obtén más información sobre cómo solicitar la autenticación de usuarios para el uso de la clave.Anula
onActivityResult()
para obtener la devolución de llamada de la credencial confirmada.
Para obtener más información, consulta Cómo solicitar la autenticación de usuario para el uso de la clave.
Cómo editar preferencias compartidas
En el siguiente fragmento de código, se muestra cómo usar EncryptedSharedPreferences
para editar el conjunto de preferencias compartidas de un usuario de una manera más segura:
Kotlin
val sharedPrefsFile: String = FILE_NAME val sharedPreferences: SharedPreferences = EncryptedSharedPreferences.create( applicationContext, sharedPrefsFile, mainKey, EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV, EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM ) with (sharedPreferences.edit()) { // Edit the user's shared preferences... apply() }
Java
String sharedPrefsFile = FILE_NAME; SharedPreferences sharedPreferences = EncryptedSharedPreferences.create( context, sharedPrefsFile, mainKey, EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV, EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM ); SharedPreferences.Editor sharedPrefsEditor = sharedPreferences.edit(); // Edit the user's shared preferences... sharedPrefsEditor.apply();