Làm việc với dữ liệu theo cách an toàn hơn

  Thuộc Android Jetpack.

Thư viện bảo mật cho phép triển khai các phương pháp hay nhất về bảo mật liên quan đến việc đọc và ghi dữ liệu tĩnh, cũng như việc tạo và xác minh khoá.

Thư viện sử dụng mẫu trình tạo để cung cấp chế độ cài đặt mặc định an toàn cho các mức bảo mật sau:

  • Mức bảo mật mạnh cân bằng giữa phương thức mã hoá hiệu quả và hiệu suất cao. Cấp độ bảo mật này phù hợp với các ứng dụng dành cho người dùng thông thường, chẳng hạn như các ứng dụng ngân hàng và trò chuyện, cũng như các ứng dụng doanh nghiệp để thực hiện quy trình kiểm tra việc thu hồi chứng chỉ.
  • Mức bảo mật tối đa. Cấp độ bảo mật này phù hợp với các ứng dụng yêu cầu kho khoá dựa trên phần cứng và sự hiện diện của người dùng để cung cấp quyền truy cập khoá.

Hướng dẫn này trình bày cách làm việc với các cấu hình bảo mật đề xuất của thư viện Bảo mật, cũng như cách đọc và ghi dữ liệu đã mã hoá được lưu trữ trong các tệp và các lựa chọn ưu tiên dùng chung.

Quản lý khoá

Thư viện Bảo mật sử dụng hệ thống gồm 2 phần để quản lý khoá:

  • Một tập hợp khoá chứa một hoặc nhiều khoá để mã hoá một tệp hoặc dữ liệu về các lựa chọn ưu tiên dùng chung. Tập hợp khoá sẽ được lưu trữ trong SharedPreferences.

  • Khoá chính (master) mã hoá tất cả các bộ khoá. Khoá này được lưu trữ bằng hệ thống kho khoá của Android.

Các lớp có trong thư viện

Thư viện bảo mật chứa các lớp sau đây để cung cấp thêm dữ liệu tĩnh an toàn:

EncryptedFile

Cung cấp các biện pháp triển khai tuỳ chỉnh của FileInputStreamFileOutputStream, giúp cho các ứng dụng của bạn được an toàn hơn trong việc truyền trực tuyến các thao tác đọc và ghi.

Để cung cấp các thao tác đọc và ghi an toàn từ luồng truyền tệp (file streams), Thư viện bảo mật sử dụng phương thức gốc Mã hoá truyền trực tuyến đã xác thực với dữ liệu liên kết (AEAD). Tìm hiểu thêm về phương thức gốc này trong tài liệu về thư viện Tink trên GitHub.

EncryptedSharedPreferences

Gói lớp SharedPreferences và tự động mã hoá các khoá và giá trị bằng phương thức hai lược đồ:

  • Khoá được mã hoá bằng một thuật toán mã hoá xác định để khoá có thể được mã hoá và tra cứu đúng cách.
  • Giá trị được mã hoá bằng AES-256 GCM và là giá trị không xác định.

Các phần sau đây cho biết cách sử dụng các lớp này để thực hiện các thao tác phổ biến với tệp và tuỳ chọn chung.

Đưa thư viện vào dự án của bạn

Để sử dụng thư viện Bảo mật, hãy thêm các phần phụ thuộc sau vào tệp build.gradle của mô-đun ứng dụng nếu thích hợp:

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 tệp

Đoạn mã sau đây minh hoạ cách dùng EncryptedFile để đọc nội dung của tệp:

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

Ghi tệp

Đoạn mã sau đây minh hoạ cách dùng EncryptedFile để ghi nội dung của tệp:

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

Với trường hợp sử dụng yêu cầu tăng cường mức bảo mật, hãy hoàn thành các bước sau:

  1. Tạo đối tượng KeyGenParameterSpec.Builder, chuyển true vào setUserAuthenticationRequired() và một giá trị lớn hơn 0 làm đối số đầu tiên của setUserAuthenticationParameters().
  2. Nhắc người dùng nhập thông tin xác thực bằng createConfirmDeviceCredentialIntent(). Tìm hiểu thêm về cách yêu cầu việc xác thực người dùng để sử dụng khoá.

  3. Ghi đè onActivityResult() để nhận lệnh gọi lại thông tin xác thực đã xác nhận.

Chỉnh sửa các lựa chọn ưu tiên dùng chung

Đoạn mã sau đây minh hoạ cách dùng EncryptedSharedPreferences để chỉnh sửa tập hợp các lựa chọn ưu tiên dùng chung của người dùng:

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

Hỗ trợ các thiết bị chạy Android 5.0 và Android 5.1

Phiên bản 1.1.0 của thư viện Bảo mật cho phép bạn hỗ trợ các thiết bị chạy Android 5.0 (API cấp 21) trở lên. Trên Android 5.0 và Android 5.1 (API cấp 22), bạn không thể dùng kho khoá Android để lưu trữ các bộ khoá.

Đọc tệp

Đoạn mã sau đây minh hoạ cách dùng EncryptedFile để đọc nội dung của tệp bằng phiên bản 1.1.0 của thư viện Bảo mật:

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

Ghi tệp

Đoạn mã sau đây minh hoạ cách dùng EncryptedFile để ghi nội dung của tệp bằng thư viện Bảo mật phiên bản 1.1.0:

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

Chỉnh sửa các lựa chọn ưu tiên dùng chung

Đoạn mã sau đây minh hoạ cách dùng EncryptedSharedPreferences để chỉnh sửa tập hợp các lựa chọn ưu tiên dùng chung của người dùng bằng thư viện Bảo mật phiên bản 1.1.0:

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