EncryptedSharedPreferences 解析:Android 安全存储
敏感数据存在 SharedPreferences 里并不安全。EncryptedSharedPreferences 是 Android 官方推荐的安全存储方案,它如何实现「即使手机被Root也能保护数据」?
在 Android 开发中,SharedPreferences 是最常用的轻量级存储方案,但明文存储的特性让它不适合存放敏感数据—— token、用户信息、密钥等一旦被提取,后果不堪设想。EncryptedSharedPreferences 就是来解决这个问题的。
开发者文档中提供了SharedPreferences加密键值对的实例代码,其中使用MasterKeys来进行密钥管理,而在 MasterKeys 的文档中提示该类已废弃,应使用MasterKey.Builder来管理主密钥(版本说明:基于 Jetpack Security 1.1.0-alpha01),示例如下
// this is equivalent to using deprecated MasterKeys.AES256_GCM_SPEC
KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(
MASTER_KEY_ALIAS,
KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.setKeySize(KEY_SIZE)
.build();
MasterKey masterKey = new MasterKey.Builder(MainActivity.this)
.setKeyGenParameterSpec(spec)
.build();
EncryptedSharedPreferences.create(
MainActivity.this,
"your-app-preferences-name",
masterKey, // masterKey created above
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM);
密钥管理
KeyGenParameterSpec是android.security.keystore中的类,用于指定密钥的参数,相当于先制定一个规范,规范中指明密钥别名、密钥用途、加密模式等密钥属性,然后在生成密钥的时候直接使用指定的规范。我们重点需要关注的是,主密钥的生成和存储,也就是下面调用的MasterKey.Builder方法。
MasterKey.Builder就是用于生成MasterKey的构建器,最终生成密钥的方法是构建器里的build(),那么在build()里面做了什么事情,需要深入到源码里面去看看。
/**
* Builds a {@link MasterKey} from this builder.
* @return The master key.
*/
@NonNull
public MasterKey build() throws GeneralSecurityException, IOException {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
return buildOnM();
} else {
return new MasterKey(mKeyAlias, null);
}
}
private MasterKey buildOnM() throws GeneralSecurityException, IOException {
...
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && mRequestStrongBoxBacked) {
if (mContext.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_STRONGBOX_KEYSTORE)) {
builder.setIsStrongBoxBacked(true);
}
}
mKeyGenParameterSpec = builder.build();//完成KeyGenParameterSpec的构建
...
@SuppressWarnings("deprecation")
String keyAlias = MasterKeys.getOrCreate(mKeyGenParameterSpec);//按照Spec指定的参数创建密钥
return new MasterKey(keyAlias, mKeyGenParameterSpec);
}
我们以 Android 9.0 为参考,build中调用的是 buildOnM(),而在buildOnM()中会检查系统是否支持基于硬件的 StrongBox Keystore,如果支持,则调用setIsStrongBoxBacked(true)以设置该密钥由 StrongBox 安全芯片保护,至此密钥参数设置的最后一步完成,并返回一个KeyGenParameterSpec的实例,build()内容很简单,直接返回一个KeyGenParameterSpec实例,如下
public KeyGenParameterSpec build() {
return new KeyGenParameterSpec(
mKeystoreAlias,
mNamespace,
mKeySize,
......
mIsStrongBoxBacked,
mUserConfirmationRequired,
mUnlockedDeviceRequired,
mCriticalToDeviceEncryption);
}
}
而后返回到buildOnM()中,调用MasterKeys.getOrCreate(mKeyGenParameterSpec)创建主密钥并返回密钥别名字符串,然后逐级返回上层调用方法。
graph TB
subgraph 应用层
A[EncryptedSharedPreferences]
end
subgraph 加密层
A -->|AES256_SIV| B[PrefKeyEncryption]
A -->|AES256_GCM| C[PrefValueEncryption]
end
subgraph 密钥层
D[MasterKey.Builder] --> E[KeyGenParameterSpec]
E --> F[Android Keystore]
F -->|硬件支持| G[StrongBox Keystore]
end
B --> D
C --> D
核心流程
| 组件 | 作用 |
|---|---|
| MasterKey | 管理主密钥生命周期 |
| KeyGenParameterSpec | 定义密钥参数(算法、长度、用途) |
| Android Keystore | 安全存储密钥的硬件/软件模块 |
| AES256_SIV | 密钥加密(确定性加密) |
| AES256_GCM | 值加密(带认证) |
StrongBox Keystore 需要硬件支持(Android 9+),提供更高级别的安全保护,密钥存储在独立的安全芯片中。
总结
- EncryptedSharedPreferences 自动完成 key/value 的加密,使用 Android Keystore 保护主密钥
- MasterKey.Builder 是推荐的方式管理主密钥,支持 StrongBox 硬件加密
- AES256_SIV 用于加密 key(需要确定性加密以支持查找)
- AES256_GCM 用于加密 value(提供机密性和完整性)
即使设备被 Root,只要用户设置了锁屏密码,密钥就不会暴露。这是目前 Android 上保护敏感数据的最佳实践。