Start · Sprachen · JavaScript · Referenz · SubtleCrypto.deriveKey()

SubtleCrypto.deriveKey()

Methode

Leitet aus einem Master-Schlüssel (Passwort, ECDH/X25519-Schlüssel oder HKDF-Material) einen neuen <code>CryptoKey</code> ab, z. B. einen AES-GCM-Schlüssel.

seit JavaScript Baseline: alle modernen Browser (sicherer Kontext) Kategorie: webapi

Signatur

SubtleCrypto.prototype.deriveKey(algorithm, baseKey, derivedKeyAlgorithm, extractable, keyUsages)

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.
  • PBKDF2: {name, salt, iterations, hash}
  • HKDF: {name, salt, info, hash}
  • ECDH/X25519: {name, public}, wobei public der öffentliche Schlüssel der Gegenseite ist
$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

Typ
Promise<CryptoKey>
Beschreibung
Ein 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);
{ name: 'AES-GCM', length: 256 } ['encrypt', 'decrypt']

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]));
true

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);
26

// Wichtig · Fallstricke

  • Nur in sicheren Kontexten: Unter http:// (außer localhost) ist crypto.subtle undefined.
  • 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 den usages, wird das Promise mit InvalidAccessError abgelehnt. Ein Schlüssel, der nur für deriveBits importiert 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 über HKDF die eigentlichen Schlüssel abzuleiten.
  • PBKDF2/HKDF-Schlüssel müssen mit extractable: false importiert werden, sonst wird ein Fehler ausgelöst.
  • extractable möglichst auf false setzen, damit der Schlüssel bei XSS-Angriffen nicht ausgelesen werden kann.
  • length für HMAC-Ziele ausdrücklich angeben, weil HKDF und PBKDF2 sonst keine Länge ableiten können.
  • Kompatibilität: X25519 wird 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 über globalThis.crypto.subtle bzw. require('node:crypto').webcrypto.subtle erreichbar.