Export RSA key pair with WebCrypto in Chromium

此生再无相见时 提交于 2020-08-09 13:35:11

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!