Reputation: 43
I'm in the process of trying to create a JWT token using my terminal/bash scripting. I currently have
header='{"typ": "JWT", "alg": "HS256"}'
payload= '{"iss": "'"dummy_key"'"}'
encoded_header=$(printf "$header" | openssl base64 -e -A | tr -d '=')
encoded_payload=$(printf "$payload" | openssl base64 -e -A | tr -d '=')
data=$encoded_header.$encoded_payload
signature=$(printf '%s' "$data" | openssl dgst -sha256 -hmac dummy_secret -binary | openssl base64 -e -A | tr -d '=')
in which data would print a valid header and payload, connected by a .
(These values are just stored in variables earlier in the script. Header & Payload are fine).
When I paste the output of the full JWT token (Which is just $data.$signature
) example output:
eyJ0eXAiOiAiSldUIiwgImFsZyI6ICJIUzI1NiJ9Cg.eyJleHAiOiAxNzI0MDczMzI1LCAiaXNzIjogImR1bW15X2tleSJ9Cg./LI0Qs6U1p8x1zRFCwUe2kHW224L9js3fVW3UjpxO70
into https://jwt.io/ I get the below error for an Invalid Signature
:
Warning: Looks like your JWT signature is not encoded correctly using base64url (https://tools.ietf.org/html/rcfc4648#section-5). Note that the padding ("=" must be omitted as per https://tools.ietf.org/html/rcfc4648#section-2
I have looked at some of the OpenSSL documentation and thought I was doing it correctly, however, this error is making me think otherwise. Does anyone know the proper bash scripting command in order to create a valid signature for this JWT header & payload?
Upvotes: 1
Views: 2036
Reputation: 22585
You got a warning:
Warning: Looks like your JWT signature is not encoded correctly using base64url (https://tools.ietf.org/html/rcfc4648#section-5). Note that the padding ("=" must be omitted as per https://tools.ietf.org/html/rcfc4648#section-2
that your token is not encoded correctly. A JWT needs to be Base64Url encoded, but you just created a token that is Base64 encoded and removed the padding. You also need to substitute +
with -
and /
with _
for proper Base64Url encoding.
signature=$(printf '%s' "$data" | openssl dgst -sha256 -hmac dummy_secret -binary | openssl base64 -e -A | sed 's/+/-/g; s:/:_:g; s/=\+$//')
The second very common problem is that many people forget to provide the secret or key that is needed to verify the signature on jwt.io. Without knowing the secret it's not possible to verify the signature. The secret needs to be pasted into the input field in the right column of the JWT debugger under "VERIFY SIGNATURE" (replace the place holder your-256-bit-secret). And make sure to do it in the right order to avoid falsely verified tokens.
Upvotes: 2
Reputation: 194
As per the RFC documentation, the signature also should be encoded using base64. Since the signature starts with a slash icon, it appears to be not a valid base64 encoded.
Below is a complete example of generating a JWT using an symmetric key. You can test the signature validation also. :) I included the iat time also.
# Test the script by changing the secret and running the script
# secret='your-secret'
#!/bin/bash
# Define the header
header='{"alg":"HS256","typ":"JWT"}'
# Get the current timestamp for the iat claim
current_timestamp=$(date +%s)
# Define the payload with the dynamic iat
payload="{\"sub\":\"1234567890\",\"name\":\"John Doe\",\"iat\":$current_timestamp}"
# Base64 URL encode the header and payload
base64UrlEncode() {
echo -n "$1" | openssl enc -base64 -A | sed 's/=*$//' | sed 's/\+/-/g' | sed 's/\//_/g'
}
encodedHeader=$(base64UrlEncode "$header")
encodedPayload=$(base64UrlEncode "$payload")
# Create the signature
secret='your-secret-key'
signature=$(echo -n "$encodedHeader.$encodedPayload" | openssl dgst -sha256 -hmac "$secret" -binary | openssl enc -base64 -A | sed 's/=*$//' | sed 's/\+/-/g' | sed 's/\//_/g')
# Concatenate the header, payload, and signature to form the JWT
jwt="$encodedHeader.$encodedPayload.$signature"
echo "JWT: $jwt"
# Validate the JWT-----------------------
# Test the script by changing the secret and running the script
# secret='your-secret'
# Split the JWT into its three parts
header=$(echo -n $jwt | cut -d "." -f1)
payload=$(echo -n $jwt | cut -d "." -f2)
signature=$(echo -n $jwt | cut -d "." -f3)
# Base64 URL decode the header and payload
base64UrlDecode() {
local padded=$(echo "$1" | sed 's/-/\+/g' | sed 's/_/\//g' | awk '{printf "%s", $0; for (i=0; i<(4-length($0)%4)%4; i++) printf "="}')
echo -n "$padded" | openssl enc -base64 -d -A
}
decodedHeader=$(base64UrlDecode "$header")
decodedPayload=$(base64UrlDecode "$payload")
# Verify the signature
verifySignature=$(echo -n "$header.$payload" | openssl dgst -sha256 -hmac "$secret" -binary | openssl enc -base64 -A | sed 's/=*$//' | sed 's/\+/-/g' | sed 's/\//_/g')
echo "Decoded header: $decodedHeader"
echo "Decoded payload: $decodedPayload"
echo "Signature: $signature"
echo "Verify signature: $verifySignature"
if [ "$verifySignature" == "$signature" ]; then
echo "Signature is valid"
else
echo "Signature is invalid"
fi
Upvotes: 1