SubtleCrypto: wrapKey() 方法
注意:此功能在 Web Workers 中可用。
SubtleCrypto
接口的 wrapKey()
方法用于“包装”一个密钥。这意味着它会以外部、可移植的格式导出密钥,然后加密导出的密钥。包装密钥有助于在不受信任的环境中保护它,例如在未受保护的数据存储中或在不受保护的网络上传输时。
与 SubtleCrypto.exportKey()
一样,您需要为密钥指定一个 导出格式。要导出密钥,其 CryptoKey.extractable
必须设置为 true
。
但由于 wrapKey()
还会加密要导出的密钥,因此您还需要传入必须用于加密它的密钥。这有时被称为“包装密钥”。
wrapKey()
的逆操作是 SubtleCrypto.unwrapKey()
:wrapKey
由导出 + 加密组成,而 unwrapKey
由导入 + 解密组成。
语法
wrapKey(format, key, wrappingKey, wrapAlgo)
参数
格式(format)
-
一个描述密钥将要导出的数据格式的字符串,之后会被加密。它可以是以下之一:
key
-
要包装的
CryptoKey
。 wrappingkey
-
用于加密导出密钥的
CryptoKey
。该密钥必须具有wrapKey
用途。 wrapAlgo
-
一个对象,指定用于加密导出密钥的 算法以及任何必需的额外参数。
- 要使用 RSA-OAEP,请传递一个
RsaOaepParams
对象。 - 要使用 AES-CTR,请传递一个
AesCtrParams
对象。 - 要使用 AES-CBC,请传递一个
AesCbcParams
对象。 - 要使用 AES-GCM,请传递一个
AesGcmParams
对象。 - 要使用 AES-KW,请传递字符串
"AES-KW"
,或者一个形式为{ name: "AES-KW" }
的对象。
- 要使用 RSA-OAEP,请传递一个
返回值
一个 Promise
,该 Promise 会 fulfilled 为一个包含加密后的导出密钥的 ArrayBuffer
。
异常
当遇到以下任一异常时,Promise 将被拒绝:
InvalidAccessError
DOMException
-
当包装密钥不是请求的包装算法的密钥时抛出。
NotSupported
DOMException
-
尝试使用未知或不适合加密或包装的算法时抛出。
TypeError
-
当尝试使用无效格式时抛出。
支持的算法
所有 可用于加密的算法 也可用于密钥包装,前提是密钥具有“wrapKey”用途。对于密钥包装,您还可以选择 AES-KW。
AES-KW
AES-KW 是一种使用 AES 密码进行密钥包装的方法。
与 AES-GCM 等其他 AES 模式相比,使用 AES-KW 的一个优点是 AES-KW 不需要初始化向量。要使用 AES-KW,输入必须是 64 位的倍数。
AES-KW 在 RFC 3394 中有规定。
示例
注意: 您可以在 GitHub 上 尝试这些工作示例。
Raw wrap
此示例包装一个 AES 密钥。它使用“raw”作为导出格式,并使用 AES-KW(带有一个密码派生密钥)来加密它。在 GitHub 上查看完整代码。
let salt;
/*
Get some key material to use as input to the deriveKey method.
The key material is a password supplied by the user.
*/
function getKeyMaterial() {
const password = window.prompt("Enter your password");
const enc = new TextEncoder();
return window.crypto.subtle.importKey(
"raw",
enc.encode(password),
{ name: "PBKDF2" },
false,
["deriveBits", "deriveKey"],
);
}
/*
Given some key material and some random salt
derive an AES-KW key using PBKDF2.
*/
function getKey(keyMaterial, salt) {
return window.crypto.subtle.deriveKey(
{
name: "PBKDF2",
salt,
iterations: 100000,
hash: "SHA-256",
},
keyMaterial,
{ name: "AES-KW", length: 256 },
true,
["wrapKey", "unwrapKey"],
);
}
/*
Wrap the given key.
*/
async function wrapCryptoKey(keyToWrap) {
// get the key encryption key
const keyMaterial = await getKeyMaterial();
salt = window.crypto.getRandomValues(new Uint8Array(16));
const wrappingKey = await getKey(keyMaterial, salt);
return window.crypto.subtle.wrapKey("raw", keyToWrap, wrappingKey, "AES-KW");
}
/*
Generate an encrypt/decrypt secret key,
then wrap it.
*/
window.crypto.subtle
.generateKey(
{
name: "AES-GCM",
length: 256,
},
true,
["encrypt", "decrypt"],
)
.then((secretKey) => wrapCryptoKey(secretKey))
.then((wrappedKey) => console.log(wrappedKey));
PKCS #8 wrap
此示例包装一个 RSA 私有签名密钥。它使用“pkcs8”作为导出格式,并使用 AES-GCM(带有一个密码派生密钥)来加密它。在 GitHub 上查看完整代码。
let salt;
let iv;
/*
Get some key material to use as input to the deriveKey method.
The key material is a password supplied by the user.
*/
function getKeyMaterial() {
const password = window.prompt("Enter your password");
const enc = new TextEncoder();
return window.crypto.subtle.importKey(
"raw",
enc.encode(password),
{ name: "PBKDF2" },
false,
["deriveBits", "deriveKey"],
);
}
/*
Given some key material and some random salt
derive an AES-GCM key using PBKDF2.
*/
function getKey(keyMaterial, salt) {
return window.crypto.subtle.deriveKey(
{
name: "PBKDF2",
salt,
iterations: 100000,
hash: "SHA-256",
},
keyMaterial,
{ name: "AES-GCM", length: 256 },
true,
["wrapKey", "unwrapKey"],
);
}
/*
Wrap the given key.
*/
async function wrapCryptoKey(keyToWrap) {
// get the key encryption key
const keyMaterial = await getKeyMaterial();
salt = window.crypto.getRandomValues(new Uint8Array(16));
const wrappingKey = await getKey(keyMaterial, salt);
iv = window.crypto.getRandomValues(new Uint8Array(12));
return window.crypto.subtle.wrapKey("pkcs8", keyToWrap, wrappingKey, {
name: "AES-GCM",
iv,
});
}
/*
Generate a sign/verify key pair,
then wrap the private key.
*/
window.crypto.subtle
.generateKey(
{
name: "RSA-PSS",
// Consider using a 4096-bit key for systems that require long-term security
modulusLength: 2048,
publicExponent: new Uint8Array([1, 0, 1]),
hash: "SHA-256",
},
true,
["sign", "verify"],
)
.then((keyPair) => wrapCryptoKey(keyPair.privateKey))
.then((wrappedKey) => {
console.log(wrappedKey);
});
SubjectPublicKeyInfo wrap
此示例包装一个 RSA 公有加密密钥。它使用“spki”作为导出格式,并使用 AES-CBC(带有一个密码派生密钥)来加密它。在 GitHub 上查看完整代码。
let salt;
let iv;
/*
Get some key material to use as input to the deriveKey method.
The key material is a password supplied by the user.
*/
function getKeyMaterial() {
const password = window.prompt("Enter your password");
const enc = new TextEncoder();
return window.crypto.subtle.importKey(
"raw",
enc.encode(password),
{ name: "PBKDF2" },
false,
["deriveBits", "deriveKey"],
);
}
/*
Given some key material and some random salt
derive an AES-CBC key using PBKDF2.
*/
function getKey(keyMaterial, salt) {
return window.crypto.subtle.deriveKey(
{
name: "PBKDF2",
salt,
iterations: 100000,
hash: "SHA-256",
},
keyMaterial,
{ name: "AES-CBC", length: 256 },
true,
["wrapKey", "unwrapKey"],
);
}
/*
Wrap the given key.
*/
async function wrapCryptoKey(keyToWrap) {
// get the key encryption key
const keyMaterial = await getKeyMaterial();
salt = window.crypto.getRandomValues(new Uint8Array(16));
const wrappingKey = await getKey(keyMaterial, salt);
iv = window.crypto.getRandomValues(new Uint8Array(16));
return window.crypto.subtle.wrapKey("spki", keyToWrap, wrappingKey, {
name: "AES-CBC",
iv,
});
}
/*
Generate an encrypt/decrypt key pair,
then wrap it.
*/
window.crypto.subtle
.generateKey(
{
name: "RSA-OAEP",
// Consider using a 4096-bit key for systems that require long-term security
modulusLength: 2048,
publicExponent: new Uint8Array([1, 0, 1]),
hash: "SHA-256",
},
true,
["encrypt", "decrypt"],
)
.then((keyPair) => wrapCryptoKey(keyPair.publicKey))
.then((wrappedKey) => console.log(wrappedKey));
JSON Web Key wrap
此示例包装一个 ECDSA 私有签名密钥。它使用“jwk”作为导出格式,并使用 AES-GCM(带有一个密码派生密钥)来加密它。在 GitHub 上查看完整代码。
let salt;
let iv;
/*
Get some key material to use as input to the deriveKey method.
The key material is a password supplied by the user.
*/
function getKeyMaterial() {
const password = window.prompt("Enter your password");
const enc = new TextEncoder();
return window.crypto.subtle.importKey(
"raw",
enc.encode(password),
{ name: "PBKDF2" },
false,
["deriveBits", "deriveKey"],
);
}
/*
Given some key material and some random salt
derive an AES-GCM key using PBKDF2.
*/
function getKey(keyMaterial, salt) {
return window.crypto.subtle.deriveKey(
{
name: "PBKDF2",
salt,
iterations: 100000,
hash: "SHA-256",
},
keyMaterial,
{ name: "AES-GCM", length: 256 },
true,
["wrapKey", "unwrapKey"],
);
}
/*
Wrap the given key.
*/
async function wrapCryptoKey(keyToWrap) {
// get the key encryption key
const keyMaterial = await getKeyMaterial();
salt = window.crypto.getRandomValues(new Uint8Array(16));
const wrappingKey = await getKey(keyMaterial, salt);
iv = window.crypto.getRandomValues(new Uint8Array(12));
return window.crypto.subtle.wrapKey("jwk", keyToWrap, wrappingKey, {
name: "AES-GCM",
iv,
});
}
/*
Generate a sign/verify key pair,
then wrap the private key
*/
window.crypto.subtle
.generateKey(
{
name: "ECDSA",
namedCurve: "P-384",
},
true,
["sign", "verify"],
)
.then((keyPair) => wrapCryptoKey(keyPair.privateKey))
.then((wrappedKey) => console.log(wrappedKey));
规范
规范 |
---|
Web 加密级别 2 # SubtleCrypto-method-wrapKey |
浏览器兼容性
加载中…