同sharedUserId下EncryptedSharedPreferences失效,如何让双应用各用独立实例?
核心思路
同android:sharedUserId的应用会共享系统默认密钥库,导致EncryptedSharedPreferences的Master Key互相覆盖冲突。要让两款应用独立使用加密存储,关键是让它们的Master Key完全隔离,同时保留sharedUserId用于数据交互。
方案1:为Free/Pro指定独立的Master Key别名
EncryptedSharedPreferences默认使用固定别名生成Master Key,同sharedUserId下会产生冲突。为两款应用分别设置唯一的Master Key别名即可解决:
- Free版代码示例:
// 生成Free专属的Master Key别名 val freeMasterKeyAlias = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC, "free_app_unique_master_key") // 创建Free版的加密SharedPreferences val freeEncryptedPrefs = EncryptedSharedPreferences.create( "free_secure_prefs", freeMasterKeyAlias, context, EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV, EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM )
- Pro版代码示例:
// 生成Pro专属的Master Key别名 val proMasterKeyAlias = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC, "pro_app_unique_master_key") // 创建Pro版的加密SharedPreferences val proEncryptedPrefs = EncryptedSharedPreferences.create( "pro_secure_prefs", proMasterKeyAlias, context, EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV, EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM )
这个方法最简单有效,两款应用的Master Key会在共享密钥库中以不同别名存储,互不干扰。
方案2:自定义密钥库存储路径(备选)
如果别名方案仍有冲突,可以彻底隔离密钥库文件:
为Free和Pro分别指定不同的密钥库存储路径(比如各自应用的私有文件目录),然后基于自定义KeyStore创建Master Key。示例逻辑:
// Free版自定义KeyStore加载 val freeKeyStoreFile = File(context.filesDir, "free_keystore.bks") val freeKeyStore = KeyStore.getInstance("BKS") freeKeyStore.load(FileInputStream(freeKeyStoreFile), "free_keystore_password".toCharArray()) // 基于自定义KeyStore生成Master Key并创建EncryptedSharedPreferences // 后续逻辑类似方案1,只是使用自定义KeyStore来管理密钥
Pro版同理使用pro_keystore.bks作为密钥库文件,完全隔离存储。
方案3:迁移时精准读取Free版加密数据
在Pro版执行迁移操作时,必须使用Free版的Master Key别名和SharedPreferences名称来读取数据:
// Pro版中获取Free应用的上下文 val freeContext = context.createPackageContext("com.yourpackage.free", Context.CONTEXT_IGNORE_SECURITY) // 使用Free版的Master Key别名读取加密配置 val freeMasterKeyAlias = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC, "free_app_unique_master_key") val freeEncryptedPrefs = EncryptedSharedPreferences.create( "free_secure_prefs", freeMasterKeyAlias, freeContext, EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV, EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM ) // 读取并迁移密码 val userPassword = freeEncryptedPrefs.getString("saved_password", null) // 写入Pro版的加密存储 val proMasterKeyAlias = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC, "pro_app_unique_master_key") val proEncryptedPrefs = EncryptedSharedPreferences.create( "pro_secure_prefs", proMasterKeyAlias, context, EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV, EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM ) proEncryptedPrefs.edit().putString("saved_password", userPassword).apply()
注意事项
- 确保两款应用的
android:sharedUserId保持一致,才能正常进行跨应用数据读取。 - 已经发布的Free版用户,需要确保迁移逻辑能兼容旧版本的加密存储(如果之前用的是默认别名,迁移时要先尝试默认别名读取,再切换到新别名)。
内容的提问来源于stack exchange,提问作者almisoft
相关产品推荐
相关产品推荐

