def __init__(self, plaintext=None, protected=None, unprotected=None, aad=None, algs=None): """Creates a JWE token. :param plaintext(bytes): An arbitrary plaintext to be encrypted. :param protected: A JSON string with the protected header. :param unprotected: A JSON string with the shared unprotected header. :param aad(bytes): Arbitrary additional authenticated data :param algs: An optional list of allowed algorithms """ self._allowed_algs = None self.objects = dict() self.plaintext = None if plaintext is not None: if isinstance(plaintext, bytes): self.plaintext = plaintext else: self.plaintext = plaintext.encode('utf-8') self.cek = None self.decryptlog = None if aad: self.objects['aad'] = aad if protected: _ = json_decode(protected) # check header encoding self.objects['protected'] = protected if unprotected: _ = json_decode(unprotected) # check header encoding self.objects['unprotected'] = unprotected if algs: self.allowed_algs = algs
def server_keys(self): if self._server_keys is None: if "server_keys" not in self.config: raise UnknownPublicKey("Server Keys not defined") skey = self.find_key(self.config["server_keys"], KEY_USAGE_SIG) ekey = self.find_key(self.config["server_keys"], KEY_USAGE_ENC) self._server_keys = [JWK(**(json_decode(skey))), JWK(**(json_decode(ekey)))] return self._server_keys
def serialize(self, compact=False): """Serializes the object into a JWE token. :param compact(boolean): if True generates the compact representation, otherwise generates a standard JSON format. :raises InvalidJWEOperation: if the object cannot serialized with the compact representation and `compat` is True. :raises InvalidJWEOperation: if no recipients have been added to the object. """ if 'ciphertext' not in self.objects: raise InvalidJWEOperation("No available ciphertext") if compact: for invalid in 'aad', 'unprotected': if invalid in self.objects: raise InvalidJWEOperation("Can't use compact encoding") if 'recipients' in self.objects: if len(self.objects['recipients']) != 1: raise InvalidJWEOperation("Invalid number of recipients") rec = self.objects['recipients'][0] else: rec = self.objects return '.'.join([base64url_encode(self.objects['protected']), base64url_encode(rec.get('encrypted_key', '')), base64url_encode(self.objects['iv']), base64url_encode(self.objects['ciphertext']), base64url_encode(self.objects['tag'])]) else: obj = self.objects enc = {'ciphertext': base64url_encode(obj['ciphertext']), 'iv': base64url_encode(obj['iv']), 'tag': base64url_encode(self.objects['tag'])} if 'protected' in obj: enc['protected'] = base64url_encode(obj['protected']) if 'unprotected' in obj: enc['unprotected'] = json_decode(obj['unprotected']) if 'aad' in obj: enc['aad'] = base64url_encode(obj['aad']) if 'recipients' in obj: enc['recipients'] = list() for rec in obj['recipients']: e = dict() if 'encrypted_key' in rec: e['encrypted_key'] = \ base64url_encode(rec['encrypted_key']) if 'header' in rec: e['header'] = json_decode(rec['header']) enc['recipients'].append(e) else: if 'encrypted_key' in obj: enc['encrypted_key'] = \ base64url_encode(obj['encrypted_key']) if 'header' in obj: enc['header'] = json_decode(obj['header']) return json_encode(enc)
def verify(sjws, pub_pem): sjws = json_decode(sjws) pub_pem = json_decode(pub_pem.replace('\n', '\\n')) if pub_pem.startswith("-----BEGIN"): pub_key = JWK.from_pem(to_bytes_2and3(pub_pem)) else: pub_key = JWK(kty='oct', k=base64url_encode(pub_pem)) sig = JWS() sig.deserialize(sjws, pub_key) sys.stdout.write(base64url_decode(json_decode(sig.serialize())['payload']))
def _get_jose_header(self, header=None): jh = dict() if 'protected' in self.objects: ph = json_decode(self.objects['protected']) jh = self._merge_headers(jh, ph) if 'unprotected' in self.objects: uh = json_decode(self.objects['unprotected']) jh = self._merge_headers(jh, uh) if header: rh = json_decode(header) jh = self._merge_headers(jh, rh) return jh
def test_1_Parse_GET(self): cli_skey = JWK(**self.client_keys[0]) jtok = make_sig_kem("mykey", None, cli_skey, "RS256") kem = KEMHandler({'KEMKeysStore': self.kk}) kem.parse(jtok, "mykey") out = kem.reply('output') jtok = JWT(jwt=json_decode(out)['value']) cli_ekey = JWK(**self.client_keys[1]) jtok.token.decrypt(cli_ekey) nested = jtok.token.payload jtok = JWT(jwt=nested.decode('utf-8')) jtok.token.verify(JWK(**test_keys[0])) payload = json_decode(jtok.token.payload)['value'] self.assertEqual(payload, 'output')
def wrap(self, key, keylen, cek, headers): self._check_key(key) if self.keydatalen is None: if cek is not None: raise InvalidJWEOperation('ECDH-ES cannot use an existing CEK') keydatalen = keylen * 8 alg = headers['enc'] else: keydatalen = self.keydatalen alg = headers['alg'] epk = JWK.generate(kty=key.key_type, crv=key.key_curve) dk = self._derive(epk.get_op_key('unwrapKey'), key.get_op_key('wrapKey'), alg, keydatalen, headers) if self.keydatalen is None: ret = {'cek': dk} else: aeskw = _AesKw(keydatalen) kek = JWK(kty="oct", use="enc", k=base64url_encode(dk)) ret = aeskw.wrap(kek, keydatalen // 8, cek, headers) ret['header'] = {'epk': json_decode(epk.export_public())} return ret
def topic(self): """ Generate token """ token = jwt.generate_jwt(payload, None, 'none', timedelta(seconds=60)) header, claims, _ = token.split('.') parsed_header = json_decode(base64url_decode(header)) del parsed_header['alg'] return u"%s.%s." % (base64url_encode(json_encode(parsed_header)), claims)
def _format_public_key(self, key): if isinstance(key, str): jwkey = json_decode(key) if 'kty' not in jwkey: raise ValueError('Invalid key, missing "kty" attribute') if jwkey['kty'] == 'RSA': pubnum = rsa.RSAPublicNumbers(jwkey['e'], jwkey['n']) pubkey = pubnum.public_key(default_backend()) elif jwkey['kty'] == 'EC': if jwkey['crv'] == 'P-256': curve = ec.SECP256R1 elif jwkey['crv'] == 'P-384': curve = ec.SECP384R1 elif jwkey['crv'] == 'P-521': curve = ec.SECP521R1 else: raise TypeError('Unsupported Elliptic Curve') pubnum = ec.EllipticCurvePublicNumbers( jwkey['x'], jwkey['y'], curve) pubkey = pubnum.public_key(default_backend()) else: raise ValueError('Unknown key type: %s' % jwkey['kty']) elif isinstance(key, rsa.RSAPublicKey): pubkey = key elif isinstance(key, ec.EllipticCurvePublicKey): pubkey = key else: raise TypeError('Unknown key type: %s' % type(key)) return pubkey.public_bytes( encoding=serialization.Encoding.DER, format=serialization.PublicFormat.SubjectPublicKeyInfo)
def _verify(self, alg, key, payload, signature, protected, header=None): # verify it is a valid JSON object and keep a decode copy if protected is not None: p = json_decode(protected) else: p = dict() if not isinstance(p, dict): raise InvalidJWSSignature('Invalid Protected header') # merge heders, and verify there are no duplicates if header: if not isinstance(header, dict): raise InvalidJWSSignature('Invalid Unprotected header') p = self._merge_headers(p, header) # verify critical headers # TODO: allow caller to specify list of headers it understands if 'crit' in p: self._check_crit(p['crit']) # check 'alg' is present if alg is None and 'alg' not in p: raise InvalidJWSSignature('No "alg" in headers') if alg: if 'alg' in p and alg != p['alg']: raise InvalidJWSSignature('"alg" mismatch, requested ' '"%s", found "%s"' % (alg, p['alg'])) a = alg else: a = p['alg'] # the following will verify the "alg" is supported and the signature # verifies S = JWSCore(a, key, protected, payload, self._allowed_algs) S.verify(signature)
def process_jwt(jwt): """ Process a JSON Web Token without verifying it. Call this before :func:`verify_jwt` if you need access to the header or claims in the token before verifying it. For example, the claims might identify the issuer such that you can retrieve the appropriate public key. :param jwt: The JSON Web Token to verify. :type jwt: str or unicode :rtype: tuple :returns: ``(header, claims)`` """ header, claims, _ = jwt.split('.') parsed_header = json_decode(base64url_decode(header)) parsed_claims = json_decode(base64url_decode(claims)) return parsed_header, parsed_claims
def import_key(self, value): v = json_decode(value) data = b64decode(v['pkcs12 data']) password = v['export password'] try: _fd, tmpdata = tempfile.mkstemp(dir=paths.TMP) with open(tmpdata, 'w') as f: f.write(data) # get the certificate from the file ipautil.run([paths.OPENSSL, "pkcs12", "-in", tmpdata, "-clcerts", "-nokeys", "-out", self.certfile, "-passin", "pass:{pwd}".format(pwd=password)], nolog=(password, )) if self.keyfile is not None: # get the private key from the file ipautil.run([paths.OPENSSL, "pkcs12", "-in", tmpdata, "-nocerts", "-nodes", "-out", self.keyfile, "-passin", "pass:{pwd}".format(pwd=password)], nolog=(password, )) finally: os.remove(tmpdata)
def deserialize(self, raw_jws, key=None, alg=None): """Deserialize a JWS token. NOTE: Destroys any current status and tries to import the raw JWS provided. :param raw_jws: a 'raw' JWS token (JSON Encoded or Compact notation) string. :param key: A (:class:`jwcrypto.jwk.JWK`) verification key (optional). If a key is provided a verification step will be attempted after the object is successfully deserialized. :param alg: The signing algorithm (optional). usually the algorithm is known as it is provided with the JOSE Headers of the token. :raises InvalidJWSObject: if the raw object is an invaid JWS token. :raises InvalidJWSSignature: if the verification fails. """ self.objects = dict() o = dict() try: try: djws = json_decode(raw_jws) o['payload'] = base64url_decode(str(djws['payload'])) if 'signatures' in djws: o['signatures'] = list() for s in djws['signatures']: os = dict() os['signature'] = base64url_decode(str(s['signature'])) if 'protected' in s: p = base64url_decode(str(s['protected'])) os['protected'] = p.decode('utf-8') if 'header' in s: os['header'] = s['header'] o['signatures'].append(os) else: o['signature'] = base64url_decode(str(djws['signature'])) if 'protected' in djws: p = base64url_decode(str(djws['protected'])) o['protected'] = p.decode('utf-8') if 'header' in djws: o['header'] = djws['header'] except ValueError: c = raw_jws.split('.') if len(c) != 3: raise InvalidJWSObject('Unrecognized representation') p = base64url_decode(str(c[0])) if len(p) > 0: o['protected'] = p.decode('utf-8') o['payload'] = base64url_decode(str(c[1])) o['signature'] = base64url_decode(str(c[2])) self.objects = o except Exception as e: # pylint: disable=broad-except raise InvalidJWSObject('Invalid format', repr(e)) if key: self.verify(key, alg)
def _get_key(self, header, usage): if 'kid' not in header: raise InvalidMessage("Missing key identifier") key = self.kkstore.find_key(header['kid'], usage) if key is None: raise UnknownPublicKey('Key found [kid:%s]' % header['kid']) return json_decode(key)
def server_keys(self): if self._server_keys is None: with open(self.config['server_keys']) as f: jsonkeys = f.read() dictkeys = json_decode(jsonkeys) self._server_keys = (JWK(**dictkeys[KEY_USAGE_SIG]), JWK(**dictkeys[KEY_USAGE_ENC])) return self._server_keys
def _check_provided_claims(self): # check_claims can be set to False to skip any check if self._check_claims is False: return try: claims = json_decode(self.claims) if not isinstance(claims, dict): raise ValueError() except ValueError: if self._check_claims is not None: raise JWTInvalidClaimFormat( "Claims check requested but claims is not a json dict") return self._check_default_claims(claims) if self._check_claims is None: return for name, value in self._check_claims.items(): if name not in claims: raise JWTMissingClaim("Claim %s is missing" % (name, )) if name in ['iss', 'sub', 'jti']: if value is not None and value != claims[name]: raise JWTInvalidClaimValue( "Invalid '%s' value. Expected '%s' got '%s'" % ( name, value, claims[name])) elif name == 'aud': if value is not None: if value == claims[name]: continue if isinstance(claims[name], list): if value in claims[name]: continue raise JWTInvalidClaimValue( "Invalid '%s' value. Expected '%s' in '%s'" % ( name, value, claims[name])) elif name == 'exp': if value is not None: self._check_exp(claims[name], value, 0) else: self._check_exp(claims[name], time.time(), self._leeway) elif name == 'nbf': if value is not None: self._check_nbf(claims[name], value, 0) else: self._check_nbf(claims[name], time.time(), self._leeway) else: if value is not None and value != claims[name]: raise JWTInvalidClaimValue( "Invalid '%s' value. Expected '%d' got '%d'" % ( name, value, claims[name]))
def generate(header, payload, priv_pem): priv_pem = json_decode(priv_pem.replace('\n', '\\n')) if priv_pem.startswith("-----BEGIN"): priv_key = JWK.from_pem(to_bytes_2and3(priv_pem)) else: priv_key = JWK(kty='oct', k=base64url_encode(priv_pem)) sig = JWS(payload) sig.add_signature(priv_key, protected=header) sys.stdout.write(sig.serialize(compact=True))
def test_A2(self): sigkey = jwk.JWK(**A2_example['key']) touter = jwt.JWT(jwt=A2_token, key=E_A2_ex['key']) tinner = jwt.JWT(jwt=touter.claims, key=sigkey, check_claims=False) self.assertEqual(A1_claims, json_decode(tinner.claims)) with self.assertRaises(jwe.InvalidJWEData): jwt.JWT(jwt=A2_token, key=E_A2_ex['key'], algs=['RSA_1_5', 'AES256GCM'])
def __get_keys(self, ca_host, cacerts_file, cacerts_pwd, data): # Fecth all needed certs one by one, then combine them in a single # p12 file prefix = data['prefix'] certlist = data['list'] cli = self.__CustodiaClient(server=ca_host) # Temporary nssdb tmpnssdir = tempfile.mkdtemp(dir=paths.TMP) try: # Temporary nssdb password nsspwfile = os.path.join(tmpnssdir, 'nsspwfile') with open(nsspwfile, 'w+') as f: f.write(b64encode(os.urandom(16))) f.flush() # Cert file password crtpwfile = os.path.join(tmpnssdir, 'crtpwfile') with open(crtpwfile, 'w+') as f: f.write(cacerts_pwd) f.flush() for nickname in certlist: value = cli.fetch_key(os.path.join(prefix, nickname), False) v = json_decode(value) pk12pwfile = os.path.join(tmpnssdir, 'pk12pwfile') with open(pk12pwfile, 'w+') as f: f.write(v['export password']) pk12file = os.path.join(tmpnssdir, 'pk12file') with open(pk12file, 'w+') as f: f.write(b64decode(v['pkcs12 data'])) ipautil.run([paths.PK12UTIL, '-d', tmpnssdir, '-k', nsspwfile, '-n', nickname, '-i', pk12file, '-w', pk12pwfile]) # Add CA certificates tmpdb = CertDB(self.realm, nssdir=tmpnssdir) self.suffix = ipautil.realm_to_suffix(self.realm) if self.admin_conn is not None: self.ldap_disconnect() self.import_ca_certs(tmpdb, True) # Now that we gathered all certs, re-export ipautil.run([paths.PKCS12EXPORT, '-d', tmpnssdir, '-p', nsspwfile, '-w', crtpwfile, '-o', cacerts_file]) finally: shutil.rmtree(tmpnssdir)
def test_3_KEMClient(self): server_keys = [JWK(**test_keys[KEY_USAGE_SIG]), JWK(**test_keys[KEY_USAGE_ENC])] client_keys = [JWK(**self.client_keys[KEY_USAGE_SIG]), JWK(**self.client_keys[KEY_USAGE_ENC])] cli = KEMClient(server_keys, client_keys) kem = KEMHandler({"KEMKeysStore": self.kk}) req = cli.make_request("key name", encalg=("RSA1_5", "A256CBC-HS512")) kem.parse(req, "key name") msg = json_decode(kem.reply("key value")) rep = cli.parse_reply("key name", msg["value"]) self.assertEqual(rep, "key value")
def f(sjwt, iat_skew=timedelta()): """ verify token using node-jsjws """ r = spawn( "fixtures.verify({now}, {sjwt}, {iat_skew}, {key}, {alg})".format( now=timegm(datetime.utcnow().utctimetuple()), sjwt=json_encode(sjwt), iat_skew=iat_skew.total_seconds(), key=json_encode(base64url_decode(json_decode(key.export())['k']) if key.is_symmetric else key.export_to_pem()), alg=json_encode(alg)), True) return tuple(r)
def test_2_KEMClient(self): server_keys = [JWK(**test_keys[KEY_USAGE_SIG]), None] client_keys = [JWK(**self.client_keys[KEY_USAGE_SIG]), JWK(**self.client_keys[KEY_USAGE_ENC])] cli = KEMClient(server_keys, client_keys) kem = KEMHandler({'KEMKeysStore': self.kk}) req = cli.make_request("key name") kem.parse(req, "key name") msg = json_decode(kem.reply('key value')) rep = cli.parse_reply("key name", msg['value']) self.assertEqual(rep, 'key value')
def setUpClass(cls): config = {"server_keys": test_keys[0]["kid"]} with open("examples/client_enc.key") as f: data = f.read() cls.client_keys = json_decode(data) cls.kk = KEMKeysStore(config) cls.kk.store = SqliteStore({"dburi": "kemtests.db"}) _store_keys(cls.kk.store, KEY_USAGE_SIG, test_keys) _store_keys(cls.kk.store, KEY_USAGE_ENC, test_keys) _store_keys(cls.kk.store, KEY_USAGE_SIG, cls.client_keys) _store_keys(cls.kk.store, KEY_USAGE_ENC, cls.client_keys)
def test_create_symKeys(self): keylist = SymmetricKeys['keys'] for key in keylist: jwkey = jwk.JWK(**key) jwkey.get_op_key('sign') jwkey.get_op_key('verify') e = jwkey.export() self.assertEqual(json_decode(e), key) jwk.JWK(**Useofx5c) jwk.JWK(**RSAPrivateKey)
def setUpClass(cls): config = {'server_keys': test_keys[0]['kid']} with open('examples/client_enc.key') as f: data = f.read() cls.client_keys = json_decode(data) cls.kk = KEMKeysStore(config) cls.kk.store = SqliteStore({'dburi': 'kemtests.db'}) _store_keys(cls.kk.store, KEY_USAGE_SIG, test_keys) _store_keys(cls.kk.store, KEY_USAGE_ENC, test_keys) _store_keys(cls.kk.store, KEY_USAGE_SIG, cls.client_keys) _store_keys(cls.kk.store, KEY_USAGE_ENC, cls.client_keys)
def _decode_key(self, key): if key is None: return None elif isinstance(key, JWK): return key elif isinstance(key, dict): return JWK(**key) elif isinstance(key, str): return JWK(**(json_decode(key))) else: raise TypeError("Invalid key type")
def add_recipient(self, key, header=None): """Encrypt the plaintext with the given key. :param key: A JWK key or password of appropriate type for the 'alg' provided in the JOSE Headers. :param header: A JSON string representing the per-recipient header. :raises ValueError: if the plaintext is missing or not of type bytes. :raises ValueError: if the compression type is unknown. :raises InvalidJWAAlgorithm: if the 'alg' provided in the JOSE headers is missing or unknown, or otherwise not implemented. """ if self.plaintext is None: raise ValueError('Missing plaintext') if not isinstance(self.plaintext, bytes): raise ValueError("Plaintext must be 'bytes'") jh = self._get_jose_header(header) alg, enc = self._get_alg_enc_from_headers(jh) rec = dict() if header: rec['header'] = header wrapped = alg.wrap(key, enc.key_size, self.cek, jh) self.cek = wrapped['cek'] if 'ek' in wrapped: rec['encrypted_key'] = wrapped['ek'] if 'header' in wrapped: h = json_decode(rec.get('header', '{}')) nh = self._merge_headers(h, wrapped['header']) rec['header'] = json_encode(nh) if 'ciphertext' not in self.objects: self._encrypt(alg, enc, jh) if 'recipients' in self.objects: self.objects['recipients'].append(rec) elif 'encrypted_key' in self.objects or 'header' in self.objects: self.objects['recipients'] = list() n = dict() if 'encrypted_key' in self.objects: n['encrypted_key'] = self.objects['encrypted_key'] del self.objects['encrypted_key'] if 'header' in self.objects: n['header'] = self.objects['header'] del self.objects['header'] self.objects['recipients'].append(n) self.objects['recipients'].append(rec) else: self.objects.update(rec)
def test_4_7_signing(self): plaintext = base64url_decode(Payload_plaintext_b64_4) header = json_encode(JWS_Unprotected_Header_4_7_2) key = jwk.JWK(**Symmetric_Key_MAC_3_5) S = jws.JWS(payload=plaintext) S.add_signature(key, None, None, header) sig = S.serialize() S.deserialize(sig, key) self.assertEqual(json_decode(sig), JWS_flattened_4_7_3) # Just deserialize each example form S.deserialize(json_encode(JWS_general_4_7_3), key) S.deserialize(json_encode(JWS_flattened_4_7_3), key)
def jose_header(self): obj = self.objects if 'signature' in obj: jh = dict() if 'protected' in obj: p = json_decode(obj['protected']) jh = self._merge_headers(jh, p) jh = self._merge_headers(jh, obj.get('header', dict())) return jh elif 'signatures' in self.objects: jhl = list() for o in obj['signatures']: jh = dict() if 'protected' in obj: p = json_decode(o['protected']) jh = self._merge_headers(jh, p) jh = self._merge_headers(jh, o.get('header', dict())) jhl.append(jh) return jhl else: raise InvalidJWSOperation("JOSE Header(s) not available")
def f(claims, alg, lifetime=None, expires=None, not_before=None): """ generate token using node-jsjws """ now = datetime.utcnow() return spawn( "fixtures.generate({now}, {header}, {claims}, {expires}, {not_before}, {key})".format( now=timegm(now.utctimetuple()), header=json_encode({'alg': alg}), claims=json_encode(claims), expires=timegm(((now + lifetime) if lifetime else expires).utctimetuple()), not_before=timegm((not_before or now).utctimetuple()), key=json_encode(base64url_decode(json_decode(key.export())['k']) if key.is_symmetric else key.export_to_pem(True, None))), False)
def __get_keys(self, ca_host, cacerts_file, cacerts_pwd, data): # Fecth all needed certs one by one, then combine them in a single # p12 file prefix = data['prefix'] certlist = data['list'] cli = self.__CustodiaClient(server=ca_host) # Temporary nssdb tmpnssdir = tempfile.mkdtemp(dir=paths.TMP) try: # Temporary nssdb password nsspwfile = os.path.join(tmpnssdir, 'nsspwfile') with open(nsspwfile, 'w+') as f: f.write(b64encode(os.urandom(16))) f.flush() # Cert file password crtpwfile = os.path.join(tmpnssdir, 'crtpwfile') with open(crtpwfile, 'w+') as f: f.write(cacerts_pwd) f.flush() for nickname in certlist: value = cli.fetch_key(os.path.join(prefix, nickname), False) v = json_decode(value) pk12pwfile = os.path.join(tmpnssdir, 'pk12pwfile') with open(pk12pwfile, 'w+') as f: f.write(v['export password']) pk12file = os.path.join(tmpnssdir, 'pk12file') with open(pk12file, 'w+') as f: f.write(b64decode(v['pkcs12 data'])) ipautil.run([ paths.PK12UTIL, '-d', tmpnssdir, '-k', nsspwfile, '-n', nickname, '-i', pk12file, '-w', pk12pwfile ]) # Add CA certificates tmpdb = CertDB(self.realm, nssdir=tmpnssdir) self.suffix = ipautil.realm_to_suffix(self.realm) self.import_ca_certs(tmpdb, True) # Now that we gathered all certs, re-export ipautil.run([ paths.PKCS12EXPORT, '-d', tmpnssdir, '-p', nsspwfile, '-w', crtpwfile, '-o', cacerts_file ]) finally: shutil.rmtree(tmpnssdir)
def test_2_KEMClient(self): server_keys = [JWK(**test_keys[kem.KEY_USAGE_SIG]), None] client_keys = [ JWK(**self.client_keys[kem.KEY_USAGE_SIG]), JWK(**self.client_keys[kem.KEY_USAGE_ENC]) ] cli = kem.KEMClient(server_keys, client_keys) kh = kem.KEMHandler({'KEMKeysStore': self.kk}) req = cli.make_request("key name") kh.parse(req, "key name") msg = kh.reply('key value') self.assertEqual(msg, json_decode(json_encode(msg))) rep = cli.parse_reply("key name", msg['value']) self.assertEqual(rep, 'key value')
def spawn(cmd, parse_json): """ run node command """ #pylint: disable=E1101 with lock: p = Popen(["node", "-e", "fixtures=require('./test/fixtures');" + cmd], stdout=PIPE, stderr=PIPE) (stdout, stderr) = p.communicate() stdout = stdout.decode('utf-8') stderr = stderr.decode('utf-8') if p.returncode == 0: return json_decode(stdout) if parse_json else stdout raise Exception(stderr if stderr else ( 'exited with {}'.format(p.returncode)))
def sign(client_id, payload, compact=True): key = jwk.JWK.from_json(open(os.environ["KEY_FILE"], 'rb').read()) public_key = jwk.JWK() public_key.import_key(**json_decode(key.export_public())) jwstoken = jws.JWS(payload) jwstoken.add_signature( key, None, json_encode({ "alg": "RS512", "cid": client_id, "kid": public_key.thumbprint() }), None) signed_payload = jwstoken.serialize(compact) return signed_payload
def test_4_6_signing(self): plaintext = base64url_decode(Payload_plaintext_b64_4) protected = \ base64url_decode(JWS_Protected_Header_4_6_2).decode('utf-8') header = json_encode(JWS_Unprotected_Header_4_6_2) key = jwk.JWK(**Symmetric_Key_MAC_3_5) S = jws.JWS(payload=plaintext) S.add_signature(key, None, protected, header) sig = S.serialize() S.deserialize(sig, key) self.assertEqual(json_decode(sig), JWS_flattened_4_6_3) # Just deserialize each example form S.deserialize(json_encode(JWS_general_4_6_3), key) S.deserialize(json_encode(JWS_flattened_4_6_3), key)
def __get_keys(self, ca_host, cacerts_file, cacerts_pwd, data): # Fecth all needed certs one by one, then combine them in a single # p12 file prefix = data['prefix'] certlist = data['list'] # Before we attempt to fetch keys from this host, make sure our public # keys have been replicated there. self.__wait_keys(ca_host) cli = self.__CustodiaClient(server=ca_host) # Temporary nssdb tmpnssdir = tempfile.mkdtemp(dir=paths.TMP) tmpdb = NSSDatabase(tmpnssdir) tmpdb.create_db() try: # Cert file password crtpwfile = os.path.join(tmpnssdir, 'crtpwfile') with open(crtpwfile, 'w+') as f: f.write(cacerts_pwd) f.flush() for nickname in certlist: value = cli.fetch_key(os.path.join(prefix, nickname), False) v = json_decode(value) pk12pwfile = os.path.join(tmpnssdir, 'pk12pwfile') with open(pk12pwfile, 'w+') as f: f.write(v['export password']) pk12file = os.path.join(tmpnssdir, 'pk12file') with open(pk12file, 'w+') as f: f.write(b64decode(v['pkcs12 data'])) ipautil.run([ paths.PK12UTIL, '-d', tmpdb.secdir, '-k', tmpdb.pwd_file, '-n', nickname, '-i', pk12file, '-w', pk12pwfile ]) # Add CA certificates self.suffix = ipautil.realm_to_suffix(self.realm) self.import_ca_certs(tmpdb, True) # Now that we gathered all certs, re-export ipautil.run([ paths.PKCS12EXPORT, '-d', tmpdb.secdir, '-p', tmpdb.pwd_file, '-w', crtpwfile, '-o', cacerts_file ]) finally: shutil.rmtree(tmpnssdir)
def parse(self, msg, name): """Parses the message. We check that the message is properly formatted. :param msg: a json-encoded value containing a JWS or JWE+JWS token :raises InvalidMessage: if the message cannot be parsed or validated :returns: A verified payload """ try: jtok = JWT(jwt=msg) except Exception as e: raise InvalidMessage('Failed to parse message: %s' % str(e)) try: token = jtok.token if isinstance(token, JWE): token.decrypt(self.kkstore.server_keys[KEY_USAGE_ENC]) # If an encrypted payload is received then there must be # a nested signed payload to verify the provenance. payload = token.payload.decode('utf-8') token = JWS() token.deserialize(payload) elif isinstance(token, JWS): pass else: raise TypeError("Invalid Token type: %s" % type(jtok)) # Retrieve client keys for later use self.client_keys = [ JWK(**self._get_key(token.jose_header, KEY_USAGE_SIG)), JWK(**self._get_key(token.jose_header, KEY_USAGE_ENC))] # verify token and get payload token.verify(self.client_keys[KEY_USAGE_SIG]) claims = json_decode(token.payload) except Exception as e: raise InvalidMessage('Failed to validate message: %s' % str(e)) check_kem_claims(claims, name) self.name = name self.payload = claims.get('value') return {'type': 'kem', 'value': {'kid': self.client_keys[KEY_USAGE_ENC].key_id, 'claims': claims}}
def export(self, private_keys=True): """Exports a RFC 7517 keyset using the standard JSON format :param private_key(bool): Whether to export private keys. Defaults to True. """ exp_dict = dict() for k, v in iteritems(self): if k == 'keys': keys = list() for jwk in v: keys.append(json_decode(jwk.export(private_keys))) v = keys exp_dict[k] = v return json_encode(exp_dict)
def unwrap_response(body_data, arguments): """Unwrap the response payload DCS signed, encrypted, and then signed the plaintext response. See https://dcs-pilot-docs.cloudapps.digital/message-structure for the documentation. """ server_signing_certificate = load_pem( arguments["--server-signing-certificate"]) client_encryption_key = load_pem(arguments["--client-encryption-key"]) encrypted = unwrap_signature(body_data, server_signing_certificate) inner_signed = decrypt(encrypted, client_encryption_key) return json_decode( unwrap_signature(inner_signed, server_signing_certificate))
def f(claims, alg, lifetime=None, expires=None, not_before=None): """ generate token using jose """ now = datetime.utcnow() return spawn( "fixtures.generate({now}, {header}, {claims}, {expires}, {not_before}, {key})" .format( now=timegm(now.utctimetuple()), header=json_encode({'alg': alg}), claims=json_encode(claims), expires=timegm( ((now + lifetime) if lifetime else expires).utctimetuple()), not_before=timegm((not_before or now).utctimetuple()), key=json_encode( base64url_decode(json_decode(key.export())['k']) if key. is_symmetric else key.export_to_pem(True, None))), False)
def _deserialize_b64(self, o, protected): if protected is None: b64n = None else: p = json_decode(protected) b64n = p.get('b64') if b64n is not None: if not isinstance(b64n, bool): raise InvalidJWSObject('b64 header must be boolean') b64 = o.get('b64') if b64 == b64n: return elif b64 is None: o['b64'] = b64n else: raise InvalidJWSObject('conflicting b64 values')
def __init__(self, config): super(EncryptedStore, self).__init__(config) if 'master_key' not in config: raise ValueError('Missing "master_key" for Encrypted Store') with open(config['master_key']) as f: data = f.read() key = json_decode(data) self.mkey = jwk.JWK(**key) if 'master_enctype' in config: self.enc = config['master_enctype'] else: self.enc = 'A256CBC_HS512'
def test_3_KEMClient(self): server_keys = [ JWK(**test_keys[KEY_USAGE_SIG]), JWK(**test_keys[KEY_USAGE_ENC]) ] client_keys = [ JWK(**self.client_keys[KEY_USAGE_SIG]), JWK(**self.client_keys[KEY_USAGE_ENC]) ] cli = KEMClient(server_keys, client_keys) kem = KEMHandler({'KEMKeysStore': self.kk}) req = cli.make_request("key name", encalg=('RSA1_5', 'A256CBC-HS512')) kem.parse(req, "key name") msg = json_decode(kem.reply('key value')) rep = cli.parse_reply("key name", msg['value']) self.assertEqual(rep, 'key value')
def __init__(self, config, section): super(EncryptedOverlay, self).__init__(config, section) self.store_name = self.backing_store self.store = None if (not os.path.isfile(self.master_key) and self.autogen_master_key): # XXX https://github.com/latchset/jwcrypto/issues/50 size = self.key_sizes.get(self.master_enctype, 512) key = JWK(generate='oct', size=size) with open(self.master_key, 'w') as f: os.fchmod(f.fileno(), 0o600) f.write(key.export()) with open(self.master_key) as f: data = f.read() key = json_decode(data) self.mkey = JWK(**key)
def __init__(self, config): super(EncryptedOverlay, self).__init__(config) if 'backing_store' not in config: raise ValueError('Missing "backing_store" for Encrypted Store') self.store_name = config['backing_store'] self.store = None if 'master_key' not in config: raise ValueError('Missing "master_key" for Encrypted Store') with open(config['master_key']) as f: data = f.read() key = json_decode(data) self.mkey = JWK(**key) self.enc = config.get('master_enctype', 'A256CBC_HS512')
def getAuthTokenFromJWT(JWEtoken): # Get the key key = Config.JWT_KEY # Decrypt token encryptedSignedToken = jwt.JWT(jwt=JWEtoken).token encryptedSignedToken.decrypt(key) raw_payload = encryptedSignedToken.payload JWStoken = raw_payload.decode("utf-8") # extract payload from signed token signedToken = jwt.JWT(jwt=JWStoken).token signedToken.verify(key) payload = json_decode(signedToken.payload) authToken = payload['authToken'] return authToken
def setUpClass(cls): cls.parser = configparser.ConfigParser( interpolation=configparser.ExtendedInterpolation()) cls.parser.read_string(CONFIG) config = {'server_keys': test_keys[0]['kid']} with open('examples/client_enc.key') as f: data = f.read() cls.client_keys = json_decode(data) cls.kk = kem.KEMKeysStore(config) cls.kk.store = SqliteStore(cls.parser, 'store:sqlite') _store_keys(cls.kk.store, kem.KEY_USAGE_SIG, test_keys) _store_keys(cls.kk.store, kem.KEY_USAGE_ENC, test_keys) _store_keys(cls.kk.store, kem.KEY_USAGE_SIG, cls.client_keys) _store_keys(cls.kk.store, kem.KEY_USAGE_ENC, cls.client_keys)
def _get_keys(self, ca_host, cacerts_file, cacerts_pwd, data): # Fetch all needed certs one by one, then combine them in a single # PKCS12 file prefix = data['prefix'] certlist = data['list'] cli = self._get_custodia_client(server=ca_host) with NSSDatabase(None) as tmpdb: tmpdb.create_db() # Cert file password crtpwfile = os.path.join(tmpdb.secdir, 'crtpwfile') with open(crtpwfile, 'w+') as f: f.write(cacerts_pwd) for nickname in certlist: value = cli.fetch_key(os.path.join(prefix, nickname), False) v = json_decode(value) pk12pwfile = os.path.join(tmpdb.secdir, 'pk12pwfile') with open(pk12pwfile, 'w+') as f: f.write(v['export password']) pk12file = os.path.join(tmpdb.secdir, 'pk12file') with open(pk12file, 'wb') as f: f.write(b64decode(v['pkcs12 data'])) tmpdb.run_pk12util([ '-k', tmpdb.pwd_file, '-n', nickname, '-i', pk12file, '-w', pk12pwfile ]) # Add CA certificates, but don't import the main CA cert. It's # already present as 'caSigningCert cert-pki-ca'. With SQL db # format, a second import would rename the certificate. See # https://pagure.io/freeipa/issue/7498 for more details. conn = api.Backend.ldap2 suffix = ipautil.realm_to_suffix(self.realm) ca_certs = get_ca_certs_nss(conn, suffix, self.realm, True) for cert, nickname, trust_flags in ca_certs: if nickname == get_ca_nickname(self.realm): continue tmpdb.add_cert(cert, nickname, trust_flags) # Now that we gathered all certs, re-export ipautil.run([ paths.PKCS12EXPORT, '-d', tmpdb.secdir, '-p', tmpdb.pwd_file, '-w', crtpwfile, '-o', cacerts_file ])
def import_key(self, value): v = json_decode(value) tdir = tempfile.mkdtemp(dir=paths.TMP) try: nsspwfile = os.path.join(tdir, 'nsspwfile') with open(nsspwfile, 'w+') as f: f.write(self.nssdb_password) pk12pwfile = os.path.join(tdir, 'pk12pwfile') with open(pk12pwfile, 'w+') as f: f.write(v['export password']) pk12file = os.path.join(tdir, 'pk12file') with open(pk12file, 'w+') as f: f.write(b64decode(v['pkcs12 data'])) ipautil.run([ paths.PK12UTIL, "-d", self.nssdb_path, "-i", pk12file, "-n", self.nickname, "-k", nsspwfile, "-w", pk12pwfile ]) finally: shutil.rmtree(tdir)
def decrypt(): public_file = (open(str(settings.ROOT) + "/public.pub", "r")) private_file = (open(str(settings.ROOT) + "/private.pem", "r")) public_key = jwk.JWK.from_pem(public_file.read().encode()) private_key = jwk.JWK.from_pem(private_file.read().encode()) enc = request.args.get("k") e = jwt.JWT(key=private_key, jwt=enc) s = jwt.JWT() s.leeway = 999999 s.deserialize(jwt=e.claims, key=public_key) # it can be validated from keyset #ks = jwk.JWKSet() #ks.import_keyset(json_encode(index())) #s.deserialize(jwt=e.claims, key=ks.get_key(json_decode(e.header)["kid"])) return json_decode(s.claims)
def import_keyset(self, keyset): """Imports a RFC 7517 key set using the standard JSON format. :param keyset: The RFC 7517 representation of a JOSE key set. """ try: jwkset = json_decode(keyset) except Exception: # pylint: disable=broad-except raise InvalidJWKValue() if 'keys' not in jwkset: raise InvalidJWKValue() for k, v in iteritems(jwkset): if k == 'keys': for jwk in v: self['keys'].add(JWK(**jwk)) else: self[k] = v
def wrap(self, key, bitsize, cek, headers): dk_size = self.keysize if self.keysize is None: if cek is not None: raise jwe.InvalidJWEOperation('ECDH-ES cannot use an existing CEK') alg = headers['enc'] dk_size = bitsize else: alg = headers['alg'] dk = self._derive(key, key, alg, dk_size, headers) if self.keysize is None: ret = {'cek': dk} else: aeskw = self.aeskwmap[self.keysize]() kek = jwk.JWK(kty="oct", use="enc", k=base64url_encode(dk)) ret = aeskw.wrap(kek, bitsize, cek, headers) ret['header'] = {'epk': json_decode(key.export_public())} return ret
def _get_keys(self, cacerts_file, cacerts_pwd, data): # Fetch all needed certs one by one, then combine them in a single # PKCS12 file prefix = data['prefix'] certlist = data['list'] cli = self._get_custodia_client() with NSSDatabase(None) as tmpdb: tmpdb.create_db() # Cert file password crtpwfile = os.path.join(tmpdb.secdir, 'crtpwfile') with open(crtpwfile, 'w+') as f: f.write(cacerts_pwd) for nickname in certlist: value = cli.fetch_key(os.path.join(prefix, nickname), False) v = json_decode(value) pk12pwfile = os.path.join(tmpdb.secdir, 'pk12pwfile') with open(pk12pwfile, 'w+') as f: f.write(v['export password']) pk12file = os.path.join(tmpdb.secdir, 'pk12file') with open(pk12file, 'wb') as f: f.write(b64decode(v['pkcs12 data'])) tmpdb.run_pk12util([ '-k', tmpdb.pwd_file, '-n', nickname, '-i', pk12file, '-w', pk12pwfile ]) # Add CA certificates self.export_ca_certs_nssdb(tmpdb, True) # Now that we gathered all certs, re-export ipautil.run([ paths.PKCS12EXPORT, '-d', tmpdb.secdir, '-p', tmpdb.pwd_file, '-w', crtpwfile, '-o', cacerts_file ])
def import_key(self, value): v = json_decode(value) tdir = tempfile.mkdtemp(dir=paths.TMP) try: pk12pwfile = os.path.join(tdir, 'pk12pwfile') with open(pk12pwfile, 'w') as f: f.write(v['export password']) pk12file = os.path.join(tdir, 'pk12file') with open(pk12file, 'wb') as f: f.write(b64decode(v['pkcs12 data'])) nssdb = NSSDatabase(self.nssdb_path) nssdb.run_pk12util([ "-i", pk12file, "-n", self.nickname, "-k", self.nssdb_pwdfile, "-w", pk12pwfile, ]) finally: shutil.rmtree(tdir)
def wrap(self, key, bitsize, cek, headers): self._check_key(key) if self.keysize is None: if cek is not None: raise InvalidJWEOperation('ECDH-ES cannot use an existing CEK') alg = headers['enc'] else: bitsize = self.keysize alg = headers['alg'] epk = JWK.generate(kty=key.key_type, crv=key.key_curve) dk = self._derive(epk.get_op_key('unwrapKey'), key.get_op_key('wrapKey'), alg, bitsize, headers) if self.keysize is None: ret = {'cek': dk} else: aeskw = self.aeskwmap[bitsize]() kek = JWK(kty="oct", use="enc", k=base64url_encode(dk)) ret = aeskw.wrap(kek, bitsize, cek, headers) ret['header'] = {'epk': json_decode(epk.export_public())} return ret
def _derive(self, key, _, alg, bitsize, headers): otherinfo = struct.pack('>I', len(alg)) otherinfo += bytes(alg.encode('utf8')) apu = base64url_decode(headers['apu']) if 'apu' in headers else b'' otherinfo += struct.pack('>I', len(apu)) otherinfo += apu apv = base64url_decode(headers['apv']) if 'apv' in headers else b'' otherinfo += struct.pack('>I', len(apv)) otherinfo += apv otherinfo += struct.pack('>I', bitsize) key = json_decode(key.export(True)) assert key['kty'] == 'EC' assert key['crv'] == 'P-256' ggx = int.from_bytes(base64url_decode(key['x']), 'big') ggy = int.from_bytes(base64url_decode(key['y']), 'big') d = int.from_bytes(base64url_decode(key['d']), 'big') shared_key = (d * AffinePoint(secp256r1, ggx, ggy)).x.to_bytes(32, 'big') ckdf = jwa.ConcatKDFHash(algorithm=jwa.hashes.SHA256(), length=jwa._inbytes(bitsize), otherinfo=otherinfo, backend=self.backend) return ckdf.derive(shared_key)
def deserialize(self, raw_jwe, key=None): """Deserialize a JWE token. NOTE: Destroys any current status and tries to import the raw JWE provided. :param raw_jwe: a 'raw' JWE token (JSON Encoded or Compact notation) string. :param key: A (:class:`jwcrypto.jwk.JWK`) decryption key (optional). If a key is provided a idecryption step will be attempted after the object is successfully deserialized. :raises InvalidJWEData: if the raw object is an invaid JWE token. :raises InvalidJWEOperation: if the decryption fails. """ self.objects = dict() self.plaintext = None self.cek = None o = dict() try: try: djwe = json_decode(raw_jwe) o['iv'] = base64url_decode(str(djwe['iv'])) o['ciphertext'] = base64url_decode(str(djwe['ciphertext'])) o['tag'] = base64url_decode(str(djwe['tag'])) if 'protected' in djwe: p = base64url_decode(str(djwe['protected'])) o['protected'] = p.decode('utf-8') if 'unprotected' in djwe: o['unprotected'] = json_encode(djwe['unprotected']) if 'aad' in djwe: o['aad'] = base64url_decode(str(djwe['aad'])) if 'recipients' in djwe: o['recipients'] = list() for rec in djwe['recipients']: e = dict() if 'encrypted_key' in rec: e['encrypted_key'] = \ base64url_decode(str(rec['encrypted_key'])) if 'header' in rec: e['header'] = json_encode(rec['header']) o['recipients'].append(e) else: if 'encrypted_key' in djwe: o['encrypted_key'] = \ base64url_decode(str(djwe['encrypted_key'])) if 'header' in djwe: o['header'] = json_encode(djwe['header']) except ValueError: c = raw_jwe.split('.') if len(c) != 5: raise InvalidJWEData() p = base64url_decode(str(c[0])) o['protected'] = p.decode('utf-8') ekey = base64url_decode(str(c[1])) if ekey != '': o['encrypted_key'] = base64url_decode(str(c[1])) o['iv'] = base64url_decode(str(c[2])) o['ciphertext'] = base64url_decode(str(c[3])) o['tag'] = base64url_decode(str(c[4])) self.objects = o except Exception as e: # pylint: disable=broad-except raise InvalidJWEData('Invalid format', repr(e)) if key: self.decrypt(key)
def generate_jwt(claims, priv_key=None, algorithm='PS512', lifetime=None, expires=None, not_before=None, jti_size=16, other_headers=None): """ Generate a JSON Web Token. :param claims: The claims you want included in the signature. :type claims: dict :param priv_key: The private key to be used to sign the token. Note: if you pass ``None`` then the token will be returned with an empty cryptographic signature and :obj:`algorithm` will be forced to the value ``none``. :type priv_key: `jwcrypto.jwk.JWK <https://jwcrypto.readthedocs.io/en/latest/jwk.html>`_ :param algorithm: The algorithm to use for generating the signature. ``RS256``, ``RS384``, ``RS512``, ``PS256``, ``PS384``, ``PS512``, ``ES256``, ``ES384``, ``ES512``, ``HS256``, ``HS384``, ``HS512`` and ``none`` are supported. :type algorithm: str :param lifetime: How long the token is valid for. :type lifetime: datetime.timedelta :param expires: When the token expires (if :obj:`lifetime` isn't specified) :type expires: datetime.datetime :param not_before: When the token is valid from. Defaults to current time (if ``None`` is passed). :type not_before: datetime.datetime :param jti_size: Size in bytes of the unique token ID to put into the token (can be used to detect replay attacks). Defaults to 16 (128 bits). Specify 0 or ``None`` to omit the JTI from the token. :type jti_size: int :param other_headers: Any headers other than "typ" and "alg" may be specified, they will be included in the header. :type other_headers: dict :rtype: unicode :returns: The JSON Web Token. Note this includes a header, the claims and a cryptographic signature. The following extra claims are added, per the `JWT spec <http://self-issued.info/docs/draft-ietf-oauth-json-web-token.html>`_: - **exp** (*IntDate*) -- The UTC expiry date and time of the token, in number of seconds from 1970-01-01T0:0:0Z UTC. - **iat** (*IntDate*) -- The UTC date and time at which the token was generated. - **nbf** (*IntDate*) -- The UTC valid-from date and time of the token. - **jti** (*str*) -- A unique identifier for the token. :raises: ValueError: If other_headers contains either the "typ" or "alg" header """ header = {'typ': 'JWT', 'alg': algorithm if priv_key else 'none'} if other_headers is not None: redefined_keys = set(header.keys()) & set(other_headers.keys()) if redefined_keys: raise ValueError( 'other_headers re-specified the headers: {}'.format( ', '.join(redefined_keys))) header.update(other_headers) claims = dict(claims) now = datetime.utcnow() if jti_size: claims['jti'] = base64url_encode(urandom(jti_size)) claims['nbf'] = timegm((not_before or now).utctimetuple()) claims['iat'] = timegm(now.utctimetuple()) if lifetime: claims['exp'] = timegm((now + lifetime).utctimetuple()) elif expires: claims['exp'] = timegm(expires.utctimetuple()) if header['alg'] == 'none': signature = '' else: token = JWS(json_encode(claims)) token.allowed_algs = [header['alg']] token.add_signature(priv_key, protected=header) signature = json_decode(token.serialize())['signature'] return u'%s.%s.%s' % (base64url_encode( json_encode(header)), base64url_encode(json_encode(claims)), signature)
def serialize(self, compact=False): """Serializes the object into a JWE token. :param compact(boolean): if True generates the compact representation, otherwise generates a standard JSON format. :raises InvalidJWEOperation: if the object cannot serialized with the compact representation and `compact` is True. :raises InvalidJWEOperation: if no recipients have been added to the object. """ if 'ciphertext' not in self.objects: raise InvalidJWEOperation("No available ciphertext") if compact: for invalid in 'aad', 'unprotected': if invalid in self.objects: raise InvalidJWEOperation( "Can't use compact encoding when the '%s' parameter" "is set" % invalid) if 'protected' not in self.objects: raise InvalidJWEOperation( "Can't use compat encoding without protected headers") else: ph = json_decode(self.objects['protected']) for required in 'alg', 'enc': if required not in ph: raise InvalidJWEOperation( "Can't use compat encoding, '%s' must be in the " "protected header" % required) if 'recipients' in self.objects: if len(self.objects['recipients']) != 1: raise InvalidJWEOperation("Invalid number of recipients") rec = self.objects['recipients'][0] else: rec = self.objects if 'header' in rec: # The AESGCMKW algorithm generates data (iv, tag) we put in the # per-recipient unpotected header by default. Move it to the # protected header and re-encrypt the payload, as the protected # header is used as additional authenticated data. h = json_decode(rec['header']) ph = json_decode(self.objects['protected']) nph = self._merge_headers(h, ph) self.objects['protected'] = json_encode(nph) jh = self._get_jose_header() alg, enc = self._get_alg_enc_from_headers(jh) self._encrypt(alg, enc, jh) del rec['header'] return '.'.join([base64url_encode(self.objects['protected']), base64url_encode(rec.get('encrypted_key', '')), base64url_encode(self.objects['iv']), base64url_encode(self.objects['ciphertext']), base64url_encode(self.objects['tag'])]) else: obj = self.objects enc = {'ciphertext': base64url_encode(obj['ciphertext']), 'iv': base64url_encode(obj['iv']), 'tag': base64url_encode(self.objects['tag'])} if 'protected' in obj: enc['protected'] = base64url_encode(obj['protected']) if 'unprotected' in obj: enc['unprotected'] = json_decode(obj['unprotected']) if 'aad' in obj: enc['aad'] = base64url_encode(obj['aad']) if 'recipients' in obj: enc['recipients'] = list() for rec in obj['recipients']: e = dict() if 'encrypted_key' in rec: e['encrypted_key'] = \ base64url_encode(rec['encrypted_key']) if 'header' in rec: e['header'] = json_decode(rec['header']) enc['recipients'].append(e) else: if 'encrypted_key' in obj: enc['encrypted_key'] = \ base64url_encode(obj['encrypted_key']) if 'header' in obj: enc['header'] = json_decode(obj['header']) return json_encode(enc)
def verify_cert_token(cert_token:str) -> dict: # In order to verify the signature of the JWT, we need first to get the public key that was used # The JWT is now in serialized format, so we need: # 1. Deserialize the JWT without verifying it (we do not yet have the public key) # 2. Get the 'kid' property from the header (the JOSE header of the JWT) # 3. The 'kid' has the format did#id where 'did' is the DID of the issuer and 'id' is the # identifier of the key in the DIDDocument associated to the DID # 4. Perform resolution of the DID of the issuer # 5. Get the public key specified inside the DIDDocument # 6. Verify the JWT using the public key associated to the DID # 7. Verify that the DID in the 'iss' field of the JWT payload is the same as the one that # signed the JWT # 1. Deserialize the JWT without verifying it (we do not yet have the public key) cert_obj = jwt.JWT() cert_obj.deserialize(jwt=cert_token) # Get the protected header of the JWT header = cert_obj.token.jose_header # 2. Get the 'kid' property from the header (the JOSE header of the JWT) key_id = str(header["kid"]) # 3. The 'kid' has the format did#id where 'did' is the DID of the issuer and 'id' is the # identifier of the key in the DIDDocument associated to the DID key_components = key_id.split("#") if len(key_components) != 2: print(f"KeyID in JOSE header invalid: {header}") return None # Get the DID component DID = key_components[0] # 4. Perform resolution of the DID of the issuer, and get the public key _DID, name, didDoc, active = tf.resolver.resolveDID(DID) if didDoc is None: print(f"No DIDDoc found for DID: {DID}") return None # 5. Get the public key specified inside the DIDDocument keys = didDoc["publicKey"] publicKeyJwk = None for key in keys: if key["id"] == key_id: publicKeyJwk = key["publicKeyJwk"] if publicKeyJwk is None: print(f"Key ID not found in DIDDoc: {key_id}") return None jwk_key = jwk.JWK(**publicKeyJwk) # 6. Verify the JWT using the public key associated to the DID cert_obj = jwt.JWT( jwt=cert_token, key=jwk_key, algs=["ES256K"] ) # 7. Verify that the DID in the 'iss' field of the JWT payload is the same as the one that # signed the JWT claims = json_decode(cert_obj.claims) if claims["iss"] != DID: print(f"Token issuer is not the same as specified in the header") return None return claims