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

SubtleCrypto.unwrapKey()

Methode

Entschlüsselt einen zuvor mit <code>wrapKey()</code> verpackten Schlüssel und importiert ihn als nutzbares <code>CryptoKey</code>-Objekt.

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

Signatur

SubtleCrypto.prototype.unwrapKey(format, wrappedKey, unwrappingKey, unwrapAlgo, unwrappedKeyAlgo, extractable, keyUsages)

Beschreibung

unwrapKey() ist das Gegenstück zu SubtleCrypto.wrapKey(). Die Methode nimmt einen verschlüsselten („verpackten“) Schlüssel entgegen, entschlüsselt ihn mit einem Entpackungsschlüssel (Key Encryption Key, KEK) und importiert das Ergebnis direkt als CryptoKey. Das ist gleichbedeutend mit decrypt() gefolgt von importKey(), allerdings in einem einzigen Schritt. Der Klartext-Schlüssel wird dabei nie als Daten an JavaScript übergeben.

Typische Einsatzfälle sind das sichere Speichern von Schlüsseln (z. B. in IndexedDB oder auf einem Server), Ende-zu-Ende-Verschlüsselung, bei der ein symmetrischer Sitzungsschlüssel mit dem öffentlichen RSA-Schlüssel des Empfängers verpackt wird, sowie passwortbasierter Schlüsselschutz mit PBKDF2 und AES-KW.

Als Entpackungsalgorithmen werden RSA-OAEP, AES-CTR, AES-CBC, AES-GCM und AES-KW unterstützt. Der unwrappingKey muss die Verwendung "unwrapKey" in seinen usages haben. Die Parameter format und unwrappedKeyAlgo müssen genau zu den Einstellungen passen, mit denen der Schlüssel beim Verpacken exportiert wurde.

Parameter

Name Typ Default Beschreibung
$format Pflicht "raw" | "pkcs8" | "spki" | "jwk" Das Datenformat, in dem der Schlüssel vor dem Verpacken exportiert wurde. Es muss mit dem format übereinstimmen, das bei wrapKey() verwendet wurde.
$wrappedKey Pflicht ArrayBuffer | TypedArray | DataView Der verpackte (verschlüsselte) Schlüssel als Binärdaten.
$unwrappingKey Pflicht CryptoKey Der Schlüssel, mit dem entschlüsselt wird. Seine usages müssen "unwrapKey" enthalten.
$unwrapAlgo Pflicht RsaOaepParams | AesCtrParams | AesCbcParams | AesGcmParams | {name: "AES-KW"} Der Algorithmus für das Entschlüsseln samt Parametern (z. B. iv bei AES-GCM). Er muss zum Verpacken passen.
$unwrappedKeyAlgo Pflicht AlgorithmIdentifier | RsaHashedImportParams | EcKeyImportParams | HmacImportParams Der Algorithmus des entpackten Schlüssels, wie bei importKey(), z. B. { name: "AES-GCM" } oder { name: "ECDSA", namedCurve: "P-256" }.
$extractable Pflicht boolean Gibt an, ob der entpackte Schlüssel später mit exportKey() oder wrapKey() exportiert werden darf.
$keyUsages Pflicht Array<string> Die erlaubten Verwendungen des neuen Schlüssels: "encrypt", "decrypt", "sign", "verify", "deriveKey", "deriveBits", "wrapKey", "unwrapKey".

Rückgabewert

Typ
Promise<CryptoKey>
Beschreibung
Ein Promise, das mit dem entpackten CryptoKey erfüllt wird. Das Promise wird mit einer DOMException abgelehnt (InvalidAccessError, NotSupportedError, OperationError, DataError, SyntaxError), wenn Schlüssel, Algorithmus oder Daten nicht zusammenpassen.

Beispiele

AES-Schlüssel mit AES-KW verpacken und wieder entpacken

const { subtle } = globalThis.crypto;

// Schlüssel zum Verpacken (Key Encryption Key)
const kek = await subtle.generateKey(
  { name: 'AES-KW', length: 256 },
  false,
  ['wrapKey', 'unwrapKey']
);

// Der eigentliche Datenschlüssel
const dataKey = await subtle.generateKey(
  { name: 'AES-GCM', length: 256 },
  true,
  ['encrypt', 'decrypt']
);

// Verpacken (z. B. zum Speichern)
const wrapped = await subtle.wrapKey('raw', dataKey, kek, { name: 'AES-KW' });
console.log('Verpackt:', wrapped.byteLength, 'Bytes');

// Entpacken
const restored = await subtle.unwrapKey(
  'raw',
  wrapped,
  kek,
  { name: 'AES-KW' },
  { name: 'AES-GCM' },
  false,
  ['encrypt', 'decrypt']
);
console.log(restored.algorithm, restored.usages);
Verpackt: 40 Bytes { name: 'AES-GCM', length: 256 } [ 'encrypt', 'decrypt' ]

Passwortgeschützten Schlüssel entpacken (PBKDF2 + AES-KW)

const { subtle } = globalThis.crypto;
const enc = new TextEncoder();

const deriveKek = async (password, salt) => {
  const base = await subtle.importKey(
    'raw', enc.encode(password), 'PBKDF2', false, ['deriveKey']
  );
  return subtle.deriveKey(
    { name: 'PBKDF2', salt, iterations: 600_000, hash: 'SHA-256' },
    base,
    { name: 'AES-KW', length: 256 },
    false,
    ['wrapKey', 'unwrapKey']
  );
};

// Beim Speichern
const salt = crypto.getRandomValues(new Uint8Array(16));
const secret = await subtle.generateKey({ name: 'AES-GCM', length: 256 }, true, ['encrypt', 'decrypt']);
const wrapped = await subtle.wrapKey('raw', secret, await deriveKek('geheim123', salt), 'AES-KW');

// Beim Laden: falsches Passwort -> OperationError
try {
  await subtle.unwrapKey('raw', wrapped, await deriveKek('falsch', salt),
    'AES-KW', 'AES-GCM', false, ['decrypt']);
} catch (err) {
  console.log('Fehler:', err.name);
}

const key = await subtle.unwrapKey('raw', wrapped, await deriveKek('geheim123', salt),
  'AES-KW', 'AES-GCM', false, ['encrypt', 'decrypt']);
console.log('Entpackt:', key.type);
Fehler: OperationError Entpackt: secret

Privaten ECDSA-Schlüssel (PKCS #8) mit AES-GCM entpacken

const { subtle } = globalThis.crypto;

const kek = await subtle.generateKey({ name: 'AES-GCM', length: 256 }, false, ['wrapKey', 'unwrapKey']);
const { privateKey } = await subtle.generateKey(
  { name: 'ECDSA', namedCurve: 'P-256' }, true, ['sign', 'verify']
);

const iv = crypto.getRandomValues(new Uint8Array(12));
const wrapped = await subtle.wrapKey('pkcs8', privateKey, kek, { name: 'AES-GCM', iv });

// IV muss mitgespeichert werden, sonst ist Entpacken unmöglich
const signingKey = await subtle.unwrapKey(
  'pkcs8',
  wrapped,
  kek,
  { name: 'AES-GCM', iv },
  { name: 'ECDSA', namedCurve: 'P-256' },
  false,
  ['sign']
);

const sig = await subtle.sign({ name: 'ECDSA', hash: 'SHA-256' }, signingKey, new TextEncoder().encode('Hallo'));
console.log('Signaturlänge:', sig.byteLength);
Signaturlänge: 64