未捕获(在Promise中)DOM异常:密钥算法与操作不匹配。

3

<!DOCTYPE html>
<html lang="en">
<head>
</head>
<body>
  <script>
    let intermediateKey;
    let publicKey;
    let privateKey;
    let wrappedKey;
    let iv;

    async function rsaKeyPair() {
      let keyPair = await crypto.subtle.generateKey({
          name: "RSA-OAEP",
          modulusLength: 4096,
          publicExponent: new Uint8Array([1, 0, 1]),
          hash: "SHA-256",
        },
        true, ["wrapKey", "unwrapKey"]
      );
      publicKey = keyPair.publicKey;
      privateKey = keyPair.privateKey;
    }

    async function encrypt(secret) {
      // generating random intermediate key to encrypt and decrypt the secret
      intermediateKey = await crypto.subtle.generateKey({
          name: "AES-GCM",
          length: 256
        },
        true, ["encrypt", "decrypt"]
      );
      // encrypt secret
      // ...
      // wrap intermediate key (export + encrypt) intermediateKey using publicKey.
      iv = crypto.getRandomValues(new Uint8Array(12));
      wrappedKey = await crypto.subtle.wrapKey(
        "jwk",
        intermediateKey,
        publicKey, {
          name: "AES-GCM",
          iv: iv
        }
      );
    }

    async function decrypt(cipher) {
      // unwrap (decrypt + import) aes key using private key.
      intermediateKey = await crypto.subtle.unwrapKey(
        "jwk",
        wrappedKey,
        privateKey, {
          name: "AES-GCM",
          iv: iv
        }, {
          name: "AES-GCM"
        },
        false, ["encrypt", "decrypt"]
      );
      // decrypt the cipher
      // ...
    }

    async function solve() {
      // generate rsa-keypairs
      await rsaKeyPair();
      // encrypt secret
      const cipher = await encrypt("secret");
      // decrypt cipher
      await decrypt(cipher);
    }
    solve();
  </script>
</body>

</html>

生成RSA-OAEP密钥对(根据http://www.w3.org/TR/WebCryptoAPI/#algorithm-overview.)。
当用户创建一个秘密时,该秘密使用AES-GCM-256和随机生成的中间密钥进行加密。最后,将此中间密钥用用户的公钥进行包装。
最后解包中间密钥并解密。
在解包中间密钥时发生错误。

在解密函数中使用@GetSet来解封密钥。 - Mayur Koli
未捕获的(在promise中)DOM异常:密钥算法与操作不匹配 解决 @ js:81 异步函数(异步) 解决 @ js:76 匿名 @ js:82 - Mayur Koli
好的,我猜我可能没有表达清楚。对于你解决问题有帮助的是,如果有人碰巧路过并且阅读评论,他们会给你提供帮助。有些人不会深入阅读评论。所以,在你的问题中包含完整的堆栈跟踪(或错误信息,如果你愿意)是有帮助的。这不是一个主要的要求,但如果你真的想要帮助的话,这是有道理的。 - GetSet
@GetSet有一个运行代码片段的选项,错误将会在Chrome控制台中显示。 - Mayur Koli
让我们在聊天中继续这个讨论。 - Mayur Koli
显示剩余8条评论
1个回答

4

wrapKey()unwrapKey()函数中,缺少正确的wrapAlgounwrapAlgo参数规范。这些参数用于指定加密密钥的算法。在本示例中,应用了RSA与OAEP算法,因此必须使用RsaOaepParams对象来设置这两个参数。

如果在这两个函数中都正确指定了参数,AES密钥将能够被正确加密和解密:

let intermediateKey;
let publicKey;
let privateKey;
let wrappedKey;
let iv;

async function rsaKeyPair() {
    
    let keyPair = await crypto.subtle.generateKey(
        {
            name: 'RSA-OAEP',
            modulusLength: 4096,
            publicExponent: new Uint8Array([1, 0, 1]),
            hash: 'SHA-256',
        },
        true, 
        ['wrapKey', 'unwrapKey']
    );
    publicKey = keyPair.publicKey;
    privateKey = keyPair.privateKey;
}

async function encrypt() {

    // Generate AES key 
    intermediateKey = await crypto.subtle.generateKey(
        {
            name: 'AES-GCM',
            length: 256
        },
        true, 
        ['encrypt', 'decrypt']
    );
    
    // Encrypt secret
    // ...
    
    // Wrap AES key
    /*
    iv = crypto.getRandomValues(new Uint8Array(12));
    wrappedKey = await crypto.subtle.wrapKey(
        'jwk',
        intermediateKey,
        publicKey, 
        {
            name: "AES-GCM",
            iv: iv
        }
    );
    */
    wrappedKey = await crypto.subtle.wrapKey(
        'raw',
        intermediateKey,
        publicKey, 
        {                           // wrapAlgo, here an RsaOaepParams object, s. https://developer.mozilla.org/en-US/docs/Web/API/RsaOaepParams
            name: 'RSA-OAEP'
        }
    );    
    console.log('Wrapped AES key: ', new Uint8Array(wrappedKey));
}

async function decrypt() {

    // Unwrap AES key
    /*
    intermediateKey = await crypto.subtle.unwrapKey(
        'jwk',
        wrappedKey,
        privateKey, 
        {
            name: "AES-GCM",
            iv: iv
        }, 
        {
            name: "AES-GCM"
        },
        false, 
        ["encrypt", "decrypt"]
    );
    */
    intermediateKey = await crypto.subtle.unwrapKey(
        'raw',
        wrappedKey,
        privateKey, 
        {                           // unwrapAlgo, here an RsaOaepParams object, s. https://developer.mozilla.org/en-US/docs/Web/API/RsaOaepParams
            name: 'RSA-OAEP'
        }, 
        {
            name: 'AES-GCM'
        },
        false, ['encrypt', 'decrypt']
    );
    console.log('Unwrapped AES key: ', intermediateKey);
  
    // Decrypt ciphertext
    // ...
}

async function solve() {
    await rsaKeyPair();
    await encrypt();
    await decrypt();
}

solve();


在AES-GCM模式下,我需要生成一个IV来包装AES密钥吗?还是IV只在AES-CBC模式下使用? - Mayur Koli
1
IV在包装/解包时不需要,但在CBC和GCM的加密/解密中是必需的。由于出于安全原因,一个密钥/IV对只能使用一次,因此通常为每个加密生成一个随机IV(CBC使用16字节的IV,GCM使用12字节的IV)。IV不是秘密,并且与密文一起发送给接收者(通常将IV前缀添加到密文中,以便只需发送一个值:IV || 密文)。接收者分离两部分并进行解密操作。 - Topaco

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接