Signatur
Beschreibung
deriveKey() erzeugt aus einem vorhandenen Basisschlüssel einen neuen geheimen Schlüssel. Typische Anwendungsfälle sind die passwortbasierte Verschlüsselung mit PBKDF2, der Schlüsselaustausch zwischen zwei Parteien mit ECDH bzw. X25519 sowie das Ableiten mehrerer Teilschlüssel aus einem gemeinsamen Geheimnis mit HKDF.
Anders als deriveBits(), das nur rohe Bytes liefert, gibt deriveKey() direkt ein fertiges CryptoKey-Objekt zurück. Dieses lässt sich sofort mit encrypt(), decrypt(), sign() usw. verwenden. Algorithmus und Länge des Zielschlüssels legst du über derivedKeyAlgorithm fest.
Der Basisschlüssel muss die Verwendung "deriveKey" in seinen usages haben. Bei PBKDF2 und HKDF importierst du ihn meist mit importKey("raw", …). Bei ECDH/X25519 ist er der eigene private Schlüssel. Den öffentlichen Schlüssel der Gegenseite übergibst du dann im Parameter algorithm.public.
Die Methode arbeitet asynchron und gibt ein Promise zurück. Sie ist nur in sicheren Kontexten (HTTPS, localhost) über crypto.subtle verfügbar.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $algorithm Pflicht | EcdhKeyDeriveParams | HkdfParams | Pbkdf2Params | {name: "X25519", public: CryptoKey} | Ableitungsalgorithmus mit seinen Parametern.
|
|
| $baseKey Pflicht | CryptoKey | Master-Schlüssel mit der Verwendung "deriveKey". Bei ECDH/X25519 ist das der eigene private Schlüssel. |
|
| $derivedKeyAlgorithm Pflicht | AesKeyGenParams | HmacImportParams | AlgorithmIdentifier | Algorithmus des Zielschlüssels, z. B. {name: "AES-GCM", length: 256} oder {name: "HMAC", hash: "SHA-256", length: 256}. |
|
| $extractable Pflicht | boolean | Gibt an, ob der abgeleitete Schlüssel später mit exportKey() exportiert werden darf. |
|
| $keyUsages Pflicht | string[] | Erlaubte Verwendungen des neuen Schlüssels, z. B. ["encrypt", "decrypt"] oder ["sign", "verify"]. Die Werte müssen zum Zielalgorithmus passen. |
Rückgabewert
Promise, das mit dem abgeleiteten CryptoKey erfüllt wird. Bei ungültigen Parametern oder fehlenden Rechten wird es abgelehnt, z. B. mit InvalidAccessError, NotSupportedError oder SyntaxError.Beispiele
AES-GCM-Schlüssel aus einem Passwort ableiten (PBKDF2)
const deriveFromPassword = async (password, salt) => {
const enc = new TextEncoder();
// Passwort als Basisschlüssel importieren
const baseKey = await crypto.subtle.importKey(
'raw', enc.encode(password), 'PBKDF2', false, ['deriveKey']
);
return crypto.subtle.deriveKey(
{ name: 'PBKDF2', salt, iterations: 600_000, hash: 'SHA-256' },
baseKey,
{ name: 'AES-GCM', length: 256 },
false,
['encrypt', 'decrypt']
);
};
const salt = crypto.getRandomValues(new Uint8Array(16));
const key = await deriveFromPassword('geheim123', salt);
console.log(key.algorithm, key.usages);
Gemeinsamen Schlüssel per ECDH aushandeln
const genPair = () => crypto.subtle.generateKey(
{ name: 'ECDH', namedCurve: 'P-256' }, false, ['deriveKey']
);
const alice = await genPair();
const bob = await genPair();
const derive = (privateKey, publicKey) => crypto.subtle.deriveKey(
{ name: 'ECDH', public: publicKey },
privateKey,
{ name: 'AES-GCM', length: 256 },
true,
['encrypt', 'decrypt']
);
const keyA = await derive(alice.privateKey, bob.publicKey);
const keyB = await derive(bob.privateKey, alice.publicKey);
// Beide Seiten erhalten denselben Schlüssel
const rawA = new Uint8Array(await crypto.subtle.exportKey('raw', keyA));
const rawB = new Uint8Array(await crypto.subtle.exportKey('raw', keyB));
console.log(rawA.every((b, i) => b === rawB[i]));
Teilschlüssel mit HKDF ableiten und damit verschlüsseln
const enc = new TextEncoder();
const secret = crypto.getRandomValues(new Uint8Array(32));
const master = await crypto.subtle.importKey('raw', secret, 'HKDF', false, ['deriveKey']);
const aesKey = await crypto.subtle.deriveKey(
{ name: 'HKDF', hash: 'SHA-256', salt: new Uint8Array(16), info: enc.encode('chat-verschluesselung') },
master,
{ name: 'AES-GCM', length: 256 },
false,
['encrypt', 'decrypt']
);
const iv = crypto.getRandomValues(new Uint8Array(12));
const cipher = await crypto.subtle.encrypt({ name: 'AES-GCM', iv }, aesKey, enc.encode('Hallo Welt'));
console.log(cipher.byteLength);
// Wichtig · Fallstricke
- Nur in sicheren Kontexten: Unter
http://(außerlocalhost) istcrypto.subtleundefined. - Salt und Iterationen speichern: Bei PBKDF2 musst du Salt, Iterationszahl und Hash zusammen mit dem Chiffrat ablegen. Ohne sie lässt sich derselbe Schlüssel nicht wieder ableiten. Verwende einen zufälligen Salt pro Passwort. OWASP empfiehlt für PBKDF2-SHA256 etwa 600.000 Iterationen.
- Schlüsselverwendung: Fehlt beim Basisschlüssel
"deriveKey"in denusages, wird das Promise mitInvalidAccessErrorabgelehnt. Ein Schlüssel, der nur fürderiveBitsimportiert wurde, reicht nicht aus. - ECDH nicht direkt als Schlüssel nutzen: Das rohe ECDH-Geheimnis ist nicht gleichmäßig zufällig verteilt. Für Protokolle empfiehlt es sich, per
deriveBits()das Geheimnis zu gewinnen und daraus überHKDFdie eigentlichen Schlüssel abzuleiten. - PBKDF2/HKDF-Schlüssel müssen mit
extractable: falseimportiert werden, sonst wird ein Fehler ausgelöst. extractablemöglichst auffalsesetzen, damit der Schlüssel bei XSS-Angriffen nicht ausgelesen werden kann.lengthfür HMAC-Ziele ausdrücklich angeben, weil HKDF und PBKDF2 sonst keine Länge ableiten können.- Kompatibilität:
X25519wird erst seit Kurzem in allen großen Browsern unterstützt (Chrome 133+, Firefox 130+, Safari 17+). ECDH, HKDF und PBKDF2 sind breit verfügbar. In Node.js ist die Methode überglobalThis.crypto.subtlebzw.require('node:crypto').webcrypto.subtleerreichbar.