Home > other >  Unable to decrypt JWE with NPM Jose package
Unable to decrypt JWE with NPM Jose package

Time:10-06

I'm able to decrypt a JWE with an older version of jose but I'm struggling to use the latest version API.

My token headers are the following:

{
  "alg": "A128KW",
  "enc": "A128CBC-HS256",
  "typ": "JWT"
}

With jose 2.0.3 this code (copied from another stackoverflow post) is working to decrypt the payload:

const { JWK, JWE } = require('jose');
const privateKey = JWK.asKey("my key");
const jwe = "my JWE"
const jwt = JWE.decrypt(jwe, privateKey);
const payload = Buffer.from(jwt.toString().split('.')[1], 'base64');
const data = JSON.parse(payload);

Obviously I cannot share the the private key. It's a string with 16 characters.

I tried to used a later version of jose (4.9.2) but the API changed totally. I tried this but it does not end well:

const jose = require('jose');
const jwe = "my token";
const key = await jose.importJWK({ kty: 'oct', k: 'my key', alg: 'A128CBC-HS256' })
const { plaintext, protectedHeader } = await jose.compactDecrypt(jwe, key);
console.log(protectedHeader)
console.log(new TextDecoder().decode(plaintext))

I get this error :

TypeError: Invalid key size for alg: A128KW

I confirm my key is 16 characters long (so 128 bits normally) so I don't get this error. Any hint?

CodePudding user response:

The issue is your secret key. You're using 16 character string and passing it in as a JWK. JWK k is meant to be base64url encoded, so your 16 characters "encoded" becomes a 12 byte secret when "decoded".

Here's what will work, don't bother with a key import for symmetric secrets, just pass a Buffer instance instead.

const { plaintext, protectedHeader } = await jose.compactDecrypt(jwe, Buffer.from('my 16 bytes secret'));
console.log(protectedHeader)
console.log(new TextDecoder().decode(plaintext))

Additionally, if your payload is a JWT claims set, you might as well do a proper JWT validation combined with the decrypt operation. docs

const { plaintext, protectedHeader } = await jose.jwtDecrypt(jwe, Buffer.from('my 16 bytes secret'));
console.log(protectedHeader);
console.log(payload);

CodePudding user response:

This is full example about jwt and jwe. Full project -- Generation -- Validation -- How to use

Generation jwt and jwe

let jwt = require('jsonwebtoken'),
    {
        JWK,
        parse
    } = require('node-jose');

 async function getJwtEncrypt(raw, format = 'compact', contentAlg = 'A256GCM', alg = 'RSA-OAEP') {
        let publicKey = await JWK.asKey(process.env.JWT_PUBLIC_KEY, 'pem');
        const buffer = Buffer.from(JSON.stringify(raw));

        return await JWE.createEncrypt(
            {format: format, contentAlg: contentAlg, fields: {alg: alg}}, publicKey)
            .update(buffer).final();
    }


function getJwtSign(payload, subject) {
        const SIGN_OPTIONS = {
            subject: `${subject}`,
            expiresIn: payload.expiresIn,
            algorithm: 'RS256'
        };

        return jwt.sign(payload, process.env.PRAIVATE_KEY, SIGN_OPTIONS);
    }

Validation jwt and jwe

let jwt ,
    {
        TokenExpiredError,
        JsonWebTokenError
    } = require('jsonwebtoken'),
    {
        JWK,
        parse
    } = require('node-jose');

  function getJwtVerify(token, cb) {
    try {
        jwt.verify(token, process.env.PUBLIC_KEY, {}, (err, decoded) => {

            if (err !== null && err instanceof TokenExpiredError) {
                cb('TOKEN_EXP');
                return Json.builder(Response.HTTP_UNAUTHORIZED_TOKEN_EXP);
            }

            if (err instanceof JsonWebTokenError) {
                cb('IN_VALID_TOKEN');
                return Json.builder(Response.HTTP_UNAUTHORIZED_INVALID_TOKEN);
            }

            cb(decoded);
        });
    } catch (e) {
        ValidationException(e);
    }
}


 async function getJwtDecrypt(encryptedBody) {
    try {
        let keystore = JWK.createKeyStore();
        await keystore.add(await JWK.asKey(process.env.JWE_PRAIVATE_KEY, 'pem'));
        let outPut = parse.compact(encryptedBody);
        let decryptedVal = await outPut.perform(keystore);
        let token = Buffer.from(decryptedVal.plaintext).toString();

        if (typeof decryptedVal.plaintext === ('undefined' || null))
            return Json.builder(Response.HTTP_UNAUTHORIZED_INVALID_TOKEN);

        return token.replace(/["] /g, '');

    } catch (e) {
        ValidationException(e);
    }
}

.env file

PRAIVATE_KEY   = "-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQDHRyVyzQTw5XkTaM3bgfKE5Ry CqqmijpvHprM1Dvkr3q7lR2A
ZTzKGnyHVbnVpKPga8AHs3E7l/MVci2BhWLbHGOn4GgrrqDmItvCvFcfGZM9USZE
2tp5zLj3aBoqImk niFHQipSPus9JhNU9HkLNTUJGwuuxfWuXwtnMhWcpwIDAQAB
AoGAMs9bJwhLSDjaRC6mvl9FvMjGKVaC6G 6Mnb1NWCv3ME5Y/bDTOeDNmzGb6NV
/Lk7547RqaUBLBa0LjWskKe36n6hfWExEaRe9ikqRiz6y0QXyfO5h6qZD0YXSsIu
5VY8LBCz45Z330I296jLbgkZ2OhZbEj8lk8rf99JtzGzRBECQQD1W0cBRIKt6e8g
KJNLp8A0dLId6bbmgG5xnOscTMfxzZAvMAz6eN4ur8vkJKDVr1YjRCHETrfdaX/d
kfY/29TtAkEAz wokyzPERFA XCdLLW9d9b3nPQhTUodBIq9EXfnhPAXRWzjObLl
zItS3bDzWpGqfwP7nc9zjpcqY0zKdJ 5YwJASnyDeecKpTG33tNypC0xNLuYt2wU
krW60dMJrXXB3a7CbxDvX7sB Lp187UK/tRUGjC875PWTemRX/rH/2sFoQJBAIN8
MFyB5aBBbPlRAdQYSezTAFs89yJNT/RjWBUH4lzrB4xbw4XlX/Tt1kVjdUE9BLi1
6BRv7/ oEKIjGZSOvUkCQEdb/CPaqXPfn7zA/SKL3hgJbbEo8aowOM4VfV ZVojp
vzvj/ZjQpRKDEJ7iiJCXBUT59BwPuirq8v6fjWMRneU=
-----END RSA PRIVATE KEY-----"

PUBLIC_KEY     = "-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDHRyVyzQTw5XkTaM3bgfKE5Ry 
CqqmijpvHprM1Dvkr3q7lR2AZTzKGnyHVbnVpKPga8AHs3E7l/MVci2BhWLbHGOn
4GgrrqDmItvCvFcfGZM9USZE2tp5zLj3aBoqImk niFHQipSPus9JhNU9HkLNTUJ
GwuuxfWuXwtnMhWcpwIDAQAB
-----END PUBLIC KEY-----"

JWE_PRAIVATE_KEY = "-----BEGIN RSA PRIVATE KEY-----
MIICWgIBAAKBgFsiRgcjHE4S0uoNvOEz/YdmPRRBo APWpWpM8 B2JSXNZdQCoin
7psz8Pup 4C95Qz/R5Zhw/IZBfvOpD6Hj9v9cMgnnMjPYW6eecByJ6S6lynetRLT
Igy4ICRynhx4rHvRwXpr6dEeaazArDJlwutMv9cW7DdSPiqRSTl9YAffAgMBAAEC
gYAsZ1qvh4/3CnzxxZfOMsLJAiuofwMV3OVKHpM7/AxG hYGj91SEGDWBkzYkk4U
wHGmD4wV3bTXdRHRSzIDtZGGDZbneWN58TRUPuSP1XiNVIb8Doaj91cprRiBxlmM
ZHJsix/OLN/Sm3UjuZolSjG3K0QucrrBUtPBfhxJ5GXmYQJBAKOwYZHyhz9gGrW7
zkkF9Fivu9P/H0/eS4HaTQLYEUG475KIj6xQLrjTRtxZ139DV3jrGoLEapFvAaOk
fETiBwMCQQCOhy7Aa5muKcELFJqxzY1CWLCaayX43ZPVYKCkLaqDYRWtufLjZ3vj
hWiQScULEGLiM axZFgTmbtWI6oy4cb1AkBT UeEzQvvSklJlChWs/RPjw/nyQjy
O1M3MZuyatAnjE1zOhWiy5u8e77tijWQdyanxMzb6xHUvEL2BYsu91mrAkAbq/tT
uJBZ1Bl6wUFXjAUFAJspH x7aOmu39fQiF02rL68wAF8TTcscVZfzTLIdyH7sP/1
KPpAs/Q/QSVmQ5eRAkBHWQgmTC5MsmLmJO6UdUjOp5a9rJzxnMej8zUiR/lZ7ssc
4jAfqi8ukBv 5THumh3TKDNPAu9 r5nWOahb/jrJ
-----END RSA PRIVATE KEY-----"

JWT_PUBLIC_KEY = "-----BEGIN PUBLIC KEY-----
MIGeMA0GCSqGSIb3DQEBAQUAA4GMADCBiAKBgFsiRgcjHE4S0uoNvOEz/YdmPRRB
o APWpWpM8 B2JSXNZdQCoin7psz8Pup 4C95Qz/R5Zhw/IZBfvOpD6Hj9v9cMgn
nMjPYW6eecByJ6S6lynetRLTIgy4ICRynhx4rHvRwXpr6dEeaazArDJlwutMv9cW
7DdSPiqRSTl9YAffAgMBAAE=
-----END PUBLIC KEY-----"
  • Related