def test_remove_after(): # initial keyjar keyjar = build_keyjar(KEYDEFS)[1] _old = [k.kid for k in keyjar.get_issuer_keys('') if k.kid] assert len(_old) == 2 # rotate_keys = create new keys + make the old as inactive keyjar = build_keyjar(KEYDEFS, keyjar=keyjar)[1] keyjar.remove_after = 1 # None are remove since none are marked as inactive yet keyjar.remove_outdated() _interm = [k.kid for k in keyjar.get_issuer_keys('') if k.kid] assert len(_interm) == 4 # Now mark the keys to be inactivated _now = time.time() for k in keyjar.get_issuer_keys(''): if k.kid in _old: if not k.inactive_since: k.inactive_since = _now keyjar.remove_outdated(_now + 5) # The remainder are the new keys _new = [k.kid for k in keyjar.get_issuer_keys('') if k.kid] assert len(_new) == 2 # should not be any overlap between old and new assert set(_new).intersection(set(_old)) == set()
def make_fs_jwks_bundle(iss, fo_liss, sign_keyjar, keydefs, base_path=''): """ Given a list of Federation identifiers creates a FSJWKBundle containing all the signing keys. :param iss: The issuer ID of the entity owning the JWKSBundle :param fo_liss: List with federation identifiers as keys :param sign_keyjar: Keys that the JWKSBundle owner can use to sign an export version of the JWKS bundle. :param keydefs: What type of keys that should be created for each federation. The same for all of them. :param base_path: Where the pem versions of the keys are stored as files :return: A FSJWKSBundle instance. """ jb = FSJWKSBundle(iss, sign_keyjar, 'fo_jwks', key_conv={'to': quote_plus, 'from': unquote_plus}) jb.clear() # start from scratch # Need to save the private parts on disc jb.bundle.value_conv['to'] = keyjar_to_jwks_private for entity in fo_liss: _name = entity.replace('/', '_') try: _ = jb[entity] except KeyError: fname = os.path.join(base_path, 'keys', "{}.key".format(_name)) _keydef = copy.deepcopy(keydefs) _keydef[0]['key'] = fname _keyjar = build_keyjar(_keydef)[1] jb[entity] = _keyjar return jb
def create_federation_entity(iss, jwks_dir, sup='', fo_jwks=None, ms_dir='', entity=None, sig_keys=None, sig_def_keys=None): fname = os.path.join(ms_dir, quote_plus(sup)) if fo_jwks: _keybundle = FSJWKSBundle('', fdir=fo_jwks, key_conv={'to': quote_plus, 'from': unquote_plus}) # Organisation information _kj = _keybundle[sup] signer = Signer(InternalSigningService(sup, _kj), ms_dir=fname) else: signer = Signer(ms_dir=fname) # And then the FOs public keys _public_keybundle = FSJWKSBundle('', fdir=jwks_dir, key_conv={'to': quote_plus, 'from': unquote_plus}) # The OPs own signing keys if sig_keys is None: sig_keys = build_keyjar(sig_def_keys)[1] return FederationEntity(entity, iss=iss, keyjar=sig_keys, signer=signer, fo_bundle=_public_keybundle)
def rotate_keys(self, keyconf=None): _old = [k.kid for k in self.keyjar.get_issuer_keys('') if k.kid] if keyconf: self.keyjar = build_keyjar(keyconf, keyjar=self.keyjar)[1] else: self.keyjar = build_keyjar(self.keyconf, keyjar=self.keyjar)[1] self.keyjar.remove_after = self.remove_after self.keyjar.remove_outdated() _now = time.time() for k in self.keyjar.get_issuer_keys(''): if k.kid in _old: if not k.inactive_since: k.inactive_since = _now
def test_key_rotation(): _keyjar = build_keyjar(KEYDEFS)[1] fo = FederationOperator(iss='https://example.com/op', keyjar=_keyjar, keyconf=KEYDEFS, remove_after=1) fo.rotate_keys() assert len(fo.keyjar.get_issuer_keys('')) == 4 time.sleep(1) fo.rotate_keys() assert len(fo.keyjar.get_issuer_keys('')) == 4
def own_sign_keys(sigkey_name, issuer, sig_def_keys): try: jwks = json.loads(open(sigkey_name, 'r').read()) sign_kj = KeyJar() sign_kj.import_jwks(jwks, issuer) except FileNotFoundError: jwks, sign_kj, _ = build_keyjar(sig_def_keys) sign_kj.issuer_keys[issuer] = sign_kj.issuer_keys[''] fp = open(sigkey_name, 'w') fp.write(json.dumps(sign_kj.export_jwks(private=True, issuer=issuer))) fp.close() return sign_kj
def test_create_verify(): sign_keyjar = build_keyjar(KEYDEFS)[1] jb = make_jwks_bundle('https://example.com', ['fo0', 'fo1', 'fo2', 'fo3'], sign_keyjar, KEYDEFS) _jws = jb.create_signed_bundle() _jwks = sign_keyjar.export_jwks() kj = KeyJar() kj.import_jwks(_jwks, 'https://example.com') bundle = verify_signed_bundle(_jws, kj) assert bundle
def test_build_keyjar_missing(tmpdir): keys = [{ "type": "RSA", "key": os.path.join(tmpdir.dirname, "missisng_file"), "use": ["enc", "sig"] }] jwks, keyjar, kidd = build_keyjar(keys) assert len(keyjar[""]) == 1 assert "RSA" in kidd["enc"] assert "RSA" in kidd["sig"]
def test_ace(): jb = JWKSBundle('') for iss in ['https://example.org/', 'https://example.com/']: jb[iss] = build_keyjar(KEYDEFS)[1] kj = build_keyjar(KEYDEFS)[1] sign_serv = InternalSigningService('https://signer.example.com', signing_keys=kj) signer = Signer(sign_serv) signer.metadata_statements['response'] = { 'https://example.org/': 'https://example.org/sms1' } ent = FederationEntity(None, keyjar=kj, signer=signer, fo_bundle=public_jwks_bundle(jb)) req = MetadataStatement(foo='bar') ent.ace(req, ['https://example.org/'], 'response') assert 'metadata_statements' in req assert 'signing_keys' not in req
def test_pack_metadata_statement_other_alg(): _keyjar = build_keyjar(KEYDEFS)[1] op = Operator(keyjar=_keyjar, iss='https://example.com/') req = MetadataStatement(issuer='https://example.org/op') sms = op.pack_metadata_statement(req, alg='ES256') assert sms # Should be a signed JWT _jwt = factory(sms) _body = json.loads(as_unicode(_jwt.jwt.part[1])) assert _body['iss'] == 'https://example.com/' # verify signature _kj = public_keys_keyjar(_keyjar, '', None, op.iss) r = _jwt.verify_compact(sms, _kj.get_signing_key(owner=op.iss)) assert r
def test_get_metadata_statement(): jb = JWKSBundle('') for iss in ['https://example.org/', 'https://example.com/']: jb[iss] = build_keyjar(KEYDEFS)[1] op = Operator(keyjar=jb['https://example.com/'], iss='https://example.com/') req = MetadataStatement(foo='bar') sms = op.pack_metadata_statement(req, alg='RS256') sms_dir = {'https://example.com': sms} req['metadata_statements'] = Message(**sms_dir) ent = FederationEntity(None, fo_bundle=public_jwks_bundle(jb)) loe = ent.get_metadata_statement(req) assert loe
def init(keydefs, tool_iss, liss, lifetime): # The FOs signing keys sig_keys = build_keyjar(keydefs)[1] key_bundle = make_fs_jwks_bundle(tool_iss, liss, sig_keys, keydefs, './') #sig_keys = build_keyjar(keydefs)[1] operator = {} for entity, _keyjar in key_bundle.items(): _keyjar[''] = _keyjar[entity] operator[entity] = Operator(iss=entity, keyjar=_keyjar, lifetime=lifetime) return {'operator': operator, 'key_bundle': key_bundle}
def setup(self): mkey = [ { "type": "RSA", "use": ["sig"] }, { "type": "RSA", "use": ["sig"] }, { "type": "RSA", "use": ["sig"] }, ] skey = [ { "type": "RSA", "use": ["sig"] }, ] kj1 = build_keyjar(mkey)[1] kj2 = build_keyjar(skey)[1] self.keyjar = KeyJar() self.keyjar['A'] = kj1[''] self.keyjar['B'] = kj2[''] _jws = JWS('{"aud": "A"}', alg='RS256') sig_key = self.keyjar.get_signing_key('rsa', owner='A')[0] self.sjwt_a = _jws.sign_compact([sig_key]) _jws = JWS('{"aud": "B"}', alg='RS256') sig_key = self.keyjar.get_signing_key('rsa', owner='B')[0] self.sjwt_b = _jws.sign_compact([sig_key])
def make_jwks_bundle(iss, fo_liss, sign_keyjar, keydefs, base_path=''): """ Given a list of Federation identifiers creates a FSJWKBundle containing all the signing keys. :param iss: The issuer ID of the entity owning the JWKSBundle :param fo_liss: List of federation identifiers :param sign_keyjar: Keys that the JWKSBundel owner can use to sign an export version of the JWKS bundle. :param keydefs: What type of keys that should be created for each federation. The same for all of them. :return: A JWKSBundle instance. """ jb = JWKSBundle(iss, sign_keyjar) for entity in fo_liss: _keydef = copy.deepcopy(keydefs) _jwks, _keyjar, _kidd = build_keyjar(_keydef) jb[entity] = _keyjar return jb
def get_signing_keys(eid, keydef, key_file): """ If the *key_file* file exists then read the keys from there, otherwise create the keys and store them a file with the name *key_file*. :param eid: The ID of the entity that the keys belongs to :param keydef: What keys to create :param key_file: A file name :return: A :py:class:`oic.utils.keyio.KeyJar` instance """ if os.path.isfile(key_file): kj = KeyJar() kj.import_jwks(json.loads(open(key_file, 'r').read()), eid) else: kj = build_keyjar(keydef)[1] # make it know under both names fp = open(key_file, 'w') fp.write(json.dumps(kj.export_jwks())) fp.close() kj.issuer_keys[eid] = kj.issuer_keys[''] return kj
def test_build_keyjar(): keys = [ { "type": "RSA", "use": ["enc", "sig"] }, { "type": "EC", "crv": "P-256", "use": ["sig"] }, ] jwks, keyjar, kidd = build_keyjar(keys) for key in jwks["keys"]: assert "d" not in key # the JWKS shouldn't contain the private part # of the keys assert len(keyjar[""]) == 2 # 1 with RSA keys and 1 with EC key assert "RSA" in kidd["enc"] assert "RSA" in kidd["sig"] assert "EC" in kidd["sig"]
def test_unpack_aggregated_response(self): claims = { "address": { "street_address": "1234 Hollywood Blvd.", "locality": "Los Angeles", "region": "CA", "postal_code": "90210", "country": "US" }, "phone_number": "+1 (555) 123-4567" } _keyjar = build_keyjar(KEYSPEC)[1] srv = JWT(_keyjar, iss='https://example.org/op/', sign_alg='ES256') _jwt = srv.pack(payload=claims) resp = OpenIDSchema(sub='diana', given_name='Diana', family_name='krall', _claim_names={ 'address': 'src1', 'phone_number': 'src1' }, _claim_sources={'src1': { 'JWT': _jwt }}) public_keys_keyjar(_keyjar, '', self.cli_info.keyjar, 'https://example.org/op/') _resp = self.req.parse_response(resp.to_json(), self.cli_info) assert set(_resp.keys()) == { 'sub', 'given_name', 'family_name', '_claim_names', '_claim_sources', 'address', 'phone_number' }
def test_pack_metadata_statement(): jb = FSJWKSBundle('', None, 'fo_jwks', key_conv={ 'to': quote_plus, 'from': unquote_plus }) _keyjar = build_keyjar(KEYDEFS)[1] op = Operator(keyjar=_keyjar, jwks_bundle=jb, iss='https://example.com/') req = MetadataStatement(issuer='https://example.org/op') sms = op.pack_metadata_statement(req) assert sms # Should be a signed JWT _jwt = factory(sms) assert _jwt assert _jwt.jwt.headers['alg'] == 'RS256' _body = json.loads(as_unicode(_jwt.jwt.part[1])) assert _body['iss'] == op.iss assert _body['issuer'] == 'https://example.org/op' # verify signature _kj = public_keys_keyjar(_keyjar, '', None, op.iss) r = _jwt.verify_compact(sms, _kj.get_signing_key(owner=op.iss)) assert r
from oicmsg.key_jar import build_keyjar from oicmsg.key_jar import public_keys_keyjar ISS = 'https://example.com' ISS2 = 'https://example.org' KEYDEFS = [{ "type": "RSA", "key": '', "use": ["sig"] }, { "type": "EC", "crv": "P-256", "use": ["sig"] }] SIGN_KEYS = build_keyjar(KEYDEFS)[1] KEYJAR = {} for iss in [ 'https://www.swamid.se', 'https://www.sunet.se', 'https://www.feide.no', 'https://www.uninett.no' ]: KEYJAR[iss] = build_keyjar(KEYDEFS)[1] def test_create(): bundle = JWKSBundle(ISS, SIGN_KEYS) assert bundle
from fedoicmsg.bundle import jwks_to_keyjar from fedoicmsg.utils import request_signed_by_signing_keys from fedoicmsg.utils import self_sign_jwks from fedoicmsg.utils import verify_request_signed_by_signing_keys from fedoicmsg.utils import verify_self_signed_jwks from oicmsg.key_jar import KeyJar from oicmsg.key_jar import build_keyjar KEYDEFS = [ {"type": "RSA", "use": ["sig"]}, {"type": "RSA", "use": ["sig"]}, {"type": "EC", "crv": "P-256", "use": ["sig"]} ] JWKS, KEYJAR, _ = build_keyjar(KEYDEFS) def test_jwks_to_keyjar(): _kj = jwks_to_keyjar(JWKS) assert list(_kj.owners()) == [''] assert len(_kj.get_signing_key('RSA',owner='')) == 2 assert len(_kj.get_signing_key('EC',owner='')) == 1 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
KEYSPEC = [ { "type": "RSA", "use": ["enc"] }, { "type": "EC", "crv": "P-256", "use": ["enc"] }, ] RECEIVER = 'https://example.org/op' keyjar = build_keyjar(KEYSPEC)[1] # reading and writing to the same KeyJAr instance public_keys_keyjar(keyjar, '', keyjar, RECEIVER) def test_request_object_encryption(): msg = AuthorizationRequest(state='ABCDE', redirect_uri='https://example.com/cb', response_type='code') conf = { 'redirect_uris': ['https://example.com/cli/authz_cb'], 'client_id': 'client_1', 'client_secret': 'abcdefghijklmnop', }
from oicmsg.jwt import JWT from oicmsg.key_jar import build_keyjar, KeyJar from oicmsg.oic import JsonWebToken __author__ = 'Roland Hedberg' BASE_PATH = os.path.abspath( os.path.join(os.path.dirname(__file__), "data/keys")) keys = [ {"type": "RSA", "key": os.path.join(BASE_PATH, "cert.key"), "use": ["enc", "sig"]}, {"type": "EC", "crv": "P-256", "use": ["sig"]}, {"type": "EC", "crv": "P-256", "use": ["enc"]} ] jwks, keyjar, kidd = build_keyjar(keys) issuer = 'https://fedop.example.org' receiver = 'https://example.com' keyjar[issuer] = keyjar[''] # just testing right !? keyjar[receiver] = keyjar[''] def _eq(l1, l2): return set(l1) == set(l2) def test_jwt_pack(): _jwt = JWT(keyjar, lifetime=3600, iss=issuer).pack() assert _jwt assert len(_jwt.split('.')) == 3
}, { "type": "EC", "crv": "P-256", "use": ["sig"] }] KEYS = {} ISSUER = {} OPERATOR = {} for entity in ['fo', 'fo1', 'org', 'inter', 'admin', 'ligo', 'op']: fname = os.path.join(BASE_PATH, "{}.key".format(entity)) _keydef = KEYDEFS[:] _keydef[0]['key'] = fname _jwks, _keyjar, _kidd = build_keyjar(_keydef) KEYS[entity] = { 'jwks': json.dumps(_jwks), 'keyjar': _keyjar, 'kidd': _kidd } ISSUER[entity] = 'https://{}.example.org'.format(entity) OPERATOR[entity] = Operator(keyjar=_keyjar, iss=ISSUER[entity]) FOP = OPERATOR['fo'] FOP.jwks_bundle = JWKSBundle(FOP.iss) FOP.jwks_bundle[FOP.iss] = FOP.keyjar FO1P = OPERATOR['fo1'] FO1P.jwks_bundle = JWKSBundle(FO1P.iss) FO1P.jwks_bundle[FO1P.iss] = FO1P.keyjar
from oicmsg.key_jar import KeyJar from oicmsg.key_jar import build_keyjar TEST_ISS = "https://test.example.com" KEYDEFS = [{ "type": "RSA", "key": '', "use": ["sig"] }, { "type": "EC", "crv": "P-256", "use": ["sig"] }] SIGN_KEYJAR = build_keyjar(KEYDEFS)[1] FO = { 'swamid': 'https://swamid.sunet.se', 'feide': 'https://www.feide.no', 'edugain': 'https://edugain.com', 'example': 'https://example.com' } OA = {'sunet': 'https://sunet.se', 'uninett': 'https://uninett.no'} SMS_DEF = { OA['sunet']: { "discovery": { FO['swamid']: [{ 'request': {}, 'requester': OA['sunet'],
keym = [ { "type": "RSA", "use": ["sig"] }, { "type": "RSA", "use": ["sig"] }, { "type": "RSA", "use": ["sig"] }, ] KEYJAR = build_keyjar(keys)[1] IKEYJAR = build_keyjar(keys)[1] IKEYJAR.issuer_keys['issuer'] = IKEYJAR.issuer_keys[''] del IKEYJAR.issuer_keys[''] KEYJARS = {} for iss in ['A', 'B', 'C']: _kj = build_keyjar(keym)[1] _kj.issuer_keys[iss] = _kj.issuer_keys[''] del _kj.issuer_keys[''] KEYJARS[iss] = _kj def url_compare(url1, url2): url1 = urlparse(url1) url2 = urlparse(url2)