How to Generate and validate JWE in node js?

2019-06-04 19:14发布

问题:

I tried the bellow code to create RSA-OAEP and A128GCM JWE generator and validator. it works with node js , ie, encrypt claims and generate the jwe and decrypt the same gives me the claims. but it is not working with the other clients , like nimbusds jose, jose4j. So for a sure I am missing something.

I am doing this by reading https://tools.ietf.org/html/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 key 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

Please help me to fix this 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

回答1:

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.