def create_provider(self): kb = KeyBundle(JWKS["keys"]) kj = KeyJar() kj.issuer_keys[''] = [kb] _sdb = SessionDB("https://example.com/", token_factory=JWTToken('T', keyjar=kj, lt_pattern={ 'code': 3600, 'token': 900 }, iss='https://example.com/as', sign_alg='RS256'), refresh_token_factory=JWTToken( 'R', keyjar=kj, lt_pattern={'': 24 * 3600}, iss='https://example.com/as')) # name, sdb, cdb, authn_broker, authz, client_authn, self.provider = Provider("as", _sdb, CDB, AUTHN_BROKER, AUTHZ, verify_client, baseurl='https://example.com/as')
def test_verify_token_encrypted_no_key(): idt = IdToken( sub="553df2bcf909104751cfd8b2", aud=["5542958437706128204e0000", "554295ce3770612820620000"], auth_time=1441364872, azp="554295ce3770612820620000", ) kj = KeyJar() kb = KeyBundle() kb.do_local_der( os.path.join(os.path.dirname(__file__), "data", "keys", "cert.key"), "some", ["enc", "sig"], ) kj.add_kb("", kb) kj.add_kb("https://sso.qa.7pass.ctf.prosiebensat1.com", kb) packer = JWT( kj, lifetime=3600, iss="https://sso.qa.7pass.ctf.prosiebensat1.com", encrypt=True, ) _jws = packer.pack(**idt.to_dict()) msg = AuthorizationResponse(id_token=_jws) # Do not pass they keyjar with keys with pytest.raises(VerificationError): verify_id_token( msg, keyjar=KeyJar(), iss="https://sso.qa.7pass.ctf.prosiebensat1.com", client_id="554295ce3770612820620000", )
def test_verify_token_encrypted(): idt = IdToken( sub="553df2bcf909104751cfd8b2", aud=["5542958437706128204e0000", "554295ce3770612820620000"], auth_time=1441364872, azp="554295ce3770612820620000", ) kj = KeyJar() kb = KeyBundle() kb.do_local_der( os.path.join(os.path.dirname(__file__), "data", "keys", "cert.key"), "some", ["enc", "sig"], ) kj.add_kb("", kb) kj.add_kb("https://sso.qa.7pass.ctf.prosiebensat1.com", kb) packer = JWT( kj, lifetime=3600, iss="https://sso.qa.7pass.ctf.prosiebensat1.com", encrypt=True, ) _jws = packer.pack(**idt.to_dict()) msg = AuthorizationResponse(id_token=_jws) vidt = verify_id_token( msg, keyjar=kj, iss="https://sso.qa.7pass.ctf.prosiebensat1.com", client_id="554295ce3770612820620000", ) assert vidt assert vidt.jwe_header == {"enc": "A128CBC-HS256", "alg": "RSA1_5", "cty": "JWT"}
def create_sdb(self): kb = KeyBundle(JWKS["keys"]) kj = KeyJar() kj.issuer_keys[""] = [kb] self.sdb = SessionDB( "https://example.com/", db=DictSessionBackend(), code_factory=DefaultToken("supersecret", "verybadpassword", typ="A", lifetime=600), token_factory=JWTToken( "T", keyjar=kj, lt_pattern={ "code": 3600, "token": 900 }, iss="https://example.com/as", sign_alg="RS256", ), refresh_token_factory=JWTToken( "R", keyjar=kj, lt_pattern={"": 24 * 3600}, iss="https://example.com/as", token_storage={}, ), )
def load_keys(self, request, client_id, client_secret): try: self.keyjar.load_keys(request, client_id) try: logger.debug("keys for %s: [%s]" % (client_id, ",".join( ["%s" % x for x in self.keyjar[client_id]]))) except KeyError: pass except Exception as err: logger.error("Failed to load client keys: %s" % request.to_dict()) logger.error("%s", err) err = ClientRegistrationError( error="invalid_configuration_parameter", error_description="%s" % err) return Response(err.to_json(), content="application/json", status="400 Bad Request") # Add the client_secret as a symmetric key to the keyjar _kc = KeyBundle([{ "kty": "oct", "key": client_secret, "use": "ver" }, { "kty": "oct", "key": client_secret, "use": "sig" }]) try: self.keyjar[client_id].append(_kc) except KeyError: self.keyjar[client_id] = [_kc]
def test_dump_private_jwks(): keys = [ { "type": "RSA", "use": ["enc", "sig"] }, { "type": "EC", "crv": "P-256", "use": ["sig"] }, ] jwks, keyjar, kidd = build_keyjar(keys) kbl = keyjar.issuer_keys[''] dump_jwks(kbl, 'foo.jwks', private=True) kb_public = KeyBundle(source='file://./foo.jwks') # All RSA keys for k in kb_public.keys(): if k.kty == 'RSA': assert k.d assert k.p assert k.q else: # MUST be 'EC' assert k.d
def test_dump_public_jwks(): keys = [ { "type": "RSA", "use": ["enc", "sig"] }, { "type": "EC", "crv": "P-256", "use": ["sig"] }, ] jwks, keyjar, kidd = build_keyjar(keys) kbl = keyjar.issuer_keys[""] dump_jwks(kbl, "foo.jwks") kb_public = KeyBundle(source="file://./foo.jwks") # All RSA keys for k in kb_public.keys(): if k.kty == "RSA": assert not k.d assert not k.p assert not k.q else: # MUST be 'EC' assert not k.d
def test_pkce_token(): kb = KeyBundle(JWKS["keys"]) kj = KeyJar() kj.issuer_keys[''] = [kb] constructor = JWTToken('A', keyjar=kj, lt_pattern={'': 900}, iss='https://example.com/as', sign_alg='RS256', encrypt=True) sid = rndstr(32) session_info = { 'sub': 'subject_id', 'client_id': 'https://example.com/rp', 'response_type': ['code'], 'authzreq': '{}' } _cli = Client(config={'code_challenge': {'method': 'S512', 'length': 96}}) args, cv = _cli.add_code_challenge() access_grant = constructor( sid, sinfo=session_info, kid='sign1', code_challenge=args['code_challenge'], code_challenge_method=args['code_challenge_method']) _info = constructor.get_info(access_grant) assert _info['code_challenge_method'] == args['code_challenge_method'] assert _info['code_challenge'] == args['code_challenge']
def test_key_export(): kj = KeyJar() url = key_export( "http://example.com/keys/", "outbound", "secret", keyjar=kj, sig={ "alg": "rsa", "format": ["x509", "jwk"] }, ) assert url == "http://example.com/keys/outbound/jwks" # Now a jwks should reside in './keys/outbound/jwks' kb = KeyBundle(source="file://./keys/outbound/jwks") # One key assert len(kb) == 1 # more specifically one RSA key assert len(kb.get("RSA")) == 1 k = kb.get("RSA")[0] # For signing assert k.use == "sig"
def export(self, client, cconf, role): # has to be there self.trace.info("EXPORT") if client.keyjar is None: client.keyjar = KeyJar() kbl = [] for typ, info in cconf["keys"].items(): kb = KeyBundle(source="file://%s" % info["key"], fileformat="der", keytype=typ) for k in kb.keys(): k.serialize() client.keyjar.add_kb("", kb) kbl.append(kb) try: new_name = "static/%s_jwks.json" % role dump_jwks(kbl, new_name) client.jwks_uri = "%s%s" % (cconf["_base_url"], new_name) except KeyError: pass if not self.args.external_server and not self.keysrv_running: self._pop = start_key_server(cconf["_base_url"]) self.environ["keyprovider"] = self._pop self.trace.info("Started key provider") time.sleep(1) self.keysrv_running = True
def test_issuer_mismatch(self): ISSUER = "https://login.microsoftonline.com/b4ea3de6-839e-4ad1-ae78-c78e5c0cdc06/v2.0/" kb = KeyBundle(JWK2["keys"]) kj = KeyJar() kj.issuer_keys[ISSUER] = [kb] kj.issuer_keys[""] = [] authz_resp = AuthorizationResponse().from_urlencoded( "id_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Ik1uQ19WWmNBVGZNNXBPWWlKSE1iYTlnb0VLWSIsImtpZCI6Ik1u" "Q19WWmNBVGZNNXBPWWlKSE1iYTlnb0VLWSJ9.eyJhdWQiOiIwMTZlZDBlNC1mYzUyLTRlYjgtOWVhYy1lODg1MmM4MjEwNTUiLCJpc3Mi" "OiJodHRwczovL2xvZ2luLm1pY3Jvc29mdG9ubGluZS5jb20vYjRlYTNkZTYtODM5ZS00YWQxLWFlNzgtYzc4ZTVjMGNkYzA2L3YyLjAvI" "iwiaWF0IjoxNDM5OTIzNDY5LCJuYmYiOjE0Mzk5MjM0NjksImV4cCI6MTQzOTkyNzM2OSwidmVyIjoiMi4wIiwidGlkIjoiYjRlYTNkZT" "YtODM5ZS00YWQxLWFlNzgtYzc4ZTVjMGNkYzA2Iiwib2lkIjoiNDJjMzliNWUtYmQwNS00YTlhLTlhNWUtMTY5ZDc2N2ZlZjJmIiwicHJ" "lZmVycmVkX3VzZXJuYW1lIjoiaW50ZXJvcEBrYXV0aS5vbm1pY3Jvc29mdC5jb20iLCJzdWIiOiJFWGlaVldjakpsREN1LXZzOUxNb1V3" "ZGRNNEJZZ2ZISzBJNk40dWpXZkRFIiwibmFtZSI6ImludGVyb3AiLCJub25jZSI6IlpkSHRxQWwzR3c4QiJ9.tH4FKM4H9YCHX2XF4V64" "SsLaKh31c0oLpEVlFxFHw8jxL5HujUthZJDUMwngXZ2mPU_1G152ybKiRCV9DKaBh1rFSlZxTDBp0SV_YTwOkGYOt-sOzFUJyvVCjGmRh" "vFkOF1kiT3IYjDoRh72U8pMchj1duWSytLczdOc4LJmg24ya5jwqApuyQu7gVqoDH1kEqBAuhBj3a7ZDwxIt-bTKZklsht0RutZjv4Ckg" "8qJpzWnY7rIjSKFKfEpAAfk_LqWvTktvDMKTHXLxEPVZymoskE1LthtC8AYoNmtVPxgxf87yGCqYZBsuAnVChdnsItXP7tPeqUjC8Lm3J" "jabV-5g&id_token_expires_in=3599&state=6o3FmQ0QZl1zifsE&session_state=d2c97e8a-497c-4ce1-bb10-5058501164eb" ) try: authz_resp.verify(keyjar=kj, skew=100000000) except MissingSigningKey: authz_resp.verify(keyjar=kj, sender=ISSUER, skew=100000000)
def init_keyjar(): # Keys that are kept by the AS kb = KeyBundle() kb.do_keys(JWKS["keys"]) keyjar = KeyJar() keyjar.add_kb("", kb) return keyjar
def test_construct(self, client): _key = rsa_load(os.path.join(BASE_PATH, "data/keys/rsa.key")) kc_rsa = KeyBundle([ { "key": _key, "kty": "RSA", "use": "ver" }, { "key": _key, "kty": "RSA", "use": "sig" }, ]) client.keyjar[""] = kc_rsa client.token_endpoint = "https://example.com/token" client.provider_info = { "issuer": "https://example.com/", "token_endpoint": "https://example.com/token", } cis = AccessTokenRequest() pkj = PrivateKeyJWT(client) http_args = pkj.construct(cis, algorithm="RS256", authn_endpoint="token") assert http_args == {} cas = cis["client_assertion"] _jwt = JWT().unpack(cas) jso = _jwt.payload() assert _eq(jso.keys(), ["aud", "iss", "sub", "jti", "exp", "iat"]) assert _jwt.headers == {"alg": "RS256"} assert jso["aud"] == [client.provider_info["token_endpoint"]]
def load_keys(self, request, client_id, client_secret): try: self.keyjar.load_keys(request, client_id) try: n_keys = len(self.keyjar[client_id]) msg = "Found {} keys for client_id={}" logger.debug(msg.format(n_keys, client_id)) except KeyError: pass except Exception as err: msg = "Failed to load client keys: {}" logger.error(msg.format(sanitize(request.to_dict()))) logger.error("%s", err) err = ClientRegistrationError( error="invalid_configuration_parameter", error_description="%s" % err) return Response(err.to_json(), content="application/json", status="400 Bad Request") # Add the client_secret as a symmetric key to the keyjar _kc = KeyBundle([{ "kty": "oct", "key": client_secret, "use": "ver" }, { "kty": "oct", "key": client_secret, "use": "sig" }]) try: self.keyjar[client_id].append(_kc) except KeyError: self.keyjar[client_id] = [_kc]
def test_remove_key(self): ks = KeyJar() ks[""] = KeyBundle([{ "kty": "oct", "key": "a1b2c3d4", "use": "sig" }, { "kty": "oct", "key": "a1b2c3d4", "use": "ver" }]) ks["http://www.example.org"] = [ KeyBundle([{ "kty": "oct", "key": "e5f6g7h8", "use": "sig" }, { "kty": "oct", "key": "e5f6g7h8", "use": "ver" }]), keybundle_from_local_file(RSAKEY, "rsa", ["enc", "dec"]) ] ks["http://www.example.com"] = keybundle_from_local_file( RSA0, "rsa", ["enc", "dec"]) coll = ks["http://www.example.org"] # coll is list of KeyBundles assert len(coll) == 2 keys = ks.get_encrypt_key(key_type="RSA", owner="http://www.example.org") assert len(keys) == 1 _key = keys[0] ks.remove_key("http://www.example.org", "RSA", _key) coll = ks["http://www.example.org"] assert len(coll) == 1 # Only one remaining key keys = ks.get_encrypt_key(key_type="rsa", owner="http://www.example.org") assert len(keys) == 0 keys = ks.verify_keys("http://www.example.com") assert len(keys) == 2 assert len([k for k in keys if k.kty == "oct"]) == 2 keys = ks.decrypt_keys("http://www.example.org") assert keys == []
def test_get_inactive_ver(self): ks = KeyJar() ks['http://example.com'] = KeyBundle( [{"kty": "oct", "key": "a1b2c3d4", "use": "sig"}, {"kty": "oct", "key": "a1b2c3d4", "use": "ver"}]) ks['http://example.com'][0]._keys[1].inactive_since = 1 key = ks.get_verify_key(owner='http://example.com') assert len(key) == 2
def keybundle_from_local_file(filename, typ, usage, kid): if typ.upper() == "RSA": kb = KeyBundle() k = RSAKey(kid=kid) k.load(filename) k.use = usage[0] kb.append(k) for use in usage[1:]: _k = RSAKey(kid=kid + "1") _k.use = use _k.load_key(k.key) kb.append(_k) elif typ.lower() == "jwk": kb = KeyBundle(source=filename, fileformat="jwk", keyusage=usage) else: raise UnknownKeyType("Unsupported key type") return kb
def store_key(self, key): kb = KeyBundle() kb.do_keys([key]) # Store key with thumbprint as key key_thumbprint = b64e(kb.keys()[0].thumbprint("SHA-256")).decode("utf8") self.thumbprint2key[key_thumbprint] = key return key_thumbprint
def create_token(self): kb = KeyBundle(JWKS["keys"]) kj = KeyJar() kj.issuer_keys[''] = [kb] self.access_token = JWTToken('T', keyjar=kj, iss='https://example.com/as', sign_alg='RS256')
def __call__(self): kb = KeyBundle(source=self.conv.entity.provider_info["jwks_uri"]) kb.verify_ssl = False kb.update() try: self.conv.keybundle.append(kb) except AttributeError: self.conv.keybundle = [kb]
def test_get_inactive_sig_for_ver(self): """get_verify_key can return inactive `sig` key.""" ks = KeyJar() ks['http://example.com'] = KeyBundle( [{"kty": "oct", "key": "a1b2c3d4", "use": "sig"}]) ks['http://example.com'][0]._keys[0].inactive_since = 1 key = ks.get_verify_key(owner='http://example.com') assert len(key) == 1
def create_token(self): kb = KeyBundle(JWKS["keys"]) kj = KeyJar() kj.issuer_keys[""] = [kb] self.access_token = JWTToken("T", keyjar=kj, iss="https://example.com/as", sign_alg="RS256")
def test_parse_jwt_request(self): ar = AuthorizationRequest( response_type=["code"], client_id="foobar", redirect_uri="http://foobar.example.com/oaclient", state="cold", ) self.srv.keyjar["foobar"] = KeyBundle([ { "kty": "oct", "key": "A1B2C3D4".encode("utf-8"), "use": "ver" }, { "kty": "oct", "key": "A1B2C3D4".encode("utf-8"), "use": "sig" }, ]) self.srv.keyjar[""] = KeyBundle([ { "kty": "oct", "key": "A1B2C3D4".encode("utf-8"), "use": "ver" }, { "kty": "oct", "key": "A1B2C3D4".encode("utf-8"), "use": "sig" }, ]) keys = self.srv.keyjar.get_signing_key(owner="foobar") _jwt = ar.to_jwt(key=keys, algorithm="HS256") req = self.srv.parse_jwt_request(txt=_jwt) assert isinstance(req, AuthorizationRequest) assert req["response_type"] == ["code"] assert req["client_id"] == "foobar" assert req["redirect_uri"] == "http://foobar.example.com/oaclient" assert req["state"] == "cold"
def __call__(self, conv, **kwargs): pi = conv.client.provider_info kb = KeyBundle(source=pi["jwks_uri"]) kb.verify_ssl = False kb.update() try: conv.keybundle.append(kb) except AttributeError: conv.keybundle = [kb]
def test_get_inactive_sig(self): """get_signing_key cannot return inactive `sig` key.""" ks = KeyJar() ks["http://example.com"] = KeyBundle( [{"kty": "oct", "key": "a1b2c3d4", "use": "sig"}] ) ks["http://example.com"][0]._keys[0].inactive_since = 1 key = ks.get_signing_key(owner="http://example.com") assert len(key) == 0
def test_update(self): kc = KeyBundle([{"kty": "oct", "key": "supersecret", "use": "sig"}]) assert len(kc.get("oct")) == 1 assert len(kc.get("rsa")) == 0 assert kc.remote is False assert kc.source is None kc.update() # Nothing should happen assert len(kc.get("oct")) == 1 assert len(kc.get("rsa")) == 0 assert kc.remote is False assert kc.source is None
def create_token(self): kb = KeyBundle(JWKS["keys"]) kj = KeyJar() kj.issuer_keys[''] = [kb] self.access_token = JWTToken('T', keyjar=kj, lt_pattern={ 'code': 3600, 'token': 900 }, iss='https://example.com/as', encrypt=True)
def verify(self, **kwargs): if "jwks" in self: try: _keys = self["jwks"]["keys"] except KeyError: raise SyntaxError('"keys" parameter missing') else: # will raise an exception if syntax error KeyBundle(_keys) for param in ["jwks_uri", "client_uri"]: verify_url(self[param]) JasonWebToken.verify(self, **kwargs)
def test_reload(): """Emulate what happens if you fetch keys from a remote site and you get back the same JWKS as the last time.""" _jwks = JWK0 kb = KeyBundle() kb.imp_jwks = _jwks kb.do_keys(kb.imp_jwks["keys"]) assert len(kb) == 1 kb.do_keys(kb.imp_jwks["keys"]) assert len(kb) == 1
def test_keyjar_group_keys(self): ks = KeyJar() ks[""] = KeyBundle( [ {"kty": "oct", "key": "a1b2c3d4", "use": "sig"}, {"kty": "oct", "key": "a1b2c3d4", "use": "ver"}, ] ) ks["http://www.example.org"] = KeyBundle( [ {"kty": "oct", "key": "e5f6g7h8", "use": "sig"}, {"kty": "oct", "key": "e5f6g7h8", "use": "ver"}, ] ) ks["http://www.example.org"].append( keybundle_from_local_file(RSAKEY, "rsa", ["ver", "sig"]) ) verified_keys = ks.verify_keys("http://www.example.org") assert len(verified_keys) == 6 assert len([k for k in verified_keys if k.kty == "oct"]) == 4 assert len([k for k in verified_keys if k.kty == "RSA"]) == 2