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

SubtleCrypto.deriveBits()

Methode

Leitet aus einem Basisschlüssel per ECDH, X25519, HKDF oder PBKDF2 eine Bitfolge ab und liefert sie als <code>ArrayBuffer</code> zurück.

seit JavaScript Baseline: alle modernen Browser (sichere Kontexte) Kategorie: webapi

Signatur

SubtleCrypto.prototype.deriveBits(algorithm, baseKey, length)

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

Typ
Promise<ArrayBuffer>
Beschreibung
Ein Promise, das mit einem 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);
64

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

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);
AES-GCM HMAC

// Wichtig · Fallstricke

  • Bits statt Bytes: length wird in Bit angegeben. Für 32 Byte also 256, nicht 32. Werte, die kein Vielfaches von 8 sind, führen je nach Algorithmus zu einem OperationError.
  • Nur in sicheren Kontexten: crypto.subtle gibt es nur über HTTPS bzw. localhost, auf unsicheren Seiten ist es undefined.
  • Schlüsselverwendung: Fehlt \"deriveBits\" in den keyUsages des Basisschlüssels, wird das Promise mit InvalidAccessError abgelehnt. 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 length bzw. null geht nur bei ECDH/X25519.
  • Vergleiche: Wenn du abgeleitete Hashes vergleichst, mach das serverseitig und in konstanter Zeit, damit keine Timing-Angriffe möglich sind.