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.Take a look at this pseudo code showing how a JWT is constructed:
Y = Base64URLEncode(header) + ‘.’ + Base64URLEncode(payload)
JWT token = Y + ‘.’ + Base64URLEncode(RSASHA256(Y))

Header

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:
eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9

Payload

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:
eyJzdWIiOiJFUzI1NmluT1RBIiwibmFtZSI6IkpvaG4gRG9lIn0

Signature

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:
ICV6gy7CDKPHMGJxV80nDZ7Vxe0ciqyzXD_Hr4mTDrdTyi6fNleYAyhEZq2J29HSI5bhWnJyOBzg2bssBUKMYlC2Sr8WFUas5MAKIr2Uh_tZHDsrCxggQuaHpF4aGCFZ1Qc0rrDXvKLuk1Kzrfw1bQbqH6xTmg2kWQuSGuTlbTbDhyhRfu1WDs-Ju9XnZV-FBRgHJDdTARq1b4kuONgBP430wJmJ6s9yl3POkHIdgV-Bwlo6aZluophoo5XWPEHQIpCCgDm3-kTN_uIZMOHs2KRdb6Px-VN19A5BYDXlUBFOo-GvkCBZCgmGGTlHF_cWlDnoA9XTWWcIYNyUI4PXNw

A complete JWT

After putting together your encoded header, payload, and signature, your token should look similar to this one:
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJSUzI1NmluT1RBIiwibmFtZSI6IkpvaG4gRG9lIn0.ICV6gy7CDKPHMGJxV80nDZ7Vxe0ciqyzXD_Hr4mTDrdTyi6fNleYAyhEZq2J29HSI5bhWnJyOBzg2bssBUKMYlC2Sr8WFUas5MAKIr2Uh_tZHDsrCxggQuaHpF4aGCFZ1Qc0rrDXvKLuk1Kzrfw1bQbqH6xTmg2kWQuSGuTlbTbDhyhRfu1WDs-Ju9XnZV-FBRgHJDdTARq1b4kuONgBP430wJmJ6s9yl3POkHIdgV-Bwlo6aZluophoo5XWPEHQIpCCgDm3-kTN_uIZMOHs2KRdb6Px-VN19A5BYDXlUBFOo-GvkCBZCgmGGTlHF_cWlDnoA9XTWWcIYNyUI4PXNw

A request with a JWT

Having the JWT ready, you can make a request.

The request:
curl -ki -vvv https://demo1.ota-updates.com/example.zip -H "X-Akamai-OTA-Uid: rs256" -H "X-Akamai-JWT-Token: 
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJSUzI1NmluT1RBIiwibmFtZSI6IkpvaG4gRG9lIn0.ICV6gy7CDKPHMGJxV80nDZ7Vxe0ciqyzXD_Hr4mTDrdTyi6fNleYAyhEZq2J29HSI5bhWnJyOBzg2bssBUKMYlC2Sr8WFUas5MAKIr2Uh_tZHDsrCxggQuaHpF4aGCFZ1Qc0rrDXvKLuk1Kzrfw1bQbqH6xTmg2kWQuSGuTlbTbDhyhRfu1WDs-Ju9XnZV-FBRgHJDdTARq1b4kuONgBP430wJmJ6s9yl3POkHIdgV-Bwlo6aZluophoo5XWPEHQIpCCgDm3-kTN_uIZMOHs2KRdb6Px-VN19A5BYDXlUBFOo-GvkCBZCgmGGTlHF_cWlDnoA9XTWWcIYNyUI4PXNw"
The response:
* Added demo1.ota-updates.com:443:23.50.55.50 to DNS cache
* Hostname demo1.ota-updates.com was found in DNS cache
*   Trying 23.50.55.50...
* TCP_NODELAY set
* Connected to demo1.ota-updates.com (23.50.55.50) 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: CN=demo.ota-updates.com
*  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 /example.zip HTTP/1.1
> Host: demo1.ota-updates.com
> 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