Kong Enterprise 184.108.40.206 release includes a few new plugins. Among all, JWE decrypt plugin interests me the most. If you’ve read my OIDC and JWT posts before, you will know that Kong supports JWT very well but those are limited to JWS tokens.
Users need to compute signature of protected header and payload using algorithm defined in JOSE header and make sure signature matches.
As you can see, base64 encoded payload is not encrypted at all. Anyone has this JWT can easily decode the payload. These tokens are good if you only care about authenticity of the tokens. If you need to hide sensitive data in payload, you must use JWE tokens.
In today post, I would like to explain what JWE tokens are, how to generate JWE tokens and how you can use JWE decrypt plugin with other authentication plugins.
Let’s get started.
Kong Enterprise version 3.1.x
What is JWE token
JWE stands for JSON Web Encryption represents encrypted content using JSON-based data structures.
The JWE compact serialization form has five main components:
The JOSE header tells recipient the “alg” (Algorithm) used to encrypt the CEK (Content Encryption Key), “cty” (Content Type) of the payload, “enc” (Encryption Algorithm) used to encrypt payload to produce the ciphertext and the Authentication Tag and “typ” (Type) of this token.
JWE Encrypted Key:
This is the encrypted (CEK) using the RSAES-OAEP algorithm. Recipient must have the private key to get the CEK (A symmetric key) for decrypting the payload.
JWE initialization vector, JWE Ciphertext, JWE Authentication Tag
To encrypt payload, AES-GCM takes in an AES key, an initialization vector (IV), a plaintext payload, and optional additional authenticated data (AAD). It generates two outputs, a ciphertext and authentication tag. Ciphertext is not base64 encoded on JWE token so we can’t get the payload. To get the payload, we need to use the private key to decrypt the AES key first, then we will be able to decrypt the ciphertext. Authentication tag is used to prove the authenticity and integrity of the payload.
These are a lot of concept to digest. Let’s create some JWE tokens to help us understand.
I will use python jwcrypto library to generate JWE token. If you are not comfortable running python code, you can also find a list of libraries here. Just choose the one for the programming language you are familiar with.
Generate RSA key pairs
In order to decrypt the token later, we need to prepare our own RSA key pair. On Mac and Linux, you should have openssl pre-install so you can run below commands.
I am using Python 3.10.6 and pip 22.3.1. You can find this library official doc here.
Payload can be any plain text but it is very common to use JWS token as payload. With that users can protect the payload as well as validating the payloads, making sure they are signed by the trusted party.
You can install the jwcrypto library by running pip install jwcrypto.
Let’s save below file in the same folder with our key pairs and save it as jwe.py.
This code is pretty easy to understand and here are the steps:
Create a JWS token (valid for 5 minutes) with EC private key.
Encrypt this JWS token as payload with a randomly generated CEK (by the library).
Create the JWE token with the CEK encrypted with RSA public key.
Now if we run python jwe.py, we should get a similar JWE as in example.
Kong plugins usage
I will show you how to use JWT plugin to validate the JWS AFTER JWE decrypt token gets it from payload. If your OIDC provider issues JWE tokens, you can use JWE decrypt and OIDC/JWT-signer plugin to validate the JWS payload.
I assume Kong Enterprise 3.1.x version is running. In the demo I am running 220.127.116.11.
Let me create a service and a route for our testing. I am using httpbin as upstream service.
We can see the upstream (httpbin) received the decoded JWS in the authorization header.
As you can see from above, JWE was decrypted but there is no consumer associated with the token. If we want to rate limit by consumer, we need to use another authentication plugin. Let’s bring in JWT plugin.
If you are not sure how JWT plugin works, please check my previous post.
Enable JWT plugin
I am also enabling JWT plugin on our route.
1 2 3
curl --request POST \ --url http://localhost:8001/routes/jwe-route/plugins \ --data name=jwt
Then we need to create a consumer.
1 2 3
curl --request POST \ --url http://localhost:8001/consumers \ --data username=jwt-user
Create JWT credential
Lastly we need to associate the EC public key to this consumer as JWT credential. (We use the EC key to sign JWS token on our python script).
Now if you send the request again you should see something headers like X-Consumer-Username and X-Consumer-Id are sent to upstream. This mean Kong also validates the JWS token inside JWE token and found the consumer.