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_verify_id_token_missing_c_hash(): code = "AccessCode1" idt = IdToken( **{ "sub": "553df2bcf909104751cfd8b2", "aud": ["5542958437706128204e0000", "554295ce3770612820620000"], "auth_time": 1441364872, "azp": "554295ce3770612820620000", } ) kj = KeyJar() kj.add_symmetric("", "dYMmrcQksKaPkhdgRNYk3zzh5l7ewdDJ", ["sig"]) kj.add_symmetric( "https://sso.qa.7pass.ctf.prosiebensat1.com", "dYMmrcQksKaPkhdgRNYk3zzh5l7ewdDJ", ["sig"], ) packer = JWT( kj, sign_alg="HS256", iss="https://sso.qa.7pass.ctf.prosiebensat1.com", lifetime=3600, ) _jws = packer.pack(**idt.to_dict()) msg = AuthorizationResponse(code=code, id_token=_jws) with pytest.raises(MissingRequiredAttribute): verify_id_token( msg, check_hash=True, keyjar=kj, iss="https://sso.qa.7pass.ctf.prosiebensat1.com", client_id="554295ce3770612820620000", )
def test_dump_issuer_keys(self): kb = keybundle_from_local_file("file://%s/jwk.json" % BASE_PATH, "jwk", ["ver", "sig"]) assert len(kb) == 1 kj = KeyJar() kj.issuer_keys[""] = [kb] res = kj.dump_issuer_keys("") assert len(res) == 1 assert res[0] == { 'use': 'sig', 'e': 'AQAB', 'kty': 'RSA', 'alg': 'RS256', 'n': 'pKybs0WaHU_y4cHxWbm8Wzj66HtcyFn7Fh3n-99qTXu5yNa30MRYIYfSDwe9JVc1JUoGw41yq2StdGBJ40HxichjE-Yopfu3B58Q' 'lgJvToUbWD4gmTDGgMGxQxtv1En2yedaynQ73sDpIK-12JJDY55pvf-PCiSQ9OjxZLiVGKlClDus44_uv2370b9IN2JiEOF-a7JB' 'qaTEYLPpXaoKWDSnJNonr79tL0T7iuJmO1l705oO3Y0TQ-INLY6jnKG_RpsvyvGNnwP9pMvcP1phKsWZ10ofuuhJGRp8IxQL9Rfz' 'T87OvF0RBSO1U73h09YP-corWDsnKIi6TbzRpN5YDw', 'kid': 'abc' }
def test_verify_id_token_mismatch_aud_azp(): idt = IdToken( **{ "sub": "553df2bcf909104751cfd8b2", "aud": ["5542958437706128204e0000", "554295ce3770612820620000"], "auth_time": 1441364872, "azp": "aaaaaaaaaaaaaaaaaaaa", } ) kj = KeyJar() kj.add_symmetric("", "dYMmrcQksKaPkhdgRNYk3zzh5l7ewdDJ", ["sig"]) kj.add_symmetric( "https://sso.qa.7pass.ctf.prosiebensat1.com", "dYMmrcQksKaPkhdgRNYk3zzh5l7ewdDJ", ["sig"], ) packer = JWT(kj, sign_alg="HS256", iss="https://example.com/as", lifetime=3600) _jws = packer.pack(**idt.to_dict()) msg = AuthorizationResponse(id_token=_jws) with pytest.raises(ValueError): verify_id_token( msg, keyjar=kj, iss="https://sso.qa.7pass.ctf.prosiebensat1.com", client_id="aaaaaaaaaaaaaaaaaaaa", )
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 verify(self, **kwargs): """ Verifies that an instance of this class adhers to the given restrictions. """ super(MetadataStatement, self).verify(**kwargs) if "signing_keys" in self: if 'signing_keys_uri' in self: raise VerificationError( 'You can only have one of "signing_keys" and ' '"signing_keys_uri" in a metadata statement') else: # signing_keys MUST be a JWKS kj = KeyJar() try: kj.import_jwks(self['signing_keys'], '') except Exception: raise VerificationError('"signing_keys" not a proper JWKS') if "metadata_statements" in self and "metadata_statement_uris" in self: s = set(self['metadata_statements'].keys()) t = set(self['metadata_statement_uris'].keys()) if s.intersection(t): raise VerificationError( 'You should not have the same key in "metadata_statements" ' 'and in "metadata_statement_uris"') return True
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_verify_id_token_at_hash(): token = "AccessTokenWhichCouldBeASignedJWT" lhsh = left_hash(token) idt = IdToken( **{ "sub": "553df2bcf909104751cfd8b2", "aud": ["5542958437706128204e0000", "554295ce3770612820620000"], "auth_time": 1441364872, "azp": "554295ce3770612820620000", "at_hash": lhsh, }) kj = KeyJar() kj.add_symmetric("", "dYMmrcQksKaPkhdgRNYk3zzh5l7ewdDJ", ["sig"]) kj.add_symmetric( "https://sso.qa.7pass.ctf.prosiebensat1.com", "dYMmrcQksKaPkhdgRNYk3zzh5l7ewdDJ", ["sig"], ) packer = JWT( kj, sign_alg="HS256", iss="https://sso.qa.7pass.ctf.prosiebensat1.com", lifetime=3600, ) _jws = packer.pack(**idt.to_dict()) msg = AuthorizationResponse(access_token=token, id_token=_jws) verify_id_token( msg, check_hash=True, keyjar=kj, iss="https://sso.qa.7pass.ctf.prosiebensat1.com", client_id="554295ce3770612820620000", )
def __init__(self, name, sdb, cdb, userinfo, client_authn, urlmap=None, ca_certs="", keyjar=None, hostname="", dist_claims_mode=None): Provider.__init__(self, name, sdb, cdb, None, userinfo, None, client_authn, "", urlmap, ca_certs, keyjar, hostname) if keyjar is None: keyjar = KeyJar(ca_certs) for cid, _dic in cdb.items(): try: keyjar.add_symmetric(cid, _dic["client_secret"], ["sig", "ver"]) except KeyError: pass self.srvmethod = OICCServer(keyjar=keyjar) self.dist_claims_mode = dist_claims_mode self.info_store = {} self.claims_userinfo_endpoint = ""
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 __setitem__(self, key, value): """ :param key: issuer ID :type: String :param value: Cryptographic keys that should be connected to to an issuer ID. :type value: KeyJar or a JWKS (JSON document) """ if not isinstance(value, KeyJar): kj = KeyJar() kj.import_jwks(value, issuer=key) value = kj else: _val = value.copy() _iss = list(_val.keys()) if _iss == ['']: _val.issuer_keys[key] = _val.issuer_keys[''] del _val.issuer_keys[''] elif len(_iss) == 1: if _iss[0] != key: _val.issuer_keys[key] = _val.issuer_keys[_iss[0]] del _val.issuer_keys[_iss[0]] else: raise ValueError('KeyJar contains to many issuers') value = _val self.bundle[key] = value
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 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 test_provider(self): provider_info = {"jwks_uri": "https://connect-op.herokuapp.com/jwks.json"} ks = KeyJar() ks.load_keys(provider_info, "https://connect-op.heroku.com") with responses.RequestsMock() as rsps: rsps.add( responses.GET, "https://connect-op.herokuapp.com/jwks.json", json={ "keys": [ { "kty": "RSA", "e": "AQAB", "n": "pKybs0WaHU_y4cHxWbm8Wzj66HtcyFn7Fh3n-99qTXu5yNa30MRYIYfSDwe9JVc1JUoGw41yq2StdGBJ" "40HxichjE-Yopfu3B58QlgJvToUbWD4gmTDGgMGxQxtv1En2yedaynQ73sDpIK-12JJDY55pvf-PCiSQ9OjxZ" "LiVGKlClDus44_uv2370b9IN2JiEOF-a7JBqaTEYLPpXaoKWDSnJNonr79tL0T7iuJmO1l705oO3Y0TQ-INLY" "6jnKG_RpsvyvGNnwP9pMvcP1phKsWZ10ofuuhJGRp8IxQL9RfzT87OvF0RBSO1U73h09YP-corWDsnKIi6Tbz" "RpN5YDw", "use": "sig", "kid": "default", } ] }, ) assert ks["https://connect-op.heroku.com"][0].keys()
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) == 1 assert len([k for k in keys if k.kty == "oct"]) == 1 keys = ks.decrypt_keys("http://www.example.org") assert keys == []
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
def create_provider(self): kb = KeyBundle(JWKS["keys"]) kj = KeyJar() kj.issuer_keys[''] = [kb] _sdb = SessionDB("https://example.com/", db={}, 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')) # 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 __init__(self, client_id=None, client_secret=None, ca_certs=None, client_authn_method=None, keyjar=None, verify_ssl=True, config=None): """ :param client_id: The client identifier :param ca_certs: Certificates used to verify HTTPS certificates :param client_authn_method: Methods that this client can use to authenticate itself. It's a dictionary with method names as keys and method classes as values. :param verify_ssl: Whether the SSL certificate should be verified. :return: Client instance """ PBase.__init__(self, ca_certs, verify_ssl=verify_ssl) self.client_id = client_id self.client_authn_method = client_authn_method self.keyjar = keyjar or KeyJar(verify_ssl=verify_ssl, client_id=client_id, client_secret=client_secret) self.verify_ssl = verify_ssl # self.secret_type = "basic " # self.state = None self.nonce = None self.grant = {} # own endpoint self.redirect_uris = [None] # service endpoints self.authorization_endpoint = None self.token_endpoint = None self.token_revocation_endpoint = None self.request2endpoint = REQUEST2ENDPOINT self.response2error = RESPONSE2ERROR self.grant_class = Grant self.token_class = Token self.provider_info = {} self._c_secret = None self.kid = {"sig": {}, "enc": {}} self.authz_req = None # the OAuth issuer is the URL of the authorization server's # configuration information location self.config = config or {} try: self.issuer = self.config['issuer'] except KeyError: self.issuer = '' self.allow = {} self.provider_info = {}
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 test_kid_usage(): kb = keybundle_from_local_file("file://jwk.json", "jwk", ["ver", "sig"]) kj = KeyJar() kj.issuer_keys["https://example.com"] = [kb] _key = kj.get_key_by_kid("abc", "https://example.com") assert _key assert _key.kid == "abc"
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_provider(self): provider_info = { "jwks_uri": "https://connect-op.herokuapp.com/jwks.json", } ks = KeyJar() ks.load_keys(provider_info, "https://connect-op.heroku.com") assert ks["https://connect-op.heroku.com"][0].keys()
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_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 get_signed_keys(self, uri, signing_keys): """ :param uri: Where the signed JWKS can be found :param signing_keys: Dictionary representation of a JWKS :return: list of KeyBundle instances or None """ r = self.server.http_request(uri, allow_redirects=True) if r.status_code == 200: _skj = KeyJar() _skj.import_jwks(signing_keys, '') _jws = factory(r.text) _jwks = _jws.verify_compact(r.text, Keys=_skj.get_signing_key()) _kj = KeyJar() _kj.import_jwks(json.loads(_jwks), '') return _kj.issuer_keys[''] else: return None
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_local_jwk_file(): kb = keybundle_from_local_file("file://jwk.json", "jwk", ["ver", "sig"]) assert len(kb) == 1 kj = KeyJar() kj.issuer_keys[""] = [kb] keys = kj.get_signing_key() assert len(keys) == 1 key = keys[0] assert isinstance(key, RSAKey) assert key.kid == "abc"
def test_self_signed_jwks(): kj = KeyJar() kj.issuer_keys['abc'] = KEYJAR.issuer_keys[''] ssj = self_sign_jwks(kj, 'abc', kid='', lifetime=3600) assert ssj res = verify_self_signed_jwks(ssj) _kj = jwks_to_keyjar(res['jwks'], res['iss']) assert list(_kj.keys()) == ['abc'] assert len(_kj.get_signing_key('RSA', owner='abc')) == 2 assert len(_kj.get_signing_key('EC', owner='abc')) == 1
def set_client_secret(self, val): if not val: self._c_secret = "" else: self._c_secret = val # client uses it for signing # Server might also use it for signing which means the # client uses it for verifying server signatures if self.keyjar is None: self.keyjar = KeyJar() self.keyjar.add_symmetric("", str(val))