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 liegt | Typische Algorithmen | Attestierung | Benutzerprüfung | |
|---|---|---|---|---|
| Apple Secure Enclave | Ein eigener Koprozessor in Apple Silicon | P-256; ML-KEM-768/1024 und ML-DSA-65/87 (CryptoKit, OS 26+) | App Attest unter iOS; keine für beliebige Schlüssel | Touch ID, Face ID, Gerätecode über Zugriffssteuerung |
| Android Keystore, TEE | Die vertrauenswürdige Ausführungsumgebung des Haupt-SoC | RSA, EC, AES, HMAC | Schlüsselattestierung bis zu den Root-Zertifikaten von Google | Anmeldedaten der Displaysperre oder Biometrie, pro Verwendung oder pro Zeitfenster |
| Android StrongBox | Ein eingebettetes Secure Element oder eine integrierte Secure Enclave (Android 9+) | RSA 2048, P-256, AES, HMAC-SHA256, 3DES | Ebenso, mit Sicherheitsniveau StrongBox | Ebenso |
| TPM 2.0 | Ein diskreter Chip, ein Firmware-TPM oder ein virtuelles TPM | RSA 2048+, P-256, P-384 (herstellerabhängig) | AK vom EK zertifiziert, EK-Zertifikat vom Hersteller | Autorisierungswerte und Policies |
| YubiKey PIV | Ein Smartcard-Applet auf dem Token | RSA 1024/2048, P-256, P-384; RSA 3072/4096, Ed25519 und X25519 ab Firmware 5.7 | Attestierungszertifikat aus Slot f9, mit Kette bis zu Yubico | PIN-Policy und Touch-Policy pro Schlüssel |
| Cloud-HSM | Vom Anbieter betriebene HSMs | Siehe Signieren mit Cloud-KMS | Attestierungs-Statements von Google Cloud HSM | IAM, 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:
// 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)")
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.
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:
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)")
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östStrongBoxUnavailableExceptionaus.
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
attestationSecurityLevelden WertTrustedEnvironmentoderStrongBox; 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:
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
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:
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
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:
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 -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
- Erzeuge Schlüssel auf dem Gerät; importiere nie einen Schlüssel, den du als hardwaregestützt bezeichnen willst.
- 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.
- Binde die Verwendung an den Benutzer (Biometrie oder PIN bei jeder Verwendung), wo eine Signatur etwas autorisiert.
- Prüfe die Attestierung auf dem Server: Root-Zertifikate (aktuelle und rotierte), Widerruf, Nonce, Sicherheitsniveau, Schlüsseleigenschaften.
- Speichere nur öffentliche Schlüssel und opake Handles; rechne damit, dass Schlüssel verschwinden, und unterstütze eine erneute Registrierung.
Quellen
- Apple: Schlüssel mit der Secure Enclave schützen, CryptoKit
SecureEnclave.MLDSA65undSecureEnclave.MLKEM768 - Android: Android-Keystore-System, Schlüsselattestierung, Attestierungsschema
- TCG-Spezifikation TPM 2.0 Library (TPMS_ATTEST, eingeschränkte Schlüssel); tpm2-tools 5.8, swtpm 0.10.2
- Yubico: PIV-Attestierung, YubiKey und PIV (Algorithmen je nach Firmware), yubico-piv-tool 2.7.3
- Google Security Blog über die C2PA-Signatur auf Pixel