问题
The following code works in 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(""))
}
but in Chromium 80 I get:
Uncaught (in promise) DOMException: The key is not of the expected type
Whence the difference? Is it a bug in Chromium? And is there a workaround?
(Related to this question. After applying the solution I still had a problem and it turns out there's another discrepancy between browsers I'm running into.)
回答1:
Object.entries
returns an array with the properties of the object as key-value pairs. The order of the key-value pairs is arbitrary, see 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...
and for...in:
A for...in loop iterates over the properties of an object in an arbitrary order...
On the other hand, ["pkcs8", "spki"][i]
assumes that the key order is private key (i = 0) followed by the public key (i = 1). In the Firfox browser the order matches coincidentally, in the Chromium browser not, which causes the exception.
The problem can be solved by sorting the array, e.g. using sort() and localeCompare(), see also the recommendation in Object.entries():
sort((key1, key2) => key1[0].localeCompare(key2[0]))
Another approach would be to set the format (pkcs8
, spki
) depending on the key type (private, public) instead of sorting.
Your JavaScript code completed with one of the two approaches runs in both, the Firefox and the Chromium browsers:
"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, "$1\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, "$1\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(""))
}
来源:https://stackoverflow.com/questions/62141756/export-rsa-key-pair-with-webcrypto-in-chromium