|
| 1 | +#!/usr/bin/env python3 |
| 2 | + |
| 3 | +# Thanks to @rohe Roland Hedberg for most of the lines in this script :). |
| 4 | + |
| 5 | +import argparse |
| 6 | +import json |
| 7 | +import os |
| 8 | +import sys |
| 9 | + |
| 10 | +from pygments import highlight |
| 11 | +from pygments.formatters.terminal import TerminalFormatter |
| 12 | +from pygments.lexers.data import JsonLexer |
| 13 | + |
| 14 | +from cryptojwt.jwe import jwe |
| 15 | +from cryptojwt.jwk.hmac import SYMKey |
| 16 | +from cryptojwt.jwk.jwk import key_from_jwk_dict |
| 17 | +from cryptojwt.jwk.rsa import RSAKey |
| 18 | +from cryptojwt.jwk.rsa import import_rsa_key |
| 19 | +from cryptojwt.jws import jws |
| 20 | +from cryptojwt.key_bundle import KeyBundle |
| 21 | +from cryptojwt.key_jar import KeyJar |
| 22 | + |
| 23 | +__author__ = 'roland' |
| 24 | + |
| 25 | +""" |
| 26 | +Tool to view, verify signature on and/or decrypt JSON Web Token. |
| 27 | +
|
| 28 | +Usage examples: |
| 29 | +
|
| 30 | +(1) read JWT from stdin, no keys |
| 31 | +
|
| 32 | +cat idtoken | ./jwtpeek.py -f - |
| 33 | +
|
| 34 | +or |
| 35 | +
|
| 36 | +cat idtoken | ./jwtpeek.py |
| 37 | +
|
| 38 | +(2) read JWT from file, use keys from file with a JWKS to verify/decrypt |
| 39 | +
|
| 40 | +./jwtpeek.py -f idtoken -J keys.jwks |
| 41 | +
|
| 42 | +or |
| 43 | +
|
| 44 | +(3) JWT from stdin, no keys |
| 45 | +
|
| 46 | +echo json.web.token | ./jwtpeek.py |
| 47 | + |
| 48 | +""" |
| 49 | + |
| 50 | + |
| 51 | +def main(jwt, keys, quiet): |
| 52 | + _jw = jwe.factory(jwt) |
| 53 | + if _jw: |
| 54 | + if not quiet: |
| 55 | + print("Encrypted JSON Web Token") |
| 56 | + print('Headers: {}'.format(_jw.jwt.headers)) |
| 57 | + if keys: |
| 58 | + res = _jw.decrypt(keys=keys) |
| 59 | + json_object = json.loads(res) |
| 60 | + json_str = json.dumps(json_object, indent=2) |
| 61 | + print(highlight(json_str, JsonLexer(), TerminalFormatter())) |
| 62 | + else: |
| 63 | + print("No keys can't decrypt") |
| 64 | + sys.exit(1) |
| 65 | + else: |
| 66 | + _jw = jws.factory(jwt) |
| 67 | + if _jw: |
| 68 | + if quiet: |
| 69 | + json_object = json.loads(_jw.jwt.part[1].decode("utf-8")) |
| 70 | + json_str = json.dumps(json_object, indent=2) |
| 71 | + print(highlight(json_str, JsonLexer(), TerminalFormatter())) |
| 72 | + else: |
| 73 | + print("Signed JSON Web Token") |
| 74 | + print('Headers: {}'.format(_jw.jwt.headers)) |
| 75 | + if keys: |
| 76 | + res = _jw.verify_compact(keys=keys) |
| 77 | + print('Verified message: {}'.format(res)) |
| 78 | + else: |
| 79 | + json_object = json.loads(_jw.jwt.part[1].decode("utf-8")) |
| 80 | + json_str = json.dumps(json_object, indent=2) |
| 81 | + print('Unverified message: {}'.format( |
| 82 | + highlight(json_str, JsonLexer(), TerminalFormatter()))) |
| 83 | + |
| 84 | + |
| 85 | +if __name__ == "__main__": |
| 86 | + parser = argparse.ArgumentParser() |
| 87 | + parser.add_argument('-r', dest="rsa_file", |
| 88 | + help="File containing a RSA key") |
| 89 | + parser.add_argument('-k', dest="hmac_key", |
| 90 | + help="If using a HMAC algorithm this is the key") |
| 91 | + parser.add_argument('-i', dest="kid", help="key id") |
| 92 | + parser.add_argument('-j', dest="jwk", help="JSON Web Key") |
| 93 | + parser.add_argument('-J', dest="jwks", help="JSON Web Keys") |
| 94 | + parser.add_argument('-u', dest="jwks_url", help="JSON Web Keys URL") |
| 95 | + parser.add_argument('-f', dest="msg", help="The message") |
| 96 | + parser.add_argument('-q', dest="quiet", |
| 97 | + help="Quiet mode -- only show the RAW but prettified JSON", |
| 98 | + action='store_true') |
| 99 | + |
| 100 | + args = parser.parse_args() |
| 101 | + |
| 102 | + if args.kid: |
| 103 | + _kid = args.kid |
| 104 | + else: |
| 105 | + _kid = '' |
| 106 | + |
| 107 | + keys = [] |
| 108 | + if args.rsa_file: |
| 109 | + keys.append(RSAKey(key=import_rsa_key(args.rsa_file), kid=_kid)) |
| 110 | + if args.hmac_key: |
| 111 | + keys.append(SYMKey(key=args.hmac_key, kid=_kid)) |
| 112 | + |
| 113 | + if args.jwk: |
| 114 | + _key = key_from_jwk_dict(open(args.jwk).read()) |
| 115 | + keys.append(_key) |
| 116 | + |
| 117 | + if args.jwks: |
| 118 | + _k = KeyJar() |
| 119 | + _k.import_jwks(open(args.jwks).read(), "") |
| 120 | + keys.extend(_k.issuer_keys("")) |
| 121 | + |
| 122 | + if args.jwks_url: |
| 123 | + _kb = KeyBundle(source=args.jwks_url) |
| 124 | + keys.extend(_kb.get()) |
| 125 | + |
| 126 | + if not args.msg: # If nothing specified assume stdin |
| 127 | + message = sys.stdin.read() |
| 128 | + elif args.msg == "-": |
| 129 | + message = sys.stdin.read() |
| 130 | + else: |
| 131 | + if os.path.isfile(args.msg): |
| 132 | + message = open(args.msg).read().strip("\n") |
| 133 | + else: |
| 134 | + message = args.msg |
| 135 | + |
| 136 | + message = message.strip() |
| 137 | + message = message.strip('"') |
| 138 | + main(message, keys, args.quiet) |
0 commit comments