def test_attack_pubkey_deception_jwk(self): rsa256_token_header = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9" rsa256_token_payload = "eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ" rsa256_token_signature = "ZR8EJ-ssd7b3Z9ZrCODkK_tJvOVXNZbcUn_FQAbLlKjPdEkNNCP_i5h84QfRXA8cWu1Z83UOb25Oogw0GIE9k-Q4AJO-BwEC2muHKRdzgHGrO6_2abeFJxAZapSf4ww39UlGzYX22b2kRYECLmsVaa0NV1KyQIf147h460wDGDr1wd2OCvUZXYULA_vzzRo3HHX440XOx_CnBvrR7shqnIjOLycEG61Ganq6oJsujVXHTQUtKdNB1Amu9NEQRRQzYWSTLuUMwV9mJnCuyr9bWp5srd3VPOC1bMXU2UgJiauw8eYu_w2_bbgZOn0jwajiygkfuJXNJGi8k_09sJmJ0w" rsa_token_ser = ".".join([ rsa256_token_header, rsa256_token_payload, rsa256_token_signature ]) rsa_token = TestJWT.deserialize(rsa_token_ser) self.assertEqual("RS256", rsa_token.get_algorithm()) self.assertEqual("JWT", rsa_token.header['typ']) self.assertEqual(2, len(rsa_token.header)) self.assertEqual("1234567890", rsa_token.payload['sub']) self.assertEqual("John Doe", rsa_token.payload['name']) self.assertEqual(1516239022, rsa_token.payload['iat']) self.assertEqual(3, len(rsa_token.payload)) pk: RSAPrivateKey = load_pem_private_key( utils.read_pem_file("./keys/pk1.pem"), None, default_backend()) pubkey: RSAPublicKey = pk.public_key() self.assertTrue( rsa_token.verify_signature( pubkey, utils.base64url_decode(rsa256_token_signature))) rsa_token_jwk = TestJWT.deserialize(rsa_token_ser) kid = "my_own_key" attack = Attack(rsa_token_ser) attack.attack_pubkey_deception_jwk(pk, kid) payloads = attack.payloads rsa_token_jwk = TestJWT.deserialize( payloads['attack_pubkey_deception_jwk']) self.assertEqual("RS256", rsa_token_jwk.get_algorithm()) self.assertEqual("JWT", rsa_token_jwk.header['typ']) self.assertEqual("RSA", rsa_token_jwk.header['jwk']['kty']) self.assertEqual(kid, rsa_token_jwk.header['jwk']['kid']) self.assertEqual("sig", rsa_token_jwk.header['jwk']['use']) e = pk.public_key().public_numbers().e n = pk.public_key().public_numbers().n expected_n = utils.force_unicode( utils.base64url_encode( n.to_bytes((n.bit_length() + 7) // 8, byteorder='big'))) expected_e = utils.force_unicode( utils.base64url_encode( e.to_bytes((e.bit_length() + 7) // 8, byteorder='big'))) self.assertEqual(expected_n, rsa_token_jwk.header['jwk']['n']) self.assertEqual(expected_e, rsa_token_jwk.header['jwk']['e']) self.assertEqual(3, len(rsa_token_jwk.header)) self.assertEqual("1234567890", rsa_token_jwk.payload['sub']) self.assertEqual("John Doe", rsa_token_jwk.payload['name']) self.assertEqual(1516239022, rsa_token_jwk.payload['iat']) self.assertEqual(3, len(rsa_token_jwk.payload)) rsa_token_jwk_signature = rsa_token_jwk.compute_signature(pk) self.assertTrue( rsa_token_jwk.verify_signature( pubkey, utils.base64url_decode(rsa_token_jwk_signature))) expected_signature = "jheDPqm8kxisAUcuDX-gHdTEIQDan_qRphZddj2VkcW-wJbVARly1Wzaw4of96Dl0xJXqJBV_kKG5UmmEiiEjQWUZzY4_XuZmI_STEf7FvVe_OPbN6fmyzGJQZSJVaoXTMYw8_yhQ1fIip6ctGVKrt6752RolratwDsYVZawQ1J9V3WIioPvXjQoyEGbothy7yOxemZXB71SMcQ-mmLKc8v0mirA8kyR8Wg0lw_JkRVRbViItn6SMFg2_Fuf12P1rkxx2qo3BVPzvgz1Nln4U8kFe2HGdtpc2IMTrAvzO0hOhc8cBe2-9HZJAFdGd_6SPhkp1VVlx-qTIL2y0EqH4Q" self.assertEqual(expected_signature, utils.force_unicode(rsa_token_jwk_signature))
def encode(payload, key, algorithm='HS256'): segments = [] header = {"typ": "JWT", "alg": algorithm} segments.append(base64url_encode(json.dumps(header))) segments.append(base64url_encode(json.dumps(payload))) signing_input = '.'.join(segments) try: signature = JWS(header, payload).sign(key) except KeyError: raise NotImplementedError("Algorithm not supported") segments.append(base64url_encode(signature)) return '.'.join(segments)
def verify(alg, contents, key, sig): if key is not None and isinstance(key, str): key = utils.force_bytes(key) test_sig = "" if alg == "HS256": test_sig = utils.base64url_encode( hmac.new(key, contents, hashlib.sha256).digest()) elif alg == "HS384": test_sig = utils.base64url_encode( hmac.new(key, contents, hashlib.sha384).digest()) elif alg == "HS512": test_sig = utils.base64url_encode( hmac.new(key, contents, hashlib.sha512).digest()) elif alg == "RS256": rsa_pk: RSAPublicKey = key rsa_pk.verify(signature=sig, data=contents, padding=padding.PKCS1v15(), algorithm=hashes.SHA256()) return True elif alg == "RS384": rsa_pk: RSAPublicKey = key rsa_pk.verify(signature=sig, data=contents, padding=padding.PKCS1v15(), algorithm=hashes.SHA384()) return True elif alg == "RS512": rsa_pk: RSAPublicKey = key rsa_pk.verify(signature=sig, data=contents, padding=padding.PKCS1v15(), algorithm=hashes.SHA512()) return True else: print("Unknown algorithm {}".format(alg)) verified = False if test_sig == sig: verified = True if len(key) > 16: print("Signature verified using key {}...".format(key[0:16])) else: print("Signature verified using key {}...".format(key)) else: if len(key) > 16: print("WARN Signature verification fail using key {}...".format( key[0:16])) else: print( "WARN Signature verification fail using key {}...".format(key)) return verified
def build_section(dict_section): """ Builds a JWT section (header or payload) by properly encoding content. :param dict_section: JSON (dict) or plain text (str) content to be encoded :return: JWT encoded section """ if utils.is_dict(dict_section): new_section = utils.base64url_encode( utils.force_bytes( json.dumps(dict_section, separators=(",", ":")))) else: new_section = utils.base64url_encode( utils.force_bytes(dict_section)) return new_section
def compute_signature(self, key): alg = self.get_algorithm() print("Computing signature using alg {}".format(alg)) if alg is None: print("Signature algorithm not set") return False if str.capitalize(alg) == "NONE": return self.build_token_without_signature() # TODO reuse build_section content_to_sign = utils.base64url_encode( utils.force_bytes(json.dumps(self.header, separators=( ",", ":")))) + utils.force_bytes(".") + utils.base64url_encode( utils.force_bytes( json.dumps(self.payload, separators=(",", ":")))) return signatures.sign(alg, content_to_sign, key=key)
def public_key_b64(self): """ Convert the public key as base64 for sharing :return: Base64 Encoded Str() """ public_numbers = self.public_key.public_numbers() return utils.base64url_encode('{x}{y}'.format(x=int2bytes(public_numbers.x), y=int2bytes(public_numbers.y)))
def sign(alg, contents, key=None): print('Computing signature for alg {}'.format(alg)) # TODO move compute signature None here from attack_none if key is not None and isinstance(key, str): key = utils.force_bytes(key) if contents is not None: contents = utils.force_bytes(contents) if alg.lower() == 'none': return b'' if alg == "HS256": return utils.base64url_encode( hmac.new(key, contents, hashlib.sha256).digest()) if alg == "HS384": return utils.base64url_encode( hmac.new(key, contents, hashlib.sha384).digest()) if alg == "HS512": return utils.base64url_encode( hmac.new(key, contents, hashlib.sha512).digest()) if alg == "RS256": rsa_pk: RSAPrivateKey = key return utils.base64url_encode( rsa_pk.sign(data=contents, padding=padding.PKCS1v15(), algorithm=hashes.SHA256())) if alg == "RS384": rsa_pk: RSAPrivateKey = key return utils.base64url_encode( rsa_pk.sign(contents, padding.PKCS1v15(), hashes.SHA384())) if alg == "RS512": rsa_pk: RSAPrivateKey = key return utils.base64url_encode( rsa_pk.sign(contents, padding.PKCS1v15(), hashes.SHA512())) return utils.force_bytes('N/A')
def make_jwt(claims, authorized_token): """ Convert claims into JWT :type claims: Dictionary that will be converted to json :param claims: payload data :param authorized_token: Token to sign the request :return: JWT """ alg = {'alg': 'ES256', 'typ': 'JWT'} alg_serialized = json.dumps(alg) alg_b64 = utils.base64url_encode(alg_serialized) claims_serialized = json.dumps(claims) claims_b64 = utils.base64url_encode(claims_serialized) payload = '{alg}.{claims}'.format(alg=alg_b64, claims=claims_b64) signature = authorized_token.sign(payload) return '{payload}.{sig}'.format(payload=payload, sig=signature)
def sign(self, payload): """ Sign a payload :param payload: String (usually jwt payload) :return: URL safe base64 signature """ signer = self._private_key.signer(ec.ECDSA(hashes.SHA256())) signer.update(payload) signature = signer.finalize() r, s = decode_dss_signature(signature) b64_signature = utils.base64url_encode('{r}{s}'.format(r=int2bytes(r), s=int2bytes(s))) return b64_signature