If you are going to brute-force JWT (which will be a huge undertaking, good luck with that) then just generate the signature yourself, directly, from the first two parts. Python dictionaries and JSON objects are unordered structures so either order is valid, the JWT specs do not specify an order, and any JWT implementation just takes the existing data for the first two parts to verify the signature. They would not re-generate the JSON.
The PyJWT library provides all the supported algorithms as separate objects in the jwt.algorithms
module; just call jwt.algorithms.get_default_algorithms()
to get a dictionary mapping name to Algorithm
instance.
Each such object has .sign(msg, key)
and .verify(msg, key, sig)
methods. Pass in the first two segments (base64-encoded, with .
, as a bytes
object) as the message, and you'll get the binary signature (not base64 encoded) back when using .sign()
, or when verifying with .verify()
, you pass in the binary signature decoded from the base64 data.
So, for a given token
as a bytes
object, you can get the algorithm and verify a key with:
import json
from jwt.utils import base64url_decode
from jwt.algorithms import get_default_algorithms
algorithms = get_default_algorithms()
msg, _, signature_part = token.rpartition(b'.')
header = json.loads(base64url_decode(msg.partition(b'.')[0]))
algo = algorithms[header['alg']]
signature = base64url_decode(signature_part)
# bytes key from other source; brute-force or otherwise
if algo.verify(msg, key, signature):
# key correct
Given your sample token
and key
set to b'secret'
, the above validates:
>>> import json
>>> from jwt.utils import base64url_decode
>>> from jwt.algorithms import get_default_algorithms
>>> token = b'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJzb21lIjoicGF5bG9hZCJ9.EgMnzcJYrElON09Bw_OwaqR_Z7Cq30n7cgTZGJqtK1YHfG1cGnGJoJGwOLj6AWg9taOyJN3Dnqd9NXeTCjTCwA'
>>> key = b'secret'
>>> algorithms = get_default_algorithms()
>>> msg, _, signature_part = token.rpartition(b'.')
>>> header = json.loads(base64url_decode(msg.partition(b'.')[0]))
>>> algo = algorithms[header['alg']]
>>> signature = base64url_decode(signature_part)
>>> algo.verify(msg, key, signature)
True
Brute-forcing by generating the key in a loop is then trivial to verify. Note that anything beyond a small key (using a limited alphabet) is very rapidly going to be unfeasible; a 16-byte fully random key value (128 bits) would take you a few decades to brute-force on modern hardware even with a system programming language, let alone the slower speed of a Python loop.