Verifying JWT (RS256) using OpenSSL

4.2k views Asked by At

My requirement is verifying a JWT using public key (RS256). The check should be based on native OpenSSL only.

I use JWT.IO initial content for testing. This token was generated:

eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.POstGetfAytaZS82wHcjoTyoqhMyxXiWdR7Nn7A29DNSl0EiXLdwJ6xC6AfgZWF1bOsS_TuYI3OG85AmiExREkrS6tDfTQ2B3WXlrr-wp5AokiRbz3_oB4OxG-W9KcEEbDRcZc0nH3L7LzYptiy1PtAylQGxHTWZXtGz4ht0bAecBgmpdgXMguEIcoqPJ1n3pIWk_dUZegpqx0Lka21H6XxUTxiy8OcaarA8zdnPUnV6AmNP3ecFawIFYdvJB_cm-GvpCSbr8G8y_Mllj8f4x9nBH8pQux89_6gUY618iYv7tuPWBFfEbLxtF2pZS6YC1aSfLQxeNe8djT9YjpvRZA

When I enter Header + Payload + Signature (items 1 and 2 in the picture below) with points between them. Next, I enter a Public key (item 3 in the picture). As result I see message ‘Signature verified’ (item 4 in the picture).

Now I want to get same result using OpenSSL.

I have performed steps:

  1. Create text file /tmp/pub.pem and past below content into it (copied from JWT.IO test case):

-----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnzyis1ZjfNB0bBgKFMSv vkTtwlvBsaJq7S5wA+kzeVOVpVWwkWdVha4s38XM/pa/yr47av7+z3VTmvDRyAHc aT92whREFpLv9cj5lTeJSibyr/Mrm/YtjCZVWgaOYIhwrXwKLqPr/11inWsAkfIy tvHWTxZYEcXLgAXFuUuaS3uF9gEiNQwzGTU1v0FqkqTBr4B8nW3HCN47XUu0t8Y0 e+lf4s4OxQawWD79J9/5d3Ry0vbV3Am1FtGJiJvOwRsIfVChDpYStTcHTCMqtvWb V6L11BWkpzGXSW4Hv43qa+GSYOD2QU68Mb59oSk2OB+BtOLpJofmbGEGgvmwyCI9 MwIDAQAB -----END PUBLIC KEY-----

  1. Create text file data /tmp/data.txt and past below content into it. This is Header and Payload separated by point (item 1 from picture) :

eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0

  1. Create text file data /tmp/signature.txt and past below content into it (this is item 2 from picture):

POstGetfAytaZS82wHcjoTyoqhMyxXiWdR7Nn7A29DNSl0EiXLdwJ6xC6AfgZWF1bOsS_TuYI3OG85AmiExREkrS6tDfTQ2B3WXlrr-wp5AokiRbz3_oB4OxG-W9KcEEbDRcZc0nH3L7LzYptiy1PtAylQGxHTWZXtGz4ht0bAecBgmpdgXMguEIcoqPJ1n3pIWk_dUZegpqx0Lka21H6XxUTxiy8OcaarA8zdnPUnV6AmNP3ecFawIFYdvJB_cm-GvpCSbr8G8y_Mllj8f4x9nBH8pQux89_6gUY618iYv7tuPWBFfEbLxtF2pZS6YC1aSfLQxeNe8djT9YjpvRZA

  1. I run OpenSSL command:

    openssl dgst -verify /tmp/pub.pem -keyform PEM -sha256 -signature /tmp/signature.txt -binary /tmp/data.txt
    

    But get result:

    Verification Failure.

What exactly am I doing wrong ?

2

There are 2 answers

6
jps On BEST ANSWER

The signature of a JWT is base64url encoded and needs to be decoded first. The suggested duplicate only deals with a base64 encoded signature and openssl seems not to be working with base64url encoding.

If you're working on a Windows system, you can decode the signature file with certutil, which can directly decode bas64url:

certutil -decode signature.txt signature.sha256

Then use the signature.sha256 as input for openssl:

openssl.exe dgst -sha256 -verify pubkey.pem -signature signature.sha256 data.txt

Then you should get the result:

Verified OK

0
Mendhak On

I found this gist quite useful in going from a starting JWT to breaking it into parts, decoding certain bits, and then verifying the payload against the signature. It uses openssl and perl which should come with most Bash implementations.

I'll paste my 'short' version of the same thing. In my case I'm verifying a JWT from Auth0.

# The JWT itself
jwt=eyJ...

# Get Auth0's public certificate
curl https://your_auth0_domain/pem > auth0publiccert.pem

# Extract the public key from the public certificate
openssl x509 -pubkey -noout -in auth0publiccert.pem > auth0publickey.pem

# Extract the input payload
input=${jwt%.*}
echo -n $input > payload.txt

# Extract the signature portion
encSig=${jwt##*.}

# Decode the signature
echo -n $encSig | perl -ne 'tr|-_|+/|; print "$1\n" while length>76 and s/(.{0,76})//; $_ .= ("", "", "==", "=")[length($_) % 4]; print' | openssl enc -base64 -d > signature.dat

# Finally, verify
openssl dgst -sha256 -verify auth0publickey.pem -signature signature.dat payload.txt
#Output should be "Verified OK"