SubtleCrypto.deriveKey()
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.
SubtleCrypto
接口的 deriveKey()
方法用于从主密钥派生密钥。
它以基本密钥、使用的派生算法和派生密钥所需的属性为参数。返回一个 Promise
,会兑现一个表示新密钥的 CryptoKey
对象。
值得注意的是,你可以使用的三种密钥派生算法有截然不同的特性,而适用于截然不同的情况。参见支持的算法以获取详细信息。
语法
deriveKey(algorithm, baseKey, derivedKeyAlgorithm, extractable, keyUsages)
参数
algorithm
-
一个对象,用于指定使用的派生算法。
- 使用 ECDH,则传入
EcdhKeyDeriveParams
对象。 - 使用 HKDF,则传入
HkdfParams
对象。 - 使用 PBKDF2,则传入
Pbkdf2Params
对象。
- 使用 ECDH,则传入
baseKey
-
一个
CryptoKey
,表示派生算法的输入。如果算法(algorithm
)为 ECDH,则该对象为 ECDH 的私钥。否则,它为派生函数的初始密钥材料(key material):例如,对于 PBKDF2,它可能是一个密码(使用SubtleCrypto.importKey()
导入为一个CryptoKey
对象)。 derivedKeyAlgorithm
-
一个用于派生密钥算法的对象。
- 对于 HMAC:传入
HmacKeyGenParams
对象。 - 对于 AES-CTR、AES-CBC、AES-GCM 或 AES-KW:传入
AesKeyGenParams
对象。
- 对于 HMAC:传入
extractable
-
一个布尔值,表示是否可以使用
SubtleCrypto.exportKey()
或SubtleCrypto.wrapKey()
来导出密钥。 keyUsages
-
一个数组,表示派生出来的密钥的用途。注意,密钥的用法必须是
derivedKeyAlgorithm
设置的算法所允许的。数组元素可能的值有:
返回值
异常
当发生一下几种异常时,promise 会被拒绝:
InvalidAccessError
DOMException
-
若主密钥与要求的派生算法所使用的密钥类型不匹配,或密钥的
keyUsages
的值中不包含deriveKey
,则会抛出此异常。 NotSupported
DOMException
-
若尝试使用未知或不适用于派生的算法,或用于派生密钥的算法(algorithm)没有定义密钥长度(key length),则会抛出此异常。
SyntaxError
DOMException
-
若
keyUsages
是空的,而解包装密钥的类型是secret
或private
,则抛出此异常。
支持的算法
deriveKey()
支持的三种算法各有特点而适用于不同的场合。
ECDH
ECDH(椭圆曲线迪菲—赫尔曼密钥交换,Elliptic Curve Diffie-Hellman)是一种密钥协商算法。它使每个人都能拥有用于生成共享密钥的 ECDH 公钥/私钥对:即,密钥仅在两人之间共享(而不包括其他人)。然后他们可以使用这个共享密钥作为对称密钥来保护他们的通信,或可以使用密钥来作为派生同类密钥(例如,使用 HKDF 算法)的输入。
ECDH 的规范定于 RFC 6090。
HKDF
HKDF 是一种密钥派生函数。它被用于从一些熵值相对较高的输入(如 ECDH 密钥协商操作的输出)派生密钥材料。
它并非用于从熵值相对较低的输入(例如密码)派生密钥。对于此种用途,请使用 PBKDF2。
HKDF 的规范定于 RFC 5869。
PBKDF2
PBKDF2 也是一种密钥派生函数。它被用于从一些熵值相对较低的输入(例如密码)派生密钥材料。它通过将例如 HMAC 等函数以及加盐(salt)操作等一起应用到输入密码上,并多次重复此过程来派生密钥材料。这个过程重复的次数越多,密钥推导计算的成本就越高:这使得攻击者难以使用字典攻击这类暴力破解的方法来找出密钥。
PBKDF2 的规范定于 RFC 2898。
示例
备注:你可以在 GitHub 上尝试可用的示例。
ECDH
在此示例中,Alice 和 Bob 分别生成了一个 ECDH 密钥对,然后相互交换公钥。并使用 deriveKey()
来派生一个可用于加密消息的共享 AES 密钥。在 GitHub 上查看完整代码。
/*
派生 AES 密钥,需要提供:
- 自己的 ECDH 私钥
- 对方的 ECDH 公钥
*/
function deriveSecretKey(privateKey, publicKey) {
return window.crypto.subtle.deriveKey(
{
name: "ECDH",
public: publicKey,
},
privateKey,
{
name: "AES-GCM",
length: 256,
},
false,
["encrypt", "decrypt"],
);
}
async function agreeSharedSecretKey() {
// 生成两个 ECDH 密钥对:一个是 Alice 的,一个是 Bob 的
// 在正常的使用情况下,他们会单独生成密钥对,并安全地交换公钥。
let alicesKeyPair = await window.crypto.subtle.generateKey(
{
name: "ECDH",
namedCurve: "P-384",
},
false,
["deriveKey"],
);
let bobsKeyPair = await window.crypto.subtle.generateKey(
{
name: "ECDH",
namedCurve: "P-384",
},
false,
["deriveKey"],
);
// 然后 Alice 使用她的私钥和 Bob 的公钥生成密钥(secret key)。
let alicesSecretKey = await deriveSecretKey(
alicesKeyPair.privateKey,
bobsKeyPair.publicKey,
);
// Bob 使用他的私钥和 Alice 的公钥来生成相同的密钥。
let bobsSecretKey = await deriveSecretKey(
bobsKeyPair.privateKey,
alicesKeyPair.publicKey,
);
// Alice 可以使用她的密钥拷贝来加密发送给 Bob 的消息。
let encryptButton = document.querySelector(".ecdh .encrypt-button");
encryptButton.addEventListener("click", () => {
encrypt(alicesSecretKey);
});
// Bob 可以使用他的拷贝来解密消息。
let decryptButton = document.querySelector(".ecdh .decrypt-button");
decryptButton.addEventListener("click", () => {
decrypt(bobsSecretKey);
});
}
PBKDF2
在此示例中,我们要求用户提供密码,然后使用 PBKDF2 派生 AES 密钥,并使用 AES 密钥来加密消息。 在 GitHub 上查看完整代码。
/*
获取用于作为 deriveKey 方法的输入的密钥材料。
密钥材料是用户提供的密码。
*/
function getKeyMaterial() {
const password = window.prompt("请输入你的密码");
const enc = new TextEncoder();
return window.crypto.subtle.importKey(
"raw",
enc.encode(password),
"PBKDF2",
false,
["deriveBits", "deriveKey"],
);
}
async function encrypt(plaintext, salt, iv) {
const keyMaterial = await getKeyMaterial();
const key = await window.crypto.subtle.deriveKey(
{
name: "PBKDF2",
salt,
iterations: 100000,
hash: "SHA-256",
},
keyMaterial,
{ name: "AES-GCM", length: 256 },
true,
["encrypt", "decrypt"],
);
return window.crypto.subtle.encrypt({ name: "AES-GCM", iv }, key, plaintext);
}
规范
Specification |
---|
Web Cryptography API # SubtleCrypto-method-deriveKey |
浏览器兼容性
BCD tables only load in the browser