Jetpack Security (JetSec) là thư viện được xây dựng từ Tink - dự án mã nguồn mở, bảo mật đa nền tảng của Google. Jetpack Security được sử dụng cho việc mã hoá File và SharedPreferences.
Sử dụng EncryptedFile
và EncryptedSharedPreferences
cho phép chúng ta bảo vệ các tệp local chứa nhưngx thông tin nhạy cảm, API key, OAuth token và các thông tin bí mật khác.
Tạo Key
Trước khi chúng ta tiến hành mã hoá dữ liệu, ta cần hiểu cách mà các Key mã hóa sẽ được giữ an toàn. Jetpack Security sử dụng một khóa chính, nó mã hóa tất cả các khóa con được sử dụng cho mỗi hoạt động mã hoá riêng. JetSec cung cấp một khóa chính mặc định được đề xuất trong lớp MasterKeys
. Lớp này sử dụng khóa AES256-GCM
cơ bản, khoá này được tạo và lưu trữ trong AndroidKeyStore. AndroidKeyStore là một container lưu trữ các khóa mật mã trong TEE hoặc StrongBox, rất khó có thể giải nén chúng. Các khóa con được lưu trữ trong một SharedPreferences
object mà có thể config được.
val keyAlias = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC)
Đối với các ứng dụng yêu cầu thêm config đặc biệt hoặc xử lý dữ liệu rất nhạy cảm, ta sẽ dùng đến KeyGenParameterSpec
, chọn các option phù hợp với mỗi trường hợp sử dụng. Các khóa có giới hạn thời gian với BiometricPrompt
giúp tăng thêm cấp độ bảo vệ chống lại các thiết bị đã bị root hoặc bị xâm phạm.
Các option:
userAuthenticationRequired()
vàuserAuthenticationValiditySeconds()
có thể được sử dụng để tạo khóa giới hạn thời gian. Các khóa có giới hạn thời gian sẽ yêu cầu ủy quyền bằngBiometricPrompt
cho cả mã hóa và giải mã.unlockDeviceRequired()
gắn thêm cờ này sẽ giúp đảm bảo việc truy cập khóa không thể xảy ra nếu thiết bị không được mở khóa (cờ này chỉ khả dụng từ Android Pie trở lên) .setIsStrongBoxBacked()
, chạy mã hoá trên một chip riêng biệt mạnh hơn. Nó sẽ có một chút ảnh hưởng đến hiệu suất, nhưng an toàn hơn (khả dụng từ Android Pie trở lên) .
// Custom Advanced Master Key
val advancedSpec = KeyGenParameterSpec.Builder( "master_key", KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT
).apply { setBlockModes(KeyProperties.BLOCK_MODE_GCM) setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE) setKeySize(256) setUserAuthenticationRequired(true) setUserAuthenticationValidityDurationSeconds(15) // must be larger than 0 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { setUnlockedDeviceRequired(true) setIsStrongBoxBacked(true) }
}.build() val advancedKeyAlias = MasterKeys.getOrCreate(advancedSpec)
Unlock Key giới hạn thời gian
Ta sẽ phải sử dụng BiometricPrompt
để cấp quyền cho thiết bị nếu khóa được tạo với các option:
userAuthenticationRequired
giá trịtrue
userAuthenticationValiditySeconds
> 0
Sau khi người dùng xác thực, khóa sẽ được mở khóa trong khoảng thời gian được đặt trong trường giây hợp lệ.
// Create BiometricPrompt instance in onCreate val biometricPrompt = BiometricPrompt( this, // Activity ContextCompat.getMainExecutor(this), authenticationCallback
) private val authenticationCallback = object : AuthenticationCallback() { override fun onAuthenticationSucceeded( result: AuthenticationResult ) { super.onAuthenticationSucceeded(result) // Unlocked -- do work here. } override fun onAuthenticationError( errorCode: Int, errString: CharSequence ) { super.onAuthenticationError(errorCode, errString) // Handle error. } } // To use
val promptInfo = PromptInfo.Builder() .setTitle("Unlock?") .setDescription("Would you like to unlock this key?") .setDeviceCredentialAllowed(true) .build()
biometricPrompt.authenticate(promptInfo)
Mã hoá File
Jetpack Security cung cấp lớp EncryptedFile
loại bỏ những thách thức trong việc mã hoá file trước đây. Giống như File
, EncryptedFile
cung cấp một đối tượng FileInputStream
để đọc và một đối tượng FileOutputStream
để ghi.
Các tệp được mã hóa bằng Streaming AEAD, tuân theo định nghĩa OAE2. Dữ liệu được chia thành nhiều phần và được mã hóa bằng AES256-GCM theo cách không thể sắp xếp lại.
val secretFile = File(filesDir, "super_secret")
val encryptedFile = EncryptedFile.Builder( secretFile, applicationContext, advancedKeyAlias, FileEncryptionScheme.AES256_GCM_HKDF_4KB) .setKeysetAlias("file_key") // optional .setKeysetPrefName("secret_shared_prefs") // optional .build() encryptedFile.openFileOutput().use { outputStream -> // Write data to your encrypted file
} encryptedFile.openFileInput().use { inputStream -> // Read data from your encrypted file
}
Mã hoá SharedPreferences
Nếu ta cần lưu các cặp Key-Value, chẳng hạn như API key, thì JetSec cung cấp lớp EncryptedSharedPreferences
, lớp này sử dụng cùng interface giống như SharedPreferences
mà ta thường sử dụng.
Cả Key và Value đều được mã hóa. Các Key được mã hóa bằng AES256-SIV-CMAC, cung cấp một văn bản mật mã xác định; các Value được mã hóa bằng AES256-GCM và được liên kết với Key được mã hóa. Việc sử dụng phương pháp mã hoá này cho phép dữ liệu quan trọng được mã hóa một cách an toàn, trong khi vẫn cho phép tra cứu.
EncryptedSharedPreferences.create( "my_secret_prefs", advancedKeyAlias, applicationContext, PrefKeyEncryptionScheme.AES256_SIV, PrefValueEncryptionScheme.AES256_GCM
).edit { // Update secret values
}
Nguồn tham khảo
https://medium.com/androiddevelopers/data-encryption-on-android-with-jetpack-security-e4cb0b2d2a9
https://github.com/android/security-samples/tree/master/FileLocker