在 Chromium 中使用 WebCrypto 导出 RSA 密钥对
Export RSA key pair with WebCrypto in Chromium
以下代码适用于 Firefox 76.0.1:
"use strict"
let RSAKeys
(async () => {
RSAKeys = await crypto.subtle.generateKey({
name: "RSA-OAEP",
modulusLength: 3072,
publicExponent: new Uint8Array([1, 0, 1]),
hash: "SHA-256"},
true,
["wrapKey", "unwrapKey"])
alert(JSON.stringify(Object.fromEntries(
await Promise.all(Object.entries(RSAKeys).map(async ([k, v], i) =>
[k, await cryptoBase64("exportKey", ["pkcs8", "spki"][i], v)])))))
})()
async function cryptoBase64(primitive, ...args) {
return ArrayBufferToBase64(await crypto.subtle[primitive](...args))
}
function ArrayBufferToBase64(buf) {
return btoa([...new Uint8Array(buf)].map(x => String.fromCharCode(x)).join(""))
}
但在 Chromium 80 中我得到:
Uncaught (in promise) DOMException: The key is not of the expected type
哪里不一样?它是 Chromium 中的错误吗?有解决方法吗?
(与 相关。应用该解决方案后我仍然遇到问题,事实证明我 运行 喜欢的浏览器之间存在另一个差异。)
Object.entries
returns 以对象的属性作为键值对的数组。键值对的顺序任意,见Object.entries()
:
The Object.entries() method returns an array of a given object's own
enumerable string-keyed property [key, value] pairs, in the same order
as that provided by a for...in loop...
和for...in:
A for...in loop iterates over the properties of an object in an
arbitrary order...
另一方面,["pkcs8", "spki"][i]
假定密钥顺序是私钥 (i = 0) 后跟 public 密钥 (i = 1)。在 Firfox 浏览器中顺序恰好匹配,在 Chromium 浏览器中不匹配,从而导致异常。
问题可以通过排序数组来解决,例如使用 sort()
和 localeCompare()
,另请参阅 Object.entries()
:
中的建议
sort((key1, key2) => key1[0].localeCompare(key2[0]))
另一种方法是根据密钥类型 (private, [设置格式 (pkcs8
, spki
) =62=]) 而不是排序。
您的 JavaScript 代码 使用两种方法之一完成 在 Firefox 和 Chromium 浏览器中运行:
"use strict"
let RSAKeys
(async () => {
RSAKeys = await crypto.subtle.generateKey({
name: "RSA-OAEP",
modulusLength: 3072,
publicExponent: new Uint8Array([1, 0, 1]),
hash: "SHA-256"},
true,
["wrapKey", "unwrapKey"])
// Approach 1
var result1 = JSON.stringify(Object.fromEntries(
await Promise.all(Object.entries(RSAKeys)
.sort((key1, key2) => key1[0].localeCompare(key2[0]))
.map(async ([k, v], i) => [k, await cryptoBase64("exportKey", ["pkcs8", "spki"][i], v)]))))
console.log(result1.replace(/(.{64})/g, "\n"));
// Approach 2
var result2 = JSON.stringify(Object.fromEntries(
await Promise.all(Object.entries(RSAKeys)
.map(async ([k, v], i) => [k, await cryptoBase64("exportKey", k == "privateKey" ? "pkcs8" : "spki", v)]))))
console.log(result2.replace(/(.{64})/g, "\n"));
})()
async function cryptoBase64(primitive, ...args) {
return ArrayBufferToBase64(await crypto.subtle[primitive](...args))
}
function ArrayBufferToBase64(buf) {
return btoa([...new Uint8Array(buf)].map(x => String.fromCharCode(x)).join(""))
}
以下代码适用于 Firefox 76.0.1:
"use strict"
let RSAKeys
(async () => {
RSAKeys = await crypto.subtle.generateKey({
name: "RSA-OAEP",
modulusLength: 3072,
publicExponent: new Uint8Array([1, 0, 1]),
hash: "SHA-256"},
true,
["wrapKey", "unwrapKey"])
alert(JSON.stringify(Object.fromEntries(
await Promise.all(Object.entries(RSAKeys).map(async ([k, v], i) =>
[k, await cryptoBase64("exportKey", ["pkcs8", "spki"][i], v)])))))
})()
async function cryptoBase64(primitive, ...args) {
return ArrayBufferToBase64(await crypto.subtle[primitive](...args))
}
function ArrayBufferToBase64(buf) {
return btoa([...new Uint8Array(buf)].map(x => String.fromCharCode(x)).join(""))
}
但在 Chromium 80 中我得到:
Uncaught (in promise) DOMException: The key is not of the expected type
哪里不一样?它是 Chromium 中的错误吗?有解决方法吗?
(与
Object.entries
returns 以对象的属性作为键值对的数组。键值对的顺序任意,见Object.entries()
:
The Object.entries() method returns an array of a given object's own enumerable string-keyed property [key, value] pairs, in the same order as that provided by a for...in loop...
和for...in:
A for...in loop iterates over the properties of an object in an arbitrary order...
另一方面,["pkcs8", "spki"][i]
假定密钥顺序是私钥 (i = 0) 后跟 public 密钥 (i = 1)。在 Firfox 浏览器中顺序恰好匹配,在 Chromium 浏览器中不匹配,从而导致异常。
问题可以通过排序数组来解决,例如使用 sort()
和 localeCompare()
,另请参阅 Object.entries()
:
sort((key1, key2) => key1[0].localeCompare(key2[0]))
另一种方法是根据密钥类型 (private, [设置格式 (pkcs8
, spki
) =62=]) 而不是排序。
您的 JavaScript 代码 使用两种方法之一完成 在 Firefox 和 Chromium 浏览器中运行:
"use strict"
let RSAKeys
(async () => {
RSAKeys = await crypto.subtle.generateKey({
name: "RSA-OAEP",
modulusLength: 3072,
publicExponent: new Uint8Array([1, 0, 1]),
hash: "SHA-256"},
true,
["wrapKey", "unwrapKey"])
// Approach 1
var result1 = JSON.stringify(Object.fromEntries(
await Promise.all(Object.entries(RSAKeys)
.sort((key1, key2) => key1[0].localeCompare(key2[0]))
.map(async ([k, v], i) => [k, await cryptoBase64("exportKey", ["pkcs8", "spki"][i], v)]))))
console.log(result1.replace(/(.{64})/g, "\n"));
// Approach 2
var result2 = JSON.stringify(Object.fromEntries(
await Promise.all(Object.entries(RSAKeys)
.map(async ([k, v], i) => [k, await cryptoBase64("exportKey", k == "privateKey" ? "pkcs8" : "spki", v)]))))
console.log(result2.replace(/(.{64})/g, "\n"));
})()
async function cryptoBase64(primitive, ...args) {
return ArrayBufferToBase64(await crypto.subtle[primitive](...args))
}
function ArrayBufferToBase64(buf) {
return btoa([...new Uint8Array(buf)].map(x => String.fromCharCode(x)).join(""))
}