SubtleCrypto.encrypt()

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)中可用。

SubtleCrypto 接口的 encrypt() 方法用于加密数据。

它以用于加密的密钥、一些特定于算法的参数,以及待加密的数据(也称为“明文”)为参数;返回一个 Promise,会兑现加密后的数据(也称为“密文”)。

语法

js
encrypt(algorithm, key, data)

参数

algorithm

一个对象,用于指定使用的算法,以及需要的任何额外的参数:

key

一个包含了密钥的、用于加密的 CryptoKey 对象。

data

一个包含了待加密的数据(也称为明文)的 ArrayBufferTypedArrayDataView 对象。

返回值

一个 Promise,会兑现一个包含密文的 ArrayBuffer

异常

当遇到以下异常时,promise 将会被拒绝:

InvalidAccessError DOMException

当针对提供的密钥执行的操作无效时(如:加密算法无效,或特定于加密算法的密钥无效),将抛出该错误。

OperationError DOMException

当特定于操作的原因使得操作失败时(如:算法参数的大小无效,或 AES-GCM 明文长度超过 239−256 字节),将抛出该错误。

支持的算法

Web Crypto API 提供了支持 encrypt()decrypt() 操作的四种算法。

其中的 RSA-OAEP 算法是一种公钥加密系统

其他三种算法则都是对称加密算法,并且它们都是基于同一种基础加密,即 AES(Advanced Encryption Standard)。它们不同之处在于模式。Web Crypto API 支持以下三种 AES 模式:

  • CTR(Counter Mode,计数器模式)
  • CBC (Cipher Block Chaining,密码块链接)
  • GCM (Galois/Counter Mode,伽罗瓦/计数器模式)

强烈建议使用认证加密authenticated encryption),它可以检测密文是否已被攻击者篡改。使用认证也可以避免选择密文攻击chosen-ciphertext attack),即攻击者可以请求系统解密任意的消息,然后使用解密结果来倒推出关于密钥的一些信息。虽然 CTR 和 CBC 模式可以添加认证,但是它们默认不提供该操作,并且在手动实现它们的时候,很容易犯一些微小但严重的错误。GCM 提供了内置的认证,因此常常推荐使用这种模式。

RSA-OAEP

RSA-OAEP 公钥加密系统,规范定于 RFC 3447

AES-CTR

使用计数器模式的 AES 算法,规范定于 NIST SP800-38A

AES 是一种分组加密算法,这意味着它将消息分成多个模块,然后逐块进行加密。在计数器模式下,每加密一个消息块,就会混入一个额外的数据块。这个额外的模块被称为“计数器模块”(counter block)。

给定的计数器模块绝不能与同一个密钥一起使用超过一次:

  • 给定一条 n 个模块长的消息,其中的每一个模块必须使用不同的计数器模块。
  • 如果使用同一个密钥加密多条消息,则必须对所有消息的所有模块使用不同的计数器模块。

通常,这是通过将初始计数器模块拆分为两个拼接起来的部分来实现:

  • 一个 nonce(即,仅能使用一次的数字)。对于消息中的每一个模块,模块的 nonce 部分保持不变。每次要加密一条新消息时,都会选择一个新的 nonce。nonce 不必是私密的,但不能将同一 nonce 与相同的密钥重复使用。
  • 一个计数器。每次加密一个模块时,这一部分的值会递增。

本质上:nonce 应该确保计数器模块不会在不同的消息间重复使用,而计数器应能确保计数器模块不会在单条消息中重复使用。

备注:参见 NIST SP800-38A 标准的附录 B 以了解详情。

AES-CBC

使用密码块链接模式的 AES 算法,规范定于 NIST SP800-38A

AES-GCM

使用伽罗瓦/计数器模式的 AES 算法,规范定于 NIST SP800-38D

这种模式与上面的模式不同之处在于,GCM 是一种“认证”(authenticated)模式,意味着它包含了检测密文是否被攻击者篡改的功能。

示例

备注:你可以在 GitHub 上尝试这个可用的示例

RSA-OAEP

以下代码获取文本框中的内容,并对其编码以进行加密,然后使用 RSA-OAEP 加密数据。在 GitHub 中查看完整的代码。

js
function getMessageEncoding() {
  const messageBox = document.querySelector(".rsa-oaep #message");
  let message = messageBox.value;
  let enc = new TextEncoder();
  return enc.encode(message);
}

function encryptMessage(publicKey) {
  let encoded = getMessageEncoding();
  return window.crypto.subtle.encrypt(
    {
      name: "RSA-OAEP",
    },
    publicKey,
    encoded,
  );
}

AES-CTR

以下代码同样获取文本框内容,进行编码后使用 AES 的计数器(CTR)模式加密,完整代码:以下代码获取文本框中的内容,并对其编码以进行加密,然后使用计数器(CTR)模式的 AES 加密数据。在 GitHub 中查看完整的代码。

js
function getMessageEncoding() {
  const messageBox = document.querySelector(".aes-ctr #message");
  let message = messageBox.value;
  let enc = new TextEncoder();
  return enc.encode(message);
}

function encryptMessage(key) {
  let encoded = getMessageEncoding();
  // 解密时也需要使用 counter
  counter = window.crypto.getRandomValues(new Uint8Array(16));
  return window.crypto.subtle.encrypt(
    {
      name: "AES-CTR",
      counter,
      length: 64,
    },
    key,
    encoded,
  );
}
js
let iv = window.crypto.getRandomValues(new Uint8Array(16));
let key = window.crypto.getRandomValues(new Uint8Array(16));
let data = new Uint8Array(12345);
// 加密函数使用 promise 包裹,因此我们必须使用 await,
// 并确保包含此代码的函数是一个异步函数
// 加密函数需要一个 cryptokey 对象
const key_encoded = await window.crypto.subtle.importKey(
  "raw",
  key.buffer,
  "AES-CTR",
  false,
  ["encrypt", "decrypt"],
);
const encrypted_content = await window.crypto.subtle.encrypt(
  {
    name: "AES-CTR",
    counter: iv,
    length: 128,
  },
  key_encoded,
  data,
);

// Uint8Array
console.log(encrypted_content);

AES-CBC

以下代码获取文本框中的内容,并对其编码以进行加密,然后使用密码块链接(CBC)模式的 AES 加密数据。在 GitHub 中查看完整的代码。

js
function getMessageEncoding() {
  const messageBox = document.querySelector(".aes-cbc #message");
  let message = messageBox.value;
  let enc = new TextEncoder();
  return enc.encode(message);
}

function encryptMessage(key) {
  let encoded = getMessageEncoding();
  // 解密时也需要使用 iv
  iv = window.crypto.getRandomValues(new Uint8Array(16));
  return window.crypto.subtle.encrypt(
    {
      name: "AES-CBC",
      iv: iv,
    },
    key,
    encoded,
  );
}

AES-GCM

以下代码获取文本框中的内容,并对其编码以进行加密,然后使用伽罗瓦/计数器(GCM)模式的 AES 加密数据。在 GitHub 中查看完整的代码。

js
function getMessageEncoding() {
  const messageBox = document.querySelector(".aes-gcm #message");
  const message = messageBox.value;
  const enc = new TextEncoder();
  return enc.encode(message);
}

function encryptMessage(key) {
  const encoded = getMessageEncoding();
  // 解密时也需要使用 iv
  const iv = window.crypto.getRandomValues(new Uint8Array(12));
  return window.crypto.subtle.encrypt(
    { name: "AES-GCM", iv: iv },
    key,
    encoded,
  );
}

规范

Specification
Web Cryptography API
# SubtleCrypto-method-encrypt

浏览器兼容性

BCD tables only load in the browser

参见