Système Android Keystore

Le système Android Keystore vous permet de stocker des clés cryptographiques dans un conteneur pour les rendre plus difficiles à extraire de l'appareil. Une fois les clés dans le keystore, vous pouvez les utiliser pour des opérations cryptographiques, mais le matériel de clé reste non exportable. En outre, le système keystore vous permet de limiter quand et comment les clés peuvent être utilisées. Par exemple, vous pouvez exiger l'authentification de l'utilisateur pour pouvoir utiliser les clés ou limiter leur utilisation à certains modes cryptographiques. Pour en savoir plus, consultez la section Fonctionnalités de sécurité.

Le système keystore est utilisé par l'API KeyChain, introduite dans Android 4.0 (niveau d'API 14), ainsi que par la fonctionnalité du fournisseur Android Keystore, introduite dans Android 4.3 (niveau d'API 18). Ce document explique quand et comment utiliser le système Android Keystore.

Fonctionnalités de sécurité

Le système Android Keystore protège le matériel de clé contre toute utilisation non autorisée de deux manières. Tout d'abord, il réduit le risque d'utilisation non autorisée du matériel de clé depuis l'extérieur de l'appareil Android en empêchant l'extraction du matériel de clé des processus d'application et de l'appareil Android dans son ensemble. Deuxièmement, le système keystore réduit le risque d'utilisation non autorisée du matériel de clé depuis l'intérieur de l'appareil Android en obligeant les applications à spécifier les utilisations autorisées de leurs clés, puis en appliquant ces restrictions en dehors des processus des applications.

Prévention de l'extraction

Le matériel des clés Android Keystore est protégé contre l'extraction à l'aide de deux mesures de sécurité :

  • Le matériel de clé n'entre jamais dans le processus de l'application. Lorsqu'une application réalise des opérations cryptographiques à l'aide d'une clé Android Keystore, le texte brut, le texte chiffré et les messages à signer ou à vérifier sont transmis en arrière-plan à un processus système qui effectue les opérations crytographiques. Si le processus de l'application est compromis, le pirate informatique peut utiliser les clés de l'application, mais ne pourra pas en extraire le matériel (par exemple, pour l'utiliser en dehors de l'appareil Android).
  • Le matériel de clé peut être lié au matériel sécurisé de l'appareil Android, tel que l'environnement d'exécution sécurisé (TEE) ou le composant sécurisé (SE). Lorsque cette fonctionnalité est activée pour une clé, son matériel n'est jamais exposé en dehors du matériel sécurisé. Si l'OS Android est compromis ou si un pirate informatique parvient à lire la mémoire de stockage interne de l'appareil, il peut utiliser les clés Android Keystore de n'importe quelle application sur l'appareil Android, mais ne pourra pas les extraire de l'appareil. Cette fonctionnalité n'est activée que si le matériel sécurisé de l'appareil est compatible avec la combinaison particulière d'algorithme de clé, de modes de blocage, de schémas de remplissage et de condensés avec lesquels la clé peut être utilisée.

    Pour vérifier si la fonctionnalité est activée pour une clé, obtenez un KeyInfo pour la clé. L'étape suivante dépend de la version du SDK cible de votre application :

    • Si votre application cible Android 10 (niveau d'API 29) ou une version ultérieure, inspectez la valeur renvoyée par getSecurityLevel(). Les valeurs renvoyées correspondant à KeyProperties.SecurityLevelEnum.TRUSTED_ENVIRONMENT ou KeyProperties.SecurityLevelEnum.STRONGBOX indiquent que la clé réside dans du matériel sécurisé.
    • Si votre application cible Android 9 (niveau d'API 28) ou une version antérieure, inspectez la valeur booléenne renvoyée par KeyInfo.isInsideSecurityHardware().

Module matériel de sécurité

Les appareils compatibles équipés d'Android 9 (niveau d'API 28) ou d'une version ultérieure peuvent disposer de StrongBox Keymaster, une implémentation du HAL Keymaster ou Keymint qui se trouve dans un composant sécurisé de type module matériel de sécurité. Bien que les modules matériels de sécurité puissent faire référence à de nombreuses implémentations différentes de stockage de clés sans qu'un compromis du noyau Linux ne les révèle, comme le TEE, StrongBox fait explicitement référence à des appareils tels que des composants sécurisés intégrés (eSE) ou des unités de traitement sécurisé (iSE) sur SoC.

Le module contient les éléments suivants :

  • Son propre processeur
  • Un espace de stockage sécurisé
  • Un véritable générateur de nombres aléatoires
  • Des mécanismes supplémentaires pour empêcher la falsification des packages et le téléchargement indépendant non autorisé d'applications
  • Un minuteur sécurisé
  • Une épingle de notification de redémarrage (ou équivalent), comme une entrée/sortie à usage général (GPIO)

Pour prendre en charge les implémentations de StrongBox basse consommation, un sous-ensemble d'algorithmes et de tailles de clé sont est pris en charge :

  • RSA 2048
  • AES 128 et 256
  • ECDSA, ECDH P-256
  • HMAC-SHA256 (accepte les tailles de clé comprises entre 8 octets et 64 octets inclus)
  • Triple DES
  • APDU de longueur étendue
  • Attestation de clé
  • Prise en charge de l'avenant H pour la mise à niveau

Lorsque vous générez ou importez des clés à l'aide de la classe KeyStore, vous indiquez une préférence pour le stockage de la clé dans StrongBox KeyMaster en transmettant true à la méthode setIsStrongBoxBacked().

Bien que StrongBox soit un peu plus lent et que ses ressources soient limitées (c'est-à-dire qu'il accepte moins d'opérations simultanées) par rapport au TEE, il offre de meilleures garanties de sécurité contre les attaques physiques et par canal auxiliaire. Si vous souhaitez privilégier des garanties de sécurité renforcées à l'efficacité des ressources des applications, nous vous recommandons d'utiliser StrongBox sur les appareils où il est disponible. Lorsque StrongBox n'est pas disponible, votre application peut toujours faire appel au TEE pour stocker les matériels de clé.

Autorisations d'utilisation des clés

Pour éviter toute utilisation non autorisée de clés sur l'appareil Android, Android Keystore permet aux applications de spécifier les utilisations autorisées de leurs clés lorsqu'elles génèrent ou importent les clés. Une fois qu'une clé est générée ou importée, ses autorisations ne peuvent plus être modifiées. Les autorisations sont ensuite appliquées par Android Keystore chaque fois que la clé est utilisée. Il s'agit d'une fonctionnalité de sécurité avancée qui n'est généralement utile que si vous avez besoin que la compromission de votre processus d'application après la génération ou l'importation de la clé (mais pas avant ni pendant) ne puisse pas entraîner une utilisation non autorisée de la clé.

Les autorisations d'utilisation des clés prises en charge appartiennent aux catégories suivantes :

  • Cryptographie : la clé ne peut être utilisée qu'avec des algorithmes, des opérations, des objectifs (chiffrer, déchiffrer, signer, vérifier), des schémas de remplissage, des modes de blocage ou des condensés autorisés.
  • Intervalle de validité temporelle : la clé ne peut être utilisée que pendant un intervalle de temps défini.
  • Authentification de l'utilisateur : la clé ne peut être utilisée que si l'utilisateur s'est authentifié suffisamment récemment. Consultez Exiger l'authentification de l'utilisateur pour l'utilisation des clés.

Par mesure de sécurité supplémentaire pour les clés dont le matériel se trouve dans du matériel sécurisé (voir KeyInfo.isInsideSecurityHardware() ou KeyInfo.getSecurityLevel() pour les applications ciblant Android 10 (niveau d'API 29) ou version ultérieure), certaines autorisations d'utilisation des clés peuvent être appliquées par le matériel sécurisé, en fonction de l'appareil Android. Le matériel sécurisé applique normalement des autorisations cryptographiques et d'authentification des utilisateurs. Toutefois, le matériel sécurisé n'applique généralement pas d'autorisations d'intervalle de validité temporelle, car il ne dispose normalement pas d'une horloge en temps réel indépendante et sécurisée.

Vous pouvez demander si l'autorisation d'authentification utilisateur d'une clé est appliquée par le matériel sécurisé à l'aide de KeyInfo.isUserAuthenticationRequirementEnforcedBySecureHardware().

Choisir entre un trousseau et le fournisseur Android Keystore

Utilisez l'API KeyChain lorsque vous souhaitez obtenir des identifiants à l'échelle du système. Lorsqu'une application demande l'utilisation de n'importe quel identifiant via l'API KeyChain, les utilisateurs peuvent choisir, via une UI fournie par le système, les identifiants installés auxquels une application peut accéder. Cela permet à plusieurs applications d'utiliser le même ensemble d'identifiants avec le consentement de l'utilisateur.

Utilisez le fournisseur Android Keystore pour permettre à une plate-forme de téléchargement d'applications de stocker ses propres identifiants, auxquels seule cette application pourra accéder. Cela permet aux applications de gérer les identifiants qu'elles sont les seules à pouvoir utiliser, tout en offrant les mêmes avantages de sécurité que l'API KeyChain pour les identifiants à l'échelle du système. Avec cette méthode, l'utilisateur n'a pas besoin de sélectionner les identifiants.

Utiliser le fournisseur Android Keystore

Pour utiliser cette fonctionnalité, utilisez les classes standards KeyStore et KeyPairGenerator ou KeyGenerator avec le fournisseur AndroidKeyStore introduit dans Android 4.3 (niveau d'API 18).

AndroidKeyStore est enregistré en tant que type KeyStore à utiliser avec la méthode KeyStore.getInstance(type) et en tant que fournisseur à utiliser avec les méthodes KeyPairGenerator.getInstance(algorithm, provider) et KeyGenerator.getInstance(algorithm, provider).

Générer une nouvelle clé privée ou secrète

Pour générer une nouvelle KeyPair contenant une PrivateKey, vous devez spécifier les attributs X.509 initiaux du certificat. Vous pouvez utiliser KeyStore.setKeyEntry() pour remplacer ultérieurement le certificat par un certificat signé par une autorité de certification.

Pour générer la paire de clés, utilisez un KeyPairGenerator avec KeyGenParameterSpec :

Kotlin

/*
 * Generate a new EC key pair entry in the Android Keystore by
 * using the KeyPairGenerator API. The private key can only be
 * used for signing or verification and only with SHA-256 or
 * SHA-512 as the message digest.
 */
val kpg: KeyPairGenerator = KeyPairGenerator.getInstance(
        KeyProperties.KEY_ALGORITHM_EC,
        "AndroidKeyStore"
)
val parameterSpec: KeyGenParameterSpec = KeyGenParameterSpec.Builder(
        alias,
        KeyProperties.PURPOSE_SIGN or KeyProperties.PURPOSE_VERIFY
).run {
    setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512)
    build()
}

kpg.initialize(parameterSpec)

val kp = kpg.generateKeyPair()

Java

/*
 * Generate a new EC key pair entry in the Android Keystore by
 * using the KeyPairGenerator API. The private key can only be
 * used for signing or verification and only with SHA-256 or
 * SHA-512 as the message digest.
 */
KeyPairGenerator kpg = KeyPairGenerator.getInstance(
        KeyProperties.KEY_ALGORITHM_EC, "AndroidKeyStore");
kpg.initialize(new KeyGenParameterSpec.Builder(
        alias,
        KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY)
        .setDigests(KeyProperties.DIGEST_SHA256,
            KeyProperties.DIGEST_SHA512)
        .build());

KeyPair kp = kpg.generateKeyPair();

Importer des clés chiffrées dans du matériel sécurisé

Android 9 (niveau d'API 28) ou version ultérieure vous permet d'importer des clés chiffrées de manière sécurisée dans le keystore à l'aide d'un format de clé ASN.1. Keymaster déchiffre ensuite les clés dans le keystore, de sorte que leur contenu n'apparaisse jamais en texte brut dans la mémoire hôte de l'appareil. Ce processus renforce la sécurité du déchiffrement des clés.

Pour utiliser l'importation sécurisée de clés chiffrées dans le keystore, procédez comme suit :

  1. Générez une paire de clés qui utilise l'objectif PURPOSE_WRAP_KEY. Nous vous recommandons également d'ajouter une attestation à cette paire de clés.

  2. Sur un serveur ou une machine de confiance, générez le message ASN.1 pour le SecureKeyWrapper.

    Le wrapper contient le schéma suivant :

       KeyDescription ::= SEQUENCE {
           keyFormat INTEGER,
           authorizationList AuthorizationList
       }
    
       SecureKeyWrapper ::= SEQUENCE {
           wrapperFormatVersion INTEGER,
           encryptedTransportKey OCTET_STRING,
           initializationVector OCTET_STRING,
           keyDescription KeyDescription,
           secureKey OCTET_STRING,
           tag OCTET_STRING
       }
    
  3. Créez un objet WrappedKeyEntry en transmettant le message ASN.1 sous forme de tableau d'octets.

  4. Transmettez cet objet WrappedKeyEntry dans la surcharge de setEntry() qui accepte un objet Keystore.Entry.

Utiliser des entrées de keystore

Vous pouvez accéder au fournisseur AndroidKeyStore via toutes les API KeyStore standards.

Répertorier les entrées

Répertoriez les entrées du keystore en appelant la méthode aliases() :

Kotlin

/*
 * Load the Android KeyStore instance using the
 * AndroidKeyStore provider to list the currently stored entries.
 */
val ks: KeyStore = KeyStore.getInstance("AndroidKeyStore").apply {
   load(null)
}
val aliases: Enumeration<String> = ks.aliases()

Java

/*
 * Load the Android KeyStore instance using the
 * AndroidKeyStore provider to list the currently stored entries.
 */
KeyStore ks = KeyStore.getInstance("AndroidKeyStore");
ks.load(null);
Enumeration<String> aliases = ks.aliases();

Signer et vérifier les données

Signez les données en récupérant la KeyStore.Entry à partir du keystore et en utilisant les API Signature, telles que sign() :

Kotlin

/*
 * Use a PrivateKey in the KeyStore to create a signature over
 * some data.
 */
val ks: KeyStore = KeyStore.getInstance("AndroidKeyStore").apply {
    load(null)
}
val entry: KeyStore.Entry = ks.getEntry(alias, null)
if (entry !is KeyStore.PrivateKeyEntry) {
    Log.w(TAG, "Not an instance of a PrivateKeyEntry")
    return null
}
val signature: ByteArray = Signature.getInstance("SHA256withECDSA").run {
    initSign(entry.privateKey)
    update(data)
    sign()
}

Java

/*
 * Use a PrivateKey in the KeyStore to create a signature over
 * some data.
 */
KeyStore ks = KeyStore.getInstance("AndroidKeyStore");
ks.load(null);
KeyStore.Entry entry = ks.getEntry(alias, null);
if (!(entry instanceof PrivateKeyEntry)) {
    Log.w(TAG, "Not an instance of a PrivateKeyEntry");
    return null;
}
Signature s = Signature.getInstance("SHA256withECDSA");
s.initSign(((PrivateKeyEntry) entry).getPrivateKey());
s.update(data);
byte[] signature = s.sign();

De même, vérifiez les données avec la méthode verify(byte[]) :

Kotlin

/*
 * Verify a signature previously made by a private key in the
 * KeyStore. This uses the X.509 certificate attached to the
 * private key in the KeyStore to validate a previously
 * generated signature.
 */
val ks = KeyStore.getInstance("AndroidKeyStore").apply {
    load(null)
}
val entry = ks.getEntry(alias, null) as? KeyStore.PrivateKeyEntry
if (entry == null) {
    Log.w(TAG, "Not an instance of a PrivateKeyEntry")
    return false
}
val valid: Boolean = Signature.getInstance("SHA256withECDSA").run {
    initVerify(entry.certificate)
    update(data)
    verify(signature)
}

Java

/*
 * Verify a signature previously made by a private key in the
 * KeyStore. This uses the X.509 certificate attached to the
 * private key in the KeyStore to validate a previously
 * generated signature.
 */
KeyStore ks = KeyStore.getInstance("AndroidKeyStore");
ks.load(null);
KeyStore.Entry entry = ks.getEntry(alias, null);
if (!(entry instanceof PrivateKeyEntry)) {
    Log.w(TAG, "Not an instance of a PrivateKeyEntry");
    return false;
}
Signature s = Signature.getInstance("SHA256withECDSA");
s.initVerify(((PrivateKeyEntry) entry).getCertificate());
s.update(data);
boolean valid = s.verify(signature);

Exiger l'authentification des utilisateurs pour l'utilisation des clés

Lors de la génération ou de l'importation d'une clé dans AndroidKeyStore, vous pouvez spécifier qu'elle n'est autorisée à être utilisée que si l'utilisateur a été authentifié. L'utilisateur est authentifié à l'aide d'un sous-ensemble de ses identifiants d'écran de verrouillage sécurisés (schéma/code/mot de passe, identifiants biométriques).

Il s'agit d'une fonctionnalité de sécurité avancée qui n'est généralement utile que si vous avez besoin que la compromission de votre processus d'application après la génération ou l'importation de la clé (mais pas avant ni pendant) ne puisse pas contourner l'exigence d'authentification de l'utilisateur pour l'utilisation de la clé.

Lorsqu'une clé n'est autorisée à être utilisée que si l'utilisateur a été authentifié, vous pouvez appeler setUserAuthenticationParameters() pour la configurer afin qu'elle fonctionne dans l'un des modes suivants :

Autoriser pendant une période donnée
Toutes les clés sont autorisées à être utilisées dès que l'utilisateur s'authentifie à l'aide de l'un des types d'identifiants spécifiés.
Autoriser pour la durée d'une opération cryptographique spécifique

Chaque opération impliquant une clé spécifique doit être autorisée individuellement par l'utilisateur.

Votre application démarre ce processus en appelant authenticate() sur une instance de BiometricPrompt.

Pour chaque clé que vous créez, vous pouvez choisir de prendre en charge des identifiants biométriques forts, des identifiants d'écran de verrouillage ou les deux types d'identifiants. Pour déterminer si l'utilisateur a configuré les identifiants sur lesquels repose la clé de votre application, appelez canAuthenticate().

Si une clé n'est compatible qu'avec les identifiants biométriques, elle est invalidée par défaut chaque fois que de nouveaux enregistrements biométriques sont ajoutés. Vous pouvez configurer la clé pour qu'elle reste valide lorsque de nouveaux enregistrements biométriques sont ajoutés. Pour ce faire, transmettez false à setInvalidatedByBiometricEnrollment().

Découvrez comment ajouter des fonctionnalités d'authentification biométrique à votre application, y compris comment afficher une boîte de dialogue d'authentification biométrique.

Algorithmes compatibles

Articles de blog

Consultez l'article de blog Unifier l'accès au keystore dans ICS.