1

I'm struggling to produce a JWE in jwcrypto equivalent to that in node-jose with the same key. The goal is to produce a key in node-jose and export the pubkey to jwcrypto to encrypt a payload, which will then be consumed by node-jose and decrypted.

My test entirely in node-jose works fine:

var jose = require("node-jose")
var keyStore = jose.JWK.createKeyStore()

keyStore.generate('EC', 'P-521').then(function (result) {

    // Use exported key to encrypt something (so we see the same thing jwcrypto does)    
    jose.JWK.asKey(result.toJSON()).then(function(result) {

        jose.JWE.createEncrypt(result).update('this is a test payload').final().then(function (result) {

            jose.JWE.createDecrypt(keyStore).decrypt(result).then(function (result) {

                // Result is good
                console.log(result)
        })
    })

})

However, when I do the same in python, node-jose produces a different JWE:

    key = jwk.JWK(**json.loads(the_exported_key))

    # This key looks exactly the same as the exported key in node-jose
    print(key.export(private_key=False))

    payload = "this is a test payload"

    header = {
        'alg': 'ECDH-ES',
        'enc': 'A128CBC-HS256',
    }
    my_jwe = jwe.JWE(payload.encode('utf-8'), header)
    my_jwe.add_recipient(key)

When node-jose tries to decrypt my_jwe, it fails with "Error: no key found". Strangely (or not, this is my first time using JWEs...), the two encryption results are (see examples below). I think I'm missing how to get jwcrypto to, like node-jose, not require 'header' values, but when I pull those it complains.

node-jose example (junk data):

{
    ciphertext: "1e7YX6hNDJWJELhHTNXEOg",
    iv: "oQZZq2smHX8u8MMwoC6NBA",
    protected: "eyJhbGciOi".....(very long string),
    tag: "3NfEqx9f2ivL8QodG5Duaw",
}

jwcrypto (junk data):

{
    ciphertext: "7ldKnkcsLZUy-SXFRv_HpkWOsb-YUUlNFv-4M5yZhCA",
    iv: "1uErMiK_RWcaPXPCPq12Uw",
    header: {
        alg: "ECDH-ES",
        enc: "A128CBC-HS256",
        epk: {
            crv: "P-521",
            kty: "EC",
            x: different from the exported key, I assume this is expected 'epk', 
            y: different from the exported key, I assume this is expected 'epk',
        },
        kid: "JCU3sWKfirVybFbpy2NPOnq-4-43JiemRZLO5dmPMVo"
    },
    tag: "51AMFyCJld5uPyMFLLl-sw",
}
obrienmd
  • 2,575
  • 3
  • 17
  • 8

1 Answers1

1

The results you got with jwcrypto or node-jose look compliant with the RFC7516. The only difference is that node-jose set your header in the protected member (integrity protected header) whereas jwcrypto set it in the header member (per-recipient unprotected header).

My understanding is that node-jose throws an error because it cannot find the public key in the header (epk member). It only checks the protected member and not other headers (header and also unprotected members if present) which is not compliant with the RFC7516 section 2 paragraph 4.:

let the JOSE Header be the union of the members of the JWE Protected Header, the JWE Shared Unprotected Header and the corresponding JWE Per-Recipient Unprotected Header

From my point of view, when a JWE is created for only one recipient, there is no reason to set the epk member (as well as the alg and enc members) in an unprotected header. The presence of those unprotected headers will prevent you from using the JWE Compact Serialization. So the behaviour of jwcrypto should be changed.

I don't know how these two libraries work, however there are two ways to fix that issue:

  • Force jwcrypto to use the integrity protected header instead of the unprotected one (best).
  • Ask node.jose to take into account the other headers (good but may take some time)
Community
  • 1
  • 1
Spomky-Labs
  • 15,473
  • 5
  • 40
  • 64