def gen_keys(key_size): try: from jwcrypto.jwk import JWK, JWKSet except ImportError as e: msg = "You have to install jwcrypto to use this function" print(msg) raise ImportError(msg) from e jwk = JWK() jwk.generate_key(generate="RSA", size=key_size) contents = jwk.export_to_pem(private_key=True, password=None) with open("private.pem", "w") as priv_pem_file: priv_pem_file.write(contents.decode("utf8")) contents = jwk.export_to_pem(private_key=False, password=None) with open("public.pem", "w") as priv_pem_file: priv_pem_file.write(contents.decode("utf8")) jwks = JWKSet() jwks.add(jwk) raw = jwks.export(private_keys=True) formatted = json.dumps(json.loads(raw), indent=2) with open("private.json", "w") as priv_jwks_file: priv_jwks_file.write(formatted) raw = jwks.export(private_keys=False) formatted = json.dumps(json.loads(raw), indent=2) with open("public.json", "w") as public_jwks_file: public_jwks_file.write(formatted)
def __init__(self, jwks_urls: "Optional[Union[str, Collection[str]]]" = None): from jwcrypto.jwk import JWKSet self.jwks_urls = jwks_urls self.keys = JWKSet() self.session = None # type: Optional[ClientSession]
def dump_pem_to_jwks(in_private): try: from jwcrypto.jwk import JWK, JWKSet except ImportError as e: msg = "You have to install jwcrypto to use this function" print(msg) raise ImportError(msg) from e with open(in_private, "rb") as privfile: data = privfile.read() jwk = JWK() jwk.import_from_pem(data) jwks = JWKSet() jwks.add(jwk) raw = jwks.export(private_keys=True) formatted = json.dumps(json.loads(raw), indent=2) with open("private.json", "w") as priv_jwks_file: priv_jwks_file.write(formatted) raw = jwks.export(private_keys=False) formatted = json.dumps(json.loads(raw), indent=2) with open("public.json", "w") as public_jwks_file: public_jwks_file.write(formatted)
def load_jwks(jwks): global _keyset _keyset = JWKSet() try: _keyset.import_keyset(jwks) except JWException as e: raise Exception("Failed to import keyset from settings") from e
def init_idp(self): self.keyset = JWKSet() with open(self.idp_key_file, 'r') as keyfile: loaded_keys = json.loads(keyfile.read()) for key in loaded_keys['keys']: self.keyset.add(JWK(**key)) static_store = OpenIDCStaticStore( self.get_config_value('static database url')) self.datastore = OpenIDCStore(self.get_config_value('database url'), static_store)
def listAll(): repo = KeyRepository(getDb()) docList = repo.fetchAll() jwkset = JWKSet() for keyDoc in docList: jwk = JWK.from_pem(base64.b64decode(keyDoc.publicKey)) # wrong way, no idea how to make it proper :| jwk._params['kid'] = str(keyDoc.id) jwk._params['alg'] = keyDoc.algorithm jwkset.add(jwk) return jwkset.export(private_keys=False, as_dict=True)
def test_validate_token(): payload = {'typ': 'Bearer', 'foo': 'bar', 'baz': 42} other_headers = {'kid': key.key_id} token = generate_jwt(payload, key, 'RS256', datetime.timedelta(minutes=5), other_headers=other_headers) header, claims = verify_jwt(token, key, ['RS256']) assert header is not None assert claims is not None keyset = JWKSet() keyset.add(key) assert validate_token(keyset, token, clock_skew_seconds=60)
def init_keyset(): """ Initialize keyset, by loading keyset from settings """ global _keyset _keyset = JWKSet() settings = get_settings() if settings.get('JWKS'): load_jwks(settings['JWKS']) if settings.get('JWKS_URL'): load_jwks_from_url(settings['JWKS_URL']) if len(_keyset['keys']) == 0: raise AuthConfigurationError('No keys loaded!')
def init(workdir): # Initialize SAML2, since this is quite tricky to get right cert = Certificate(os.path.join(workdir, 'saml2')) cert.generate('certificate', 'ipsilon-quickrun') url = 'http://localhost:8080/' validity = 365 * 5 meta = IdpMetadataGenerator(url, cert, timedelta(validity)) meta.output(os.path.join(workdir, 'saml2', 'metadata.xml')) # Also initalize OpenID Connect keyfile = os.path.join(workdir, 'openidc.key') keyset = JWKSet() # We generate one RSA2048 signing key rsasig = JWK(generate='RSA', size=2048, use='sig', kid='quickstart') keyset.add(rsasig) with open(keyfile, 'w') as m: m.write(keyset.export())
def init_keyset(): """ Initialize keyset, by loading keyset from settings and/or from url """ global _keyset, _keyset_last_update _keyset = JWKSet() _keyset_last_update = time.time() settings = get_settings() if settings.get('jwks'): load_jwks(settings['jwks']) if settings.get('jwks_url'): load_jwks_from_url(settings['jwks_url']) if len(_keyset['keys']) == 0: raise ConfigError('No keys loaded!')
def start_authz(self, arguments): request_data = { 'scope': [], 'response_type': [], 'client_id': None, 'redirect_uri': None, 'state': None, 'response_mode': None, 'nonce': None, 'display': None, 'prompt': [], 'max_age': None, 'ui_locales': None, 'id_token_hint': None, 'login_hint': None, 'acr_values': None, 'claims': '{}' } # Get the request # Step 1: get the get query arguments for data in request_data.keys(): if arguments.get(data, None): request_data[data] = arguments[data] # This is a workaround for python not understanding the splits we # do later if request_data['prompt'] == []: request_data['prompt'] = None for required_arg in ['scope', 'response_type', 'client_id']: if request_data[required_arg] is None or \ len(request_data[required_arg]) == 0: return self._respond_error( request_data, 'invalid_request', 'missing required argument %s' % required_arg) client = self.cfg.datastore.getClient(request_data['client_id']) if not client: return self._respond_error(request_data, 'unauthorized_client', 'Unknown client ID') request_data['response_type'] = request_data.get('response_type', '').split(' ') for rtype in request_data['response_type']: if rtype not in ['id_token', 'token', 'code']: return self._respond_error( request_data, 'unsupported_response_type', 'response type %s is not supported' % rtype) if request_data['response_type'] != ['code'] and \ not request_data['nonce']: return self._respond_error(request_data, 'invalid_request', 'nonce missing in non-code flow') # Step 2: get any provided request or request_uri if 'request' in arguments or 'request_uri' in arguments: # This is a JWT-encoded request if 'request' in arguments and 'request_uri' in arguments: return self._respond_error( request_data, 'invalid_request', 'both request and request_uri ' + 'provided') if 'request' in arguments: jwt_object = arguments['request'] else: try: # FIXME: MAY cache this at client registration time and # cache permanently until client registration is changed. jwt_object = requests.get(arguments['request_uri']).text except Exception as ex: # pylint: disable=broad-except self.debug('Unable to get request: %s' % ex) return self._respond_error(request_data, 'invalid_request', 'unable to parse request_uri') jwt_request = None try: # FIXME: Implement decryption decoded = JWT(jwt=jwt_object) if client['request_object_signing_alg'] != 'none': # Client told us we need to check signature if decoded.token.jose_header['alg'] != \ client['request_object_signing_alg']: raise Exception('Invalid algorithm used: %s' % decoded.token.jose_header['alg']) if client['request_object_signing_alg'] == 'none': jwt_request = json.loads(decoded.token.objects['payload']) else: keyset = None if client['jwks']: keys = json.loads(client['jkws']) else: keys = requests.get(client['jwks_uri']).json() keyset = JWKSet() for key in keys['keys']: keyset.add(JWK(**key)) key = keyset.get_key(decoded.token.jose_header['kid']) decoded = JWT(jwt=jwt_object, key=key) jwt_request = json.loads(decoded.claims) except Exception as ex: # pylint: disable=broad-except self.debug('Unable to parse request: %s' % ex) return self._respond_error(request_data, 'invalid_request', 'unable to parse request') if 'response_type' in jwt_request: jwt_request['response_type'] = \ jwt_request['response_type'].split(' ') if jwt_request['response_type'] != \ request_data['response_type']: return self._respond_error(request_data, 'invalid_request', 'response_type does not match') if 'client_id' in jwt_request: if jwt_request['client_id'] != request_data['client_id']: return self._respond_error(request_data, 'invalid_request', 'client_id does not match') for data in request_data.keys(): if data in jwt_request: request_data[data] = jwt_request[data] # Split these options since they are space-separated lists for to_split in ['prompt', 'ui_locales', 'acr_values', 'scope']: if request_data[to_split] is not None: # We know better than pylint in this regard # pylint: disable=no-member request_data[to_split] = request_data[to_split].split(' ') else: request_data[to_split] = [] # Start checking the request if request_data['redirect_uri'] is None: if len(client['redirect_uris']) != 1: return self._respond_error(request_data, 'invalid_request', 'missing redirect_uri') else: request_data['redirect_uri'] = client['redirect_uris'][0] for scope in request_data['scope']: if scope not in self.cfg.supported_scopes: return self._respond_error( request_data, 'invalid_scope', 'unknown scope %s requested' % scope) for response_type in request_data['response_type']: if response_type not in ['code', 'id_token', 'token']: return self._respond_error( request_data, 'unsupported_response_type', 'response_type %s is unknown' % response_type) if request_data['redirect_uri'] not in client['redirect_uris']: raise InvalidRequest('Invalid redirect_uri') # Build the "claims" values from scopes try: request_data['claims'] = json.loads(request_data['claims']) except Exception as ex: # pylint: disable=broad-except return self._respond_error(request_data, 'invalid_request', 'claims malformed: %s' % ex) if 'userinfo' not in request_data['claims']: request_data['claims']['userinfo'] = {} if 'id_token' not in request_data['claims']: request_data['claims']['id_token'] = {} scopes_to_claim = { 'profile': [ 'name', 'family_name', 'given_name', 'middle_name', 'nickname', 'preferred_username', 'profile', 'picture', 'website', 'gender', 'birthdate', 'zoneinfo', 'locale', 'updated_at' ], 'email': ['email', 'email_verified'], 'address': ['address'], 'phone': ['phone_number', 'phone_number_verified'] } for scope in scopes_to_claim: if scope in request_data['scope']: for claim in scopes_to_claim[scope]: if claim not in request_data['claims']: # pylint: disable=invalid-sequence-index request_data['claims']['userinfo'][claim] = None # Add claims from extensions for n, e in self.cfg.extensions.available().items(): data = e.get_claims(request_data['scope']) self.debug('%s returned %s' % (n, repr(data))) for claim in data: # pylint: disable=invalid-sequence-index request_data['claims']['userinfo'][claim] = None # Store data so we can continue with the request us = UserSession() user = us.get_user() returl = '%s/%s/Continue?%s' % (self.basepath, URLROOT, self.trans.get_GET_arg()) data = { 'login_target': client.get('client_name', None), 'login_return': returl, 'openidc_stage': 'continue', 'openidc_request': json.dumps(request_data) } if request_data['login_hint']: data['login_username'] = request_data['login_hint'] if not data['login_target']: data['login_target'] = get_url_hostpart( request_data['redirect_uri']) # Decide what to do with the request if request_data['max_age'] is None: request_data['max_age'] = client.get('default_max_age', None) needs_auth = True if not user.is_anonymous: if request_data['max_age'] in [None, 0]: needs_auth = False else: auth_time = us.get_user_attrs()['_auth_time'] needs_auth = ((int(auth_time) + int(request_data['max_age'])) <= int(time.time())) if needs_auth or 'login' in request_data['prompt']: if 'none' in request_data['prompt']: # We were asked not to provide a UI. Answer with false. return self._respond_error(request_data, 'login_required', 'user interface required') # Either the user wasn't logged in, or we were explicitly # asked to re-auth them. Let's do so! us.logout(user) # Let the user go to auth self.trans.store(data) redirect = '%s/login?%s' % (self.basepath, self.trans.get_GET_arg()) self.debug('Redirecting: %s' % redirect) raise cherrypy.HTTPRedirect(redirect) # Return error if authz check fails authz_check_res = self._authz_stack_check(request_data, client, user.name, us.get_user_attrs()) if authz_check_res: return authz_check_res self.trans.store(data) # The user was already signed on, and no request to re-assert its # identity. Let's forward directly to /Continue/ self.debug('Redirecting: %s' % returl) raise cherrypy.HTTPRedirect(returl)
def get_keys(self): if self.jwks: return JWKSet.from_json(self.jwks) return JWKSet()
from urllib.request import urlopen from jwcrypto.jwk import JWKSet jwkeys = JWKSet() jwk_sets = ['https://www.googleapis.com/oauth2/v3/certs'] for keyurl in jwk_sets: with urlopen(keyurl) as key: jwkeys.import_keyset(key.read().decode())
def __init__(self): self.ctr = dict() self.known_keys = JWKSet()
#!/usr/bin/python import time import os.path from jwcrypto.jwk import JWK, JWKSet keyid = int(time.time()) keyset = JWKSet() rsasig = JWK(generate='RSA', size=2048, use='sig', kid='%s-sig' % keyid) keyset.add(rsasig) rsasig = JWK(generate='RSA', size=2048, use='enc', kid='%s-enc' % keyid) keyset.add(rsasig) if not os.path.exists('/var/lib/ipsilon/idp/openidc'): os.makedirs('/var/lib/ipsilon/idp/openidc') with open('/var/lib/ipsilon/idp/openidc/openidc.key', 'w') as m: m.write(keyset.export())
def jwks(self) -> str: jwks = JWKSet() jwks.add(self._jwk) return jwks.export(private_keys=False)
def configure(self, opts, changes): if opts['openidc'] != 'yes': return path = os.path.join(opts['data_dir'], 'openidc') if not os.path.exists(path): os.makedirs(path, 0o700) keyfile = os.path.join(path, 'openidc.key') keyid = int(time.time()) keyset = JWKSet() # We generate one RSA2048 signing key rsasig = JWK(generate='RSA', size=2048, use='sig', kid='%s-sig' % keyid) keyset.add(rsasig) # We generate one RSA2048 encryption key rsasig = JWK(generate='RSA', size=2048, use='enc', kid='%s-enc' % keyid) keyset.add(rsasig) with open(keyfile, 'w') as m: m.write(keyset.export()) proto = 'https' url = '%s://%s%s/openidc/' % (proto, opts['hostname'], opts['instanceurl']) subject_salt = uuid.uuid4().hex if opts['openidc_subject_salt']: subject_salt = opts['openidc_subject_salt'] # Add configuration data to database po = PluginObject(*self.pargs) po.name = 'openidc' po.wipe_data() po.wipe_config_values() config = { 'endpoint url': url, 'database url': opts['openidc_dburi'] or opts['database_url'] % { 'datadir': opts['data_dir'], 'dbname': 'openidc' }, 'static database url': opts['openidc_static_dburi'] or opts['database_url'] % { 'datadir': opts['data_dir'], 'dbname': 'openidc.static' }, 'enabled extensions': opts['openidc_extensions'], 'idp key file': keyfile, 'idp sig key id': '%s-sig' % keyid, 'idp subject salt': subject_salt } po.save_plugin_config(config) # Update global config to add login plugin po.is_enabled = True po.save_enabled_state()
def oidc_provider_jwkset(): key = JWK.generate(kty='RSA', size=512) jwkset = JWKSet() jwkset.add(key) return jwkset