Skip to content

Instantly share code, notes, and snippets.

@rogergcc
Last active January 9, 2024 04:57
Show Gist options
  • Save rogergcc/b761573433f39116dbf0758e28f39ce3 to your computer and use it in GitHub Desktop.
Save rogergcc/b761573433f39116dbf0758e28f39ce3 to your computer and use it in GitHub Desktop.
Encrypted SharedPreferences
import android.app.Application
import android.util.Log
import com.rogergcc.encryptedsharedpreferencessample.encrypt.EncryptedSharedPreferencesManager
import com.rogergcc.encryptedsharedpreferencessample.encrypt.SharedPreferencesMigrator
import com.rogergcc.encryptedsharedpreferencessample.preferences.SharedPreferencesManager
/**
* Created on enero.
* year 2024 .
*/
class BaseApp : Application() {
override fun onCreate() {
super.onCreate()
SharedPreferencesManager.getInstance(this, FILENAME_PREFERENCES)
EncryptedSharedPreferencesManager.getInstance(this, FILENAME_PREFERENCES)
migrateSharedPreferences(FILENAME_PREFERENCES, FILENAME_PREFERENCES)
}
companion object {
const val TAG = "BaseApp"
const val FILENAME_PREFERENCES: String = "app_prefs"
}
private fun migrateSharedPreferences(oldFileName: String, newFileName: String) {
Log.d(TAG, "migrateSharedPreferences: init")
if (sharedPreferencesExist(oldFileName)) {
Log.d(TAG, "migrateSharedPreferences: ANTERIORES")
val oldPrefs = SharedPreferencesManager.getInstance(this, oldFileName).getSharedPreferences()
val encryptedPrefs = EncryptedSharedPreferencesManager.getInstance(this,newFileName).getSharedPreferences()
val migrator = SharedPreferencesMigrator(oldPrefs, encryptedPrefs)
migrator.migrateAllData()
}else{
Log.d(TAG, "migrateSharedPreferences: Not SharedPreferences found ")
}
}
private fun sharedPreferencesExist(fileName: String): Boolean {
return try {
SharedPreferencesManager.getInstance(this, fileName).getSharedPreferences().all.isNotEmpty()
} catch (e: Exception) {
false
}
}
}
class EncryptedSharedPreferencesManager private constructor(
private val context: Context,
private val fileName: String,
) {
private var sharedPreferences: SharedPreferences? = null
init {
if (sharedPreferences == null) {
val keyGenParameterSpec = MasterKeys.AES256_GCM_SPEC
val masterKeyAlias: String
try {
masterKeyAlias = MasterKeys.getOrCreate(keyGenParameterSpec)
sharedPreferences = EncryptedSharedPreferences.create(
fileName,
masterKeyAlias,
context,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
} catch (e: Exception) {
e.printStackTrace()
}
}
}
fun getSharedPreferences(): SharedPreferences {
return sharedPreferences ?: throw IllegalStateException("SharedPreferences not initialized")
}
companion object {
private var instances = mutableMapOf<String, EncryptedSharedPreferencesManager>()
fun getInstance(context: Context, fileName: String): EncryptedSharedPreferencesManager {
return instances[fileName] ?: synchronized(this) {
instances[fileName] ?: EncryptedSharedPreferencesManager(
context.applicationContext,
fileName
).also {
instances[fileName] = it
}
}
}
}
}
class SharedPreferencesManager private constructor(private val context: Context, private val fileName: String) {
private var sharedPreferences: SharedPreferences? = null
init {
if (sharedPreferences == null) {
sharedPreferences = context.getSharedPreferences(fileName, Context.MODE_PRIVATE)
}
}
fun getSharedPreferences(): SharedPreferences {
return sharedPreferences ?: throw IllegalStateException("SharedPreferences not initialized")
}
companion object {
private var instances = mutableMapOf<String, SharedPreferencesManager>()
fun getInstance(context: Context, fileName: String): SharedPreferencesManager {
return instances[fileName] ?: synchronized(this) {
instances[fileName] ?: SharedPreferencesManager(context.applicationContext, fileName).also {
instances[fileName] = it
}
}
}
}
}
package com.rogergcc.encryptedsharedpreferencessample.encrypt
import android.content.SharedPreferences
import androidx.core.content.edit
/**
* Created on enero.
* year 2024 .
*/
class SharedPreferencesMigrator(
private val oldSharedPreferences: SharedPreferences,
private val newSharedPreferences: SharedPreferences
) {
companion object {
private const val CRYPTO_KEYSET_KEY = "__androidx_security_crypto_encrypted_prefs_key_keyset__"
private const val CRYPTO_VALUE_KEYSET_KEY = "__androidx_security_crypto_encrypted_prefs_value_keyset__"
}
fun migrateAllData() {
val allKeys = oldSharedPreferences.all.keys.toList()
val allData = mutableMapOf<String, Any?>()
allKeys.forEach { key ->
allData[key] = getValueFromOldPreferences(key)
}
clearOldPreferencesExceptKeys(oldSharedPreferences, setOf(CRYPTO_KEYSET_KEY, CRYPTO_VALUE_KEYSET_KEY))
allData.forEach { (key, value) ->
migrateKey(key, value)
}
}
private fun migrateKey(key: String, value: Any?) {
// Excluir las claves relacionadas con el cifrado
if (!isCryptoKey(key)) {
when (value) {
is String -> newSharedPreferences.edit().putString(key, value).apply()
is Int -> newSharedPreferences.edit().putInt(key, value).apply()
is Boolean -> newSharedPreferences.edit().putBoolean(key, value).apply()
is Long -> newSharedPreferences.edit().putLong(key, value).apply()
is Float -> newSharedPreferences.edit().putFloat(key, value).apply()
is Set<*> -> newSharedPreferences.edit().putStringSet(key, value as Set<String>).apply()
}
}
}
private fun isCryptoKey(key: String): Boolean {
return key == CRYPTO_KEYSET_KEY || key == CRYPTO_VALUE_KEYSET_KEY
}
private fun clearOldPreferencesExceptKeys(
sharedPreferences: SharedPreferences,
keysToKeep: Set<String>
) {
sharedPreferences.edit {
sharedPreferences.all.keys.filter { it !in keysToKeep }.forEach { key ->
remove(key)
}
apply()
}
}
private fun getValueFromOldPreferences(key: String): Any? {
return oldSharedPreferences.all[key]
}
}
@rogergcc
Copy link
Author

rogergcc commented Jan 9, 2024

Sample
Migrate SharedPreference to EncryptedSharedPreferences
v.1.0,0 SharedPreferences to v1.1.0 EncryptedSharedPreferences

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment