Signatur
Beschreibung
HmacImportParams ist keine Klasse, die man mit new erzeugt. Es ist ein Wörterbuch (Dictionary), also ein ganz normales JavaScript-Objekt. Es beschreibt, wie die Web Crypto API rohes Schlüsselmaterial als HMAC-Schlüssel behandeln soll. Du übergibst es als algorithm-Parameter an SubtleCrypto.importKey(), SubtleCrypto.unwrapKey() (als unwrappedKeyAlgorithm) oder SubtleCrypto.deriveKey() (als derivedKeyAlgorithm).
Das Wichtigste daran ist die Eigenschaft hash. Sie legt die Hash-Funktion fest, die HMAC intern nutzt, also zum Beispiel SHA-256. Die Hash-Funktion gehört fest zum Schlüssel: Ein mit SHA-256 importierter Schlüssel lässt sich später nicht mit SHA-512 verwenden. Mit dem optionalen length gibst du die Schlüssellänge in Bit an. Fehlt der Wert, gilt beim Import die Länge des übergebenen Schlüsselmaterials.
Typische Einsatzfälle sind das Prüfen von Webhook-Signaturen (z. B. von GitHub oder Stripe), das Signieren von API-Anfragen, JWTs mit HS256 und das Ableiten eines HMAC-Schlüssels aus einem Passwort mit PBKDF2 oder HKDF. Zum Neuerzeugen eines Schlüssels nimmst du dagegen das verwandte Wörterbuch HmacKeyGenParams mit generateKey().
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $name Pflicht | string | Muss "HMAC" sein. |
|
| $hash Pflicht | string | Algorithm | Die zu verwendende Hash-Funktion: "SHA-1", "SHA-256", "SHA-384" oder "SHA-512", entweder als Zeichenkette oder als Objekt { name: "SHA-256" }. |
|
| $length | number | Länge des Schlüsselmaterials in Bit | Optionale Schlüssellänge in Bit. Das ist nur selten nötig. Bei deriveKey() gilt ohne Angabe die Blockgröße der Hash-Funktion (z. B. 512 Bit bei SHA-256). |
Rückgabewert
importKey(), unwrapKey() und deriveKey() liefern mit diesen Parametern ein Promise<CryptoKey>. Dessen algorithm ist ein HmacKeyAlgorithm mit name, hash und length.Beispiele
Geheimnis als HMAC-SHA-256-Schlüssel importieren und signieren
const encoder = new TextEncoder();
const key = await crypto.subtle.importKey(
'raw',
encoder.encode('mein-geheimer-schluessel'),
{ name: 'HMAC', hash: 'SHA-256' }, // HmacImportParams
false, // nicht exportierbar
['sign', 'verify']
);
const signatur = await crypto.subtle.sign('HMAC', key, encoder.encode('Hallo Welt'));
const hex = [...new Uint8Array(signatur)]
.map((b) => b.toString(16).padStart(2, '0'))
.join('');
console.log(key.algorithm);
console.log(hex.length);
Webhook-Signatur prüfen (z. B. GitHub, X-Hub-Signature-256)
const hexToBytes = (hex) =>
new Uint8Array(hex.match(/.{2}/g).map((b) => parseInt(b, 16)));
const verifyWebhook = async (secret, body, signatureHeader) => {
const enc = new TextEncoder();
const key = await crypto.subtle.importKey(
'raw',
enc.encode(secret),
{ name: 'HMAC', hash: { name: 'SHA-256' } },
false,
['verify']
);
const sigHex = signatureHeader.replace(/^sha256=/, '');
// verify() vergleicht in konstanter Zeit
return crypto.subtle.verify('HMAC', key, hexToBytes(sigHex), enc.encode(body));
};
const ok = await verifyWebhook('geheim', '{"event":"push"}', 'sha256=00ff');
console.log(ok);
HMAC-Schlüssel aus Passwort per PBKDF2 ableiten
const enc = new TextEncoder();
const basisKey = await crypto.subtle.importKey(
'raw', enc.encode('Passwort123'), 'PBKDF2', false, ['deriveKey']
);
const hmacKey = await crypto.subtle.deriveKey(
{ name: 'PBKDF2', salt: crypto.getRandomValues(new Uint8Array(16)), iterations: 310000, hash: 'SHA-256' },
basisKey,
{ name: 'HMAC', hash: 'SHA-512', length: 512 }, // HmacImportParams
false,
['sign', 'verify']
);
console.log(hmacKey.algorithm.hash.name, hmacKey.algorithm.length);
// Wichtig · Fallstricke
- Nur in sicheren Kontexten:
crypto.subtlegibt es ausschließlich unter HTTPS oderlocalhost. Sonst ist esundefined. lengthwird in Bit angegeben, nicht in Byte. Passt die Angabe nicht zum Schlüsselmaterial, wirftimportKey()einenDataError. Am einfachsten lässt dulengthbeim Import weg.- Wenn
hashfehlt oder falsch geschrieben ist (z. B.\"SHA256\"ohne Bindestrich), folgt einTypeErrorbzw.NotSupportedError. - Beim Import im Format
\"jwk\"muss das Feldalg(z. B.HS256) zuhashpassen. Sonst gibt es einenDataError. - Sicherheit: Prüfe Signaturen mit
crypto.subtle.verify()und nicht per String-Vergleich (===). Ein einfacher Vergleich ist anfällig für Timing-Angriffe. Setzeextractableauffalse, solange du den Schlüssel nicht exportieren musst. VermeideSHA-1bei neuen Anwendungen. - In Node.js ist die API über
globalThis.crypto.subtle(ab v19) bzw.require('node:crypto').webcrypto.subtle(ab v15) verfügbar.