使用 public RSA 密钥包装对称密钥会导致 DOMException (InvalidAccessError)

Wrapping symmtric key with public RSA key causes DOMException (InvalidAccessError)

我正在尝试使用 Web Crypto API 包装对称密钥,但出现错误: DOMException: key.usages does not permit this operation

对使用 Web Crypto API 还是很陌生,所以我不完全理解为什么这是一个问题。我想用 Public 密钥包装对称密钥,然后用我的私钥打开它。

我将问题提取到一个小代码沙箱中https://codesandbox.io/s/cranky-driscoll-v0itr?file=/src/index.ts

或者,可以使用以下步骤重现该问题。

这是来自沙盒的完整代码:

sync function foo() {
  try {
    const cryptoKeyPair = await window.crypto.subtle.generateKey(
      {
        name: "RSA-OAEP",
        modulusLength: 4096,
        publicExponent: new Uint8Array([1, 0, 1]),
        hash: "SHA-256"
      },
      true,
      ["encrypt", "decrypt"]
    );
    const publicKey = cryptoKeyPair.publicKey;

    const fileKey = await window.crypto.subtle.generateKey(
      { name: "AES-GCM", length: 256 },
      true,
      ["encrypt", "decrypt"]
    );

    const wrapKeyResult = await window.crypto.subtle.wrapKey(
      "raw",
      fileKey,
      publicKey,
      publicKey.algorithm.name
    );
    // ^^ here we get error: DOMException: key.usages does not permit this operation
    /** 
    * {
    *  code: 15
    *  message: "key.usages does not permit this operation"
    *  name: "InvalidAccessError"
    * }
    */
    console.log("Wrapped Key", wrapKeyResult);
  } catch (error) {
    console.error("something went wrong", error);
    throw error; 
}

foo()
  .then((res) => {
    console.log("I succeeded!");
  })
  .catch((error) => console.log("I failed with ", error));

generateKey() the wrong key usages中应用。正确的是 wrapKeyunwrapKey,因为 RSA 密钥对用于 wrapping/unwrapping 对称 AES-GCM 密钥:

async function foo() {
    try {
        const cryptoKeyPair = await window.crypto.subtle.generateKey(
            {
                name: "RSA-OAEP",
                modulusLength: 4096,
                publicExponent: new Uint8Array([1, 0, 1]),
                hash: "SHA-256"
            },
            true,
            ["wrapKey", "unwrapKey"]                      // Apply wrapKey and unwrapKey here! 
        );
        const publicKey = cryptoKeyPair.publicKey;

        const fileKey = await window.crypto.subtle.generateKey(
            { name: "AES-GCM", length: 256 },
            true,
            ["encrypt", "decrypt"]
        );

        const wrapKeyResult = await window.crypto.subtle.wrapKey(
            "raw",
            fileKey,
            publicKey,
            publicKey.algorithm.name
        );
        console.log("Wrapped Key", new Uint8Array(wrapKeyResult));
  
    } catch (error) {
        console.error("something went wrong", error);
        throw error;
    }
}

foo()
    .then((res) => {
        console.log("I succeeded!");
    })
    .catch((error) => console.log("I failed with ", error));