This procedure explains how to generate a pair of RSA keys that you can use to sign and verify JWTs.
---
title: Generate a JWT with RSA keys
subtitle: This procedure explains how to generate a pair of RSA keys that you can use to sign and verify JWTs.
author: Akamai
date: March 2, 2024
source: https://techdocs.akamai.com/iot-token-access-control/docs/generate-rsa-keys
snippet: https://jonlabelle.com/snippets/view/markdown/generate-a-jwt-with-rsa-keys
notoc: false
---
## Generate RSA keys
> Source: <https://techdocs.akamai.com/iot-token-access-control/docs/generate-rsa-keys>
This procedure explains how to generate a pair of RSA keys that you can use to
sign and verify JWTs.
Create a private RSA keys that are between 1024 and 4096 bits long. You have a
`jwtRSA256-private.pem` private key in PEM format.
```bash
openssl genrsa -out jwtRSA256-private.pem 2048
```
> Don't add a passphrase.
Extract a public key from the private key.
```bash
openssl rsa -in jwtRSA256-private.pem -pubout -outform PEM -out jwtRSA256-public.pem
```
Sample contents of the `jwtRSA256-public.pem` public key in PEM format:
```text
-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA2vOMbfP11bZDIrTRVH4f
k6VOfvSx3XnDSfqgwcvX1f0fxyOp7xHhA6ZOv9cN726RuNB/g1nE3G5ugOS3f28N
rAPeADetuk6ZzeEWy/WHBQCW8k2O9uMWxmMSZiMLwwWXMUCBXe0L2qKAoVJIS9Nh
0ihEdIf6XDZvQPbnNrX3wW9gcPHQwuBjD6r0jO59tPQEwVfScE1qqwLJzqDq/xR3
JIxPnZf1H4JiOHDYxIePmrsL+lq/F1CK1U1Ei8tDGpBJ9Dvg+ra7MfWksROTJct0
n0Te3gjG98EaSsld9vCFe5sebb3zJ6tWaZyYaP8UQlgk4MfNC5bmIdodx15oHOzO
lJPeP7U25+x2T2QiFKWPQSNYdCwDCMV6kUuRKgHbEitTnURtHA4Kl0LbdQgLkXe/
nF3tanLC9Y2dFOD18hHlpu87hY45hY6TnOENEqOlTvXiKsmEbB2ICXpAGIU4cw8u
AAd5r9EzmjMrnzsM2z3dwFJqOhas2TUbMR/JGJOYQhWiXW574gPcGX7ejCYj9DG2
spIYwcSPv9pQnUObfjmJDyKr/y1g++D5mMyJ5myNlg+ixSSGWIsVb7mZFSjg5Wnq
11XVNfTY6lt6jfq7ZIc6vDQFw3ZwBQKYbdn+UmyxelkiScYaZPU8wXi0ZCgNVZR/
9zWuZV+w4v3j7EfuemACqukCAwEAAQ==
-----END PUBLIC KEY-----
```
## Generate a JWT with RSA keys
> Source: <https://techdocs.akamai.com/iot-token-access-control/docs/generate-jwt-rsa-keys>
To generate a JWT signed with the RS256 algorithm and RSA keys, you need to use
openssl commands or the [auth0](https://auth0.com/) 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:
```text
Y = Base64URLEncode(header) + '.' + Base64URLEncode(payload)
JWT token = Y + '.' + Base64URLEncode(RSASHA256(Y))
```
### Header
The header of a token signed with RS256:
```json
{
"alg": "RS256",
"typ": "JWT"
}
```
To encode the header, you can use the following command:
```bash
echo -n '{"alg":"RS256","typ":"JWT"}' | base64 | sed s/\+/-/ | sed -E s/=+$//
```
The result is the encoded header:
```text
eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9
```
### Payload
The payload of a token signed with RS256:
```json
{
"sub": "ES256inOTA",
"name": "John Doe"
}
```
To encode the payload, you can use the following command:
```bash
echo -n '{"sub":"ES256inOTA","name":"John Doe"}' | base64 | sed s/\+/-/ | sed -E s/=+$//
```
The result is the encoded payload:
```text
eyJzdWIiOiJFUzI1NmluT1RBIiwibmFtZSI6IkpvaG4gRG9lIn0
```
### Signature
To create a signature with the an RSA private key, you can use a command similar
to this one:
```bash
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](#generate-rsa-keys).
The result is the encoded signature:
```text
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:
```text
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:
```bash
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:
```text
* 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
```
## Useful commands
Here is a handful of openssl commands that you may need.
### Checking the curve specification of your public key
The command:
```bash
openssl ec -pubin -in ec-secp256k1-pub-key.pem -text -noout -param_out
```
The result for the `ec-secp256k1-pub-key.pem` public key:
```text
read EC key
Private-Key: (256 bit)
pub:
04:d4:cb:47:23:19:46:3f:94:c0:46:a0:5c:72:b7:
5d:36:6d:45:9d:81:f5:16:9f:a8:9c:44:f3:e4:a6:
d7:3d:27:86:e4:51:b0:28:c5:17:19:f0:26:49:92:
6a:da:01:0d:50:b1:5e:c3:0c:a6:b7:ef:5b:4d:79:
23:05:98:61:f0
ASN1 OID: secp256k1
```
### Extracting a public key from a PEM certificate file
You can extract a public key from an ECDSA certificate in PEM format. In this
example, the certificate file is named: `ec256_cert.pem` and looks like that:
```text
-----BEGIN CERTIFICATE-----
MIIBfTCCASMCCQCKDA6MX6K5vDAKBggqhkjOPQQDAjBIMQswCQYDVQQGEwJQTDET
MBEGA1UECgwKQWthbWFpLmNvbTEPMA0GA1UECwwGQWthbWFpMRMwEQYDVQQDDAph
a2FtYWkuY29tMB4XDTIwMDQyODExMjcxMloXDTIyMTIxNDExMjcxMlowSDELMAkG
A1UEBhMCUEwxEzARBgNVBAoMCkFrYW1haS5jb20xDzANBgNVBAsMBkFrYW1haTET
MBEGA1UEAwwKYWthbWFpLmNvbTBWMBAGByqGSM49AgEGBSuBBAAKA0IABNTLRyMZ
Rj+UwEagXHK3XTZtRZ2B9RafqJxE8+Sm1z0nhuRRsCjFFxnwJkmSatoBDVCxXsMM
prfvW015IwWYYfAwCgYIKoZIzj0EAwIDSAAwRQIhAJZtIa0TeuLQgfSPJ9rVZwwx
Y0wxsGvf02XKhx0s77UmAiB7S9K8cJl+KNT5KDDZV8wSNfgYCdObzWXeqW3xa6ml
Wg==
-----END CERTIFICATE-----
```
The command:
```bash
openssl x509 -pubkey -noout -in ec256_cert.pem > ec-secp256k1-pub-key-extracted.pem
```
The result is the `ec-secp256k1-pub-key-extracted.pem` public key:
```text
-----BEGIN PUBLIC KEY-----
MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAE1MtHIxlGP5TARqBccrddNm1FnYH1Fp+o
nETz5KbXPSeG5FGwKMUXGfAmSZJq2gENULFewwymt+9bTXkjBZhh8A==
-----END PUBLIC KEY-----
```
You can check the curve specification of the extracted
`ec-secp256k1-pub-key-extracted.pem` key with the command:
```bash
openssl ec -pubin -in ec-secp256k1-pub-key-extracted.pem -text -noout -param_out
```
You'll see a result similar to this one:
```text
read EC key
Private-Key: (256 bit)
pub:
04:d4:cb:47:23:19:46:3f:94:c0:46:a0:5c:72:b7:
5d:36:6d:45:9d:81:f5:16:9f:a8:9c:44:f3:e4:a6:
d7:3d:27:86:e4:51:b0:28:c5:17:19:f0:26:49:92:
6a:da:01:0d:50:b1:5e:c3:0c:a6:b7:ef:5b:4d:79:
23:05:98:61:f0
ASN1 OID: secp256k1
```
### Removing a passphrase from a private key
You can remove a passphrase if you added it when creating an RSA private key In
this example, the private key file is named: `jwtRSA256-private.pem`.
The command:
```bash
openssl rsa -in jwtRSA256-private.pem -out noPassphraseJwtRS256.pem
```
The result is the `noPassphraseJwtRS256.pem` public private key without a passphrase.