Únete a ⁠ #Android11: The Beta Launch Show el 3 de junio.

Cómo trabajar con datos de forma más segura

La biblioteca de seguridad, parte de Android Jetpack, proporciona una implementación de las prácticas recomendadas de seguridad en relación con la lectura y escritura de datos en reposo, y con 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 apps de consumo, 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. El conjunto de claves se almacena en SharedPreferences.

  • Una clave maestra que encripta todos los conjuntos de claves. Esta clave se almacena utilizando el sistema Android Keystore.

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 y FileOutputStream, lo que otorga a tu app operaciones de lectura y escritura más seguras.

Para proporcionar operaciones seguras de lectura y escritura desde secuencias 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 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 keyGenParameterSpec = MasterKeys.AES256_GCM_SPEC
    val masterKeyAlias = MasterKeys.getOrCreate(keyGenParameterSpec)

    val fileToRead = "my_sensitive_data.txt"
    lateinit var byteStream: ByteArrayOutputStream
    val encryptedFile = EncryptedFile.Builder(
        File(directory, fileToRead),
        context,
        masterKeyAlias,
        EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB
    ).build()

    val contents = encryptedFile.bufferedReader().useLines { lines ->
        lines.fold("") { working, line ->
            "$working\n$line"
        }
    }
    

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);

    String fileToRead = "my_sensitive_data.txt";
    ByteArrayOutputStream byteStream;

    EncryptedFile encryptedFile = new EncryptedFile.Builder(
            File(directory, fileToRead),
            context,
            masterKeyAlias,
            EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB
    ).build();

    StringBuffer stringBuffer = new StringBuffer();
    try (BufferedReader reader =
                 new BufferedReader(new FileReader(encryptedFile))) {

        String line = reader.readLine();
        while (line != null) {
            stringBuffer.append(line).append('\n');
            line = reader.readLine();
        }
    } catch (IOException e) {
        // Error occurred opening raw file for reading.
    } finally {
        String contents = stringBuffer.toString();
    }
    

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 keyGenParameterSpec = MasterKeys.AES256_GCM_SPEC
    val masterKeyAlias = MasterKeys.getOrCreate(keyGenParameterSpec)

    // 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(
        File(directory, fileToWrite),
        context,
        masterKeyAlias,
        EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB
    ).build()

    encryptedFile.bufferedWriter().use { writer ->
        writer.write("MY SUPER-SECRET INFORMATION")
    }
    

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);

    // 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";
    try {
        EncryptedFile encryptedFile = new EncryptedFile.Builder(
                new File(directory, fileToWrite),
                context,
                masterKeyAlias,
                EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB
        ).build();

        // Write to a file.
        BufferedWriter writer = new BufferedWriter(new FileWriter(encryptedFile));
        writer.write("MY SUPER-SECRET INFORMATION");
    } catch (GeneralSecurityException gse) {
        // Error occurred getting or creating keyset.
    } catch (IOException ex) {
        // Error occurred opening file for writing.
    }
    

En los casos prácticos que requieran seguridad adicional, completa los siguientes pasos:

  1. Crea un objeto KeyGenParameterSpec.Builder y pasa true a setUserAuthenticationRequired() y un valor mayor que 0 a setUserAuthenticationValidityDurationSeconds().
  2. 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.

  3. 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 sharedPreferences = EncryptedSharedPreferences
        .create(
        fileName,
        masterKeyAlias,
        context,
        EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
        EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
    )

    val sharedPrefsEditor = sharedPreferences.edit()
    

Java

    EncryptedSharedPreferences sharedPreferences = EncryptedSharedPreferences
            .create(
                    fileName,
                    masterKeyAlias,
                    context,
                    EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
                    EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
            );

    SharedPreferences.Editor sharedPrefsEditor = sharedPreferences.edit();