SubtleCrypto.importKey()

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.

* Some parts of this feature may have varying levels of support.

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

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

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

语法

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

参数

format

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

keyData

一个 ArrayBufferTypedArrayDataViewJSONWebKey 对象,包含了给定格式的密钥。

algorithm

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

extractable

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

keyUsages

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

返回值

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

异常

当发生以下几种异常时,promise 会被拒绝:

SyntaxError DOMException

当结果是类型为 secretprivate 的解包装的密钥,但 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 编码PrivateKeyInfoArrayBuffer 对象。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 和椭圆曲线算法的公钥。

SubjectPublicKeyRFC 5280 第 4.1 节中定义,使用 ASN.1 表示法 进行表示:

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

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

同样地,你最有可能遇到的此类对象的格式为 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

浏览器兼容性

Report problems with this compatibility data on GitHub
desktopmobileserver
Chrome
Edge
Firefox
Opera
Safari
Chrome Android
Firefox for Android
Opera Android
Safari on iOS
Samsung Internet
WebView Android
WebView on iOS
Deno
Node.js
importKey
Ed25519 algorithm
X25519 algorithm

Legend

Tip: you can click/tap on a cell for more information.

Full support
Full support
Partial support
Partial support
In development. Supported in a pre-release version.
In development. Supported in a pre-release version.
No support
No support
See implementation notes.
User must explicitly enable this feature.
Has more compatibility info.

参见