SubtleCrypto: phương thức wrapKey()

Baseline Widely available

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

Secure context: This feature is available only in secure contexts (HTTPS), in some or all supporting browsers.

Note: This feature is available in Web Workers.

Phương thức wrapKey() của giao diện SubtleCrypto "bọc" một khóa. Điều này có nghĩa là nó xuất khóa ở định dạng bên ngoài, có thể di chuyển, sau đó mã hóa khóa đã xuất. Việc bọc khóa giúp bảo vệ khóa trong các môi trường không đáng tin, chẳng hạn như trong kho lưu trữ không được bảo vệ hoặc khi truyền qua mạng không bảo mật.

Như với SubtleCrypto.exportKey(), bạn chỉ định một định dạng xuất cho khóa. Để xuất một khóa, nó phải có CryptoKey.extractable được đặt thành true.

Nhưng vì wrapKey() cũng mã hóa khóa cần xuất, bạn cũng cần truyền khóa phải dùng để mã hóa nó. Đôi khi đây được gọi là "khóa bọc khóa".

Ngược lại với wrapKey()SubtleCrypto.unwrapKey(): trong khi wrapKey được cấu thành từ export + encrypt, unwrapKey được cấu thành từ import + decrypt.

Cú pháp

js
wrapKey(format, key, wrappingKey, wrapAlgo)

Tham số

format

Một chuỗi mô tả định dạng dữ liệu mà khóa sẽ được xuất trước khi được mã hóa. Nó có thể là một trong các giá trị sau:

raw

Định dạng Raw.

pkcs8

Định dạng PKCS #8.

spki

Định dạng SubjectPublicKeyInfo.

jwk

Định dạng JSON Web Key.

key

CryptoKey cần bọc.

wrappingKey

CryptoKey được sử dụng để mã hóa khóa đã xuất. Khóa phải có cách sử dụng wrapKey.

wrapAlgo

Một đối tượng chỉ định thuật toán sẽ sử dụng để mã hóa khóa đã xuất, và bất kỳ tham số bổ sung nào cần thiết:

Giá trị trả về

Một Promise được fulfill với một ArrayBuffer chứa khóa đã xuất được mã hóa.

Ngoại lệ

Promise bị reject khi gặp một trong các ngoại lệ sau:

InvalidAccessError DOMException

Ném lên khi khóa bọc không phải là khóa cho thuật toán bọc được yêu cầu.

NotSupported DOMException

Ném lên khi cố sử dụng một thuật toán không xác định hoặc không phù hợp để mã hóa hoặc bọc.

TypeError

Ném lên khi cố sử dụng một định dạng không hợp lệ.

Các thuật toán được hỗ trợ

Tất cả các thuật toán có thể sử dụng để mã hóa đều có thể sử dụng để bọc khóa, miễn là khóa có cách sử dụng "wrapKey". Để bọc khóa, bạn có thêm lựa chọn AES-KW.

AES-KW

AES-KW là một cách sử dụng bộ mã AES để bọc khóa.

Một ưu điểm của việc sử dụng AES-KW so với chế độ AES khác như AES-GCM là AES-KW không yêu cầu vector khởi tạo. Để sử dụng AES-KW, đầu vào phải là bội số của 64 bit.

AES-KW được chỉ định trong RFC 3394.

Ví dụ

Note: Bạn có thể thử các ví dụ hoạt động trên GitHub.

Bọc Raw

Ví dụ này bọc một khóa AES. Nó sử dụng "raw" làm định dạng xuất và AES-KW, với khóa phái sinh từ mật khẩu, để mã hóa nó. Xem mã đầy đủ trên GitHub.

js
let salt;

/*
Lấy một số tài liệu khóa để sử dụng làm đầu vào cho phương thức deriveKey.
Tài liệu khóa là mật khẩu do người dùng cung cấp.
*/
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"],
  );
}

/*
Với một số tài liệu khóa và một số salt ngẫu nhiên
phái sinh một khóa AES-KW bằng 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"],
  );
}

/*
Bọc khóa đã cho.
*/
async function wrapCryptoKey(keyToWrap) {
  // lấy khóa mã hóa khóa
  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");
}

/*
Tạo một khóa bí mật mã hóa/giải mã,
sau đó bọc nó.
*/
window.crypto.subtle
  .generateKey(
    {
      name: "AES-GCM",
      length: 256,
    },
    true,
    ["encrypt", "decrypt"],
  )
  .then((secretKey) => wrapCryptoKey(secretKey))
  .then((wrappedKey) => console.log(wrappedKey));

Bọc PKCS #8

Ví dụ này bọc một khóa riêng tư ký RSA. Nó sử dụng "pkcs8" làm định dạng xuất và AES-GCM, với khóa phái sinh từ mật khẩu, để mã hóa nó. Xem mã đầy đủ trên GitHub.

js
let salt;
let iv;

/*
Lấy một số tài liệu khóa để sử dụng làm đầu vào cho phương thức deriveKey.
Tài liệu khóa là mật khẩu do người dùng cung cấp.
*/
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"],
  );
}

/*
Với một số tài liệu khóa và một số salt ngẫu nhiên
phái sinh một khóa AES-GCM bằng 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"],
  );
}

/*
Bọc khóa đã cho.
*/
async function wrapCryptoKey(keyToWrap) {
  // lấy khóa mã hóa khóa
  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,
  });
}

/*
Tạo một cặp khóa ký/xác minh,
sau đó bọc khóa riêng tư.
*/
window.crypto.subtle
  .generateKey(
    {
      name: "RSA-PSS",
      // Cân nhắc sử dụng khóa 4096-bit cho các hệ thống yêu cầu bảo mật dài hạn
      modulusLength: 2048,
      publicExponent: new Uint8Array([1, 0, 1]),
      hash: "SHA-256",
    },
    true,
    ["sign", "verify"],
  )
  .then((keyPair) => wrapCryptoKey(keyPair.privateKey))
  .then((wrappedKey) => {
    console.log(wrappedKey);
  });

Bọc SubjectPublicKeyInfo

Ví dụ này bọc một khóa công khai mã hóa RSA. Nó sử dụng "spki" làm định dạng xuất và AES-CBC, với khóa phái sinh từ mật khẩu, để mã hóa nó. Xem mã đầy đủ trên GitHub.

js
let salt;
let iv;

/*
Lấy một số tài liệu khóa để sử dụng làm đầu vào cho phương thức deriveKey.
Tài liệu khóa là mật khẩu do người dùng cung cấp.
*/
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"],
  );
}

/*
Với một số tài liệu khóa và một số salt ngẫu nhiên
phái sinh một khóa AES-CBC bằng 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"],
  );
}

/*
Bọc khóa đã cho.
*/
async function wrapCryptoKey(keyToWrap) {
  // lấy khóa mã hóa khóa
  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,
  });
}

/*
Tạo một cặp khóa mã hóa/giải mã,
sau đó bọc nó.
*/
window.crypto.subtle
  .generateKey(
    {
      name: "RSA-OAEP",
      // Cân nhắc sử dụng khóa 4096-bit cho các hệ thống yêu cầu bảo mật dài hạn
      modulusLength: 2048,
      publicExponent: new Uint8Array([1, 0, 1]),
      hash: "SHA-256",
    },
    true,
    ["encrypt", "decrypt"],
  )
  .then((keyPair) => wrapCryptoKey(keyPair.publicKey))
  .then((wrappedKey) => console.log(wrappedKey));

Bọc JSON Web Key

Ví dụ này bọc một khóa riêng tư ký ECDSA. Nó sử dụng "jwk" làm định dạng xuất và AES-GCM, với khóa phái sinh từ mật khẩu, để mã hóa nó. Xem mã đầy đủ trên GitHub.

js
let salt;
let iv;

/*
Lấy một số tài liệu khóa để sử dụng làm đầu vào cho phương thức deriveKey.
Tài liệu khóa là mật khẩu do người dùng cung cấp.
*/
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"],
  );
}

/*
Với một số tài liệu khóa và một số salt ngẫu nhiên
phái sinh một khóa AES-GCM bằng 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"],
  );
}

/*
Bọc khóa đã cho.
*/
async function wrapCryptoKey(keyToWrap) {
  // lấy khóa mã hóa khóa
  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,
  });
}

/*
Tạo một cặp khóa ký/xác minh,
sau đó bọc khóa riêng tư
*/
window.crypto.subtle
  .generateKey(
    {
      name: "ECDSA",
      namedCurve: "P-384",
    },
    true,
    ["sign", "verify"],
  )
  .then((keyPair) => wrapCryptoKey(keyPair.privateKey))
  .then((wrappedKey) => console.log(wrappedKey));

Đặc tả kỹ thuật

Specification
Web Cryptography Level 2
# SubtleCrypto-method-wrapKey

Tương thích trình duyệt

Xem thêm