EncryptedSharedPreferences es un componente de la biblioteca AndroidX Security que proporciona cifrado transparente de los datos guardados a través de la API SharedPreferences. A diferencia de las SharedPreferences normales, donde los datos se almacenan en un archivo XML sin cifrar, EncryptedSharedPreferences cifra automáticamente las claves y los valores antes de escribirlos en el disco. Según Android Developers, la biblioteca utiliza AES-256 GCM para los valores y AES-256 SIV (RFC 5297) para las claves, garantizando la confidencialidad e integridad de los datos.
Puntos Clave
EncryptedSharedPreferences es una clase del paquete androidx.security.crypto, presentada en AndroidX Security 1.0.0 (2019). Implementa la interfaz SharedPreferences, pero todas las operaciones de escritura (putString, putInt, putBoolean, etc.) cifran los datos de antemano, y las operaciones de lectura los descifran antes de devolverlos.
Las SharedPreferences estándar guardan los datos en un archivo XML en el directorio de la aplicación (/data/data/package/shared_prefs/). El archivo no está cifrado — con acceso root al dispositivo o durante el análisis de la copia de seguridad, todos los datos se leen como XML sin formato. Los tokens de autenticación, las claves API y los datos personales del usuario quedan accesibles para un atacante.
EncryptedSharedPreferences resuelve este problema a nivel de biblioteca: los datos se cifran antes de escribirlos en el disco y se descifran al leerlos. El desarrollador no necesita llamar a funciones criptográficas manualmente — la API sigue siendo idéntica a las SharedPreferences normales.
La biblioteca AndroidX Security v1.0.0 se lanzó en diciembre de 2019. EncryptedSharedPreferences reemplazó el enfoque obsoleto de cifrado manual mediante Cipher + SharedPreferences. La versión estable actual es 1.1.0-alpha06 (2024), compatible con API 19+. La biblioteca forma parte de Jetpack y no requiere permisos adicionales.
Según Google Security Blog (2024), EncryptedSharedPreferences es la forma recomendada de almacenar configuraciones confidenciales de la aplicación que no requieren sincronización en la nube. Para escenarios más complejos, se recomienda Room con cifrado SQLCipher.
EncryptedSharedPreferences utiliza un esquema de cifrado de dos niveles: la Master Key se almacena en Android Keystore y se utilizan claves derivadas para el cifrado de datos. Esto combina la protección de Keystore con el rendimiento del cifrado simétrico.
Para los valores se utiliza AES-256 GCM (modo Galois/Counter) — un modo de cifrado autenticado (AEAD) que garantiza la confidencialidad e integridad de los datos. Para las claves (nombres de parámetros) se aplica AES-256 SIV (RFC 5297) — cifrado determinista necesario para la búsqueda de claves sin revelar su contenido.
Cada archivo de EncryptedSharedPreferences contiene pares clave-valor cifrados. La estructura del archivo incluye: primero un encabezado con metadatos (versión, identificador de clave), luego una lista de entradas cifradas. El archivo no es XML válido y no se puede leer con editores de texto.
La clase MasterKey se encarga de crear y gestionar una clave maestra de 256 bits almacenada en Android Keystore. MasterKey.Builder permite configurar: el tipo de almacenamiento (Keystore o software), la protección biométrica y la vida útil de la clave. Por defecto, la clave maestra se genera en Android Keystore con el algoritmo AES/GCM/NoPadding.
import androidx.security.crypto.MasterKey
import androidx.security.crypto.EncryptedSharedPreferences
fun getEncryptedPrefs() {
val masterKey = MasterKey.Builder(context)
.setKeyScheme(MasterKey.AES256_GCM_SPEC)
.build()
val prefs = EncryptedSharedPreferences.create(
context,
"secure_prefs",
masterKey,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
}
EncryptedSharedPreferences.create acepta cinco parámetros: contexto, nombre de archivo, clave maestra, esquema de cifrado de claves y esquema de cifrado de valores. La elección de los esquemas afecta al rendimiento y al nivel de seguridad.
AES256_SIV — cifrado determinista: claves idénticas siempre producen el mismo texto cifrado. Esto es necesario para la búsqueda de claves (SharedPreferences.getX(key)). Inconveniente: un atacante puede determinar qué claves se utilizan comparando textos cifrados repetidos. AES256_SIV2 — una versión mejorada con aleatorización adicional.
Para los valores se utiliza AES256_GCM. GCM añade un IV de 12 bytes (vector de inicialización) y una etiqueta de autenticación de 16 bytes a cada valor. Esto proporciona confidencialidad (nadie puede leer el valor) y autenticación (nadie puede manipular el valor sin ser detectado).
El método setUserAuthenticationRequired(true) en MasterKey.Builder requiere confirmación biométrica antes de recuperar la clave maestra de Keystore. Esto añade una capa adicional: incluso si la aplicación se ejecuta en un dispositivo desbloqueado, un atacante no puede leer EncryptedSharedPreferences sin Face ID o Touch ID.
Importante: con setUserAuthenticationRequired, la clave maestra deja de estar disponible si el usuario cambia o elimina los datos biométricos. Debe manejar KeyPermanentlyInvalidatedException y crear una nueva clave maestra con migración de datos.
fun createBiometricKey(): MasterKey {
return MasterKey.Builder(context)
.setKeyScheme(MasterKey.AES256_GCM_SPEC)
.setUserAuthenticationRequired(true)
.setRequestStrongBoxBacked(true)
.build()
}
fun writeSecureToken(token: String) {
try {
prefs.edit().putString("auth_token", token).apply()
} catch (e: KeyPermanentlyInvalidatedException) {
// La biometría ha cambiado — es necesario recrear la clave
}
}
Veamos un ejemplo completo de integración de EncryptedSharedPreferences en una aplicación Android con Kotlin. La biblioteca androidx.security:security-crypto se añade a través de Gradle.
En el archivo build.gradle (app), añada: implementation "androidx.security:security-crypto:1.1.0-alpha06". Para proyectos Kotlin, también se requiere kotlin-stdlib. La inicialización de MasterKey ocurre una vez, normalmente en Application.onCreate o a través de un contenedor DI.
Después de crear una instancia de EncryptedSharedPreferences, la API no difiere de las SharedPreferences normales. edit() devuelve un Editor, todos los métodos (putString, getString, putBoolean, getBoolean) funcionan de la misma manera. La única diferencia es interna: los datos se cifran al escribir y se descifran al leer.
class AuthRepository(context: Context) {
private val prefs = createEncryptedPrefs(context)
fun saveCredentials(login: String, password: String) {
prefs.edit()
.putString("login", login)
.putString("password", password)
.apply()
}
fun getToken(): String? {
return prefs.getString("auth_token", null)
}
fun clearAll() {
prefs.edit().clear().apply()
}
}
Para migrar datos existentes desde SharedPreferences no cifradas a EncryptedSharedPreferences, es necesario: leer todos los datos del archivo antiguo, crear una nueva EncryptedSharedPreferences, escribir todos los datos y eliminar el archivo antiguo. Google no proporciona un migrador integrado — el desarrollador lo implementa manualmente.
La elección entre SharedPreferences y EncryptedSharedPreferences depende del tipo de datos almacenados. Para configuraciones de interfaz (tema, idioma, ordenación), las SharedPreferences normales son suficientes. Para información confidencial (tokens, contraseñas, claves), EncryptedSharedPreferences es obligatorio.
EncryptedSharedPreferences es más lento que el normal debido a las operaciones criptográficas. Escribir un solo valor de cadena toma ~5-15 ms (dependiendo del tamaño de los datos y la aceleración hardware AES). La lectura toma 2-5 ms. Para la mayoría de las aplicaciones esto es imperceptible, pero para operaciones por lotes (migración, restauración) use apply() en lugar de commit().
Las SharedPreferences normales no proporcionan ninguna protección criptográfica: el archivo XML puede ser leído por cualquier proceso con acceso root o mediante adb backup. EncryptedSharedPreferences cifra los datos a nivel de aplicación, y la clave maestra se almacena en Android Keystore con protección hardware opcional (StrongBox).
| Característica | SharedPreferences | EncryptedSharedPreferences |
|---|---|---|
| Almacenamiento | XML sin cifrar | Archivo binario cifrado |
| Cifrado | Ninguno | AES-256 GCM + SIV |
| Protección de claves | Ninguna | Android Keystore + StrongBox |
| Rendimiento | 0.1-1 ms | 2-15 ms |
| Recomendación | Configuración de UI | Tokens, claves, PII |
Use EncryptedSharedPreferences para almacenar: tokens de actualización OAuth, claves API para servicios externos, correo electrónico o número de teléfono del usuario, y configuraciones confidenciales de la aplicación (PIN, indicadores de autenticación). EncryptedSharedPreferences no es adecuado para almacenar datos biométricos o documentos grandes — use EncryptedFile o Room con SQLCipher.
La regla general: si una fuga de datos dañaría al usuario o al negocio — use EncryptedSharedPreferences. Si los datos son solo cosméticos (tema, idioma, ordenación) — SharedPreferences normales. Tiene sentido implementar EncryptedSharedPreferences desde el principio, sin refactorización: reemplazarlo en un proyecto existente requiere migración y manejo de datos antiguos no cifrados.
Recuerde que EncryptedSharedPreferences no protege los datos mientras la aplicación se ejecuta — solo en el disco. Si un atacante tiene acceso a la memoria del proceso, los datos descifrados pueden ser interceptados. Use protección adicional: ProGuard/DexGuard para ofuscación.
Preguntas Frecuentes
Jetpack DataStore es una alternativa más moderna a SharedPreferences, basada en Flow y corutinas de Kotlin. DataStore no cifra los datos por defecto, pero puede combinarse con EncryptedSharedPreferences o usarse con cifrado manual a través de Proto DataStore con protocolos criptográficos.
No se recomienda. EncryptedSharedPreferences está diseñado para volúmenes pequeños (hasta 100-200 KB). Para datos más grandes, use Room con SQLCipher o cifrado de archivos a través de EncryptedFile de la misma biblioteca AndroidX Security.
No, no hay migración de esquema automática. Al cambiar la estructura de datos, el desarrollador debe leer manualmente los datos antiguos a través del antiguo KeyGen y escribirlos a través del nuevo. Se recomienda almacenar la versión del esquema en un parámetro separado.
AndroidX Security 1.0.0 es compatible con API 19+ (Android KitKat). La versión 1.1.0-alpha06 también es compatible con API 19+. StrongBox requiere API 28+ y un dispositivo con soporte hardware (Google Pixel 3+, Samsung Galaxy S9+).
Sí, el refresh token es uno de los casos de uso principales. Cifrado AES-256 GCM, clave maestra en Keystore, protección biométrica — un nivel suficiente para tokens OAuth. Para tokens de acceso de corta duración también es adecuado, aunque algunos equipos prefieren almacenarlos en memoria.
Resumen
Desarrollaremos una aplicación móvil llave en mano
IT Sectr crea aplicaciones para iOS y Android para startups y empresas desde 2017. Le asesoraremos y le propondremos la mejor solución.
Lea también