Android 官方推薦使用 Jetpack Security 進行數據加密

作為一名開發人員,您希望確保數據的安全,并使其掌握在打算使用的一方手中。但是,如果你像大多數Android開發者一樣,你沒有專門的安全團隊來幫助正確加密你的應用程序的數據。通過搜索Web來了解如何加密數據,您可能會得到一些已經過時好幾年的答案,并提供錯誤的示例。
Jetpack Security(JetSec)加密庫對Files和SharedPreferences讀寫進行加密解密處理。該庫促進了AndroidKeyStore的使用,同時使用了安全且眾所周知的加密原語。使用EncryptedFile和EncryptedSharedPreferences可讓您本地保護可能包含敏感數據,API密鑰,OAuth令牌和其他類型機密的文件
Jetpack Security基于Tink,它是Google的一個開源,跨平臺安全項目。如果您需要常規加密、混合加密或類似的加密,Tink可能是合適的。Jetpack Security數據結構與Tink完全兼容。
密鑰生成
Jetpack Security使用一個主密鑰,該主密鑰對用于每個加密操作的所有子密鑰進行加密。JetSec在MasterKeys類中提供了推薦的默認主密鑰。此類使用基本的AES256-GCM密鑰,該密鑰已生成并存儲在AndroidKeyStore中。AndroidKeyStore是一個在TEE或StrongBox中存儲加密密鑰的容器,這使得它們很難提取。子項存儲在可配置的SharedPreferences對象中。
首先,我們使用Jetpack Security中的AES256_GCM_SPEC規范,推薦用于一般用例。AES256-GCM是對稱的,在現代設備上通常速度很快。
val keyAlias = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC)
對于需要更多配置或處理非常敏感數據的應用,建議構建您的KeyGenParameterSpec,選擇適合您使用的選項。帶有BiometricPrompt的時限密鑰可以提供更高級別的保護,以防止設備被盜用。
重要選項:
userAuthenticationRequired()和userAuthenticationValiditySeconds()用于創建有時間限制的密鑰。限時密鑰需要BiometricPrompt用于對稱密鑰的加密和解密的授權。unlockedDeviceRequired()設置一個標志,該標志有助于確保在設備未解鎖的情況下不會發生密鑰訪問。此標志在Android Pie及更高版本中可用。- 使用
setIsStrongBoxBacked(),可以在功能更強大的單獨芯片上運行加密操作。這對性能有輕微影響,但更安全。它可以在一些運行Android Pie或更高版本的設備上使用。
注意:如果您的應用需要在后臺加密數據,你不應該使用有時間限制的密鑰或要求設備解鎖,因為如果沒有用戶在場,你將無法完成這一任務。
// 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)
解鎖有時間限制的密鑰
如果您的密鑰是使用以下選項創建的,則必須使用BiometricPrompt對設備進行授權:
- userAuthenticationRequired is true
- userAuthenticationValiditySeconds > 0
用戶驗證后,將在有效秒字段中設置的時間量內解鎖密鑰。AndroidKeystore沒有用于查詢鍵設置的API,因此您的應用必須跟蹤這些設置。您應該使用onCreate()將對話框呈現給用戶的活動方法來構建BiometricPrompt實例。
用于解鎖時間限制密鑰的BiometricPrompt代碼:
// Activity.onCreate
val promptInfo = PromptInfo.Builder()
.setTitle("Unlock?")
.setDescription("Would you like to unlock this key?")
.setDeviceCredentialAllowed(true)
.build()
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:
biometricPrompt.authenticate(promptInfo)
加密文件
Jetpack Security包含一個EncryptedFile類,它消除了加密文件數據的挑戰。與File類似,EncryptedFile提供一個FileInputStream對象用于讀取,一個FileOutputStream對象用于寫入。使用流式AEAD(遵循OAE2定義)對文件進行加密。數據被分為多個塊,并使用AES256-GCM進行加密,從而無法重新排序。
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
}
加密SharedPreferences
如果您的應用程序需要保存Key-value(例如 API 密鑰),JetSec提供EncryptedSharedPreferences類,它使用與您習慣的SharedPreferences接口相同的接口。
Key和value均被加密。密鑰使用提供確定性密文的AES256-SIV-CMAC加密。值使用AES256-GCM加密,并綁定到加密密鑰。此方案允許對密鑰數據進行安全加密,同時仍然允許查找。
EncryptedSharedPreferences.create(
"my_secret_prefs",
advancedKeyAlias,
applicationContext,
PrefKeyEncryptionScheme.AES256_SIV,
PrefValueEncryptionScheme.AES256_GCM
).edit {
// Update secret values
}