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

SubtleCrypto.wrapKey()

Methode

Exportiert einen <code>CryptoKey</code> und verschlüsselt ihn sofort mit einem zweiten Schlüssel, damit er sicher gespeichert oder übertragen werden kann.

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

Signatur

SubtleCrypto.prototype.wrapKey(format, key, wrappingKey, wrapAlgo)

Beschreibung

wrapKey() umhüllt einen Schlüssel („Key Wrapping“). Das heißt: Der Schlüssel wird zuerst in ein externes Format exportiert (raw, pkcs8, spki oder jwk) und dann mit einem Wrapping-Schlüssel verschlüsselt. Das Ergebnis ist ein ArrayBuffer mit dem verschlüsselten Schlüsselmaterial. Den Klartext des Schlüssels bekommt dabei kein JavaScript-Code zu sehen.

Typische Einsatzfälle: Man will einen Sitzungs- oder Datenschlüssel verschlüsselt in IndexedDB oder auf einem Server ablegen, einen symmetrischen Schlüssel mit dem öffentlichen RSA-Schlüssel eines Empfängers übertragen (hybride Verschlüsselung) oder Schlüssel mit einem aus einem Passwort abgeleiteten Schlüssel schützen (PBKDF2 + AES-KW). Das Gegenstück ist SubtleCrypto.unwrapKey(). Es entschlüsselt die Daten und importiert sie wieder als CryptoKey.

Als Umhüllungsalgorithmus sind erlaubt: RSA-OAEP, AES-GCM, AES-CBC, AES-CTR und AES-KW. Zwei Bedingungen müssen erfüllt sein: Der zu umhüllende Schlüssel muss extractable: true sein, und der Wrapping-Schlüssel braucht in seinen keyUsages den Eintrag "wrapKey".

Parameter

Name Typ Default Beschreibung
$format Pflicht "raw" | "pkcs8" | "spki" | "jwk" Das Exportformat, in das der Schlüssel vor dem Verschlüsseln gebracht wird. raw für symmetrische Schlüssel, pkcs8 für private Schlüssel, spki für öffentliche Schlüssel, jwk für alle Arten (wird als JSON-Text verschlüsselt).
$key Pflicht CryptoKey Der zu umhüllende Schlüssel. Er muss mit extractable: true erzeugt oder importiert worden sein.
$wrappingKey Pflicht CryptoKey Der Schlüssel, mit dem verschlüsselt wird. Er braucht den Verwendungszweck "wrapKey". Bei RSA-OAEP ist das der öffentliche Schlüssel.
$wrapAlgo Pflicht RsaOaepParams | AesGcmParams | AesCbcParams | AesCtrParams | AlgorithmIdentifier Algorithmus samt Parametern, z. B. { name: "AES-GCM", iv }, { name: "RSA-OAEP" } oder "AES-KW".

Rückgabewert

Typ
Promise<ArrayBuffer>
Beschreibung
Ein Promise, das mit einem ArrayBuffer erfüllt wird. Der Puffer enthält den verschlüsselten (umhüllten) Schlüssel. Das Promise wird mit einer DOMException abgelehnt: InvalidAccessError, wenn der Wrapping-Schlüssel nicht zum Algorithmus passt oder "wrapKey" fehlt, NotSupportedError bei unbekanntem Algorithmus, TypeError bei ungültigem Format.

Beispiele

AES-Schlüssel mit AES-KW umhüllen (raw-Format)

const subtle = globalThis.crypto.subtle;

// Datenschlüssel, der geschützt werden soll (muss extractable sein)
const dataKey = await subtle.generateKey(
  { name: 'AES-GCM', length: 256 },
  true,
  ['encrypt', 'decrypt']
);

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

const wrapped = await subtle.wrapKey('raw', dataKey, kek, 'AES-KW');
console.log('Umhüllte Länge:', wrapped.byteLength);
Umhüllte Länge: 40

Sitzungsschlüssel für einen Empfänger mit RSA-OAEP umhüllen

const subtle = globalThis.crypto.subtle;

// Schlüsselpaar des Empfängers
const { publicKey, privateKey } = await subtle.generateKey(
  {
    name: 'RSA-OAEP',
    modulusLength: 2048,
    publicExponent: new Uint8Array([1, 0, 1]),
    hash: 'SHA-256'
  },
  true,
  ['wrapKey', 'unwrapKey']
);

const sessionKey = await subtle.generateKey(
  { name: 'AES-GCM', length: 256 }, true, ['encrypt', 'decrypt']
);

// Absender umhüllt mit dem öffentlichen Schlüssel
const wrapped = await subtle.wrapKey('raw', sessionKey, publicKey, { name: 'RSA-OAEP' });

// Empfänger packt mit dem privaten Schlüssel wieder aus
const restored = await subtle.unwrapKey(
  'raw', wrapped, privateKey, { name: 'RSA-OAEP' },
  { name: 'AES-GCM' }, false, ['encrypt', 'decrypt']
);
console.log(wrapped.byteLength, restored.algorithm.name);
256 AES-GCM

Privaten Schlüssel passwortgeschützt als JWK umhüllen (PBKDF2 + AES-GCM)

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

const deriveWrappingKey = 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-GCM', length: 256 },
    false,
    ['wrapKey', 'unwrapKey']
  );
};

const { privateKey } = await subtle.generateKey(
  { name: 'ECDSA', namedCurve: 'P-256' }, true, ['sign', 'verify']
);

const salt = crypto.getRandomValues(new Uint8Array(16));
const iv = crypto.getRandomValues(new Uint8Array(12));
const wrappingKey = await deriveWrappingKey('geheimes Passwort', salt);

const wrapped = await subtle.wrapKey('jwk', privateKey, wrappingKey, { name: 'AES-GCM', iv });

// salt, iv und wrapped gemeinsam speichern (z. B. in IndexedDB)
console.log({ salt: salt.length, iv: iv.length, bytes: wrapped.byteLength > 0 });
{ salt: 16, iv: 12, bytes: true }