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 _create_op(self, issuer, endpoint_baseurl, jwks_uri): """ Create the necessary Provider instance. :type issuer: str :type endpoint_baseurl: str :type jwks_uri: str :param issuer: issuer URL for the OP :param endpoint_baseurl: baseurl to build endpoint URL from :param jwks_uri: URL to where the JWKS will be published """ kj = KeyJar() signing_key = KeyBundle(source="file://{}".format(self.conf["signing_key_path"]), fileformat="der", keyusage=["sig"]) kj.add_kb("", signing_key) capabilities = { "response_types_supported": ["id_token"], "id_token_signing_alg_values_supported": [self.sign_alg], "response_modes_supported": ["fragment", "query"], "subject_types_supported": ["public", "pairwise"], "grant_types_supported": ["implicit"], "claim_types_supported": ["normal"], "claims_parameter_supported": True, "request_parameter_supported": False, "request_uri_parameter_supported": False, } if "client_db_path" in self.conf: cdb = shelve_wrapper.open(self.conf["client_db_path"]) else: cdb = {} # client db in memory only self.provider = Provider(issuer, None, cdb, None, None, None, None, None, keyjar=kj, capabilities=capabilities, jwks_uri=jwks_uri) self.provider.baseurl = endpoint_baseurl self.provider.endp = [RegistrationEndpoint, AuthorizationEndpoint]
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 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 export_keys(keys): kbl = [] keyjar = KeyJar() for typ, info in keys.items(): kb = KeyBundle(source="file://%s" % info["key"], fileformat="der", keytype=typ) keyjar.add_kb("", kb) kbl.append(kb) try: new_name = "static/jwks.json" dump_jwks(kbl, new_name) except KeyError: pass return keyjar
def _create_op(self, issuer, endpoint_baseurl, jwks_uri): """ Create the necessary Provider instance. :type issuer: str :type endpoint_baseurl: str :type jwks_uri: str :param issuer: issuer URL for the OP :param endpoint_baseurl: baseurl to build endpoint URL from :param jwks_uri: URL to where the JWKS will be published """ kj = KeyJar() signing_key = KeyBundle(source="file://{}".format( self.conf["signing_key_path"]), fileformat="der", keyusage=["sig"]) kj.add_kb("", signing_key) capabilities = { "response_types_supported": ["id_token"], "id_token_signing_alg_values_supported": [self.sign_alg], "response_modes_supported": ["fragment", "query"], "subject_types_supported": ["public", "pairwise"], "grant_types_supported": ["implicit"], "claim_types_supported": ["normal"], "claims_parameter_supported": True, "request_parameter_supported": False, "request_uri_parameter_supported": False, } if "client_db_path" in self.conf: cdb = shelve_wrapper.open(self.conf["client_db_path"]) else: cdb = {} # client db in memory only self.provider = Provider(issuer, None, cdb, None, None, None, None, None, keyjar=kj, capabilities=capabilities, jwks_uri=jwks_uri) self.provider.baseurl = endpoint_baseurl self.provider.endp = [RegistrationEndpoint, AuthorizationEndpoint]
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'}
signed_reg_req = _jws.sign_compact(keys) rr['signed_metadata'] = signed_reg_req print(70 * "-") print('Client registration request') print(70 * "-") print_lines( json.dumps(rr.to_dict(), sort_keys=True, indent=2, separators=(',', ': '))) #### ====================================================================== ## On the OP #### ====================================================================== op_keyjar = KeyJar() op_keyjar.add_kb('https://fo.example.com/', KeyBundle(fo_jwks['keys'])) # ----------------------------------------------------------------------------- # Unpack software_statements # ----------------------------------------------------------------------------- msgs = [] # Only one software statement sost = rr['software_statements'][0] _sost = unpack_software_statement(sost, '', op_keyjar) fo_id = _sost['iss'] # ------------------------------ # get the long lived RP key (A)
class OIDCFederationEntity(object): def __init__(self, name, root_key, software_statements, federation_keys, signed_jwks_uri): # type: (str, Key, Sequence[str], Sequence[Key], str) -> None """ :param name: URI identifying the entity :param root_key: root signing key for this entity :param software_statements: all software statements isssued by federations for this entity :param federation_keys: public keys from all federations this entity is part of :param signed_jwks_uri: URL endpoint where the signed JWKS is published """ verify_signing_key(root_key) self.root_key = root_key self.name = name self.software_statements = [self._verify(ss, federation_keys) for ss in software_statements] self.federation_keys = federation_keys self.signed_jwks_uri = signed_jwks_uri self.intermediate_key = None self.jwks = None self.rotate_intermediate_key() self.rotate_jwks() @property def signed_intermediate_key(self): # type: () -> str """ :return: JWS containing the intermediate key """ return self._sign(self.intermediate_key.serialize(private=False), self.root_key) @property def signed_jwks(self): # type: () -> str """ :return: JWS containing the JWKS """ return self._sign(self.jwks.export_jwks(), self.intermediate_key) @property def software_statements_jws(self): # type: () -> List[str] """ :return: all the entity's software statements as JWS """ return [ss.jwt.pack() for ss in self.software_statements] def rotate_intermediate_key(self): # type: () -> None """Replace the current intermediate key with a fresh one.""" self.intermediate_key = RSAKey(key=RSA.generate(1024), use="sig", alg="RS256", kid=self._create_kid()) def rotate_jwks(self): # type: () -> None """Replace the current JWKS with a fresh one.""" self.jwks = KeyJar() kb = KeyBundle(keyusage=["enc", "sig"]) kb.append(RSAKey(key=RSA.generate(1024), kid=self._create_kid())) self.jwks.add_kb("", kb) def _create_kid(self): # type () -> str """ Create a scope (by the entity's name) key id. :return: a new key id """ return "{}/{}".format(self.name, uuid.uuid4()) def _sign(self, data, key): # type: (Mapping[str, Union[str, Sequence[str]]], Key) -> str """ Create a JWS containing the data, signed with key. :param data: data to sign :param key: key to use for signature :return: JWS containing the data """ return JWS(json.dumps(data), alg=key.alg).sign_compact(keys=[key]) def _verify(self, jws, keys): # type: (str, Sequence[Key]) -> Dict[str, Union[str, Lists[str]]] """ Verify signature of JWS. :param jws: JWS to verify signature of :param keys: possible keys to verify the signature with :return: payload of the JWS """ unpacked = JWS() unpacked.verify_compact(jws, keys=keys) return unpacked def _verify_signature_chain(self, software_statements, signing_key): # type: (Sequence[str], str) -> Tuple[str, Key] """ Verify the signature chain: signature of software statement (containing root key) and signature of a signing key (in the form of a JWS). :param software_statements: all software statements from the provider :param signing_key: the entity's intermediate signing key :return: """ software_statement = self._verify_software_statements(software_statements) root_key = keyrep(software_statement.msg["root_key"]) signing_key = self._verify_signing_key(signing_key, root_key) return software_statement, signing_key def _verify_signing_key(self, signing_key, verification_key): # type: (str, Key) -> Key """ Verify the signature of an intermediate signing key. :param signing_key: JWS containing the providers intermediate key :param verification_key: key to verify the signature with :raise OIDCFederationError: if the signature could not be verified :return: key contained in the JWS """ try: signing_key = self._verify(signing_key, keys=[verification_key]).msg except JWKESTException as e: raise OIDCFederationError("The entity's signing key could not be verified.") return keyrep(signing_key) def _verify_software_statements(self, software_statements): # type: (Sequence[str]) -> Dict[str, Union[str, List[str]]] """ Find and verify the signature of the first software statement issued by a common federation. :param software_statements: all software statements the entity presented in the metadata :raise OIDCFederationError: if no software statement has been issued by a common federation :return: payload of the first software statement issued by a common federation """ for jws in software_statements: try: return self._verify(jws, self.federation_keys) except JWKESTException as e: pass raise OIDCFederationError("No software statement issued by known federation.") def _find_software_statement_for_federation(self, federation_kid): # type: (str) -> Optional[JWS] """ Find a software statement signed by the specified key id. :param federation_kid: key id to search for :return: the first occurrence of a software statement signed by the specified key id """ for ss in self.software_statements: if ss.jwt.headers["kid"] == federation_kid: return ss return None
} ) print('Registration Request published by RP') print(70 * "-") print_lines(json.dumps(rere.to_dict(), sort_keys=True, indent=2, separators=(',', ': '))) # ### ====================================================================== # # On the OP # ### ====================================================================== print('The OP chooses which federation it will work under - SWAMID of course') op_keyjar = KeyJar() op_keyjar.add_kb(swamid_issuer, KeyBundle(swamid_jwks['keys'])) # ----------------------------------------------------------------------------- # Unpacking the russian doll (= the software_statement) # ----------------------------------------------------------------------------- msgs = [] # Only one software statement _rp_jwt = factory(rp_swamid_sost) _rp_sost = json.loads(_rp_jwt.jwt.part[1].decode('utf8')) # Only one Software Statement within the signed sost = _rp_sost['software_statements'][0] _sost_dev = unpack_software_statement(sost, '', op_keyjar)
print('Registration Request published by RP') print(70 * "-") print_lines( json.dumps(rere.to_dict(), sort_keys=True, indent=2, separators=(',', ': '))) # ### ====================================================================== # # On the OP # ### ====================================================================== print('The OP chooses which federation it will work under - SWAMID of course') op_keyjar = KeyJar() op_keyjar.add_kb(swamid_issuer, KeyBundle(swamid_jwks['keys'])) # ----------------------------------------------------------------------------- # Unpacking the russian doll (= the software_statement) # ----------------------------------------------------------------------------- msgs = [] # Only one software statement _rp_jwt = factory(rp_swamid_sost) _rp_sost = json.loads(_rp_jwt.jwt.part[1].decode('utf8')) # Only one Software Statement within the signed sost = _rp_sost['software_statements'][0] _sost_dev = unpack_software_statement(sost, '', op_keyjar)
keys = a_keyjar.keys_by_alg_and_usage("", "RS384", "sig") signed_reg_req = _jws.sign_compact(keys) rr["signed_metadata"] = signed_reg_req print(70 * "-") print("Client registration request") print(70 * "-") print_lines(json.dumps(rr.to_dict(), sort_keys=True, indent=2, separators=(",", ": "))) #### ====================================================================== ## On the OP #### ====================================================================== op_keyjar = KeyJar() op_keyjar.add_kb("https://fo.example.com/", KeyBundle(fo_jwks["keys"])) # ----------------------------------------------------------------------------- # Unpack software_statements # ----------------------------------------------------------------------------- msgs = [] # Only one software statement sost = rr["software_statements"][0] _sost = unpack_software_statement(sost, "", op_keyjar) fo_id = _sost["iss"] # ------------------------------ # get the long lived RP key (A)