Signatur
Beschreibung
deriveBits() gehört zur Web Crypto API und leitet aus einem Basisschlüssel (CryptoKey) rohes Schlüsselmaterial ab. Unterstützt werden die Algorithmen ECDH und X25519 (Schlüsselaustausch), HKDF (Schlüsselableitung aus hochentropischem Material) und PBKDF2 (Ableitung aus Passwörtern).
Anders als deriveKey() bekommst du hier keinen fertigen CryptoKey, sondern einen ArrayBuffer mit der gewünschten Anzahl Bits. Das brauchst du z. B., wenn du einen Passwort-Hash speichern oder vergleichen willst, wenn aus einer Ableitung mehrere Schlüssel entstehen sollen (etwa Verschlüsselungs- und MAC-Schlüssel) oder wenn du das gemeinsame ECDH-Geheimnis selbst weiterverarbeitest.
Der Basisschlüssel muss "deriveBits" in seinen keyUsages haben. Bei PBKDF2 und HKDF importierst du dafür das Rohmaterial mit importKey("raw", …), bei ECDH/X25519 verwendest du den privaten Schlüssel des eigenen Schlüsselpaars.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $algorithm Pflicht | EcdhKeyDeriveParams | HkdfParams | Pbkdf2Params | {name: "X25519", public: CryptoKey} | Parameterobjekt für den Algorithmus.
|
|
| $baseKey Pflicht | CryptoKey | Basisschlüssel, aus dem abgeleitet wird. Braucht "deriveBits" in usages. Bei ECDH/X25519 der eigene private Schlüssel. |
|
| $length | number | null | null (nur ECDH/X25519) | Anzahl der abzuleitenden Bits (nicht Bytes), ein Vielfaches von 8. Bei HKDF und PBKDF2 Pflicht; bei ECDH/X25519 darf null übergeben werden, dann kommt die volle Länge des gemeinsamen Geheimnisses zurück. |
Rückgabewert
ArrayBuffer der abgeleiteten Bits erfüllt wird. Passen Parameter oder Schlüssel nicht, wird es mit einer DOMException abgelehnt (InvalidAccessError, NotSupportedError oder OperationError).Beispiele
Passwort-Hash mit PBKDF2 erzeugen
const hashPassword = async (password, salt) => {
const enc = new TextEncoder();
// Passwort als Basisschlüssel importieren
const baseKey = await crypto.subtle.importKey(
'raw', enc.encode(password), 'PBKDF2', false, ['deriveBits']
);
const bits = await crypto.subtle.deriveBits(
{ name: 'PBKDF2', hash: 'SHA-256', salt, iterations: 600_000 },
baseKey,
256 // 256 Bit = 32 Byte
);
return [...new Uint8Array(bits)]
.map((b) => b.toString(16).padStart(2, '0'))
.join('');
};
const salt = crypto.getRandomValues(new Uint8Array(16));
const hash = await hashPassword('geheim123', salt);
console.log(hash.length);
Gemeinsames Geheimnis per ECDH berechnen
const params = { name: 'ECDH', namedCurve: 'P-256' };
const alice = await crypto.subtle.generateKey(params, false, ['deriveBits']);
const bob = await crypto.subtle.generateKey(params, false, ['deriveBits']);
// Jede Seite nutzt eigenen privaten + fremden öffentlichen Schlüssel
const secretA = await crypto.subtle.deriveBits(
{ name: 'ECDH', public: bob.publicKey }, alice.privateKey, 256
);
const secretB = await crypto.subtle.deriveBits(
{ name: 'ECDH', public: alice.publicKey }, bob.privateKey, 256
);
const same = new Uint8Array(secretA).every((b, i) => b === new Uint8Array(secretB)[i]);
console.log(same);
Zwei Schlüssel aus einem Geheimnis per HKDF ableiten
const ikm = crypto.getRandomValues(new Uint8Array(32)); // z. B. ECDH-Geheimnis
const baseKey = await crypto.subtle.importKey('raw', ikm, 'HKDF', false, ['deriveBits']);
const bits = await crypto.subtle.deriveBits(
{
name: 'HKDF',
hash: 'SHA-256',
salt: new Uint8Array(16),
info: new TextEncoder().encode('app-v1 enc+mac'),
},
baseKey,
512
);
const encKey = await crypto.subtle.importKey(
'raw', bits.slice(0, 32), 'AES-GCM', false, ['encrypt', 'decrypt']
);
const macKey = await crypto.subtle.importKey(
'raw', bits.slice(32), { name: 'HMAC', hash: 'SHA-256' }, false, ['sign', 'verify']
);
console.log(encKey.algorithm.name, macKey.algorithm.name);
// Wichtig · Fallstricke
- Bits statt Bytes:
lengthwird in Bit angegeben. Für 32 Byte also256, nicht32. Werte, die kein Vielfaches von 8 sind, führen je nach Algorithmus zu einemOperationError. - Nur in sicheren Kontexten:
crypto.subtlegibt es nur über HTTPS bzw.localhost, auf unsicheren Seiten ist esundefined. - Schlüsselverwendung: Fehlt
\"deriveBits\"in denkeyUsagesdes Basisschlüssels, wird das Promise mitInvalidAccessErrorabgelehnt. Ein Schlüssel, der nur\"deriveKey\"erlaubt, reicht nicht. - PBKDF2-Iterationen: Wähle hohe Werte (OWASP empfiehlt für SHA-256 rund 600.000) und pro Benutzer ein zufälliges Salt. Die Berechnung läuft asynchron, braucht aber spürbar Rechenzeit.
- Rohes ECDH-Geheimnis: Das Ergebnis von ECDH/X25519 ist nicht gleichmäßig verteilt. Nutze es nicht direkt als Schlüssel, sondern leite es vorher per HKDF weiter ab.
- Kompatibilität: ECDH, HKDF und PBKDF2 laufen in allen modernen Browsern. X25519 ist neuer (Chrome 133+, Firefox 130+, Safari 17+). Das Weglassen von
lengthbzw.nullgeht nur bei ECDH/X25519. - Vergleiche: Wenn du abgeleitete Hashes vergleichst, mach das serverseitig und in konstanter Zeit, damit keine Timing-Angriffe möglich sind.