Signatur
Beschreibung
Die Methode getPublicKey() der Schnittstelle AuthenticatorAttestationResponse liefert den öffentlichen Schlüssel eines Berechtigungsnachweises (Passkey), der gerade mit navigator.credentials.create() registriert wurde. Das Ergebnis ist ein ArrayBuffer mit der DER-Kodierung einer SubjectPublicKeyInfo-Struktur (X.509, RFC 5280).
Der große Vorteil: Ohne diese Methode müsste man das CBOR-kodierte attestationObject selbst parsen, die authenticatorData zerlegen und den COSE-Schlüssel in ein verwendbares Format umwandeln. Mit getPublicKey() erhält man direkt ein Standardformat, das Server-Bibliotheken (z. B. Node.js crypto.createPublicKey(), OpenSSL, Java, Go) oder SubtleCrypto.importKey("spki", …) verstehen.
Typischer Einsatz: Der Client liest bei der Registrierung den Schlüssel, zusammen mit getPublicKeyAlgorithm() und der Credential-ID, kodiert alles (meist Base64URL) und sendet es an die Relying Party, die den Schlüssel speichert und später zur Prüfung von Anmelde-Signaturen (AuthenticatorAssertionResponse.signature) verwendet.
Ist der verwendete Algorithmus dem Browser unbekannt oder lässt er sich nicht als SubjectPublicKeyInfo darstellen, gibt die Methode null zurück. Garantiert unterstützt werden EdDSA (−8), ES256 (−7) und RS256 (−257).
Rückgabewert
ArrayBuffer mit dem DER-kodierten SubjectPublicKeyInfo des neuen öffentlichen Schlüssels, oder null, wenn der Algorithmus nicht in diesem Format darstellbar ist bzw. vom Browser nicht unterstützt wird.Beispiele
Passkey registrieren und öffentlichen Schlüssel an den Server senden
const toBase64Url = (buffer) =>
btoa(String.fromCharCode(...new Uint8Array(buffer)))
.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
const registerPasskey = async (challenge, userId) => {
const credential = await navigator.credentials.create({
publicKey: {
challenge, // Uint8Array vom Server
rp: { name: 'Beispiel GmbH', id: location.hostname },
user: { id: userId, name: 'max@example.com', displayName: 'Max Mustermann' },
pubKeyCredParams: [
{ type: 'public-key', alg: -7 }, // ES256
{ type: 'public-key', alg: -257 } // RS256
],
authenticatorSelection: { residentKey: 'preferred', userVerification: 'preferred' }
}
});
const response = credential.response; // AuthenticatorAttestationResponse
const publicKey = response.getPublicKey();
if (publicKey === null) {
throw new Error('Algorithmus nicht als SPKI darstellbar – attestationObject serverseitig auswerten');
}
await fetch('/api/passkeys', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
id: credential.id,
publicKey: toBase64Url(publicKey),
algorithm: response.getPublicKeyAlgorithm(),
clientDataJSON: toBase64Url(response.clientDataJSON),
attestationObject: toBase64Url(response.attestationObject)
})
});
};
Schlüssel im Browser mit SubtleCrypto importieren
const importCredentialKey = async (response) => {
const spki = response.getPublicKey();
const alg = response.getPublicKeyAlgorithm();
if (alg !== -7 || !spki) return null; // nur ES256 in diesem Beispiel
const key = await crypto.subtle.importKey(
'spki',
spki,
{ name: 'ECDSA', namedCurve: 'P-256' },
true,
['verify']
);
console.log(key.type, key.algorithm.name, key.algorithm.namedCurve);
return key;
};
Serverseitig (Node.js) in PEM umwandeln
import { createPublicKey } from 'node:crypto';
// publicKeyB64Url wurde vom Client gesendet
const toPem = (publicKeyB64Url) => {
const der = Buffer.from(publicKeyB64Url, 'base64url');
const key = createPublicKey({ key: der, format: 'der', type: 'spki' });
return key.export({ format: 'pem', type: 'spki' });
};
console.log(toPem(storedPublicKey).split('\n')[0]);
// Wichtig · Fallstricke
- Nur bei Registrierung verfügbar: Die Methode existiert ausschließlich auf
AuthenticatorAttestationResponse(Ergebnis voncredentials.create()), nicht aufAuthenticatorAssertionResponsebei der Anmeldung. Den Schlüssel also sofort speichern. nullbehandeln: Bei unbekannten Algorithmen liefert die Methodenull. Für diesen Fall sollte der Server dasattestationObjectweiterhin selbst (CBOR/COSE) auswerten können.- Keine Sicherheitsgarantie: Der Wert stammt vom Client und ist nicht vertrauenswürdig. Der Server muss Challenge, Origin (
clientDataJSON) und ggf. die Attestierung prüfen; bei Bedarf sollte der Schlüssel aus demattestationObjectabgeglichen werden. - Binärdaten transportieren:
ArrayBufferlässt sich nicht direkt per JSON senden – vorher Base64URL-kodieren. Das Spread inString.fromCharCode(...)kann bei sehr großen Puffern (z. B. RSA) an Stack-Grenzen stoßen; dann in Blöcken kodieren. - Kompatibilität: Firefox unterstützt die Methode erst ab Version 119; ältere Browser benötigen einen Fallback über das
attestationObject. WebAuthn erfordert einen sicheren Kontext (HTTPS oderlocalhost).