summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRuben Pollan <meskio@sindominio.net>2017-12-05 11:55:02 +0100
committerRuben Pollan <meskio@sindominio.net>2017-12-07 17:05:40 +0100
commitce5cd2d49dac5f89deb0c10dee96160656fe2055 (patch)
tree05544cbb2abd412592ba33d64c3afb579cf7369c
parent34fa8f02be2c7a6e7f9969c12a8ae1af61964c11 (diff)
[feat] add provider pinning
Pin the provider.json and the ca cert for the public providers. - Resolves: #9074
-rw-r--r--docs/changelog.rst1
-rw-r--r--src/leap/bitmask/bonafide/_protocol.py11
-rw-r--r--src/leap/bitmask/bonafide/config.py67
-rw-r--r--src/leap/bitmask/bonafide/providers/__init__.py0
-rw-r--r--src/leap/bitmask/bonafide/providers/calyx.net.json37
-rw-r--r--src/leap/bitmask/bonafide/providers/calyx.net.pem31
-rw-r--r--src/leap/bitmask/bonafide/providers/demo.bitmask.net.json42
-rw-r--r--src/leap/bitmask/bonafide/providers/demo.bitmask.net.pem32
-rw-r--r--src/leap/bitmask/bonafide/providers/riseup.net.json37
-rw-r--r--src/leap/bitmask/bonafide/providers/riseup.net.pem32
10 files changed, 266 insertions, 24 deletions
diff --git a/docs/changelog.rst b/docs/changelog.rst
index c7dd8806..d5cd0a08 100644
--- a/docs/changelog.rst
+++ b/docs/changelog.rst
@@ -8,6 +8,7 @@ Changelog
Features
~~~~~~~~
- `#8217 <https://0xacab.org/leap/bitmask-dev/issues/8217>`_: renew OpenPGP keys before they expire.
+- `#9074 <https://0xacab.org/leap/bitmask-dev/issues/9074>`_: pin provider ca certs.
- Set a windows title, so that Bitmask windows can be programmatically manipulated.
Misc
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-----