def pick_key(keys, use, alg='', key_type='', kid=''): """ :param keys: List of keys :param use: What the key is going to be used for :param alg: crypto algorithm :param key_type: Type of key :param kid: Ley ID :return: list of keys that match the pattern """ res = [] if not key_type: if use == 'sig': key_type = jws.alg2keytype(alg) else: key_type = jwe.alg2keytype(alg) for key in keys: if key.use != use: continue if key.kty == key_type: if key.alg == '' or alg == '' or key.alg == alg: if key.kid == '' or kid == '' or key.kid == kid: res.append(key) return res
def get_jwt_keys(jwt, keys, use): try: if use == 'sig': _key_type = jws.alg2keytype(jwt.headers['alg']) else: _key_type = jwe.alg2keytype(jwt.headers['alg']) except KeyError: _key_type = '' try: _kid = jwt.headers['kid'] except KeyError: _kid = '' # Unknown # pick issuer keys if use == 'sig': payload = json.loads(as_unicode(jwt.part[1])) try: _keys = keys[payload['iss']] except KeyError: # No issuer, not kosher raise MissingValue('iss') if not _kid: try: _kid = payload['kid'] except KeyError: _kid = '' # Unknown else: _keys = keys return pick_key(_keys, use, key_type=_key_type, kid=_kid)
def store_signed_jwks(keyjar, sign_keyjar, path, alg, iss=''): _jwks = keyjar.export_jwks() _jws = JWS(_jwks, alg=alg) _jwt = _jws.sign_compact( sign_keyjar.get_signing_key(owner=iss, key_type=alg2keytype(alg))) fp = open(path, 'w') fp.write(_jwt) fp.close()
def get_signing_key(self, algorithm, cli_info): """ Pick signing key based on signing algorithm to be used :param algorithm: Signing algorithm :param cli_info: A :py:class:`oiccli.client_info.ClientInfo` instance :return: A key """ return cli_info.keyjar.get_signing_key( alg2keytype(algorithm), alg=algorithm)
def verify(self, areq, **kwargs): try: try: argv = {'sender': areq['client_id']} except KeyError: argv = {} bjwt = AuthnToken().from_jwt(areq["client_assertion"], keyjar=self.srv.keyjar, **argv) except (Invalid, MissingKey) as err: logger.info("%s" % sanitize(err)) raise AuthnFailure("Could not verify client_assertion.") logger.debug("authntoken: %s" % sanitize(bjwt.to_dict())) areq['parsed_client_assertion'] = bjwt # logger.debug("known clients: %s" % sanitize(self.cli.cdb.keys())) try: cid = kwargs["client_id"] except KeyError: cid = bjwt["iss"] try: # There might not be a client_id in the request assert str(cid) in self.srv.cdb # It's a client I know except KeyError: pass # aud can be a string or a list _aud = bjwt["aud"] logger.debug("audience: %s, baseurl: %s" % (_aud, self.srv.baseurl)) # figure out authn method if alg2keytype(bjwt.jws_header['alg']) == 'oct': # Symmetric key authn_method = 'client_secret_jwt' else: authn_method = 'private_key_jwt' try: if isinstance(_aud, six.string_types): assert str(_aud).startswith(self.srv.baseurl) else: for target in _aud: if target.startswith(self.srv.baseurl): return cid, authn_method raise NotForMe("Not for me!") except AssertionError: raise NotForMe("Not for me!") return cid, authn_method
def get_key_by_kid(self, kid, algorithm, cli_info): """ Pick a key that matches a given key ID and signing algorithm. :param kid: Key ID :param algorithm: Signing algorithm :param cli_info: A :py:class:`oiccli.client_info.ClientInfo` instance :return: A matching key """ _key = cli_info.keyjar.get_key_by_kid(kid) if _key: ktype = alg2keytype(algorithm) if _key.kty != ktype: raise NoMatchingKey("Wrong key type") else: return _key else: raise NoMatchingKey("No key with kid:%s" % kid)
def oic_post_construct(self, cli_info, req, **kwargs): if 'openid' in req['scope']: _response_type = req['response_type'][0] if 'id_token' in _response_type or 'code' in _response_type: if 'nonce' not in req: _nonce = rndstr(32) req['nonce'] = _nonce cli_info.state_db.bind_nonce_to_state(_nonce, req['state']) try: _request_param = kwargs['request_param'] except KeyError: return req else: del kwargs['request_param'] alg = None for arg in ["request_object_signing_alg", "algorithm"]: try: # Trumps everything alg = kwargs[arg] except KeyError: pass else: break if not alg: try: alg = cli_info.behaviour["request_object_signing_alg"] except KeyError: # Use default alg = "RS256" kwargs["request_object_signing_alg"] = alg if "keys" not in kwargs and alg and alg != "none": _kty = jws.alg2keytype(alg) try: _kid = kwargs["sig_kid"] except KeyError: _kid = cli_info.kid["sig"].get(_kty, None) kwargs["keys"] = cli_info.keyjar.get_signing_key(_kty, kid=_kid) _req = make_openid_request(req, **kwargs) # Should the request be encrypted _req = request_object_encryption(_req, cli_info, **kwargs) if _request_param == "request": req["request"] = _req else: try: _webname = cli_info.registration_response['request_uris'][ 0] filename = cli_info.filename_from_webname(_webname) except KeyError: filename, _webname = construct_request_uri(**kwargs) fid = open(filename, mode="w") fid.write(_req) fid.close() req["request_uri"] = _webname return req
def get_signing_key(self, algorithm, cli_info=None): return cli_info.keyjar.get_signing_key( alg2keytype(algorithm), "", alg=algorithm)
def construct(self, request, cli_info=None, http_args=None, **kwargs): """ Constructs a client assertion and signs it with a key. The request is modified as a side effect. :param request: The request :param cli_info: A :py:class:`oiccli.client_info.ClientInfo` instance :param http_args: HTTP arguments :param kwargs: Extra arguments :return: Constructed HTTP arguments, in this case none """ if 'client_assertion' in kwargs: request["client_assertion"] = kwargs['client_assertion'] if 'client_assertion_type' in kwargs: request[ 'client_assertion_type'] = kwargs['client_assertion_type'] else: request["client_assertion_type"] = JWT_BEARER elif 'client_assertion' in request: if 'client_assertion_type' not in request: request["client_assertion_type"] = JWT_BEARER else: algorithm = None # audience for the signed JWT depends on which endpoint # we're talking to. if kwargs['authn_endpoint'] in ['token', 'refresh']: try: algorithm = cli_info.registration_info[ 'token_endpoint_auth_signing_alg'] except (KeyError, AttributeError): pass audience = cli_info.provider_info['token_endpoint'] else: audience = cli_info.provider_info['issuer'] if not algorithm: algorithm = self.choose_algorithm(**kwargs) ktype = alg2keytype(algorithm) try: if 'kid' in kwargs: signing_key = [self.get_key_by_kid(kwargs["kid"], algorithm, cli_info)] elif ktype in cli_info.kid["sig"]: try: signing_key = [self.get_key_by_kid( cli_info.kid["sig"][ktype], algorithm, cli_info)] except KeyError: signing_key = self.get_signing_key(algorithm, cli_info) else: signing_key = self.get_signing_key(algorithm, cli_info) except NoMatchingKey as err: logger.error("%s" % sanitize(err)) raise try: _args = {'lifetime': kwargs['lifetime']} except KeyError: _args = {} # construct the signed JWT with the assertions and add # it as value to the 'client_assertion' claim of the request request["client_assertion"] = assertion_jwt( cli_info.client_id, signing_key, audience, algorithm, **_args) request["client_assertion_type"] = JWT_BEARER try: del request["client_secret"] except KeyError: pass # If client_id is not required to be present, remove it. if not request.c_param["client_id"][VREQUIRED]: try: del request["client_id"] except KeyError: pass return {}