def __init__(self, json): super().__init__(json) decoded_n = bytes_to_int(urlsafe_b64decode(self.n)) decoded_e = bytes_to_int(urlsafe_b64decode(self.e)) rsa_public_key = RSAPublicNumbers(decoded_e, decoded_n).public_key( default_backend()) self.public_key = rsa_public_key.public_bytes( Encoding.PEM, PublicFormat.SubjectPublicKeyInfo).decode()
def __init__(self, json): self._json = json self.expires = datetime.now() + timedelta(minutes=JWK_EXPIRES_MIN) decoded_n = bytes_to_int(urlsafe_b64decode(self.n)) decoded_e = bytes_to_int(urlsafe_b64decode(self.e)) rsa_public_key = RSAPublicNumbers(decoded_e, decoded_n).public_key( default_backend()) self.public_key = rsa_public_key.public_bytes( Encoding.PEM, PublicFormat.SubjectPublicKeyInfo).decode()
def get_rsa_public_key(n, e): """ Retrieve an RSA public key based on a module and exponent as provided by the JWKS format. :param n: :param e: :return: a RSA Public Key in PEM format """ n = int(binascii.hexlify(jwt.utils.base64url_decode(bytes(n))), 16) e = int(binascii.hexlify(jwt.utils.base64url_decode(bytes(e))), 16) pub = RSAPublicNumbers(e, n).public_key(default_backend()) return pub.public_bytes( encoding=serialization.Encoding.PEM, format=serialization.PublicFormat.SubjectPublicKeyInfo)
def get_rsa_public_key(n, e): """ Retrieve an RSA public key based on a module and exponent as provided by the JWKS format. :param n: :param e: :return: a RSA Public Key in PEM format """ n = int(binascii.hexlify(jwt.utils.base64url_decode(bytes(n))), 16) e = int(binascii.hexlify(jwt.utils.base64url_decode(bytes(e))), 16) pub = RSAPublicNumbers(e, n).public_key(default_backend()) return pub.public_bytes( encoding=serialization.Encoding.PEM, format=serialization.PublicFormat.SubjectPublicKeyInfo )
def jwt_token_verify(id_token, client_id, issuer, autoconfig=None, jwk=None): """ Verify a JWT token using public key. If jwk is not provided the issuer must support auto-discovery. This will also slow down the login process since multiple remote calls are required to fetch jwk. :param id_token: The openid id_token returned by the authorisation call :param client_id: The client_id, required for JWT verification :param issuer: The issuer, required for JWT verification and for auto-configuration if necessary :param autoconfig: Dictionary of auto-configuration properties, if empty will be fetched if required :param jwk: The JSON web key, if empty will be fetched using autoconfig :return dict: The decoded verified token :raises Exception: If verification failed """ # https://pyjwt.readthedocs.io/en/latest/usage.html # https://openid.net/specs/openid-connect-core-1_0.html#IDToken if not jwk: header = jwt.get_unverified_header(id_token) if not autoconfig: autoconfig = openid_connect_discover(issuer) jwks = _cache_get(autoconfig['jwks_uri']) for jwk in jwks['keys']: if jwk['kid'] == header['kid']: break if not jwk: raise Exception('Failed to get public key for {}'.format(issuer)) e = int(codecs.encode(base64url_decode(jwk['e']), 'hex'), 16) n = int(codecs.encode(base64url_decode(jwk['n']), 'hex'), 16) public_key = RSAPublicNumbers(e, n).public_key(backend=default_backend()) pem = public_key.public_bytes( encoding=serialization.Encoding.PEM, format=serialization.PublicFormat.SubjectPublicKeyInfo) d = jwt.decode(id_token, key=pem, algorithms=jwk['alg'], audience=client_id, issuer=issuer) return d
def main(): parser = ArgumentParser( description='Swiss Army Knife for keys and certificates') parser.add_argument('--import', dest='do_import', action='store_true') parser.add_argument('--export', dest='do_export', action='store_true') parser.add_argument('--search', dest='do_search', action='store_true') parser.add_argument('--list', dest='do_list', action='store_true') parser.add_argument('--format', dest='format') parser.add_argument('--comment', dest='comment') args = parser.parse_args() db = sqlite3.connect(path.expanduser('~/.config/ktool.sqlite3')) db.execute( 'CREATE TABLE IF NOT EXISTS rsa_keys (e, n, comment, PRIMARY KEY (e, n))' ) if args.format: args.format = args.format.lower() if args.format == 'rb64': e = '\x01\x00\x01' # XXX n = stdin.read().decode('base64') elif args.format == 'rhex': e = '\x01\x00\x01' # XXX n = re.sub('[^0-9a-f]+', '', stdin.read(), flags=re.I).decode('hex') elif args.format in ('der', 'pem'): from Crypto.PublicKey import RSA k = RSA.importKey(stdin.read()) n = number_to_str(k.n) e = number_to_str(k.e) else: n = '' n = n.lstrip('\0') if args.do_import: try: db.execute('INSERT INTO rsa_keys (e, n, comment) VALUES (?, ?, ?)', (sqlite3.Binary(e), sqlite3.Binary(n), args.comment)) db.commit() except sqlite3.IntegrityError: row = db.execute('SELECT comment FROM rsa_keys WHERE n = ?', (sqlite3.Binary(n), )).fetchone() if row: print('Key already in database with comment: ' + row[0], file=stderr) raise SystemExit(1) else: raise elif args.do_export: row = db.execute('SELECT e, n FROM rsa_keys WHERE comment = ?', (args.comment, )).fetchone() if row: from cryptography.hazmat.primitives.asymmetric.rsa import RSAPublicNumbers from cryptography.hazmat.primitives import serialization from cryptography.hazmat.backends import default_backend e, n = (str_to_number(p) for p in row) pk = RSAPublicNumbers(e, n).public_key(default_backend()) print( pk.public_bytes( encoding=serialization.Encoding.PEM, format=serialization.PublicFormat.SubjectPublicKeyInfo)) else: print('No key with matching comment found', file=stderr) raise SystemExit(1) elif args.do_search: row = db.execute('SELECT comment FROM rsa_keys WHERE n = ?', (sqlite3.Binary(n), )).fetchone() if row: print('Comment: ' + row[0]) else: print('No modulus matches in database', file=stderr) raise SystemExit(1) elif args.do_list: c = db.execute('SELECT e, n, comment FROM rsa_keys ORDER BY e, n') print('{0:>8} {1:32} {2:>5} {3}'.format( 'e', 'n (first 16 octets from MSB)', 'nbits', 'comment')) print('=' * 80) for e, n, comment in c: print('{0:>8} {1:32} {2:>5} {3}'.format(e[:4].encode('hex'), n[:16].encode('hex'), len(n) * 8, comment))