Exemple #1
1
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.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
    )
Exemple #2
0
 def make_tok(self, key, alg, name):
     pri_key = JWK(**key)
     protected = {"typ": "JOSE+JSON", "kid": key["kid"], "alg": alg}
     plaintext = {"sub": name, "exp": int(time.time()) + (5 * 60)}
     S = JWS(payload=json_encode(plaintext))
     S.add_signature(pri_key, None, json_encode(protected))
     return S.serialize()
Exemple #3
0
 def make_tok(self, key, alg, name):
     pri_key = JWK(**key)
     protected = {"typ": "JOSE+JSON", "kid": key['kid'], "alg": alg}
     plaintext = {"sub": name, "exp": int(time.time()) + (5 * 60)}
     jws = JWS(payload=json_encode(plaintext))
     jws.add_signature(pri_key, None, json_encode(protected))
     return jws.serialize()
Exemple #4
0
def sign_request(payload, nonce, jwk):
    # type: (dict, str, JWK) -> None
    header = generate_header(jwk)
    protected = dict(nonce=nonce)
    jws = JWS(json_encode(payload).encode())
    jws.add_signature(jwk, header['alg'], protected, header)
    return json_decode(jws.serialize())
Exemple #5
0
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))
Exemple #6
0
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))
Exemple #7
0
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']))
Exemple #8
0
def verify_proof(did_document: list, proof: jws.JWS, signer: str):
    document_sha256 = hashlib.sha256()
    document_sha256.update(json.dumps(did_document).encode('utf-8'))
    document_sha256_b64 = base64url_encode(document_sha256.digest())
    payload = json.loads(proof.objects['payload'].decode())
    if (document_sha256_b64 != payload['sha-256']):
        raise Exception("The sha-256 field of the proof payload is not valid")
        return -1
    signer_jwk = did_to_jwk(signer)
    proof.verify(signer_jwk)
Exemple #9
0
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']))
Exemple #10
0
    def resolve(self, next, root, info, **args):
        headers = info.context.headers
        if "Authorization" in headers:
            token = JWS()
            token.deserialize(headers["Authorization"].removeprefix("Bearer "))

            byte_payload = token.objects.pop("payload")
            json_payload = byte_payload.decode("UTF-8")
            payload = json.loads(json_payload)
            info.context.token = payload

        return next(root, info, **args)
Exemple #11
0
    def make_signed_token(self, key):
        """Signs the payload.

        Creates a JWS token with the header as the JWS protected header and
        the claims as the payload. See (:class:`jwcrypto.jws.JWS`) for
        details on the exceptions that may be reaised.

        :param key: A (:class:`jwcrypto.jwk.JWK`) key.
        """

        t = JWS(self.claims)
        t.add_signature(key, protected=self.header)
        self.token = t
Exemple #12
0
    def make_signed_token(self, key):
        """Signs the payload.

        Creates a JWS token with the header as the JWS protected header and
        the claims as the payload. See (:class:`jwcrypto.jws.JWS`) for
        details on the exceptions that may be reaised.

        :param key: A (:class:`jwcrypto.jwk.JWK`) key.
        """

        t = JWS(self.claims)
        t.add_signature(key, protected=self.header)
        self.token = t
Exemple #13
0
    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:
            logger.debug('Failed to validate message', exc_info=True)
            raise InvalidMessage('Failed to validate message: %s' % str(e))

        check_kem_claims(claims, name)
        self.name = name
        self.payload = claims.get('value')
        self.msg_type = 'kem'

        return {'type': self.msg_type,
                'value': {'kid': self.client_keys[KEY_USAGE_ENC].key_id,
                          'claims': claims}}
def sign(message: str, signing_key: JWK, sha1_thumbprint: str,
         sha256_thumbprint: str) -> str:
    """Create a signature layer for a message for DCS"""
    jwstoken = JWS(payload=message)
    jwstoken.add_signature(
        key=signing_key,
        alg=None,
        protected=json_encode({
            "alg": "RS256",
            "x5t": sha1_thumbprint,
            "x5t#S256": sha256_thumbprint
        }),
    )
    return jwstoken.serialize(compact=True)
Exemple #15
0
    def deserialize(self, jwt, key=None):
        """Deserialize a JWT token.

        NOTE: Destroys any current status and tries to import the raw
        token provided.

        :param jwt: a 'raw' JWT token.
        :param key: A (:class:`jwcrypto.jwk.JWK`) verification or
         decryption key, or a (:class:`jwcrypto.jwk.JWKSet`) that
         contains a key indexed by the 'kid' header.
        """
        c = jwt.count('.')
        if c == 2:
            self.token = JWS()
        elif c == 4:
            self.token = JWE()
        else:
            raise ValueError("Token format unrecognized")

        # Apply algs restrictions if any, before performing any operation
        if self._algs:
            self.token.allowed_algs = self._algs

        # now deserialize and also decrypt/verify (or raise) if we
        # have a key
        if key is None:
            self.token.deserialize(jwt, None)
        elif isinstance(key, JWK):
            self.token.deserialize(jwt, key)
        elif isinstance(key, JWKSet):
            self.token.deserialize(jwt, None)
            if 'kid' not in self.token.jose_header:
                raise JWTMissingKeyID('No key ID in JWT header')

            token_key = key.get_key(self.token.jose_header['kid'])
            if not token_key:
                raise JWTMissingKey('Key ID %s not in key set' %
                                    self.token.jose_header['kid'])

            if isinstance(self.token, JWE):
                self.token.decrypt(token_key)
            elif isinstance(self.token, JWS):
                self.token.verify(token_key)
            else:
                raise RuntimeError("Unknown Token Type")
        else:
            raise ValueError("Unrecognized Key Type")

        if key is not None:
            self.header = self.token.jose_header
            self.claims = self.token.payload.decode('utf-8')
            self._check_provided_claims()
Exemple #16
0
    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:
            logger.debug('Failed to validate message', exc_info=True)
            raise InvalidMessage('Failed to validate message: %s' % str(e))

        check_kem_claims(claims, name)
        self.name = name
        self.payload = claims.get('value')
        self.msg_type = 'kem'

        return {
            'type': self.msg_type,
            'value': {
                'kid': self.client_keys[KEY_USAGE_ENC].key_id,
                'claims': claims
            }
        }
Exemple #17
0
    def _validateSignature(self, cvs):
        if cvs.antecedent is None:
            # Special cased to bootstrap data structures
            cvs.ratchet(self)

        jws = JWS()
        jws.deserialize(self.serialize())
        tprint = jws.jose_header["kid"]
        if (cvs.antecedent.pkt == tprint):
            key = keystore()[tprint]
            jws.verify(key)
        else:
            # XXX: This case is only revavent on the GodBlock
            # TODO: support cases where block isn't signed by preceding key
            # (key recovery, issuer tombstone)
            raise NotImplementedError("TODO")  # pragma: no cover
Exemple #18
0
    def _validateSignature(self, cvs):
        if cvs.antecedent is None:
            # Special cased to bootstrap data structures
            cvs.ratchet(self)

        jws = JWS()
        jws.deserialize(self.serialize())
        tprint = jws.jose_header["kid"]
        idchain = chainstore()[self.creator]
        if self.creator not in cvs._recent_thumbprints:
            raise ChainValidationError("No grants for creator: " + self.creator)
        creator_print = cvs._recent_thumbprints[self.creator]

        if idchain.isSameOrSubsequent(tprint, creator_print):
            key = keystore()[tprint]
            jws.verify(key)
        else:
            raise ChainValidationError("Out of date key.")
Exemple #19
0
def verify_jwt(jwt,
               pub_key=None,
               allowed_algs=None,
               iat_skew=timedelta(),
               checks_optional=False,
               ignore_not_implemented=False):
    """
    Verify a JSON Web Token.

    :param jwt: The JSON Web Token to verify.
    :type jwt: str or unicode

    :param pub_key: The public key to be used to verify the token. Note: if you pass ``None`` and **allowed_algs** contains ``none`` then the token's signature will not be verified.
    :type pub_key: `jwcrypto.jwk.JWK <https://jwcrypto.readthedocs.io/en/latest/jwk.html>`_

    :param allowed_algs: Algorithms expected to be used to sign the token. The ``in`` operator is used to test membership.
    :type allowed_algs: list or NoneType (meaning an empty list)

    :param iat_skew: The amount of leeway to allow between the issuer's clock and the verifier's clock when verifiying that the token was generated in the past. Defaults to no leeway.
    :type iat_skew: datetime.timedelta

    :param checks_optional: If ``False``, then the token must contain the **typ** header property and the **iat**, **nbf** and **exp** claim properties.
    :type checks_optional: bool

    :param ignore_not_implemented: If ``False``, then the token must *not* contain the **jku**, **jwk**, **x5u**, **x5c** or **x5t** header properties.
    :type ignore_not_implemented: bool

    :rtype: tuple
    :returns: ``(header, claims)`` if the token was verified successfully. The token must pass the following tests:

    - Its header must contain a property **alg** with a value in **allowed_algs**.
    - Its signature must verify using **pub_key** (unless its algorithm is ``none`` and ``none`` is in **allowed_algs**).
    - If the corresponding property is present or **checks_optional** is ``False``:

      - Its header must contain a property **typ** with the value ``JWT``.
      - Its claims must contain a property **iat** which represents a date in the past (taking into account :obj:`iat_skew`).
      - Its claims must contain a property **nbf** which represents a date in the past.
      - Its claims must contain a property **exp** which represents a date in the future.

    :raises: If the token failed to verify.
    """
    if allowed_algs is None:
        allowed_algs = []

    if not isinstance(allowed_algs, list):
        # jwcrypto only supports list of allowed algorithms
        raise _JWTError('allowed_algs must be a list')

    header, claims, _ = jwt.split('.')

    parsed_header = json_decode(base64url_decode(header))

    alg = parsed_header.get('alg')
    if alg is None:
        raise _JWTError('alg header not present')
    if alg not in allowed_algs:
        raise _JWTError('algorithm not allowed: ' + alg)

    if not ignore_not_implemented:
        for k in parsed_header:
            if k not in JWSHeaderRegistry:
                raise _JWTError('unknown header: ' + k)
            if not JWSHeaderRegistry[k].supported:
                raise _JWTError('header not implemented: ' + k)

    if pub_key:
        token = JWS()
        token.allowed_algs = allowed_algs
        token.deserialize(jwt, pub_key)
    elif 'none' not in allowed_algs:
        raise _JWTError('no key but none alg not allowed')

    parsed_claims = json_decode(base64url_decode(claims))

    utcnow = datetime.utcnow()
    now = timegm(utcnow.utctimetuple())

    typ = parsed_header.get('typ')
    if typ is None:
        if not checks_optional:
            raise _JWTError('typ header not present')
    elif typ != 'JWT':
        raise _JWTError('typ header is not JWT')

    iat = parsed_claims.get('iat')
    if iat is None:
        if not checks_optional:
            raise _JWTError('iat claim not present')
    elif iat > timegm((utcnow + iat_skew).utctimetuple()):
        raise _JWTError('issued in the future')

    nbf = parsed_claims.get('nbf')
    if nbf is None:
        if not checks_optional:
            raise _JWTError('nbf claim not present')
    elif nbf > now:
        raise _JWTError('not yet valid')

    exp = parsed_claims.get('exp')
    if exp is None:
        if not checks_optional:
            raise _JWTError('exp claim not present')
    elif exp <= now:
        raise _JWTError('expired')

    return parsed_header, parsed_claims
Exemple #20
0
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)
Exemple #21
0
def verify_jwt(jwt,
               pub_key=None,
               allowed_algs=None,
               iat_skew=timedelta(),
               checks_optional=False,
               ignore_not_implemented=False):
    """
    Verify a JSON Web Token.

    :param jwt: The JSON Web Token to verify.
    :type jwt: str or unicode

    :param pub_key: The public key to be used to verify the token. Note: if you pass ``None`` and **allowed_algs** contains ``none`` then the token's signature will not be verified.
    :type pub_key: `jwcrypto.jwk.JWK <https://jwcrypto.readthedocs.io/en/latest/jwk.html>`_

    :param allowed_algs: Algorithms expected to be used to sign the token. The ``in`` operator is used to test membership.
    :type allowed_algs: list or NoneType (meaning an empty list)

    :param iat_skew: The amount of leeway to allow between the issuer's clock and the verifier's clock when verifiying that the token was generated in the past. Defaults to no leeway.
    :type iat_skew: datetime.timedelta

    :param checks_optional: If ``False``, then the token must contain the **typ** header property and the **iat**, **nbf** and **exp** claim properties.
    :type checks_optional: bool

    :param ignore_not_implemented: If ``False``, then the token must *not* contain the **jku**, **jwk**, **x5u**, **x5c** or **x5t** header properties.
    :type ignore_not_implemented: bool

    :rtype: tuple
    :returns: ``(header, claims)`` if the token was verified successfully. The token must pass the following tests:

    - Its header must contain a property **alg** with a value in **allowed_algs**.
    - Its signature must verify using **pub_key** (unless its algorithm is ``none`` and ``none`` is in **allowed_algs**).
    - If the corresponding property is present or **checks_optional** is ``False``:

      - Its header must contain a property **typ** with the value ``JWT``.
      - Its claims must contain a property **iat** which represents a date in the past (taking into account :obj:`iat_skew`).
      - Its claims must contain a property **nbf** which represents a date in the past.
      - Its claims must contain a property **exp** which represents a date in the future.

    :raises: If the token failed to verify.
    """
    if allowed_algs is None:
        allowed_algs = []

    if not isinstance(allowed_algs, list):
        # jwcrypto only supports list of allowed algorithms
        raise _JWTError('allowed_algs must be a list')

    header, claims, _ = jwt.split('.')

    parsed_header = json_decode(base64url_decode(header))

    alg = parsed_header.get('alg')
    if alg is None:
        raise _JWTError('alg header not present')
    if alg not in allowed_algs:
        raise _JWTError('algorithm not allowed: ' + alg)

    if not ignore_not_implemented:
        for k in parsed_header:
            if k not in JWSHeaderRegistry:
                raise _JWTError('unknown header: ' + k)
            if not JWSHeaderRegistry[k].supported:
                raise _JWTError('header not implemented: ' + k)

    if pub_key:
        token = JWS()
        token.allowed_algs = allowed_algs
        token.deserialize(jwt, pub_key)
    elif 'none' not in allowed_algs:
        raise _JWTError('no key but none alg not allowed')

    parsed_claims = json_decode(base64url_decode(claims))

    utcnow = datetime.utcnow()
    now = timegm(utcnow.utctimetuple())

    typ = parsed_header.get('typ')
    if typ is None:
        if not checks_optional:
            raise _JWTError('typ header not present')
    elif typ != 'JWT':
        raise _JWTError('typ header is not JWT')

    iat = parsed_claims.get('iat')
    if iat is None:
        if not checks_optional:
            raise _JWTError('iat claim not present')
    elif iat > timegm((utcnow + iat_skew).utctimetuple()):
        raise _JWTError('issued in the future')

    nbf = parsed_claims.get('nbf')
    if nbf is None:
        if not checks_optional:
            raise _JWTError('nbf claim not present')
    elif nbf > now:
        raise _JWTError('not yet valid')

    exp = parsed_claims.get('exp')
    if exp is None:
        if not checks_optional:
            raise _JWTError('exp claim not present')
    elif exp <= now:
        raise _JWTError('expired')

    return parsed_header, parsed_claims
    def deserialize(self, jwt, key=None):
        """Deserialize a JWT token.

        NOTE: Destroys any current status and tries to import the raw
        token provided.

        :param jwt: a 'raw' JWT token.
        :param key: A (:class:`jwcrypto.jwk.JWK`) verification or
         decryption key, or a (:class:`jwcrypto.jwk.JWKSet`) that
         contains a key indexed by the 'kid' header.
        """
        c = jwt.count('.')
        if c == 2:
            self.token = JWS()
        elif c == 4:
            self.token = JWE()
        else:
            raise ValueError("Token format unrecognized")

        # Apply algs restrictions if any, before performing any operation
        if self._algs:
            self.token.allowed_algs = self._algs

        self.deserializelog = list()
        # now deserialize and also decrypt/verify (or raise) if we
        # have a key
        if key is None:
            self.token.deserialize(jwt, None)
        elif isinstance(key, JWK):
            self.token.deserialize(jwt, key)
            self.deserializelog.append("Success")
        elif isinstance(key, JWKSet):
            self.token.deserialize(jwt, None)
            if 'kid' in self.token.jose_header:
                kid_key = key.get_key(self.token.jose_header['kid'])
                if not kid_key:
                    raise JWTMissingKey('Key ID %s not in key set'
                                        % self.token.jose_header['kid'])
                self.token.deserialize(jwt, kid_key)
            else:
                for k in key:
                    try:
                        self.token.deserialize(jwt, k)
                        self.deserializelog.append("Success")
                        break
                    except Exception as e:  # pylint: disable=broad-except
                        keyid = k.key_id
                        if keyid is None:
                            keyid = k.thumbprint()
                        self.deserializelog.append('Key [%s] failed: [%s]' % (
                            keyid, repr(e)))
                        continue
                if "Success" not in self.deserializelog:
                    raise JWTMissingKey('No working key found in key set')
        else:
            raise ValueError("Unrecognized Key Type")

        if key is not None:
            self.header = self.token.jose_header
            self.claims = self.token.payload.decode('utf-8')
            self._check_provided_claims()
def unwrap_signature(message: str, signing_certificate: JWK) -> str:
    """Validate and strip a signature from a response from DCS"""
    jwstoken = JWS()
    jwstoken.deserialize(raw_jws=message, key=signing_certificate)
    return jwstoken.payload.decode("utf-8")