<!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.
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();