SubtleCrypto.importKey()
========================

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

`SubtleCrypto` 接口的 **`importKey()`** 方法用于导入密钥：也就是说，它以外部可移植格式的密钥作为输入，并给出对应的、可用于 Web Crypto API 的 `CryptoKey` 对象。

该函数接受多种导入的格式：参阅支持的格式以获取详细信息。

语法
--


```js
importKey(format, keyData, algorithm, extractable, keyUsages)
```

### 参数

`format`

一个字符串，用于描述要导入的密钥的数据格式。可以是以下值之一：

* `raw`：Raw 格式。
* `pkcs8`：PKCS #8 格式。
* `spki`：SubjectPublicKeyInfo 格式。
* `jwk`：JSON Web Key 格式。

`keyData`

一个 `ArrayBuffer`、TypedArray、`DataView` 或 `JSONWebKey` 对象，包含了给定格式的密钥。

`algorithm`

一个对象，定义了要导入的密钥的类型和特定于算法的额外参数。

* 对于 RSASSA-PKCS1-v1\_5、RSA-PSS 或 RSA-OAEP：传递 `RsaHashedImportParams` (en-US) 对象。
* 对于 ECDSA 或 ECDH：传递 `EcKeyImportParams` (en-US) 对象。
* 对于 HMAC：传递 `HmacImportParams` (en-US) 对象。
* 对于 AES-CTR、AES-CBC、AES-GCM 或 AES-KW：传递标识算法的字符串，或一个 `{ "name": ALGORITHM }` 形式的对象，其中的 `ALGORITHM` 是算法的名称。
* 对于 PBKDF2：传递字符串 `PBKDF2`。
* 对于 HKDF：传递字符串 `HKDF`。

`extractable`

一个布尔值，表示是否可能使用 `SubtleCrypto.exportKey()` 或 `SubtleCrypto.wrapKey()` 方法来导出密钥。

`keyUsages`

一个数组，表示生成出来的密钥可被用于做什么，数组元素可能的值有：

* `encrypt`：密钥可用于加密消息。
* `decrypt`：密钥可用于解密消息。
* `sign`：密钥可用于对消息进行签名。
* `verify`：密钥可用于验证签名。
* `deriveKey`：密钥可用于派生新的密钥。
* `deriveBits`：密钥可用于派生比特序列。
* `wrapKey`：密钥可用于包装一个密钥。
* `unwrapKey`：密钥可用于解开一个密钥的包装。

### 返回值

一个 `Promise`，会兑现为表示导入的密钥的 `CryptoKey` 对象。

### 异常

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

`SyntaxError` `DOMException`

当结果是类型为 `secret` 或 `private` 的解包装的密钥，但 `keyUsages` 参数为空时触发。

`TypeError`

当尝试使用无效的格式，或 `keyData` 与给定的格式不匹配时触发。

支持的格式
-----

此 API 支持四种不同的密钥导入/导出格式：Raw、PKCS #8、SubjectPublicKeyInfo 和 JSON Web Key。

### Raw

你可以使用此格式导入或导出 AES 和 HMAC 的密钥，或椭圆曲线算法的公钥。

这种格式的密钥需要已包含密钥的原始字节的 `ArrayBuffer` 对象的形式提供。

### PKCS #8

你可以使用此格式导入或导出 RSA 和椭圆曲线算法的私钥。

PKCS #8 格式在 RFC 5208 中定义，并使用 ASN.1 表示法 进行表示：
```

PrivateKeyInfo ::= SEQUENCE {
    version                   Version,
    privateKeyAlgorithm       PrivateKeyAlgorithmIdentifier,
    privateKey                PrivateKey,
    attributes           [0]  IMPLICIT Attributes OPTIONAL }
```


`importKey()` 方法期望接收一个包含 DER 编码的 `PrivateKeyInfo` 的 `ArrayBuffer` 对象。DER 是一组将 ASN.1 结构编码为二进制格式的规则。

你最有可能遇到的此类对象的格式为 PEM 格式。PEM 格式是一种使用 ASCII 编码二进制数据的方法。它由头部、尾部和中间 base64 编码 的二进制数据组成。PEM 编码的 `PrivateKeyInfo` 类似于这样：
```

-----BEGIN PRIVATE KEY-----
MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDAU9BD0jxDfF5OV380z
9VIEUN2W5kJDZ3hbuaDenCxLiAMsoquKTfFaou71eLdN0TShZANiAARMUhCee/cp
xmjGc1roj0D0k6VlUqtA+JVCWigXcIAukOeTHCngZDKCrD4PkXDBvbciJdZKvO+l
ml2FIkoovZh/8yeTKmjUMb804g6OmjUc9vVojCRV0YdaSmYkkJMJbLg=
-----END PRIVATE KEY-----
```


要将其转换为你可以传递给 `importKey()` 的格式，你需要做两件事：

* 头部和尾部之间的部分使用 `window.atob()` 进行 base64 解码。
* 将生成的字符串转换为 `ArrayBuffer`。

请参阅示例部分，以获得更具体的指导。

### SubjectPublicKeyInfo

你可以使用此格式来导入/导出 RSA 和椭圆曲线算法的公钥。

`SubjectPublicKey` 在 RFC 5280 第 4.1 节中定义，使用 ASN.1 表示法 进行表示：
```

SubjectPublicKeyInfo  ::=  SEQUENCE  {
    algorithm            AlgorithmIdentifier,
    subjectPublicKey     BIT STRING  }
```


类似于 PKCS #8，`importKey()` 方法期望接收一个包含 DER 编码的 `SubjectPublicKeyInfo` 的 `ArrayBuffer` 对象。

同样地，你最有可能遇到的此类对象的格式为 PEM 格式。PEM 编码的 `SubjectPublicKeyInfo` 类似于这样：
```

-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3j+HgSHUnc7F6XzvEbD0
r3M5JNy+/kabiJVu8IU1ERAl3Osi38VgiMzjDBDOrFxVzNNzl+SXAHwXIV5BHiXL
CQ6qhwYsDgH6OqgKIwiALra/wNH4UHxj1Or/iyAkjHRR/kGhUtjyVCjzvaQaDpJW
2G+syd1ui0B6kJov2CRUWiPwpff8hBfVWv8q9Yc2yD5hCnykVL0iAiyn+SDAk/rv
8dC5eIlzCI4efUCbyG4c9O88Qz7bS14DxSfaPTy8P/TWoihVVjLaDF743LgM/JLq
CDPUBUA3HLsZUhKm3BbSkd7Q9Ngkjv3+yByo4/fL+fkYRa8j9Ypa2N0Iw53LFb3B
gQIDAQAB
-----END PUBLIC KEY-----
```


与 PKCS #8 一样，你可以传递给 `importKey()` 的格式，你需要做两件事：

* 头部和尾部之间的部分使用 `window.atob()` 进行 base64 解码。
* 将生成的字符串转换为 `ArrayBuffer`。

请参阅示例部分，以获得更具体的指导。

### JSON Web Key

你可以使用 JSON Web Key 格式来导入/导出 RSA 和椭圆曲线算法的公钥或私钥，以及 AES 和 HMAC 的密钥。

JSON Web Key 格式在 RFC 7517 中定义。它描述了一种将公钥、私钥和密钥表示为 JSON 对象的方法。

JSON Web Key 看起来像这样（这是一个椭圆曲线算法的私钥）：


```json
{
  "crv": "P-384",
  "d": "wouCtU7Nw4E8\_7n5C1-xBjB4xqSb\_liZhYMsy8MGgxUny6Q8NCoH9xSiviwLFfK\_",
  "ext": true,
  "key\_ops": ["sign"],
  "kty": "EC",
  "x": "SzrRXmyI8VWFJg1dPUNbFcc9jZvjZEfH7ulKI1UkXAltd7RGWrcfFxqyGPcwu6AQ",
  "y": "hHUag3OvDzEr0uUQND4PXHQTXP5IDGdYhJhL-WLKjnGjQAw0rNGy5V29-aV-yseW"
};
```

示例
--

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

### 导入 Raw 格式的密钥

该示例从一个包含字节序列的 `ArrayBuffer` 导入将被使用的 AES 密钥。在 GitHub 上查看完整代码。


```js
const rawKey = window.crypto.getRandomValues(new Uint8Array(16));

/\*
从一个包含原始字节序列的 ArrayBuffer 导入 AES 密钥。
传入包含字节序列的 ArrayBuffer，返回一个 Promise，
会被兑现为一个表示密钥的 CryptoKey 对象。
\*/
function importSecretKey(rawKey) {
  return window.crypto.subtle.importKey("raw", rawKey, "AES-GCM", true, [
    "encrypt",
    "decrypt",
  ]);
}
```

### 导入 PKCS #8 格式的密钥

该示例从 PEM 编码的 PKCS #8 对象导入一个 RSA 私有签名密钥。在 GitHub 上查看完整代码。


```js
/\*
将字符串转换为 ArrayBuffer
来自 https://developers.google.com/web/updates/2012/06/How-to-convert-ArrayBuffer-to-and-from-String
\*/
function str2ab(str) {
  const buf = new ArrayBuffer(str.length);
  const bufView = new Uint8Array(buf);
  for (let i = 0, strLen = str.length; i < strLen; i++) {
    bufView[i] = str.charCodeAt(i);
  }
  return buf;
}

const pemEncodedKey = `-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDD0tPV/du2vftjvXj1t/gXTK39sNBVrOAEb/jKzXae+Xa0H+3LhZaQIQNMfACiBSgIfZUvEGb+7TqXWQpoLoFR/R7MvGWcSk98JyrVtveD8ZmZYyItSY7m2hcasqAFiKyOouV5vzyRe87/lEyzzBpF3bQQ4IDaQu+K9Hj5fKuU6rrOeOhsdnJc+VdDQLScHxvMoLZ9Vtt+oK9J4/tOLwr4CG8khDlBURcBY6gPcLo3dPU09SW+6ctX2cX4mkXx6O/0mmdTmacr/vu50KdRMleFeZYOWPAEhhMfywybTuzBiPVIZVP8WFCSKNMbfi1S9A9PdBqnebwwHhX3/hsEBt2BAgMBAAECggEABEI1P6nf6Zs7mJlyBDv+Pfl5rjL2cOqLy6TovvZVblMkCPpJyFuNIPDK2tK2i897ZaXfhPDBIKmllM2Hq6jZQKB110OAnTPDg0JxzMiIHPs32S1d/KilHjGff4Hjd4NXp1l1Dp8BUPOllorR2TYm2x6dcCGFw9lhTr8O03Qp4hjn84VjGIWADYCk83mgS4nRsnHkdiqYnWx1AjKlY51yEK6RcrDMi0Th2RXrrINoC35sVv+APt2rkoMGi52RwTEseA1KZGFrxjq61ReJif6p2VXEcvHeX6CWLx014LGk43z6Q28P6HgeEVEfIjyqCUea5Du/mYb/QsRSCosXLxBqwQKBgQD1+fdC9ZiMrVI+km7Nx2CKBn8rJrDmUh5SbXn2MYJdrUd8bYNnZkCgKMgxVXsvJrbmVOrby2txOiqudZkk5mD3E5O/QZWPWQLgRu8ueYNpobAX9NRgNfZ7rZD+81vh5MfZiXfuZOuzv29iZhU0oqyZ9y75eHkLdrerNkwYOe5aUQKBgQDLzapDi1NxkBgsj9iiO4KUa7jvD4JjRqFy4Zhj/jbQvlvM0F/uFp7sxVcHGx4r11C+6iCbhX4u+Zuu0HGjT4d+hNXmgGyxR8fIUVxOlOtDkVJa5sOBZK73/9/MBeKusdmJPRhalZQfMUJRWIoEVDMhfg3tW/rBj5RYAtP2dTVUMQKBgDs8yr52dRmT+BWXoFWwaWB0NhYHSFz/c8v4D4Ip5DJ5M5kUqquxJWksySGQa40sbqnD05fBQovPLU48hfgr/zghn9hUjBcsoZOvoZR4sRw0UztBvA+7jzOz1hKAOyWIulR6Vca0yUrNlJ6G5R56+sRNkiOETupi2dLCzcqb0PoxAoGAZyNHvTLvIZN4iGSrjz5qkM4LIwBIThFadxbv1fq6pt0O/BGf2o+cEdq0diYlGK64cEVwBwSBnSg4vzlBqRIAUejLjwEDAJyA4EE8Y5A9l04dzV7nJb5cRak6CrgXxay/mBJRFtaHxVlaZGxYPGSYE6UFS0+3EOmmevvDZQBf4qECgYEA0ZF6Vavz28+8wLO6SP3w8NmpHk7K9tGEvUfQ30SgDx4G7qPIgfPrbB4OP/E0qCfsIImi3sCPpjvUMQdVVZyPOIMuB+rV3ZOxkrzxEUOrpOpR48FZbL7RN90yRQsAsrp9e4iv8QwB3VxLe7X0TDqqnRyqrc/osGzuS2ZcHOKmCU8=
-----END PRIVATE KEY-----`;

/\*
导入一个 PEM 编码的 RSA 私钥，用于 RSA-PSS 签名。
输入一个包含 PEM 编码的私钥的字符串，返回一个 Promise，
会兑现为一个表示私钥的 CryptoKey 对象。
\*/
function importPrivateKey(pem) {
  // 获取 PEM 字符串在头部和尾部之间的部分
  const pemHeader = "-----BEGIN PRIVATE KEY-----";
  const pemFooter = "-----END PRIVATE KEY-----";
  const pemContents = pem.substring(
    pemHeader.length,
    pem.length - pemFooter.length,
  );
  // 将字符串通过 base64 解码为二进制数据
  const binaryDerString = window.atob(pemContents);
  // 将二进制字符串转换为 ArrayBuffer
  const binaryDer = str2ab(binaryDerString);

  return window.crypto.subtle.importKey(
    "pkcs8",
    binaryDer,
    {
      name: "RSA-PSS",
      hash: "SHA-256",
    },
    true,
    ["sign"],
  );
}
```

### 导入 SubjectPublicKeyInfo 格式的密钥

该示例从一个 PEM 编码的 SubjectPublicKeyInfo 对象导入一个 RSA 公钥。在 GitHub 上查看完整代码。


```js
// 来自 https://developers.google.com/web/updates/2012/06/How-to-convert-ArrayBuffer-to-and-from-String
function str2ab(str) {
  const buf = new ArrayBuffer(str.length);
  const bufView = new Uint8Array(buf);
  for (let i = 0, strLen = str.length; i < strLen; i++) {
    bufView[i] = str.charCodeAt(i);
  }
  return buf;
}

const pemEncodedKey = `-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAy3Xo3U13dc+xojwQYWoJLCbOQ5fOVY8LlnqcJm1W1BFtxIhOAJWohiHuIRMctv7dzx47TLlmARSKvTRjd0dF92jx/xY20Lz+DXp8YL5yUWAFgA3XkO3LSJgEOex10NB8jfkmgSb7QIudTVvbbUDfd5fwIBmCtaCwWx7NyeWWDb7A9cFxj7EjRdrDaK3ux/ToMLHFXVLqSL341TkCf4ZQoz96RFPUGPPLOfvN0x66CM1PQCkdhzjE6U5XGE964ZkkYUPPsy6Dcie4obhW4vDjgUmLzv0z7UD010RLIneUgDE2FqBfY/C+uWigNPBPkkQ+Bv/UigS6dHqTCVeD5wgyBQIDAQAB
-----END PUBLIC KEY-----`;

function importRsaKey(pem) {
  // 获取 PEM 字符串在头部和尾部之间的部分
  const pemHeader = "-----BEGIN PUBLIC KEY-----";
  const pemFooter = "-----END PUBLIC KEY-----";
  const pemContents = pem.substring(
    pemHeader.length,
    pem.length - pemFooter.length,
  );
  // 将字符串通过 base64 解码为二进制数据
  const binaryDerString = window.atob(pemContents);
  // 将二进制字符串转换为 ArrayBuffer
  const binaryDer = str2ab(binaryDerString);

  return window.crypto.subtle.importKey(
    "spki",
    binaryDer,
    {
      name: "RSA-OAEP",
      hash: "SHA-256",
    },
    true,
    ["encrypt"],
  );
}
```

### 导入 JSON Web Key 格式的密钥

此示例从给定的 JSON Web Key 对象导入一个 ECDSA 私有签名密钥。在 GitHub 上查看完整代码。


```js
const jwkEcKey = {
  crv: "P-384",
  d: "wouCtU7Nw4E8\_7n5C1-xBjB4xqSb\_liZhYMsy8MGgxUny6Q8NCoH9xSiviwLFfK\_",
  ext: true,
  key\_ops: ["sign"],
  kty: "EC",
  x: "SzrRXmyI8VWFJg1dPUNbFcc9jZvjZEfH7ulKI1UkXAltd7RGWrcfFxqyGPcwu6AQ",
  y: "hHUag3OvDzEr0uUQND4PXHQTXP5IDGdYhJhL-WLKjnGjQAw0rNGy5V29-aV-yseW",
};

/\*
以 JSON Web Key 格式导入椭圆曲线算法的私钥，用与 ECDSA 签名。
输入一个表示 JSON Web Key 的对象，返回一个 Promise，
会兑现为一个表示私钥的 CryptoKey 对象。
\*/
function importPrivateKey(jwk) {
  return window.crypto.subtle.importKey(
    "jwk",
    jwk,
    {
      name: "ECDSA",
      namedCurve: "P-384",
    },
    true,
    ["sign"],
  );
}
```

规范
--

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

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

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

参见
--

* `SubtleCrypto.exportKey()`
* PKCS #8 格式。
* SubjectPublicKeyInfo 格式。
* JSON Web Key 格式。