SubtleCrypto: wrapKey() 方法

基线 广泛可用

此功能已经很成熟,并且在许多设备和浏览器版本上都能正常使用。 它自 2015 年 7 月.

报告反馈

安全上下文: 此功能仅在安全上下文(HTTPS)中可用,在一些或所有支持的浏览器中可用。

SubtleCrypto 接口的 wrapKey() 方法将密钥“包装”。 也就是说,它将密钥导出为外部、可移植的格式,然后对导出的密钥进行加密。 包装密钥有助于在不受信任的环境中保护密钥,例如在未受保护的数据存储区内或通过未受保护的网络进行传输。

SubtleCrypto.exportKey() 一样,您需要为密钥指定导出格式。 要导出密钥,必须将CryptoKey.extractable 设置为 true

但由于 wrapKey() 还会对要导出的密钥进行加密,因此您还需要传入必须用于加密它的密钥。 这有时被称为“包装密钥”。

语法

wrapKey() 的逆操作是SubtleCrypto.unwrapKey()wrapKey 由导出 + 加密组成,而 unwrapKey 由导入 + 解密组成。
wrapKey(format, key, wrappingKey, wrapAlgo)

js

参数

format

一个字符串,描述了在加密之前导出密钥时使用的格式。 它可以是以下之一

raw

原始 格式。

pkcs8

PKCS #8 格式。

spki

SubjectPublicKeyInfo 格式。

jwk

JSON Web 密钥 格式。

key

要包装的CryptoKey

wrappingkey

用于加密导出密钥的CryptoKey。 密钥必须设置 wrapKey 用途。

wrapAlgo

要使用AES-KW,请传递字符串 "AES-KW",或一个格式为 { name: "AES-KW" } 的对象。

返回值

一个Promise,它将完成并返回一个包含加密导出密钥的ArrayBuffer

异常

当遇到以下异常之一时,promise 将被拒绝

InvalidAccessError DOMException

当包装密钥不是请求的包装算法的密钥时引发。

NotSupported DOMException

当尝试使用未知或不适合加密或包装的算法时引发。

TypeError

支持的算法

当尝试使用无效格式时引发。

所有可用于加密的算法 也可用于密钥包装,只要密钥设置了“wrapKey”用途。 对于密钥包装,您还有AES-KW 的额外选项。

AES-KW

AES-KW 是一种使用 AES 密码进行密钥包装的方法。

与其他 AES 模式(如 AES-GCM)相比,使用 AES-KW 的一个优势是 AES-KW 不需要初始化向量。 要使用 AES-KW,输入必须是 64 位的倍数。

示例

AES-KW 在RFC 3394 中有规范。

注意: 您可以在 GitHub 上尝试运行示例

原始包装

wrapKey() 的逆操作是SubtleCrypto.unwrapKey()wrapKey 由导出 + 加密组成,而 unwrapKey 由导入 + 解密组成。
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));

此示例包装 AES 密钥。 它使用“raw”作为导出格式,并使用 AES-KW(使用密码派生的密钥)进行加密。 查看 GitHub 上的完整代码。

PKCS #8 包装

wrapKey() 的逆操作是SubtleCrypto.unwrapKey()wrapKey 由导出 + 加密组成,而 unwrapKey 由导入 + 解密组成。
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);
  });

此示例包装 RSA 私有签名密钥。 它使用“pkcs8”作为导出格式,并使用 AES-GCM(使用密码派生的密钥)进行加密。 查看 GitHub 上的完整代码。

SubjectPublicKeyInfo 包装

wrapKey() 的逆操作是SubtleCrypto.unwrapKey()wrapKey 由导出 + 加密组成,而 unwrapKey 由导入 + 解密组成。
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));

此示例包装 RSA 公共加密密钥。 它使用“spki”作为导出格式,并使用 AES-CBC(使用密码派生的密钥)进行加密。 查看 GitHub 上的完整代码。

JSON Web 密钥包装

wrapKey() 的逆操作是SubtleCrypto.unwrapKey()wrapKey 由导出 + 加密组成,而 unwrapKey 由导入 + 解密组成。
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));

规范

此示例包装 ECDSA 私有签名密钥。 它使用“jwk”作为导出格式,并使用 AES-GCM(使用密码派生的密钥)进行加密。 查看 GitHub 上的完整代码。
规范
# Web 加密 API

浏览器兼容性

SubtleCrypto-method-wrapKey

另请参阅