javascript - JavaScript WebCrypto API unwrapKey
问题描述
我正在使用 JavaScript WebCrypto API。我需要做下一个序列:
- 生成 RSA-PSS 密钥对;
- 使用 AES-GCM 封装私钥;
- 有机会解开第 2 步的结果。
我知道我必须从 AES-GCM 存储盐和 InitialVector。但是,当我尝试解开包装(ArrayBuffer
)的结果时,它失败了:
DataError - Data provided to an operation does not meet requirements.
我试图将一个空的传递ArrayBuffer
给unwrapKey
但它失败了OperationError
。我也尝试将结果wrapKey
直接传递给unwrapKey
(而不转换为UInt8Array
),但结果是一样的。
我使用相同的盐、IV 和密钥材料来导出加密/解密密钥。
我不能说什么是错的。
我的代码:
var salt;
var iv;
window.onload = function() {
salt = window.crypto.getRandomValues(new Uint8Array(16));
iv = window.crypto.getRandomValues(new Uint8Array(12));
};
/*This function was copied from Mozilla's example*/
function bytesToArrayBuffer(bytes) {
const bytesAsArrayBuffer = new ArrayBuffer(bytes.length);
const bytesUint8 = new Uint8Array(bytesAsArrayBuffer);
bytesUint8.set(bytes);
return bytesAsArrayBuffer;
}
/*
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 = "123qweasd";
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: 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();
const wrappingKey = await getKey(keyMaterial, salt);
return window.crypto.subtle.wrapKey(
"pkcs8",
keyToWrap,
wrappingKey,
{
name: "AES-GCM",
iv: iv
}
);
}
/*
Derive an AES-GCM key using PBKDF2.
*/
async function getUnwrappingKey() {
// 1. get the key material (user-supplied password)
const keyMaterial = await getKeyMaterial();
return window.crypto.subtle.deriveKey(
{
"name": "PBKDF2",
salt: salt,
"iterations": 100000,
"hash": "SHA-256"
},
keyMaterial,
{ "name": "AES-GCM", "length": 256},
true,
[ "wrapKey", "unwrapKey" ]
);
}
async function unwrapPrivateKey(wrappedKeyPar) {
// 1. get the unwrapping key
const unwrappingKey = await getUnwrappingKey();
console.log('Got unwrapping key');
// 2. initialize the wrapped key
console.log('Param: '+wrappedKeyPar);
var wrappedKeyBuffer = bytesToArrayBuffer(wrappedKeyPar);
console.log('BufferLength: '+wrappedKeyBuffer.byteLength);
// 3. initialize the iv
const ivBuffer = iv;
// 4. unwrap the key
console.log('BEFORE!');
const unwrappedPKey = await window.crypto.subtle.unwrapKey(
"pkcs8", // import format
wrappedKeyBuffer, // ArrayBuffer representing key to unwrap
unwrappingKey, // CryptoKey representing key encryption key
{ // algorithm params for key encryption key
name: "AES-GCM",
iv: iv
},
{ // algorithm params for key to unwrap
name: "RSA-PSS",
hash: "SHA-256"
},
true, // extractability of key to unwrap
["sign", "verify"] // key usages for key to unwrap
);
}
function generateKeys() {
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) => {
console.log('Key pair has been generated!');
//wrap key
return wrapCryptoKey(keyPair.privateKey);
})
.then((wrappedKey) => {
console.log('Trying to log wrapped key');
console.log(wrappedKey);
let nArr = new Uint8Array(wrappedKey);
console.log('UInt8 '+nArr);
console.log('In base64');
console.log('Trying to unwrap key');
unwrapPrivateKey(nArr);
});
}
解决方案
unwrapKey
返回在当前情况下Promise
提供CryptoKey
封装私钥的 a。由于私钥只能用于签名而不用于验证,verify
因此必须从keyUsages
传递给的 -parameter中删除unwrapKey
,即keyUsages
-parameter 必须仅包含sign
:
...
const unwrappedPKey = await window.crypto.subtle.unwrapKey(
"pkcs8", // import format
wrappedKeyBuffer, // ArrayBuffer representing key to unwrap
unwrappingKey, // CryptoKey representing key encryption key
{ // algorithm params for key encryption key
name: "AES-GCM",
iv: iv
},
{ // algorithm params for key to unwrap
name: "RSA-PSS",
hash: "SHA-256"
},
true, // extractability of key to unwrap
["sign"] // key usages for key to unwrap <-- verify removed!
);
...
另请参阅此处,展开 pkcs8 密钥部分以获取适当的示例。
推荐阅读
- swift - 来自 Amadeus 的 Swift 解码 JSON
- http - 如何使用 REST API 创建 Firebase 动态链接
- javascript - Lambda 函数从一个不起作用的函数返回一个 Promise
- go - 当模块在我的 GOPATH 中时,为什么列出 go 模块依赖项会失败?
- excel - 从 Excel vba 将 Recordset 写入文本文件
- python - 如何检查列表中是否存在索引,如果不存在则使用默认值?
- php - 如何使用 php 文件从托管在服务器中的 MariaDB 中获取数据?
- java - Junit测试方法调用实际服务方法
- javascript - 在道具更改时对 axios 发布请求和 setState 做出反应
- python-3.x - Pandas:连接多级索引,使另一个 Dataframe 具有不同的排序