【问题标题】:Export RSA key pair with WebCrypto in Chromium在 Chromium 中使用 WebCrypto 导出 RSA 密钥对
【发布时间】:2020-06-01 22:08:10
【问题描述】:

以下代码适用于 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 中我得到:

未捕获(承诺中)DOMException:密钥不是预期的类型

差异在哪里?它是 Chromium 中的错误吗?是否有解决方法?

(与this question相关。应用解决方案后我仍然遇到问题,结果发现我遇到的浏览器之间存在另一个差异。)

【问题讨论】:

    标签: google-chrome rsa chromium webcrypto-api


    【解决方案1】:

    Object.entries 返回一个数组,其中包含对象的属性作为键值对。键值对的顺序是任意,见Object.entries():

    Object.entries() 方法返回给定对象自己的数组 可枚举的字符串键属性 [key, value] 对,顺序相同 正如 for...in 循环所提供的...

    for...in:

    for...in 循环遍历一个对象的属性 任意顺序...

    另一方面,["pkcs8", "spki"][i] 假设密钥顺序是私钥 (i = 0),然后是公钥 (i = 1)。在 Firfox 浏览器中顺序匹配,在 Chromium 浏览器中不匹配,导致异常。

    问题可以通过排序数组来解决,例如使用sort()localeCompare(),另见Object.entries()中的推荐:

    sort((key1, key2) => key1[0].localeCompare(key2[0])) 
    

    另一种方法是根据密钥类型(privatepublic)设置格式(pkcs8spki),而不是排序。 p>

    您的 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, "$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(""))
    }

    【讨论】:

    • 哇,很棒的收获,谢谢!我知道这一点,甚至教人们 ECMAScript 如此神秘的细微差别,但不知何故,正如它经常发生的那样,我并没有想到它是我自己代码中问题的根源,我需要其他人重新审视一下。 Medice,cura te ipso...顺便说一句,在这种情况下,简单地调用sort() 就足够了(不过,取决于实现,效率可能会低一些)。
    • 也不错的提示:replace(/(.{64})/g, "$1\n")。简体:replace(/.{64}/g, "$&\n").
    猜你喜欢
    • 1970-01-01
    • 2018-07-16
    • 2018-09-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-17
    相关资源
    最近更新 更多