SubtleCrypto: wrapKey() メソッド

Baseline Widely available

This feature is well established and works across many devices and browser versions. It’s been available across browsers since July 2015.

安全なコンテキスト用: この機能は一部またはすべての対応しているブラウザーにおいて、安全なコンテキスト (HTTPS) でのみ利用できます。

wrapKey()SubtleCrypto インターフェイスのメソッドで、鍵を「ラップ」します。 これは、外部のポータブルな形式で鍵をエクスポートし、エクスポートされた鍵を暗号化することを意味しています。 鍵をラップすることで、保護されていないデータストア内や保護されていないネットワーク上での送信など、信頼されていない環境で鍵を保護することを支援します。

SubtleCrypto.exportKey() にて、鍵のエクスポート形式を指定します。 鍵をエクスポートするには、CryptoKey.extractabletrue に設定する必要があります。

しかし、wrapKey() はエクスポートする鍵も暗号化するので、暗号化に使用する鍵も渡す必要があります。 これは「ラッピング鍵」と呼ばれることもあります。

wrapKey() の逆は SubtleCrypto.unwrapKey() です。wrapKey がエクスポート+暗号化で構成されるのに対して、unwrapKey はインポート+複合で構成されます。

構文

js
wrapKey(format, key, wrappingKey, wrapAlgo)

引数

format

鍵が暗号化される前にエクスポートされるデータ形式を記述する文字列。以下のいずれかになります。

raw

Raw 形式。

pkcs8

PKCS #8 形式。

spki

SubjectPublicKeyInfo 形式。

jwk

JSON ウェブ鍵 形式。

key

ラップする CryptoKey

wrappingkey

エクスポートされたキーを暗号化する CryptoKey。これは wrapKey の使用法がなければなりません。

wrapAlgo

エクスポートされた鍵を暗号化するために使用するアルゴリズムと、必要な追加引数を指定するオブジェクトです。

  • RSA-OAEP を使用するには、 RsaOaepParams オブジェクトを渡してください。
  • AES-CTR を使用するには、 AesCtrParams オブジェクトを渡してください。
  • AES-CBC を使用するには、 AesCbcParams オブジェクトを渡してください。
  • AES-GCM を使用するには、 AesGcmParams オブジェクトを渡してください。
  • AES-KW を使用するには、文字列 "AES-KW" または { name: "AES-KW } の形のオブジェクトを渡してください。

返値

暗号化されたエクスポート鍵を格納した ArrayBuffer で履行されるプロミス (Promise) です。

例外

以下の例外が発生した場合、プロミスは拒否されます。

InvalidAccessError DOMException

ラップキーがリクエストされたラップアルゴリズムのキーでない場合に発生します。

NotSupported DOMException

未知のアルゴリズム、または暗号化やラッピングに適していないアルゴリズムを使用しようとしたときに発生します。

TypeError

無効な書式を使用しようとしたときに発生します。

対応しているアルゴリズム

暗号化に使用できるアルゴリズムはすべて、鍵に "wrapKey" の用途を設定している限り、鍵のラッピングにも使用できます。 鍵のラッピングには、 AES-KW という追加オプションがあります。

AES-KW

AES-KW は AES 暗号を鍵のラッピングに使用する方法です。

AES-GCM のような他の AES モードと比較して AES-KW を使用する利点の 1 つは、 AES-KW が初期化ベクトルを必要としないことです。 AES-KW を使用するには、入力は 64 ビットの倍数でなければなりません。

AES-KW は RFC 3394 で定義されています。

メモ: GitHub で動作例を試すことができます。

Raw ラップ

この例は AES 鍵をラップしています。 エクスポート形式として "raw" を使用し、暗号化にはパスワード由来の鍵による AES-KW を使用しています。完全なコードは GitHub で参照してください。

js
let salt;

/*
Get some key material to use as input to the deriveKey method.
The key material is a password supplied by the user.
*/
function getKeyMaterial() {
  const password = window.prompt("Enter your password");
  const enc = new TextEncoder();
  return window.crypto.subtle.importKey(
    "raw",
    enc.encode(password),
    { name: "PBKDF2" },
    false,
    ["deriveBits", "deriveKey"],
  );
}

/*
Given some key material and some random salt
derive an AES-KW key using PBKDF2.
*/
function getKey(keyMaterial, salt) {
  return window.crypto.subtle.deriveKey(
    {
      name: "PBKDF2",
      salt,
      iterations: 100000,
      hash: "SHA-256",
    },
    keyMaterial,
    { name: "AES-KW", length: 256 },
    true,
    ["wrapKey", "unwrapKey"],
  );
}

/*
Wrap the given key.
*/
async function wrapCryptoKey(keyToWrap) {
  // get the key encryption key
  const keyMaterial = await getKeyMaterial();
  salt = window.crypto.getRandomValues(new Uint8Array(16));
  const wrappingKey = await getKey(keyMaterial, salt);

  return window.crypto.subtle.wrapKey("raw", keyToWrap, wrappingKey, "AES-KW");
}

/*
Generate an encrypt/decrypt secret key,
then wrap it.
*/
window.crypto.subtle
  .generateKey(
    {
      name: "AES-GCM",
      length: 256,
    },
    true,
    ["encrypt", "decrypt"],
  )
  .then((secretKey) => wrapCryptoKey(secretKey))
  .then((wrappedKey) => console.log(wrappedKey));

PKCS #8 ラップ

この例は RSA 秘密署名鍵をラップしています。エクスポート形式として "pkcs8" を用い、暗号化にはパスワードから派生した鍵を用いた AES-GCM を使用しています。 完全なコードは GitHub で参照してください。

js
let salt;
let iv;

/*
Get some key material to use as input to the deriveKey method.
The key material is a password supplied by the user.
*/
function getKeyMaterial() {
  const password = window.prompt("Enter your password");
  const enc = new TextEncoder();
  return window.crypto.subtle.importKey(
    "raw",
    enc.encode(password),
    { name: "PBKDF2" },
    false,
    ["deriveBits", "deriveKey"],
  );
}

/*
Given some key material and some random salt
derive an AES-GCM key using PBKDF2.
*/
function getKey(keyMaterial, salt) {
  return window.crypto.subtle.deriveKey(
    {
      name: "PBKDF2",
      salt,
      iterations: 100000,
      hash: "SHA-256",
    },
    keyMaterial,
    { name: "AES-GCM", length: 256 },
    true,
    ["wrapKey", "unwrapKey"],
  );
}

/*
Wrap the given key.
*/
async function wrapCryptoKey(keyToWrap) {
  // get the key encryption key
  const keyMaterial = await getKeyMaterial();
  salt = window.crypto.getRandomValues(new Uint8Array(16));
  const wrappingKey = await getKey(keyMaterial, salt);
  iv = window.crypto.getRandomValues(new Uint8Array(12));

  return window.crypto.subtle.wrapKey("pkcs8", keyToWrap, wrappingKey, {
    name: "AES-GCM",
    iv,
  });
}

/*
Generate a sign/verify key pair,
then wrap the private key.
*/
window.crypto.subtle
  .generateKey(
    {
      name: "RSA-PSS",
      // Consider using a 4096-bit key for systems that require long-term security
      modulusLength: 2048,
      publicExponent: new Uint8Array([1, 0, 1]),
      hash: "SHA-256",
    },
    true,
    ["sign", "verify"],
  )
  .then((keyPair) => wrapCryptoKey(keyPair.privateKey))
  .then((wrappedKey) => {
    console.log(wrappedKey);
  });

SubjectPublicKeyInfo ラップ

この例では、 RSA 公開暗号鍵をラップしています。エクスポート形式として "spki" を用い、暗号化にはパスワード由来の鍵による AES-CBC を使用しています。 完全なコードは GitHub で参照してください。

js
let salt;
let iv;

/*
Get some key material to use as input to the deriveKey method.
The key material is a password supplied by the user.
*/
function getKeyMaterial() {
  const password = window.prompt("Enter your password");
  const enc = new TextEncoder();
  return window.crypto.subtle.importKey(
    "raw",
    enc.encode(password),
    { name: "PBKDF2" },
    false,
    ["deriveBits", "deriveKey"],
  );
}

/*
Given some key material and some random salt
derive an AES-CBC key using PBKDF2.
*/
function getKey(keyMaterial, salt) {
  return window.crypto.subtle.deriveKey(
    {
      name: "PBKDF2",
      salt,
      iterations: 100000,
      hash: "SHA-256",
    },
    keyMaterial,
    { name: "AES-CBC", length: 256 },
    true,
    ["wrapKey", "unwrapKey"],
  );
}

/*
Wrap the given key.
*/
async function wrapCryptoKey(keyToWrap) {
  // get the key encryption key
  const keyMaterial = await getKeyMaterial();
  salt = window.crypto.getRandomValues(new Uint8Array(16));
  const wrappingKey = await getKey(keyMaterial, salt);
  iv = window.crypto.getRandomValues(new Uint8Array(16));

  return window.crypto.subtle.wrapKey("spki", keyToWrap, wrappingKey, {
    name: "AES-CBC",
    iv,
  });
}

/*
Generate an encrypt/decrypt key pair,
then wrap it.
*/
window.crypto.subtle
  .generateKey(
    {
      name: "RSA-OAEP",
      // Consider using a 4096-bit key for systems that require long-term security
      modulusLength: 2048,
      publicExponent: new Uint8Array([1, 0, 1]),
      hash: "SHA-256",
    },
    true,
    ["encrypt", "decrypt"],
  )
  .then((keyPair) => wrapCryptoKey(keyPair.publicKey))
  .then((wrappedKey) => console.log(wrappedKey));

JSON ウェブ鍵ラップ

この例は ECDSA 秘密鍵をラップしています。エクスポート形式として "jwk" を用い、暗号化にはパスワード由来の鍵による AES-GCM を使用しています。 完全なコードは GitHub で参照してください。

js
let salt;
let iv;

/*
Get some key material to use as input to the deriveKey method.
The key material is a password supplied by the user.
*/
function getKeyMaterial() {
  const password = window.prompt("Enter your password");
  const enc = new TextEncoder();
  return window.crypto.subtle.importKey(
    "raw",
    enc.encode(password),
    { name: "PBKDF2" },
    false,
    ["deriveBits", "deriveKey"],
  );
}

/*
Given some key material and some random salt
derive an AES-GCM key using PBKDF2.
*/
function getKey(keyMaterial, salt) {
  return window.crypto.subtle.deriveKey(
    {
      name: "PBKDF2",
      salt,
      iterations: 100000,
      hash: "SHA-256",
    },
    keyMaterial,
    { name: "AES-GCM", length: 256 },
    true,
    ["wrapKey", "unwrapKey"],
  );
}

/*
Wrap the given key.
*/
async function wrapCryptoKey(keyToWrap) {
  // get the key encryption key
  const keyMaterial = await getKeyMaterial();
  salt = window.crypto.getRandomValues(new Uint8Array(16));
  const wrappingKey = await getKey(keyMaterial, salt);
  iv = window.crypto.getRandomValues(new Uint8Array(12));

  return window.crypto.subtle.wrapKey("jwk", keyToWrap, wrappingKey, {
    name: "AES-GCM",
    iv,
  });
}

/*
Generate a sign/verify key pair,
then wrap the private key
*/
window.crypto.subtle
  .generateKey(
    {
      name: "ECDSA",
      namedCurve: "P-384",
    },
    true,
    ["sign", "verify"],
  )
  .then((keyPair) => wrapCryptoKey(keyPair.privateKey))
  .then((wrappedKey) => console.log(wrappedKey));

仕様書

Specification
Web Cryptography API
# SubtleCrypto-method-wrapKey

ブラウザーの互換性

BCD tables only load in the browser

関連情報