Android Keystore è un meccanismo di sistema in Android per archiviare in modo sicuro chiavi crittografiche in isolamento hardware. Il sistema utilizza Trusted Execution Environment (TEE) su dispositivi con ARM TrustZone o un Secure Element dedicato per proteggere le chiavi a livello di chip. Secondo Android Open Source Project, Keystore supporta gli algoritmi RSA, EC, AES e HMAC con generazione di chiavi direttamente nell'ambiente sicuro.
Punti chiave
Android Keystore è un provider crittografico implementato in Android dall'API 1 (Android 1.0), ma il supporto hardware completo è apparso con Android 4.3 (API 18). Keystore risolve il problema dell'archiviazione sicura delle chiavi private in modo che anche se il sistema operativo viene compromesso, un utente malintenzionato non possa estrarre le chiavi in testo chiaro.
L'architettura di Android Keystore è composta da tre livelli: l'API applicativa (java.security.KeyStore), il servizio di sistema (keystore daemon) e il livello hardware (Keymaster HAL). L'applicazione accede tramite l'API standard Java Cryptography Architecture (JCA) e il servizio di sistema instrada le richieste a Keymaster in esecuzione in TEE.
Tutte le operazioni crittografiche con le chiavi (firma, decifratura) vengono eseguite all'interno di TEE o Secure Element. Le chiavi non lasciano mai l'ambiente sicuro — l'applicazione riceve solo un handle (alias) per riferire la chiave. Questa è una differenza fondamentale rispetto ai KeyStore software, dove le chiavi sono potenzialmente accessibili nella memoria del processo.
Il JKS (Java KeyStore) standard o BKS (Bouncy Castle) archiviano le chiavi in file protetti da password. Android Keystore archivia le chiavi in isolamento hardware, dove sono protette anche dall'utente root. JKS è vulnerabile all'accesso diretto al filesystem; Android Keystore no.
Un'altra differenza: in Android Keystore, le chiavi hanno parametri di utilizzo rigorosi (purpose — solo sign/verify/encrypt/decrypt) specificati al momento della generazione. Non possono essere modificati successivamente, impedendo l'uso improprio della chiave.
Quando si crea una nuova chiave, l'applicazione chiama KeyPairGenerator o KeyGenerator con KeyGenParameterSpec, che contiene tutti i parametri della chiave futura. Il sistema passa la richiesta a Keymaster HAL, che genera la chiave all'interno di TEE e restituisce un handle.
Il metodo KeyGenParameterSpec.Builder accetta parametri obbligatori: nome della chiave in Keystore, scopo (PURPOSE_SIGN, PURPOSE_ENCRYPT), algoritmo (RSA, EC, AES). Aggiuntivi: digest (SHA-256), padding (PKCS7), userAuthenticationRequired (biometria), keyValidityStart/End (restrizioni temporali).
Dopo aver impostato i parametri, KeyPairGenerator.generateKeyPair() restituisce un KeyPair, dove PrivateKey è un oggetto che delega le operazioni a Keymaster. La chiave pubblica può essere estratta, quella privata no. Esiste solo all'interno di TEE.
import java.security.KeyPairGenerator
import android.security.keystore.KeyGenParameterSpec
import android.security.keystore.KeyProperties
fun generateKey(alias: String) {
val spec = KeyGenParameterSpec.Builder(alias,
KeyProperties.PURPOSE_SIGN or KeyProperties.PURPOSE_VERIFY
).setDigests(KeyProperties.DIGEST_SHA256)
.setSignaturePaddings(KeyProperties.SIGN_PADDING_RSA_PKCS1)
.setUserAuthenticationRequired(true)
.build()
val kpGen = KeyPairGenerator.getInstance(
KeyProperties.KEY_ALGORITHM_RSA,
"AndroidKeyStore"
)
kpGen.initialize(spec)
kpGen.generateKeyPair()
}
Signature per ECDSA o RSA-PSS viene creata tramite l'API standard: Signature.getInstance(algorithm).initSign(privateKey). L'operazione di firma viene eseguita in TEE: l'applicazione passa i dati, Keymaster li firma hardwaremente e restituisce la firma. La chiave e i dati non si mescolano nella memoria condivisa.
Per la protezione biometrica, l'utente deve essere autenticato tramite BiometricPrompt prima della firma. Senza autenticazione riuscita, Keymaster non esegue l'operazione e restituisce CryptoAuthenticationException.
import java.security.KeyStore
import java.security.Signature
import androidx.biometric.BiometricPrompt
fun signWithBiometric(alias: String) {
val ks = KeyStore.getInstance("AndroidKeyStore")
ks.load(null)
val entry = ks.getEntry(alias, null) as KeyStore.PrivateKeyEntry
val signature = Signature.getInstance("SHA256withRSA")
signature.initSign(entry.privateKey)
// BiometricPrompt con CryptoObject(signature) richiede FaceID/PIN
}
Android supporta due modalità di archiviazione delle chiavi: software (su dispositivi senza TEE) e hardware (su dispositivi con TEE o Secure Element). La modalità dipende dalle capacità del SoC e dalla versione di Android.
Su dispositivi senza Trusted Execution Environment (precedenti ad Android 4.3 o SoC economici), le chiavi vengono archiviate crittografate utilizzando una chiave master derivata dalla password della schermata di blocco. Questa modalità è meno sicura — le chiavi sono accessibili nella memoria del processo durante le operazioni crittografiche.
Il livello di protezione si basa sulla crittografia del file KeyStore con AES-256-GCM. La chiave di crittografia viene generata dalla password o dal PIN dell'utente tramite Scrypt (PBKDF2 con un numero elevato di iterazioni).
Sui dispositivi moderni, viene utilizzato Keymaster 4.x in TEE (ARM TrustZone). Le chiavi vengono generate, archiviate e utilizzate esclusivamente all'interno di TrustZone. Anche il kernel Linux non ha accesso alle chiavi private — solo Keymaster HAL può eseguire operazioni.
Secure Element (ad esempio, eSE in Samsung Knox o StrongBox in Google Pixel 3+) è un chip separato con proprio processore e memoria. È certificato Common Criteria EAL 4+ e fornisce il massimo livello di protezione, inclusa la protezione contro la manomissione fisica.
| Tipo | Posizione di archiviazione | Livello di protezione | Disponibile da API |
|---|---|---|---|
| Software | File /data/misc/keystore | Medio (AES-256) | API 1+ |
| Keymaster 3 | TEE (TrustZone) | Alto | API 23+ |
| Keymaster 4 | TEE + Secure I/O | Molto alto | API 28+ |
| StrongBox | Secure Element hardware | Massimo | API 28+, opzionale |
Android Keystore è integrato in Java Cryptography Architecture (JCA). Per accedere al provider, si utilizza KeyStore.getInstance("AndroidKeyStore"). L'API è disponibile dall'API 18.
Il metodo KeyStore.load(null) carica il contenitore KeyStore dell'applicazione. Non è richiesta alcuna password — Android utilizza il contesto dell'applicazione e il suo UID per il controllo degli accessi. Ogni applicazione vede solo le proprie voci, a meno che non venga utilizzato un UID condiviso.
I metodi setEntry e getEntry funzionano con KeyStore.PrivateKeyEntry, SecretKeyEntry o TrustedCertificateEntry. Il parametro ProtectionParameter è sempre null per Android Keystore (la protezione è implementata a livello di sistema).
import java.security.KeyStore
import java.security.cert.Certificate
import android.security.keystore.KeyProtection
fun storeSecretKey(alias: String, key: SecretKey) {
val ks = KeyStore.getInstance("AndroidKeyStore")
ks.load(null)
val prot = KeyProtection.Builder(
KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT
).setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.setUserAuthenticationRequired(true)
.build()
ks.setEntry(alias, KeyStore.SecretKeyEntry(key), prot)
}
Utilizzando KeyCharacteristics, è possibile determinare in quale ambiente è archiviata la chiave: KeyStore software, TEE o StrongBox. Il metodo getKeyCharacteristics() restituisce un insieme di flag: FLAG_HARDWARE (keymaster), FLAG_SECURE_ELEMENT (StrongBox), FLAG_TRUSTED_USER_PRESENCE_REQUIRED (biometria).
Android Keystore supporta un'ampia gamma di algoritmi crittografici divisi in tre categorie: asimmetrici, simmetrici e MAC. Il supporto per algoritmi specifici dipende dalla versione di Keymaster HAL.
RSA (1024–4096 bit) — per firma (PKCS1, PSS) e crittografia (OAEP, PKCS1). EC (P-224, P-256, P-384, P-521) — per firma ECDSA e accordo chiavi ECDH. AES (128, 256 bit) — per crittografia simmetrica in modalità CBC, CTR, GCM. HMAC (SHA1, SHA256, SHA512) — per autenticazione dei messaggi.
Per ogni chiave, viene specificato setPurposes per limitare le operazioni possibili. Una chiave RSA con PURPOSE_SIGN non può essere utilizzata per la crittografia, anche se un utente malintenzionato ha accesso all'API. Questa è l'applicazione dell'uso della chiave a livello hardware.
Keymaster include un contatore di tentativi di autenticazione biometrica falliti. Dopo un numero specificato di fallimenti (configurabile tramite setInvalidatedByBiometricEnrollment), la chiave diventa non disponibile e richiede eliminazione/rigenerazione. Quando tutti i modelli biometrici vengono rimossi, tutte le chiavi con userAuthenticationRequired=true vengono automaticamente invalidate.
È supportato anche Key Attestation (Android 8.1+): su richiesta dell'applicazione, Keymaster firma un certificato con informazioni sulle caratteristiche della chiave (hardware/software, algoritmo, scopi). Il server può verificare questo certificato per confermare che la chiave è stata creata in un ambiente affidabile.
Domande frequenti
Java KeyStore archivia le chiavi in un file protetto da password (JKS, BKS). Android Keystore utilizza l'isolamento hardware tramite TEE o Secure Element. Java KeyStore è vulnerabile all'accesso root; Android Keystore no, perché le chiavi private non lasciano mai l'ambiente sicuro.
Sì, tramite KeyStore.setEntry con KeyProtection. Tuttavia, la chiave importata non avrà protezione hardware — verrà archiviata nel Keystore software, crittografata con una chiave master. Per la massima sicurezza, genera sempre le chiavi all'interno di Keystore.
Utilizza KeyChain.isBoundKeyAlgorithm o verifica KeyCharacteristics dopo la generazione della chiave. La presenza di FLAG_HARDWARE nelle caratteristiche significa che la chiave è stata creata in TEE. Puoi anche verificare android.security.keystore.isHardwareBacked().
Quando l'applicazione viene disinstallata, Android rimuove tutte le sue chiavi dal Keystore. I dati vengono persi irreversibilmente. Alla reinstallazione, l'applicazione deve generare nuove chiavi. Il backup delle chiavi tramite TEE è architetturalmente impossibile.
Su un dispositivo bloccato, Keymaster non esegue alcuna operazione. Le chiavi con userAuthenticationRequired=true richiedono conferma biometrica ogni volta. Anche con accesso root, un utente malintenzionato non può chiamare Keymaster direttamente — solo tramite il servizio Android Keystore.
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