cisco / node-jose

Apache License 2.0
699 stars 125 forks source link

How to generate JWE from public RSA JWK #164

Closed ollyblink closed 6 years ago

ollyblink commented 6 years ago

So I try to generate an encrypted JWE using a public RSA key, but the error produced says (Error2) "unsupported algorithm". After quite some time of trying to figuring out the problem, I gave up and just ask you how to do this or if it's a bug?

` const webkey = { "keys": [ { "kty": "RSA", "e": "AQAB", "kid": "a024254d-0321-459f-9530-93020ce9d54a", "key_ops": [ "encrypt" ], "n": "jkHgYN98dlR2w7NX-gekCWaCdbxs7X4XXh52DVQrK--krwUYqRbBIUEw1bV8KX0ox6TLt-e6wpYsYYFUItSd5ySqohHRMq1IhyE2zpEC95BA9V7VrFUYnczf1bd5c-aR079aoz5JPXfqx01TzNfxWBb04SlRjsmJeY1v6JrDUI5U0FSOmnJTb3tSS6Szrvi_qOyViYp4v9V2_OVYy45kF_LQQy-pr-kP4gapXL235cieeTW6UvkhzaPT2D-JKyzVjjjgnfRXr8Ox9I9c4wpef2-5nPPeafB5EnOMpJE11KzO_8xxiTGUywPPLQagBvY35gkhQbYS2dv3NGIVSLZHFw" } ] }; console.log("webkey", webkey);

    //generate key store from public JWK
    jose.JWK.asKeyStore(webkey)
        .then((result) => {
            console.log("Key Store File", JSON.stringify(result.toJSON()));
            let keyStore = result;

            //get the key to encrypt
            const encryptionKey: jose.JWK.Key = keyStore.get(webkey.keys[0].kid);
            console.log("Encryption Key", JSON.stringify(encryptionKey));
            const output = jose.util.base64url.encode("Hello World");
            const input = jose.util.asBuffer(output);

            //encrypting content
            let options = {};
            jose.JWE.createEncrypt(encryptionKey)
                .update(input)
                .final()
                .then((jweInGeneralSerialization) => {
                    console.log("Encryption result", JSON.stringify(jweInGeneralSerialization));
                }, (error) => {
                    console.log("Error2", error.message)
                });

        }, (error) => {
            console.log("error1", error.message);
        })`

Any suggestions?

linuxwolf commented 6 years ago

I am sorry you're running into issues. After correcting some coding errors in the example above, I was able to successfully generate a JWE. I did this using version 0.11.0.

What version of node-jose are you running?

ollyblink commented 6 years ago

Well no worries, I'm probably too stupid to use the library correctly ;) I am also using version 0.11.0. Can you please post the correct version so I can try it on my machine as well? Thanks a lot!

linuxwolf commented 6 years ago

I was able to successfully create a JWE from the following:

const webkey = {
    "keys": [
        {
            "kty": "RSA", 
            "e": "AQAB",
            "kid": "a024254d-0321-459f-9530-93020ce9d54a",
            "key_ops": [
                "encrypt"
            ],
            "n": "jkHgYN98dlR2w7NX-gekCWaCdbxs7X4XXh52DVQrK--krwUYqRbBIUEw1bV8KX0ox6TLt-e6wpYsYYFUItSd5ySqohHRMq1IhyE2zpEC95BA9V7VrFUYnczf1bd5c-aR079aoz5JPXfqx01TzNfxWBb04SlRjsmJeY1v6JrDUI5U0FSOmnJTb3tSS6Szrvi_qOyViYp4v9V2_OVYy45kF_LQQy-pr-kP4gapXL235cieeTW6UvkhzaPT2D-JKyzVjjjgnfRXr8Ox9I9c4wpef2-5nPPeafB5EnOMpJE11KzO_8xxiTGUywPPLQagBvY35gkhQbYS2dv3NGIVSLZHFw"
        }
    ]
};
console.log("webkey", webkey);

        //generate key store from public JWK
jose.JWK.asKeyStore(webkey).
        then((result) => {
                console.log("Key Store File", JSON.stringify(result.toJSON()));
                let keyStore = result;

                //get the key to encrypt
                const encryptionKey = keyStore.get(webkey.keys[0].kid);
                console.log("Encryption Key", JSON.stringify(encryptionKey));
                const output = jose.util.base64url.encode("Hello World");
                const input = jose.util.asBuffer(output);

                //encrypting content
                let options = {};
                jose.JWE.createEncrypt(encryptionKey).
                    update(input).
                    final().
                    then((jweInGeneralSerialization) => {
                        console.log("Encryption result", JSON.stringify(jweInGeneralSerialization));
                    }, (error) => {
                        console.log("Error2", error.message)
                    });

            }, (error) => {
                console.log("error1", error.message);
            })
ollyblink commented 6 years ago

Hm okay, this is really strange. I still get unsupported algorithm with that code. Did you use the "options" variable somewhere?

linuxwolf commented 6 years ago

No, I did not. I also tested in node version 9.4.0 ... in what environment are you running into the problem?

ollyblink commented 6 years ago

the version we use in our system is still 6.11.3. Apparently, we cannot update. Might that have something to do with it? I mean I guess the algorithm comes from another library, not node itself?

linuxwolf commented 6 years ago

the actual implementation does come from elsewhere; the bundled support in ... the Travis CI tests are run across v4 through v8 (and need to be updated to include v9), all without failure. There are tests for RSA key encryption use.

Is this exactly the code that is failing? If not, are you using specific options not listed here?

Also a reminder that RSA is for key encryption and not content encryption. node-jose should be defaulting correctly for that, though.

ollyblink commented 6 years ago

I'm using exactly that code, nothing more or less. And yes, I'm aware that the RSA key is for key encryption due to the content size limit possible to encrypt with it. But that shouldn't make a difference, right? I tried the tests with RSA key encryption and couldn't get them to run, either... Sadly. It's a pity because I have no other error output to understand what's actually going wrong. I figured that in basekey.js, the "algorithms" function is called 3 times, first 2 times with "use == ''" and "mode == wrap", and then once with "use == ''" and "mode == enc", and when this happens, the "unsupported algorithm" error is thrown... Do I have to specify the symmetric encryption algorithm somewhere for the encryption? Or could it have something to do with the fact that I use type script?

ollyblink commented 6 years ago

Okay so everywhere that "unsupported algorithm" occurs (basekey.js), I put a print in the form of "function::alg", which prints the alg var. Additionally, in the "algotithms" function (JWK.Key#algorithms), I print out mode. If I do that, I get:

'wrap::alg', 'RSA-OAEP' 'algorithms::mode', 'wrap'

'encrypt::alg', 'A256GCM' 'algorithms::mode', 'encrypt' 'Error2', 'unsupported algorithm: A256GCM'

So it seems like it doesn't find the symmetric encryption keys for some reason...

ollyblink commented 6 years ago

Update: it finds the algorithm in the browser, but not in node! Do you know how to fix that?

linuxwolf commented 6 years ago

This looks suspiciously like that your deployment of node doesn't in fact support AES-GCM for some reason node-jose supposed to fallback to an all-JS implementation, but it appears that is not working I really don't understand how this error happens -:

I don't think the reason is TypeScript. I am curious that you get A256GCM, when the current default is A128CBC-HS256 (I keep meaning to change that -: ). You can specify the content encryption algorithm with .createEncrypt({ contentAlg: "A256GCM" }, encryptionKey). Are you sure the sample above is exactly what you are using, and that only the changes to node-jose are the console.log() calls?

ollyblink commented 6 years ago

Actually it's not the default value. I changed it to A256GCM because I need that instead in the options:

{ format: 'compact', fields: { "alg": "RSA-OAEP-256", "kid": publicKey.kid, "enc": "A256GCM" } }

I will leave it now like that and debug it in the browser instead for the time being...

Another question. I need to set the JTI field of the JWT to a certain value and append the publicKey.kid in the header of the JWE. How do I specify that? Thanks a lot!

linuxwolf commented 6 years ago

Since there's no reliably reproducible scenario (outside your specific environment), I'm going to close this for now.

As to the other question:

ollyblink commented 6 years ago

Thanks a lot, it works!