create_jwt.sh
Reads a JSON payload and creates a JWT, with or without a signature
decode_jwt.sh
Reads a JWT from stdin and decodes as human-readable JSON to stdout
#!/bin/bash | |
# create_jwt.sh (c) NGINX, Inc. [v0.3 10-Jul-2019] Liam Crilly <liam.crilly@nginx.com> | |
# | |
# This script accepts a JSON claimset (JWT payload) and creates produces a | |
# JOSE-signed token to stdout using one of the supported signature algorithms. | |
# | |
# CHANGELOG | |
# v0.1 Initial version (hs256 only) | |
# v0.2 Added support for various hs signature lengths | |
# v0.3 Checks for expired exp claims, option to specify exp duration | |
if [ $# -eq 0 ]; then | |
echo "USAGE: ${0##*/} [-exp <mins>] <alg [secret]> <payload>" | |
echo " -exp will set or override the exp claim with the specified duration" | |
echo " alg may be 'none' or 'hs*'" | |
echo " secret will be used for HMAC (hs*) signature" | |
echo " payload of '-' will read from stdin" | |
exit 1 | |
fi | |
# Check that jq is installed so we can check/modify the exp claim | |
# | |
hash jq 2> /dev/null | |
if [ $? -ne 0 ]; then | |
echo "${0##*/}: ERROR: please install 'jq' for 'exp' payload manipulation" | |
exit | |
fi | |
# Command line processing | |
# | |
VALIDITY_MINS=0 | |
if [ "$1" == "-exp" ]; then | |
VALIDITY_MINS=$2 | |
shift; shift | |
fi | |
case $1 in | |
"none") | |
HEADER='{"alg":"none","typ":"JWT"}' | |
PAYLOAD=$2 | |
shift | |
;; | |
"hs256"|"hs384"|"hs512") | |
LENGTH=`echo $1 | awk -F"hs" '{print $2}'` | |
HEADER="{\"alg\":\"HS$LENGTH\",\"typ\":\"JWT\",\"kid\":\"0001\"}" | |
SECRET=$2 | |
shift; shift | |
;; | |
*) | |
echo "${0##*/}: ERROR: unrecognised or unsupported algorithm, $1" | |
exit 1 | |
;; | |
esac | |
if [ "$1" == "-" ]; then | |
PAYLOAD=$(cat) | |
else | |
PAYLOAD=$1 | |
fi | |
# Check payload is valid JSON | |
# | |
if [ `echo $PAYLOAD | jq -r . 2>&1 | grep -c ^parse` -gt 0 ]; then | |
echo "${0##*/}: ERROR: JWT payload is invalid (not JSON)" | |
echo $PAYLOAD | jq . | |
exit 1 | |
fi | |
if [ $VALIDITY_MINS -gt 0 ]; then | |
# Set/update the exp claim | |
EXP_CLAIM=`date -v +${VALIDITY_MINS}M +%s` | |
PAYLOAD=`echo $PAYLOAD | jq ".exp = $EXP_CLAIM"` | |
else | |
# Check/warn if exp is in the past | |
EXP_CLAIM=`echo $PAYLOAD | jq -r .exp` | |
if [ "$EXP_CLAIM" != "null" ] && [ $EXP_CLAIM -lt `date +%s` ]; then | |
echo "${0##*/}: WARNING: exp claim is in the past, `date -j -f %s $EXP_CLAIM`" 1>&2 | |
fi | |
fi | |
JWT=`echo -n $HEADER | base64 | tr '+\/' '-_' | tr -d '='`.`echo -n $PAYLOAD | base64 | tr '+\/' '-_' | tr -d '='` | |
if [ "$LENGTH" != "" ]; then | |
SIG=`echo -n $JWT | openssl dgst -binary -sha$LENGTH -hmac $SECRET | base64 | tr '+\/' '-_' | tr -d '='` | |
fi | |
echo $JWT.$SIG |
#!/usr/bin/env bash | |
# | |
# decode_jwt.sh (c) NGINX, Inc. [v0.5 19-Nov-2018] Liam Crilly <liam.crilly@nginx.com> | |
# | |
# This script accepts a JWT on stdin and decodes the header and payload into | |
# human-readable JSON texts. It does not perform signature validation. | |
# | |
# CHANGELOG | |
# v0.4 [28-Jul-2016] Stable | |
# v0.5 [19-Nov-2018] Support for multiple JSON-prettifiers, reports on decode failure | |
if [ $# -gt 0 ]; then | |
echo "${0##*/}: reads a JWT from stdin and decodes as human-readable JSON to stdout" | |
exit 1 | |
fi | |
for json_cli in "jq -C ." "python -m json.tool"; do | |
hash ${json_cli%% *} 2> /dev/null # Remove chars beyond space | |
if [ $? -eq 0 ]; then | |
DO_JSON=$json_cli | |
break #for | |
fi | |
done | |
if [ "$DO_JSON" == "" ]; then | |
echo "${0##*/}: WARNING: please install 'jq' or 'python' for prettified JSON output" | |
DO_JSON=cat | |
fi | |
jwt=$(cat) | |
for section in `echo -n $jwt | cut -f1-2 -d . | tr '.' ' '`; do | |
padding="=" | |
while [ `echo -n "$padding" | wc -c` -lt 4 ]; do | |
decoded=`echo -n "${section}$padding" | base64 -D` | |
if [ $? -eq 0 ]; then | |
# Decoded OK, see if we can prettify the JSON (might need a trailing '}') | |
echo $decoded | $DO_JSON 2>&1 | grep -ve ^Ex -e ^parse | |
if [ $? -eq 1 ]; then | |
echo $decoded} | $DO_JSON 2>&1 | grep -ve ^Ex -e ^parse | |
fi | |
# Failed to parse JSON, show what's wrong | |
if [ $? -eq 1 ]; then | |
echo "${0##*/}: ERROR parsing JSON" | |
echo $decoded | |
echo $decoded} | $DO_JSON | |
fi | |
break #while | |
fi | |
padding="${padding}=" | |
done | |
done |