EncryptedSharedPreferences è un componente della libreria AndroidX Security che fornisce una crittografia trasparente dei dati salvati tramite l'API SharedPreferences. A differenza delle SharedPreferences normali, dove i dati vengono memorizzati in un file XML in chiaro, EncryptedSharedPreferences crittografa automaticamente chiavi e valori prima di scriverli su disco. Secondo Android Developers, la libreria utilizza AES-256 GCM per i valori e AES-256 SIV (RFC 5297) per le chiavi, garantendo riservatezza e integrità dei dati.
Punti Chiave
EncryptedSharedPreferences è una classe del pacchetto androidx.security.crypto, introdotta in AndroidX Security 1.0.0 (2019). Implementa l'interfaccia SharedPreferences, ma tutte le operazioni di scrittura (putString, putInt, putBoolean, ecc.) crittografano i dati in anticipo e le operazioni di lettura li decrittografano prima di restituirli.
Le SharedPreferences standard salvano i dati in un file XML nella directory dell'app (/data/data/package/shared_prefs/). Il file non è crittografato — con accesso root al dispositivo o durante l'analisi del backup, tutti i dati vengono letti come XML in chiaro. I token di autenticazione, le chiavi API e i dati personali dell'utente diventano accessibili a un malintenzionato.
EncryptedSharedPreferences risolve questo problema a livello di libreria: i dati vengono crittografati prima di essere scritti su disco e decrittografati durante la lettura. Lo sviluppatore non deve chiamare manualmente funzioni crittografiche — l'API rimane identica alle SharedPreferences normali.
La libreria AndroidX Security v1.0.0 è stata rilasciata a dicembre 2019. EncryptedSharedPreferences ha sostituito l'approccio obsoleto della crittografia manuale tramite Cipher + SharedPreferences. La versione stabile attuale è 1.1.0-alpha06 (2024), che supporta API 19+. La libreria fa parte di Jetpack e non richiede autorizzazioni aggiuntive.
Secondo il Google Security Blog (2024), EncryptedSharedPreferences è il metodo consigliato per memorizzare impostazioni sensibili dell'app che non richiedono sincronizzazione cloud. Per scenari più complessi, si consiglia Room con crittografia SQLCipher.
EncryptedSharedPreferences utilizza uno schema di crittografia a due livelli: la Master Key è memorizzata in Android Keystore e chiavi derivate vengono utilizzate per la crittografia dei dati. Questo combina la protezione del Keystore con le prestazioni della crittografia simmetrica.
Per i valori viene utilizzato AES-256 GCM (modalità Galois/Counter) — una modalità di crittografia autenticata (AEAD) che garantisce riservatezza e integrità dei dati. Per le chiavi (nomi dei parametri) viene applicato AES-256 SIV (RFC 5297) — crittografia deterministica necessaria per la ricerca delle chiavi senza rivelarne il contenuto.
Ogni file di EncryptedSharedPreferences contiene coppie chiave-valore crittografate. La struttura del file include: prima un'intestazione con metadati (versione, identificatore di chiave), poi un elenco di voci crittografate. Il file non è XML valido e non può essere letto da editor di testo.
La classe MasterKey è responsabile della creazione e gestione di una chiave master a 256 bit memorizzata in Android Keystore. MasterKey.Builder consente di configurare: tipo di archiviazione (Keystore o software), protezione biometrica e durata della chiave. Per impostazione predefinita, la chiave master viene generata in Android Keystore con l'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 accetta cinque parametri: contesto, nome file, chiave master, schema di crittografia delle chiavi e schema di crittografia dei valori. La scelta degli schemi influisce sulle prestazioni e sul livello di sicurezza.
AES256_SIV — crittografia deterministica: chiavi identiche producono sempre lo stesso testo cifrato. Questo è necessario per la ricerca delle chiavi (SharedPreferences.getX(key)). Svantaggio: un malintenzionato può determinare quali chiavi vengono utilizzate confrontando testi cifrati ripetuti. AES256_SIV2 — una versione migliorata con randomizzazione aggiuntiva.
Per i valori viene utilizzato AES256_GCM. GCM aggiunge un IV di 12 byte (vettore di inizializzazione) e un tag di autenticazione di 16 byte a ogni valore. Questo fornisce riservatezza (nessuno può leggere il valore) e autenticazione (nessuno può alterare il valore senza essere scoperto).
Il metodo setUserAuthenticationRequired(true) in MasterKey.Builder richiede la conferma biometrica prima di recuperare la chiave master dal Keystore. Questo aggiunge un ulteriore livello: anche se l'app è in esecuzione su un dispositivo sbloccato, un malintenzionato non può leggere EncryptedSharedPreferences senza Face ID o Touch ID.
Importante: con setUserAuthenticationRequired, la chiave master diventa indisponibile se l'utente modifica o rimuove i dati biometrici. È necessario gestire KeyPermanentlyInvalidatedException e creare una nuova chiave master con migrazione dei dati.
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 biometria è cambiata — è necessario ricreare la chiave
}
}
Vediamo un esempio completo di integrazione di EncryptedSharedPreferences in un'app Android con Kotlin. La libreria androidx.security:security-crypto viene aggiunta tramite Gradle.
Nel file build.gradle (app), aggiungi: implementation “androidx.security:security-crypto:1.1.0-alpha06”. Per i progetti Kotlin, è necessario anche kotlin-stdlib. L'inizializzazione di MasterKey avviene una volta, di solito in Application.onCreate o tramite un contenitore DI.
Dopo aver creato un'istanza di EncryptedSharedPreferences, l'API non differisce dalle SharedPreferences normali. edit() restituisce un Editor, tutti i metodi (putString, getString, putBoolean, getBoolean) funzionano allo stesso modo. L'unica differenza è interna: i dati vengono crittografati in scrittura e decrittografati in lettura.
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()
}
}
Per migrare i dati esistenti dalle SharedPreferences non crittografate a EncryptedSharedPreferences, è necessario: leggere tutti i dati dal vecchio file, creare una nuova EncryptedSharedPreferences, scrivere tutti i dati ed eliminare il vecchio file. Google non fornisce un migratore integrato — lo sviluppatore lo implementa manualmente.
La scelta tra SharedPreferences e EncryptedSharedPreferences dipende dal tipo di dati memorizzati. Per le impostazioni dell'interfaccia (tema, lingua, ordinamento), le SharedPreferences normali sono sufficienti. Per informazioni riservate (token, password, chiavi), EncryptedSharedPreferences è obbligatorio.
EncryptedSharedPreferences è più lento di quello normale a causa delle operazioni crittografiche. La scrittura di un singolo valore stringa richiede ~5-15 ms (a seconda della dimensione dei dati e dell'accelerazione hardware AES). La lettura richiede 2-5 ms. Per la maggior parte delle app questo è impercettibile, ma per operazioni batch (migrazione, ripristino) usa apply() invece di commit().
Le SharedPreferences normali non offrono alcuna protezione crittografica: il file XML può essere letto da qualsiasi processo con accesso root o tramite adb backup. EncryptedSharedPreferences crittografa i dati a livello di applicazione e la chiave master è memorizzata in Android Keystore con protezione hardware opzionale (StrongBox).
| Caratteristica | SharedPreferences | EncryptedSharedPreferences |
|---|---|---|
| Archiviazione | XML in chiaro | File binario crittografato |
| Crittografia | Nessuna | AES-256 GCM + SIV |
| Protezione chiavi | Nessuna | Android Keystore + StrongBox |
| Prestazioni | 0.1-1 ms | 2-15 ms |
| Raccomandazione | Impostazioni UI | Token, chiavi, PII |
Usa EncryptedSharedPreferences per memorizzare: token di aggiornamento OAuth, chiavi API per servizi esterni, email o numero di telefono dell'utente e impostazioni sensibili dell'app (PIN, flag di autenticazione). EncryptedSharedPreferences non è adatto per memorizzare dati biometrici o documenti di grandi dimensioni — usa EncryptedFile o Room con SQLCipher.
La regola generale: se una perdita di dati danneggerebbe l'utente o l'azienda — usa EncryptedSharedPreferences. Se i dati sono solo estetici (tema, lingua, ordinamento) — SharedPreferences normali. Ha senso implementare EncryptedSharedPreferences dall'inizio, senza refactoring: sostituirlo in un progetto esistente richiede migrazione e gestione dei vecchi dati non crittografati.
Ricorda che EncryptedSharedPreferences non protegge i dati durante l'esecuzione dell'app — solo su disco. Se un malintenzionato ha accesso alla memoria del processo, i dati decrittografati possono essere intercettati. Usa protezione aggiuntiva: ProGuard/DexGuard per l'offuscamento.
Domande Frequenti
Jetpack DataStore è un'alternativa più moderna a SharedPreferences, basata su Flow e coroutine Kotlin. DataStore non crittografa i dati per impostazione predefinita, ma può essere combinato con EncryptedSharedPreferences o utilizzato con crittografia manuale tramite Proto DataStore con protocolli crittografici.
Non raccomandato. EncryptedSharedPreferences è progettato per volumi ridotti (fino a 100-200 KB). Per dati più grandi, usa Room con SQLCipher o la crittografia dei file tramite EncryptedFile della stessa libreria AndroidX Security.
No, non esiste migrazione automatica dello schema. Quando si modifica la struttura dei dati, lo sviluppatore deve leggere manualmente i vecchi dati tramite il vecchio KeyGen e scriverli tramite il nuovo. Si consiglia di memorizzare la versione dello schema in un parametro separato.
AndroidX Security 1.0.0 supporta API 19+ (Android KitKat). La versione 1.1.0-alpha06 supporta anch'essa API 19+. StrongBox richiede API 28+ e un dispositivo con supporto hardware (Google Pixel 3+, Samsung Galaxy S9+).
Sì, il refresh token è uno dei casi d'uso principali. Crittografia AES-256 GCM, chiave master in Keystore, protezione biometrica — un livello sufficiente per i token OAuth. Per i token di accesso a breve durata è adatto, anche se alcuni team preferiscono conservarli in memoria.
Riepilogo
Svilupperemo un'applicazione mobile chiavi in mano
IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.
Leggi anche