def test_jws_1(): msg = {"iss": "joe", "exp": 1300819380, "http://example.com/is_root": True} key = SYMKey(key=jwkest.intarr2bin(HMAC_KEY)) _jws = JWS(msg, cty="JWT", alg="HS256", jwk=key.to_dict()) res = _jws.sign_compact() _jws2 = JWS(alg="HS256") _jws2.verify_compact(res, keys=[key]) assert _jws2.msg == msg
def test_jws_1(): msg = {"iss": "joe", "exp": 1300819380, "http://example.com/is_root": True} key = SYMKey(key=jwkest.intarr2bin(HMAC_KEY)) _jws = JWS(msg, cty="JWT", alg="HS256", jwk=key.serialize()) res = _jws.sign_compact() _jws2 = JWS(alg="HS256") _jws2.verify_compact(res, keys=[key]) assert _jws2.msg == msg
def test_relay_state(self): key = SYMKey(key=rndstr(32), kid="1") key.serialize() payload = {"state": "STATE", "nonce": "NONCE"} _state = construct_state(payload, key) transformed = deconstruct_state(_state, [key]) assert transformed == payload
def test_jws_1(): msg = {"iss": "joe", "exp": 1300819380, "http://example.com/is_root": True} jwk = SYMKey(key=jwkest.intarr2bin(HMAC_KEY)) _jws = JWS(msg, cty="JWT", alg="HS256", jwk=json.dumps(jwk.to_dict())) res = _jws.sign_compact() _jws2 = JWS(alg="HS256") _jws2.verify_compact(res) assert _jws2.msg == msg
def test_jws_1(): msg = {"iss": "joe", "exp": 1300819380, "http://example.com/is_root": True} jwk = SYMKey(key=jwkest.intarr2bin(HMAC_KEY)) jwk.serialize() _jws = JWS(msg, cty="JWT", alg="HS256", jwk=json.dumps(jwk.to_dict())) res = _jws.sign_compact() _jws2 = JWS() _jws2.verify_compact(res) assert _jws2.msg == msg
def test_encryption_key(): sk = SYMKey(key='df34db91c16613deba460752522d28f6ebc8a73d0d9185836270c26b') _enc = sk.encryption_key(alg='A128KW') _v = as_unicode(b64e(_enc)) assert _v == 'xCo9VhtommCTGMWi-RyWBw' sk = SYMKey(key='df34db91c16613deba460752522d28f6ebc8a73d0d9185836270c26b') _enc = sk.encryption_key(alg='A192KW') _v = as_unicode(b64e(_enc)) assert _v == 'xCo9VhtommCTGMWi-RyWB14GQqHAGC86' sk = SYMKey(key='df34db91c16613deba460752522d28f6ebc8a73d0d9185836270c26b') _enc = sk.encryption_key(alg='A256KW') _v = as_unicode(b64e(_enc)) assert _v == 'xCo9VhtommCTGMWi-RyWB14GQqHAGC86vweU_Pi62X8' ek = sha256_digest( 'YzE0MjgzNmRlODI5Yzg2MGYyZTRjNGE0NTZlMzBkZDRiNzJkNDA5MzUzNjM0ODkzM2E2MDk3ZWY')[ :16] assert as_unicode(b64e(ek)) == 'yf_UUkAFZ8Pn_prxPPgu9w' sk = SYMKey( key='YzE0MjgzNmRlODI5Yzg2MGYyZTRjNGE0NTZlMzBkZDRiNzJkNDA5MzUzNjM0ODkzM2E2MDk3ZWY') _enc = sk.encryption_key(alg='A128KW') _v = as_unicode(b64e(_enc)) assert _v == as_unicode(b64e(ek))
def test_faulty_idtoken(self): _now = time_util.utc_time_sans_frac() idval = { "nonce": "KUEYfRM2VzKDaaKD", "sub": "EndUserSubject", "iss": "https://alpha.cloud.nds.rub.de", "exp": _now + 3600, "iat": _now, "aud": "TestClient", } idts = IdToken(**idval) key = SYMKey(key="TestPassword") _signed_jwt = idts.to_jwt(key=[key], algorithm="HS256") # Mess with the signed id_token p = _signed_jwt.split(".") p[2] = "aaa" _faulty_signed_jwt = ".".join(p) _info = { "access_token": "accessTok", "id_token": _faulty_signed_jwt, "token_type": "Bearer", "expires_in": 3600, } at = AccessTokenResponse(**_info) with pytest.raises(BadSignature): at.verify(key=[key])
def create_jwt(self, user): """ Creates a signed (JWS) ID token. Returns: str: JWS """ key = SYMKey(key=self.site.siteconfiguration. oauth_settings['SOCIAL_AUTH_EDX_OAUTH2_SECRET']) now = datetime.datetime.utcnow() expiration_datetime = now + datetime.timedelta(seconds=3600) issue_datetime = now payload = { 'iss': self.site.siteconfiguration.lms_url_root, 'administrator': False, 'iat': timegm(issue_datetime.utctimetuple()), 'sub': str(uuid.uuid4()), 'preferred_username': user.username, 'aud': self.site.siteconfiguration. oauth_settings['SOCIAL_AUTH_EDX_OAUTH2_KEY'], 'exp': timegm(expiration_datetime.utctimetuple()), } access_token = JWS(payload, jwk=key, alg='HS512').sign_compact() return access_token
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.")
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
def prepare_access_token_body(self, client_key=None, tamper_message=False, expiration_datetime=None, issue_datetime=None, nonce=None, issuer=None): body = {'access_token': 'foobar', 'token_type': 'bearer'} client_key = client_key or self.client_key now = datetime.datetime.utcnow() expiration_datetime = expiration_datetime or ( now + datetime.timedelta(seconds=30)) issue_datetime = issue_datetime or now id_token = self.get_id_token( client_key, timegm(expiration_datetime.utctimetuple()), timegm(issue_datetime.utctimetuple())) key = SYMKey(key=self.jwt_secret, alg='HS256') body['access_token'] = JWS(id_token, jwk=key, alg='HS256').sign_compact() if tamper_message: header, msg, sig = body['id_token'].split('.') id_token['sub'] = '1235' msg = b64encode_item(id_token).decode('utf-8') body['access_token'] = '.'.join([header, msg, sig]) return json.dumps(body)
def test_client_secret_jwt(self, client): client.token_endpoint = "https://example.com/token" client.provider_info = { 'issuer': 'https://example.com/', 'token_endpoint': "https://example.com/token" } csj = ClientSecretJWT(client) cis = AccessTokenRequest() csj.construct(cis, algorithm="HS256", authn_endpoint='userinfo') assert cis["client_assertion_type"] == JWT_BEARER assert "client_assertion" in cis cas = cis["client_assertion"] _jwt = JWT().unpack(cas) jso = _jwt.payload() assert _eq(jso.keys(), ["aud", "iss", "sub", "jti", "exp", "iat"]) assert _jwt.headers == {'alg': 'HS256'} _rj = JWS() info = _rj.verify_compact( cas, [SYMKey(k=b64e(as_bytes(client.client_secret)))]) assert _eq(info.keys(), ["aud", "iss", "sub", "jti", "exp", "iat"]) assert info['aud'] == [client.provider_info['issuer']]
def test_faulty_idtoken(self): idval = { 'nonce': 'KUEYfRM2VzKDaaKD', 'sub': 'EndUserSubject', 'iss': 'https://alpha.cloud.nds.rub.de', 'aud': 'TestClient' } idts = IdToken(**idval) key = SYMKey(key="TestPassword") _signed_jwt = idts.to_jwt(key=[key], algorithm="HS256", lifetime=300) # Mess with the signed id_token p = _signed_jwt.split(".") p[2] = "aaa" _faulty_signed_jwt = ".".join(p) _info = { "access_token": "accessTok", "id_token": _faulty_signed_jwt, "token_type": "Bearer", "expires_in": 3600 } at = AccessTokenResponse(**_info) with pytest.raises(BadSignature): at.verify(key=[key])
def test_at_hash(): lifetime = 3600 _token = {'access_token': 'accessTok'} idval = { 'nonce': 'KUEYfRM2VzKDaaKD', 'sub': 'EndUserSubject', 'iss': 'https://alpha.cloud.nds.rub.de', 'aud': 'TestClient' } idval.update(_token) idts = IdToken(**idval) key = SYMKey(key="TestPassword") _signed_jwt = idts.to_jwt(key=[key], algorithm="HS256", lifetime=lifetime) _info = { "id_token": _signed_jwt, "token_type": "Bearer", "expires_in": lifetime } _info.update(_token) at = AuthorizationResponse(**_info) assert at.verify(key=[key], algs={"sign": "HS256"}) assert 'at_hash' in at['verified_id_token']
def test_a_1_3b(): _jwt = ("eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJl" "eHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0c" "nVlfQ.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk") keys = [SYMKey(key=jwkest.intarr2bin(HMAC_KEY))] _jws2 = JWS() _jws2.verify_compact(_jwt, keys)
def test_to_from_jwt(self): item = DummyMessage( req_str="Fair", opt_str="game", opt_int=9, opt_str_list=["one", "two"], req_str_list=["spike", "lee"], opt_json='{"ford": "green"}', ) keys = [SYMKey(key="A1B2C3D4")] jws = item.to_jwt(keys, "HS256") jitem = DummyMessage().from_jwt(jws, key=keys) assert _eq( jitem.keys(), [ "opt_str", "req_str", "opt_json", "req_str_list", "opt_str_list", "opt_int", ], )
def test_wrong_alg(): idval = { 'nonce': 'KUEYfRM2VzKDaaKD', 'sub': 'EndUserSubject', 'iss': 'https://alpha.cloud.nds.rub.de', 'exp': 1420823073, 'iat': 1420822473, 'aud': 'TestClient' } idts = IdToken(**idval) key = SYMKey(key="TestPassword") _signed_jwt = idts.to_jwt(key=[key], algorithm="HS256") _info = { "access_token": "accessTok", "id_token": _signed_jwt, "token_type": "Bearer", "expires_in": 3600 } at = AccessTokenResponse(**_info) try: at.verify(key=[key], algs={"sign": "HS512"}) except WrongSigningAlgorithm: pass
def test_faulty_idtoken(): idval = { 'nonce': 'KUEYfRM2VzKDaaKD', 'sub': 'EndUserSubject', 'iss': 'https://alpha.cloud.nds.rub.de', 'exp': 1420823073, 'iat': 1420822473, 'aud': 'TestClient' } idts = IdToken(**idval) key = SYMKey(key="TestPassword") _signed_jwt = idts.to_jwt(key=[key], algorithm="HS256") #Mess with the signed id_token p = _signed_jwt.split(".") p[2] = "aaa" _faulty_signed_jwt = ".".join(p) _info = { "access_token": "accessTok", "id_token": _faulty_signed_jwt, "token_type": "Bearer", "expires_in": 3600 } # Should fail at = AccessTokenResponse(**_info) try: at.verify(key=[key]) except BadSignature: pass else: raise
def test_missing_c_hash(): lifetime = 3600 _token = {'code': 'grant'} idval = { 'nonce': 'KUEYfRM2VzKDaaKD', 'sub': 'EndUserSubject', 'iss': 'https://alpha.cloud.nds.rub.de', 'aud': 'TestClient' } # idval.update(_token) idts = IdToken(**idval) key = SYMKey(key="TestPassword") _signed_jwt = idts.to_jwt(key=[key], algorithm="HS256", lifetime=lifetime) _info = { "id_token": _signed_jwt, "token_type": "Bearer", "expires_in": lifetime } _info.update(_token) at = AuthorizationResponse(**_info) with pytest.raises(MissingRequiredAttribute): at.verify(key=[key], algs={"sign": "HS256"})
def test_hmac_256(): payload = b'Please take a moment to register today' keys = [SYMKey(key=jwkest.intarr2bin(HMAC_KEY))] _jws = JWS(payload, alg="HS256") _jwt = _jws.sign_compact(keys) info = JWS().verify_compact(_jwt, keys) assert info == payload.decode("utf-8")
def test_complete_auth_token_idtoken_no_alg_config(self): _state = "state0" self.consumer.consumer_config["response_type"] = ["id_token", "token"] self.consumer.provider_info = ProviderConfigurationResponse( issuer="https://example.com") # abs min self.consumer.authz_req = {} # Store AuthzReq with state as key args = { "client_id": self.consumer.client_id, "response_type": self.consumer.consumer_config["response_type"], "scope": ["openid"], "nonce": "nonce", } token = IdToken( iss="https://example.com", aud="client_1", sub="some_sub", exp=1565348600, iat=1565348300, nonce="nonce", ) location = ( "https://example.com/cb?state=state0&access_token=token&token_type=bearer&" "scope=openid&id_token={}".format( token.to_jwt(key=[SYMKey(key="hemlig")], algorithm="HS256"))) with responses.RequestsMock() as rsps: rsps.add( responses.GET, "https://example.com/authorization", status=302, headers={"location": location}, ) result = self.consumer.do_authorization_request(state=_state, request_args=args) query = parse_qs(urlparse(result.request.url).query) assert query["client_id"] == ["client_1"] assert query["scope"] == ["openid"] assert query["response_type"] == ["id_token token"] assert query["state"] == ["state0"] assert query["nonce"] == ["nonce"] assert query["redirect_uri"] == ["https://example.com/cb"] parsed = urlparse(result.headers["location"]) with freeze_time("2019-08-09 11:00:00"): part = self.consumer.parse_authz(query=parsed.query, algs={"sign": "HS256"}) assert isinstance(part, tuple) auth = part[0] atr = part[1] idt = part[2] assert auth is None assert isinstance(atr, AccessTokenResponse) assert _eq( atr.keys(), ["access_token", "id_token", "token_type", "state", "scope"]) assert isinstance(idt, IdToken)
def test_hmac_512(): payload = "Please take a moment to register today" keys = [SYMKey(key=b'My hollow echo', alg="HS512")] _jws = JWS(payload, alg="HS512") _jwt = _jws.sign_compact(keys) _rj = JWS() info = _rj.verify_compact(_jwt, keys) assert info == payload
def test_faulty_id_token(self): _faulty_signed_jwt = self._faulty_id_token() with pytest.raises(BadSignature): IdToken().from_jwt(_faulty_signed_jwt, key=[SYMKey(key="TestPassword")]) # What if no verification key is given ? # Should also result in an exception with pytest.raises(MissingSigningKey): IdToken().from_jwt(_faulty_signed_jwt)
def test_sym_encrypt_decrypt(): encryption_key = SYMKey(use="enc", key='DukeofHazardpass', kid="some-key-id") jwe = JWE_SYM("some content", alg="A128KW", enc="A128CBC-HS256") _jwe = jwe.encrypt(key=encryption_key, kid="some-key-id") jwdec = JWE_SYM() resp = jwdec.decrypt(_jwe, encryption_key) assert resp == b'some content'
def test_init(self): info = user_info(None, USERDB, "diana") keys = [SYMKey(key="hemlig")] cresp = UserClaimsResponse(jwt=info.to_jwt(key=keys, algorithm="HS256"), claims_names=list(info.keys())) assert _eq(list(cresp.keys()), ["jwt", "claims_names"]) assert _eq(cresp["claims_names"], ['gender', 'birthdate']) assert "jwt" in cresp
def main(): parser = argparse.ArgumentParser( description="Generate a new symmetric key and print it to stdout.") parser.add_argument("-n", dest="key_length", default=48, type=int, help="Length of the random string used as key.") parser.add_argument("--kid", dest="kid", help="Key id.") args = parser.parse_args() key = SYMKey(key=rndstr(args.key_length), kid=args.kid).serialize() jwks = dict(keys=[key]) print(json.dumps(jwks))
def auth_app(self, app_id, app_secret, auth_code, state=""): """ Authenticate an app :param app_id: the app id :param app_secret: the app secret :param auth_code: the app auth code """ headers = {"Content-type": "application/json"} payload = { "application": app_id, "auth_code": auth_code, "state": state } try: full_url = utils.urljoin(self.host, "/api/v1/application-tokens/validate") response = requests.post(full_url, data=json.dumps(payload), headers=headers, verify=self.tls_verify) except RequestException: raise exceptions.TaigaRestException(full_url, 400, "NETWORK ERROR", "POST") if response.status_code != 200: raise exceptions.TaigaRestException(full_url, response.status_code, response.text, "POST") cyphered_token = response.json().get("cyphered_token", "") if cyphered_token: from jwkest.jwe import JWE from jwkest.jwk import SYMKey sym_key = SYMKey(key=app_secret, alg="A128KW") data, success = JWE().decrypt(cyphered_token, keys=[sym_key]), True if isinstance(data, tuple): data, success = data try: self.token = json.loads(data.decode("utf-8")).get( "token", None) except ValueError: # pragma: no cover self.token = None if not success: self.token = None else: self.token = None if self.token is None: raise exceptions.TaigaRestException(full_url, 400, "INVALID TOKEN", "POST") self.raw_request = RequestMaker("/api/v1", self.host, self.token, "Application", self.tls_verify) self._init_resources()
def auth_app(self, app_id, app_secret, auth_code, state=''): """ Authenticate an app :param app_id: the app id :param app_secret: the app secret :param auth_code: the app auth code """ headers = {'Content-type': 'application/json'} payload = { 'application': app_id, 'auth_code': auth_code, 'state': state } try: full_url = utils.urljoin(self.host, '/api/v1/application-tokens/validate') response = requests.post(full_url, data=json.dumps(payload), headers=headers, verify=self.tls_verify) except RequestException: raise exceptions.TaigaRestException(full_url, 400, 'NETWORK ERROR', 'POST') if response.status_code != 200: raise exceptions.TaigaRestException(full_url, response.status_code, response.text, 'POST') cyphered_token = response.json().get('cyphered_token', '') if cyphered_token: from jwkest.jwk import SYMKey from jwkest.jwe import JWE sym_key = SYMKey(key=app_secret, alg='A128KW') data, success = JWE().decrypt(cyphered_token, keys=[sym_key]), True if isinstance(data, tuple): data, success = data try: self.token = json.loads(data.decode('utf-8')).get( 'token', None) except ValueError: # pragma: no cover self.token = None if not success: self.token = None else: self.token = None if self.token is None: raise exceptions.TaigaRestException(full_url, 400, 'INVALID TOKEN', 'POST') self.raw_request = RequestMaker('/api/v1', self.host, self.token, 'Application', self.tls_verify) self._init_resources()
def test_sign_json_hs256(): payload = "Please take a moment to register today" keys = [SYMKey(key=jwkest.intarr2bin(HMAC_KEY))] _jws = JWS(payload, alg="HS256") _sig = {'alg': 'HS256'} _jwt = _jws.sign_json(per_signature_head=[_sig], keys=keys, alg='HS256') _jwt_sig = "%s.%s.%s" % (_jwt['signatures'][0]['header'], b64e(_jwt['payload']), _jwt['signatures'][0]['signature']) info = _jws.verify_compact(_jwt_sig, keys) assert info == payload
def test_get_all(): kb = rsa_init({ 'use': ['enc', 'sig'], 'size': 1024, 'name': 'rsa', 'path': 'keys' }) _sym = SYMKey(**{"kty": "oct", "key": "secret", "use": "enc"}) kb.append(_sym) assert len(kb.get()) == 3 _k = kb.keys() assert len(_k) == 3
def _faulty_id_token(self): idval = {'nonce': 'KUEYfRM2VzKDaaKD', 'sub': 'EndUserSubject', 'iss': 'https://alpha.cloud.nds.rub.de', 'exp': 1420823073, 'iat': 1420822473, 'aud': 'TestClient'} idts = IdToken(**idval) _signed_jwt = idts.to_jwt(key=[SYMKey(key="TestPassword")], algorithm="HS256") # Mess with the signed id_token p = _signed_jwt.split(".") p[2] = "aaa" return ".".join(p)
def test_wrong_alg(self): _now = time_util.utc_time_sans_frac() idval = {'nonce': 'KUEYfRM2VzKDaaKD', 'sub': 'EndUserSubject', 'iss': 'https://alpha.cloud.nds.rub.de', 'exp': _now + 3600, 'iat': _now, 'aud': 'TestClient'} idts = IdToken(**idval) key = SYMKey(key="TestPassword") _signed_jwt = idts.to_jwt(key=[key], algorithm="HS256") _info = {"access_token": "accessTok", "id_token": _signed_jwt, "token_type": "Bearer", "expires_in": 3600} at = AccessTokenResponse(**_info) with pytest.raises(WrongSigningAlgorithm): at.verify(key=[key], algs={"sign": "HS512"})
def test_negative_transaction(self): state = "STATE" error_msg = "Error message test" logger = logging.getLogger() key = SYMKey(key=rndstr(32), kid="1") key.serialize() transaction_session = {"state": state, "nonce": "NONCE", "start_time": time.time(), "client_id": "client1", "redirect_uri": "https://example.com"} transaction_id = construct_state(transaction_session, key) environ = MagicMock() with pytest.raises(cherrypy.HTTPRedirect) as redirect: negative_transaction_response(transaction_id, transaction_session, environ, logger, error_msg, "test_idp_entity") response = urlparse.parse_qs(urlparse.urlparse(redirect.value.urls[0]).fragment) assert response["state"][0] == state assert response["error"][0] == "access_denied" assert response["error_description"][0] == error_msg
def test_to_jwe_from_jwt(self): msg = DummyMessage(req_str="Fair", opt_str="game", opt_int=9, opt_str_list=["one", "two"], req_str_list=["spike", "lee"], opt_json='{"ford": "green"}') keys = [SYMKey(key="A1B2C3D4")] jwe = msg.to_jwe(keys, alg="A128KW", enc="A128CBC-HS256") jitem = DummyMessage().from_jwt(jwe, key=keys) assert _eq(jitem.keys(), [ 'opt_str', 'req_str', 'opt_json', 'req_str_list', 'opt_str_list', 'opt_int' ])
def get_client_alg_keys(cls, client): """ Takes a client and returns the set of keys associated with it. Returns a list of keys. """ if client.jwt_alg == 'RS256': keys = [] for rsa_key in get_oidc_rsa_key_model().objects.all(): keys.append(RSAKey(key=importKey(rsa_key.key), kid=rsa_key.kid)) if not keys: raise Exception('You must add at least one RSA Key.') elif client.jwt_alg == 'HS256': keys = [SYMKey(key=client.client_secret, alg=client.jwt_alg)] else: raise Exception('Unsupported key algorithm.') return keys