KeyStore (Android) è un'implementazione del provider crittografico Java Cryptography Architecture (JCA) integrata in Android per l'archiviazione sicura delle chiavi con capacità di isolamento hardware. Da Android 4.3 (API 18), KeyStore supporta chiavi hardware tramite Keymaster HAL e, a partire da Android 9 (API 28), StrongBox Keymaster per chiavi in un Secure Element dedicato. Secondo la Documentazione sulla sicurezza Android, il provider “AndroidKeyStore” sostituisce il Bouncy Castle o OpenSSL KeyStore standard, fornendo protezione a livello di sistema contro l'estrazione non autorizzata delle chiavi.
Punti chiave
KeyStore in Android non è un'applicazione o un file separato, ma un provider crittografico che implementa l'interfaccia java.security.KeyStore. Fornisce un'API unificata per archiviare e utilizzare chiavi private, chiavi simmetriche e certificati CA attendibili. Il provider è registrato con il nome “AndroidKeyStore” ed è accessibile tramite il KeyStore.getInstance() standard.
Prima di Android 4.3, le operazioni crittografiche venivano eseguite tramite Bouncy Castle. Android 4.3 ha introdotto Keymaster HAL 1.0, consentendo l'uso di TEE su ARM TrustZone. Android 6.0 (API 23) ha aggiunto Keymaster 2.0 con autenticazione biometrica hardware. Android 9 (API 28) ha presentato Keymaster 4.0 e StrongBox Keymaster per un Secure Element dedicato.
Ogni versione di Keymaster aggiunge nuove funzionalità e migliora l'isolamento delle chiavi. I dispositivi moderni (2022+) devono supportare Keymaster 4.0 per la certificazione Google Mobile Services, garantendo la disponibilità di TEE per tutte le applicazioni Android.
Android KeyStore è composto da tre livelli: API Java (KeyStore, KeyPairGenerator), processo di sistema keystore (C++, eseguito come servizio di sistema) e Keymaster HAL (libreria in TEE o Secure Element). L'applicazione chiama l'API, il servizio keystore instrada la richiesta a Keymaster e l'operazione viene eseguita nell'ambiente sicuro.
Tutte le chiavi private sono archiviate in TEE e non possono essere lette dallo spazio utente. Anche il servizio keystore di sistema non ha accesso alle chiavi grezze — solo agli handle che puntano alle chiavi all'interno di Keymaster.
Android KeyStore implementa l'interfaccia standard del provider di servizi JCA. Quando un'applicazione chiama Cipher.getInstance(“RSA/ECB/PKCS1Padding”, “AndroidKeyStore”), il provider di sicurezza Android delega l'operazione a Keymaster tramite la catena: Java → JNI → servizio keystore → Keymaster HAL.
Il provider AndroidKeyStore viene registrato automaticamente all'avvio del processo. La sua priorità è superiore a quella di Bouncy Castle o Conscrypt. Pertanto, quando si chiama KeyStore.getInstance() senza specificare un provider, nella maggior parte dei casi viene restituito AndroidKeyStore. Per una chiamata esplicita, utilizzare KeyStore.getInstance(“AndroidKeyStore”).
Ogni applicazione Android ha un contenitore isolato in KeyStore. Le applicazioni con lo stesso UID (shared userId) possono condividere l'accesso a determinate chiavi, ma la configurazione standard garantisce che l'applicazione A non possa leggere le chiavi dell'applicazione B.
load(null) — inizializzazione di KeyStore. Il parametro è sempre null per AndroidKeyStore. setEntry — salva una chiave con KeyProtection specificata (scopi, digest, padding). getEntry — recupera KeyStore.PrivateKeyEntry, SecretKeyEntry o TrustedCertificateEntry. containsAlias — verifica se una chiave esiste. deleteEntry — elimina definitivamente una chiave.
import java.security.KeyStore
import java.security.KeyPairGenerator
import android.security.keystore.KeyGenParameterSpec
import android.security.keystore.KeyProperties
object KeyStoreManager {
private val keyStore by lazy {
KeyStore.getInstance("AndroidKeyStore").apply { load(null) }
}
fun createRsaKey(alias: String) {
val spec = KeyGenParameterSpec.Builder(alias,
KeyProperties.PURPOSE_SIGN or KeyProperties.PURPOSE_VERIFY
).setKeySize(2048)
.setDigests(KeyProperties.DIGEST_SHA256)
.setSignaturePaddings(KeyProperties.SIGN_PADDING_RSA_PKCS1)
.build()
val kpg = KeyPairGenerator.getInstance(
KeyProperties.KEY_ALGORITHM_RSA,
"AndroidKeyStore"
)
kpg.initialize(spec)
kpg.generateKeyPair()
}
}
Android KeyStore supporta un'ampia gamma di algoritmi crittografici, che varia a seconda della versione di Keymaster HAL sul dispositivo. Uno sviluppatore può ottenere l'elenco degli algoritmi supportati tramite KeyGenParameterSpec.Builder durante il tentativo di generazione — i parametri incompatibili generano InvalidAlgorithmParameterException.
RSA (1024–4096 bit) — per firma (PKCS1, PSS con SHA-1/SHA-256/SHA-384/SHA-512) e crittografia (OAEP con SHA-1/SHA-256). EC (P-224, P-256, P-384, P-521) — per firma ECDSA e accordo chiavi ECDH. X25519 e Ed25519 — da Android 12 (API 31) per protocolli crittografici moderni.
Per le chiavi asimmetriche, genera sempre all'interno di Keymaster, NON importare mai chiavi private. Le chiavi private importate non sono protette hardware — vengono archiviate nel livello software e sono vulnerabili se il processo dell'applicazione viene compromesso.
AES (128, 256 bit) — per crittografia simmetrica in modalità CBC, CTR, GCM. HMAC (SHA-1, SHA-256, SHA-512) — per autenticazione dei messaggi. ChaCha20 (Android 12+) — per crittografia di flusso ad alte prestazioni con autenticazione Poly1305.
| Algoritmo | Keymaster | Scopo | API |
|---|---|---|---|
| RSA | KM 1.0+ | Firma, Crittografia | 18+ |
| EC | KM 1.0+ | ECDSA, ECDH | 18+ |
| AES | KM 2.0+ | Crittografia simmetrica | 23+ |
| HMAC | KM 2.0+ | Codice di autenticazione | 23+ |
| ChaCha20 | KM 3.0+ | Crittografia di flusso | 31+ |
| X25519/Ed25519 | KM 3.0+ | Scambio di chiavi | 31+ |
KeyStore.PrivateKeyEntry — contiene una chiave privata (non esportabile) e una catena di certificati. KeyStore.SecretKeyEntry — per chiavi simmetriche. KeyStore.TrustedCertificateEntry — per certificati CA attendibili. Le chiavi pubbliche sono esportabili tramite keyStore.getCertificate(alias).publicKey.
Esaminiamo uno scenario completo: generare una chiave AES per la crittografia dei dati e generare una chiave EC per la firma con protezione biometrica. Entrambe le chiavi vengono create all'interno di Android KeyStore con supporto hardware.
Una chiave AES viene creata tramite KeyGenerator con KeyGenParameterSpec. Parametri: PURPOSE_ENCRYPT + PURPOSE_DECRYPT, BLOCK_MODE_GCM (modalità consigliata con autenticazione), ENCRYPTION_PADDING_NONE (nessun padding necessario per GCM).
import javax.crypto.KeyGenerator
import javax.crypto.Cipher
import javax.crypto.spec.GCMParameterSpec
fun generateAndEncrypt(alias: String, plainText: ByteArray): ByteArray {
val spec = KeyGenParameterSpec.Builder(alias,
KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT
).setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.setKeySize(256)
.build()
val kg = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore")
kg.initialize(spec)
kg.generateKey()
val cipher = Cipher.getInstance("AES/GCM/NoPadding")
cipher.init(Cipher.ENCRYPT_MODE, getKeyFromStore(alias))
return cipher.doFinal(plainText)
}
Una chiave EC con userAuthenticationRequired=true richiede l'autenticazione dell'utente prima di ogni operazione di firma. A tale scopo viene utilizzato BiometricPrompt con CryptoObject contenente l'oggetto Signature. Dopo la verifica biometrica riuscita, Keymaster consente l'operazione.
fun createBiometricSignKey(alias: String) {
val spec = KeyGenParameterSpec.Builder(alias,
KeyProperties.PURPOSE_SIGN
).setAlgorithmParameterSpec(
ECGenParameterSpec("secp256r1")
).setDigests(KeyProperties.DIGEST_SHA256)
.setUserAuthenticationRequired(true)
.setInvalidatedByBiometricEnrollment(true)
.build()
val kpg = KeyPairGenerator.getInstance(
KeyProperties.KEY_ALGORITHM_EC,
"AndroidKeyStore"
)
kpg.initialize(spec)
kpg.generateKeyPair()
}
Android KeyStore fornisce garanzie di sicurezza a livello hardware che i KeyStore basati su software (JKS, BKS) non possono offrire. Le chiavi sono protette a livello di SoC e anche il controllo completo dello spazio utente Android non consente di estrarre la chiave privata.
Key Attestation è un meccanismo che consente a un'applicazione (e al server) di verificare l'ambiente in cui è stata creata una chiave. Android Keystore firma un certificato contenente un elenco di caratteristiche della chiave: algoritmo, dimensione, scopi, supporto hardware (True/False), origine (GENERATED, IMPORTED). Il server verifica la catena di certificati fino al certificato root di Google.
Questo è fondamentale per le applicazioni finanziarie: il server può richiedere che la chiave sia stata creata in un ambiente hardware (Hardware-Backed = True) e rifiutare le chiavi create in Keystore software. Key Attestation previene attacchi in cui un utente malintenzionato sostituisce il Keystore con un emulatore.
setInvalidatedByBiometricEnrollment(true) significa che Keymaster eliminerà automaticamente la chiave quando i modelli biometrici vengono modificati o rimossi. Questo protegge da attacchi in cui un utente malintenzionato aggiunge la propria impronta digitale a un account esistente. Dopo l'aggiunta di una nuova impronta, le chiavi precedenti diventano inaccessibili.
Il contatore dei tentativi falliti di autenticazione biometrica è gestito anch'esso da Keymaster. Dopo maxBiometricAttempt (configurabile dal produttore, generalmente 5), Keymaster blocca tutte le operazioni con chiavi biometriche per 30 secondi. Dopo 10 tentativi falliti — fino all'inserimento della password del dispositivo (PIN segreto).
Domande frequenti
Bouncy Castle (BKS) è un KeyStore basato su software che archivia le chiavi in un file protetto da password. Android KeyStore utilizza l'isolamento hardware TEE/StrongBox. Le chiavi BKS possono essere estratte con accesso root, le chiavi Android KeyStore no. BKS è adatto per certificati CA, Android KeyStore per chiavi private.
Sì, se specifichi PURPOSE_ENCRYPT o PURPOSE_DECRYPT o PURPOSE_SIGN o PURPOSE_VERIFY al momento della generazione. Tuttavia, la best practice è creare chiavi separate per diverse operazioni. Questo limita il danno se una chiave viene compromessa e segue il principio del minimo privilegio.
Usa KeyStore.getKeyCharacteristics(alias), disponibile tramite android.security.keystore. Il metodo restituisce un insieme di flag: FLAG_HARDWARE — chiave in TEE, FLAG_SECURE_ELEMENT — chiave in StrongBox. Se non ci sono flag, la chiave è solo software.
Tutte le chiavi create con setInvalidatedByBiometricEnrollment(true) verranno automaticamente invalidate da Keymaster. Al tentativo di utilizzo, l'applicazione riceverà KeyPermanentlyInvalidatedException. I dati crittografati con queste chiavi andranno persi definitivamente.
Le chiavi hardware (in TEE/StrongBox) non supportano il backup — sono legate a un dispositivo specifico. Le chiavi software possono essere incluse nel backup di Google Drive. Per trasferire dati tra dispositivi, crittografa i dati sul server e decrittografa sul nuovo dispositivo.
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