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>

generating RSA-OAEP key pair (according to http://www.w3.org/TR/WebCryptoAPI/#algorithm-overview.)

When the user create a secret, the secret is encrypted using AES-GCM-256 with a randomly generated intermediate key. Finally, this intermediate key is wrapped with the user's public key.

finally unwrap intermediate key & decryption.

the error is generated during unwrapping the intermediate key.

Mayur Koli
  • 77
  • 8
  • If the connection is not https, it does not matter how you encrypt keys (for a hacker or sniffer code to sniff out). But onto your error, which line? What is the verbatim "stack trace"? – GetSet Jan 24 '21 at 14:28
  • @GetSet in a decrypt function during unwrapping the key. – Mayur Koli Jan 24 '21 at 14:30
  • You already state that. What is your stack trace was my question, no answer from you yet? – GetSet Jan 24 '21 at 14:31
  • A "stack trace" is the full error, not a summary. When you find it, perhaps update (edit) your question with it. Helps on debugging since no one is on your computer to see it. – GetSet Jan 24 '21 at 14:32
  • Uncaught (in promise) DOMException: key.algorithm does not match that of operation solve @ js:81 async function (async) solve @ js:76 (anonymous) @ js:82 – Mayur Koli Jan 24 '21 at 14:34
  • getting this only sir not stack trace – Mayur Koli Jan 24 '21 at 14:35
  • `Uncaught (in promise) DOMException` etc ... thats the "stack trace". Put it in your question. Not here in comments – GetSet Jan 24 '21 at 14:36
  • Ok, I guess i am not being clear. What will help you as in you get help for your problem is for someone who happens along and *doesn't* read comments. Some ppl dont dive thru comments. So, include your full stack trace (or error, if you prefer) *in* your question. Its not a major requirement. But makes sense if you really want help – GetSet Jan 24 '21 at 14:41
  • Or try current approach, and it just never gets solved or closed for lack of debugging details. Your pick. – GetSet Jan 24 '21 at 14:45
  • @GetSet there is option to Run code snippet and error will appear in chrome console – Mayur Koli Jan 24 '21 at 14:49
  • So you dont want to provide your own error / stack trace? You do realize we could be using different browsers and even if same browsers, different versions? You just stuck on "fill in the blank"? .... Good to know why your question is just sitting here unanswered. I dont feel guilty anymore, knowing that you have chose this. – GetSet Jan 24 '21 at 14:53
  • @GetSet sir i am getting only these ```Uncaught (in promise) DOMException: key.algorithm does not match that of operation solve @ crypto.js:72 async function (async)``` not complete stack trace if there is any way to print complete stack trace please tell me. solve @ crypto.js:67 (anonymous) @ crypto.js:73 – Mayur Koli Jan 24 '21 at 15:04
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/227784/discussion-between-mayur-koli-and-getset). – Mayur Koli Jan 25 '21 at 09:43

1 Answers1

4

Both, the wrapKey() and unwrapKey() calls lack the proper specification of the wrapAlgo and unwrapAlgo parameters, respectively, which are used to specify the algorithm to encrypt the key. In this example, RSA with OAEP is applied, so a RsaOaepParams object must be used for both parameters.

If the parameters are specified correctly in both functions, the AES key will be encrypted and decrypted correctly:

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();
Topaco
  • 40,594
  • 4
  • 35
  • 62
  • do i need to generate an IV during to wrapped the AES key in AES-GCM mode? or iv is just only use in AES-CBC mode? – Mayur Koli Jan 25 '21 at 10:26
  • 1
    The IV is not needed for wrapping/unwrapping, but for encryption/decryption, both for CBC and GCM. Since a key/IV pair may only be used once for security reasons, a random IV is commonly generated for each encryption (CBC uses a 16 bytes IV, GCM a 12 bytes IV). The IV is not secret and is sent to the recipient along with the ciphertext (usually the IV is prefixed so that only one value needs to be sent: IV || ciphertext). The recipient separates both parts and can perform the decryption. – Topaco Jan 25 '21 at 10:49