Hi @dchiesa1
We intending on encrypting a JSON payload with a dynamically generated CEK using AES256GCM and then encrypting the CEK using a RSA-OAEP-256 algorithm (public-private key pair) and sending it to the Apigee proxy as an encrypted JWT.
Does the verifyEncryptedJWT callout https://github.com/DinoChiesa/Apigee-CustomPolicy-EncryptedJWT also decrypt the payload as well apart from decrypting the key?
Additionally what would be the recommended approach to achieve this through Apigee?
Thank you
Solved! Go to Solution.
The backend response sent back to Apigee which is encrypted using the GenerateJWT callout and sent back to the client.
You can do that. But with crypto, in particular with JWE and JWT, I caution you to take extra care with the terms you use, so you are clear on what's happening.
There is a "family" of IETF specifications called JSON Object Signing and Encryption, under the acronym "JOSE".
JOSE includes these specs:
Specification | Quick summary | Apigee support? |
RFC 7515 - JWS | JSON Web Signature Describes how to use JSON to encode a signature of *any* data stream. |
Built-in policies |
RFC 7516 - JWE | JSON Web Encryption Describes how to use JSON to encrypt and wrap any data stream. (XML or PDF or PNG or JSON or whatever) |
via a Java callout. Support only for RSA-based encryption algorithms. |
RFC 7519 - JWT | JSON Web Token Describes how to use either JWS or JWE to sign or encrypt a JSON payload, and also prescribes the semantics of some "well known" claims or properties within that JSON payload, like iss for "issuer" or "issuing party", "aud" for "audience", "sub" for Subject, "exp" for expiry, and so on. |
Built-in policies. Works for either signed or encrypted tokens. Supports all the encryption algorithms in RFC 7518. |
RFC 7518 - JWA | JSON Web Algorithms Specifies algorithms for signing or encrypting. |
Built in support for all of these, via the JWS and JWT policies. |
The "JSON" in the names of these specifications does not imply that the signing or encrypting can be done only on JSON objects. Instead, it implies that the JSON is used to express encoded metadata about the signature or ciphertext. For example you could use JWS to sign a JSON, or a PDF, or a .PNG, or even an XML document. Likewise with JWE you can encrypt a JSON, or XML, or PDF etc. The result (either a signature or a ciphertext) is "wrapped in" a JSON wrapper that encodes the signed or encrypted thing (maybe an XML document etc) as well as a JSON header that describes how the signature or ciphertext was computed, and the signature or ciphertext bytestream itself.
Under the banner of JWS, one option is to sign a JSON payload. The result of that, we can call a JWT. Similarly, under JWE, one option is to encrypt a JSON payload. The result of that operation we can call an encrypted JWT. These "Tokens" are distinct from the general case only in that the payload itself is JSON. Whether using signing or encryption, if the signed or encrypted payload itself is a JSON object, we can call the result a JWT.
So you see, Apigee has builtin support for "encrypted JWT", which is to say, for JWT that are encrypted according to the JWE spec. Another way to say it is, Apigee has support for JWE, only if the encrypted thing is a JSON object. But Apigee does not have builtin support for JWE in general. For that you need to use the external callout (link), which as of March 2022, is limited in that it supports only RSA-based crypto algorithms. (Edit: As of June 2024, that callout supports ECDH algorithms as well).
I hope this is all clear.
So getting back to your goal:
The intention is for the client to encrypt the entire payload which gets decrypted in Apigee and passed unencrypted to the backend. The backend response sent back to Apigee which is encrypted using the GenerateJWT callout and sent back to the client.
No problem. If you use "encrypted JWT", then you can use the builtin policies to do the things you want in the Apigee proxy:
If you use JWE (which implies that the client's original payload is not JSON), then within Apigee you must use the Java callout to do those things. It works the same way.
You earlier said
We intending on encrypting a JSON payload with a dynamically generated CEK using AES256GCM and then encrypting the CEK using a RSA-OAEP-256 algorithm (public-private key pair) and sending it to the Apigee proxy as an encrypted JWT.
That sounds to me like an encrypted JWT, which means you can use the builtin policies in Apigee to accomplish that work. The builtin policy supports A256GCM for the content-encryption algorithm and RSA-OAEP-256 for the key-encryption algorithm. You do not need to resort to the Encrypted JWT callout. On the request flow, the configuration would look something like this:
<VerifyJWT name='VJWT-encrypted-token'>
<!-- You must specify AT LEAST the key-encryption algorithm -->
<Algorithms>
<Key>RSA-OAEP-256</Key>
<Content>A256GCM</Content>
</Algorithms>
<!-- specify your private RSA key here -->
<PrivateKey>
<Value ref='private.private_rsa_key'/>
</PrivateKey>
<!--
If the inbound JWT is in a variable specify it here.
If the inbound JWT is in the Authorization header, omit the following line.
-->
<Source>inbound.jwt</Source>
<!-- The policy will implicitly verify the expiration of the JWT -->
<!-- Optional: You can also verify specific claims if you like -->
<Subject>dino@example.org</Subject>
<Issuer>whatever-issuer-you-like</Issuer>
</VerifyJWT>
On the Response flow (generating an encrypted JWT) it would look like this:
<GenerateJWT name='GJWT-response'>
<Algorithms>
<Key>RSA-OAEP-256</Key>
<Content>A256GCM</Content>
</Algorithms>
<IgnoreUnresolvedVariables>false</IgnoreUnresolvedVariables>
<PublicKey>
<Value ref='public.key_pem'/>
</PublicKey>
<Subject>subject-here</Subject>
<Issuer>issuer-here</Issuer>
<Audience>audience-here</Audience>
<AdditionalClaims>
<Claim name='additional-claim'>additional-claim-value</Claim>
</AdditionalClaims>
<AdditionalHeaders>
<Claim name='hdr1'>hdr1</Claim>
</AdditionalHeaders>
<OutputVariable>output_variable</OutputVariable>
</GenerateJWT>
[EDIT] there is a way to pass a JSON blob as the payload to the GenerateJWT policy, but it is a little obscure. If you have the entire JSON that you want to encrypt, then you need to use something like this:
<GenerateJWT name="GenerateJWT-3">
<IgnoreUnresolvedVariables>false</IgnoreUnresolvedVariables>
<Algorithms>
<Key>RSA-OAEP-256</Key>
<Content>A256GCM</Content>
</Algorithms>
<PublicKey>
<Value ref="rsa_public_key"/>
</PublicKey>
<!-- specify the context variable containing the desired payload here -->
<AdditionalClaims ref='raw-jwt-payload'/>
<OutputVariable>output_jwt</OutputVariable>
</GenerateJWT>