第二个 Android 11 开发者预览版现已推出,快来测试并分享您的反馈吧

更安全地处理数据

作为 Android Jetpack 的一部分,Security 库提供了与读取和写入静态数据以及密钥创建和验证相关的安全最佳做法实现方法。

该库使用构建器模式为以下安全等级提供安全的默认设置:

  • 在可靠加密和良好性能之间取得平衡的强安全性。这种安全等级适用于银行和聊天应用等消费者应用,以及执行证书吊销检查的企业应用。
  • 最高安全性。这种安全等级适用于需要由硬件支持的密钥库和用户上线以提供密钥访问权限的应用。

本指南介绍了如何使用 Security 库的建议安全配置,以及如何轻松且安全地读取和写入存储在文件和共享偏好设置中的加密数据。

密钥管理

Security 库使用一个由两部分组成的密钥管理系统:

  • 包含一个或多个密钥的密钥集,用于加密文件或共享偏好设置数据。密钥集本身存储在 SharedPreferences 中。

  • 用于加密所有密钥集的主密钥。此密钥使用 Android 密钥库系统进行存储。

库中包含的类

Security 库包含以下类,以提供更安全的静态数据:

EncryptedFile

提供 FileInputStreamFileOutputStream 的自定义实现,为您的应用赋予更安全的流式读写操作。

为了提供安全的文件流读写操作,Security 库使用了流式带关联数据的认证加密 (AEAD) 基元。要详细了解该基元,请参阅 GitHub 上的 Tink 库文档

EncryptedSharedPreferences

封装 SharedPreferences 类,并使用双重方案方法自动加密密钥和值:

  • 密钥使用确定性加密算法进行加密,这样便可以正确查找和加密密钥。
  • 使用 AES-256 GCM 加密,并且具有不确定性。

以下部分介绍了如何使用这些类对文件和共享偏好设置执行常见操作。

读取文件

以下代码段演示了如何使用 EncryptedFile 以更安全的方式读取文件内容:

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"
    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";
    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();
    }
    

写入文件

以下代码段演示了如何使用 EncryptedFile 以更安全的方式写入文件内容:

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.openFileOutput().bufferedWriter().use {
        it.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 OutputStreamWriter(
                encryptedFile.openFileOutput()));
        writer.write("MY SUPER-SECRET INFORMATION");
    } catch (GeneralSecurityException gse) {
        // Error occurred getting or creating keyset.
    } catch (IOException ex) {
        // Error occurred opening file for writing.
    }
    

对于需要额外安全性的用例,请完成以下步骤:

  1. 创建一个 KeyGenParameterSpec.Builder 对象,将 true 传递到 setUserAuthenticationRequired(),并将一个大于 0 的值传递到 setUserAuthenticationValidityDurationSeconds()
  2. 使用 createConfirmDeviceCredentialIntent() 提示用户输入凭据。详细了解如何要求用户验证身份才能使用密钥

  3. 替换 onActivityResult() 以获取经确认的凭据回调。

如需了解详情,请参阅要求用户验证身份才能使用密钥

修改共享偏好设置

以下代码段演示了如何使用 EncryptedSharedPreferences 以更安全的方式修改用户的一组共享偏好设置:

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