Android-Schlüsselspeichersystem

Mit dem Android-Schlüsselspeichersystem kannst du kryptografische Schlüssel in einem Container speichern, um deren Extraktion vom Gerät zu erschweren. Sobald sich Schlüssel im Schlüsselspeicher befinden, können Sie sie für kryptografische Vorgänge verwenden. Das Schlüsselmaterial kann jedoch nicht exportiert werden. Außerdem können Sie mit dem Keystore-System einschränken, wann und wie Schlüssel verwendet werden können, z. B. die Nutzerauthentifizierung für die Schlüsselnutzung erfordern oder die Verwendung von Schlüsseln auf bestimmte kryptografische Modi beschränken. Weitere Informationen finden Sie im Abschnitt Sicherheitsfunktionen.

Das Keystore-System wird von der KeyChain API verwendet, die in Android 4.0 (API-Level 14) eingeführt wurde, sowie von der Android Keystore-Anbieterfunktion, die in Android 4.3 (API-Level 18) eingeführt wurde. In diesem Dokument wird beschrieben, wann und wie Sie das Android Keystore-System verwenden.

Sicherheitsfunktionen

Das Android Keystore-System schützt Schlüsselmaterial auf zwei Arten vor unbefugter Verwendung. Erstens wird das Risiko einer unbefugten Verwendung von Schlüsselmaterial außerhalb des Android-Geräts verringert, da das Schlüsselmaterial nicht aus Anwendungsprozessen und nicht aus dem Android-Gerät insgesamt extrahiert werden kann. Zweitens reduziert das Schlüsselspeichersystem das Risiko der nicht autorisierten Nutzung von Schlüsselmaterial innerhalb des Android-Geräts, indem Apps die autorisierte Nutzung ihrer Schlüssel angeben und diese Einschränkungen dann außerhalb der App-Prozesse erzwingen.

Extraktion verhindern

Das Schlüsselmaterial von Android Keystore-Schlüsseln wird durch zwei Sicherheitsmaßnahmen vor dem Extrahieren geschützt:

  • Schlüsselmaterial wird niemals in den Bewerbungsprozess aufgenommen. Wenn eine App kryptografische Vorgänge mit einem Android-Schlüsselspeicherschlüssel ausführt, werden Klartext, Geheimtext und Nachrichten, die signiert oder verifiziert werden müssen, im Hintergrund in einen Systemprozess eingespeist, der die kryptografischen Vorgänge ausführt. Wenn der Prozess der App manipuliert wird, kann der Angreifer möglicherweise die Schlüssel der App verwenden, aber nicht das Schlüsselmaterial extrahieren, um es beispielsweise außerhalb des Android-Geräts zu verwenden.
  • Schlüsselmaterial kann an die sichere Hardware des Android-Geräts gebunden werden, z. B. an die Trusted Execution Environment (TEE) oder an ein Secure Element (SE). Wenn diese Funktion für einen Schlüssel aktiviert ist, wird das Schlüsselmaterial niemals außerhalb sicherer Hardware freigegeben. Wenn das Android-Betriebssystem manipuliert wurde oder ein Angreifer den internen Speicher des Geräts lesen kann, kann er möglicherweise die Android Keystore-Schlüssel einer beliebigen App auf dem Android-Gerät verwenden, sie aber nicht vom Gerät extrahieren. Diese Funktion ist nur aktiviert, wenn die sichere Hardware des Geräts die bestimmte Kombination aus Schlüsselalgorithmus, Blockmodi, Padding-Schemas und Digests unterstützt, für die der Schlüssel autorisiert ist.

    Wenn Sie prüfen möchten, ob die Funktion für einen Schlüssel aktiviert ist, rufen Sie KeyInfo für den Schlüssel ab. Der nächste Schritt hängt von der SDK-Zielversion Ihrer App ab:

    • Wenn Ihre App auf Android 10 (API-Level 29) oder höher ausgerichtet ist, prüfen Sie den Rückgabewert von getSecurityLevel(). Rückgabewerte, die mit KeyProperties.SecurityLevelEnum.TRUSTED_ENVIRONMENT oder KeyProperties.SecurityLevelEnum.STRONGBOX übereinstimmen, geben an, dass sich der Schlüssel auf sicherer Hardware befindet.
    • Wenn Ihre App auf Android 9 (API-Level 28) oder niedriger ausgerichtet ist, prüfen Sie den booleschen Rückgabewert von KeyInfo.isInsideSecurityHardware().

Hardware-Sicherheitsmodul

Unterstützte Geräte mit Android 9 (API-Level 28) oder höher können einen StrongBox Keymaster haben, eine Implementierung der Keymaster- oder Keymint-HAL, die sich in einem Secure Element befindet, das einem Hardware Security Module ähnelt. Hardwaresicherheitsmodule können sich auf viele verschiedene Implementierungen des Schlüsselspeichers beziehen, bei denen ein Linux-Kernel-Kompromittierungsversuch nicht auf sie zugreifen kann, z. B. TEE. StrongBox bezieht sich ausdrücklich auf Geräte wie eingebettete sichere Elemente (eSE) oder sichere On-SoC-Verarbeitungseinheiten (iSE).

Das Modul enthält Folgendes:

  • Eigene CPU
  • Sichere Speicherung
  • Echter Zufallszahlengenerator
  • Zusätzliche Mechanismen zum Schutz vor Paketmanipulation und unbefugtem Sideloading von Apps
  • Ein sicherer Timer
  • Ein Neustart-Benachrichtigungspin (oder ein vergleichbarer Pin), z. B. ein GPIO (General Purpose Input/Output)

Zur Unterstützung von Low-Power-StrongBox-Implementierungen wird eine Teilmenge von Algorithmen und Schlüsselgrößen unterstützt:

  • RSA-2048
  • AES 128 und 256
  • ECDSA, ECDH P-256
  • HMAC-SHA256 (unterstützt Schlüsselgrößen zwischen 8 und 64 Byte, einschließlich)
  • Triple DES
  • APDUs mit erweiterter Länge
  • Schlüsselattestierung
  • Unterstützung für Upgrade gemäß Änderung H

Wenn Sie Schlüssel mit der Klasse KeyStore generieren oder importieren, können Sie angeben, dass der Schlüssel im StrongBox Keymaster gespeichert werden soll. Dazu übergeben Sie true an die Methode setIsStrongBoxBacked().

Obwohl StrongBox im Vergleich zu TEE etwas langsamer und ressourcenbeschränkt ist (d. h., es werden weniger gleichzeitige Vorgänge unterstützt), bietet StrongBox bessere Sicherheitsgarantien gegen physische und Side-Channel-Angriffe. Wenn Sie höhere Sicherheitsgarantien gegenüber der Effizienz von App-Ressourcen priorisieren möchten, empfehlen wir die Verwendung von StrongBox auf den Geräten, auf denen es verfügbar ist. Wenn StrongBox nicht verfügbar ist, kann Ihre Anwendung immer auf TEE zurückgreifen, um wichtige Materialien zu speichern.

Autorisierungen für die Schlüsselverwendung

Um eine unbefugte Verwendung von Schlüsseln auf dem Android-Gerät zu vermeiden, können Apps mit Android Keystore autorisierte Verwendungen ihrer Schlüssel angeben, wenn sie die Schlüssel generieren oder importieren. Nachdem ein Schlüssel generiert oder importiert wurde, können seine Autorisierungen nicht mehr geändert werden. Autorisierungen werden dann vom Android Keystore erzwungen, wenn der Schlüssel verwendet wird. Dies ist eine erweiterte Sicherheitsfunktion, die im Allgemeinen nur dann nützlich ist, wenn Ihre Anforderungen besagen, dass eine Manipulation Ihres Anwendungsprozesses nach der Schlüsselgenerierung/dem Schlüsselimport (aber nicht davor oder währenddessen) nicht zu einer unbefugten Verwendung des Schlüssels führen darf.

Die unterstützten Autorisierungen für die Schlüsselnutzung fallen in die folgenden Kategorien:

  • Kryptografie: Der Schlüssel kann nur mit autorisierten Schlüsselalgorithmen, Vorgängen oder Zwecken (Verschlüsseln, Entschlüsseln, Signieren, Verifizieren), Padding-Schemata, Blockmodi oder Digests verwendet werden.
  • Temporales Gültigkeitsintervall:Der Schlüssel darf nur während eines bestimmten Zeitraums verwendet werden.
  • Nutzerauthentifizierung:Der Schlüssel kann nur verwendet werden, wenn sich der Nutzer in letzter Zeit authentifiziert hat. Weitere Informationen finden Sie unter Nutzerauthentifizierung für die Schlüsselnutzung erforderlich machen.

Als zusätzliche Sicherheitsmaßnahme für Schlüssel, deren Schlüsselmaterial sich in sicherer Hardware befindet (siehe KeyInfo.isInsideSecurityHardware() oder für Apps, die auf Android 10 (API-Level 29) oder höher ausgerichtet sind, KeyInfo.getSecurityLevel()), werden einige Autorisierungen zur Schlüsselnutzung je nach Android-Gerät möglicherweise von der sicheren Hardware erzwungen. Sichere Hardware erzwingt normalerweise kryptografische und Nutzerauthentifizierungsautorisierungen. Sichere Hardware erzwingt in der Regel jedoch keine Autorisierungen für zeitliche Gültigkeitsintervalle, da sie normalerweise keine unabhängige, sichere Echtzeituhr hat.

Mit KeyInfo.isUserAuthenticationRequirementEnforcedBySecureHardware() können Sie prüfen, ob die Autorisierung der Nutzerauthentifizierung eines Schlüssels von der sicheren Hardware erzwungen wird.

Wähle zwischen einem Schlüsselbund und dem Android Keystore-Anbieter

Verwenden Sie die KeyChain API, wenn Sie systemweite Anmeldedaten benötigen. Wenn eine Anwendung die Verwendung von Anmeldedaten über die KeyChain API anfordert, können Nutzer über eine vom System bereitgestellte UI festlegen, auf welche der installierten Anmeldedaten eine Anwendung zugreifen kann. Dadurch können mehrere Apps dieselben Anmeldedaten mit Nutzereinwilligung verwenden.

Mit dem Android Keystore-Anbieter können Sie einer einzelnen App erlauben, eigene Anmeldedaten zu speichern, auf die nur diese App zugreifen kann. So können Apps Anmeldedaten verwalten, die nur von ihnen verwendet werden können, und gleichzeitig dieselben Sicherheitsvorteile nutzen, die die KeyChain API für systemweite Anmeldedaten bietet. Bei dieser Methode müssen die Anmeldedaten nicht vom Nutzer ausgewählt werden.

Android Keystore-Anbieter verwenden

Für diese Funktion verwenden Sie die Standardklassen KeyStore und KeyPairGenerator oder KeyGenerator zusammen mit dem AndroidKeyStore-Anbieter, der in Android 4.3 (API-Ebene 18) eingeführt wurde.

AndroidKeyStore ist als KeyStore-Typ für die Verwendung mit der KeyStore.getInstance(type)-Methode und als Anbieter für die Verwendung mit den KeyPairGenerator.getInstance(algorithm, provider)- und KeyGenerator.getInstance(algorithm, provider)-Methoden registriert.

Da kryptografische Vorgänge zeitaufwendig sein können, sollten Apps AndroidKeyStore nicht im Hauptthread verwenden, damit die Benutzeroberfläche der App reaktionsschnell bleibt. Mit StrictMode können Sie Orte finden, an denen dies nicht der Fall ist.

Neuen privaten oder geheimen Schlüssel generieren

Wenn Sie eine neue KeyPair mit einer PrivateKey generieren möchten, müssen Sie die ursprünglichen X.509-Attribute des Zertifikats angeben. Mit KeyStore.setKeyEntry() können Sie das Zertifikat später durch ein von einer Zertifizierungsstelle signiertes Zertifikat ersetzen.

Verwende zum Generieren des Schlüsselpaars einen KeyPairGenerator mit 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();

Verschlüsselte Schlüssel in sichere Hardware importieren

Mit Android 9 (API-Level 28) und höher können Sie verschlüsselte Schlüssel mithilfe eines ASN.1-codierten Schlüsselformats sicher in den Schlüsselspeicher importieren. Der Keymaster entschlüsselt dann die Schlüssel im Schlüsselspeicher, sodass der Inhalt der Schlüssel nie als Klartext im Hostspeicher des Geräts erscheint. Dieser Prozess bietet zusätzliche Sicherheit bei der Schlüsselentschlüsselung.

Führen Sie die folgenden Schritte aus, um den sicheren Import verschlüsselter Schlüssel in den Schlüsselspeicher zu unterstützen:

  1. Generieren Sie ein Schlüsselpaar mit dem Zweck PURPOSE_WRAP_KEY. Wir empfehlen, diesem Schlüsselpaar auch eine Attestierung hinzuzufügen.

  2. Erstellen Sie auf einem vertrauenswürdigen Server oder Computer die ASN.1-Nachricht für die SecureKeyWrapper.

    Der Wrapper enthält das folgende Schema:

       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. Erstelle ein WrappedKeyEntry-Objekt und übergebe die ASN.1-Nachricht als Byte-Array.

  4. Übergeben Sie dieses WrappedKeyEntry-Objekt in die Überlast von setEntry(), die ein Keystore.Entry-Objekt akzeptiert.

Mit Schlüsselspeichereinträgen arbeiten

Sie können über alle Standard-KeyStore APIs auf den AndroidKeyStore-Anbieter zugreifen.

Einträge auflisten

Rufen Sie die Methode aliases() auf, um Einträge im Schlüsselspeicher aufzulisten:

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();

Daten signieren und bestätigen

Daten signieren, indem du die KeyStore.Entry aus dem Schlüsselspeicher abrufst und die Signature APIs verwendest, z. B. 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();

Prüfen Sie die Daten auch mit der verify(byte[])-Methode:

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);

Nutzerauthentifizierung für die Schlüsselnutzung erforderlich machen

Wenn Sie einen Schlüssel generieren oder in die AndroidKeyStore importieren, können Sie angeben, dass der Schlüssel nur verwendet werden darf, wenn der Nutzer authentifiziert wurde. Der Nutzer wird mit einem Teil seiner sicheren Sperrbildschirm-Anmeldedaten (Muster/PIN/Passwort, biometrische Anmeldedaten) authentifiziert.

Dies ist ein erweitertes Sicherheitsfeature, das im Allgemeinen nur dann nützlich ist, wenn eine Kompromittierung Ihres Anwendungsprozesses nach der Schlüsselgenerierung/-import (aber nicht vor oder während) die Anforderung einer Authentifizierung des Nutzers zur Verwendung des Schlüssels nicht umgehen kann.

Wenn ein Schlüssel nur dann verwendet werden darf, wenn sich der Nutzer authentifiziert hat, kannst du setUserAuthenticationParameters() aufrufen, um ihn für einen der folgenden Modi zu konfigurieren:

Autorisierung für einen bestimmten Zeitraum
Alle Schlüssel sind zur Verwendung autorisiert, sobald sich der Nutzer mit einer der angegebenen Anmeldedaten authentifiziert.
Für die Dauer eines bestimmten kryptografischen Vorgangs autorisieren

Jeder Vorgang, der einen bestimmten Schlüssel umfasst, muss vom Nutzer einzeln autorisiert werden.

Ihre App startet diesen Vorgang, indem sie authenticate() auf einer Instanz von BiometricPrompt aufruft.

Für jeden Schlüssel, den Sie erstellen, können Sie starke biometrische Anmeldedaten, Anmeldedaten für den Sperrbildschirm oder beide Anmeldedatentypen unterstützen. Rufe canAuthenticate() auf, um zu prüfen, ob der Nutzer die Anmeldedaten eingerichtet hat, auf die der Schlüssel deiner App angewiesen ist.

Wenn ein Schlüssel nur biometrische Anmeldedaten unterstützt, wird er standardmäßig ungültig, wenn neue biometrische Registrierungen hinzugefügt werden. Sie können den Schlüssel so konfigurieren, dass er gültig bleibt, wenn neue biometrische Registrierungen hinzugefügt werden. Übergeben Sie dazu false an setInvalidatedByBiometricEnrollment().

Weitere Informationen zum Hinzufügen von Funktionen für die biometrische Authentifizierung in Ihre App, einschließlich der Anzeige eines Dialogfelds für die biometrische Authentifizierung

Unterstützte Algorithmen

Blogartikel

Weitere Informationen finden Sie im Blogeintrag Key Store Access in ICS vereinheitlichen.