La biblioteca de seguridad proporciona una implementación de las prácticas recomendadas de seguridad en relación con la lectura y la escritura de datos en reposo, y la creación y la 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 datos encriptados que se almacenan en archivos y preferencias compartidas.
Administración de claves
La biblioteca de seguridad utiliza un sistema de dos 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 las siguientes dependencias, según corresponda, al archivo build.gradle
del módulo de tu app:
Groovy
dependencies { implementation "androidx.security:security-crypto:1.0.0" // For Identity Credential APIs implementation "androidx.security:security-identity-credential:1.0.0-alpha03" // For App Authentication APIs implementation "androidx.security:security-app-authenticator:1.0.0-alpha02" // For App Authentication API testing androidTestImplementation "androidx.security:security-app-authenticator:1.0.0-alpha01" }
Kotlin
dependencies { implementation("androidx.security:security-crypto:1.0.0") // For Identity Credential APIs implementation("androidx.security:security-identity-credential:1.0.0-alpha03") // For App Authentication APIs implementation("androidx.security:security-app-authenticator:1.0.0-alpha02") // For App Authentication API testing androidTestImplementation("androidx.security:security-app-authenticator: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:
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 mainKeyAlias = MasterKeys.getOrCreate(keyGenParameterSpec) val fileToRead = "my_sensitive_data.txt" val encryptedFile = EncryptedFile.Builder( File(DIRECTORY, fileToRead), applicationContext, mainKeyAlias, 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
Context context = getApplicationContext(); // 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 mainKeyAlias = MasterKeys.getOrCreate(keyGenParameterSpec); String fileToRead = "my_sensitive_data.txt"; EncryptedFile encryptedFile = new EncryptedFile.Builder( new File(DIRECTORY, fileToRead), context, mainKeyAlias, 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:
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 mainKeyAlias = MasterKeys.getOrCreate(keyGenParameterSpec) // Create a file with this name or replace an entire existing file // that has the same name. Note that you cannot append to an existing file, // and the filename cannot contain path separators. val fileToWrite = "my_sensitive_data.txt" val encryptedFile = EncryptedFile.Builder( File(DIRECTORY, fileToWrite), applicationContext, mainKeyAlias, 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
Context context = getApplicationContext(); // 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 mainKeyAlias = MasterKeys.getOrCreate(keyGenParameterSpec); // Create a file with this name or replace an entire existing file // that has the same name. Note that you cannot append to an existing file, // and the filename cannot contain path separators. String fileToWrite = "my_sensitive_data.txt"; EncryptedFile encryptedFile = new EncryptedFile.Builder( new File(DIRECTORY, fileToWrite), context, mainKeyAlias, 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 superior a 0 como primer argumento desetUserAuthenticationParameters()
. 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.
Edita 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:
Kotlin
val sharedPrefsFile: String = FILE_NAME val sharedPreferences: SharedPreferences = EncryptedSharedPreferences.create( sharedPrefsFile, mainKeyAlias, applicationContext, 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( sharedPrefsFile, mainKeyAlias, getApplicationContext(), EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV, EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM ); SharedPreferences.Editor sharedPrefsEditor = sharedPreferences.edit(); // Edit the user's shared preferences... sharedPrefsEditor.apply();
Compatible con dispositivos Android 5.0 y Android 5.1
La versión 1.1.0 de la biblioteca de seguridad te permite admitir dispositivos que ejecutan Android 5.0 (nivel de API 21) y versiones posteriores. En Android 5.0 y Android 5.1 (nivel de API 22), no puedes usar el almacén de claves de Android para almacenar conjuntos de claves.
Cómo leer archivos
En el siguiente fragmento de código, se muestra cómo usar EncryptedFile
para leer el contenido de un archivo con la versión 1.1.0 de la biblioteca de seguridad:
Kotlin
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
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 con la versión 1.1.0 de la biblioteca de seguridad:
Kotlin
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 = File(DIRECTORY, "my_sensitive_data.txt") val encryptedFile = EncryptedFile.Builder(applicationContext, fileToWrite, mainKey, EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB ).build() // File cannot exist before using openFileOutput if (fileToWrite.exists()) { fileToWrite.delete() } val fileContent = "MY SUPER-SECRET INFORMATION" .toByteArray(StandardCharsets.UTF_8)) encryptedFile.openFileOutput().apply { write(fileContent) flush() close() }
Java
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. File fileToWrite = new File(DIRECTORY, "my_sensitive_data.txt"); EncryptedFile encryptedFile = new EncryptedFile.Builder(context, fileToWrite, mainKey, EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB ).build(); // File cannot exist before using openFileOutput if (fileToWrite.exists()) { fileToWrite.delete(); } byte[] fileContent = "MY SUPER-SECRET INFORMATION" .getBytes(StandardCharsets.UTF_8); OutputStream outputStream = encryptedFile.openFileOutput(); outputStream.write(fileContent); outputStream.flush(); outputStream.close();
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 con la versión 1.1.0 de la biblioteca de seguridad:
Kotlin
val context = applicationContext val mainKey = MasterKey.Builder(applicationContext) .setKeyScheme(MasterKey.KeyScheme.AES256_GCM) .build() val sharedPreferences = EncryptedSharedPreferences.create( applicationContext, FILE_NAME, mainKey, EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV, EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM ) with (sharedPreferences.edit()) { // Edit the user's shared preferences... apply() }
Java
Context context = getApplicationContext(); MasterKey mainKey = new MasterKey.Builder(context) .setKeyScheme(MasterKey.KeyScheme.AES256_GCM) .build(); SharedPreferences sharedPreferences = EncryptedSharedPreferences .create( context, FILE_NAME, mainKey, EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV, EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM ); SharedPreferences.Editor sharedPrefsEditor = sharedPreferences.edit(); // Edit the user's shared preferences... sharedPrefsEditor.apply();
Recomendaciones para ti
- Nota: El texto del vínculo se muestra cuando JavaScript está desactivado
- Cómo ejecutar código DEX incorporado directamente desde un APK
- Tapjacking
- android:exported