具有 EncryptedSharedPreferences 的 AutoBackUp 无法恢复
AutoBackUp with EncryptedSharedPreferences failing to restore
我正在使用 EncryptedSharedPreferences
在本地存储用户信息(参见 this if you are not familiar). I have implemented AutoBackUp with BackUp rules. I backed up the preferences, cleared data on my app, and attempted to restore the data (following the steps outlined for backup and restore)。
查看 Android Studio 中的设备文件资源管理器,我可以确认我的首选项文件正在恢复(它已正确命名并且其中包含加密数据)。但是,我的应用程序运行时就好像首选项文件不存在一样。
我错过了什么?
首选项代码:
class PreferenceManager(context: Context) {
companion object {
private const val KEY_STORE_ALIAS = "APP_KEY_STORE"
private const val privatePreferences = "APP_PREFERENCES"
}
// See https://developer.android.com/topic/security/data#kotlin for more info
private val sharedPreferences = EncryptedSharedPreferences.create(
privatePreferences,
KEY_STORE_ALIAS,
context,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
init {
//val all = sharedPreferences.all
//for (item in all) {
//Log.e("PREFERENCES", "${item.key} - ${item.value}")
//}
}
@SuppressLint("ApplySharedPref")
fun clear() {
// Normally you want apply, but we need the changes to be done immediately
sharedPreferences.edit().clear().commit()
}
fun readBoolean(key: String, defaultValue: Boolean): Boolean {
return sharedPreferences.getBoolean(key, defaultValue)
}
fun readDouble(key: String): Double {
return sharedPreferences.getFloat(key, 0f).toDouble()
}
fun readString(key: String): String {
return sharedPreferences.getString(key, "")!!
}
fun removePreference(key: String) {
sharedPreferences.edit().remove(key).apply()
}
fun writeBoolean(key: String, value: Boolean) {
sharedPreferences.edit().putBoolean(key, value).apply()
}
fun writeDouble(key: String, value: Double) {
sharedPreferences.edit().putFloat(key, value.toFloat()).apply()
}
fun writeString(key: String, value: String) {
sharedPreferences.edit().putString(key, value).apply()
}
}
我目前没有实施 BackupAgent。
根据我的理解,Jetpack Security 依赖于在设备硬件上生成的密钥,因此您不能依赖原始密钥在备份恢复后仍然存在(考虑更改的设备)。
加密的安全性取决于密钥的安全性,只要它不能离开 Keystore 或设备,备份和恢复就无法自动进行(无需用户交互)。
我的方法 (1) 是您要求用户输入密码,根据该密码加密您的常规共享首选项(可能使用另一个加密库:例如 https://github.com/iamMehedi/Secured-Preference-Store),然后保存密码使用来自 Jetpack 的加密共享首选项。恢复备份后要求用户输入密码,用 Jetpack 再次保存并解密常规 SharedPreferences。
这样即使硬件密钥库发生变化,您也可以恢复备份。缺点是,用户需要记住密码。
我在我的应用程序中采用这种方法,只是不使用共享首选项(它们在我的用例中不合理),而是使用应用程序数据库。
另一种方法 (2) 是检查加密备份(可从 Pie 获得),如果您只关心云中的备份。使用这种方法,您不会在本地加密共享首选项,但默认情况下会加密备份。如果您需要本地加密,这种方法不适合您,但优点是,用户只需在恢复备份时输入 his/her 锁屏密码,之后一切都会恢复,无需进一步的用户交互。
如果您可以在没有本地加密的情况下生活,那么组合也是可以想象的并且更可取:方法 1 用于 pre-9,方法 2 用于 post-9.
正如@Floj12 提到的那样EncryptedSharedPreferences
使用密钥库并且您无法备份密钥库,因此当您的加密数据将被恢复时您将无法解密它。
Google 强迫两个不能一起工作的东西是非常可悲的。 Android 上的密钥库没有像 iOS 上的钥匙串那样的备份选项。
在这里我会给你更多的选择你如何备份数据:
- 存储用户数据一个后端
- 使用 user-stored 令牌解密备份
- 为所有应用设置静态密码
- 用户在设置中手动导出备份
我正在使用 EncryptedSharedPreferences
在本地存储用户信息(参见 this if you are not familiar). I have implemented AutoBackUp with BackUp rules. I backed up the preferences, cleared data on my app, and attempted to restore the data (following the steps outlined for backup and restore)。
查看 Android Studio 中的设备文件资源管理器,我可以确认我的首选项文件正在恢复(它已正确命名并且其中包含加密数据)。但是,我的应用程序运行时就好像首选项文件不存在一样。
我错过了什么?
首选项代码:
class PreferenceManager(context: Context) {
companion object {
private const val KEY_STORE_ALIAS = "APP_KEY_STORE"
private const val privatePreferences = "APP_PREFERENCES"
}
// See https://developer.android.com/topic/security/data#kotlin for more info
private val sharedPreferences = EncryptedSharedPreferences.create(
privatePreferences,
KEY_STORE_ALIAS,
context,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
init {
//val all = sharedPreferences.all
//for (item in all) {
//Log.e("PREFERENCES", "${item.key} - ${item.value}")
//}
}
@SuppressLint("ApplySharedPref")
fun clear() {
// Normally you want apply, but we need the changes to be done immediately
sharedPreferences.edit().clear().commit()
}
fun readBoolean(key: String, defaultValue: Boolean): Boolean {
return sharedPreferences.getBoolean(key, defaultValue)
}
fun readDouble(key: String): Double {
return sharedPreferences.getFloat(key, 0f).toDouble()
}
fun readString(key: String): String {
return sharedPreferences.getString(key, "")!!
}
fun removePreference(key: String) {
sharedPreferences.edit().remove(key).apply()
}
fun writeBoolean(key: String, value: Boolean) {
sharedPreferences.edit().putBoolean(key, value).apply()
}
fun writeDouble(key: String, value: Double) {
sharedPreferences.edit().putFloat(key, value.toFloat()).apply()
}
fun writeString(key: String, value: String) {
sharedPreferences.edit().putString(key, value).apply()
}
}
我目前没有实施 BackupAgent。
根据我的理解,Jetpack Security 依赖于在设备硬件上生成的密钥,因此您不能依赖原始密钥在备份恢复后仍然存在(考虑更改的设备)。
加密的安全性取决于密钥的安全性,只要它不能离开 Keystore 或设备,备份和恢复就无法自动进行(无需用户交互)。
我的方法 (1) 是您要求用户输入密码,根据该密码加密您的常规共享首选项(可能使用另一个加密库:例如 https://github.com/iamMehedi/Secured-Preference-Store),然后保存密码使用来自 Jetpack 的加密共享首选项。恢复备份后要求用户输入密码,用 Jetpack 再次保存并解密常规 SharedPreferences。 这样即使硬件密钥库发生变化,您也可以恢复备份。缺点是,用户需要记住密码。
我在我的应用程序中采用这种方法,只是不使用共享首选项(它们在我的用例中不合理),而是使用应用程序数据库。
另一种方法 (2) 是检查加密备份(可从 Pie 获得),如果您只关心云中的备份。使用这种方法,您不会在本地加密共享首选项,但默认情况下会加密备份。如果您需要本地加密,这种方法不适合您,但优点是,用户只需在恢复备份时输入 his/her 锁屏密码,之后一切都会恢复,无需进一步的用户交互。 如果您可以在没有本地加密的情况下生活,那么组合也是可以想象的并且更可取:方法 1 用于 pre-9,方法 2 用于 post-9.
正如@Floj12 提到的那样EncryptedSharedPreferences
使用密钥库并且您无法备份密钥库,因此当您的加密数据将被恢复时您将无法解密它。
Google 强迫两个不能一起工作的东西是非常可悲的。 Android 上的密钥库没有像 iOS 上的钥匙串那样的备份选项。
在这里我会给你更多的选择你如何备份数据:
- 存储用户数据一个后端
- 使用 user-stored 令牌解密备份
- 为所有应用设置静态密码
- 用户在设置中手动导出备份