Skip to content

Instantly share code, notes, and snippets.

@Drowze
Forked from shu-yusa/create_jwt.sh
Created March 4, 2024 19:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Drowze/2d7c0dee1517c6ceefd349e34eb4d42d to your computer and use it in GitHub Desktop.
Save Drowze/2d7c0dee1517c6ceefd349e34eb4d42d 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
pack() {
# Remove line breaks and spaces
echo "$1" | sed -e "s/[\r\n]\+//g" | sed -e "s/ //g"
}
generate_keys_and_token() {
local prefix=$1
local payload='
{
"iss": "https://example.com",
"sub": "'"${prefix}"'user-id-123",
"aud": "client-app-id-123",
"exp": 1735689600,
"iat": 1563980400
}'
local header='
{
"kid": "'"${prefix}"'12345",
"alg": "RS256"
}'
if [ ! -f "${prefix}private-key.pem" ]; then
# Private and Public keys
openssl genrsa 2048 > "${prefix}private-key.pem"
openssl rsa -in "${prefix}private-key.pem" -pubout -out "${prefix}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 "${prefix}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 "${prefix}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 --- ${prefix}"
cat jwt.txt
echo -e "\n--- JWK --- ${prefix}"
jq . jwks.json
}
generate_keys_and_token
generate_keys_and_token other-
# frozen_string_literal: true
encrypted_jwt = 'CnsKImtpZCI6IjEyMzQ1IiwKImFsZyI6IlJTMjU2Igp9Cg==.CnsKImlzcyI6Imh0dHBzOi8vZXhhbXBsZS5jb20iLAoic3ViIjoidXNlci1pZC0xMjMiLAoiYXVkIjoiY2xpZW50LWFwcC1pZC0xMjMiLAoiZXhwIjoxNzM1Njg5NjAwLAoiaWF0IjoxNTYzOTgwNDAwCn0K.MZkwUpUOYaCznsdtDxb/x/4LUrOwo/uSy8UofKyDJorZnd2JmI6YQVyvC+I88VoAOQzfsml5EUk9si/a/WcGirddXe2x82Qykk+dsIf3dR0Z1qRJMCiOxid3FOlFGhzXfnGrfPzlH3QaJk0sDqUUMqTfPyWGZIqA8tqmCaI3vEqPGuPSG3eMf2YHOFioscP/axBXpcDZMu0mtuTkmJ7+VIxCv53H0saZ5KNIitMHT63FF3KCRge261Vj2PR32vKm22SVhG0o18JNllVNtyqgcOfTiEG4dVOxPZ+58Uzk3PuFHsaN/zcig8Dt3Ei4hN6k8DoWqmw4wgyfCHDY5mOPlg=='
# should come from some endpoint
# e.g.: https://login.microsoftonline.com/common/discovery/v2.0/keys
jwk = {
use: 'sig',
kty: 'RSA',
n: 'u7XaXWpAa5Dc2zIlaHTac2fhve-piw7KedNTCg7HMNAYtENYDFP-78U9V7gN2boz7T1Oj5kwFEbwtrov8P88b0WILv24ABHFRuuitt8Vl8ajEvEiI7SS8TnwXd7Qs0PUNcDcdqwwjbrX_QmW-4KvkKxW4rCEDWRfGRwhEIbDMvGDxQKaaX4Zn5O7ewxBm5CPjbm1oZ97loMiwEwJrLlLr3k8FLBWf87jFl6gAUzXSel1x3rBpfnutIJejSgv3X27rHgWga4N9eeCj_U21O1WCVk_COIlf437rw07fOUJPRirRfJ3u36kS28K7Z9PGp0tQifePyyGo2bA42g11uzG1w',
e: 'AQAB',
kid: '12345',
alg: 'RS256'
}
other_encrypted_jwt = 'CnsKImtpZCI6Im90aGVyLTEyMzQ1IiwKImFsZyI6IlJTMjU2Igp9Cg==.CnsKImlzcyI6Imh0dHBzOi8vZXhhbXBsZS5jb20iLAoic3ViIjoib3RoZXItdXNlci1pZC0xMjMiLAoiYXVkIjoiY2xpZW50LWFwcC1pZC0xMjMiLAoiZXhwIjoxNzM1Njg5NjAwLAoiaWF0IjoxNTYzOTgwNDAwCn0K.jErEOnGnebL/99FDOSEz/lY4nZKanh+wdluvGJT/KwqsqIpW9PXUDsznEUfux0wQMIEqDE2mdzHBS5ab3z+LmprCJ99JhnYc3DWZdXyxJ1N7Pb2Z4WEjw7DbRff0kNWTyJsOYzjGusjC3BygQwlJbNKUAS8xlpVY0oZd20Lqe7OFlrcvIv3DQ9LNOVS7NC8sYaExn/ZE0bcNSYPrO+4VaoYuXJ5sqKZKUGEXVyWISpCseAmOcotQCj8fprY8xbjOJ4cM1BJAGodC80Yp/xktRDBJ75DMv7wNojF2ti6oeIZjUtG3vwjA6ex2p5gLwy0Wz99FDPNNB5dhROqrqZdHaw=='
other_jwk = {
use: 'sig',
kty: 'RSA',
n: 'lcjY4WrHBF4LwNWL3wNDdZjUuVw57MhyooB_Kmk4uHUsDcOY2x0BOY91mpxivvJDQvY_CK9Gugz2Tgi-QrXQLrL2F9EdcOYntvUL4iotX7BiC3A1kplYhOwDa_BNlvEl7vuJvHjbD-GVkosuDfYCqzo4QCHpy8UAsxxH9TNsCcz6k99soA-maWltugPiqFyk-ocBKHDPfO-0-j1zJjljjDsQXqT8NekxxuXyyDZsQ-EX_lIt77Y4e7KdGxRVos79RV5FC8MagrNwhhablbkdMcEjHAbHFDLY_NCvwLz27u-tD5wSPOJEkWijQBlJIbIlytBdXzz1mtNytXFElabYCw',
e: 'AQAB',
kid: 'other-12345',
alg: 'RS256'
}
require 'jwt'
def decode(jwt, jwks)
jwk_set = jwks.select { |key| key[:use] == 'sig' } # Signing keys only!
algorithms = jwk_set.map { |key| key[:alg] }.compact.uniq
jwk_set = JWT::JWK::Set.new(jwk_set)
JWT.decode(jwt, nil, true, algorithms:, jwks: jwk_set)
end
# can decode, regardless of how many keys are available
p decode(encrypted_jwt, [jwk])
p decode(other_encrypted_jwt, [other_jwk])
p decode(encrypted_jwt, [jwk, other_jwk])
p decode(other_encrypted_jwt, [jwk, other_jwk])
# can't decode if desired key isn't available
p decode(encrypted_jwt, [other_jwk])
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment