共计 4264 个字符,预计需要花费 11 分钟才能阅读完成。
作者 / Jon Markoff, Staff Developer Advocate, Android Security
您是否尝试过对利用中的数据进行加密?作为开发者,您想要爱护数据安全,并确保数据把握在其正当使用者的手中。然而,大多数 Android 开发者没有专门的平安团队来帮忙他们正确地加密利用数据。就算通过网络来搜寻如何加密数据,您失去的答案也可能曾经过期好几年了,找到的示例也难以保障准确性。
Jetpack Security (JetSec) 加密库为 Files 和 SharedPreferences 对象的加密操作提供了形象反对。该库应用了平安且使用宽泛的 密码学原语 (cryptographic primitives),强化了 AndroidKeyStore 的应用。应用 EncryptedFile 和 EncryptedSharedPreferences 能够让您在本地爱护可能蕴含敏感数据、API 密钥、OAuth 令牌和其余类型机密信息的文件。
从 5.0 开始,Android 会默认 对用户数据分区的内容进行加密,那您为什么还须要加密利用中的数据呢?这是因为在某些场合中,您可能须要额定的爱护。如果您的利用应用 共享存储 (shared storage),则应该对数据进行加密。如果您的利用解决敏感信息,包含但不限于个人身份可辨认信息 (Personally Identifiable Information, PII)、衰弱记录、财务信息或企业数据,那么您的利用应该对其主目录中的数据进行加密。如果可能,咱们建议您将此类信息与生物验证操作绑定,以提供额定的爱护。
Jetpack Security 基于 Tink,而 Tink 是 Google 的一个开源并反对跨平台的平安我的项目。如果您须要惯例加密、混合加密或相似的安全措施,那么 Tink 可能实用于您的我的项目。Jetpack Security 的数据结构与 Tink 齐全兼容。
密钥生成
在开始加密数据之前,首先要理解您的加密密钥是如何被爱护的。Jetpack Security 应用一个主密钥 (master key) 对所有的子密钥 (subkey) 进行加密,子密钥则被用于每个加密操作。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,抉择适宜您需要的选项。针对设施被 root 或受到篡改的状况,带有 BiometricPrompt 生物验证步骤的限时密钥能够提供更高级别的爱护。
重要选项:
- userAuthenticationRequired() 和 userAuthenticationValiditySeconds() 能够用来创立限时密钥。限时密钥须要通过 BiometricPrompt 取得受权,能力对对称密钥进行加密和解密。
- unlockedDeviceRequired() 能够设置一个标记,用于确保在设施未解锁时不会产生密钥拜访。该开关值在 Android 9 及更高版本上可用。
- 应用 setIsStrongBoxBacked(),即可在更弱小的独立芯片上运行加密操作。这会对性能带来轻微的影响,但更加平安。此性能在运行 Android 9 或更高版本的某些设施上可用。
留神 : 如果您的利用须要在后盾加密数据,则不应应用限时密钥或要求设施处于解锁状态,因为如果没有用户在场,您的操作将无奈实现。
// 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 值为 true
- userAuthenticationValiditySeconds > 0
在用户进行验证后,将基于无效秒数字段中给出时长解锁密钥。AndroidKeystore 没有用于查问密钥设置的 API,因而您的利用必须本人记录这些设置。您应该在展现受权界面的 Activity 的 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 对象用于写入。咱们应用遵循 OAE2 定义的 Streaming AHEAD 对文件进行加密。数据被分为多个区块,并应用 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
如果您的利用须要保留键值对 (例如 API 密钥),JetSec 提供了 EncryptedSharedPreferences 类,该类应用的是您所熟知的 SharedPreferences 接口。
键和值均会被加密。键应用能提供确定性密文的 AES256-SIV-CMAC 进行加密;值则应用 AES256-GCM 进行加密,并绑定到加密的键。该计划容许对机要数据进行平安加密,同时依然便于查问。
EncryptedSharedPreferences.create( | |
"my_secret_prefs", | |
advancedKeyAlias, | |
applicationContext, | |
PrefKeyEncryptionScheme.AES256_SIV, | |
PrefValueEncryptionScheme.AES256_GCM | |
).edit {// Update secret values} |
更多资源
FileLocker 是咱们筹备的一个示例利用,您能够在 Android Security GitHub 示例页面上找到它。这个利用很好地展现了应该如何应用 Jetpack Security 进行文件加密。
祝大家加密欢快!