Exemple #1
0
    def setup(self):
        httpretty.enable()

        self.key = RSAKey(kid='testkey').load(
            os.path.join(FIXTURE_ROOT, 'testkey.pem'))

        def jwks(_request, _uri, headers):  # noqa: E306
            ks = KEYS()
            ks.add(self.key.serialize())
            return 200, headers, ks.dump_jwks()

        httpretty.register_uri(httpretty.GET,
                               oidc_rp_settings.PROVIDER_JWKS_ENDPOINT,
                               status=200,
                               body=jwks)
        httpretty.register_uri(httpretty.POST,
                               oidc_rp_settings.PROVIDER_TOKEN_ENDPOINT,
                               body=json.dumps({
                                   'id_token': self.generate_jws(),
                                   'access_token': 'accesstoken',
                                   'refresh_token': 'refreshtoken',
                               }),
                               content_type='text/json')
        httpretty.register_uri(httpretty.GET,
                               oidc_rp_settings.PROVIDER_USERINFO_ENDPOINT,
                               body=json.dumps({
                                   'sub': '1234',
                                   'email': '*****@*****.**',
                               }),
                               content_type='text/json')

        yield

        httpretty.disable()
    def __init__(self, public_key=None, keyset_url=None):
        """
        Instance message validator

        Import a public key from the tool by either using a keyset url
        or a combination of public key + key id.

        Keyset URL takes precedence because it makes key rotation easier to do.
        """
        # Only store keyset URL to avoid blocking the class
        # instancing on an external url, which is only used
        # when validating a token.
        self.keyset_url = keyset_url
        self.public_key = None

        # Import from public key
        if public_key:
            try:
                new_key = RSAKey(use='sig')

                # Unescape key before importing it
                raw_key = codecs.decode(public_key, 'unicode_escape')

                # Import Key and save to internal state
                new_key.load_key(RSA.import_key(raw_key))
                self.public_key = new_key
            except ValueError as err:
                raise exceptions.InvalidRsaKey() from err
Exemple #3
0
def key_setup(vault, **kwargs):
    """
    :param vault: Where the keys are kept
    :return: 2-tuple: result of urlsplit and a dictionary with
        parameter name as key and url and value
    """
    vault_path = proper_path(vault)

    if not os.path.exists(vault_path):
        os.makedirs(vault_path)

    kb = KeyBundle()
    for usage in ["sig", "enc"]:
        if usage in kwargs:
            if kwargs[usage] is None:
                continue

            _args = kwargs[usage]
            if _args["alg"].upper() == "RSA":
                try:
                    _key = rsa_load('%s%s' % (vault_path, "pyoidc"))
                except Exception:
                    devnull = open(os.devnull, 'w')
                    with RedirectStdStreams(stdout=devnull, stderr=devnull):
                        _key = create_and_store_rsa_key_pair(path=vault_path)

                k = RSAKey(key=_key, use=usage)
                k.add_kid()
                kb.append(k)
    return kb
Exemple #4
0
    def generate_jwks(self, mode):
        if "rotenc" in self.behavior_type:  # Rollover encryption keys
            rsa_key = RSAKey(kid="rotated_rsa_{}".format(time.time()),
                             use="enc").load_key(RSA.generate(2048))
            ec_key = ECKey(kid="rotated_ec_{}".format(time.time()),
                           use="enc").load_key(P256)

            keys = [rsa_key.serialize(private=True),
                    ec_key.serialize(private=True)]
            new_keys = {"keys": keys}
            #self.do_key_rollover(new_keys, "%d")

            signing_keys = [k.to_dict() for k in self.keyjar.get_signing_key()]
            new_keys["keys"].extend(signing_keys)
            return json.dumps(new_keys)
        elif "nokid1jwk" in self.behavior_type:
            alg = mode["sign_alg"]
            if not alg:
                alg = "RS256"
            keys = [k.to_dict() for kb in self.keyjar[""] for k in
                    list(kb.keys())]
            for key in keys:
                if key["use"] == "sig" and key["kty"].startswith(alg[:2]):
                    key.pop("kid", None)
                    jwk = dict(keys=[key])
                    return json.dumps(jwk)
            raise Exception(
                "Did not find sig {} key for nokid1jwk test ".format(alg))
        else:  # Return all keys
            keys = [k.to_dict() for kb in self.keyjar[""] for k in
                    list(kb.keys())]
            jwks = dict(keys=keys)
            return json.dumps(jwks)
Exemple #5
0
def test_cmp_rsa_ec():
    _key1 = RSAKey()
    _key1.load_key(pem_cert2rsa(CERT))

    _key2 = ECKey(**ECKEY)

    assert _key1 != _key2
 def _add_key_to_keyjar(self, pkey, owner=''):
     kb = keyio.KeyBundle()
     priv_key = RSA.importKey(pkey)
     key = RSAKey().load_key(priv_key)
     key.use = "sig"
     kb.append(key)
     self.keyjar.add_kb(owner, kb)
Exemple #7
0
def test_cmp_rsa_ec():
    _key1 = RSAKey()
    _key1.load_key(pem_cert2rsa(CERT))

    _key2 = ECKey(**ECKEY)

    assert _key1 != _key2
Exemple #8
0
def key_setup(vault, **kwargs):
    """
    :param vault: Where the keys are kept
    :return: 2-tuple: result of urlsplit and a dictionary with
        parameter name as key and url and value
    """
    vault_path = proper_path(vault)

    if not os.path.exists(vault_path):
        os.makedirs(vault_path)

    kb = KeyBundle()
    for usage in ["sig", "enc"]:
        if usage in kwargs:
            if kwargs[usage] is None:
                continue

            _args = kwargs[usage]
            if _args["alg"].upper() == "RSA":
                try:
                    _key = rsa_load('%s%s' % (vault_path, "pyoidc"))
                except Exception:
                    devnull = open(os.devnull, 'w')
                    with RedirectStdStreams(stdout=devnull, stderr=devnull):
                        _key = create_and_store_rsa_key_pair(
                            path=vault_path)

                k = RSAKey(key=_key, use=usage)
                k.add_kid()
                kb.append(k)
    return kb
Exemple #9
0
def test_equal():
    rsa1 = RSAKey(
        alg="RS256",
        e="AQAB",
        kty="RSA",
        n=
        "wkpyitec6TgFC5G41RF6jBOZghGVyaHL79CzSjjS9VCkWjpGo2hajOsiJ1RnSoat9XDmQAqiqn18rWx4xa4ErdWVqug88pLxMVmnV9tF10uJNgIi_RSsIQz40J9aKrxOotN6Mnq454BpanAxbrbC5hLlp-PIGgmWzUDNwCSfnWBjd0yGwdYKVB6d-SGNfLvdMUhFiYIX0POUnJDNl_j3kLYQ0peYRbunyQzST5nLPOItePCuZ12G5e0Eo1meSF1Md3IkuY8paqKk-vsWrT22X7CUV3HZow06ogRcFMMzvooE7yDqS53I_onsUrqgQ2aUnoo8OaD0eLlEWdaTyeNAIw",
        use="sig")

    rsa2 = RSAKey(
        n=
        "pKXuY5tuT9ibmEcq4B6VRx3MafdSsajrOndAk5FjJFedlA6qSpdqDUr9wWUkNeO8h_efdLfg43CHXk3mH6Fp1t2gbHzBQ4-SzT3_X5tsdG2PPqvngem7f5NHO6Kefhq11Zk5q4-FyTL9FUQQW6ZANbrU7GifSAs82Ck20ciIvFdv7cPCphk_THMVv14aW5w0eKEXumgx4Bc7HrQFXQUHSze3dVAKg8hKHDIQOGUU0fkolEFmOC4Gb-G57RpBJryZxXqgdUdEG66xl1f37tqpYgaLViFDWDiI8S7BMVHEbGHN4-f_MD9f6gMduaxrL6a6SfyIW1So2VqtvlAyanesTw",
        kid="gtH4v3Yr2QqLreBSz0ByQQ8vkf8eFo1KIit3s-3Bbww",
        use="enc",
        e="AQAB",
        kty="RSA")

    rsa3 = RSAKey(
        n=
        "pKXuY5tuT9ibmEcq4B6VRx3MafdSsajrOndAk5FjJFedlA6qSpdqDUr9wWUkNeO8h_efdLfg43CHXk3mH6Fp1t2gbHzBQ4-SzT3_X5tsdG2PPqvngem7f5NHO6Kefhq11Zk5q4-FyTL9FUQQW6ZANbrU7GifSAs82Ck20ciIvFdv7cPCphk_THMVv14aW5w0eKEXumgx4Bc7HrQFXQUHSze3dVAKg8hKHDIQOGUU0fkolEFmOC4Gb-G57RpBJryZxXqgdUdEG66xl1f37tqpYgaLViFDWDiI8S7BMVHEbGHN4-f_MD9f6gMduaxrL6a6SfyIW1So2VqtvlAyanesTw",
        use="enc",
        e="AQAB",
        kty="RSA")

    assert rsa1 == rsa1
    assert rsa1 != rsa2
    assert rsa2 != rsa3
Exemple #10
0
def test_extract_rsa_from_cert_2():
    _ckey = pem_cert2rsa(CERT)
    _key = RSAKey()
    _key.load_key(_ckey)

    print(_key)

    assert _ckey.n == _key.get_key().n
Exemple #11
0
def test_serialize_rsa_priv_key():
    rsakey = RSAKey(key=import_rsa_key_from_file(full_path("rsa.key")))
    assert rsakey.d

    d_rsakey = rsakey.serialize(private=True)
    restored_key = RSAKey(**d_rsakey)

    assert rsa_eq(restored_key, rsakey)
Exemple #12
0
def test_serialize_rsa_priv_key():
    rsakey = RSAKey(key=import_rsa_key_from_file(full_path("rsa.key")))
    assert rsakey.d

    d_rsakey = rsakey.serialize(private=True)
    restored_key = RSAKey(**d_rsakey)

    assert rsa_eq(restored_key, rsakey)
Exemple #13
0
def test_extract_rsa_from_cert_2():
    _ckey = pem_cert2rsa(CERT)
    _key = RSAKey()
    _key.load_key(_ckey)

    print(_key)

    assert _ckey.n == _key.get_key().n
Exemple #14
0
def test_kspec():
    _ckey = pem_cert2rsa(CERT)
    _jwk = RSAKey(key=_ckey)
    _jwk.serialize()

    print _jwk
    assert _jwk.kty == "RSA"
    assert _jwk.e == JWK["keys"][0]["e"]
    assert _jwk.n == JWK["keys"][0]["n"]
Exemple #15
0
def test_thumbprint_7638_example():
    key = RSAKey(
        n=
        '0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw',
        e='AQAB',
        alg='RS256',
        kid='2011-04-29')
    thumbprint = key.thumbprint('SHA-256')
    assert b64e(thumbprint) == b'NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs'
Exemple #16
0
def test_thumbprint_7638_example():
    key = RSAKey(
        n="0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw",
        e="AQAB",
        alg="RS256",
        kid="2011-04-29",
    )
    thumbprint = key.thumbprint("SHA-256")
    assert b64e(thumbprint) == b"NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs"
Exemple #17
0
 def get_public_jwk(self):
     try:
         _rsakey = get_key_storage().public
     except FileNotFoundError:
         self.generate_keys()
         _rsakey = get_key_storage().public
     _rsakey = RSA.import_key(_rsakey)
     _rsajwk = RSAKey(kid="test", use="sig", alg="RS256", key=_rsakey)
     return _rsajwk.serialize(private=True)
Exemple #18
0
def test_kspec():
    _ckey = pem_cert2rsa(CERT)
    _key = RSAKey()
    _key.load_key(_ckey)

    print(_key)
    jwk = _key.serialize()
    assert jwk["kty"] == "RSA"
    assert jwk["e"] == JWK["keys"][0]["e"]
    assert jwk["n"] == JWK["keys"][0]["n"]
Exemple #19
0
def test_extract_rsa_from_cert_2():
    _ckey = pem_cert2rsa(CERT)
    _jwk = RSAKey(key=_ckey)
    _jwk.serialize()

    print _jwk

    _n = base64_to_long(str(_jwk.n))

    assert _ckey.n == _n
Exemple #20
0
def test_kspec():
    _ckey = pem_cert2rsa(CERT)
    _key = RSAKey()
    _key.load_key(_ckey)

    print(_key)
    jwk = _key.serialize()
    assert jwk["kty"] == "RSA"
    assert jwk["e"] == JWK["keys"][0]["e"].encode("utf-8")
    assert jwk["n"] == JWK["keys"][0]["n"].encode("utf-8")
Exemple #21
0
    def do_local_der(self, filename, keytype, keyusage):
        # This is only for RSA keys
        _bkey = rsa_load(filename)

        if not keyusage:
            keyusage = ["enc", "sig"]

        for use in keyusage:
            _key = RSAKey().load_key(_bkey)
            _key.use = use
            self._keys.append(_key)
Exemple #22
0
def test_encrypt_decrypt_rsa_cbc():
    _key = RSAKey(key=rsa)
    _key._keytype = "private"
    _jwe0 = JWE(plain, alg="RSA1_5", enc="A128CBC-HS256")

    jwt = _jwe0.encrypt([_key])

    _jwe1 = factory(jwt)
    msg = _jwe1.decrypt(jwt, [_key])

    assert msg == plain
Exemple #23
0
def test_encrypt_decrypt_rsa_cbc():
    _key = RSAKey(key=rsa)
    _key._keytype = "private"
    _jwe0 = JWE(plain, alg="RSA1_5", enc="A128CBC-HS256")

    jwt = _jwe0.encrypt([_key])

    _jwe1 = JWE()
    msg = _jwe1.decrypt(jwt, [_key])

    assert msg == plain
Exemple #24
0
    def do_local_der(self, filename, keytype, keyusage):
        # This is only for RSA keys
        _bkey = rsa_load(filename)

        if not keyusage:
            keyusage = ["enc", "sig"]

        for use in keyusage:
            _key = RSAKey().load_key(_bkey)
            _key.use = use
            self._keys.append(_key)
Exemple #25
0
def test_cmp_rsa_ec():
    _key1 = RSAKey()
    _key1.load_key(pem_cert2rsa(CERT))

    _key2 = ECKey(**ECKEY)

    try:
        assert _key1 == _key2
    except AssertionError:
        pass
    else:
        assert False
Exemple #26
0
def test_cmp_rsa_ec():
    _key1 = RSAKey()
    _key1.load_key(pem_cert2rsa(CERT))

    _key2 = ECKey(**ECKEY)

    try:
        assert _key1 == _key2
    except AssertionError:
        pass
    else:
        assert False
Exemple #27
0
def gen_rsa_key(ekid, pub=None, priv=None):

    if priv == None:
        new_priv = RSA.generate(2048)
        jwk_priv = RSAKey(key=new_priv).to_dict()
        iv = None
    else:
        iv = optestlib.opb64d(priv['iv'])
        jwk_priv = priv['key']

    if pub == None:
        new_pub = new_priv.publickey()
        jwk_pub = RSAKey(key=new_pub).to_dict()
    else:
        jwk_pub = pub

    jwk_priv['alg'] = 'RSA-OAEP'
    jwk_priv['key_ops'] = ['decrypt']
    jwk_priv['kid'] = ekid

    jwk_pub['key_ops'] = ['encrypt']
    jwk_pub['alg'] = 'RSA-OAEP'
    jwk_pub['ext'] = True
    jwk_pub['kid'] = ekid

    optestlib.p_str("New Private key", json.dumps(jwk_priv, indent=4))

    pri_keys[ekid] = jwk_priv
    pub_keys[ekid] = jwk_pub

    key_dat_str = json.dumps(jwk_priv)

    optestlib.p_debug('\n*** Encrypting pri key with AES kid %s' % ekid)

    iv, ct = optestlib.enc_aes_gcm(key_dat_str, sym_keys[ekid], iv=iv)

    optestlib.p_data('IV', iv, dump=False)
    optestlib.p_data('KEY', sym_keys[ekid], dump=False)
    optestlib.p_data('Ciphertext', ct, dump=False)

    priv_out = {
        'kid': ekid,
        'cty': 'b5+jwk+json',
        'enc': 'A256GCM',
        'data': optestlib.opb64e(ct),
        'iv': optestlib.opb64e(iv)
    }

    optestlib.p_str("New Public key", json.dumps(jwk_pub, indent=4))

    return jwk_pub, priv_out
Exemple #28
0
    def id_token_as_signed_jwt(self, session, loa="2", alg="", code=None,
                               access_token=None, user_info=None, auth_time=0,
                               exp=None, extra_claims=None, **kwargs):

        kwargs = {}

        if "rotsig" in self.behavior_type:  # Rollover signing keys
            if alg == "RS256":
                key = RSAKey(kid="rotated_rsa_{}".format(time.time()),
                             use="sig").load_key(RSA.generate(2048))
            else:  # alg == "ES256"
                key = ECKey(kid="rotated_ec_{}".format(time.time()),
                            use="sig").load_key(P256)

            new_keys = {"keys": [key.serialize(private=True)]}
            self.events.store("New signing keys", new_keys)
            self.do_key_rollover(new_keys, "%d")
            self.events.store("Rotated signing keys", '')

        if "nokid1jwks" in self.behavior_type:
            kwargs['keys'] = self.no_kid_keys()
            # found_key = None
            # for kb in self.keyjar.key_summary[""]:
            #     issuer_key = list(kb.keys())[0]
            #     if issuer_key.use == "sig" and \
            #             issuer_key.kty.startswith(
            #                 alg[:2]):
            #         issuer_key.kid = None
            #         found_key = key
            #         break
            # self.keyjar.key_summary[""] = [found_key]

        if "nokidmuljwks" in self.behavior_type:
            kwargs['keys'] = self.no_kid_keys()
            # for key in self.keyjar.key_summary[""]:
            #     for inner_key in list(key.keys()):
            #         inner_key.kid = None

        _jws = provider.Provider.id_token_as_signed_jwt(
            self, session, loa=loa, alg=alg, code=code,
            access_token=access_token, user_info=user_info,
            auth_time=auth_time,
            exp=exp, extra_claims=extra_claims, **kwargs)

        if "idts" in self.behavior_type:  # mess with the signature
            #
            p = _jws.split(".")
            p[2] = sort_string(p[2])
            _jws = ".".join(p)

        return _jws
Exemple #29
0
    def id_token_as_signed_jwt(self, session, loa="2", alg="", code=None,
                               access_token=None, user_info=None, auth_time=0,
                               exp=None, extra_claims=None, **kwargs):

        kwargs = {}

        if "rotsig" in self.behavior_type:  # Rollover signing keys
            if alg == "RS256":
                key = RSAKey(kid="rotated_rsa_{}".format(time.time()),
                             use="sig").load_key(RSA.generate(2048))
            else:  # alg == "ES256"
                key = ECKey(kid="rotated_ec_{}".format(time.time()),
                            use="sig").load_key(P256)

            new_keys = {"keys": [key.serialize(private=True)]}
            self.events.store("New signing keys", new_keys)
            self.do_key_rollover(new_keys, "%d")
            self.events.store("Rotated signing keys", '')

        if "nokid1jwks" in self.behavior_type:
            kwargs['keys'] = self.no_kid_keys()
            # found_key = None
            # for kb in self.keyjar.key_summary[""]:
            #     issuer_key = list(kb.keys())[0]
            #     if issuer_key.use == "sig" and \
            #             issuer_key.kty.startswith(
            #                 alg[:2]):
            #         issuer_key.kid = None
            #         found_key = key
            #         break
            # self.keyjar.key_summary[""] = [found_key]

        if "nokidmuljwks" in self.behavior_type:
            kwargs['keys'] = self.no_kid_keys()
            # for key in self.keyjar.key_summary[""]:
            #     for inner_key in list(key.keys()):
            #         inner_key.kid = None

        _jws = provider.Provider.id_token_as_signed_jwt(
            self, session, loa=loa, alg=alg, code=code,
            access_token=access_token, user_info=user_info,
            auth_time=auth_time,
            exp=exp, extra_claims=extra_claims, **kwargs)

        if "idts" in self.behavior_type:  # mess with the signature
            #
            p = _jws.split(".")
            p[2] = sort_string(p[2])
            _jws = ".".join(p)

        return _jws
Exemple #30
0
def test_cmp_rsa():
    _key1 = RSAKey()
    _key1.load_key(pem_cert2rsa(CERT))

    _key2 = RSAKey()
    _key2.load_key(pem_cert2rsa(CERT))

    assert _key1 == _key2
Exemple #31
0
def rsa_init(spec):
    """
    Initiates a :py:class:`oicmsg.oauth.keybundle.KeyBundle` instance
    containing newly minted RSA keys according to a spec.
    
    Example of specification::
        {'name': 'myrsakey', 'path': 'keystore', 'size':2048, 
         'use': ['enc', 'sig'] }
         
    Using the spec above 2 RSA keys would be minted, one for 
    encryption and one for signing.
        
    :param spec:
    :return: KeyBundle
    """
    arg = {}
    for param in ["name", "path", "size"]:
        try:
            arg[param] = spec[param]
        except KeyError:
            pass

    kb = KeyBundle(keytype="RSA")
    for use in harmonize_usage(spec["use"]):
        _key = create_and_store_rsa_key_pair(use=use, **arg)
        kb.append(RSAKey(use=use, key=_key))
    return kb
    def test_full_flow(self, account_linking_config, internal_response, context):
        ticket = "ticket"
        with responses.RequestsMock() as rsps:
            rsps.add(
                responses.GET,
                "%s/get_id" % account_linking_config["api_url"],
                status=404,
                body=ticket,
                content_type="text/html"
            )
            result = self.account_linking.process(context, internal_response)
        assert isinstance(result, Redirect)
        assert result.message.startswith(account_linking_config["redirect_url"])

        data = {
            "idp": internal_response.auth_info.issuer,
            "id": internal_response.user_id,
            "redirect_endpoint": self.account_linking.base_url + "/account_linking/handle_account_linking"
        }
        key = RSAKey(key=rsa_load(account_linking_config["sign_key"]), use="sig", alg="RS256")
        jws = JWS(json.dumps(data), alg=key.alg).sign_compact([key])
        uuid = "uuid"
        with responses.RequestsMock() as rsps:
            # account is linked, 200 OK
            rsps.add(
                responses.GET,
                "%s/get_id?jwt=%s" % (account_linking_config["api_url"], jws),
                status=200,
                body=uuid,
                content_type="text/html",
                match_querystring=True
            )
            internal_response = self.account_linking._handle_al_response(context)
        assert internal_response.user_id == uuid
    def test_handle_authn_response_returns_id_token_for_verified_affiliation(
            self, signing_key_path, context, scope_value, affiliation):
        authn_req = AuthorizationRequest(
            scope='openid ' + scope_value,
            client_id='client1',
            redirect_uri='https://client.example.com',
            response_type='id_token')
        context.state[self.frontend.name] = {
            'oidc_request': authn_req.to_urlencoded()
        }
        internal_response = InternalResponse(
            AuthenticationInformation(None, str(datetime.now()),
                                      'https://idp.example.com'))
        internal_response.attributes['affiliation'] = [affiliation]
        internal_response.user_id = 'user1'

        resp = self.frontend.handle_authn_response(context, internal_response)
        auth_resp = AuthorizationResponse().from_urlencoded(
            urlparse(resp.message).fragment)

        id_token = IdToken().from_jwt(
            auth_resp['id_token'],
            key=[RSAKey(key=rsa_load(signing_key_path))])
        assert id_token['iss'] == self.frontend.base_url
        assert id_token['aud'] == ['client1']
        assert id_token['auth_time'] == internal_response.auth_info.timestamp
Exemple #34
0
    def setUp(self):
        super().setUp()

        self.rsa_key_id = "1"
        # Generate RSA and save exports
        rsa_key = RSA.generate(2048)
        self.key = RSAKey(key=rsa_key, kid=self.rsa_key_id)
        self.public_key = rsa_key.publickey().export_key()

        self.xblock_attributes = {
            'lti_version': 'lti_1p3',
            'lti_1p3_launch_url': 'http://tool.example/launch',
            'lti_1p3_oidc_url': 'http://tool.example/oidc',
            # We need to set the values below because they are not automatically
            # generated until the user selects `lti_version == 'lti_1p3'` on the
            # Studio configuration view.
            'lti_1p3_tool_public_key': self.public_key,
            'has_score': True,
        }
        self.xblock = make_xblock('lti_consumer', LtiConsumerXBlock,
                                  self.xblock_attributes)
        # Set dummy location so that UsageKey lookup is valid
        self.xblock.location = 'block-v1:course+test+2020+type@problem+block@test'

        # Creates an LTI configuration objects for testing
        self.lti_1p1_config = LtiConfiguration.objects.create(
            location=str(self.xblock.location),
            version=LtiConfiguration.LTI_1P1)

        self.lti_1p3_config = LtiConfiguration.objects.create(
            location=str(self.xblock.location),
            version=LtiConfiguration.LTI_1P3)
Exemple #35
0
    def update(self, msg, state, key_size=0):
        """
        Use to 'update' the AccessToken Request.

        :param msg:
        :param state: Used to map access token response to this request
        :param key_size:
        :return:
        """
        if not key_size:
            key_size = self.key_size

        key = RSAKey(key=RSA.generate(key_size))
        self.state2key[state] = key
        msg["key"] = json.dumps(key.serialize())
        return msg
Exemple #36
0
    def update(self, msg, state, key_size=0):
        """
        Used to 'update' the AccessToken Request

        :param msg:
        :param state: Used to map access token response to this request
        :param key_size:
        :return:
        """
        if not key_size:
            key_size = self.key_size

        key = RSAKey(key=RSA.generate(key_size))
        self.state2key[state] = key
        msg['key'] = json.dumps(key.serialize())
        return msg
Exemple #37
0
def init_oidc_provider(app):
    with app.app_context():
        issuer = url_for('oidc_provider.index')[:-1]
        authentication_endpoint = url_for('oidc_provider.authentication_endpoint')
        jwks_uri = url_for('oidc_provider.jwks_uri')
        token_endpoint = url_for('oidc_provider.token_endpoint')
        userinfo_endpoint = url_for('oidc_provider.userinfo_endpoint')
        registration_endpoint = url_for('oidc_provider.registration_endpoint')
        end_session_endpoint = url_for('oidc_provider.end_session_endpoint')
        userinfo_ldap = LdapUserInfo()

    configuration_information = {
        'issuer': issuer,
        'authorization_endpoint': authentication_endpoint,
        'jwks_uri': jwks_uri,
        'token_endpoint': token_endpoint,
        'userinfo_endpoint': userinfo_endpoint,
        'registration_endpoint': registration_endpoint,
        'end_session_endpoint': end_session_endpoint,
        'scopes_supported': ['openid', 'profile'],
        'response_types_supported': ['code', 'code id_token', 'code token', 'code id_token token'],  # code and hybrid
        'response_modes_supported': ['query', 'fragment'],
        'grant_types_supported': ['authorization_code', 'implicit'],
        'subject_types_supported': ['pairwise'],
        'token_endpoint_auth_methods_supported': ['client_secret_basic'],
        'claims_parameter_supported': True
    }

    signing_key = RSAKey(key=rsa_load('signing_key.pem'), alg='RS256')
    provider = Provider(signing_key, configuration_information,
                        AuthorizationState(HashBasedSubjectIdentifierFactory(app.config['SUBJECT_ID_HASH_SALT'])),
                        {}, userinfo_ldap)

    return provider
    def setUpClass(cls):
        cls.g_config = {}
        with open("../src/config/config.json") as j:
            cls.g_config = json.load(j)

        wkh = WellKnownHandler(cls.g_config["auth_server_url"], secure=False)
        cls.__TOKEN_ENDPOINT = wkh.get(TYPE_OIDC, KEY_OIDC_TOKEN_ENDPOINT)

        _rsajwk = RSAKey(kid="RSA1",
                         key=import_rsa_key_from_file("../src/private.pem"))
        _payload = {
            "iss": cls.g_config["client_id"],
            "sub": cls.g_config["client_id"],
            "aud": cls.__TOKEN_ENDPOINT,
            "user_name": "admin",
            "jti": datetime.datetime.today().strftime('%Y%m%d%s'),
            "exp": int(time.time()) + 3600,
            "isOperator": False
        }
        _jws = JWS(_payload, alg="RS256")

        cls.jwt = _jws.sign_compact(keys=[_rsajwk])

        cls.scopes = 'protected_access'
        cls.PDP_HOST = "http://" + cls.g_config["host"]
        cls.PDP_PORT = cls.g_config["port"]
        cls.UID = cls.g_config["client_id"]
Exemple #39
0
    def get_jwt_keys(self) -> list[Key]:
        """
        Takes a provider and returns the set of keys associated with it.
        Returns a list of keys.
        """
        if self.jwt_alg == JWTAlgorithms.RS256:
            # if the user selected RS256 but didn't select a
            # CertificateKeyPair, we fall back to HS256
            if not self.rsa_key:
                Event.new(
                    EventAction.CONFIGURATION_ERROR,
                    provider=self,
                    message=
                    "Provider was configured for RS256, but no key was selected.",
                ).save()
                self.jwt_alg = JWTAlgorithms.HS256
                self.save()
            else:
                # Because the JWT Library uses python cryptodome,
                # we can't directly pass the RSAPublicKey
                # object, but have to load it ourselves
                key = import_rsa_key(self.rsa_key.key_data)
                keys = [RSAKey(key=key, kid=self.rsa_key.kid)]
                if not keys:
                    raise Exception("You must add at least one RSA Key.")
                return keys

        if self.jwt_alg == JWTAlgorithms.HS256:
            return [SYMKey(key=self.client_secret, alg=self.jwt_alg)]

        raise Exception("Unsupported key algorithm.")
Exemple #40
0
    def test_jws(self):
        keys = [
            SYMKey(key=TestMDQHandler.SYM_KEY_PHRASE, alg="HS256"),
            RSAKey(key=import_rsa_key_from_file(
                full_test_path("test_data/certs/rsa2048.pub"))),
            TestMDQHandler.EC_KEY
        ]

        # Test support for algorithms with supplied keys are working
        for alg in TestMDQHandler.SIGNING_ALGS_SUPPORTED:
            response = requests.get(
                TestMDQHandler.URL,
                params={MDQHandler.SIGNING_ALG_QUERY_PARAM: alg},
                headers=TestMDQHandler.HEADERS)

            payload = JWS().verify_compact(response.text, keys)
            assert json.loads(payload) == TestMDQHandler.METADATA_FROM_FILE[
                TestMDQHandler.CLIENT_ID]

        # Unsupported signing algorithm
        response = requests.get(
            TestMDQHandler.URL,
            params={MDQHandler.SIGNING_ALG_QUERY_PARAM: "PS256"},
            headers=TestMDQHandler.HEADERS)
        assert response.status_code == 400

        # Request signing with MDQ server without keys
        TestMDQHandler.MDQ = MDQHandler(TestMDQHandler.file_name, 36000)
        response = requests.get(
            TestMDQHandler.URL,
            params={MDQHandler.SIGNING_ALG_QUERY_PARAM: "PS256"},
            headers=TestMDQHandler.HEADERS)
        assert response.status_code == 400
Exemple #41
0
def test_pop_jwe():
    jwk = {"kty": "oct", "alg": "HS256",
           "k": "ZoRSOrFzN_FzUA5XKMYoVHyzff5oRJxl-IXRtztJ6uE"}

    encryption_keys = [RSAKey(use="enc", key=rsa,
                              kid="some-key-id")]
    jwe = JWE(json.dumps(jwk), alg="RSA-OAEP", enc="A256CBC-HS512")
    _jwe = jwe.encrypt(keys=encryption_keys, kid="some-key-id")

    jwt = {
        "iss": "https://server.example.com",
        "aud": "https://client.example.org",
        "exp": 1361398824,
        "cnf": {
            "jwe": _jwe
        }
    }

    pjwt = PJWT(**jwt)

    s = pjwt.to_json()

    de_pjwt = PJWT().from_json(s)
    assert _eq(de_pjwt.keys(), ['iss', 'aud', 'exp', 'cnf'])
    assert list(de_pjwt['cnf'].keys()) == ['jwe']
    _jwe = de_pjwt['cnf']['jwe']
    msg = jwe.decrypt(_jwe, encryption_keys)
    assert msg

    assert json.loads(msg.decode('utf8')) == jwk
Exemple #42
0
    def __call__(self):
        keyjar = self.conv.entity.keyjar
        self.conv.entity.original_keyjar = keyjar.copy()

        # invalidate the old key
        old_key_spec = self.op_args["old_key"]
        old_key = keyjar.keys_by_alg_and_usage('', old_key_spec['alg'],
                                               old_key_spec['use'])[0]
        old_key.inactive_since = time.time()

        # setup new key
        key_spec = self.op_args["new_key"]
        typ = key_spec["type"].upper()
        if typ == "RSA":
            kb = KeyBundle(keytype=typ, keyusage=key_spec["use"])
            kb.append(RSAKey(use=key_spec["use"][0]).load_key(
                RSA.generate(key_spec["bits"])))
        elif typ == "EC":
            kb = ec_init(key_spec)
        else:
            raise Unknown('keytype: {}'.format(typ))

        # add new key to keyjar with
        list(kb.keys())[0].kid = self.op_args["new_kid"]
        keyjar.add_kb("", kb)

        # make jwks and update file
        keys = []
        for kb in keyjar[""]:
            keys.extend(
                [k.to_dict() for k in list(kb.keys()) if not k.inactive_since])
        jwks = dict(keys=keys)
        with open(self.op_args["jwks_path"], "w") as f:
            f.write(json.dumps(jwks))
Exemple #43
0
    def assert_registstration_req(self, request, sign_key_str):
        split_path = request.path_url.lstrip("/").split("/")
        assert len(split_path) == 2

        jwks = split_path[1]

        # Verify signature
        public_key = import_rsa_key(private_to_public_key(sign_key_str))
        sign_key = RSAKey().load_key(public_key)
        sign_key.use = "sig"
        _jw = jws.factory(jwks)
        _jw.verify_compact(jwks, [sign_key])

        # Verify JWT
        _jwt = JWT().unpack(jwks)
        consent_args = _jwt.payload()

        assert "attr" in consent_args
        assert "redirect_endpoint" in consent_args
        assert "id" in consent_args
Exemple #44
0
def create_and_store_rsa_key_pair(name="pyoidc", path=".", size=1024):
    key = RSA.generate(size)

    keyfile = os.path.join(path, name)

    f = open("%s.key" % keyfile, "w")
    f.write(key.exportKey("PEM"))
    f.close()
    f = open("%s.pub" % keyfile, "w")
    f.write(key.publickey().exportKey("PEM"))
    f.close()

    rsa_key = RSAKey(key=key)
    rsa_key.serialize()
    # This will create JWK from the public RSA key
    jwk_spec = json.dumps(rsa_key.to_dict(), "enc")
    f = open(keyfile + ".jwk", "w")
    f.write(str(jwk_spec))
    f.close()

    return key
Exemple #45
0
def test_cmp_rsa():
    _key1 = RSAKey()
    _key1.load_key(pem_cert2rsa(CERT))

    _key2 = RSAKey()
    _key2.load_key(pem_cert2rsa(CERT))

    assert _key1 == _key2
Exemple #46
0
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
Exemple #47
0
def keybundle_from_local_file(filename, typ, usage):
    if typ.upper() == "RSA":
        kb = KeyBundle()
        k = RSAKey()
        k.load(filename)
        k.use = usage[0]
        kb.append(k)
        for use in usage[1:]:
            _k = RSAKey()
            _k.use = use
            _k.key = k.key
            kb.append(_k)
    elif typ.lower() == "jwk":
        kb = KeyBundle(source=filename, fileformat="jwk", keyusage=usage)
    else:
        raise Exception("Unsupported key type")

    return kb
    def setUp(self):
        super(OpenIdConnectTestMixin, self).setUp()
        test_root = os.path.dirname(os.path.dirname(__file__))
        self.key = RSAKey(kid='testkey').load(os.path.join(test_root, 'testkey.pem'))
        HTTPretty.register_uri(HTTPretty.GET,
          self.backend.OIDC_ENDPOINT + '/.well-known/openid-configuration',
          status=200,
          body=self.openid_config_body
        )
        oidc_config = json.loads(self.openid_config_body)

        def jwks(_request, _uri, headers):
            ks = KEYS()
            ks.add(self.key.serialize())
            return 200, headers, ks.dump_jwks()

        HTTPretty.register_uri(HTTPretty.GET,
                               oidc_config.get('jwks_uri'),
                               status=200,
                               body=jwks)
Exemple #49
0
    def __init__(self, conf, key, idp_conf, xmlsec_path):
        """
        Constructor.
        Initiates the class.
        :param conf: Specific metadata conf
        :param key: A RSA key to be used for encryption.
        :param idp_conf: idp_conf see IdpProxy/idp_conf.example.py
        :param xmlsec_path:
        :raise:
        """
        if (conf is None) or (key is None):
            raise ValueError(
                "A new instance must include a value for logger, conf and key.")

        #Key to be used for encryption.
        self.key = RSAKey(key=key)
        self.key.serialize()
        self.alg = 'RSA-OAEP'
        self.enc = 'A128CBC-HS256'

        #Used for presentation of mako files.
        self.lookup = TemplateLookup(
            directories=[CONST_STATIC_MAKO + 'templates',
                         CONST_STATIC_MAKO + 'htdocs'],
            module_directory='modules',
            input_encoding='utf-8',
            output_encoding='utf-8')

        #A list of all social services used by this IdPproxy.
        self.social_service_key_list = []
        for key in conf:
            self.social_service_key_list.append(conf[key]["name"])

        #A list of all service providers used by this sp.
        self.sp_key_list = idp_conf.metadata.service_providers()

        self.xmlsec_path = xmlsec_path
Exemple #50
0
#
# Now a JWK can be created as follow:
# - retrieve the rsa key
# - create a RSAKey object, and load the key with the load_key method
#
# A JWKS can instead be created as follow:
# - retrieve the rsa key
# - create a KEYS object and add the keys specifying the algorithm used for creation and the usage allowed for the key
#   (signature or encryption)
#
# A key jar can also be created with the method build_keyjar specifying a key_conf containing a list of keys to be
# created, with their type, name and usage (encryption of signature)

key = create_and_store_rsa_key_pair("foo", size=4096)
key2 = create_and_store_rsa_key_pair("foo2", size=4096)
rsa = RSAKey().load_key(key)

print "--- JWK ---"
print json.dumps(rsa.serialize(), sort_keys=True, indent=4, separators=(',', ': '))
print

########################################################

keys = KEYS()
keys.wrap_add(key, use="sig", kid="rsa1")
keys.wrap_add(key2, use="enc", kid="rsa1")

print "--- JWKS---"
print keys.dump_jwks()
print
Exemple #51
0
def test_verify_2():
    _key = RSAKey()
    _key.load_key(pem_cert2rsa(CERT))
    assert _key.verify()
Exemple #52
0
import json
import os
import argparse
from jwkest.jwk import RSAKey, rsa_load, dump_jwks

__author__ = 'rolandh'

parser = argparse.ArgumentParser()
parser.add_argument('-n', dest="name", default="pyoidc",
                    help="file names")
parser.add_argument('-p', dest="path", default=".",
                    help="Path to the directory for the files")
parser.add_argument('-k', dest="key", help="Key file")

args = parser.parse_args()

key = rsa_load(args.key)
rsa_key = RSAKey(key=key)
rsa_key.serialize()

# This will create JWK from the public RSA key
jwk_spec = json.dumps(rsa_key.to_dict(), "enc")

keyfile = os.path.join(args.path, args.name)

_out = dump_jwks([{"key":key, "use":"enc"}])

f = open(keyfile + ".jwk", "w")
f.write(_out)
f.close()
Exemple #53
0
    parser.add_argument("message", nargs="?", help="The message to encrypt")

    args = parser.parse_args()

    keys = {}
    if args.jwk_url:
        keys = load_jwks_from_url(args.jwk_url)
    elif args.jwk_file:
        keys = load_jwks(open(args.jwk_file).read())
    elif args.x509_url:
        keys = load_x509_cert(args.x509_url, {})
    elif args.x509_file:
        keys = [import_rsa_key_from_file(args.x509_file)]
    elif args.rsa_file:
        key = rsa_load(args.rsa_file)
        rsa_key = RSAKey(key=key)
        rsa_key.serialize()
        keys = [rsa_key]
    else:
        print("Needs encryption key")
        exit()

    if args.file:
        msg = open(args.file).read()
        msg = msg.strip("\n\r")
    else:
        msg = args.message

    jwe = JWE()
    print(jwe.decrypt(msg, keys))
Exemple #54
0
    args = parser.parse_args()

    keys = {}
    if args.jwk_url:
        keys = load_jwks_from_url(args.jwk_url, {})
    elif args.jwk_file:
        keys = load_jwks(open(args.jwk_file).read())
    elif args.x509_url:
        # load_x509_cert returns list of 2-tuples
        keys = [RSAKey(key=x) for x, y in load_x509_cert(lrequest,
                                                         args.x509_url)]
        for key in keys:
            key.serialize()
    elif args.x509_file:
        # import_rsa_key_from_file returns RSA key instance
        _key = RSAKey(key=import_rsa_key_from_file(args.x509_file))
        _key.serialize()
        keys = [_key]
    elif args.rsa_file:
        _key = RSAKey(key=rsa_load(args.rsa_file))
        _key.serialize()
        keys = [_key]
    else:
        print >> sys.stderr, "Needs encryption key"
        exit()

    if not args.enc or not args.alg:
        print >> sys.stderr, "There are no default encryption methods"
        exit()

    if args.enc not in SUPPORTED["enc"]:
Exemple #55
0
class MetadataGeneration(object):

    def __init__(self, conf, key, idp_conf, xmlsec_path):
        """
        Constructor.
        Initiates the class.
        :param conf: Specific metadata conf
        :param key: A RSA key to be used for encryption.
        :param idp_conf: idp_conf see IdpProxy/idp_conf.example.py
        :param xmlsec_path:
        :raise:
        """
        if (conf is None) or (key is None):
            raise ValueError(
                "A new instance must include a value for logger, conf and key.")

        #Key to be used for encryption.
        self.key = RSAKey(key=key)
        self.key.serialize()
        self.alg = 'RSA-OAEP'
        self.enc = 'A128CBC-HS256'

        #Used for presentation of mako files.
        self.lookup = TemplateLookup(
            directories=[CONST_STATIC_MAKO + 'templates',
                         CONST_STATIC_MAKO + 'htdocs'],
            module_directory='modules',
            input_encoding='utf-8',
            output_encoding='utf-8')

        #A list of all social services used by this IdPproxy.
        self.social_service_key_list = []
        for key in conf:
            self.social_service_key_list.append(conf[key]["name"])

        #A list of all service providers used by this sp.
        self.sp_key_list = idp_conf.metadata.service_providers()

        self.xmlsec_path = xmlsec_path

    @staticmethod
    def verify_handle_request(path):
        """
        Verifies if the given path should be handled by this class.
        :param path: A path.
        :return: True if the path should be handled by this class, otherwise
            false.
        """
        return re.match(CONST_METADATA + ".*", path)

    @staticmethod
    def get_query_dict(environ):
        """
        Retrieves a dictionary with query parameters.
        :param environ: The wsgi enviroment.
        :return: A dictionary with query parameters.
        """
        qs = {}
        query = environ.get("QUERY_STRING", "")
        if not query:
            post_env = environ.copy()
            post_env['QUERY_STRING'] = ''
            query = cgi.FieldStorage(fp=environ['wsgi.input'], environ=post_env,
                                     keep_blank_values=True)
            if query is not None:
                try:
                    for item in query:
                        qs[query[item].name] = query[item].value
                except:
                    qs[CONST_BODY] = query.file.read()

        else:
            qs = dict((k, v if len(v) > 1 else v[0]) for k, v in
                      parse_qs(query).iteritems())
        return qs

    def handle_request(self, environ, start_response, path):
        """
        Call this method from the wsgi application.
        Handles the request if the path matched by verify_handle_request
        and any static file or CONST_METADATA or CONST_METADATASAVE.

        :param environ: wsgi enviroment
        :param start_response: the start response
        :param path: the requested path
        :return: a response fitted for wsgi application.
        """
        try:
            if path == CONST_METADATA:
                return self.handle_metadata(environ, start_response)
            elif path == CONST_METADATAVERIFY:
                return self.handle_metadata_verify(environ, start_response)
            elif path == CONST_METADATAVERIFYJSON:
                return self.handle_metadata_verify_json(
                    environ, start_response, self.get_query_dict(environ))
            elif path == CONST_METADATASAVE:
                return self.handle_metadata_save(environ, start_response,
                                                 self.get_query_dict(environ))
            else:
                filename = CONST_STATIC_FILE + self.get_static_file_name(path)
                if self.verify_static(filename):
                    return self.handle_static(environ, start_response, filename)
                else:
                    return self.handle_static(environ, start_response,
                                              CONST_UNKNOWFILE)
        except Exception:
            _logger.fatal('Unknown error in handle_request.', exc_info=True)
            return self.handle_static(environ, start_response,
                                      CONST_UNKNOWERROR)

    def get_static_file_name(self, path):
        """
        Parses out the static file name from the path.
        :param path: The requested path.
        :return: The static file name.
        """
        if self.verify_handle_request(path):
            try:
                return path[len(CONST_METADATA) + 1:]
            except:
                pass
        return ""

    @staticmethod
    def verify_static(filename):
        """
        Verifies if a static file exists in the folder
        IdPproxy/src/idpproxy/metadata/files/static

        :param filename: The name of the file.
        :return: True if the file exists, otherwise false.
        """
        try:
            with open(filename):
                pass
        except IOError:
            return False
        return True

    def handle_metadata(self, environ, start_response):
        """
        Creates the response for the first page in the metadata generation.
        :param environ: wsgi enviroment
        :param start_response: wsgi start respons
        :return: wsgi response for the mako file metadata.mako.
        """
        resp = Response(mako_template="metadata.mako",
                        template_lookup=self.lookup, headers=[])

        argv = {
            "action": CONST_METADATASAVE,
            "sociallist": sorted(self.social_service_key_list),
            "spKeyList": sorted(self.sp_key_list),
            "verify": CONST_METADATAVERIFY,
        }
        return resp(environ, start_response, **argv)

    def handle_metadata_save(self, environ, start_response, qs):
        """
        Takes the input for the page metadata.mako.
        Encrypts entity id and secret information for the social services.
        Creates the partial xml to be added to the metadata for the service
        provider.
        :param environ: wsgi enviroment
        :param start_response: wsgi start respons
        :param qs: Query parameters in a dictionary.
        :return: wsgi response for the mako file metadatasave.mako.
        """
        resp = Response(mako_template="metadatasave.mako",
                        template_lookup=self.lookup,
                        headers=[])
        if "entityId" not in qs or "secret" not in qs:
            xml = ("Xml could not be generated because no entityId or secret"
                   "has been sent to the service.")
            _logger.warning(xml)
        else:
            try:
                secret_data = json.dumps({"entityId": json.loads(qs["entityId"]),
                                          "secret": json.loads(qs["secret"])})

                # create a JWE
                jwe = JWE(secret_data, alg=self.alg, enc=self.enc)
                secret_data_encrypted = jwe.encrypt([self.key])

                val = AttributeValue()
                val.set_text(secret_data_encrypted)
                attr = Attribute(
                    name_format=NAME_FORMAT_URI,
                    name="http://social2saml.nordu.net/customer",
                    attribute_value=[val])
                eattr = mdattr.EntityAttributes(attribute=[attr])
                nspair = {
                    "mdattr": "urn:oasis:names:tc:SAML:metadata:attribute",
                    "samla": "urn:oasis:names:tc:SAML:2.0:assertion",
                }
                xml = eattr.to_string(nspair)
                xml_list = xml.split("\n", 1)

                if len(xml_list) == 2:
                    xml = xml_list[1]

            except Exception:
                _logger.fatal('Unknown error in handle_metadata_save.',
                                  exc_info=True)
                xml = "Xml could not be generated."
        argv = {
            "home": CONST_METADATA,
            "action": CONST_METADATAVERIFY,
            "xml": xml
        }
        return resp(environ, start_response, **argv)

    def handle_metadata_verify(self, environ, start_response):
        """
        Will show the page for metadata verification (metadataverify.mako).
        :param environ: wsgi enviroment
        :param start_response: wsgi start respons
        :return: wsgi response for the mako file metadatasave.mako.
        """
        resp = Response(mako_template="metadataverify.mako",
                        template_lookup=self.lookup,
                        headers=[])
        argv = {
            "home": CONST_METADATA,
            "action": CONST_METADATAVERIFYJSON
        }
        return resp(environ, start_response, **argv)

    def handle_metadata_verify_json(self, environ, start_response, qs):
        """
        Handles JSON metadata verifications.
        The post body must contains a JSON message like
        { 'xml' : 'a metadata file'}

        :param environ: wsgi enviroment
        :param start_response: wsgi start respons
        :param qs: Query parameters in a dictionary.
        :return: wsgi response contaning a JSON response. The JSON message will
            contain the parameter ok and services.
            ok will contain true if the metadata file can be parsed, otherwise
            false.
            services will contain a list of all the service names contained in
            the metadata file.
        """
        ok = False
        services = "[]"
        try:
            if CONST_BODY in qs:
                json_message = json.loads(qs[CONST_BODY])
                if "xml" in json_message:
                    xml = json_message["xml"]
                    xml = xml.strip()
                    metadata_ok = False
                    ci = None
                    mds = MetadataStore(
                        CONST_ONTS.values(), CONST_ATTRCONV,
                        self.xmlsec_path,
                        disable_ssl_certificate_validation=True)

                    _md = MetaData(CONST_ONTS.values(), CONST_ATTRCONV,
                                  metadata=xml)
                    try:
                        _md.load()
                    except:
                        _logger.info(
                            'Could not parse the metadata file in handleMetadataVerifyJSON.',
                            exc_info=True)
                    else:
                        entity_id = _md.entity.keys()[0]
                        mds.metadata[entity_id] = _md
                        args = {"metad": mds, "dkeys": [self.key]}
                        ci = utils.ConsumerInfo(['metadata'], **args)
                        metadata_ok = True

                    services = "["
                    first = True
                    if ci is not None:
                        for item in ci.info:
                            if item.ava is not None and entity_id in item.ava:
                                for social in item.ava[entity_id]:
                                    if not first:
                                        services += ","
                                    else:
                                        first = False
                                    services += '"' + social + '"'
                    services += "]"
                    if metadata_ok:
                        ok = True
        except:
            _logger.fatal('Unknown error in handleMetadataVerifyJSON.',
                              exc_info=True)
        resp = Response('{"ok":"' + str(ok) + '", "services":' + services + '}',
                        headers=[('Content-Type', CONST_TYPEJSON)])
        return resp(environ, start_response)

    @staticmethod
    def handle_static(environ, start_response, path):
        """
        Creates a response for a static file.
        :param environ: wsgi enviroment
        :param start_response: wsgi start response
        :param path: the static file and path to the file.
        :return: wsgi response for the static file.
        """
        try:
            text = open(path).read()
            if path.endswith(".ico"):
                resp = Response(text,
                                headers=[('Content-Type', "image/x-icon")])
            elif path.endswith(".html"):
                resp = Response(text, headers=[('Content-Type', 'text/html')])
            elif path.endswith(".txt"):
                resp = Response(text, headers=[('Content-Type', 'text/plain')])
            elif path.endswith(".css"):
                resp = Response(text, headers=[('Content-Type', 'text/css')])
            else:
                resp = Response(text, headers=[('Content-Type', 'text/xml')])
        except IOError:
            resp = NotFound()
        return resp(environ, start_response)
Exemple #56
0
 def serialize_rsa_key(key):
     kid = hashlib.md5(key.encode('utf-8')).hexdigest()
     key = RSAKey(kid=kid, key=RSA.importKey(key), use='sig', alg='RS512')
     return key.serialize(private=False)
Exemple #57
0
def setup_server_env(proxy_conf, conf_mod, key):
    """

    :param proxy_conf:
    :param conf_mod:
    :param key: RSA key
    :return:
    """
    global SERVER_ENV
    global logger
    #noinspection PyUnboundLocalVariable
    SERVER_ENV = dict([(k, v) for k, v in proxy_conf.__dict__.items()
                       if not k.startswith("__")])

    SERVER_ENV["sessions"] = {}

    SERVER_ENV["eptid"] = EptidShelve(proxy_conf.SECRET, proxy_conf.EPTID_DB)

    _idp = server.Server(conf_mod)

    if key:
        rsa_key = RSAKey(key=key)
        rsa_key.serialize()
    else:
        rsa_key = None

    args = {"metad": _idp.metadata, "dkeys": [rsa_key]}

    SERVER_ENV["consumer_info"] = utils.ConsumerInfo(proxy_conf.CONSUMER_INFO,
                                                     **args)
    SERVER_ENV["service"] = proxy_conf.SERVICE

    # add the service endpoints
    part = urlparse.urlparse(_idp.config.entityid)
    base = "%s://%s/" % (part.scheme, part.netloc)
    SERVER_ENV["SCHEME"] = part.scheme
    try:
        (host, port) = part.netloc.split(":")
        port = int(port)
    except ValueError:  # no port specification
        host = part.netloc
        if part.scheme == "http":
            port = 80
        elif part.scheme == "https":
            port = 443
        else:
            raise ValueError("Unsupported scheme")

    SERVER_ENV["HOST"] = host
    SERVER_ENV["PORT"] = port

    endpoints = {"single_sign_on_service": [], "single_logout_service": []}
    for key, _dict in proxy_conf.SERVICE.items():
        _sso = _dict["saml_endpoint"]
        endpoints["single_sign_on_service"].append("%s%s" % (base, _sso))
        endpoints["single_logout_service"].append(("%s%s/logout" % (base, _sso),
                                                   BINDING_HTTP_REDIRECT))

    _idp.config.setattr("idp", "endpoints", endpoints)

    SERVER_ENV["idp"] = _idp
    SERVER_ENV["template_lookup"] = LOOKUP
    SERVER_ENV["sid_generator"] = session_nr()
    SERVER_ENV["base_url"] = base
    SERVER_ENV["STATIC_DIR"] = proxy_conf.STATIC_DIR
    SERVER_ENV["METADATA_DIR"] = proxy_conf.METADATA_DIR
    SERVER_ENV["SIGN"] = proxy_conf.SIGN

    #print SERVER_ENV
    if proxy_conf.CACHE == "memory":
        SERVER_ENV["CACHE"] = cache.Cache(SERVER_ENV["SERVER_NAME"],
                                          SERVER_ENV["SECRET"])
    elif proxy_conf.CACHE.startswith("file:"):
        SERVER_ENV["CACHE"] = cache.Cache(SERVER_ENV["SERVER_NAME"],
                                          SERVER_ENV["SECRET"],
                                          filename=proxy_conf.CACHE[5:])

    logger = setup_logger(_idp.config)
    if proxy_conf.DEBUG:
        logger.setLevel(logging.DEBUG)

    logger.debug("SERVER_ENV: %s" % SERVER_ENV)
    return _idp
Exemple #58
0
def vetting_result():
    if not current_app.config.get('FREJA_CALLBACK_X5T_CERT'):
        current_app.logger.info('Configuration error: FREJA_CALLBACK_X5T_CERT is not set')
        return make_response('Configuration error', 500)

    _freja_callback_x5t_cert = current_app.config.get('FREJA_CALLBACK_X5T_CERT')
    _freja_callback_x5t_pub_key = RSA.importKey(_freja_callback_x5t_cert)
    _freja_callback_rsa_pub_key = RSAKey()
    _freja_callback_rsa_pub_key.load_key(_freja_callback_x5t_pub_key)

    current_app.logger.debug('flask.request.headers: \'{!s}\''.format(flask.request.headers))
    current_app.logger.debug('flask.request.data: \'{!s}\''.format(flask.request.get_data()))

    try:
        if flask.request.headers['Content-Type'] == 'application/jose':
            current_app.logger.info('Received a callback with MIME application/jose')
        else:
            current_app.logger.info('Received a callback with an invalid MIME: \'{!s}\''
                                    .format(flask.request.headers['Content-Type']))
            return make_response('Invalid MIME', 400)
    except KeyError:
        current_app.logger.info('Received a callback without a MIME')
        return make_response('No MIME specified', 400)

    try:
        data = flask.request.get_json(force=True)
    except:
        current_app.logger.info('Invalid verisec callback: missing or invalid JSON')
        return make_response('missing or invalid JSON', 400)

    if not data:
        current_app.logger.info('Invalid verisec callback: no JSON data provided')
        return make_response('Missing JSON data', 400)

    ia_response_data = data.get('iaResponseData')

    if not ia_response_data:
        current_app.logger.info('Missing iaResponseData in verisec callback: \'{!s}\''.format(data))
        return make_response('Missing iaResponseData', 400)

    current_app.logger.info('Received verisec iaResponseData: \'{!s}\''.format(ia_response_data))

    jws_parts = ia_response_data.split('.')

    # A correctly formatted JWS is made up of 3 parts
    if len(jws_parts) != 3:
        current_app.logger.info('iaResponseData response doesn\'t seems to be a JWS')
        return make_response('iaResponseData is not a JWS', 400)

    # This is for testing only and therefore we do not verify the JWS yet
    unverified_header = jws_parts[0]
    unverified_payload = jws_parts[1]

    # It should be possible to base64 decode the header and payload
    try:
        # urlsafe_b64decode returns bytes object so we decode to get str aka utf8
        unverified_header_decoded = urlsafe_b64decode(unverified_header + '=' * (4 - (len(unverified_header) % 4))).decode('utf8')
        unverified_payload_decoded = urlsafe_b64decode(unverified_payload + '=' * (4 - (len(unverified_payload) % 4))).decode('utf8')
    except UnicodeDecodeError:
        current_app.logger.info('Couldn\'t urlsafe_b64decode iaResponseData because it contains invalid UTF-8')
        return make_response('Incorrect UTF-8 in iaResponseData', 400)
    except binascii.Error:
        current_app.logger.info('Couldn\'t urlsafe_b64decode iaResponseData because non-base64 digit found')
        return make_response('Incorrect UTF-8 in iaResponseData', 400)
    except TypeError:
        current_app.logger.info('Couldn\'t urlsafe_b64decode iaResponseData')
        return make_response('Incorrect base64 encoded iaResponseData', 400)

    try:
        json.loads(unverified_header_decoded)
        json.loads(unverified_payload_decoded)
    except JSONDecodeError:
        current_app.logger.info('Incorrect UTF-8 BOM or invalid JSON data from base64 decoded iaResponseData')
        return make_response('Incorrectly encoded JSON in base64 decoded iaResponseData', 400)
    except TypeError:
        current_app.logger.info('JSON in base64 decoded iaResponseData is not str')
        return make_response('Incorrectly encoded JSON in base64 decoded iaResponseData', 400)

    try:
        verified_payload = JWS().verify_compact(ia_response_data, keys=[_freja_callback_rsa_pub_key], sigalg='RS256')
    except BadSignature as e:
        current_app.logger.info('The JWS was not properly signed')
        return make_response('Invalid signature', 400)
    except Exception as e:
        current_app.logger.info(str(e))
        return make_response('Invalid JWS', 400)

    try:
        verified_payload_country = verified_payload.pop('country')
        verified_payload_opaque = verified_payload.pop('opaque') # The opaque contains nonce and token
        verified_payload_ref = verified_payload.pop('ref')
        verified_payload_ssn = verified_payload.pop('ssn')
    except KeyError:
        current_app.logger.info('The verified JWS payload is missing some required claims')
        return make_response('The verified JWS payload is missing some required claims', 400)

    # Make sure that we have processed all claims in the payload
    if len(verified_payload) == 0:

        try:
            verified_opaque_deserialized = parse_opaque_data(verified_payload_opaque)
        except InvalidOpaqueDataError as e:
            # This is by design since we want the message from this exception
            return make_response(str(e), 400)

        if verified_opaque_deserialized['nonce'] == current_app.config.get('FREJA_TEST_NONCE', None):
            current_app.logger.info('Received a valid callback-test')
            return make_response('OK', 200)

        auth_req_data = current_app.authn_requests.pop(verified_opaque_deserialized['nonce'], None)
        if not auth_req_data:
            current_app.logger.info('Unknown nonce in verified JWS payload: \'{!s}\''.format(verified_opaque_deserialized['nonce']))
            return make_response('Unknown nonce in verified JWS payload', 400)

        user_id = str(uuid.uuid4())

        current_app.users[user_id] = {
            'results': {
                'freja_eid': {
                    'vetting_time': time.time(),
                    'country': verified_payload_country,
                    'opaque': verified_payload_opaque,
                    'ref': verified_payload_ref,
                    'ssn': verified_payload_ssn,
                }
            }
        }

        auth_req = AuthorizationRequest(**auth_req_data)
        authn_response = create_authentication_response(auth_req=auth_req,
                                                        user_id=user_id,
                                                        extra_userinfo=extra_userinfo)

        response_url = authn_response.request(auth_req['redirect_uri'], should_fragment_encode(auth_req))
        headers = {'Authorization': 'Bearer {}'.format(verified_opaque_deserialized['token'])}
        current_app.authn_response_queue.enqueue(deliver_response_task, response_url, headers=headers)

        return make_response('OK', 200)

    current_app.logger.info('Received an invalid verisec callback')
    return make_response('Invalid request', 400)