Skip to content

Instantly share code, notes, and snippets.

@shu-yusa
Last active March 21, 2024 14:57
Show Gist options
  • Star 11 You must be signed in to star a gist
  • Fork 7 You must be signed in to fork a gist
  • Save shu-yusa/213901a5a0902de5ad3f62a61036f4ce to your computer and use it in GitHub Desktop.
Save shu-yusa/213901a5a0902de5ad3f62a61036f4ce to your computer and use it in GitHub Desktop.
Generate private and public keys, and create JWT and JWKs
#!/bin/sh
## Requires openssl, nodejs, jq
header='
{
"kid": "12345",
"alg": "RS256"
}'
payload='
{
"iss": "https://example.com",
"sub": "user-id-123",
"aud": "client-app-id-123",
"exp": 1735689600,
"iat": 1563980400
}'
function pack() {
# Remove line breaks and spaces
echo $1 | sed -e "s/[\r\n]\+//g" | sed -e "s/ //g"
}
if [ ! -f private-key.pem ]; then
# Private and Public keys
openssl genrsa 2048 > private-key.pem
openssl rsa -in private-key.pem -pubout -out public-key.pem
fi
# Base64 Encoding
b64_header=$(pack "$header" | openssl enc -e -A -base64)
b64_payload=$(pack "$payload" | openssl enc -e -A -base64)
signature=$(echo -n $b64_header.$b64_payload | openssl dgst -sha256 -sign private-key.pem | openssl enc -e -A -base64)
# Export JWT
echo $b64_header.$b64_payload.$signature > jwt.txt
# Create JWK from public key
if [ ! -d ./node_modules/pem-jwk ]; then
# A tool to convert PEM to JWK
npm install pem-jwk
fi
jwk=$(./node_modules/.bin/pem-jwk public-key.pem)
# Add additional fields
jwk=$(echo '{"use":"sig"}' $jwk $header | jq -cs add)
# Export JWK
echo '{"keys":['$jwk']}'| jq . > jwks.json
echo "--- JWT ---"
cat jwt.txt
echo -e "\n--- JWK ---"
jq . jwks.json
@SqueezedLight
Copy link

This script was a perfect starting point, but unfortunately it is not compliant with jwt.io. Adjustments i made were 1) using base64url encoding and 2) adding the -n option for echo in the pack function. Here is the full jwt.io compliant example:

#!/bin/sh
## Requires openssl, nodejs, jq
## FIXES for jwt.io compliance
# use base64Url encoding
# use echo -n in pack function

header='
{
  "kid": "12345",
  "alg": "RS256"
}'
payload='
{
  "iss": "https://example.com",
  "sub": "user-id-123",
  "aud": "client-app-id-123",
  "exp": 1735689600,
  "iat": 1563980400
}'

function pack() {
  # Remove line breaks and spaces
  echo $1 | sed -e "s/[\r\n]\+//g" | sed -e "s/ //g"
}

function base64url_encode {
  (if [ -z "$1" ]; then cat -; else echo -n "$1"; fi) |
    openssl base64 -e -A |
      sed s/\\+/-/g |
      sed s/\\//_/g |
      sed -E s/=+$//
}

# just for debugging
function base64url_decode {
  INPUT=$(if [ -z "$1" ]; then echo -n $(cat -); else echo -n "$1"; fi)
  MOD=$(($(echo -n "$INPUT" | wc -c) % 4))
  PADDING=$(if [ $MOD -eq 2 ]; then echo -n '=='; elif [ $MOD -eq 3 ]; then echo -n '=' ; fi)
  echo -n "$INPUT$PADDING" |
    sed s/-/+/g |
    sed s/_/\\//g |
    openssl base64 -d -A
}

if [ ! -f private-key.pem ]; then
  # Private and Public keys
  openssl genrsa 2048 > private-key.pem
  openssl rsa -in private-key.pem -pubout -out public-key.pem
fi

# Base64 Encoding
b64_header=$(pack "$header" | base64url_encode)
b64_payload=$(pack "$payload" | base64url_encode)
signature=$(echo -n $b64_header.$b64_payload | openssl dgst -sha256 -sign private-key.pem | base64url_encode)
# Export JWT
echo $b64_header.$b64_payload.$signature > jwt.txt
# Create JWK from public key
if [ ! -d ./node_modules/pem-jwk ]; then
  # A tool to convert PEM to JWK
  npm install pem-jwk
fi
jwk=$(./node_modules/.bin/pem-jwk public-key.pem)
# Add additional fields
jwk=$(echo '{"use":"sig"}' $jwk $header | jq -cs add)
# Export JWK
echo '{"keys":['$jwk']}'| jq . > jwks.json

echo "--- JWT ---"
cat jwt.txt
echo -e "\n--- JWK ---"
jq . jwks.json

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment