diff --git a/adal/__init__.py b/adal/__init__.py index fe37f88b..7e9775d3 100644 --- a/adal/__init__.py +++ b/adal/__init__.py @@ -27,7 +27,7 @@ # pylint: disable=wrong-import-position -__version__ = '1.1.0' +__version__ = '1.2.0' import logging diff --git a/adal/authentication_context.py b/adal/authentication_context.py index 45b3894c..8b5ca270 100644 --- a/adal/authentication_context.py +++ b/adal/authentication_context.py @@ -235,19 +235,22 @@ def token_func(self): return self._acquire_token(token_func) def acquire_token_with_client_certificate(self, resource, client_id, - certificate, thumbprint): + certificate, thumbprint, public_certificate=None): '''Gets a token for a given resource via certificate credentials :param str resource: A URI that identifies the resource for which the token is valid. :param str client_id: The OAuth client id of the calling application. :param str certificate: A PEM encoded certificate private key. - :param str thumbprint: hex encoded thumbprint of the certificate. + :param str thumbprint: hex encoded thumbprint of the certificate. + :param public_certificate(optional): if not None, it will be sent to the service for subject name + and issuer based authentication, which is to support cert auto rolls. The value must match the + certificate private key parameter. :returns: dict with several keys, include "accessToken". ''' def token_func(self): token_request = TokenRequest(self._call_context, self, client_id, resource) - return token_request.get_token_with_certificate(certificate, thumbprint) + return token_request.get_token_with_certificate(certificate, thumbprint, public_certificate) return self._acquire_token(token_func) diff --git a/adal/mex.py b/adal/mex.py index a2e3e75a..3ffa14e1 100644 --- a/adal/mex.py +++ b/adal/mex.py @@ -144,7 +144,7 @@ def _select_username_password_polices(self, xpath): policy_id = self._check_policy(policy_node) if policy_id: id_ref = '#' + policy_id - policies[id_ref] = {id:id_ref} + policies[id_ref] = {policy_id:id_ref} return policies if policies else None diff --git a/adal/self_signed_jwt.py b/adal/self_signed_jwt.py index 54c0fd99..66bfb9a8 100644 --- a/adal/self_signed_jwt.py +++ b/adal/self_signed_jwt.py @@ -78,12 +78,13 @@ def __init__(self, call_context, authority, client_id): self._token_endpoint = authority.token_endpoint self._client_id = client_id - def _create_header(self, thumbprint): + def _create_header(self, thumbprint, public_certificate): x5t = _create_x5t_value(thumbprint) header = {'typ':'JWT', 'alg':'RS256', 'x5t':x5t} - - self._log.debug("Creating self signed JWT header. x5t: %(x5t)s", - {"x5t": x5t}) + if public_certificate: + header['x5c'] = public_certificate + self._log.debug("Creating self signed JWT header. x5t: %(x5t)s, x5c: %(x5c)s", + {"x5t": x5t, "x5c": public_certificate}) return header @@ -117,8 +118,9 @@ def _reduce_thumbprint(self, thumbprint): self._raise_on_invalid_thumbprint(canonical) return canonical - def create(self, certificate, thumbprint): + def create(self, certificate, thumbprint, public_certificate): thumbprint = self._reduce_thumbprint(thumbprint) - header = self._create_header(thumbprint) + + header = self._create_header(thumbprint, public_certificate) payload = self._create_payload() return _sign_jwt(header, payload, certificate) diff --git a/adal/token_request.py b/adal/token_request.py index dd6f4501..6332708b 100644 --- a/adal/token_request.py +++ b/adal/token_request.py @@ -351,20 +351,20 @@ def get_token_from_cache_with_refresh(self, user_id): self._user_id = user_id return self._find_token_from_cache() - def _create_jwt(self, certificate, thumbprint): + def _create_jwt(self, certificate, thumbprint, public_certificate): ssj = self._create_self_signed_jwt() - jwt = ssj.create(certificate, thumbprint) + jwt = ssj.create(certificate, thumbprint, public_certificate) if not jwt: raise AdalError("Failed to create JWT.") return jwt - def get_token_with_certificate(self, certificate, thumbprint): + def get_token_with_certificate(self, certificate, thumbprint, public_certificate): self._log.info("Getting a token via certificate.") - jwt = self._create_jwt(certificate, thumbprint) + jwt = self._create_jwt(certificate, thumbprint, public_certificate) oauth_parameters = self._create_oauth_parameters(OAUTH2_GRANT_TYPE.CLIENT_CREDENTIALS) oauth_parameters[OAUTH2_PARAMETERS.CLIENT_ASSERTION_TYPE] = OAUTH2_GRANT_TYPE.JWT_BEARER diff --git a/tests/test_self_signed_jwt.py b/tests/test_self_signed_jwt.py index 629e381b..47c22552 100644 --- a/tests/test_self_signed_jwt.py +++ b/tests/test_self_signed_jwt.py @@ -52,13 +52,16 @@ class TestSelfSignedJwt(unittest.TestCase): testNowDate = cp['nowDate'] testJwtId = cp['jwtId'] - expectedJwt = cp['expectedJwt'] + expectedJwtWithThumbprint = cp['expectedJwtWithThumbprint'] + expectedJwtWithPublicCert = cp['expectedJwtWithPublicCert'] + unexpectedJwt = 'unexpectedJwt' testAuthority = Authority('https://login.windows.net/naturalcauses.com', False) testClientId = 'd6835713-b745-48d1-bb62-7a8248477d35' testCert = cp['cert'] + testPublicCert=cp['publicCert'] - def _create_jwt(self, cert, thumbprint, encodeError = None): + def _create_jwt(self, cert, thumbprint, public_certificate = None, encodeError = None): ssjwt = SelfSignedJwt(cp['callContext'], self.testAuthority, self.testClientId) self_signed_jwt._get_date_now = mock.MagicMock(return_value = self.testNowDate) @@ -67,19 +70,24 @@ def _create_jwt(self, cert, thumbprint, encodeError = None): if encodeError: self_signed_jwt._encode_jwt = mock.MagicMock(return_value = self.unexpectedJwt) else: - self_signed_jwt._encode_jwt = mock.MagicMock(return_value = self.expectedJwt) + expected = self.expectedJwtWithPublicCert if public_certificate else self.expectedJwtWithThumbprint + self_signed_jwt._encode_jwt = mock.MagicMock(return_value = expected) - jwt = ssjwt.create(cert, thumbprint) + jwt = ssjwt.create(cert, thumbprint, public_certificate=public_certificate) return jwt def _create_jwt_and_match_expected_err(self, testCert, thumbprint, encodeError = None): with self.assertRaises(Exception): - self._create_jwt(testCert, thumbprint, encodeError) + self._create_jwt(testCert, thumbprint, encodeError = encodeError) def _create_jwt_and_match_expected_jwt(self, cert, thumbprint): jwt = self._create_jwt(cert, thumbprint) self.assertTrue(jwt, 'No JWT generated') - self.assertTrue(jwt == self.expectedJwt, 'Generated JWT does not match expected:{}'.format(jwt)) + self.assertTrue(jwt == self.expectedJwtWithThumbprint, 'Generated JWT does not match expected:{}'.format(jwt)) + + def test_jwt_hash_with_public_cert(self): + jwt = self._create_jwt(self.testCert, cp['certHash'], public_certificate = self.testPublicCert) + self.assertTrue(jwt == self.expectedJwtWithPublicCert, 'Generated JWT does not match expected:{}'.format(jwt)) def test_create_jwt_hash_colons(self): self._create_jwt_and_match_expected_jwt(self.testCert, cp['certHash']) @@ -93,7 +101,7 @@ def test_create_jwt_hash_straight_hex(self): self._create_jwt_and_match_expected_jwt(self.testCert, thumbprint) def test_create_jwt_invalid_cert(self): - self._create_jwt_and_match_expected_err('foobar', cp['certHash'], True) + self._create_jwt_and_match_expected_err('foobar', cp['certHash'], encodeError = True) def test_create_jwt_invalid_thumbprint_1(self): self._create_jwt_and_match_expected_err(self.testCert, 'zzzz') diff --git a/tests/util.py b/tests/util.py index 9ffe439e..f855df44 100644 --- a/tests/util.py +++ b/tests/util.py @@ -176,41 +176,58 @@ # This is a dummy RSA private cert used for testing purpose.It does not represent valid credential. # privatePem variable is a fake certificate in the form of a string. def get_self_signed_cert(): - private_pem = ("-----BEGIN RSA PRIVATE KEY-----" - "MIIEpAIBAAKCAQEAoMGZTZi0vU/ICYVgV4vcTwzvZCNXdJ9EgGBBFu1E0/j4FF0Y" - "Fd2sP7IwmWVZLlWJ5VbwAtdMiRdrogX/QnWPfsNfsPzDdRRJD+Erh9tmBzJm08h7" - "1RggS1/VehZ9WNdTDlQM3P+zNg0IG274VIr+ZSBzIbYxV6ecPdRU/EsZ5Wa5SCwG" - "Fu1qPJW8KY8yvse9PHdFiHjrmcZSKTbBCp/2grdBrk/N1jwtH6Yj100l7G69HPE/" - "4kXYRX9f/LjpzF77VMCj7UJtmb1yR3fRHpppbm7GkqvJFM2Kg3UG5fsp8nQBDRc+" - "R3kjm+DU05MoFdsfo3DkzpNJjDcLUPdANe+mWwIDAQABAoIBACdb/1r+XpJTbFjY" - "bSRCPCimtB5CgPEu5ajA6G7inQ2BUcw6luETq07VJA0KwXEUxHSAerdXW4fdUh8T" - "dNIi0oVo9I7y9DBATTs0GGJlF2//qSmFVrxv8chCqJQB2aLc5ZsGfTfG62v6eNeu" - "reKVPYApF8dTQnWBtkF1MXGsOaTuxEecrM6KbES97kElC0QsJ89sDnTUjKuihfc9" - "Q9IfWDbX/5WY6JL7XMbQtKRIzd+y/E9dpU3Hu+UErKWyb5pQiud81/Q/xQThSrVt" - "zpmXwlsEFCrSzDML+aOTDqrsRwypRc5sTNAMadkeRrrlo+5OzoUG1aTxco8tZ1MD" - "ch7RTJECgYEA1fqn93X6S1sA8R4lYOJUDd5JmEskKwLl5Vg2VSinSD1YyMdtPnQa" - "ZWCEbJGXN60tEd7ZjOM4wA0mIrwEkkApFWEiGpMe4aGTD11455rA5IfrZUGPXlcw" - "lkmt4wPytKx/xDLBfa8oAu33dFDe/nhRqQqAMTi7DAnttqjUxPg/N8UCgYEAwFNG" - "qLG+5+J4sq5xoXgDj0Zggi5hx2IEfN68BmyYHvfL8VSDkQMsiYXNAhTWByCkjW9D" - "j/hdouGlDwMCLWq0XPgO/XsSlU7jJExsrRch63kf72PTZP/qapSkOonCe9TViNTQ" - "KiRXu/v9OfJYSRPnpKz0/5goFSq7E12mBWZJJ58CgYEAvmmKNLSAobP+t5H68ycU" - "Yy7u0J31Nm0ixR7lYoyFp8wniKumdA//OT1VOgOoy/vIAoILl8rPQl+xEvG7I6YC" - "qSrBnWJT9bbBVcf5Aih9BCBLgdSATxRJgUNZgI2P2eUy4RXFhyFp+olmTdR1S38o" - "M8PLZYG1OTZQmd3NUOYT430CgYBzU7yEPgnPPTPJWefTvobL7JTEm5GQoQs14c54" - "P7g8obUO4vH+DBwx3yUfAWWSYpWqJjUqaPGlUY/L3673kwvS0AEVKS7sj6CPTLDC" - "XqO9cyWeRIsn/noQLVAJtkAER41AfvTQwHhHxoSDsfoU4DXAvuIvPouSncwOgdKj" - "XEGz2wKBgQDQmB/u4oGaPRf5DdasiAcqofYDEoo/OcpdRPeW2t5z7WDZcjeN4ajR" - "GDoQssBpy1fpsPnghksMhYZL2l9xiSInkFw87ax5EYBS43Mt5HfJPgwpEnA5yV3W" - "WGt3TBp7BgYOKhIID6803lBYfDmtQzdD+xMjlJKSQ9wfZYCuXrYwSg==" - "-----END RSA PRIVATE KEY-----") - - return private_pem - -parameters['certHash'] = 'C1:5D:EA:86:56:AD:DF:67:BE:80:31:D8:5E:BD:DC:5A:D6:C4:36:E1' + private_pem = ("-----BEGIN PRIVATE KEY-----\n" + "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDLTKKMn3WJiDnc" + "8VuJPDr1kj9VYr9zdobMUSJ9YgRb6Rz05YiUOlSeNiMA6Y7yrpVbSxewIAkVC/gI" + "W/Ywp4FtR9j/SzQ91HIHvmKBrOAorpPDS0ZQ6nfeaBtZ14UIF16H/OvgyMkweBBd" + "7pIbF4i7ty3gdhFzpN2xd+qXTeDVMtxaQOM8RTAVp1RUpuNpPSIQxo9dLyOotcAe" + "Fj8uc0mMa0o6DbdxD26RiBIOgzcsYr5WUCMihmE3h1EIbXtQC5YwFnU9Q8OgupWz" + "i31QE1fbFgWzpjx1/KU9Yb8doWs1jwXSo4UGYecxiOKBrFuj0I6Kyelv9dfiayZF" + "GpwR+FmXAgMBAAECggEABmSxk/SL0LhtAWrBsy4muIRR45CIbswibxh6GjFT68QH" + "+heh1O+Eq7kOHsA5k54z6jwRUaOgRX4r3a9urZcG9fXVeCnYSb19nIq7NFLIdd8P" + "nIuoeXD2NhNWENw7PcbmXSZyEI6f7RtJgHq5M4ro7OZU1gNAhz9/DU61HO8BDBNP" + "9TT9h2Kf5cAHC79lrRs7Cj9yLK/JFFPFSyTEqxo9O6xHQfRv6X25rZeYo9JzGaqP" + "mwTvmheEqW7a75apBEpbzqD0f0jT4anaOSab0D10LyD9qEiCpzgDKG8Q31c0y9zS" + "Utk0suVR35abo22LdtvXMSyQMDfOOx3hqbUZ9c34uQKBgQD+oJUtfUYT9DBg/+jR" + "t/u+Tq/VnpolciQiIpIvUArhIFmOzLkt/hjH8ozOJlRrFADUWAE+pSWuAMdPjAsi" + "NGRYueAPQ29bqRF+5i0cJHXlxVNsqhF1SD5z/qaKU/MVL358v++g5shazQm45gUg" + "BeXyeRc++aFBTUAjUyoDOCh+bQKBgQDMZTacfAcGORL8TVvjq+0IJ6IppICmwRVU" + "FlZ/7HSJ+F1itggp0Kn9xzEk7SPgU8w0koysN+2189wn77PhQAAp1oGH2xvubXIz" + "nnAFpS9XbmzrG3JhHEVJqMe2qZ/pFOSqZxnNAekyWcE3BLamBBrMIx4wJBpmxVkf" + "EWUGSvolkwKBgDqQ8P8Pi2jXh7En64MhUFQLgUIfQtFOGaWIUhtzy6zQZgkEaat8" + "gHKtBVn9Uvl2FmLBAzhHgA0vvKg9S+pIJrSJvFGGbzyj/JQ1mTaZ5Ew/QNsDmxRg" + "04yWi/PRL142GF/VPebCbl8EPjI7Jf6hnKxS0df4TvDYNeJqJIWtCxNZAoGBAI04" + "rUfnhe7txklepcujgW1t/OQqzdzpcXQczv0qAcdGPDe0r+U8UAeQ9kqeMniPTXtR" + "ejKPngVmjUlmm/FZCAPgOrUEVcMiCZLSuHGeFRyipky3NQsVvmXLYNm7T0p67hcy" + "jygPVvE8BHygHBaOpXlAFl6Kw1cYqaAGo7d6XGVTAoGBAL0FucFmEAZOH7Bcnl30" + "JMXMcoedCAMMZG235cL2xBz6z+MzWVMkiZxblOVqAHRExGDoT01fymVte1OoKx7Q" + "SKiGNXCVBatkk7PlRUVnL0ziSwgYVxNX9eGNXZRBXUH3BuoYPlfdUMH36vgmukbT" + "Ui28YpkjQ5RY1UwUY6tk+Bka\n" + "-----END PRIVATE KEY-----") + public_pem = ("MIICoTCCAYkCAgPoMA0GCSqGSIb3DQEBBQUAMBQxEjAQBgNVBAMMCUNMSS1Mb2dp" + "bjAiGA8yMDE4MTAxNzE2MTAxN1oYDzIwMTkxMDE3MTYxMDE5WjAUMRIwEAYDVQQD" + "DAlDTEktTG9naW4wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDLTKKM" + "n3WJiDnc8VuJPDr1kj9VYr9zdobMUSJ9YgRb6Rz05YiUOlSeNiMA6Y7yrpVbSxew" + "IAkVC/gIW/Ywp4FtR9j/SzQ91HIHvmKBrOAorpPDS0ZQ6nfeaBtZ14UIF16H/Ovg" + "yMkweBBd7pIbF4i7ty3gdhFzpN2xd+qXTeDVMtxaQOM8RTAVp1RUpuNpPSIQxo9d" + "LyOotcAeFj8uc0mMa0o6DbdxD26RiBIOgzcsYr5WUCMihmE3h1EIbXtQC5YwFnU9" + "Q8OgupWzi31QE1fbFgWzpjx1/KU9Yb8doWs1jwXSo4UGYecxiOKBrFuj0I6Kyelv" + "9dfiayZFGpwR+FmXAgMBAAEwDQYJKoZIhvcNAQEFBQADggEBADfEqXzcI/fs82T0" + "9B3H3lGWQL1JlcxOxD2TeMPtubDNllhZBT5GaYiw1LWAq+xJZZh+QPNxvZVw5Q/p" + "wgXo32maLNwjuhlDl/5bNNOMsxszRz60C2QQXzIaBxd6T2EUcnMQozu5y/33HT8k" + "k/ipBKbfmLP7Hgvs2xdhjHQcG61a2QP6qxD0UjVpXlgsL8wwc28ZSk1RqhxnHG0s" + "HRrRuwNhqWRe7JCGNkOwUghlemrqSuL3i6iAaeqipBqS0vVFGN8KS12jKYirEV5T" + "YkJ2HRrzSWEWbGhk+LnVis47nYRFzQB/sec/m+rpCpX6Spmiez6Yge2u874Oks/A" + "OGQyeYk=") + + return private_pem, public_pem + +parameters['certHash'] = 'B8:D3:FC:F1:51:50:63:7F:B0:ED:EE:32:C5:A2:4B:A2:28:D8:93:91' parameters['nowDate'] = datetime.fromtimestamp(1418433646.179) parameters['jwtId'] = '09841beb-a2c2-4777-a347-34ef055238a8' -parameters['expectedJwt'] = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6IndWM3FobGF0MzJlLWdESFlYcjNjV3RiRU51RSJ9.eyJhdWQiOiJodHRwczovL2xvZ2luLndpbmRvd3MubmV0L3JyYW5kYWxsYWFkMS5vbm1pY3Jvc29mdC5jb20vb2F1dGgyL3Rva2VuIiwiaXNzIjoiY2xpZW4mJj8_P3RJZCIsInN1YiI6ImNsaWVuJiY_Pz90SWQiLCJuYmYiOjE0MTg0MzM2NDYsImV4cCI6MTQxODQzNDI0NiwianRpIjoiMDk4NDFiZWItYTJjMi00Nzc3LWEzNDctMzRlZjA1NTIzOGE4In0.dgF0TRlcASgTMp_1dlm8vd7tudr6n40VeuOQGFnz566s3n76WR_jJDBBBKlYeqc9gwCPFOzrLVAJehVYZ3N7YPzVdulf47rLoQdAp8R_p4Q4hdBZuIzfgDWwXjnP9x_NlfzezEYE4r8KTS2g5BBzPmx538AfIdNM93hWIxQySZGWY5UAhTkT1qE1ce1Yjo1M2HqzEJhTg5TTyfrnDtNxFxmzYhSyA9B41lB5kBuJTXUWXPrr-6eG8cEUOS-iiH7YB1Tf4J7_9JQloevTiOrfv4pSp6xLLXm2ntNBg3gaKsGKdYd-3tsCG0mHn7BzL0b-QCLalkUr8KtgtLqkxuAiLQ' -parameters['cert'] = get_self_signed_cert() +parameters['expectedJwtWithThumbprint'] = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6InVOUDg4VkZRWTMtdzdlNHl4YUpMb2lqWWs1RT0ifQ.eyJleHAiOjE0MTg0MzQyNDYsImF1ZCI6bnVsbCwiaXNzIjoiZDY4MzU3MTMtYjc0NS00OGQxLWJiNjItN2E4MjQ4NDc3ZDM1IiwianRpIjoiMDk4NDFiZWItYTJjMi00Nzc3LWEzNDctMzRlZjA1NTIzOGE4IiwibmJmIjoxNDE4NDMzNjQ2LCJzdWIiOiJkNjgzNTcxMy1iNzQ1LTQ4ZDEtYmI2Mi03YTgyNDg0NzdkMzUifQ.sV5CPEQjYqlnGXhv2f8ozCpAD281is1aOjOHZRKQlPe8zuRhEC4DnAv66QcrxA9HkPs3OAR1GWHnlgVL88uCcAbdEgFo7cAVaQQeRr90zlDMOMoZqULXnorbO90q91BrnJdbcygzsba4Z_FPzKAsJ7J8NXWfWcbkFGrisjuyi97Nm-nCCpjH1zM6gi3paGg_53GFb2S7xMv1lvB7LfPQMI8QvOC64kmVia-cr2NQoT9XLz2U_1ahCKidN2ozyCv09shRjfBu2QSeIctbv0BKVfQQCUnLuMQ-O4_NKY3THZn5hl5PvFDPjlI3X_Om58gPhwISkgtndGTMJ9W-H5z71Q' +parameters['expectedJwtWithPublicCert'] = 'eyJ4NWMiOiJNSUlDb1RDQ0FZa0NBZ1BvTUEwR0NTcUdTSWIzRFFFQkJRVUFNQlF4RWpBUUJnTlZCQU1NQ1VOTVNTMU1iMmRwYmpBaUdBOHlNREU0TVRBeE56RTJNVEF4TjFvWUR6SXdNVGt4TURFM01UWXhNREU1V2pBVU1SSXdFQVlEVlFRRERBbERURWt0VEc5bmFXNHdnZ0VpTUEwR0NTcUdTSWIzRFFFQkFRVUFBNElCRHdBd2dnRUtBb0lCQVFETFRLS01uM1dKaURuYzhWdUpQRHIxa2o5VllyOXpkb2JNVVNKOVlnUmI2UnowNVlpVU9sU2VOaU1BNlk3eXJwVmJTeGV3SUFrVkMvZ0lXL1l3cDRGdFI5ai9TelE5MUhJSHZtS0JyT0FvcnBQRFMwWlE2bmZlYUJ0WjE0VUlGMTZIL092Z3lNa3dlQkJkN3BJYkY0aTd0eTNnZGhGenBOMnhkK3FYVGVEVk10eGFRT004UlRBVnAxUlVwdU5wUFNJUXhvOWRMeU9vdGNBZUZqOHVjMG1NYTBvNkRiZHhEMjZSaUJJT2d6Y3NZcjVXVUNNaWhtRTNoMUVJYlh0UUM1WXdGblU5UThPZ3VwV3ppMzFRRTFmYkZnV3pwangxL0tVOVliOGRvV3MxandYU280VUdZZWN4aU9LQnJGdWowSTZLeWVsdjlkZmlheVpGR3B3UitGbVhBZ01CQUFFd0RRWUpLb1pJaHZjTkFRRUZCUUFEZ2dFQkFEZkVxWHpjSS9mczgyVDA5QjNIM2xHV1FMMUpsY3hPeEQyVGVNUHR1YkRObGxoWkJUNUdhWWl3MUxXQXEreEpaWmgrUVBOeHZaVnc1US9wd2dYbzMybWFMTndqdWhsRGwvNWJOTk9Nc3hzelJ6NjBDMlFRWHpJYUJ4ZDZUMkVVY25NUW96dTV5LzMzSFQ4a2svaXBCS2JmbUxQN0hndnMyeGRoakhRY0c2MWEyUVA2cXhEMFVqVnBYbGdzTDh3d2MyOFpTazFScWh4bkhHMHNIUnJSdXdOaHFXUmU3SkNHTmtPd1VnaGxlbXJxU3VMM2k2aUFhZXFpcEJxUzB2VkZHTjhLUzEyaktZaXJFVjVUWWtKMkhScnpTV0VXYkdoaytMblZpczQ3bllSRnpRQi9zZWMvbStycENwWDZTcG1pZXo2WWdlMnU4NzRPa3MvQU9HUXllWWs9IiwieDV0IjoidU5QODhWRlFZMy13N2U0eXhhSkxvaWpZazVFPSIsImFsZyI6IlJTMjU2IiwidHlwIjoiSldUIn0.eyJqdGkiOiIwOTg0MWJlYi1hMmMyLTQ3NzctYTM0Ny0zNGVmMDU1MjM4YTgiLCJleHAiOjE0MTg0MzQyNDYsImF1ZCI6bnVsbCwic3ViIjoiZDY4MzU3MTMtYjc0NS00OGQxLWJiNjItN2E4MjQ4NDc3ZDM1IiwibmJmIjoxNDE4NDMzNjQ2LCJpc3MiOiJkNjgzNTcxMy1iNzQ1LTQ4ZDEtYmI2Mi03YTgyNDg0NzdkMzUifQ.ROcEKjjuKN0-iK4seRCYftvEh8F5Esj1Y3NJF0MbUGWZQYTRnjibJAnVkvmCqFSGT_mDFhasTM67pwAWtfYNP875UM87HG4aUyZG48pFojnWxnMMf9gBardPmpaDNi3U_iIGoTGVLR60JV30WjsOCkEJY79l68EMc5i6XqYtOSyJDlI0rn8ZTqoyVHQYqCwkTLDF0cqTrqK6HV9iWuiT0rq3LMP2lShwAhKaTYIeAAek5Bw5LwRR2mo9ybreq_02vCDxIQg0C3kBDGMU8GxQ2tAWMYSqnxNfrjgUhARDQYdZTjCyuq1kOb8QrHly29mPT7xdS7Xnc0IF6JZb1PXj0Q' +parameters['cert'], parameters['publicCert'] = get_self_signed_cert() correlation_id_regex = re.compile("[^\s]+")