SubtleCrypto.deriveBits()
=========================

**安全上下文:** 此项功能仅在一些支持的浏览器的安全上下文（HTTPS）中可用。

`SubtleCrypto` 接口的 **`deriveBits()`** 方法用于从一个基本密钥派生比特序列（数组）。

它以基本密钥、使用的派生算法和需要派生的比特长度为参数。返回一个 `Promise`，会兑现一个包含派生比特序列的 `ArrayBuffer`。

此方法与 `SubtleCrypto.deriveKey()` 非常类似，区别在于 `deriveKey()` 返回的是 `CryptoKey` 对象，而不是 `ArrayBuffer`。本质上，`deriveKey()` 是由 `deriveBits()` 和 `importKey()` 这两个方法组合而成的。

该函数支持的派生算法与 `deriveKey()` 相同：ECDH、HKDF 和 PBKDF2。参见支持的算法以了解这些算法的详细信息。

语法
--


```js
deriveBits(algorithm, baseKey, length)
```

### 参数

`algorithm`

一个对象，用于定义使用的派生算法。

* 使用 ECDH，则传入 `EcdhKeyDeriveParams` (en-US) 对象。
* 使用 HKDF，则传入 `HkdfParams` (en-US) 对象。
* 使用 PBKDF2，则传入 `Pbkdf2Params` (en-US) 对象。

`baseKey`

一个 `CryptoKey`，表示派生算法的输入。如果算法（`algorithm`）是 ECDH，则该对象为 ECDH 的私钥。否则，它为派生函数的初始密钥材料（key material）：例如，对于 PBKDF2，它可能是一个密码（使用 `SubtleCrypto.importKey()` 导入为一个 `CryptoKey` 对象）。

`length`

一个数字，表示要派生的比特位数。为了兼容所有浏览器，此数字应为 8 的倍数。

### 返回值

一个 `Promise`，会兑现一个包含派生的比特序列的 `ArrayBuffer`。

### 异常

当发生一下几种异常时，promise 会被拒绝：

`OperationError` `DOMException`

若 `deriveBits()` 的 *length* 参数为 null，或在某些情况下如果 *length* 参数不是 8 的倍数，则会抛出此异常。

`InvalidAccessError` `DOMException`

若基本密钥不是要求的派生算法的密钥，或 `CryptoKey.usages` 的值不包含 `deriveBits`，则会抛出此异常。

`NotSupported` `DOMException`

若尝试使用未知或不适用于派生的算法，则会抛出此异常。

支持的算法
-----

参见 `deriveKey()` 文档的支持的算法部分。

示例
--

**备注：** 你可以在 GitHub 上尝试可用的示例。

### ECDH

在此示例中，Alice 和 Bob 分别生成了一个 ECDH 密钥对。

然后，我们使用 Alice 的私钥和 Bob 的公钥来派生一个共享密钥（shared secret）。在 GitHub 上查看完整代码。


```js
async function deriveSharedSecret(privateKey, publicKey) {
  const sharedSecret = await window.crypto.subtle.deriveBits(
    {
      name: "ECDH",
      namedCurve: "P-384",
      public: publicKey,
    },
    privateKey,
    128,
  );

  const buffer = new Uint8Array(sharedSecret, 0, 5);
  const sharedSecretValue = document.querySelector(".ecdh .derived-bits-value");
  sharedSecretValue.classList.add("fade-in");
  sharedSecretValue.addEventListener("animationend", () => {
    sharedSecretValue.classList.remove("fade-in");
  });
  sharedSecretValue.textContent = `${buffer}…[共 ${sharedSecret.byteLength} 字节]`;
}

// 生成两个 ECDH 密钥对：一个是 Alice 的，一个是 Bob 的
// 在正常的使用情况下，他们会单独生成密钥对，并安全地交换公钥。
const generateAlicesKeyPair = window.crypto.subtle.generateKey(
  {
    name: "ECDH",
    namedCurve: "P-384",
  },
  false,
  ["deriveBits"],
);

const generateBobsKeyPair = window.crypto.subtle.generateKey(
  {
    name: "ECDH",
    namedCurve: "P-384",
  },
  false,
  ["deriveBits"],
);

Promise.all([generateAlicesKeyPair, generateBobsKeyPair]).then((values) => {
  const alicesKeyPair = values[0];
  const bobsKeyPair = values[1];

  const deriveBitsButton = document.querySelector(".ecdh .derive-bits-button");
  deriveBitsButton.addEventListener("click", () => {
    // 然后 Alice 使用她的私钥和 Bob 的公钥生成一个密钥（secret）。
    // Bob 可以使用他的私钥和 Alice 的公钥来生成相同的密钥。
    deriveSharedSecret(alicesKeyPair.privateKey, bobsKeyPair.publicKey);
  });
});
```

### PBKDF2

在此示例中，我们要求用户提供密码，然后使用 PBKDF2 派生比特序列。在 GitHub 上查看完整代码。


```js
let salt;

/\*
获取用于作为 deriveBits 方法的输入的密钥材料。
密钥材料是用户提供的密码。
\*/
function getKeyMaterial() {
  const password = window.prompt("请输入你的密码");
  const enc = new TextEncoder();
  return window.crypto.subtle.importKey(
    "raw",
    enc.encode(password),
    { name: "PBKDF2" },
    false,
    ["deriveBits", "deriveKey"],
  );
}

/\*
通过用户提供的密码派生比特序列。
\*/
async function getDerivedBits() {
  const keyMaterial = await getKeyMaterial();
  salt = window.crypto.getRandomValues(new Uint8Array(16));
  const derivedBits = await window.crypto.subtle.deriveBits(
    {
      name: "PBKDF2",
      salt,
      iterations: 100000,
      hash: "SHA-256",
    },
    keyMaterial,
    256,
  );

  const buffer = new Uint8Array(derivedBits, 0, 5);
  const derivedBitsValue = document.querySelector(
    ".pbkdf2 .derived-bits-value",
  );
  derivedBitsValue.classList.add("fade-in");
  derivedBitsValue.addEventListener("animationend", () => {
    derivedBitsValue.classList.remove("fade-in");
  });
  derivedBitsValue.textContent = `${buffer}…[共 ${derivedBits.byteLength} 字节]`;
}

const deriveBitsButton = document.querySelector(".pbkdf2 .derive-bits-button");
deriveBitsButton.addEventListener("click", () => {
  getDerivedBits();
});
```

规范
--

| Specification |
| --- |
| Web Cryptography API # SubtleCrypto-method-deriveBits |

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

参见
--

* HKDF 规范。
* NIST 基于密码的密钥派生指南。
* 密码存储备忘录。
* 关于为 PBKDF2 选择迭代计数器的建议。