Search code examples
node.jsjwtjose

Implementing JWE encryption for a JWS signed token in Node.JS with Jose 4.11


I have difficulty manipulating the Jose Node.JS documentation to chain the creation of a JWS and JWE. I cannot find the proper constructor for encryption. It looks like I can only encrypt a basic payload not a signed JWS.

Here is the code sample I try to fix to get something that would look like

const jws = await createJWS("myUserId");
const jwe = await encryptAsJWE(jws);

with the following methods

export const createJWS = async (userId) => {
  const payload = {
  }

  payload['urn:userId'] = userId

  // importing key from base64 encrypted secret key for signing...
  const secretPkcs8Base64 = process.env.SMART_PRIVATE_KEY
  const key = new NodeRSA()
  key.importKey(Buffer.from(secretPkcs8Base64, 'base64'), 'pkcs8-private-der')
  const privateKey = key.exportKey('pkcs8')
  const ecPrivateKey = await jose.importPKCS8(privateKey, 'ES256')

  const assertion = await new jose.SignJWT(payload)
    .setProtectedHeader({ alg: 'RS256' })
    .setIssuer('demolive')
    .setExpirationTime('5m')
    .sign(ecPrivateKey)

  return assertion
}

export const encryptAsJWE = async (jws) => {

  // importing key similar to createJWS key import
  const idzPublicKey = process.env.IDZ_PUBLIC_KEY //my public key for encryption
  ...
  const pkcs8PublicKey = await jose.importSPKI(..., 'ES256')

 
  // how to pass a signed JWS as parameter?
  const jwe = await new jose.CompactEncrypt(jws)
                     .encrypt(pkcs8PublicKey)
  return jwe
}

Solution

  • The input to the CompactEncrypt constructor needs to be a Uint8Array, so just wrapping the jws like so (new TextEncoder().encode(jws)) will allow you to move forward.

    Moving forward then:

    You are also missing the JWE protected header, given you likely use an EC key (based on the rest of your code) you should a) choose an appropriate EC-based JWE Key Management Algorithm (e.g. ECDH-ES) and put that as the public key import algorithm, then proceed to call .setProtectedHeader({ alg: 'ECDH-ES', enc: 'A128CBC-HS256' }) on the constructed object before calling encrypt.

    Here's a full working example https://github.com/panva/jose/issues/112#issue-746919790 using a different combination of algorithms but it out to help you get the gist of it.