【问题标题】:Wrapping an RSA private key with AES key and then unwrapping用 AES 密钥包装 RSA 私钥,然后解包
【发布时间】:2018-06-10 19:50:22
【问题描述】:

我一直在尝试对使用 Web Cryptography API 生成的 RSA 私钥进行密码保护。为此,

  1. 我首先生成一个 RSA 密钥对
  2. 然后我从密码中导出一个 AES 对称密钥
  3. 然后,我将步骤 1 中的 RSA 私钥与步骤 2 中的 AES 密钥包装在一起。
  4. 完成后,我尝试通过立即将所有这些传递给 unwrap 方法来验证它是否有效,并在该方法中尝试解开它

代码如下:

<html>
<script>
function wrap(password) {
  var iterations = 1000000;

  // Utility function
  var stringToByteArray = function(s){
    if ("TextEncoder" in window) {
      encoder = new window.TextEncoder;
      return encoder.encode(s);
    }
    var result = new Uint8Array(s.length);
    for (var i=0; i<s.length; i++){
        result[i] = s.charCodeAt(i);
    }
    return result;
  }

  var saltBytes = stringToByteArray("NaCl");
  var passphraseBytes = stringToByteArray(password);

  return crypto.subtle.generateKey({
    name: "RSA-OAEP",
    modulusLength: 2048,
    publicExponent: new Uint8Array([1, 0, 1]),
    hash: {name: "SHA-256"}
  }, true, ["encrypt", "decrypt"]).then(function(keyPair) {
    return crypto.subtle.importKey(
      "raw", passphraseBytes, {name: "PBKDF2"}, false, ["deriveKey"]
    ).then(function(baseKey) {
      return window.crypto.subtle.deriveKey(
        {name: "PBKDF2", salt: saltBytes, iterations: iterations, hash: "SHA-1"},
        baseKey,
        {name: "AES-CBC", length: 256},
        false,
        ["encrypt", "decrypt", "wrapKey", "unwrapKey"]
      ).then(function(wrapperKey) {
        var iv = crypto.getRandomValues(new Uint8Array(16));
        return crypto.subtle.wrapKey(
          "pkcs8",
          keyPair.privateKey,
          wrapperKey,
          {name: "AES-CBC", iv: iv }
        ).then(function(wrappedKey) {
          return {
            iv: iv,
            wrapper: wrapperKey,
            wrapped: wrappedKey
          }
        })
      });
    }).catch(function(err) {
      console.log(err);
    });
  })
}


function unwrap(account) {
  console.log(account);
  crypto.subtle.unwrapKey(
    "pkcs8",
    account.wrapped,
    account.wrapper,
    {
      name: "AES-CBC",
      iv: account.iv
    },
    {
      name: "RSA-OAEP",
      modulusLength: 2048,
      publicExponent: new Uint8Array([1, 0, 1]),
      hash: {name: "SHA-256"}
    },
    true,
    ['decrypt', 'encrypt']
  ).then(function(privateKey) {
    console.log("unwrapped = ", privateKey);
  }).catch(function(e) {
    console.log(e)
  })
}

// Finally I call "wrap" and then "unwrap"
wrap("password").then(unwrap)

</script>
</html>

但是代码不起作用。包装代码不会引发任何错误,并且似乎会生成密钥(尽管我不知道这些是否有效)。但是当我尝试运行unwrapKey 方法时,出现以下错误:

DOMException: Cannot create a key using the specified key usages.

在过去的 24 小时里,我一直在拔头发,因为我无法让它发挥作用。谁能发现问题?这是一段完全独立的代码,因此您可以通过复制并粘贴到 HTML 文件中并在浏览器中打开来试用它。

【问题讨论】:

    标签: javascript encryption cryptography webcrypto-api


    【解决方案1】:

    您尝试做的是用对称密钥包装公钥/私钥,这并不是 wrap/unwrap 的预期工作方式。

    • wrapKey,允许密钥包装一个对称密钥以供使用 (传输、存储)在不安全的环境中。
    • unwrapKey,允许 解开对称密钥以供使用(传输、存储)的密钥 不安全的环境。

    您可以打包/解包对称密钥,但不能打包/解包公钥/私钥对(或密钥),因为打包/解包可能期望打包的密钥是对称密钥而不是非对称密钥。

    SubtleCrypto.wrapKey() 方法返回一个包装好的 Promise 对称密钥,用于在不安全的环境中使用(传输、存储)。 返回的包装缓冲区采用参数中给出的格式,并且 包含由给定包装密钥包装的密钥 算法。

    SubtleCrypto.unwrapKey() 方法返回一个 CryptoKey 的 Promise 对应于参数中给出的包装键。

    【讨论】:

    • 如果您是根据 Mozilla 文档发言,我不相信他们在谈到 WebCrypto API 时,因为我了解到他们的文档不完整,有时至少对 WebCrypto API 具有误导性。最初的 W3C 提案没有任何此类限制,规范说它只是一种加密机制。另外,自从我问了这个问题后,我发现了一个库可以做到这一点github.com/safebash/OpenCrypto
    • 确实是 Mozilla 文档,并且确实是大多数(所有?)当前主流浏览器的状态。正如您所说,W3C 也不限制包装。但是......以上是通过 broswer API 为开发人员提供的内容。很高兴您找到了满足您要求的解决方案。总而言之:MBC 的糟糕实施......(主要浏览器联盟)......新年快乐!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-08-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-07-08
    • 1970-01-01
    相关资源
    最近更新 更多