I am trying to decrypt a compact JWE
formatted response message with the crypto.subtle
libraries.
I am sending to the Server my public key in JWK format
with curve algo ECDH-ES+A128KW
, encryption A256GCM
, curve name P-256
.
The server sends me back a compact JWE
response.
As I understand this flow, it should be something like:
- Client sends the public key to the Server
- Server responds to client back the
compact JWE
message - Client derives the shared
AES 128 KW
key based on servers public key and own private key - Client unwraps the
AES 128 GCM
key using the sharedAES 128 KW
key - Clients decrypts the ciphertext using the
AES 128 GCM
key.
When my code reaches the unwrapKey
step, i am only getting the error The operation failed for an operation-specific reason. At the moment I fail to find the problem.
My code looks like this right now:
export const decryptCompactJWE = async (
compactJWE: string,
privateKey: CryptoKey
) => {
const [protectedHeader, encryptedKey, iv, ciphertext, tag] =
compactJWE.split(".");
const header = JSON.parse(Buffer.from(protectedHeader, "base64").toString());
console.log("header:", header);
const publicKey = await crypto.subtle.importKey(
"jwk",
header.epk,
{
name: "ECDH",
namedCurve: "P-256",
},
true,
["deriveKey", "deriveBits"]
);
const derivedKey = await crypto.subtle.deriveKey(
{ name: "ECDH", public: publicKey },
privateKey,
{ name: "AES-KW", length: 128 },
true,
["unwrapKey"]
);
const myJWK = await crypto.subtle.exportKey("jwk", derivedKey);
console.log("jwk", myJWK);
const myAESKey = await crypto.subtle.unwrapKey(
"raw",
Buffer.from(encryptedKey, "base64url"),
derivedKey,
"AES-KW",
{ name: "AES-GCM" },
false,
["decrypt"]
);
console.log(myAESKey);
return crypto.subtle.decrypt(
{ name: "AES-GCM", iv: Buffer.from(iv, "base64url") },
myAESKey,
Buffer.from(ciphertext, "base64url")
);
};
Here is my test data:
const privateKey = {
kty: "EC",
crv: "P-256",
ext: true,
key_ops: ["deriveKey", "deriveBits"],
d: "vPZxnkg-j1xZ_8BZfH6jIvV52NvG2pxsZhmYgI9BEec",
x: "CorZZG9qa5korQ6eVLenbFz2QyGKkpoEYlAJxF1JzGA",
y: "yIEnQSGlMNVp6JEzZO3QvjQ0UDAwepzUZqwgsv0OTQE",
};
const JWE_RESPONSE = "eyJhbGciOiJFQ0RILUVTK0ExMjhLVyIsImVuYyI6IkExMjhHQ00iLCJraWQiOiJhYmMxMjMiLCJlcGsiOnsia3R5IjoiRUMiLCJ4IjoiNmNReW1GUlJSTjVkVHdoOHA5dWx1NkgwS3paSkRGcm4xdjFKb2NzVURCUSIsInkiOiJTSGliQjFEMnBHMmVMbUxMV09HTTB4UUtCRDFpM3ZtZjJRNjZIM2RnbzJ3IiwiY3J2IjoiUC0yNTYifX0.OwriqBm-PXkIj_QwbqKZRVxql0sja2-p.UrZs5Ixu_rFCxpCw.z9Rfhw.m6AgqKsttsp9TV2dREgbWw";
So far I looked up a all examples I could find to implement this and based on those it kinda looks okay. The debugger is not stepping into the native crypto.subtle code and the error message is also not telling much about what is going wrong for me. The existing examples I found so far, are mostly less complex and skip the key derive part.