Announcements
This site is in read only until July 22 as we migrate to a new platform; refer to this community post for more details.
Get hands-on experience with 20+ free Google Cloud products and $300 in free credit for new customers.

Verifying encrypted and signed JAR (nested JWT) with VerifyJWT policy

Hi everyone,

I am trying to verify an encrypted and signed JAR (RFC9101) with Apigee. How I imagine this to work would be like this:

  1. VerifyJWT => Decrypt JWE
  2. DecodeJWT => Get the kid for the signing key
  3. VerifyJWT => Verify the signature

I am using the below policy to do the first step of decryption.

 

 

<VerifyJWT name="VJWT-DecryptToken">
    <Algorithms>
        <Key>RSA-OAEP-256</Key>
        <Content>A128GCM</Content>
    </Algorithms>
    <Type>Encrypted</Type>
    <PrivateKey>
        <Value ref="private.webKey"/>
    </PrivateKey>
    <Source>request.header.Authorization</Source>
</VerifyJWT>

 

 

This works when the JWT is just an JWE and the payload is a valid JSON with claims inside, but in my case I have a nested JWT. The payload itself is actually again an encoded JWT with signature. This is usually marked in the JWE with a header ("cty": "JWT") specifying the payload to be a JWT. Apigee seems to be ignoring this. The error I get is the following:

 

 

{
  "fault":{
    "faultstring": "Invalid token: policy(VJWT-DecryptToken)",
    "detail": {
      "errorcode":"steps.jwt.InvalidToken"
    }
  }
}

 

 

My code to generate the JWE in Java looks like this(using the nimbus jose lib😞

 

 

        final var jwtHeader = new JWSHeader.Builder(JWSAlgorithm.ES256)
                .type(JOSEObjectType.JWT)
                .keyID(kid);

        final var jwtPayload = new JWTClaimsSet.Builder();
        jwtPayload
                .claim("nonce", "test")
                .claim("redirect_uri", "test")
                .claim("response_mode", "jwt")
                .claim("response_type", "code")
                .claim("scope", "openid")
                .claim("state", "test")
                .issuer("test")
                .issueTime(Date.from(now))
                .audience("test");        
        final var signedJwt = new SignedJWT(jwtHeader.build(), jwtPayload.build());
        final var privateKey = getPrivateKey(privateKeyPath);

        signedJwt.sign(new ECDSASigner(privateKey, Curve.P_256));

        final var encryptedJwt = new JWEObject(
                new JWEHeader.Builder(JWEAlgorithm.RSA_OAEP_256, EncryptionMethod.A128GCM)
                        .contentType("JWT")
                        .build(),
                new Payload(signedJwt)
        );

        final var publicKey = getPublicKey(publicKeyPath);
        encryptedJwt.encrypt(new RSAEncrypter(publicKey));

        String resultJWE = encryptedJwt.serialize();

 

 

Is it possible to verify a nested JWT(first signed then encrypted) in Apigee with the built-in policies?

Solved Solved
2 2 259
1 ACCEPTED SOLUTION


@beach wrote:

Is it possible to verify a nested JWT(first signed then encrypted) in Apigee with the built-in policies?


 

It is not possible to do this in Apigee with built-in policies.  The VerifyJWT policy handles JWT, either signed or encrypted. What you are describing is a signed JWT, but wrapped in a JWE.  (Not a JWT).  

There is no built-in policy, as of 2024 December, in Apigee, that handles generic JWE.  (There is a feature request in the backlog, but it hasn't been priortiized.)  It's important to understand the distinction here: the payload for a JWT is always JSON.  For a JWE, the payload is "anything", and  "anything" includes the possibility that the payload is a signed JWT.

For that case, in which the encrypted payload is a signed JWT,  as described in RFC 9101 Section 6.1, you will need to resort to community-contributed custom policies, like this one: Apigee-CustomPolicy-EncryptedJWT, which, despite the name of the repo, actually handles encrypted JWT as well as JWE.

So you would use that to decrypt the outer JWE, which then recovers the signed JWT. Then you could use the built-in VerifyJWT to verify the signature on the signed JWT. 

One comment: When you describe using DecodeJWT to retrieve "the kid for the signing key" , you need to take care about this.  In a signed JWT , the kid header in the JWT provides the ID of the signing key (and probably the verifying key as well).  When using public/private algorithms like RSA or ECDSA, the VerifyJWT policy can retrieve the JWKS from a well-known endpoint, and then extract the key identified by the kid, automatically.  You do not have to manually extract the kid and resolve it to a key. 

But it sounds like, in your case, you want to manually resolve the kid to a key.  That's no problem, you can do that.  Just make sure you NEED to do that. 

View solution in original post

2 REPLIES 2


@beach wrote:

Is it possible to verify a nested JWT(first signed then encrypted) in Apigee with the built-in policies?


 

It is not possible to do this in Apigee with built-in policies.  The VerifyJWT policy handles JWT, either signed or encrypted. What you are describing is a signed JWT, but wrapped in a JWE.  (Not a JWT).  

There is no built-in policy, as of 2024 December, in Apigee, that handles generic JWE.  (There is a feature request in the backlog, but it hasn't been priortiized.)  It's important to understand the distinction here: the payload for a JWT is always JSON.  For a JWE, the payload is "anything", and  "anything" includes the possibility that the payload is a signed JWT.

For that case, in which the encrypted payload is a signed JWT,  as described in RFC 9101 Section 6.1, you will need to resort to community-contributed custom policies, like this one: Apigee-CustomPolicy-EncryptedJWT, which, despite the name of the repo, actually handles encrypted JWT as well as JWE.

So you would use that to decrypt the outer JWE, which then recovers the signed JWT. Then you could use the built-in VerifyJWT to verify the signature on the signed JWT. 

One comment: When you describe using DecodeJWT to retrieve "the kid for the signing key" , you need to take care about this.  In a signed JWT , the kid header in the JWT provides the ID of the signing key (and probably the verifying key as well).  When using public/private algorithms like RSA or ECDSA, the VerifyJWT policy can retrieve the JWKS from a well-known endpoint, and then extract the key identified by the kid, automatically.  You do not have to manually extract the kid and resolve it to a key. 

But it sounds like, in your case, you want to manually resolve the kid to a key.  That's no problem, you can do that.  Just make sure you NEED to do that. 

Welcome to the community, @beach! Thank you for sharing your question and for marking the reply as an accepted solution – it really helps others find answers to similar issues. A big shout-out to @Din for the response! We’d love for you to check out our articles and upcoming events – there’s plenty to explore. Glad to have you here 🙂