diff options
author | Ruben Pollan <meskio@sindominio.net> | 2017-12-05 11:55:02 +0100 |
---|---|---|
committer | Ruben Pollan <meskio@sindominio.net> | 2017-12-07 17:05:40 +0100 |
commit | ce5cd2d49dac5f89deb0c10dee96160656fe2055 (patch) | |
tree | 05544cbb2abd412592ba33d64c3afb579cf7369c /src/leap/bitmask | |
parent | 34fa8f02be2c7a6e7f9969c12a8ae1af61964c11 (diff) |
[feat] add provider pinning
Pin the provider.json and the ca cert for the public providers.
- Resolves: #9074
Diffstat (limited to 'src/leap/bitmask')
-rw-r--r-- | src/leap/bitmask/bonafide/_protocol.py | 11 | ||||
-rw-r--r-- | src/leap/bitmask/bonafide/config.py | 67 | ||||
-rw-r--r-- | src/leap/bitmask/bonafide/providers/__init__.py | 0 | ||||
-rw-r--r-- | src/leap/bitmask/bonafide/providers/calyx.net.json | 37 | ||||
-rw-r--r-- | src/leap/bitmask/bonafide/providers/calyx.net.pem | 31 | ||||
-rw-r--r-- | src/leap/bitmask/bonafide/providers/demo.bitmask.net.json | 42 | ||||
-rw-r--r-- | src/leap/bitmask/bonafide/providers/demo.bitmask.net.pem | 32 | ||||
-rw-r--r-- | src/leap/bitmask/bonafide/providers/riseup.net.json | 37 | ||||
-rw-r--r-- | src/leap/bitmask/bonafide/providers/riseup.net.pem | 32 |
9 files changed, 265 insertions, 24 deletions
diff --git a/src/leap/bitmask/bonafide/_protocol.py b/src/leap/bitmask/bonafide/_protocol.py index 04c5d451..e044875f 100644 --- a/src/leap/bitmask/bonafide/_protocol.py +++ b/src/leap/bitmask/bonafide/_protocol.py @@ -17,7 +17,7 @@ """ Bonafide protocol. """ -import os +import os.path from collections import defaultdict from leap.bitmask.bonafide import config @@ -31,7 +31,7 @@ from twisted.logger import Logger COMMANDS = 'signup', 'authenticate', 'logout', 'stats' -_preffix = get_path_prefix() +_preffix = os.path.join(get_path_prefix(), 'leap') class BonafideProtocol(object): @@ -60,7 +60,7 @@ class BonafideProtocol(object): username, provider_id = config.get_username_and_provider(full_id) credentials = UsernamePassword(username, password) api = self._get_api(provider) - provider_pem = _get_provider_ca_path(provider_id) + provider_pem = config.get_ca_cert_path(_preffix, provider_id) session = Session(credentials, api, provider_pem) self._sessions[full_id] = session return session @@ -192,8 +192,3 @@ class BonafideProtocol(object): def do_update_user(self): # FIXME to be implemented pass - - -def _get_provider_ca_path(provider_id): - return os.path.join( - _preffix, 'leap', 'providers', provider_id, 'keys', 'ca', 'cacert.pem') diff --git a/src/leap/bitmask/bonafide/config.py b/src/leap/bitmask/bonafide/config.py index 3417e498..53a2b2a3 100644 --- a/src/leap/bitmask/bonafide/config.py +++ b/src/leap/bitmask/bonafide/config.py @@ -20,6 +20,7 @@ Configuration for a LEAP provider. import binascii import json import os +import pkg_resources import platform import shutil import sys @@ -37,6 +38,7 @@ from twisted.web.client import downloadPage from leap.bitmask.bonafide._http import httpRequest from leap.bitmask.bonafide.provider import Discovery from leap.bitmask.bonafide.errors import NotConfiguredError, NetworkError +from leap.bitmask.util import here, STANDALONE from leap.common.check import leap_assert from leap.common.config import get_path_prefix as common_get_path_prefix @@ -73,10 +75,17 @@ def get_provider_path(domain, config='provider.json'): return os.path.join('providers', domain, config) -def get_ca_cert_path(domain): - # TODO sanitize domain +def get_ca_cert_path(basedir, domain): leap_assert(domain is not None, 'get_provider_path: We need a domain') - return os.path.join('providers', domain, 'keys', 'ca', 'cacert.pem') + + enc_domain = domain.encode(sys.getfilesystemencoding()) + cert_path = os.path.join(basedir, 'providers', enc_domain, 'keys', 'ca', + 'cacert.pem') + if not is_file(cert_path): + pinned_cert = get_pinned_path(domain, '.pem') + if is_file(pinned_cert): + return pinned_cert + return cert_path def update_modification_ts(path): @@ -128,7 +137,12 @@ def list_providers(): path = os.path.expanduser(path) if not os.path.isdir(path): os.makedirs(path) - return os.listdir(path) + configured = os.listdir(path) + + pinned = os.listdir(get_pinned_path()) + pinned = [provider[:-5] for provider in pinned if provider[-5:] == ".json"] + + return set(configured + pinned) def delete_provider(domain): @@ -141,6 +155,20 @@ def delete_provider(domain): Provider.providers[domain] = None +def get_pinned_path(domain=None, extension='.json'): + if domain is None: + filename = '' + else: + filename = domain.encode(sys.getfilesystemencoding()) + extension + + if STANDALONE: + # TODO: do the bundling part + return os.path.join(here(), "..", "apps", "providers", filename) + + return pkg_resources.resource_filename( + 'leap.bitmask.bonafide.providers', filename) + + class Provider(object): SERVICES_MAP = { @@ -210,9 +238,13 @@ class Provider(object): def is_configured(self): provider_json = self._get_provider_json_path() - if not is_file(provider_json): + pinned_json = get_pinned_path(self._domain) + if not is_file(provider_json) and not is_file(pinned_json): return False - if not is_file(self._get_ca_cert_path()): + + provider_cert = self._get_ca_cert_path() + pinned_cert = get_pinned_path(self._domain, '.pem') + if not is_file(provider_cert) and not is_file(pinned_cert): return False return True @@ -246,7 +278,7 @@ class Provider(object): return failure d = self.maybe_download_provider_info(replace=replace_if_newer) - d.addCallback(self.maybe_download_ca_cert) + d.addCallback(self.maybe_download_ca_cert, replace_if_newer) d.addCallback(self.validate_ca_cert) d.addCallbacks(first_bootstrap_done, first_bootstrap_error) d.addCallback(self.maybe_download_services_config) @@ -270,8 +302,6 @@ class Provider(object): Download the provider.json info from the main domain. This SHOULD only be used once with the DOMAIN url. """ - # TODO handle pre-seeded providers? - # or let client handle that? We could move them to bonafide. provider_json = self._get_provider_json_path() if is_file(provider_json) and not replace: @@ -300,12 +330,15 @@ class Provider(object): """ pass - def maybe_download_ca_cert(self, ignored): + def maybe_download_ca_cert(self, ignored, replace=False): """ :rtype: deferred """ - path = self._get_ca_cert_path() - if is_file(path): + # TODO: doesn't update the cert :(((( + enc_domain = self._domain.encode(sys.getfilesystemencoding()) + path = os.path.join(self._basedir, 'providers', enc_domain, 'keys', + 'ca', 'cacert.pem') + if not replace and is_file(path): return defer.succeed('ca_cert_path_already_exists') def errback(failure): @@ -452,9 +485,7 @@ class Provider(object): return configs_path def _get_ca_cert_path(self): - domain = self._domain.encode(sys.getfilesystemencoding()) - cert_path = os.path.join(self._basedir, get_ca_cert_path(domain)) - return cert_path + return get_ca_cert_path(self._basedir, self._domain) def _get_ca_cert_uri(self): try: @@ -468,7 +499,11 @@ class Provider(object): path = self._get_provider_json_path() if not is_file(path): self.log.debug('cannot LOAD provider config path %s' % path) - return + path = get_pinned_path(self._domain) + if not is_file(path): + return + + self.log.debug('using pinned provider %s' % path) with open(path, 'r') as config: self._provider_config = Record(**json.load(config)) diff --git a/src/leap/bitmask/bonafide/providers/__init__.py b/src/leap/bitmask/bonafide/providers/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/src/leap/bitmask/bonafide/providers/__init__.py diff --git a/src/leap/bitmask/bonafide/providers/calyx.net.json b/src/leap/bitmask/bonafide/providers/calyx.net.json new file mode 100644 index 00000000..69e1c2a6 --- /dev/null +++ b/src/leap/bitmask/bonafide/providers/calyx.net.json @@ -0,0 +1,37 @@ +{ + "api_uri": "https://api.calyx.net:4430", + "api_version": "1", + "ca_cert_fingerprint": "SHA256: 43683c9ba3862c5384a8c1885072fcac40b5d2d4dd67331443f13a3077fa2e69", + "ca_cert_uri": "https://calyx.net/ca.crt", + "default_language": "en", + "description": { + "en": "Calyx Institute privacy focused ISP testbed" + }, + "domain": "calyx.net", + "enrollment_policy": "open", + "languages": [ + "en" + ], + "name": { + "en": "calyx" + }, + "service": { + "allow_anonymous": false, + "allow_free": true, + "allow_limited_bandwidth": false, + "allow_paid": false, + "allow_registration": true, + "allow_unlimited_bandwidth": true, + "bandwidth_limit": 102400, + "default_service_level": 1, + "levels": { + "1": { + "description": "Please donate.", + "name": "free" + } + } + }, + "services": [ + "openvpn" + ] +}
\ No newline at end of file diff --git a/src/leap/bitmask/bonafide/providers/calyx.net.pem b/src/leap/bitmask/bonafide/providers/calyx.net.pem new file mode 100644 index 00000000..2923144f --- /dev/null +++ b/src/leap/bitmask/bonafide/providers/calyx.net.pem @@ -0,0 +1,31 @@ +-----BEGIN CERTIFICATE----- +MIIFYzCCA0ugAwIBAgIBATANBgkqhkiG9w0BAQ0FADBEMQ4wDAYDVQQKDAVjYWx5 +eDEaMBgGA1UECwwRaHR0cHM6Ly9jYWx5eC5uZXQxFjAUBgNVBAMMDWNhbHl4IFJv +b3QgQ0EwHhcNMTMwNzAyMDAwMDAwWhcNMjMwNzAyMDAwMDAwWjBEMQ4wDAYDVQQK +DAVjYWx5eDEaMBgGA1UECwwRaHR0cHM6Ly9jYWx5eC5uZXQxFjAUBgNVBAMMDWNh +bHl4IFJvb3QgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDupdnx +Bgat537XOqrZOulE/RvjoXB1S07sy9/MMtksXFoQuWJZRCSTp1Jaqg3H/e9o1nct +LQO91+izfJe07TUyajFl7CfllYgMeyKTYcT85dFwNX4pcIHZr8UpmO0MpGBoR4W1 +8cPa3vxAG0CsyUmrASJVyhRouk4qazRosM5RwBxTdMzCK7L3SwqPQoxlY9YmRJlD +XYZlK5VMJd0dj9XxhMeFs5n43R0bsDENryrExSbuxoNfnUoQg3wffKk+Z0gW7YgW +ivPsbObqOgXUuBEU0xr9xMNBpU33ffLIsccrHq1EKp8zGfCOcww6v7+zEadUkVLo +6j/rRhYYgRw9lijZG1rMuV/mTGnUqbjHsdoz5mzkFFWeTSqo44lvhveUyCcwRNmi +2sjS77l0fCTzfreufffFoOEcRVMRfsnJdu/xPeARoXILEx8nQ421mSn6spOZlDQr +Tt0T0BAWt+VNc+m0IGSW3SwS7r5MUyQ/M5GrbQBGi5W2SzPriKZ79YTOwPVmXKLZ +vJoEuKRDkEPJLBAhcD5oSQljOm/Wp/hjmRH4HnI1y4XMshWlDsyRDB1Au5yrsfwN +noFVSskEcbXlZfNgml4lktLBqz+qwsw+voq6Ak7ROKbc0ii5s8+iNMbAtIK7GcFF +kuKKIyRmmGlDim/SDhlNdWo7Ah4Akde7zfWufwIDAQABo2AwXjAdBgNVHQ4EFgQU +AY8+K4ZupAQ+L9ttFJG3vaLBq5gwDgYDVR0PAQH/BAQDAgIEMAwGA1UdEwQFMAMB +Af8wHwYDVR0jBBgwFoAUAY8+K4ZupAQ+L9ttFJG3vaLBq5gwDQYJKoZIhvcNAQEN +BQADggIBAOpXi5o3g/2o2rPa53iG7Zgcy8RpePGgZk6xknGYWeLamEqSh+XWQZ2w +2kQP54bf8HfPj3ugJBWsVtYAs/ltJwzeBfYDrwEJd1N8tw2IRuGlQOWiTAVVLBj4 +Zs+dikSuMoA399f/7BlUIEpVLUiV/emTtbkjFnDeKEV9zql6ypR0BtR8Knf8ALvL +YfMsWLvTe4rXeypzxIaE2pn8ttcXLYAX0ml2MofTi5xcDhMn1vznKIvs82xhncQx +I1MJMWqPHNHgJUJpA+y1IFh5LPbpag9PKQ0yQ9sM+/dyGumF2jElsMw71flh/Txr +2dEv8+FNV1pPK26XJZBK24rNWFs30eAFfH9EQCwVla174I4PDoWqsIR7vtQMObDt +Bq34R3TjjJJIt2sCSlYLooWwiK7Q+d/SgYqA+MSDmmwhzm86ToK6cwbCsvuw1AxR +X6VIs4U8wOotgljzX/CSpKqlxcqZjhnAuelZ1+KiN8RHKPj7AzSLYOv/YwTjLTIq +EOxquoNR58uDa5pBG22a7xWbSaKosn/mEl8SrUr6klzzc8Vh09IMoxrw74uLdAg2 +1jnrhm7qg91Ttb0aXiqbV+Kg/qQzojdewnnoBFnv4jaQ3y8zDCfMhsBtWlWz4Knb +Zqga1WyRm3Gj1j6IV0oOincYMrw5YA7bgXpwop/Lo/mmliMA14ps +-----END CERTIFICATE----- diff --git a/src/leap/bitmask/bonafide/providers/demo.bitmask.net.json b/src/leap/bitmask/bonafide/providers/demo.bitmask.net.json new file mode 100644 index 00000000..e7fe6099 --- /dev/null +++ b/src/leap/bitmask/bonafide/providers/demo.bitmask.net.json @@ -0,0 +1,42 @@ +{ + "api_uri": "https://api.demo.bitmask.net:4430", + "api_version": "1", + "ca_cert_fingerprint": "SHA256: 0f17c033115f6b76ff67871872303ff65034efe7dd1b910062ca323eb4da5c7e", + "ca_cert_uri": "https://demo.bitmask.net/ca.crt", + "default_language": "en", + "description": { + "el": "demo.bitmask.net allows you to test the Bitmask application. User accounts may be periodically deleted.", + "en": "demo.bitmask.net allows you to test the Bitmask application. User accounts may be periodically deleted.", + "es": "demo.bitmask.net allows you to test the Bitmask application. User accounts may be periodically deleted." + }, + "domain": "demo.bitmask.net", + "enrollment_policy": "open", + "languages": [ + "de", + "en", + "es", + "pt" + ], + "name": { + "en": "Bitmask" + }, + "service": { + "allow_anonymous": true, + "allow_free": true, + "allow_limited_bandwidth": false, + "allow_paid": false, + "allow_registration": true, + "allow_unlimited_bandwidth": true, + "bandwidth_limit": 102400, + "default_service_level": 1, + "levels": { + "1": { + "description": "Please donate.", + "name": "free" + } + } + }, + "services": [ + "openvpn" + ] +}
\ No newline at end of file diff --git a/src/leap/bitmask/bonafide/providers/demo.bitmask.net.pem b/src/leap/bitmask/bonafide/providers/demo.bitmask.net.pem new file mode 100644 index 00000000..80d41c3e --- /dev/null +++ b/src/leap/bitmask/bonafide/providers/demo.bitmask.net.pem @@ -0,0 +1,32 @@ +-----BEGIN CERTIFICATE----- +MIIFbzCCA1egAwIBAgIBATANBgkqhkiG9w0BAQ0FADBKMRgwFgYDVQQDDA9CaXRt +YXNrIFJvb3QgQ0ExEDAOBgNVBAoMB0JpdG1hc2sxHDAaBgNVBAsME2h0dHBzOi8v +Yml0bWFzay5uZXQwHhcNMTIxMTA2MDAwMDAwWhcNMjIxMTA2MDAwMDAwWjBKMRgw +FgYDVQQDDA9CaXRtYXNrIFJvb3QgQ0ExEDAOBgNVBAoMB0JpdG1hc2sxHDAaBgNV +BAsME2h0dHBzOi8vYml0bWFzay5uZXQwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw +ggIKAoICAQC1eV4YvayaU+maJbWrD4OHo3d7S1BtDlcvkIRS1Fw3iYDjsyDkZxai +dHp4EUasfNQ+EVtXUvtk6170EmLco6Elg8SJBQ27trE6nielPRPCfX3fQzETRfvB +7tNvGw4Jn2YKiYoMD79kkjgyZjkJ2r/bEHUSevmR09BRp86syHZerdNGpXYhcQ84 +CA1+V+603GFIHnrP+uQDdssW93rgDNYu+exT+Wj6STfnUkugyjmPRPjL7wh0tzy+ +znCeLl4xiV3g9sjPnc7r2EQKd5uaTe3j71sDPF92KRk0SSUndREz+B1+Dbe/RGk4 +MEqGFuOzrtsgEhPIX0hplhb0Tgz/rtug+yTT7oJjBa3u20AAOQ38/M99EfdeJvc4 +lPFF1XBBLh6X9UKF72an2NuANiX6XPySnJgZ7nZ09RiYZqVwu/qt3DfvLfhboq+0 +bQvLUPXrVDr70onv5UDjpmEA/cLmaIqqrduuTkFZOym65/PfAPvpGnt7crQj/Ibl +DEDYZQmP7AS+6zBjoOzNjUGE5r40zWAR1RSi7zliXTu+yfsjXUIhUAWmYR6J3KxB +lfsiHBQ+8dn9kC3YrUexWoOqBiqJOAJzZh5Y1tqgzfh+2nmHSB2dsQRs7rDRRlyy +YMbkpzL9ZsOUO2eTP1mmar6YjCN+rggYjRrX71K2SpBG6b1zZxOG+wIDAQABo2Aw +XjAdBgNVHQ4EFgQUuYGDLL2sswnYpHHvProt1JU+D48wDgYDVR0PAQH/BAQDAgIE +MAwGA1UdEwQFMAMBAf8wHwYDVR0jBBgwFoAUuYGDLL2sswnYpHHvProt1JU+D48w +DQYJKoZIhvcNAQENBQADggIBADeG67vaFcbITGpi51264kHPYPEWaXUa5XYbtmBl +cXYyB6hY5hv/YNuVGJ1gWsDmdeXEyj0j2icGQjYdHRfwhrbEri+h1EZOm1cSBDuY +k/P5+ctHyOXx8IE79DBsZ6IL61UKIaKhqZBfLGYcWu17DVV6+LT+AKtHhOrv3TSj +RnAcKnCbKqXLhUPXpK0eTjPYS2zQGQGIhIy9sQXVXJJJsGrPgMxna1Xw2JikBOCG +htD/JKwt6xBmNwktH0GI/LVtVgSp82Clbn9C4eZN9E5YbVYjLkIEDhpByeC71QhX +EIQ0ZR56bFuJA/CwValBqV/G9gscTPQqd+iETp8yrFpAVHOW+YzSFbxjTEkBte1J +aF0vmbqdMAWLk+LEFPQRptZh0B88igtx6tV5oVd+p5IVRM49poLhuPNJGPvMj99l +mlZ4+AeRUnbOOeAEuvpLJbel4rhwFzmUiGoeTVoPZyMevWcVFq6BMkS+jRR2w0jK +G6b0v5XDHlcFYPOgUrtsOBFJVwbutLvxdk6q37kIFnWCd8L3kmES5q4wjyFK47Co +Ja8zlx64jmMZPg/t3wWqkZgXZ14qnbyG5/lGsj5CwVtfDljrhN0oCWK1FZaUmW3d +69db12/g4f6phldhxiWuGC/W6fCW5kre7nmhshcltqAJJuU47iX+DarBFiIj816e +yV8e +-----END CERTIFICATE----- diff --git a/src/leap/bitmask/bonafide/providers/riseup.net.json b/src/leap/bitmask/bonafide/providers/riseup.net.json new file mode 100644 index 00000000..bad3805b --- /dev/null +++ b/src/leap/bitmask/bonafide/providers/riseup.net.json @@ -0,0 +1,37 @@ +{ + "api_uri": "https://api.black.riseup.net:443", + "api_version": "1", + "ca_cert_fingerprint": "SHA256: a5244308a1374709a9afce95e3ae47c1b44bc2398c0a70ccbf8b3a8a97f29494", + "ca_cert_uri": "https://black.riseup.net/ca.crt", + "default_language": "en", + "description": { + "en": "Riseup is a non-profit collective in Seattle that provides online communication tools for people and groups working toward liberatory social change." + }, + "domain": "riseup.net", + "enrollment_policy": "open", + "languages": [ + "en" + ], + "name": { + "en": "Riseup Networks" + }, + "service": { + "allow_anonymous": false, + "allow_free": true, + "allow_limited_bandwidth": false, + "allow_paid": false, + "allow_registration": true, + "allow_unlimited_bandwidth": true, + "bandwidth_limit": 102400, + "default_service_level": 1, + "levels": { + "1": { + "description": "Please donate.", + "name": "free" + } + } + }, + "services": [ + "openvpn" + ] +} diff --git a/src/leap/bitmask/bonafide/providers/riseup.net.pem b/src/leap/bitmask/bonafide/providers/riseup.net.pem new file mode 100644 index 00000000..cbec39c6 --- /dev/null +++ b/src/leap/bitmask/bonafide/providers/riseup.net.pem @@ -0,0 +1,32 @@ +-----BEGIN CERTIFICATE----- +MIIFjTCCA3WgAwIBAgIBATANBgkqhkiG9w0BAQ0FADBZMRgwFgYDVQQKDA9SaXNl +dXAgTmV0d29ya3MxGzAZBgNVBAsMEmh0dHBzOi8vcmlzZXVwLm5ldDEgMB4GA1UE +AwwXUmlzZXVwIE5ldHdvcmtzIFJvb3QgQ0EwHhcNMTQwNDI4MDAwMDAwWhcNMjQw +NDI4MDAwMDAwWjBZMRgwFgYDVQQKDA9SaXNldXAgTmV0d29ya3MxGzAZBgNVBAsM +Emh0dHBzOi8vcmlzZXVwLm5ldDEgMB4GA1UEAwwXUmlzZXVwIE5ldHdvcmtzIFJv +b3QgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC76J4ciMJ8Sg0m +TP7DF2DT9zNe0Csk4myoMFC57rfJeqsAlJCv1XMzBmXrw8wq/9z7XHv6n/0sWU7a +7cF2hLR33ktjwODlx7vorU39/lXLndo492ZBhXQtG1INMShyv+nlmzO6GT7ESfNE +LliFitEzwIegpMqxCIHXFuobGSCWF4N0qLHkq/SYUMoOJ96O3hmPSl1kFDRMtWXY +iw1SEKjUvpyDJpVs3NGxeLCaA7bAWhDY5s5Yb2fA1o8ICAqhowurowJpW7n5ZuLK +5VNTlNy6nZpkjt1QycYvNycffyPOFm/Q/RKDlvnorJIrihPkyniV3YY5cGgP+Qkx +HUOT0uLA6LHtzfiyaOqkXwc4b0ZcQD5Vbf6Prd20Ppt6ei0zazkUPwxld3hgyw58 +m/4UIjG3PInWTNf293GngK2Bnz8Qx9e/6TueMSAn/3JBLem56E0WtmbLVjvko+LF +PM5xA+m0BmuSJtrD1MUCXMhqYTtiOvgLBlUm5zkNxALzG+cXB28k6XikXt6MRG7q +hzIPG38zwkooM55yy5i1YfcIi5NjMH6A+t4IJxxwb67MSb6UFOwg5kFokdONZcwj +shczHdG9gLKSBIvrKa03Nd3W2dF9hMbRu//STcQxOailDBQCnXXfAATj9pYzdY4k +ha8VCAREGAKTDAex9oXf1yRuktES4QIDAQABo2AwXjAdBgNVHQ4EFgQUC4tdmLVu +f9hwfK4AGliaet5KkcgwDgYDVR0PAQH/BAQDAgIEMAwGA1UdEwQFMAMBAf8wHwYD +VR0jBBgwFoAUC4tdmLVuf9hwfK4AGliaet5KkcgwDQYJKoZIhvcNAQENBQADggIB +AGzL+GRnYu99zFoy0bXJKOGCF5XUXP/3gIXPRDqQf5g7Cu/jYMID9dB3No4Zmf7v +qHjiSXiS8jx1j/6/Luk6PpFbT7QYm4QLs1f4BlfZOti2KE8r7KRDPIecUsUXW6P/ +3GJAVYH/+7OjA39za9AieM7+H5BELGccGrM5wfl7JeEz8in+V2ZWDzHQO4hMkiTQ +4ZckuaL201F68YpiItBNnJ9N5nHr1MRiGyApHmLXY/wvlrOpclh95qn+lG6/2jk7 +3AmihLOKYMlPwPakJg4PYczm3icFLgTpjV5sq2md9bRyAg3oPGfAuWHmKj2Ikqch +Td5CHKGxEEWbGUWEMP0s1A/JHWiCbDigc4Cfxhy56CWG4q0tYtnc2GMw8OAUO6Wf +Xu5pYKNkzKSEtT/MrNJt44tTZWbKV/Pi/N2Fx36my7TgTUj7g3xcE9eF4JV2H/sg +tsK3pwE0FEqGnT4qMFbixQmc8bGyuakr23wjMvfO7eZUxBuWYR2SkcP26sozF9PF +tGhbZHQVGZUTVPyvwahMUEhbPGVerOW0IYpxkm0x/eaWdTc4vPpf/rIlgbAjarnJ +UN9SaWRlWKSdP4haujnzCoJbM7dU9bjvlGZNyXEekgeT0W2qFeGGp+yyUWw8tNsp +0BuC1b7uW/bBn/xKm319wXVDvBgZgcktMolak39V7DVO +-----END CERTIFICATE----- |