Zum Inhalt springen
ecdsa.com
← Alle Artikel

Hardwareschlüssel: Secure Enclave, StrongBox, TPM, PIV

Wir haben Schlüssel in der Secure Enclave dieses Macs erzeugt, darunter Post-Quanten-Schlüssel, außerdem in einem Software-TPM mit Attestierung, und die Attestierungsregeln von Android und Yubico gelesen. Das gemeinsame Versprechen ist eng gefasst und wertvoll: Der Schlüssel lässt sich nicht kopieren.

Kurz gesagt. Ein hardwaregestützter Schlüssel wird in einem separaten Chip oder einem isolierten Prozessor erzeugt und verlässt diesen nie im Klartext; dein Code hält nur ein Handle und bittet die Hardware, zu signieren. Das garantiert eine Sache zuverlässig: Der private Schlüssel lässt sich nicht vom Gerät kopieren, ein gestohlenes Festplatten-Image, Backup oder Speicherabbild ist also nutzlos. Es hindert Malware auf demselben Gerät nicht daran, die Hardware zum Signieren aufzufordern, es sei denn, du verlangst bei jeder Verwendung Benutzeranwesenheit oder Biometrie. Unterstützt werden nur wenige Algorithmen (P-256 überall, RSA auf manchen Plattformen, die Post-Quanten-Verfahren ML-DSA und ML-KEM in Apples Secure Enclave seit den 26er-Releases). Attestierung lässt einen Server prüfen, ob ein Schlüssel wirklich in solcher Hardware liegt, und zwar über eine Zertifikatskette bis zum Hersteller: zu den Root-Zertifikaten von Google bei Android, von Yubico bei YubiKeys und des TPM-Herstellers bei TPMs. Wer das Gerät verliert, verliert auch den Schlüssel; plane deshalb von Anfang an die Registrierung mehrerer Schlüssel und ihre Rotation ein.

Die Plattformen

Wo der Schlüssel liegtTypische AlgorithmenAttestierungBenutzerprüfung
Apple Secure EnclaveEin eigener Koprozessor in Apple SiliconP-256; ML-KEM-768/1024 und ML-DSA-65/87 (CryptoKit, OS 26+)App Attest unter iOS; keine für beliebige SchlüsselTouch ID, Face ID, Gerätecode über Zugriffssteuerung
Android Keystore, TEEDie vertrauenswürdige Ausführungsumgebung des Haupt-SoCRSA, EC, AES, HMACSchlüsselattestierung bis zu den Root-Zertifikaten von GoogleAnmeldedaten der Displaysperre oder Biometrie, pro Verwendung oder pro Zeitfenster
Android StrongBoxEin eingebettetes Secure Element oder eine integrierte Secure Enclave (Android 9+)RSA 2048, P-256, AES, HMAC-SHA256, 3DESEbenso, mit Sicherheitsniveau StrongBoxEbenso
TPM 2.0Ein diskreter Chip, ein Firmware-TPM oder ein virtuelles TPMRSA 2048+, P-256, P-384 (herstellerabhängig)AK vom EK zertifiziert, EK-Zertifikat vom HerstellerAutorisierungswerte und Policies
YubiKey PIVEin Smartcard-Applet auf dem TokenRSA 1024/2048, P-256, P-384; RSA 3072/4096, Ed25519 und X25519 ab Firmware 5.7Attestierungszertifikat aus Slot f9, mit Kette bis zu YubicoPIN-Policy und Touch-Policy pro Schlüssel
Cloud-HSMVom Anbieter betriebene HSMsSiehe Signieren mit Cloud-KMSAttestierungs-Statements von Google Cloud HSMIAM, keine Personen

Passkeys sind die häufigste Anwendung all dessen: Ein Plattform-Authenticator ist eine der ersten vier Zeilen, ergänzt um eine WebAuthn-Schnittstelle (Passkeys für Entwickler).

Apple Secure Enclave, getestet auf diesem Mac

Die Secure Enclave ist Apples isolierter Schlüsselprozessor. Mit CryptoKit ist ein Schlüssel eine einzige Zeile. Wir haben das auf einem Apple M4 Max mit macOS 27.0.1 und Swift 6.4 ausgeführt, als einfaches Befehlszeilentool ohne besondere Entitlements:

main.swift: ein P-256-Schlüssel in der Secure Enclave neben einem Software-Schlüssel
// Secure Enclave from a command-line tool: create a P-256 signing key, sign, export the public key,
// and look at what the "private key" object actually contains.
import CryptoKit
import Foundation

print("SecureEnclave.isAvailable: \(SecureEnclave.isAvailable)")

let key = try SecureEnclave.P256.Signing.PrivateKey()
let message = Data("release 1.4.2".utf8)
let signature = try key.signature(for: message)

print("public key (x9.63): \(key.publicKey.x963Representation.count) bytes")
print("signature (DER): \(signature.derRepresentation.count) bytes, raw r||s: \(signature.rawRepresentation.count) bytes")
print("verifies with the public key: \(key.publicKey.isValidSignature(signature, for: message))")

// dataRepresentation is an opaque blob: the key encrypted by the Secure Enclave for this device.
let blob = key.dataRepresentation
print("dataRepresentation: \(blob.count) bytes (opaque, usable only by this device's Secure Enclave)")
let restored = try SecureEnclave.P256.Signing.PrivateKey(dataRepresentation: blob)
print("restored from the blob signs too: \(restored.publicKey.isValidSignature(try restored.signature(for: message), for: message))")

// A software key from the same API family, for comparison: its raw bytes are the private scalar.
let software = P256.Signing.PrivateKey()
print("software key rawRepresentation: \(software.rawRepresentation.count) bytes (the private scalar itself)")
Ausgabe (macOS 27.0.1, Apple M4 Max; der öffentliche Schlüssel im PEM-Format ist weggelassen)
SecureEnclave.isAvailable: true
public key (x9.63): 65 bytes
signature (DER): 71 bytes, raw r||s: 64 bytes
verifies with the public key: true
dataRepresentation: 284 bytes (opaque, usable only by this device's Secure Enclave)
restored from the blob signs too: true
software key rawRepresentation: 32 bytes (the private scalar itself)

Eine App speichert die 284 Byte große dataRepresentation (im Schlüsselbund oder in einer Datei), um den Schlüssel später wieder zu verwenden; die Secure Enclave hat sie verschlüsselt, und nur die Enclave dieses Geräts kann sie verwenden. Es gibt überhaupt keinen Weg, den privaten Skalar abzufragen: Code, der das versucht, lässt sich nicht kompilieren.

swiftc mit einer Datei, die key.rawRepresentation aus einem Secure-Enclave-Schlüssel liest
export.swift:5:11: error: value of type 'SecureEnclave.P256.Signing.PrivateKey' has no member 'rawRepresentation'

Apples Anleitung Schlüssel mit der Secure Enclave schützen, geschrieben für die API SecKey des Security-Frameworks, sagt noch immer, dass die Secure Enclave „nur mit Schlüsseln auf der elliptischen Kurve NIST P-256 funktioniert“. CryptoKit geht weiter: SecureEnclave.MLDSA65, MLDSA87, MLKEM768 und MLKEM1024 sind ab iOS und macOS 26 verfügbar. Auf diesem Mac:

pq.swift: Post-Quanten-Schlüssel in der Secure Enclave
let mldsa = try SecureEnclave.MLDSA65.PrivateKey()
let signature = try mldsa.signature(for: message)
print("ML-DSA-65 in the Secure Enclave: public key \(mldsa.publicKey.rawRepresentation.count) bytes, signature \(signature.count) bytes")
print("verifies: \(mldsa.publicKey.isValidSignature(signature, for: message))")

let mlkem = try SecureEnclave.MLKEM768.PrivateKey()
let encapsulated = try mlkem.publicKey.encapsulate()
let shared = try mlkem.decapsulate(encapsulated.encapsulated)
print("ML-KEM-768 in the Secure Enclave: ciphertext \(encapsulated.encapsulated.count) bytes, shared secrets match: \(shared == encapsulated.sharedSecret)")
Ausgabe
ML-DSA-65 in the Secure Enclave: public key 1952 bytes, signature 3309 bytes
verifies: true
ML-KEM-768 in the Secure Enclave: ciphertext 1088 bytes, shared secrets match: true

Die Größen entsprechen den Werten aus FIPS 204 und FIPS 203. Für beliebige Schlüssel bietet Apple keine Attestierung, die ein Server prüfen könnte; unter iOS attestiert App Attest (DeviceCheck) die App und einen Secure-Enclave-Schlüssel, der eigens zu diesem Zweck erzeugt wird.

Android Keystore: TEE und StrongBox

Unter Android bleiben Schlüssel, die im Android Keystore erzeugt werden, „nicht exportierbar“: „Schlüsselmaterial gelangt nie in den Anwendungsprozess“, und wenn die sichere Hardware des Geräts den Algorithmus und die Parameter des Schlüssels unterstützt, „wird sein Schlüsselmaterial nie außerhalb der sicheren Hardware offengelegt“. Hardware gibt es in zwei Stufen:

  • TEE (KeyProperties.SECURITY_LEVEL_TRUSTED_ENVIRONMENT): eine isolierte Umgebung auf dem Hauptprozessor.
  • StrongBox (Android 9 und neuer, setIsStrongBoxBacked(true)): „eingebettete SEs oder integrierte Secure Enclaves (iSE), die im Vergleich zur TEE eine stärkere Isolation und Manipulationssicherheit bieten“, mit eigener CPU, eigenem Speicher, eigenem Zufallszahlengenerator und sicherem Timer. StrongBox unterstützt nur „RSA 2048, AES 128 und 256, ECDSA, ECDH P-256, HMAC-SHA256“ sowie Triple DES, und Google warnt, StrongBox sei „langsamer, ressourcenbeschränkter“, und schreibt: „Für die meisten Apps ist StrongBox nicht notwendig.“ Eine nicht unterstützte Anforderung löst StrongBoxUnavailableException aus.

Wo ein Schlüssel gelandet ist, prüfst du mit KeyInfo.getSecurityLevel() (API 31+; isInsideSecureHardware() bei älteren Versionen), und mit setUserAuthenticationParameters() bindest du die Verwendung an den Benutzer, entweder pro Vorgang oder für ein Zeitfenster nach dem Entsperren. Ein Vorbehalt von derselben Seite: Hardware „erzwingt Autorisierungen mit zeitlichem Gültigkeitsintervall in der Regel nicht, da sie normalerweise keine unabhängige, sichere Echtzeituhr hat“.

Schlüsselattestierung belegt das gegenüber deinem Server. Das Gerät liefert eine Zertifikatskette für den Schlüssel; das erste Zertifikat mit der Attestierungserweiterung (OID 1.3.6.1.4.1.11129.2.1.17) beschreibt den Schlüssel, seine Eigenschaften und sein Sicherheitsniveau. Diese Regeln zur Schlüsselattestierung lohnt es sich, in deinen Verifizierer zu übernehmen:

  • Prüfe auf einem Server, niemals auf dem Gerät selbst.
  • Das Root-Zertifikat muss eines der Attestierungs-Root-Zertifikate von Google sein, veröffentlicht unter android.googleapis.com/attestation/root. Ein ECDSA-P-384-Root-Zertifikat, „Key Attestation CA1“, „wird ab dem 1. Februar 2026 Ketten von Attestierungszertifikaten signieren“, neben dem seit Langem bestehenden RSA-Root-Zertifikat; akzeptiere beide.
  • Prüfe jedes Zertifikat gegen die Sperrliste von Google unter android.googleapis.com/attestation/status.
  • „Nur dem ersten Vorkommen der Erweiterung in der Kette kann vertraut werden“; spätere können von einem Angreifer stammen, der die Kette verlängert.
  • Verlange für attestationSecurityLevel den Wert TrustedEnvironment oder StrongBox; eine Kette zu einem anderen Root-Zertifikat oder eine Software-Attestierung sagt nichts über Hardware aus.

Google empfiehlt seine Kotlin-Bibliothek zur Prüfung statt eigener Verifizierer. Die C2PA-Signatur der Pixel-Kamera ist ein Beispiel, das darauf aufbaut: Die Signaturschlüssel für Claims werden „mit Android StrongBox im Sicherheitschip Titan M2 erzeugt und gespeichert“, und Zertifikate werden erst nach der Schlüsselattestierung ausgestellt (C2PA Content Credentials).

TPM 2.0: Schlüssel, Policies und Attestierung in einem Software-TPM

Ein TPM ist das Gegenstück für PCs und Server. Um seine Mechanik ohne Hardware zu zeigen, lief im Labor swtpm 0.10.2 (ein Software-TPM, das konstruktionsbedingt nichts schützt) mit tpm2-tools 5.8 in debian:forky-20261005-slim. Ein Signaturschlüssel wird unter einem Primärschlüssel mit Attributen erzeugt, die ihn an dieses TPM binden:

lab.sh (Auszug): ein nicht exportierbarer P-256-Signaturschlüssel, eine Signatur, Prüfung mit OpenSSL
tpm2_createprimary -Q -C o -g sha256 -G ecc256 -c primary.ctx
tpm2_create -Q -C primary.ctx -G ecc256:ecdsa-sha256 -a "fixedtpm|fixedparent|sensitivedataorigin|userwithauth|sign" -u sig.pub -r sig.priv
tpm2_load -Q -C primary.ctx -u sig.pub -r sig.priv -c sig.ctx
tpm2_readpublic -c sig.ctx -f pem -o sig.pem
tpm2_sign -Q -c sig.ctx -g sha256 -f plain -o msg.sig msg.txt
openssl dgst -sha256 -verify sig.pem -signature msg.sig msg.txt
Ausgabe (gefiltert)
attributes:
  value: fixedtpm|fixedparent|sensitivedataorigin|userwithauth|sign
sig.priv: 128 bytes (encrypted by the TPM, usable only under this parent)
Verified OK

fixedtpm und fixedparent bedeuten, dass der Schlüssel nie in ein anderes TPM oder unter einen anderen übergeordneten Schlüssel dupliziert werden kann; sensitivedataorigin bedeutet, dass das TPM ihn erzeugt hat. sig.priv ist der Schlüssel, verschlüsselt unter dem übergeordneten Schlüssel, und kann gefahrlos auf der Festplatte liegen. Die Signatur ist gewöhnliches ECDSA in DER, geprüft von OpenSSL mit dem exportierten öffentlichen Schlüssel.

Die Attestierung verwendet einen separaten Attestierungsschlüssel (AK) mit dem Attribut restricted. tpm2_certify lässt den AK eine Aussage über einen anderen Schlüssel signieren, einschließlich der Nonce des Verifizierers:

lab.sh (Auszug): den Signaturschlüssel mit einem eingeschränkten Attestierungsschlüssel zertifizieren
tpm2_create -Q -C primary.ctx -G ecc256:ecdsa-sha256:null -a "fixedtpm|fixedparent|sensitivedataorigin|userwithauth|restricted|sign" -u ak.pub -r ak.priv
tpm2_load -Q -C primary.ctx -u ak.pub -r ak.priv -c ak.ctx
tpm2_readpublic -Q -c ak.ctx -f pem -o ak.pem
printf 'server-nonce-7f3a' > nonce.bin
tpm2_certify -Q -c sig.ctx -C ak.ctx -g sha256 -q nonce.bin -o attest.bin -s attest.sig -f plain
openssl dgst -sha256 -verify ak.pem -signature attest.sig attest.bin
tpm2_print -t TPMS_ATTEST attest.bin
Ausgabe (gefiltert)
Verified OK
magic: ff544347
type: 8017
extraData: 7365727665722d6e6f6e63652d37663361
    name: 000b2d7a257558828e1caf174b67daabff261f8c02077768266be3cdc58015de4bee
    qualifiedName: 000b6d68f45fa311b20dc86b37e1659c1f06055e5641aa6a03e28cbb2caad6646fcd
name of the signing key: 000b2d7a257558828e1caf174b67daabff261f8c02077768266be3cdc58015de4bee

Die Struktur beginnt mit der Magic Number 0xff544347 („TPM_GENERATED“), Typ 0x8017 ist eine Certify-Aussage, extraData ist unsere Nonce in Hex, und name ist der Hash über den öffentlichen Bereich und die Attribute des zertifizierten Schlüssels: Er passt zum Signaturschlüssel. Warum ist das vertrauenswürdig? Weil sich ein eingeschränkter Schlüssel (restricted) weigert, Daten zu signieren, die mit der Magic Number TPM_GENERATED beginnen, sofern das TPM diese Daten nicht selbst erzeugt hat:

Derselbe Attestierungsschlüssel: gewöhnliche Daten, dann eine gefälschte Attestierung (ein Byte geändert)
ordinary data (msg.txt): signed by the attestation key
ERROR: Esys_Sign(0x3E0) - tpm:parameter(3):invalid ticket

Im realen Einsatz geht die Kette weiter: Der AK ist an den Endorsement Key des TPM gebunden, dessen Zertifikat der TPM-Hersteller ausgestellt hat, und der Verifizierer prüft diese Kette; das Format tpm für Attestierungs-Statements in WebAuthn funktioniert so. Ein Software-TPM hat kein solches Zertifikat, und genau deshalb darf seiner Attestierung niemand vertrauen.

YubiKey PIV und Attestierung

Das PIV-Applet eines YubiKeys speichert Schlüssel in Slots (9a Authentifizierung, 9c Signatur, 9d Schlüsselmanagement, 9e Kartenauthentifizierung), jeweils mit einer PIN-Policy und einer Touch-Policy, die bei der Erzeugung festgelegt werden. Die PIV-Attestierung zeigt, „dass ein bestimmter asymmetrischer Schlüssel auf dem Gerät erzeugt und nicht importiert wurde“: Der YubiKey signiert ein Zertifikat für den Schlüssel mit seinem Attestierungsschlüssel in Slot f9; zu diesem Attestierungsschlüssel gehört ein Zertifikat einer Yubico-PIV-CA. Die Befehle stammen aus yubico-piv-tool 2.7.3 (im Labor haben wir kein Token, deshalb wurde nur --help geprüft):

yubico-piv-tool (nicht mit einem Token ausgeführt)
yubico-piv-tool -a generate -s 9c -A ECCP256 --pin-policy=always --touch-policy=always -o 9c.pub
yubico-piv-tool -a attest -s 9c > 9c-attest.pem
yubico-piv-tool -a read-certificate -s f9 > f9-intermediate.pem

Das Attestierungszertifikat enthält Yubico-Erweiterungen: Firmwareversion (1.3.6.1.4.1.41482.3.3), Seriennummer (…3.7), PIN- und Touch-Policy (…3.8), Formfaktor (…3.9) sowie FIPS- oder CSPN-Status (…3.10, …3.11). Ein Verifizierer prüft die Kette vom Slot-Zertifikat über das Zertifikat aus f9 bis zum Root-Zertifikat von Yubico und liest dann die Policies: „touch always“ bedeutet, dass bei jeder Signatur eine Person physisch anwesend war. Yubico wechselt sein Root-Zertifikat: „YubiKeys mit Firmwareversion 5.7.4 und neuer werden von unserer neuen Root-CA signiert“, Verifizierer brauchen also beide Root-Zertifikate. Das CA/Browser Forum akzeptiert genau diese Art von Nachweis für Schlüssel zur Codesignatur (Authenticode), und OpenSSH nutzt dieselben Tokens stattdessen über FIDO2 (ssh-keygen).

Was Hardwareschlüssel dir nicht bieten

  • Schutz vor Code, der mit den Rechten des Benutzers läuft. Malware auf dem Gerät kann die Hardware bitten, etwas zu signieren; sie kann den Schlüssel nur nicht mitnehmen. Verlange für Schlüssel, auf die es ankommt, Benutzeranwesenheit (Berührung), Benutzerverifizierung (PIN, Biometrie) oder eine Bestätigung.
  • Verfügbarkeit. Ein verlorenes, gelöschtes oder defektes Gerät nimmt den Schlüssel mit, und das Löschen eines TPM (was manche Firmware-Updates und Neuinstallationen tun) macht die darunter angelegten Schlüssel ungültig. Registriere mindestens zwei Authenticators, halte einen Wiederherstellungsweg bereit und plane die Rotation ein.
  • Freie Algorithmenwahl. Du bekommst, was der Chip implementiert. Viele Plattformen bieten weiterhin nur P-256 und RSA; wenn ein Protokoll Ed25519 oder secp256k1 braucht, prüfe das, bevor du dein System um die Hardware herum planst.
  • Vertrauen in das, was die Hardware signiert. Ein sicherer Schlüssel signiert alles, was ihm das Betriebssystem übergibt; der Bildschirm mit „Zahlung genehmigen“ gehört zu demselben, möglicherweise kompromittierten Betriebssystem. Aus diesem Grund gibt es Hardware-Wallets mit eigenem Display.
  • Nachweis ohne Attestierung. Wenn dein Server keine Attestierungskette prüft, ist „der Schlüssel liegt in Hardware“ nur eine Behauptung des Clients.

Checkliste für den Einsatz von Hardwareschlüsseln

  1. Erzeuge Schlüssel auf dem Gerät; importiere nie einen Schlüssel, den du als hardwaregestützt bezeichnen willst.
  2. Wähle die Stufe, die du brauchst: Secure Enclave oder TEE für die meisten Apps, StrongBox oder ein Secure Element für besonders wertvolle Schlüssel, ein HSM für Server-Schlüssel.
  3. Binde die Verwendung an den Benutzer (Biometrie oder PIN bei jeder Verwendung), wo eine Signatur etwas autorisiert.
  4. Prüfe die Attestierung auf dem Server: Root-Zertifikate (aktuelle und rotierte), Widerruf, Nonce, Sicherheitsniveau, Schlüsseleigenschaften.
  5. Speichere nur öffentliche Schlüssel und opake Handles; rechne damit, dass Schlüssel verschwinden, und unterstütze eine erneute Registrierung.

Quellen