19、Security-Crypto加密库:文件级加密与密钥管理
说到Android安全,很多人第一反应就是网络传输要加密。但说实话,本地存储的安全往往更容易被忽视。我见过不少项目,数据库里存着用户隐私,SharedPreferences里躺着Token,结果反编译APK就能直接读出来——这太危险了。
Android官方其实早就给了我们一套完整的本地加密方案,就是Security-Crypto库。它封装了文件级加密、密钥管理,还集成了Android Keystore系统。今天我就带你彻底搞懂它。
核心要点:Security-Crypto库不是让你自己写加密算法,而是提供了一套开箱即用的安全存储方案。你只需要关心业务逻辑,加密细节交给它。
19.1 为什么需要文件级加密?
先问个问题:你的App里哪些数据需要保护?
- 用户登录Token、Session ID
- 支付信息、银行卡号
- 聊天记录、私密文件
- App配置、业务敏感数据
这些数据如果明文存储,一旦手机被root或者被恶意应用读取,后果很严重。我曾在一次安全审计中发现,某个知名App把用户身份证号直接写在SharedPreferences里——嗯,那个版本很快就被紧急下架了。
文件级加密的好处在于:即使攻击者拿到了文件,没有密钥也读不出内容。而且加密和解密对开发者几乎是透明的,你只需要在读写时调用对应的API。
19.2 EncryptedSharedPreferences:安全的首选
SharedPreferences是我们最常用的轻量级存储方案。但默认情况下,它存的是明文。如果你在里头存了敏感信息,那基本等于把钥匙挂在门上。
EncryptedSharedPreferences就是它的安全升级版。它使用AES-256加密数据,密钥由Android Keystore保护。
19.2.1 基本使用
先加依赖:
implementation "androidx.security:security-crypto:1.1.0-alpha06"
然后创建实例:
val masterKey = MasterKey.Builder(context)
.setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
.build()
val sharedPreferences = EncryptedSharedPreferences.create(
context,
"secure_prefs",
masterKey,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
// 写入数据
sharedPreferences.edit().putString("token", "your_token_here").apply()
// 读取数据
val token = sharedPreferences.getString("token", null)
你看,使用方式和普通SharedPreferences几乎一样。唯一的区别是多了个MasterKey和加密方案的选择。
我的建议:项目中所有涉及用户身份、支付、隐私的SharedPreferences,都应该换成EncryptedSharedPreferences。性能开销很小,安全收益却很大。
19.2.2 密钥方案选择
| 方案 | 说明 | 适用场景 |
|---|---|---|
| AES256_SIV | 密钥加密,确定性加密 | 适合Key的加密,因为Key需要可查询 |
| AES256_GCM | 值加密,带认证标签 | 适合Value的加密,提供完整性校验 |
这里有个细节:Key的加密用了SIV模式,因为它是确定性的——同样的Key加密结果相同,这样你才能通过Key查询到对应的值。而Value用GCM模式,每次加密结果不同,安全性更高。
19.3 EncryptedFile:大文件的安全读写
SharedPreferences适合小数据,但如果你要存图片、视频、数据库文件,就得用EncryptedFile了。
它同样基于AES-256加密,支持流式读写。这意味着即使文件很大,也不会一次性加载到内存。
19.3.1 写入加密文件
val masterKey = MasterKey.Builder(context)
.setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
.build()
val encryptedFile = EncryptedFile.Builder(
context,
File(filesDir, "secret_data.dat"),
masterKey,
EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB
).build()
encryptedFile.openFileOutput().use { outputStream ->
outputStream.write("这是加密内容".toByteArray())
}
19.3.2 读取加密文件
val encryptedFile = EncryptedFile.Builder(
context,
File(filesDir, "secret_data.dat"),
masterKey,
EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB
).build()
encryptedFile.openFileInput().use { inputStream ->
val content = inputStream.readBytes().toString(Charsets.UTF_8)
Log.d("EncryptedFile", "解密内容: $content")
}
注意:EncryptedFile的读写都是流式的,所以不要忘了用use块自动关闭流。我曾经在早期版本中忘记关闭,导致文件锁未释放,后续读写全部失败——排查了半天才发现是流没关。
19.4 Android Keystore系统集成
说了这么多,你可能好奇:密钥存在哪里?
答案是Android Keystore。这是一个硬件隔离的安全区域,密钥一旦生成,就无法从App进程中导出。即使手机被root,攻击者也拿不到密钥本身。
Security-Crypto库的MasterKey默认就是存储在Keystore中的。你不需要手动管理密钥生命周期,库帮你搞定了。
19.4.1 MasterKey的构建细节
val masterKey = MasterKey.Builder(context)
.setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
.setRequestStrongBoxBacked(true) // 要求使用硬件安全模块
.setUserAuthenticationRequired(true) // 要求用户生物识别验证
.setUserAuthenticationValidityDurationSeconds(300) // 验证有效时长
.build()
这里有几个选项值得注意:
- StrongBox:如果设备支持,密钥会存储在独立的硬件安全芯片中,安全性最高
- 用户认证:可以要求每次使用密钥前进行指纹或人脸验证,适合支付场景
- 有效期:用户认证后一段时间内可重复使用,避免频繁弹窗
避坑指南:我曾经在项目中开启了setUserAuthenticationRequired(true),但没设置有效期。结果每次读取SharedPreferences都要弹指纹——用户体验极差。后来改成300秒有效期,既安全又流畅。
19.5 知识体系总览
下面这张图帮你理清Security-Crypto库的整体架构:
从这张图可以清楚看到:应用层只管调用API,加密引擎处理算法细节,密钥管理层保证密钥安全,最底层的Keystore提供硬件级保护。每一层各司其职,你不需要关心底层实现。
19.6 最佳实践总结
最后,分享几条我在实战中总结的经验:
- 小数据用EncryptedSharedPreferences,大数据用EncryptedFile——别混用,也别滥用
- MasterKey尽量用默认配置,除非你有特殊的安全需求(比如支付场景需要用户认证)
- 不要自己存密钥——永远依赖Keystore,别把密钥写在代码里或Asset中
- 注意Android版本兼容:Security-Crypto库最低支持API 23(Android 6.0),如果你的App还要支持更低版本,需要额外处理
- 测试时记得处理异常:加密操作可能抛出
GeneralSecurityException,别让App直接崩溃
一个小技巧:如果你在调试阶段想查看加密文件的内容,可以用adb shell进到data目录,但你会发现文件内容全是乱码——这就是加密的效果。别慌,不是文件坏了。
好了,关于Security-Crypto加密库的核心内容就这些。说白了,它就是Android官方给我们的一把安全锁,用好了能挡住绝大多数本地数据泄露的风险。下次写代码时,记得问自己一句:这个数据需要加密吗?