As discussed previously, we will be using the RSA algorithm to generate the signature of our JWT, which requires the generation of private and public keys. Therefore, the first thing we must do is to generate the key pair. We can do this locally using the ssh-keygen command:
$ mkdir keys && ssh-keygen -t rsa -b 4096 -f ./keys/key
Here, we are using the -t flag to specify that we want to generate an RSA key pair, and the -b flag to specify a key with a bit size of 4,096. Lastly, we use the -f flag to specify where we want the key to be stored. This will generate a private key that looks like this (truncated for brevity):
-----BEGIN RSA PRIVATE KEY-----
MIIJKAIBAAKCAgEAsTwK1Tireh3TVaJ66yUEAtLPP5tNuqwZW/kA64t7hgIRVKee
1WjbKLcHIJcAcioHJnqME96M+YRaj/xvlIFSwIbY1CRPgRkqH7kHs6mnrOIvmiRT
...
...
/cH3z0iGJh6WPrrw/xhil4VQ7UUSrD/4GC64r1sFS9wZ6d+PHPtcmlbkbWVQb/it
2goH/g6WLIKABZNz2uWxmEnT7wOO+++tIPL8q4u1p9pabuO8tsgHX4Tl6O4=
-----END RSA PRIVATE KEY-----
It will also generate a public key that looks like this (truncated for brevity):
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAA....7j7CyQ== username@hostname
However, the jsonwebtoken package expects our RSA key to be PEM-encoded, and thus we must perform one more step to export the public key as an encoded PEM file:
$ ssh-keygen -f ./keys/key.pub -e -m pem > ./keys/key.pub.pem
This will produce a key similar to the following (truncated for brevity):
-----BEGIN RSA PUBLIC KEY-----
MIICCgKCAgEAsTwK1Tireh3TVaJ66yUEAtLPP5tNuqwZW/kA64t7hgIRVKee1Wjb
KLcHIJcAcioHJnqME96M+YRaj/xvlIFSwIbY1CRPgRkqH7kHs6mnrOIvmiRTPxSO
...
XjxHHzaebcsy1ccp3cUHP2/3WOAz35x1UdFvYwQ/Qjh9Ud1Yoe4+wskCAwEAAQ==
-----END RSA PUBLIC KEY-----
Now, we don't want to commit these key files into the history of our repository, for several reasons:
- Anyone with access to the repository will be able to get a copy of the keys (most importantly the private key), and be able to impersonate the real server. The private key should be known by as few parties as possible; not even the developers should need to know the production keys. The only people who need to know are the system administrators who manage the server.
- If our keys are hardcoded into the code, then if we want to change these keys, we'd have to update the code, make a commit to the repository, and redeploy the entire application.
So, what's a better alternative?
The most secure alternative is to use a Trusted Platform Module (TPM), which is a microcontroller (a computer chip) that is embedded into the motherboard of the server and allows you to securely store cryptographic keys. If you encrypt your development machine, the key it uses to encrypt and decrypt your machine is stored in the TPM. Similarly, you can use a Hardware Security Module (HSM), which is similar to a TPM, but instead of being embedded into the motherboard, is a removable external device.
However, using a TPM and HSM are not a viable option for most cloud servers. Therefore, the next best thing is to store the keys as environment variables. However, our keys span across multiple lines; how do we define multi-line environment variables?