是什么
Jetpack Security 是 Google I/O 2019 发布的安全组件库。Security构成简单,主要包含EncryptedFile 和EncryptedSharedPreferences 两个类,分别用来对File 和SharedPreferences 的读写进行加密解密处理。Security要求min SDK version 23。
EncryptedFile 封装了Google的加密库tink 的逻辑,提供FileInputStream 和FileOutputStream ,可以更安全的进行流的读写。 EncryptedSharedPreferences 是SharedPreferences 包装类,通过两种方式自动加密键/值: Key加密使用的是确定性的加密算法,使得秘钥可以被加密 Value加密使用AES-256 GCM加密,不确定加密
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 完全兼容。
2018年9月3日,Google宣布了一个新的加密库叫Tink ,称 Tink 旨在提供安全、易于使用和难以误用的加密 API,它是基于现有的加密库 BoringSSL( 是谷歌创建的 OpenSSL 分支) 和 Java Cryptography Architecture(Java加密体系结构),加入了Project Wycheproof(它包含一系列安全测试,用来检测加密库(cryptographic libraries)软件是否存在各种已知漏洞。这些软件负责对存储于设备、或互联网上传输的数据进行加密。)项目发现的弱点反制措施。
如果您存储的数据特别敏感或私密,请考虑使用 Security 库中提供的 EncryptedFile 对象,而不要使用 File 对象。
使用
声明依赖项
如需添加 Security 的依赖项,您必须将 Google Maven 代码库添加到项目中。如需了解详情,请参阅 Google 的 Maven 代码库。
在应用或模块的 build.gradle 文件中添加所需工件的依赖项:
dependencies {
implementation "androidx.security:security-crypto:1.0.0"
implementation "androidx.security:security-identity-credential:1.0.0-alpha03"
implementation "androidx.security:security-app-authenticator:1.0.0-alpha02"
androidTestImplementation "androidx.security:security-app-authenticator:1.0.0-alpha01"
}
秘钥管理
Security库秘钥管理分为两个部分:
秘钥集合(Key set) 包含一个或多个秘钥来加密文件或SharedPreferences数据,存储在SharedPreferences中。 主密钥(Master Key) 用来加密所有秘钥集合,存储在Android Keystore系统中 使用Android Keystore的包装类MasterKeys只用两行就可以制作Master Key。
val keyGenParameterSpec = MasterKeys.AES256_GCM_SPEC
val masterKeyAlias = MasterKeys.getOrCreate(keyGenParameterSpec)
EncryptedSharedPreferences
普通SharedPreferences
val data = getSharedPreferences("SPNormal", Context.MODE_PRIVATE)
val editor = data.edit()
editor.putInt("IntSave", 10)
editor.apply()
val intSaved = data.getInt("IntSave", 1)
Log.e("IntSave", intSaved.toString())
key和value都被明文保存在xml中 SPNormal
使用EncryptedSharedPreferences
val keyGenParameterSpec = MasterKeys.AES256_GCM_SPEC
val masterKeyAlias = MasterKeys.getOrCreate(keyGenParameterSpec)
val sharedPreferences = EncryptedSharedPreferences
.create(
"SPEncrypted",
masterKeyAlias,
this,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
val editor = sharedPreferences.edit()
editor.putInt("IntSave", 10)
editor.apply()
val intSaved = sharedPreferences.getInt("IntSave", 1)
Log.e("IntSave", intSaved.toString())
key和value都被明文保存在xml中 SPEncrypted
性能对比
性能上有10倍以上的劣化
EncryptedFile
Write File
例如向text 文件中中写入 "MY SUPER SECRET INFORMATION" 字符串
val fileToWrite = "my_other_sensitive_data.txt"
val encryptedFile = EncryptedFile.Builder(
File(filesDir, fileToWrite),
this,
masterKeyAlias,
EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB
).build()
try {
val outputStream: FileOutputStream? = encryptedFile.openFileOutput()
outputStream?.apply {
write("MY SUPER SECRET INFORMATION"
.toByteArray(Charset.forName("UTF-8")))
flush()
close()
}
} catch (ex: IOException) {
}
Read File
通过EncryptedFile可以输出明文”MY SUPER SECRET INFORMATION“; 仅使用BufferedReader则会输出不可读的密文
val fileToRead = "my_sensitive_data.txt"
lateinit var byteStream: ByteArrayOutputStream
val encryptedFile = EncryptedFile.Builder(
File(context.getFilesDir(), fileToRead),
context,
masterKeyAlias,
EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB
).build()
try {
encryptedFile.openFileInput().use { fileInputStream ->
try {
val sb = StringBuilder()
val br = BufferedReader(InputStreamReader(fileInputStream) as Reader?)
br.readLine()
.forEach {
sb.append(it)
}
br.close()
Log.d("fileContents", sb.toString())
Toast.makeText(this, sb.toString(), Toast.LENGTH_SHORT).show()
} catch (ex: Exception) {
} finally {
fileInputStream.close()
}
}
} catch (ex: IOException) {
}
|