Generate a JWT with RSA keys

To generate a JWT signed with the RS256 algorithm and RSA keys, you need to use openssl commands or the auth0 library.

This procedure explains how to generate a JWT with openssl commands.

A JWT consists of three parts separated by dots.

  • Header
  • Payload
  • Signature

Take a look at this pseudo code showing how a JWT is constructed:

Y = Base64URLEncode(header) + '.' + Base64URLEncode(payload)
JWT token = Y + '.' + Base64URLEncode(RSASHA256(Y))


The header of a token signed with RS256:

"alg": "RS256",
"typ": "JWT"

To encode the header, you can use the following command:

echo -n '{"alg":"RS256","typ":"JWT"}' | base64 | sed s/\+/-/ | sed -E s/=+$//

The result is the encoded header:



The payload of a token signed with RS256 is similar to this one:

"sub": "RS256InOTA",
"name": "John Doe"

To encode the payload, use a command similar to this one:

echo -n '{"sub":"RS256inOTA","name":"John Doe"}' | base64 | sed s/\+/-/ | sed -E s/=+$//

The result is the encoded payload:



To create a signature with the an RSA private key, you can use a command similar to this one:

echo -n "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJFUzI1NmluT1RBIiwibmFtZSI6IkpvaG4gRG9lIn0" | openssl dgst -sha256 -binary -sign jwtRSA256-private.pem  | openssl enc -base64 | tr -d '\n=' | tr -- '+/' '-_'

where jwtRSA256-private.pem is the private RSA key you created previously. See Generate RSA keys.

The result is the encoded signature:


A complete JWT

After putting together your encoded header, payload, and signature, your token should look similar to this one:


A request with a JWT

Having the JWT ready, you can make a request.

The request:

curl -ki -vvv -H "X-Akamai-OTA-Uid: rs256" -H "X-Akamai-JWT-Token:

The response:

* Added to DNS cache
* Hostname was found in DNS cache
*   Trying
* Connected to ( port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
*   CAfile: /etc/ssl/cert.pem
  CApath: none
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384
* ALPN, server accepted to use http/1.1
* Server certificate:
*  subject:
*  start date: Jul 22 06:51:03 2020 GMT
*  expire date: Oct 20 06:51:03 2020 GMT
*  issuer: C=US; O=Let's Encrypt; CN=Let's Encrypt Authority X3
*  SSL certificate verify ok.
> GET / HTTP/1.1
> Host:
> User-Agent: curl/7.64.1
> Accept: */*
> X-Akamai-OTA-Uid: rs256
> X-Akamai-JWT-Token: eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJSUzI1NmluT1RBIiwibmFtZSI6IkpvaG4gRG9lIn0.ICV6gy7CDKPHMGJxV80nDZ7Vxe0ciqyzXD_Hr4mTDrdTyi6fNleYAyhEZq2J29HSI5bhWnJyOBzg2bssBUKMYlC2Sr8WFUas5MAKIr2Uh_tZHDsrCxggQuaHpF4aGCFZ1Qc0rrDXvKLuk1Kzrfw1bQbqH6xTmg2kWQuSGuTlbTbDhyhRfu1WDs-Ju9XnZV-FBRgHJDdTARq1b4kuONgBP430wJmJ6s9yl3POkHIdgV-Bwlo6aZluophoo5XWPEHQIpCCgDm3-kTN_uIZMOHs2KRdb6Px-VN19A5BYDXlUBFOo-GvkCBZCgmGGTlHF_cWlDnoA9XTWWcIYNyUI4PXNw
< HTTP/1.1 200 OK
HTTP/1.1 200 OK
< Accept-Ranges: bytes
Accept-Ranges: bytes
< Content-Type: application/zip
Content-Type: application/zip
< ETag: "fc1b24f7ac7f087a7e8072fc27df7f09:1496993002"
ETag: "fc1b24f7ac7f087a7e8072fc27df7f09:1496993002"
< Last-Modified: Fri, 09 Jun 2017 07:23:22 GMT
Last-Modified: Fri, 09 Jun 2017 07:23:22 GMT
< Server: AkamaiNetStorage
Server: AkamaiNetStorage
< Content-Length: 16588461
Content-Length: 16588461
< Date: Mon, 24 Aug 2020 13:30:28 GMT
Date: Mon, 24 Aug 2020 13:30:28 GMT
< Connection: keep-alive
Connection: keep-alive
< X-Akamai-Staging: ESSL
X-Akamai-Staging: ESSL