Reputation: 1499
I tried the below code to create an RSA-OAEP and A128GCM JWE generator and validator. It works with node.js, i.e. encrypts claims and generates the JWE and decrypts the same gives me the claims. But it is not working with the other clients, like Nimbus jose, jose4j. So for sure, I am missing something.
I am doing this by reading https://www.rfc-editor.org/rfc/rfc7516
index.js
var crypto = require('crypto');
var randomstring = require("randomstring");
var ursa = require("ursa");
var fs = require("fs");
var base64url = require('base64url');
var ascii = require("./ASCII");
var claims = {
firstName: "vimal"
};
var header = {
"enc": "A128GCM",
"alg": "RSA-OAEP"
};
var headerBase64Url = base64url.encode(JSON.stringify(header));
console.log("headerBase64Url : " + headerBase64Url);
console.log("headerBase64Url to UTF8 : " + base64url.decode(headerBase64Url));
var cek = randomstring.generate(16);
console.log("cek : " + cek);
var publicKey = ursa.createPublicKey(fs.readFileSync('./pubkey.pem'));
var encryptedKey = publicKey.encrypt(cek, 'utf8', 'base64');
console.log("encryptedKey : " + encryptedKey);
// var privateKey = ursa.createPrivateKey(fs.readFileSync('./privkey.pkc8.pem'));
// var decryptedKey = privateKey.decrypt(encryptedKey, "hex", "utf8");
// console.log("decryptedKey : " + decryptedKey);
var iv = randomstring.generate(12);
console.log("IV : " + iv);
var cipher = crypto.createCipheriv('aes-128-gcm', cek, iv);
cipher.setAAD(Buffer.from(ascii.toASCII(headerBase64Url)));
var chipherText = cipher.update(JSON.stringify(claims), "utf8", "base64");
chipherText += cipher.final('base64');
console.log("chipherText : " + chipherText);
var chipherTextAuthTag = cipher.getAuthTag().toString("base64");
console.log("chipherText Auth Tag : " + chipherTextAuthTag);
var jweToken = headerBase64Url + "." + base64url.encode(encryptedKey, "base64") + "." + base64url.encode(iv, "base64") + "." + base64url.encode(chipherText, "base64") +
"." + base64url.encode(chipherTextAuthTag, "base64");
console.log("jweToken : " + jweToken);
// decrypt
var jweTokenParts = jweToken.split(".");
var headerHex = base64url.decode(jweTokenParts[0]);
console.log(headerHex);
var encryptedKeyHex = base64url.decode(jweTokenParts[1], "base64");
console.log(encryptedKeyHex);
var ivHex = base64url.decode(jweTokenParts[2], "base64");
console.log(ivHex);
var chipperTextHex = base64url.decode(jweTokenParts[3], "base64");
console.log(chipperTextHex);
var chipherTextAuthTagHex = base64url.decode(jweTokenParts[4], "base64");
console.log(chipherTextAuthTagHex);
var privateKey = ursa.createPrivateKey(fs.readFileSync('./privkey.pkc8.pem'));
var decryptedKeyHex = privateKey.decrypt(encryptedKeyHex, "base64", "utf8");
console.log("decryptedKeyHex : " + decryptedKeyHex);
var dcipher = crypto.createDecipheriv('aes-128-gcm', decryptedKeyHex, iv);
dcipher.setAAD(Buffer.from(ascii.toASCII(jweTokenParts[0])));
dcipher.setAuthTag(Buffer.from(chipherTextAuthTagHex, "base64"));
var planText = dcipher.update(chipperTextHex, "base64", "utf8");
planText += dcipher.final('utf8');
console.log(planText);
ASCII.js
function toASCII(text) {
var ascii = "";
for (var f in text) {
ascii = ascii + text.charCodeAt(f);
}
return ascii;
}
module.exports = {
toASCII: toASCII
};
Generated public and private keys using the below command:
openssl genrsa -out ./privkey.pem 2048
openssl pkcs8 -topk8 -inform pem -in ./privkey.pem -outform pem -nocrypt -out ./privkey.pkc8.pem
openssl rsa -in ./privkey.pkc8.pem -pubout -out ./pubkey.pem
What needs to be dome to fix the code?
This is my public key
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyT0HZHrlk8nN8HfTDq5t
dv6UCKHf7+RF1bICxaR4h2vzGCqcYUlzyW7Sp33BZAHDeO3d5tX26m7z2EOPaOPn
SSe6psgvBmE4Ivyc3+uyIYJm+Eo9bXiqqfvuDRidXsHj23w41l6GMERKbpOBVvp+
dmWt/cWU8FESvKUqNw/Au2R9mE1sQ5irMQj42hhUrVA1azs2AYpysKNZABm11YMb
/vd/xSPLsNqcDefuCs7j3CcT9xNLrMV6K63QjCAP+h0IMuA+ayi3WRUbl04D6cAC
AC97/cKMC0YLRumbf5x5/KjUBwNlzgRA3/n9KE+YjJ9Rs9dtiGnlg+c70Kgx4hm9
9QIDAQAB
-----END PUBLIC KEY-----
Generated JWE:
eyJlbmMiOiJBMTI4R0NNIiwiYWxnIjoiUlNBLU9BRVAifQ.A_KMJqfr6FZSoejRGWPsZKMCNZmPyaWoNvpG6KMRpqyv7Alb8Ui5ELWLjpcaemjNM8EFU8d4-Yzz8jRRZ5TpK2pEEc4NXfDLcnj2b0-38_-P-0HbW1YyMkkGMVXIpJDYMo8vKgVHIBj0pNlzgF7xmxLFWZlJlXmYzXi4QZcig5HezHg7AAQB7U2HYry25cQDYam60747gRCH372NaSm_dfRCNvH8copVXqiJGNs6xhslxMt_LopnZt9iIcAC9o7m0FPdnu_0Ui_w0jp5OUam8i0v8k6SSajBvXSedtUENxcehPGRSFYzi8KqZ53u4CpRygir84wNFRTi7zmLV6TlVw.84xgyx6TTI8I.4zTt1fI1XCbvxW2L-pH8_Mfp_ySF.EPmpEHiMYAvA2nqz9M0v5Q
Upvotes: 4
Views: 2788
Reputation: 16695
It looks like the IV is not correctly encoded using Base64 Url. On my computer, when I execute the following line of code:
console.log(iv);
console.log(base64url.encode(iv, "base64"));
Then the two lines are identical. When I modify your code:
var jweToken = headerBase64Url + "." + base64url.encode(encryptedKey, "base64") + "." + base64url.encode(iv, "base64") + "." + base64url.encode(chipherText, "base64") +
"." + base64url.encode(chipherTextAuthTag, "base64");
lnto these lines:
var bufferedIV = new Buffer(iv);
var jweToken = headerBase64Url + "." + base64url.encode(encryptedKey, "base64") + "." + base64url.encode(bufferedIV, "base64") + "." + base64url.encode(chipherText, "base64") +
"." + base64url.encode(chipherTextAuthTag, "base64");
Then it works fine ; I can load the resulting JWE using my PHP library.
From my understanding, the error comes from the base64url dependency that does not correctly encode the IV.
Upvotes: 2