diff --git a/doc/source/guides/basic-cookbook.rst b/doc/source/guides/basic-cookbook.rst index bb083aa785..f795de9836 100644 --- a/doc/source/guides/basic-cookbook.rst +++ b/doc/source/guides/basic-cookbook.rst @@ -385,10 +385,11 @@ listener using Server Name Indication (SNI) technology. * TLS certificates, keys, and intermediate certificate chains for www.example.com and www2.example.com have been obtained from an external certificate authority. These now exist in the files server.crt, server.key, - ca-chain.p7b, server2.crt, server2.key, and ca-chain2.p7b in the current - directory. The keys and certificates are PEM-encoded, and the - intermediate certificate chains are PKCS7 PEM encoded. The keys are not - encrypted with a passphrase. + ca-chain.p7b, server2.crt, server2-encrypted.key, and ca-chain2.p7b in the + current directory. The keys and certificates are PEM-encoded, and the + intermediate certificate chains are PKCS7 PEM encoded. +* The key for www.example.com is not encrypted with a passphrase. +* The key for www2.example.com is encrypted with the passphrase "abc123". * The *admin* user on this cloud installation has keystone ID *admin_id* * We want to configure a TLS-terminated HTTPS load balancer that is accessible from the internet using the keys and certificates mentioned above, which @@ -402,16 +403,18 @@ listener using Server Name Indication (SNI) technology. 1. Create barbican *secret* resources for the certificates, keys, and intermediate certificate chains. We will call these *cert1*, *key1*, *intermediates1*, *cert2*, *key2* and *intermediates2* respectively. -2. Create *secret container* resources combining the above appropriately. We +2. Create a barbican *secret* resource *passphrase2* for the passphrase for + *key2* +3. Create *secret container* resources combining the above appropriately. We will call these *tls_container1* and *tls_container2*. -3. Grant the *admin* user access to all the *secret* and *secret container* +4. Grant the *admin* user access to all the *secret* and *secret container* barbican resources above. -4. Create load balancer *lb1* on subnet *public-subnet*. -5. Create listener *listener1* as a TERMINATED_HTTPS listener referencing +5. Create load balancer *lb1* on subnet *public-subnet*. +6. Create listener *listener1* as a TERMINATED_HTTPS listener referencing *tls_container1* as its default TLS container, and referencing both *tls_container1* and *tls_container2* using SNI. -6. Create pool *pool1* as *listener1*'s default pool. -7. Add members 192.0.2.10 and 192.0.2.11 on *private-subnet* to *pool1*. +7. Create pool *pool1* as *listener1*'s default pool. +8. Add members 192.0.2.10 and 192.0.2.11 on *private-subnet* to *pool1*. **CLI commands**: @@ -422,9 +425,10 @@ listener using Server Name Indication (SNI) technology. openstack secret store --name='intermediates1' --payload-content-type='text/plain' --payload="$(cat ca-chain.p7b)" openstack secret container create --name='tls_container1' --type='certificate' --secret="certificate=$(openstack secret list | awk '/ cert1 / {print $2}')" --secret="private_key=$(openstack secret list | awk '/ key1 / {print $2}')" --secret="intermediates=$(openstack secret list | awk '/ intermediates1 / {print $2}')" openstack secret store --name='cert2' --payload-content-type='text/plain' --payload="$(cat server2.crt)" - openstack secret store --name='key2' --payload-content-type='text/plain' --payload="$(cat server2.key)" + openstack secret store --name='key2' --payload-content-type='text/plain' --payload="$(cat server2-encrypted.key)" openstack secret store --name='intermediates2' --payload-content-type='text/plain' --payload="$(cat ca-chain2.p7b)" - openstack secret container create --name='tls_container2' --type='certificate' --secret="certificate=$(openstack secret list | awk '/ cert2 / {print $2}')" --secret="private_key=$(openstack secret list | awk '/ key2 / {print $2}')" --secret="intermediates=$(openstack secret list | awk '/ intermediates2 / {print $2}')" + openstack secret store --name='passphrase2' --payload-content-type='text/plain' --payload="abc123" + openstack secret container create --name='tls_container2' --type='certificate' --secret="certificate=$(openstack secret list | awk '/ cert2 / {print $2}')" --secret="private_key=$(openstack secret list | awk '/ key2 / {print $2}')" --secret="intermediates=$(openstack secret list | awk '/ intermediates2 / {print $2}')" --secret="private_key_passphrase=$(openstack secret list | awk '/ passphrase2 / {print $2}')" openstack acl user add -u admin_id $(openstack secret list | awk '/ cert1 / {print $2}') openstack acl user add -u admin_id $(openstack secret list | awk '/ key1 / {print $2}') openstack acl user add -u admin_id $(openstack secret list | awk '/ intermediates1 / {print $2}') @@ -614,6 +618,58 @@ that they also ensure the back-end server responds to SSLv3 client hello messages. +Intermediate certificate chains +=============================== +Some TLS certificates require you to install an intermediate certificate chain +in order for web client browsers to trust the certificate. This chain can take +several forms, and is a file provided by the organization from whom you +obtained your TLS certificate. + +PEM-encoded chains +------------------ +The simplest form of the intermediate chain is a PEM-encoded text file that +either contains a sequence of individually-encoded PEM certificates, or a PEM +encoded PKCS7 block(s). If this is the type of intermediate chain you have been +provided, the file will contain either ``-----BEGIN PKCS7-----`` or +``-----BEGIN CERTIFICATE-----`` near the top of the file, and one or more +blocks of 64-character lines of ASCII text (that will look like gobbedlygook to +a human). These files are also typically named with a ``.crt`` or ``.pem`` +extension. + +To upload this type of intermediates chain to barbican, run a command similar +to the following (assuming "intermediates-chain.pem" is the name of the file): + +:: + + openstack secret store --name='intermediates1' --payload-content-type='text/plain' --payload="$(cat intermediates-chain.pem)" + +DER-encoded chains +------------------ +If the intermediates chain provided to you is a file that contains what appears +to be random binary data, it is likely that it is a PKCS7 chain in DER format. +These files also may be named with a ``.p7b`` extension. In order to use this +intermediates chain, you can either convert it to a series of PEM-encoded +certificates with the following command: + +:: + + openssl pkcs7 -in intermediates-chain.p7b -inform DER -print_certs -out intermediates-chain.pem + +...or convert it into a PEM-encoded PKCS7 bundle with the following command: + +:: + + openssl pkcs7 -in intermediates-chain.p7b -inform DER -outform PEM -out intermediates-chain.pem + +...or simply upload the binary DER file to barbican without conversion: + +:: + + openstack secret store --name='intermediates1' --payload-content-type='application/octet-stream' --payload-content-encoding='base64' --payload="$(cat intermediates-chain.p7b | base64)" + +In any case, if the file is not a PKCS7 DER bundle, then either of the above +two openssl commands will fail. + Further reading =============== For examples of using Layer 7 features for more advanced load balancing, please diff --git a/octavia/certificates/common/barbican.py b/octavia/certificates/common/barbican.py index 95809be60c..9a639dd89b 100644 --- a/octavia/certificates/common/barbican.py +++ b/octavia/certificates/common/barbican.py @@ -44,7 +44,8 @@ class BarbicanCert(cert.Cert): def get_intermediates(self): if self._cert_container.intermediates: intermediates = self._cert_container.intermediates.payload - return [imd for imd in cert_parser._split_x509s(intermediates)] + return [imd for imd in cert_parser.get_intermediates_pems( + intermediates)] def get_private_key(self): if self._cert_container.private_key: diff --git a/octavia/common/tls_utils/cert_parser.py b/octavia/common/tls_utils/cert_parser.py index cdadf4a2b2..92d0120be5 100644 --- a/octavia/common/tls_utils/cert_parser.py +++ b/octavia/common/tls_utils/cert_parser.py @@ -13,10 +13,15 @@ # License for the specific language governing permissions and limitations # under the License. +import base64 + from cryptography.hazmat import backends from cryptography.hazmat.primitives import serialization from cryptography import x509 from oslo_log import log as logging +from pyasn1.codec.der import decoder as der_decoder +from pyasn1.codec.der import encoder as der_encoder +from pyasn1_modules import rfc2315 import six from octavia.common import data_models as data_models @@ -24,8 +29,10 @@ import octavia.common.exceptions as exceptions from octavia.i18n import _LE -X509_BEG = "-----BEGIN CERTIFICATE-----" -X509_END = "-----END CERTIFICATE-----" +X509_BEG = '-----BEGIN CERTIFICATE-----' +X509_END = '-----END CERTIFICATE-----' +PKCS7_BEG = '-----BEGIN PKCS7-----' +PKCS7_END = '-----END PKCS7-----' LOG = logging.getLogger(__name__) @@ -40,15 +47,17 @@ def validate_cert(certificate, private_key=None, :param certificate: A PEM encoded certificate :param private_key: The private key for the certificate :param private_key_passphrase: Passphrase for accessing the private key - :param intermediates: PEM encoded intermediate certificates + :param intermediates: PEM or PKCS7 encoded intermediate certificates :returns: boolean """ cert = _get_x509_from_pem_bytes(certificate) if intermediates: - for x509Pem in _split_x509s(intermediates): - _get_x509_from_pem_bytes(x509Pem) + for imd in get_intermediates_pems(intermediates): + # Loading the certificates validates them + pass if private_key: - pkey = _read_privatekey(private_key, passphrase=private_key_passphrase) + pkey = _read_private_key(private_key, + passphrase=private_key_passphrase) pknum = pkey.public_key().public_numbers() certnum = cert.public_key().public_numbers() if pknum != certnum: @@ -56,7 +65,13 @@ def validate_cert(certificate, private_key=None, return True -def _read_privatekey(privatekey_pem, passphrase=None): +def _read_private_key(private_key_pem, passphrase=None): + """Reads a private key PEM block and returns a RSAPrivatekey + + :param private_key_pem: The private key PEM block + :param passphrase: Optional passphrase needed to decrypt the private key + :returns: a RSAPrivatekey object + """ if passphrase: if six.PY2: passphrase = passphrase.encode("utf-8") @@ -64,13 +79,54 @@ def _read_privatekey(privatekey_pem, passphrase=None): passphrase = six.b(passphrase) try: - pkey = privatekey_pem.encode('ascii') + pkey = private_key_pem.encode('ascii') return serialization.load_pem_private_key(pkey, passphrase, backends.default_backend()) except Exception: + LOG.exception(_LE("Passphrase required.")) raise exceptions.NeedsPassphrase +def prepare_private_key(private_key, passphrase=None): + """Prepares an unencrypted PEM-encoded private key for printing + + :param private_key: The private key in PEM format (encrypted or not) + :returns: The unencrypted private key in PEM format + """ + pk = _read_private_key(private_key, passphrase) + return pk.private_bytes( + encoding=serialization.Encoding.PEM, + format=serialization.PrivateFormat.TraditionalOpenSSL, + encryption_algorithm=serialization.NoEncryption()).decode( + 'unicode_escape').strip() + + +def get_intermediates_pems(intermediates=None): + """Split the input string into individual x509 text blocks + + :param intermediates: PEM or PKCS7 encoded intermediate certificates + :returns: A list of strings where each string represents an + X509 pem block surrounded by BEGIN CERTIFICATE, + END CERTIFICATE block tags + """ + if X509_BEG in str(intermediates): + for x509Pem in _split_x509s(intermediates): + yield _prepare_x509_cert(_get_x509_from_pem_bytes(x509Pem)) + else: + for x509Pem in _parse_pkcs7_bundle(intermediates): + yield _prepare_x509_cert(_get_x509_from_der_bytes(x509Pem)) + + +def _prepare_x509_cert(cert=None): + """Prepares a PEM-encoded X509 certificate for printing + + :param intermediates: X509Certificate object + :returns: A PEM-encoded X509 certificate + """ + return cert.public_bytes(encoding=serialization.Encoding.PEM).decode( + 'unicode_escape').strip() + + def _split_x509s(xstr): """Split the input string into individual x509 text blocks @@ -95,6 +151,104 @@ def _split_x509s(xstr): inside_x509 = True +def _parse_pkcs7_bundle(pkcs7): + """Parse a PKCS7 certificate bundle in DER or PEM format + + :param pkcs7: A pkcs7 bundle in DER or PEM format + :returns: A list of individual DER-encoded certificates + """ + # Look for PEM encoding + if PKCS7_BEG in str(pkcs7): + try: + for substrate in _read_pem_blocks(pkcs7, (PKCS7_BEG, PKCS7_END)): + for cert in _get_certs_from_pkcs7_substrate(substrate): + yield cert + except Exception: + LOG.exception(_LE('Unreadable Certificate.')) + raise exceptions.UnreadableCert + + # If no PEM encoding, assume this is DER encoded and try to decode + else: + for cert in _get_certs_from_pkcs7_substrate(pkcs7): + yield cert + + +def _read_pem_blocks(data, *markers): + """Parse a series of PEM-encoded blocks + + This method is based on pyasn1-modules.pem.readPemBlocksFromFile, but + eliminates the need to operate on a file handle and is a generator. + + :param data: A long text string containing one or more PEM-encoded blocks + :param markers: A tuple containing the test strings that indicate the + start and end of the the PEM-encoded blocks + :returns: An ASN1 substrate suitable for DER decoding. + + """ + stSpam, stHam, stDump = 0, 1, 2 + + startMarkers = dict(map(lambda x: (x[1], x[0]), + enumerate(map(lambda x: x[0], markers)))) + stopMarkers = dict(map(lambda x: (x[1], x[0]), + enumerate(map(lambda x: x[1], markers)))) + idx = -1 + state = stSpam + if six.PY3: + data = str(data) + for certLine in data.replace('\r', '').split('\n'): + if not certLine: + continue + certLine = certLine.strip() + if state == stSpam: + if certLine in startMarkers: + certLines = [] + idx = startMarkers[certLine] + state = stHam + continue + if state == stHam: + if certLine in stopMarkers and stopMarkers[certLine] == idx: + state = stDump + else: + certLines.append(certLine) + if state == stDump: + if six.PY2: + yield ''.join([ + base64.b64decode(x) for x in certLines]) + elif six.PY3: + yield ''.encode().join([ + base64.b64decode(x) for x in certLines]) + state = stSpam + + +def _get_certs_from_pkcs7_substrate(substrate): + """Extracts DER-encoded X509 certificates from a PKCS7 ASN1 DER substrate + + :param substrate: The substrate to be processed + :returns: A list of DER-encoded X509 certificates + """ + try: + contentInfo, _ = der_decoder.decode(substrate, + asn1Spec=rfc2315.ContentInfo()) + contentType = contentInfo.getComponentByName('contentType') + except Exception: + LOG.exception(_LE('Unreadable Certificate.')) + raise exceptions.UnreadableCert + if contentType != rfc2315.signedData: + LOG.exception(_LE('Unreadable Certificate.')) + raise exceptions.UnreadableCert + + try: + content, _ = der_decoder.decode( + contentInfo.getComponentByName('content'), + asn1Spec=rfc2315.SignedData()) + except Exception: + LOG.exception(_LE('Unreadable Certificate.')) + raise exceptions.UnreadableCert + + for cert in content.getComponentByName('certificates'): + yield der_encoder.encode(cert) + + def get_host_names(certificate): """Extract the host names from the Pem encoded X509 certificate @@ -127,7 +281,7 @@ def get_host_names(certificate): return host_names except Exception: - LOG.exception(_LE("Unreadable certificate.")) + LOG.exception(_LE('Unreadable Certificate.')) raise exceptions.UnreadableCert @@ -143,8 +297,8 @@ def get_cert_expiration(certificate_pem): cert = x509.load_pem_x509_certificate(certificate, backends.default_backend()) return cert.not_valid_after - except Exception as e: - LOG.exception(e) + except Exception: + LOG.exception(_LE('Unreadable Certificate.')) raise exceptions.UnreadableCert @@ -160,6 +314,22 @@ def _get_x509_from_pem_bytes(certificate_pem): x509cert = x509.load_pem_x509_certificate(certificate, backends.default_backend()) except Exception: + LOG.exception(_LE('Unreadable Certificate.')) + raise exceptions.UnreadableCert + return x509cert + + +def _get_x509_from_der_bytes(certificate_der): + """Parse X509 data from a DER encoded certificate + + :param certificate_der: Certificate in DER format + :returns: crypto high-level x509 data from the DER-encoded certificate + """ + try: + x509cert = x509.load_der_x509_certificate(certificate_der, + backends.default_backend()) + except Exception: + LOG.exception(_LE('Unreadable Certificate.')) raise exceptions.UnreadableCert return x509cert @@ -175,7 +345,7 @@ def build_pem(tls_container): pem = [tls_container.certificate, tls_container.private_key] if tls_container.intermediates: pem.extend(tls_container.intermediates[:]) - return '\n'.join(pem) + return '\n'.join(pem) + '\n' def load_certificates_data(cert_mngr, listener): @@ -204,7 +374,9 @@ def load_certificates_data(cert_mngr, listener): def _map_cert_tls_container(cert): return data_models.TLSContainer( primary_cn=get_primary_cn(cert), - private_key=cert.get_private_key(), + private_key=prepare_private_key( + cert.get_private_key(), + cert.get_private_key_passphrase()), certificate=cert.get_certificate(), intermediates=cert.get_intermediates()) diff --git a/octavia/tests/unit/amphorae/drivers/haproxy/test_rest_api_driver.py b/octavia/tests/unit/amphorae/drivers/haproxy/test_rest_api_driver.py index 5036aac882..deb2dad34a 100644 --- a/octavia/tests/unit/amphorae/drivers/haproxy/test_rest_api_driver.py +++ b/octavia/tests/unit/amphorae/drivers/haproxy/test_rest_api_driver.py @@ -26,6 +26,7 @@ from octavia.amphorae.drivers.haproxy import rest_api_driver as driver from octavia.db import models from octavia.network import data_models as network_models from octavia.tests.unit import base as base +from octavia.tests.unit.common.sample_configs import sample_certs from octavia.tests.unit.common.sample_configs import sample_configs FAKE_CIDR = '198.51.100.0/24' @@ -79,7 +80,7 @@ class TestHaproxyAmphoraLoadBalancerDriverTest(base.TestCase): @mock.patch('octavia.common.tls_utils.cert_parser.load_certificates_data') @mock.patch('octavia.common.tls_utils.cert_parser.get_host_names') def test_update(self, mock_cert, mock_load_crt): - mock_cert.return_value = {'cn': 'aFakeCN'} + mock_cert.return_value = {'cn': sample_certs.X509_CERT_CN} sconts = [] for sni_container in self.sl.sni_containers: sconts.append(sni_container.tls_container) @@ -88,7 +89,7 @@ class TestHaproxyAmphoraLoadBalancerDriverTest(base.TestCase): 'sni_certs': sconts } self.driver.client.get_cert_md5sum.side_effect = [ - exc.NotFound, 'Fake_MD5', 'd41d8cd98f00b204e9800998ecf8427e'] + exc.NotFound, 'Fake_MD5', 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'] self.driver.jinja.build_config.side_effect = ['fake_config'] self.driver.client.get_listener_status.side_effect = [ dict(status='ACTIVE')] @@ -99,18 +100,24 @@ class TestHaproxyAmphoraLoadBalancerDriverTest(base.TestCase): # verify result # this is called 3 times self.driver.client.get_cert_md5sum.assert_called_with( - self.amp, self.sl.id, 'aFakeCN.pem') + self.amp, self.sl.id, sample_certs.X509_CERT_CN_3 + '.pem') # this is called three times (last MD5 matches) - fp1 = ('--imapem1--\n\n--imakey1--\n' - '\n--imainter1--\n\n--imainter1too--\n') - fp2 = ('--imapem2--\n\n--imakey2--\n' - '\n--imainter2--\n\n--imainter2too--\n') - fp3 = ('--imapem3--\n\n--imakey3--\n' - '\n--imainter3--\n\n--imainter3too--\n') + fp1 = '\n'.join([sample_certs.X509_CERT, + sample_certs.X509_CERT_KEY, + sample_certs.X509_IMDS]) + '\n' + fp2 = '\n'.join([sample_certs.X509_CERT_2, + sample_certs.X509_CERT_KEY_2, + sample_certs.X509_IMDS]) + '\n' + fp3 = '\n'.join([sample_certs.X509_CERT_3, + sample_certs.X509_CERT_KEY_3, + sample_certs.X509_IMDS]) + '\n' ucp_calls = [ - mock.call(self.amp, self.sl.id, 'aFakeCN.pem', fp1), - mock.call(self.amp, self.sl.id, 'aFakeCN.pem', fp2), - mock.call(self.amp, self.sl.id, 'aFakeCN.pem', fp3) + mock.call(self.amp, self.sl.id, + sample_certs.X509_CERT_CN + '.pem', fp1), + mock.call(self.amp, self.sl.id, + sample_certs.X509_CERT_CN_2 + '.pem', fp2), + mock.call(self.amp, self.sl.id, + sample_certs.X509_CERT_CN_3 + '.pem', fp3) ] self.driver.client.upload_cert_pem.assert_has_calls(ucp_calls, any_order=True) diff --git a/octavia/tests/unit/certificates/common/test_barbican.py b/octavia/tests/unit/certificates/common/test_barbican.py index e7f3528608..1e80cf243d 100644 --- a/octavia/tests/unit/certificates/common/test_barbican.py +++ b/octavia/tests/unit/certificates/common/test_barbican.py @@ -17,32 +17,17 @@ import mock import octavia.certificates.common.barbican as barbican_common import octavia.tests.unit.base as base - -X509_IMDS = """-----BEGIN CERTIFICATE----- -First Intermediate Data ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -Second Intermediate Data ------END CERTIFICATE-----""" - -X509_IMDS_LIST = [ - """-----BEGIN CERTIFICATE----- -First Intermediate Data ------END CERTIFICATE-----""", - """-----BEGIN CERTIFICATE----- -Second Intermediate Data ------END CERTIFICATE-----""" -] +import octavia.tests.unit.common.sample_configs.sample_certs as sample class TestBarbicanCert(base.TestCase): def setUp(self): # Certificate data - self.certificate = "My Certificate" - self.intermediates = X509_IMDS_LIST - self.private_key = "My Private Key" - self.private_key_passphrase = "My Private Key Passphrase" + self.certificate = sample.X509_CERT + self.intermediates = sample.X509_IMDS_LIST + self.private_key = sample.X509_CERT_KEY_ENCRYPTED + self.private_key_passphrase = sample.X509_CERT_KEY_PASSPHRASE self.certificate_secret = barbican_client.secrets.Secret( api=mock.MagicMock(), @@ -50,7 +35,7 @@ class TestBarbicanCert(base.TestCase): ) self.intermediates_secret = barbican_client.secrets.Secret( api=mock.MagicMock(), - payload=X509_IMDS + payload=sample.X509_IMDS ) self.private_key_secret = barbican_client.secrets.Secret( api=mock.MagicMock(), diff --git a/octavia/tests/unit/certificates/manager/test_barbican.py b/octavia/tests/unit/certificates/manager/test_barbican.py index 2634f1ea44..a038e4bdb5 100644 --- a/octavia/tests/unit/certificates/manager/test_barbican.py +++ b/octavia/tests/unit/certificates/manager/test_barbican.py @@ -22,26 +22,11 @@ import octavia.certificates.common.barbican as barbican_common import octavia.certificates.common.cert as cert import octavia.certificates.manager.barbican as barbican_cert_mgr import octavia.tests.unit.base as base +import octavia.tests.unit.common.sample_configs.sample_certs as sample PROJECT_ID = "12345" -X509_IMDS = """-----BEGIN CERTIFICATE----- -First Intermediate Data ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -Second Intermediate Data ------END CERTIFICATE-----""" - -X509_IMDS_LIST = [ - """-----BEGIN CERTIFICATE----- -First Intermediate Data ------END CERTIFICATE-----""", - """-----BEGIN CERTIFICATE----- -Second Intermediate Data ------END CERTIFICATE-----""" -] - class TestBarbicanManager(base.TestCase): @@ -58,7 +43,7 @@ class TestBarbicanManager(base.TestCase): self.private_key = mock.Mock(spec=secrets.Secret) self.certificate = mock.Mock(spec=secrets.Secret) self.intermediates = mock.Mock(spec=secrets.Secret) - self.intermediates.payload = X509_IMDS + self.intermediates.payload = sample.X509_IMDS self.private_key_passphrase = mock.Mock(spec=secrets.Secret) container = mock.Mock(spec=containers.CertificateContainer) @@ -197,7 +182,7 @@ class TestBarbicanManager(base.TestCase): self.assertEqual(data.get_certificate(), self.certificate.payload) self.assertEqual(data.get_intermediates(), - X509_IMDS_LIST) + sample.X509_IMDS_LIST) self.assertEqual(data.get_private_key_passphrase(), self.private_key_passphrase.payload) @@ -222,7 +207,7 @@ class TestBarbicanManager(base.TestCase): self.assertEqual(data.get_certificate(), self.certificate.payload) self.assertEqual(data.get_intermediates(), - X509_IMDS_LIST) + sample.X509_IMDS_LIST) self.assertEqual(data.get_private_key_passphrase(), self.private_key_passphrase.payload) diff --git a/octavia/tests/unit/common/sample_configs/sample_certs.py b/octavia/tests/unit/common/sample_configs/sample_certs.py new file mode 100644 index 0000000000..6a924cc57c --- /dev/null +++ b/octavia/tests/unit/common/sample_configs/sample_certs.py @@ -0,0 +1,815 @@ +# Copyright 2016 IBM +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# + +import base64 + +import six + + +X509_CERT_CN = 'www.example.com' + +X509_CERT = """-----BEGIN CERTIFICATE----- +MIIE8TCCAtmgAwIBAgICEAEwDQYJKoZIhvcNAQELBQAwIzEhMB8GA1UEAwwYY2Et +aW50QHNiYWx1a29mZi5pYm0uY29tMB4XDTE2MDkyNzA4MjkzNFoXDTI2MDkyNTA4 +MjkzNFowGjEYMBYGA1UEAwwPd3d3LmV4YW1wbGUuY29tMIIBIjANBgkqhkiG9w0B +AQEFAAOCAQ8AMIIBCgKCAQEA34asqEe1MexBKGmBcrco08LYYFfJjpmW8m1yKJsm +S2nmHNhJy4Fl+3cPDyHYOiVxnsaMIv1Q8ZMRpjYH2LhvzLt2doyMiiJrqA3ScdhZ +VlGKaURvASSj9dmbRBMqdXZBvTZnMH4aSkL4DalU7NiW+jbMb5Gmf+bozE4ZAOES +6eXsP5+yEhJvzgmT/RvD/2w7EtCtrRnnAlMwHJACqozRQYXuY8iLw7YJZtk35wyc +EJRilXIcKUCuwQfHG6akd6da8PIzEZ5bbsYLtpslIoh53vG3htXTp7eGDp+MXzlr +yB0+QqjXuOMR1ml1sNwVMpHO4oUFuXFGvuIYnT2QhYerdwIDAQABo4IBNjCCATIw +CQYDVR0TBAIwADARBglghkgBhvhCAQEEBAMCBkAwMwYJYIZIAYb4QgENBCYWJE9w +ZW5TU0wgR2VuZXJhdGVkIFNlcnZlciBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUVXOS +1PSqVuhOP1OKBMNfSHfhAsAwgZgGA1UdIwSBkDCBjYAUN1MP5SS5ZJyrWuPVSkEF +KK2SnXShcaRvMG0xCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApXYXNoaW5ndG9uMRAw +DgYDVQQHDAdTZWF0dGxlMQwwCgYDVQQKDANJQk0xKTAnBgNVBAMMIG1hc3Rlci1j +YS10ZXN0QHNiYWx1a29mZi5pYm0uY29tggIQADAOBgNVHQ8BAf8EBAMCBaAwEwYD +VR0lBAwwCgYIKwYBBQUHAwEwDQYJKoZIhvcNAQELBQADggIBAFcxJtGRESeflY6+ +WNp9q3LGYP+uzyUvjimdFQzKRi+Sq0Mi2YI7agpvtE9IZHov+JwzPaBXM4yC6Cap +lI88cE0KNowkjkON4g99F8m9WvaXRChtlJ53BizRkGKgw4Zg/0PAbpjLN7IU/Zrm +hOwyhBxmewMX3WAk76xvgFXTVN7c9FnCRvuN/6xO+CUb/a8fcNASdD+8aw+iS7iq +gvV1WGeGY8n8F19NggWSiRyb/z4Y1VoqaeIPfD9kjFrGApEGpiZphbzl9jSX8cPQ +YbDbbxBsUyfxtMK1aVx258ow92NRsDsoLGELpzF1AekzfQDWtHOpqkaPNunV2l4f +UGRi5J5stDi80Zf1t5JiFkHRXLeWAPa16AifF4WhmAaw0+zxINUqYH1/kt7LQP62 +PT5g3TK1S7TLvqfouw69AQUZAezBUfEkfy1816WGpuntWEIe3x4sCviqVHdjDtE6 +Pntzq5bvIIQ6/em2y5gvG68yOXYNTWmxOVaXPJ60eilbPyCD8UrkSMbqX+ZlAfFJ +dsAnySgPfz47dhd9jHulx4/rBZfPx330DNiO/wQZxQMTbjhlTJViojfQuNRaBT4E +Vi/XwUwVUqRURyQtuP8QJdPh9KD7uX6xHjqBALdwzCYAFaqelPue7TJ7R/I5+02A +DV8BnY7U3zPtHtPf6i8vdYwgAOJG +-----END CERTIFICATE-----""" + +X509_CERT_KEY = """-----BEGIN RSA PRIVATE KEY----- +MIIEogIBAAKCAQEA34asqEe1MexBKGmBcrco08LYYFfJjpmW8m1yKJsmS2nmHNhJ +y4Fl+3cPDyHYOiVxnsaMIv1Q8ZMRpjYH2LhvzLt2doyMiiJrqA3ScdhZVlGKaURv +ASSj9dmbRBMqdXZBvTZnMH4aSkL4DalU7NiW+jbMb5Gmf+bozE4ZAOES6eXsP5+y +EhJvzgmT/RvD/2w7EtCtrRnnAlMwHJACqozRQYXuY8iLw7YJZtk35wycEJRilXIc +KUCuwQfHG6akd6da8PIzEZ5bbsYLtpslIoh53vG3htXTp7eGDp+MXzlryB0+QqjX +uOMR1ml1sNwVMpHO4oUFuXFGvuIYnT2QhYerdwIDAQABAoIBACVPOmSAS5tAnwOa +0LOQJO1ruWgjXw5BTrO6VvK2Kuctju5Dn9WrDJWzorzY3lmeRF/HLj7s32TjMm/2 +1spyxp56r+RLw22PHz8Wx4ifHxJMW/kEJi8fqYpwvvzW4iBnE8P8X671bXf1w6es +GvPJlzG+kdMRkaQJq9PmOUAvUVPe7+xLuouU+7q4DAiq4oXMoidVbhm0FC5k4LB2 +q+oMzcdMiQ6rQfQB1uh2s659zwW7wAtRMgx4LeY+lIpyf0Bh83Ibi4JybH+DQk8g +AkrEXDE9aslNx9ZXVdfdQiCRDklbg41HejZPRhsRntH0v4cnjOGCYrVDfTKEatka +ltWYyXECgYEA+LFxGZH9vn2nh2PtAs5wnsjc/79ZOKJwq6j4stuRr8MBazRtMhP1 +T1g6pEkBjEoUUomcpH/eG49PrB2G9oYIfhJqkRo07m5Fyom3r35/V9Q/biqcGLEM +EodujvziHbUQDFxO7jLigRjsVoG4Uo0TXT6V8KzKxHGgpdCvYKNP3A8CgYEA5hfv +829n55dkNUFU33VKhlyLD1+mAUdPkjRHYiOianv1m8C5z8rzjxs+Fa8Xl+Ujsr0m +JpRvOiNEwm/b6bF4NLKOhaBPK2IAYzGPwy2yhXELcxHuxNArJ4kVp+YdwvvRSWCa +767r/CBS7gCCM5bXlU3saMS03goZd+l4fo778hkCgYBxkVZ8vtaJbwhaI5/QcEWt +vTxu7groegXJ3lf0FaDqCrtTIZXcEJEtsrTU4SH71riBGKaX2GytWTyg9Lr1STAH +opFXwgf5+hGU9F8VnUa57QsqW/r8q50/uOkcEw+PUWgKvPyuej5FhgQnXQW3bQUy +x6nhRocyPlGGZ04va2TEsQKBgDlIpFh61+d0bWJEzZiEXvVsfMJrEa0nz8uacFsi +fAD+s3r/VENDR7fNFHvZh4otZeHN7X2VXsuelDPEHX/kywRzn7/s1Uj7sRUA9cWl +ztgh+LPBNyyQlu3U1ythwu8UOlqGTox1hBLVCVBvl/q4BxwItl6u+kh9QzHzUihP ++LGhAoGAGRjYSOy4aiFK1Ds/5doEcBj3eGsfNx0va85UYDMnoMxkw+qHlFOCzrG1 +nUBaaqVibLaROn4V1QnlSOA2vjc2jMMDKMfnjawtqBC018tQDVcE75sun7UzyxtS +OWaQy6KhqrKpPy3tS1wt1vAYPWZw/EIo4dDXYBo55REI5mSBZrM= +-----END RSA PRIVATE KEY-----""" + +X509_CERT_KEY_ENCRYPTED = """-----BEGIN RSA PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: AES-256-CBC,086BA545587FF5F6F4DD9AACC122603A + +mWGjuSlBRAU3QruM/CI7m2LCN7wTHp00V9XbSDHXQ9D2MqgRJTf09iRlSsH7GxKF +jbc0TWvrYkojb5BZBg3PePfRUxhiwGu7hYk+GLbRQsA0iL53wA2a7aPnwzBFuwky +u/d0bK39n8QoIj+vUgVe/7C4Xj6eRC9SGlGBO5syCQqx/KCmovv+cqKG+hti9KFm +e7KAsdd/7noEQNwo2Don+0gZwDc7lKzR29NlyqnASkbllKMzGaMXVfdDPGjC62AF +3rT0HllONHo7McmRfbCWs7nMEvKFgxKvHoP2/0ph5DD+DOKFCnLSfWdK22EgG9TT +UUcNiNCY/A88M2GnHdYjBMVokL/sQ4LAsf9Tz7aO1D6c2p50t9gBhDpOwKwgYGJu +sp2FLO3/HzHS30s8kfOg2ZDzRm5jOlFsK8XY175xUGrsCkSQmQPY11b7v8baBHp7 +KOA6xeJHD7+K1oKvxAqlU7Lwfmm0lbS9/JnIDiDel7oTHESk5mqSUZkkyWze+iNb +S/3J/8mtnHl72UpULoWkvSfE5xTu5W7uhXqCOayiOeiUpalKG+gwUZI1lgvDlXn/ +2LpFEFY/y21NWGIm9c1lxZdOzJpnfzvXw+27lGPjNhtjhro0wIFjQ7YCTyq7Ky36 +qPdJfU+206vkX7tzETyGPh0oO/1eP5b1QjJrtP7tMNS45yn4yzjICNhC5NXAXgbU +F5bUkWqhQDJ6UDa6hCrJ6bf63AdnqTtJ4layKyl6dz06qrVNpCyGTNNhJykdlSq/ +PkVes4X4yh6TA5pJowV2bVnM8nqN7H8TXbEetF9MP3ImYnVzDTnBWugWT1cVA45h +GyV/j4VHBqwPojGhRwFDM9reQ38tTrmss4l0hxC6B5ivIJtUvCqNa+E/cKecopmb +5fAdiROnS548tXuPzsz1EtcVor7k1i//SJJrSgqpaQb8E36uYw6r8yXQ6zhOyoUF +Pz4OVN9WR21G5R4sAjHV9U2l6ulgzwpE7O7Z5fSuTzZBttFX4U0OZZDfrDIF6jNB +jrd2RBacsjsm0PRGw2qrMZlPmhhHl0prfIPOrkRffre3wDk7POOoa2U/+CKcn86Y +780WrIGL6jMp31D8HDmLZbvsWtzKjTqMIsqo3gsFwCgtu9PKZ/z/sQGND6f9b8u9 +gpt/osBxSi5b7lHE34InizhzakEMtQ/bshO4WAayGY3Kaf0dG89mwQEOOzUw54Xk +x9F+hzYGb42IaTHO+h+mMznB4sh0iLyekt7eybwYGX/1/Oz8WQ/EfDHYu16XG681 +Zb5ev/6rojAWe6yib3MEWVjVcsoNUUA+51+hEO4UKEliNX0FvOe3q0aflqPVzi/0 +VVB3erVNQ/5uunGdZVzjgef0EbhFlHANjIcSD8N80NEaG2JmhVBd6kc7Ev1uoCK5 +r3kHNhyy/fipKamS84mhjTq3rgSeUCndf/TI+HSvJwQaA3sm1Bu5UuErjf9Qpq5P +osar1zVgWl2jEUejqwnt4626J8g0MG92amHHsHG1htzjAzaTqtMlORdUmWgppYVs +dlGLDA9eMkmOBo1WdQYZDDnCcNVdT6MoeKmDsqmM6+ma4vpHuelYmDJ5l0a3hGbF +-----END RSA PRIVATE KEY-----""" + +X509_CERT_KEY_PASSPHRASE = """asdf""" + +X509_CERT_CN_2 = 'www2.example.com' + +X509_CERT_2 = """-----BEGIN CERTIFICATE----- +MIIEbjCCAlagAwIBAgICEAIwDQYJKoZIhvcNAQELBQAwIzEhMB8GA1UEAwwYY2Et +aW50QHNiYWx1a29mZi5pYm0uY29tMB4XDTE2MDkyOTIzNDk0MFoXDTI2MDkyNzIz +NDk0MFowGzEZMBcGA1UEAwwQd3d3Mi5leGFtcGxlLmNvbTCBnzANBgkqhkiG9w0B +AQEFAAOBjQAwgYkCgYEAp8q9ybIlTP+Aka1jaLE22gE784t3rQ0KC83ODSY0283R +QX6BfHrAVTj1ctyvz0D6hxXiYXwi9mXXHvBzzxScPxImQ7jbvYyP0CtagQ4QGj7w ++XVWY94bY7X5cF5NlGHl0EIHBO2G0wc455Mgzlakkfoa7k9YJM37hfwlBV6IX9UC +AwEAAaOCATYwggEyMAkGA1UdEwQCMAAwEQYJYIZIAYb4QgEBBAQDAgZAMDMGCWCG +SAGG+EIBDQQmFiRPcGVuU1NMIEdlbmVyYXRlZCBTZXJ2ZXIgQ2VydGlmaWNhdGUw +HQYDVR0OBBYEFLc+lXNhKO+47kWgrMirpmSU2FMWMIGYBgNVHSMEgZAwgY2AFDdT +D+UkuWScq1rj1UpBBSitkp10oXGkbzBtMQswCQYDVQQGEwJVUzETMBEGA1UECAwK +V2FzaGluZ3RvbjEQMA4GA1UEBwwHU2VhdHRsZTEMMAoGA1UECgwDSUJNMSkwJwYD +VQQDDCBtYXN0ZXItY2EtdGVzdEBzYmFsdWtvZmYuaWJtLmNvbYICEAAwDgYDVR0P +AQH/BAQDAgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMBMA0GCSqGSIb3DQEBCwUAA4IC +AQCpLBSdj9eeXWZNFXZzhtrI34f+oPcyZeERo04gBcNik6nSL+iwgv7TqqEnH+X2 +lSCXAjxQEsXHSf6Ox/YWlXhr20jCoTpZBQIB0kbu3wBfowxNFIwKKyIbpt4pbJzC +Hnx2EsOVkxywAzE9aos7JyXk4ya2U30v/m54shC0Jxxpp8KqNAUQ535NCM+563eN +8GXAV8uS6UwTnDwepU79xixDmk1XIsMlJP0e6ROsNFBSdZ1QwCjOwbA7clAdlpch +f7eF0mJTXKkrFUBVqZh2iGFQ4lasoXeTM6yR3be/tO12NdM1tGT9HT88WeRpRin5 +73pTSETUMy9+80T57DxpGNOVkBLI1AhRWkqQ7kgyNmm9jajZVyZTuSPhXpQAunxs +XxS9gPqe7LuBoRXsxLEGrXJ4h5+D3OBr4KGMHcFbI3o71ZzgDMWQ8Hyik7j6BE3J +zXmoSZjbvJBiz3asU74/a3dH2XkNOdzErN8RkMRzL8Z1TdgL+SRndXMpSM8cI44v +jpyx6T1AdxgMrstDuPX6U0EMl2WoEvkwtePUc3hBYCkm376yVbtbJcAqndFW2lAY +HULxFHp3QLrnbQEvPIcD0EWppJ1GMqb/Gv8jORzOks56UtOIfavrzGrcvRSKoC4Q +lDApYKCiRvvBSVfgpoiVungh2NWSmNW5bn2uOkPt+vTjcA== +-----END CERTIFICATE-----""" + +X509_CERT_KEY_2 = """-----BEGIN RSA PRIVATE KEY----- +MIICXAIBAAKBgQCnyr3JsiVM/4CRrWNosTbaATvzi3etDQoLzc4NJjTbzdFBfoF8 +esBVOPVy3K/PQPqHFeJhfCL2Zdce8HPPFJw/EiZDuNu9jI/QK1qBDhAaPvD5dVZj +3htjtflwXk2UYeXQQgcE7YbTBzjnkyDOVqSR+hruT1gkzfuF/CUFXohf1QIDAQAB +AoGBAJevcq8ZuxrGiAYqBwon2nxuTTI4TLJGbWSKYtIEThi/EYLxyEEt/x2L5mg2 +FUF5boIcSJD0Ve8F1dmEak00RqJO96V7riNe3a0SGhYjak6okEXB1W75LfFQ7Jik +I2Wkdg+M2gdcHNKXmVWrO83aR+zWFXv0yHINANQLaUhunW4BAkEA1TKfKbdSkTkn +T98j6rGM73nd56yCIK45CZmHg33ICyKjH/fUiNpHmZtBxCgrYTeFOJtLEW4QENy8 +vusxB1zbQQJBAMl6eOliCfr6y85pCIKGFHL/Mwzij41xRPv50wWkFpdNOPW8lqlK +SGZHdMn6fWi8e02tkcIQqeRrg2cU6WsrA5UCQCMBqeLGqDcSRGM4B4S83KBhyU8G +I2PMV68812R+3b7+U/ymy+4gsUsGlDjqQ5016ZkO3reg8+Bg7lkG80j7NUECQHJr +DPNs68IOX2OPHngRcNeFuhYdK+zlYInAbGiNsQ6nmitjuCPXvZnoBpkVmda7A0Mv +yNDu6ayAqhUGOTDVMqkCQG9Vk7xjpe8iLkI4h7PaxaqiSwY+pyY3QoErlumALffM +t3c9Zw9YGbij+605loxv5jREFeSQMYgp2GK7rO7DTbI= +-----END RSA PRIVATE KEY-----""" + +X509_CERT_KEY_ENCRYPTED_2 = """-----BEGIN RSA PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: AES-256-CBC,3CAEB474D1526248CA20B5E4F84A6BB7 + +t6D+JPlgkUbxQzP4dSifigWPrY64diq85Kl3R+XSjh4az3hsvi6dZCEna7f+G+UH +Bgoak3/EPVcTe5g09ewxfnkBvjej78ktc4fUDWqcPwl6xwSbqVkz+ejEe8MAOR4d +VN5bG559HXD1AYbhr3XyONpUrNlyQrdtaxNjPtt2U77aPfEo96/sEaYA3KXKq6pd +NEXU0K/4MSRP2sErybUubyJBz6XJLZ3LwILXRONV41GvFmnDGJ20I1X+IzlV/YDo +HpFKspuTrDzXttlMFMcQVdCWX450Zs988FWa4vwN0Ma1sgl8VwjcbWDAgx5tM1Ml ++t0PT1yL2kIGIPbnsVoPphIet+qjZZmmOFCRwfvXiYSTf9FZ8eawnqQrmoSN5iNt +T63Aidf1dV0nHk+IZxkdgzm3C7ffeIPG4yMx6px8NnJzp7lCMx76FudeeqUx0ezC +Del0Thfh8/N7RX7mUP7HdybXIrR9Gp+p9WUelag6DpMgCcGWNvTtk8NUK+3TXAax +Ud+eZLP6k5LXiqhwSuWb0/r6I7OSgseOBsSvAw8PVfDsg6LwyhLqLmOLgxVas1Ay +EXJVqD0QviMl9aXBK/kpsg6rdhJCBJ6WQlytS73Iyx0plD38SwAS84d6B4ASLHye +wXyd6UrKQ3c6hQV8c9jzHvllaEafF3WUjacwuwmNOlBuWh7887JsFeYqbEIlY82u +pVM7cDTfJhEggpKK+q3muntMeLTVaIKcqvYoITbVoRJG8F4Zc29cibZjz19zshBM +OEUKHsL+I+kFr0SBLY8UnAOjIt9AjJLgo3uVC13fj6omO4EeXQjY82GKo70RRszs +-----END RSA PRIVATE KEY-----""" + +X509_CERT_KEY_PASSPHRASE_2 = """asdf""" + +# Wildcard cert for testing +X509_CERT_CN_3 = '*.www3.example.com' + +X509_CERT_3 = """-----BEGIN CERTIFICATE----- +MIIFJTCCAw2gAwIBAgICEAUwDQYJKoZIhvcNAQELBQAwIzEhMB8GA1UEAwwYY2Et +aW50QHNiYWx1a29mZi5pYm0uY29tMB4XDTE2MDkzMDE3MDkyNloXDTI2MDkyODE3 +MDkyNlowHTEbMBkGA1UEAwwSKi53d3czLmV4YW1wbGUuY29tMIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6v/973etopk2Vz95DUcx8X6hLfJ5m8s+scn7 +nMZ37fSqAGPF0veGpqyqxorwh+GYLjlrvZkhVi7IZJAsLU2ztG4+MEoYzbyhgJer +FmepBC7xPIJEjh8FKhtpvxVOMFcXJ1CZT89Ww0rVPnaoE09DS0DRo5s+lW0dD6Ta +QW0S/6RCZ5RpD1q5MP86JvTspkWhhKY29eEMFZQYDwc9HEPE+C2scapGM6reP+Ix +c/Q8806BUXNkLoXvGo+LqmeONquCUGCXL9HLP70Osp2jfqgTT3RfOFx3k5OaASeZ +MhHRqntdReYXN16PhMU/eDvKr42QxCwNAVLDSrkJGG8eChOgVwIDAQABo4IBZzCC +AWMwCQYDVR0TBAIwADARBglghkgBhvhCAQEEBAMCBkAwMwYJYIZIAYb4QgENBCYW +JE9wZW5TU0wgR2VuZXJhdGVkIFNlcnZlciBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQU +eOw2E5rYzvCuhuAR11GtoyT/qgswgZgGA1UdIwSBkDCBjYAUN1MP5SS5ZJyrWuPV +SkEFKK2SnXShcaRvMG0xCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApXYXNoaW5ndG9u +MRAwDgYDVQQHDAdTZWF0dGxlMQwwCgYDVQQKDANJQk0xKTAnBgNVBAMMIG1hc3Rl +ci1jYS10ZXN0QHNiYWx1a29mZi5pYm0uY29tggIQADAOBgNVHQ8BAf8EBAMCBaAw +EwYDVR0lBAwwCgYIKwYBBQUHAwEwLwYDVR0RBCgwJoIQd3d3My5leGFtcGxlLmNv +bYISKi53d3czLmV4YW1wbGUuY29tMA0GCSqGSIb3DQEBCwUAA4ICAQBvDBAbwipb +h1bgfOIo8Wv17QdyFDVvzj23eO+BbYbUNF+JD7HAcq/Z53RSj9Kv76NS48OLqWUk +RM81TPiHimolFvF6AZLXeYKVpl48nCQRQixHSkmW0I8BlpQ5/Cl1WUqFRcDAl3i8 +lMLpAUhMb8dt4d2hviktK+L5CiBLdmKCnlz0LOK/4GuF4Z586jrrWyjw/GBYvmXX +0ujjli4J6WMJXVZ1IIwIM438N0eG6wKRNBbJQl5tJjKVX56hSkVdgQPz0gjhNGlJ +VvImaAtLORgBUqXXs2PhcZ5HHeSd/dF2pJeOYC6P4qjb2BqhDHwDKjsSDD2sPoMF +fvI6pQ0zPCpx7waCxpk+UxshJk3CG1XoWdlWZmDBLMl2KjDH0nWM7nI6oWPXK8K1 +R+iBL4IUp8p/ZvGGcGeP2dUpm6AKcz45kYEWPm5OtB10eUaCQPkeUvWRmY35f0e5 +/7LlFF1VDlRlxJPkroxrDDm5IIWS1VPTnelXzvBKenqTFFbQUzS1mmEEY/pPEKvS +Z8NAha3g0/jex5sT6KwB0JI8fvyCzfCS8U9/n4N87IrFcKThw+KMWkR3qjZD0iz1 +LwW88v99ZsWWIkE6O22+MmJGs4kxPXBFhlDUCC9zPBn2UBK8dXSYL0+F3O7cjWQ7 +UUddoYPP4r24JRrqzBEldSDzWeNSORpUkg== +-----END CERTIFICATE-----""" + +X509_CERT_KEY_3 = """-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEA6v/973etopk2Vz95DUcx8X6hLfJ5m8s+scn7nMZ37fSqAGPF +0veGpqyqxorwh+GYLjlrvZkhVi7IZJAsLU2ztG4+MEoYzbyhgJerFmepBC7xPIJE +jh8FKhtpvxVOMFcXJ1CZT89Ww0rVPnaoE09DS0DRo5s+lW0dD6TaQW0S/6RCZ5Rp +D1q5MP86JvTspkWhhKY29eEMFZQYDwc9HEPE+C2scapGM6reP+Ixc/Q8806BUXNk +LoXvGo+LqmeONquCUGCXL9HLP70Osp2jfqgTT3RfOFx3k5OaASeZMhHRqntdReYX +N16PhMU/eDvKr42QxCwNAVLDSrkJGG8eChOgVwIDAQABAoIBAQCbp336bKn9BkCh +H7C9o8en7rkx5ua307KuLlxTpn3vhlxIL83rg/qTUdE3+vWA+2NCUtRTXCdhn1Eq +kvg/9bSvMUpNz/aH54aN12gCSh0AYVt2Oc7Q2Ckij8/GOoV0rWrvpoo1+967Mkj2 +u79uMtUe9ksldAHLFd/m6cmLBoVL/6rxByO9JsQjb+qFcNcLmNwTsGWttAT1a/Sa +Cy6JESzJzL6jMB1hNr/UI4nh8CkD2Ox+G6efs6FyMtayOP/AVwr8jSywVWZ+9tiX +kidCNS5xzazt1aMeJcu1h3yzYt2PvNHVE17T5imQGDUKuhmH/PZdySldnAU2srm5 +b6tGNAJpAoGBAPcjPNJHnUSYh5GooeOPCG8QtnhwEDbuwcOzoeFvJBNlSs2XF25O +cXPjUx5HVpJqeBTiOX2zvWr6jK6AggC8K+eF7ElecEeCEFf4feO6iv9n97bzntmi +lPlfKBkQOYfUA/Syva6CiLuz+dZS8zYIDiB6C5/hhIFi+O5fG+hny8ILAoGBAPNt +VBxjz8bl/oaj6wm5oVk7HMBABBbAbPcH31YelF/cEFe+sTMk07Dpqebq3BQcEQAZ +YgERoZeqA7Ix47N2LUfvUWba8Kg83JvwSYV2WRLxbGBMsubUHBX3J7+2d7mMbaUb +NycvS3K+M5HYDOdGuXwObJod54pl0D+8Kk6QHXZlAoGAOPfLdmGBtCVA4uevYag/ +9nIwzbRvWGpTCgynXTLkawAnbRrOEOROLLuTFmC1aQzX32CChiJfoIBe237N+ONn +b3gkjokRcrpdkBm80zjV/6f0pxyjWmGq24z+zkA6MsBBpS9qoAaBBFupVKlMXQEg +WIYpldJDXBv3a+NKqJj8lB8CgYA0rjlgt30U11tg+gJ4pVK0w+Ux+eion9Y1E+AS +fCWyJSboRl2E6vhhNgBN+A/OzlAjjFq4Tn+BGgsYulFD2lRsV+9u6fzg++LmYHcY +ygb24YaJxK+G4up9GnLgu3Vnk2t7Ksuh0EtstprkejQ4rQahQWHhbI1aVzRdRrSF +Mg0ePQKBgFn2yh/gKf0InDtR6IlIG9HVI+lMKxyU5iRH/9MQ7GS+sSjiAXdOtGJJ +1QT9hTtPzR9SBXqu6yWJiIvYAfnrmea6kMb9XH5L/XIciZA86DapUl+TWicpI6jH +KX8jFiCL+HcZX+pqAaUuifgwnqd88EX7MPoU6Yjq02To9ZAPA+SA +-----END RSA PRIVATE KEY-----""" + +X509_CERT_KEY_ENCRYPTED_3 = """-----BEGIN RSA PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: AES-256-CBC,088E3835B2238C332FC7AED391C9CF8D + +qKPZwlDrkX/0J8Un29cmvUuVi4FzSWRRQe+m63/ezh1vzv8IOtmw87lDffPhbJ/C +mLAj09l9Z5I2yLWb8tIJoYOzFjxU/lKI/M85EPEpuAggzpqCf4uwor7MUx+++fKv +aJZuwgbmfb4//A9iNfSriWfrrS7dzVm2RrlNtj0ityiigzTSfY8oiL7fGTfocdx3 +F0mXGEumySEMge3RF/I6/bAa8c7T4JRUc4yN1kzMftxH1H36QMjV+2thyo8UrB4Y +cBIIUncFbTvnhvjqszQrCrh08Tes+AtxUXevCLRiaLqOlnF1LIyic/PRob+yqXKw +jqUyjaj48MvJwxP1l4NtzzPz1QM4aaXZAq8O79DVHxaE9d6Qe9fw8cASFGrTzU2l +8jbHo+loXcG8LWp6Cqdvv0VMuK44G5TIOuFmuZ1YHgawCtihocoqUOyXJ3JryMUN +0RiaF3ybkplMEVtZBtTYkhx6vYnq7KX3CghpigcOajvzZ5jj6JMXkcenFSU87VaW +tfzbZRk5LYuJOLu9MN3joftvD4m/mnFVXM4SF5ypTUW5PRSZGEa0im4LPWq9LH3s +lrgh44jVxqfyAxtyVC8Mf3X7tOmm2dlHWLB8kBcqHfcJjRaZQeD25V2DbmCAp7NN +UsUKT0ftRfSKGTmsSfPv62mFo4RrdI+/Xws1iOY8V1LekGvKc6zpSwYfQnrwIUi2 +7PkslX0UyXaN7j020anNE9LV2NnccAWX/lkGCoUn8EPPrAum5wopLxm02caNKUlK +RM+Te+LJeexLadkFStDentCmH3m9GoehDvWBLHGbdb/5sXqvxuemBxkyhjqXvOau ++cyDRmfUtLf8ik9PvzP/dQqBn93fMkWRlJ6zRjn5q4lG+qKbw43UDWuYMmSBQd5Z +ZUuTaT7bymQyPLUFmjkQlQm+WOFgCg26WuaXn8sXvQCtK3Ha9v0C21gJWQ7PnhKh +hXFwuD0Jfu7G2Pie5ToBhsxC5PNYyVYZQOCJ3ZcvH1Hv8RCvIDPHMFdZohJVGwdA +8X90Z433Nv1ke2jAjMX9+Ph4txxRYwcV3IpfdyAFk6cjukdkBrcPPFARZiOSeNwO +XskiNT6E0KUAc1KNyhsBRTSxmNkkzfqe4hzEkLukWBsyJ11/jmgKJqApyKZfePGR +/kDGbJVbSlMvftmBNCkT9owMDjKmwHvo5iiV+rkhWEq3jaISu3+qtTj11S4+bRS8 +vlh3G+BjSvpA2SBbXKWM0UrSnxtLow41kIZTZJ+5QnuQ9LYER1CAuMxlqManBWq9 +JwHGmLHqcLVPxDXo2fTsDHAZlw6TD3pC53WDYbAZC7SsePyNvbPk9P8YG47F2IZP +ioxamytTKal/abrfrU8izw1HM87LNVQ4yAGCIlbj+0utN+aZfFDXgm+/FafraANr +Ti580sCEkDrRrzAp0lG3AcSGTM83Jxz5Sz1o6xdWDBdshfcPIJgv9g6NlzPWzy3/ +39Xhe11dMDqKOdiY+KtdDCT4R3rp49Zctc8KopEX9yjzmPm8aekgyzIG8ak4ka6I +V1OqZUUKNVGYtDAMDqqDEKNp3Y1mmeD8637oWVTQvbVJpatVIVoKb+MtKrGkVf0d +-----END RSA PRIVATE KEY-----""" + +X509_CERT_KEY_PASSPHRASE_3 = """asdf""" + +# The following intermediates were used to sign all of the above +# certificates and keys. Listing the same information various +# ways so that we can test the different ways users may load +# intermediate certificate chains into barbican. + +X509_IMDS_LIST = [ + """-----BEGIN CERTIFICATE----- +MIIFcjCCA1qgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwbTELMAkGA1UEBhMCVVMx +EzARBgNVBAgMCldhc2hpbmd0b24xEDAOBgNVBAcMB1NlYXR0bGUxDDAKBgNVBAoM +A0lCTTEpMCcGA1UEAwwgbWFzdGVyLWNhLXRlc3RAc2JhbHVrb2ZmLmlibS5jb20w +HhcNMTYwOTI3MDgxODMzWhcNMjYwOTI1MDgxODMzWjAjMSEwHwYDVQQDDBhjYS1p +bnRAc2JhbHVrb2ZmLmlibS5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK +AoICAQC8KiZi1t9vX8IBtiOgOXHwyTo4ljYOqz92BqyIj4r/YJNHxeJ7JHNiM29e +u6K/LdJleOkDXV+m0LDchn6pMf1/VJ5/Zpr7eWTqyKSaP5zTVbIOI1It60MQpsSi +GbcfVPJ7TrwzNKtyBXREHwC+mEvIuEwDRHd8DljJG5J2CpS3Re/CgxR8DrKXnT6Z +XHikog3yYJ7vULtxz8ktgOjM7uFh27YmHU4o1WyeAmMwpesVkqY7E7frbIYYbQo5 +B1D1eWqM3KldqOQqvq34kPkf0vdfXxPurysNJrEaOXzDRdI6GiXaCKtnSEF0urCR +denIDWMeqq2w5H1ekNgpK3XeFFxQYkhvXN85HiRmPO9wrd4qdEs1nvTIVVEDpB5F +Fe4aFQmsAe57Ll1DTKZcja30VD1uJ5PbeoysHhN270+IbFeXK3x/icS5F1QdfE/p +YIA0L3JRSY88IXw4giHnlLnYb55NwLac3EXmr2Qks/T87/gbk5gk9G+0XK3FSRxF ++MDdmRiLAKLSb4Ej3wX1dXnSgelx5cBZ0n+NBY/865oauui27/OIaL7ZaDCDZU/t +jIJDy/uQkuAjH4UVF4m5PqRaykqrjbyRJeADbL2E7CxamOsgyAfzhgIt04hpKkvZ +oCCTRREeNp9rRITQiGMsfCUuxackDajsW/pnFD7m1ZKej8fcdQIDAQABo2YwZDAd +BgNVHQ4EFgQUN1MP5SS5ZJyrWuPVSkEFKK2SnXQwHwYDVR0jBBgwFoAUhmmo2HE3 +7bnky9h7IQ5phCFGU18wEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMC +AYYwDQYJKoZIhvcNAQELBQADggIBABGdyLUs9Pm9SXpSQLSY4BWnYdZvUoS5rhJ7 +coVrkTm3bBmI6haSWyoSy/l0Qno+7rXz1YruKYqKaYTAakuDL8C/J2VuL/p29nyC +lH0rDKNuz+6jU8xLVmcZwBtsKK1QxNgAdQ3DWygtmXzZ/tigfIVVIyaTkOvOjovu +IxTWAbmJu/xbkq4zZEaYJ0xobK8LGq3ZDauCsPNykqH/jwLwYWHQbKPmppTkNff7 +unXrxQ+eSH/a1aCCldZI/NZywjZpNUdylEcnZhWshiWChD6j+CgrirdO0JeH9sGR +0La71VqujFWvVJUYYSbb7l4KFBLFw8Od5Z5rpYXm/qTHd6OvyS3qajse8ardqN0g +2Hunu0AtJ99JBHxzTP6blAcuTTrwS2XjB83/7k5YfN0jGbqQOYCJMTZ3pk3JkrZi +pxhjY1ZX1N8Opb7IwgjIXwzNy/joL7smUNBQlTPDN1IfM5b83NGNSDKaS1pWiqaL +XO6erkwabZxCVfGgvIk9hE4x6+Cu+jdOLTpAwq1mcQroAp1+CInHrZeHdnhz0zR8 +4CUmddOos2WYTF+OvRfel32rBCaKlH6Ssij0JGxSYT24WXygsCdpDXfimg3O4Fk2 +sJlV015O7iIu22bowsDcF9RfvkdHNULrClWI12sRspXF9VmRjbDyG4eASBiulJQV +bk9D26vP +-----END CERTIFICATE-----""", + """-----BEGIN CERTIFICATE----- +MIIFwDCCA6igAwIBAgIJAJLWg/Z3x5xpMA0GCSqGSIb3DQEBCwUAMG0xCzAJBgNV +BAYTAlVTMRMwEQYDVQQIDApXYXNoaW5ndG9uMRAwDgYDVQQHDAdTZWF0dGxlMQww +CgYDVQQKDANJQk0xKTAnBgNVBAMMIG1hc3Rlci1jYS10ZXN0QHNiYWx1a29mZi5p +Ym0uY29tMB4XDTE2MDkyNzA4MDU1M1oXDTI2MDkyNTA4MDU1M1owbTELMAkGA1UE +BhMCVVMxEzARBgNVBAgMCldhc2hpbmd0b24xEDAOBgNVBAcMB1NlYXR0bGUxDDAK +BgNVBAoMA0lCTTEpMCcGA1UEAwwgbWFzdGVyLWNhLXRlc3RAc2JhbHVrb2ZmLmli +bS5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDdYtZguUzRpohJ +8GI2/KCXZxd6FzmZtqrKz1JhZxSV56WhYnYljzQgRsPX8lxUWC/nSm13cjitfKG/ +TvDNTs6bb9t7VkYM0k0ewvArcNRSSR/YHO0r7fWv7XkwTvt3yFupWkeNBaqDChaZ +vlblcQxNUgXI3r/dOJDhOlfzhF0LML5FIIHgkgQCHAUQ62OfLkmXqNPYAKa9K/TE +4UGtG9LYT0dy3AwKUpvXfnKJSEgrRd8Nul7Rp6BgYWoJg6pZD4GLFiqT2bxphJJa +AYulgtF1jDHeZgyunm7WrwZvxPC8AIcFcksRMxB5XOEo8PBXaGHxbIjl+PCw6WpF +5g7ZO95keYonpQ8nK9Vcn7BgWcQUY5SuZCaMTk79Hs/kD1upc22IHg//t1qy+0i2 +SNTxj7n7mkynBHoKSrlVviUkyZHQYniuAGciYYKTfRy0F1LaM3QOUF3XA9j+2g1j +CWolMPWpzWFTOkBwoCmCs0MX7FaYvsAeLx4rDVLRQWzvKZKGTubDBWS8wBsAq0hD +v4b3r4k6cIz9a4PYNFARsnShkKHwln9lM5HjPHUNSZ6oaaIdi4wEf0xwipMiEi+x +h3Ukztq6pBGlNbdxdlBP3PVap0AI81alswLWqCL5yBHzv0NQp+x7/EODJDcvE6sK +PRmBVTzO9Y20KMlHrcdlPiNbBDhJ+QIDAQABo2MwYTAdBgNVHQ4EFgQUhmmo2HE3 +7bnky9h7IQ5phCFGU18wHwYDVR0jBBgwFoAUhmmo2HE37bnky9h7IQ5phCFGU18w +DwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQELBQAD +ggIBAAbqGW0/XCmvptoh/6oHH8OvMUbn4m8Y9DqEnlWkfadhYkYaOh5SAZ51mD7O +uwi+he8gV0evMgmW9liDw/i/2IWfHLT56yHSQqj8kRYRu8ZEdfC8IQg7Pj8mCJru +JKtWWfCHWnoqxPmCkYfWrb2wb5K3Me33zMdvtdMg3Hzxg9UoRFvqyRJtYj7coK8A +3uWiX6vjDZTG+x7SF03zB40n6pR2mq+i7gqXeZBxV6VrIuYMQFVUk2VICe9hlsLs +MFzq5Y3/P9evKMAI8JoxLLVlmI29pLY6A6VCiAFfyjiflXGtFRGNfHyo6FTMPzoL +fGb0R/jAli47CVhvI7JyNqGMb6Oa4jqoVw5+RMmrgkaI5RhOplcTnqnxuEBqvxpk +utnLNFTZ4LLRjYyaGYiYybZF9NG/OkCbTzT4fwLxqHqa4HCzijnbdAZbLtGC2aL/ +SXMqHf1EHZmii9NZ/ndseom0l2+eVMaR8auZsSrpSbgzBB+UssVcBTD79Qb8LBQy +C6WXGJPCEOfOYsxdZMDbD7q9CqgT5P4kI8VfryB5iqaLfDtUwjT8GPoTybFiWHMk +0DiS1quLYFZK2QhyFY2D1VLweyTQl8Hb/yYbxmd9QZDpDGCaIRkDt5H+rX17+MG2 +n3yPHeLbGBLg9jphH7MMmsn57Z9fYjJADOOLFKG+W6txAQV3 +-----END CERTIFICATE-----"""] + +X509_IMDS = '\n'.join(X509_IMDS_LIST) + +PKCS7_PEM = """This line of spam should be ignored, as should the next line. + +-----BEGIN PKCS7----- +MIILZwYJKoZIhvcNAQcCoIILWDCCC1QCAQExADALBgkqhkiG9w0BBwGgggs6MIIF +cjCCA1qgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwbTELMAkGA1UEBhMCVVMxEzAR +BgNVBAgMCldhc2hpbmd0b24xEDAOBgNVBAcMB1NlYXR0bGUxDDAKBgNVBAoMA0lC +TTEpMCcGA1UEAwwgbWFzdGVyLWNhLXRlc3RAc2JhbHVrb2ZmLmlibS5jb20wHhcN +MTYwOTI3MDgxODMzWhcNMjYwOTI1MDgxODMzWjAjMSEwHwYDVQQDDBhjYS1pbnRA +c2JhbHVrb2ZmLmlibS5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC +AQC8KiZi1t9vX8IBtiOgOXHwyTo4ljYOqz92BqyIj4r/YJNHxeJ7JHNiM29eu6K/ +LdJleOkDXV+m0LDchn6pMf1/VJ5/Zpr7eWTqyKSaP5zTVbIOI1It60MQpsSiGbcf +VPJ7TrwzNKtyBXREHwC+mEvIuEwDRHd8DljJG5J2CpS3Re/CgxR8DrKXnT6ZXHik +og3yYJ7vULtxz8ktgOjM7uFh27YmHU4o1WyeAmMwpesVkqY7E7frbIYYbQo5B1D1 +eWqM3KldqOQqvq34kPkf0vdfXxPurysNJrEaOXzDRdI6GiXaCKtnSEF0urCRdenI +DWMeqq2w5H1ekNgpK3XeFFxQYkhvXN85HiRmPO9wrd4qdEs1nvTIVVEDpB5FFe4a +FQmsAe57Ll1DTKZcja30VD1uJ5PbeoysHhN270+IbFeXK3x/icS5F1QdfE/pYIA0 +L3JRSY88IXw4giHnlLnYb55NwLac3EXmr2Qks/T87/gbk5gk9G+0XK3FSRxF+MDd +mRiLAKLSb4Ej3wX1dXnSgelx5cBZ0n+NBY/865oauui27/OIaL7ZaDCDZU/tjIJD +y/uQkuAjH4UVF4m5PqRaykqrjbyRJeADbL2E7CxamOsgyAfzhgIt04hpKkvZoCCT +RREeNp9rRITQiGMsfCUuxackDajsW/pnFD7m1ZKej8fcdQIDAQABo2YwZDAdBgNV +HQ4EFgQUN1MP5SS5ZJyrWuPVSkEFKK2SnXQwHwYDVR0jBBgwFoAUhmmo2HE37bnk +y9h7IQ5phCFGU18wEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYw +DQYJKoZIhvcNAQELBQADggIBABGdyLUs9Pm9SXpSQLSY4BWnYdZvUoS5rhJ7coVr +kTm3bBmI6haSWyoSy/l0Qno+7rXz1YruKYqKaYTAakuDL8C/J2VuL/p29nyClH0r +DKNuz+6jU8xLVmcZwBtsKK1QxNgAdQ3DWygtmXzZ/tigfIVVIyaTkOvOjovuIxTW +AbmJu/xbkq4zZEaYJ0xobK8LGq3ZDauCsPNykqH/jwLwYWHQbKPmppTkNff7unXr +xQ+eSH/a1aCCldZI/NZywjZpNUdylEcnZhWshiWChD6j+CgrirdO0JeH9sGR0La7 +1VqujFWvVJUYYSbb7l4KFBLFw8Od5Z5rpYXm/qTHd6OvyS3qajse8ardqN0g2Hun +u0AtJ99JBHxzTP6blAcuTTrwS2XjB83/7k5YfN0jGbqQOYCJMTZ3pk3JkrZipxhj +Y1ZX1N8Opb7IwgjIXwzNy/joL7smUNBQlTPDN1IfM5b83NGNSDKaS1pWiqaLXO6e +rkwabZxCVfGgvIk9hE4x6+Cu+jdOLTpAwq1mcQroAp1+CInHrZeHdnhz0zR84CUm +ddOos2WYTF+OvRfel32rBCaKlH6Ssij0JGxSYT24WXygsCdpDXfimg3O4Fk2sJlV +015O7iIu22bowsDcF9RfvkdHNULrClWI12sRspXF9VmRjbDyG4eASBiulJQVbk9D +26vPMIIFwDCCA6igAwIBAgIJAJLWg/Z3x5xpMA0GCSqGSIb3DQEBCwUAMG0xCzAJ +BgNVBAYTAlVTMRMwEQYDVQQIDApXYXNoaW5ndG9uMRAwDgYDVQQHDAdTZWF0dGxl +MQwwCgYDVQQKDANJQk0xKTAnBgNVBAMMIG1hc3Rlci1jYS10ZXN0QHNiYWx1a29m +Zi5pYm0uY29tMB4XDTE2MDkyNzA4MDU1M1oXDTI2MDkyNTA4MDU1M1owbTELMAkG +A1UEBhMCVVMxEzARBgNVBAgMCldhc2hpbmd0b24xEDAOBgNVBAcMB1NlYXR0bGUx +DDAKBgNVBAoMA0lCTTEpMCcGA1UEAwwgbWFzdGVyLWNhLXRlc3RAc2JhbHVrb2Zm +LmlibS5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDdYtZguUzR +pohJ8GI2/KCXZxd6FzmZtqrKz1JhZxSV56WhYnYljzQgRsPX8lxUWC/nSm13cjit +fKG/TvDNTs6bb9t7VkYM0k0ewvArcNRSSR/YHO0r7fWv7XkwTvt3yFupWkeNBaqD +ChaZvlblcQxNUgXI3r/dOJDhOlfzhF0LML5FIIHgkgQCHAUQ62OfLkmXqNPYAKa9 +K/TE4UGtG9LYT0dy3AwKUpvXfnKJSEgrRd8Nul7Rp6BgYWoJg6pZD4GLFiqT2bxp +hJJaAYulgtF1jDHeZgyunm7WrwZvxPC8AIcFcksRMxB5XOEo8PBXaGHxbIjl+PCw +6WpF5g7ZO95keYonpQ8nK9Vcn7BgWcQUY5SuZCaMTk79Hs/kD1upc22IHg//t1qy ++0i2SNTxj7n7mkynBHoKSrlVviUkyZHQYniuAGciYYKTfRy0F1LaM3QOUF3XA9j+ +2g1jCWolMPWpzWFTOkBwoCmCs0MX7FaYvsAeLx4rDVLRQWzvKZKGTubDBWS8wBsA +q0hDv4b3r4k6cIz9a4PYNFARsnShkKHwln9lM5HjPHUNSZ6oaaIdi4wEf0xwipMi +Ei+xh3Ukztq6pBGlNbdxdlBP3PVap0AI81alswLWqCL5yBHzv0NQp+x7/EODJDcv +E6sKPRmBVTzO9Y20KMlHrcdlPiNbBDhJ+QIDAQABo2MwYTAdBgNVHQ4EFgQUhmmo +2HE37bnky9h7IQ5phCFGU18wHwYDVR0jBBgwFoAUhmmo2HE37bnky9h7IQ5phCFG +U18wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQEL +BQADggIBAAbqGW0/XCmvptoh/6oHH8OvMUbn4m8Y9DqEnlWkfadhYkYaOh5SAZ51 +mD7Ouwi+he8gV0evMgmW9liDw/i/2IWfHLT56yHSQqj8kRYRu8ZEdfC8IQg7Pj8m +CJruJKtWWfCHWnoqxPmCkYfWrb2wb5K3Me33zMdvtdMg3Hzxg9UoRFvqyRJtYj7c +oK8A3uWiX6vjDZTG+x7SF03zB40n6pR2mq+i7gqXeZBxV6VrIuYMQFVUk2VICe9h +lsLsMFzq5Y3/P9evKMAI8JoxLLVlmI29pLY6A6VCiAFfyjiflXGtFRGNfHyo6FTM +PzoLfGb0R/jAli47CVhvI7JyNqGMb6Oa4jqoVw5+RMmrgkaI5RhOplcTnqnxuEBq +vxpkutnLNFTZ4LLRjYyaGYiYybZF9NG/OkCbTzT4fwLxqHqa4HCzijnbdAZbLtGC +2aL/SXMqHf1EHZmii9NZ/ndseom0l2+eVMaR8auZsSrpSbgzBB+UssVcBTD79Qb8 +LBQyC6WXGJPCEOfOYsxdZMDbD7q9CqgT5P4kI8VfryB5iqaLfDtUwjT8GPoTybFi +WHMk0DiS1quLYFZK2QhyFY2D1VLweyTQl8Hb/yYbxmd9QZDpDGCaIRkDt5H+rX17 ++MG2n3yPHeLbGBLg9jphH7MMmsn57Z9fYjJADOOLFKG+W6txAQV3oQAxAA== +-----END PKCS7----- +More spam here, too. Should be ignored.""" + +# Needed because we want PKCS7_DER to be raw bytes, not base64 encoded +if six.PY2: + def b64decode(thing): + return base64.decodestring(thing) +elif six.PY3: + def b64decode(thing): + return base64.decodebytes(bytes(thing, encoding='UTF-8')) + +PKCS7_DER = b64decode( + 'MIILZwYJKoZIhvcNAQcCoIILWDCCC1QCAQExADALBgkqhkiG9w0BBwGgggs6MIIF' + + 'cjCCA1qgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwbTELMAkGA1UEBhMCVVMxEzAR' + + 'BgNVBAgMCldhc2hpbmd0b24xEDAOBgNVBAcMB1NlYXR0bGUxDDAKBgNVBAoMA0lC' + + 'TTEpMCcGA1UEAwwgbWFzdGVyLWNhLXRlc3RAc2JhbHVrb2ZmLmlibS5jb20wHhcN' + + 'MTYwOTI3MDgxODMzWhcNMjYwOTI1MDgxODMzWjAjMSEwHwYDVQQDDBhjYS1pbnRA' + + 'c2JhbHVrb2ZmLmlibS5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC' + + 'AQC8KiZi1t9vX8IBtiOgOXHwyTo4ljYOqz92BqyIj4r/YJNHxeJ7JHNiM29eu6K/' + + 'LdJleOkDXV+m0LDchn6pMf1/VJ5/Zpr7eWTqyKSaP5zTVbIOI1It60MQpsSiGbcf' + + 'VPJ7TrwzNKtyBXREHwC+mEvIuEwDRHd8DljJG5J2CpS3Re/CgxR8DrKXnT6ZXHik' + + 'og3yYJ7vULtxz8ktgOjM7uFh27YmHU4o1WyeAmMwpesVkqY7E7frbIYYbQo5B1D1' + + 'eWqM3KldqOQqvq34kPkf0vdfXxPurysNJrEaOXzDRdI6GiXaCKtnSEF0urCRdenI' + + 'DWMeqq2w5H1ekNgpK3XeFFxQYkhvXN85HiRmPO9wrd4qdEs1nvTIVVEDpB5FFe4a' + + 'FQmsAe57Ll1DTKZcja30VD1uJ5PbeoysHhN270+IbFeXK3x/icS5F1QdfE/pYIA0' + + 'L3JRSY88IXw4giHnlLnYb55NwLac3EXmr2Qks/T87/gbk5gk9G+0XK3FSRxF+MDd' + + 'mRiLAKLSb4Ej3wX1dXnSgelx5cBZ0n+NBY/865oauui27/OIaL7ZaDCDZU/tjIJD' + + 'y/uQkuAjH4UVF4m5PqRaykqrjbyRJeADbL2E7CxamOsgyAfzhgIt04hpKkvZoCCT' + + 'RREeNp9rRITQiGMsfCUuxackDajsW/pnFD7m1ZKej8fcdQIDAQABo2YwZDAdBgNV' + + 'HQ4EFgQUN1MP5SS5ZJyrWuPVSkEFKK2SnXQwHwYDVR0jBBgwFoAUhmmo2HE37bnk' + + 'y9h7IQ5phCFGU18wEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYw' + + 'DQYJKoZIhvcNAQELBQADggIBABGdyLUs9Pm9SXpSQLSY4BWnYdZvUoS5rhJ7coVr' + + 'kTm3bBmI6haSWyoSy/l0Qno+7rXz1YruKYqKaYTAakuDL8C/J2VuL/p29nyClH0r' + + 'DKNuz+6jU8xLVmcZwBtsKK1QxNgAdQ3DWygtmXzZ/tigfIVVIyaTkOvOjovuIxTW' + + 'AbmJu/xbkq4zZEaYJ0xobK8LGq3ZDauCsPNykqH/jwLwYWHQbKPmppTkNff7unXr' + + 'xQ+eSH/a1aCCldZI/NZywjZpNUdylEcnZhWshiWChD6j+CgrirdO0JeH9sGR0La7' + + '1VqujFWvVJUYYSbb7l4KFBLFw8Od5Z5rpYXm/qTHd6OvyS3qajse8ardqN0g2Hun' + + 'u0AtJ99JBHxzTP6blAcuTTrwS2XjB83/7k5YfN0jGbqQOYCJMTZ3pk3JkrZipxhj' + + 'Y1ZX1N8Opb7IwgjIXwzNy/joL7smUNBQlTPDN1IfM5b83NGNSDKaS1pWiqaLXO6e' + + 'rkwabZxCVfGgvIk9hE4x6+Cu+jdOLTpAwq1mcQroAp1+CInHrZeHdnhz0zR84CUm' + + 'ddOos2WYTF+OvRfel32rBCaKlH6Ssij0JGxSYT24WXygsCdpDXfimg3O4Fk2sJlV' + + '015O7iIu22bowsDcF9RfvkdHNULrClWI12sRspXF9VmRjbDyG4eASBiulJQVbk9D' + + '26vPMIIFwDCCA6igAwIBAgIJAJLWg/Z3x5xpMA0GCSqGSIb3DQEBCwUAMG0xCzAJ' + + 'BgNVBAYTAlVTMRMwEQYDVQQIDApXYXNoaW5ndG9uMRAwDgYDVQQHDAdTZWF0dGxl' + + 'MQwwCgYDVQQKDANJQk0xKTAnBgNVBAMMIG1hc3Rlci1jYS10ZXN0QHNiYWx1a29m' + + 'Zi5pYm0uY29tMB4XDTE2MDkyNzA4MDU1M1oXDTI2MDkyNTA4MDU1M1owbTELMAkG' + + 'A1UEBhMCVVMxEzARBgNVBAgMCldhc2hpbmd0b24xEDAOBgNVBAcMB1NlYXR0bGUx' + + 'DDAKBgNVBAoMA0lCTTEpMCcGA1UEAwwgbWFzdGVyLWNhLXRlc3RAc2JhbHVrb2Zm' + + 'LmlibS5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDdYtZguUzR' + + 'pohJ8GI2/KCXZxd6FzmZtqrKz1JhZxSV56WhYnYljzQgRsPX8lxUWC/nSm13cjit' + + 'fKG/TvDNTs6bb9t7VkYM0k0ewvArcNRSSR/YHO0r7fWv7XkwTvt3yFupWkeNBaqD' + + 'ChaZvlblcQxNUgXI3r/dOJDhOlfzhF0LML5FIIHgkgQCHAUQ62OfLkmXqNPYAKa9' + + 'K/TE4UGtG9LYT0dy3AwKUpvXfnKJSEgrRd8Nul7Rp6BgYWoJg6pZD4GLFiqT2bxp' + + 'hJJaAYulgtF1jDHeZgyunm7WrwZvxPC8AIcFcksRMxB5XOEo8PBXaGHxbIjl+PCw' + + '6WpF5g7ZO95keYonpQ8nK9Vcn7BgWcQUY5SuZCaMTk79Hs/kD1upc22IHg//t1qy' + + '+0i2SNTxj7n7mkynBHoKSrlVviUkyZHQYniuAGciYYKTfRy0F1LaM3QOUF3XA9j+' + + '2g1jCWolMPWpzWFTOkBwoCmCs0MX7FaYvsAeLx4rDVLRQWzvKZKGTubDBWS8wBsA' + + 'q0hDv4b3r4k6cIz9a4PYNFARsnShkKHwln9lM5HjPHUNSZ6oaaIdi4wEf0xwipMi' + + 'Ei+xh3Ukztq6pBGlNbdxdlBP3PVap0AI81alswLWqCL5yBHzv0NQp+x7/EODJDcv' + + 'E6sKPRmBVTzO9Y20KMlHrcdlPiNbBDhJ+QIDAQABo2MwYTAdBgNVHQ4EFgQUhmmo' + + '2HE37bnky9h7IQ5phCFGU18wHwYDVR0jBBgwFoAUhmmo2HE37bnky9h7IQ5phCFG' + + 'U18wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQEL' + + 'BQADggIBAAbqGW0/XCmvptoh/6oHH8OvMUbn4m8Y9DqEnlWkfadhYkYaOh5SAZ51' + + 'mD7Ouwi+he8gV0evMgmW9liDw/i/2IWfHLT56yHSQqj8kRYRu8ZEdfC8IQg7Pj8m' + + 'CJruJKtWWfCHWnoqxPmCkYfWrb2wb5K3Me33zMdvtdMg3Hzxg9UoRFvqyRJtYj7c' + + 'oK8A3uWiX6vjDZTG+x7SF03zB40n6pR2mq+i7gqXeZBxV6VrIuYMQFVUk2VICe9h' + + 'lsLsMFzq5Y3/P9evKMAI8JoxLLVlmI29pLY6A6VCiAFfyjiflXGtFRGNfHyo6FTM' + + 'PzoLfGb0R/jAli47CVhvI7JyNqGMb6Oa4jqoVw5+RMmrgkaI5RhOplcTnqnxuEBq' + + 'vxpkutnLNFTZ4LLRjYyaGYiYybZF9NG/OkCbTzT4fwLxqHqa4HCzijnbdAZbLtGC' + + '2aL/SXMqHf1EHZmii9NZ/ndseom0l2+eVMaR8auZsSrpSbgzBB+UssVcBTD79Qb8' + + 'LBQyC6WXGJPCEOfOYsxdZMDbD7q9CqgT5P4kI8VfryB5iqaLfDtUwjT8GPoTybFi' + + 'WHMk0DiS1quLYFZK2QhyFY2D1VLweyTQl8Hb/yYbxmd9QZDpDGCaIRkDt5H+rX17' + + '+MG2n3yPHeLbGBLg9jphH7MMmsn57Z9fYjJADOOLFKG+W6txAQV3oQAxAA==') + +# Keys for the above CA certs, logged here to make it simple to sign other +# certs for testing purposes in the future. + +INTERMEDIATE_KEY = """-----BEGIN RSA PRIVATE KEY----- +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 +-----END RSA PRIVATE KEY-----""" + +CA_KEY = """-----BEGIN RSA PRIVATE KEY----- +-----BEGIN RSA PRIVATE KEY----- +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 +-----END RSA PRIVATE KEY-----""" + +# An expired self-signed cert for testing. +X509_EXPIRED_CN = 'www.example.com' + +X509_EXPIRED = """-----BEGIN CERTIFICATE----- +MIIDfzCCAmegAwIBAgIJAKByYqy5fAfLMA0GCSqGSIb3DQEBCwUAMFYxCzAJBgNV +BAYTAlVTMQ0wCwYDVQQIDAREZWFkMRAwDgYDVQQHDAdUb3RhbGx5MQwwCgYDVQQK +DANJQk0xGDAWBgNVBAMMD3d3dy5leGFtcGxlLmNvbTAeFw0xNjA5MjQxODAxNTRa +Fw0xNjA5MjUxODAxNTRaMFYxCzAJBgNVBAYTAlVTMQ0wCwYDVQQIDAREZWFkMRAw +DgYDVQQHDAdUb3RhbGx5MQwwCgYDVQQKDANJQk0xGDAWBgNVBAMMD3d3dy5leGFt +cGxlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKZXgoeI7tzM +KLUs0Fp9qEnILZTH2Xj5Q/j0KTkLD5A3VLROJof1lMb8voAkF16jnCC+A4RuphEO +QtEUAUwlbvYv0rrSEWKYtkGKpEAg7mH05/BiiLSuveIQido6u4659FJ3bgYNE/P0 +xb8vMuxy4M7JH1OF8XReI05UfLqGr5isjri/IS4ofZy97aMciDdqeAs+yDg6lCpk +e0UcPLmJw5tIMg30Pl0AsxkD9U5JejAHEOvYgNgCyk9lo8uf/S41pzmU4Wc9TmL0 +WDunicpqngmajV+V45VN6t4NDHo093kyZ/4gJcqRfsNQ2DQRyFzd8Yjllz36dO9B +HT2NhI9yKhECAwEAAaNQME4wHQYDVR0OBBYEFBRND67rjYxqeUFH3p9+vSoQS1Qe +MB8GA1UdIwQYMBaAFBRND67rjYxqeUFH3p9+vSoQS1QeMAwGA1UdEwQFMAMBAf8w +DQYJKoZIhvcNAQELBQADggEBAFOcwM8mlTsP+sw4yhxcPD72qiIn4DRI++17Yoeu +eVJWO5ZlelOaBVdMFV573/7LR0j4y22RNPiFRCj+oG+w/kQLVRBnxj/LhZj3T+sQ +DIlahXIWCroPqVXEbRejxOLugNLS7SoWp9pKqWXPawkyHIS0Ht7LyZQYm9Pt7PKc +uerOX3Qzt+W2nmgxA3mHhL76tCRqDATdn5guLH1F0g29WB614oI43kSt4WW0i4JT +S+aDmoFsO3i/E+x+qm5H0swjU9dLCvdMjo0VUpk5f1aJJ10xpeKTUYOB55haalJI +j+/EXRZyEna+vPrS8mCl0GMvlFm0ZWFdWaWPR7l3J/J4is0= +-----END CERTIFICATE-----""" + +X509_EXPIRED_KEY = """-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEApleCh4ju3MwotSzQWn2oScgtlMfZePlD+PQpOQsPkDdUtE4m +h/WUxvy+gCQXXqOcIL4DhG6mEQ5C0RQBTCVu9i/SutIRYpi2QYqkQCDuYfTn8GKI +tK694hCJ2jq7jrn0UnduBg0T8/TFvy8y7HLgzskfU4XxdF4jTlR8uoavmKyOuL8h +Lih9nL3toxyIN2p4Cz7IODqUKmR7RRw8uYnDm0gyDfQ+XQCzGQP1Tkl6MAcQ69iA +2ALKT2Wjy5/9LjWnOZThZz1OYvRYO6eJymqeCZqNX5XjlU3q3g0MejT3eTJn/iAl +ypF+w1DYNBHIXN3xiOWXPfp070EdPY2Ej3IqEQIDAQABAoIBAD4c/0jNASTBt5Gv +oj2oHmcusJaV6ccajR8xTRNX5f/cKW0KoaizM1L6ncgLsg5M2cgALCAPkUNdJ+Ya +qkFc2Qpk4TORrZw7mhLvSlYH9fvuD43bvWB6v7zioBc1R0QMfAcvQY5Q49p81DqH +zWQtoXSV9XSi1360iEp/kfO0x20ip9rP7qDOKuN5gdvRa8sXKD+jnmp17e1rx+fS +U0UoReBUbn4iLbOdEVyH9HSqTB+p5nPq63KJBioJZMGhLNntKMAff8uMiVhhb7Io +vIIHgoIfFce9YwC4fn+0UDrBCAx+SAyw2cmmMyXIqhd3c2Ca7zFmezSuC3H5Y4si +535VO2ECgYEA2/7I8QOkrRx+Bd2sxe6n+jeA6yRVqBb+bE6rZUUQUlSAFqoM8RKJ +K8cRjePmtkd9UkGrfDN6XTyqKD5Vt1Cd7FNl5Q08C/WP5VUOaKgdq3MkeOoJT8xf +c0LWAoRw5InP7n6TRASExekagQEIMMOHZFtwSjz+HauLqohrk6CaBRcCgYEAwZDK +J0mYspt8Wwlwrv0ouQG6tCy0NkWCdNs4EbT12qH6FKsdUuvJku+Zr1amCq/8apTn +pdn2YlRDp5+jqsKf0dui5M2zC088XJov3VF1Ujm4BtSVwRRhi7BxM9BCv1txUs20 +e2XPKV7RKexOL6iWPWDIcB6ZFhJdxQI5mOF9ExcCgYEAmLHPZvnQYxdToV6rfPaZ +QOMlaBBgI7tR/HreG/xDx+E+xnxhXzIuY2RYmtOEXyBfq6hJDnvsgqqIsEYT2Jjs +BAwevUziUKqwpczTo3CMp2PT/Nj0fZ6s4aOSR00FzpqY6ECSlrNMNNIGw2Oj+7S7 +VLziw6Rx/MYEuujVQjJGtSECgYAXlwC8BwEgC0j9g0oDWYEEAwzw9l3EG3aJrUnJ +BqfLzF/A8xWwzGGVkbPGJaY4uTfm+Vy93rFjTGeuXwtAPVXi6oSQo+0FHNP7aSMa +Mto8eiJOWswhas10i71QFjp8PbWy5LTxMPgtT4voMw9YSZB9zHTBDUmU4gohf2Lr +mdd3YwKBgHu4IlMxt40w+Bn5xasvACB5iaO5EBKO7rp0ba0Po3t9SG9iPSr8Yruq +Qv1cDRGlM5jHboqSM2ju2/b/Wc2ezdjoktrwgG+ElQuptwwNIsFrooHMLMY3B53k +Je8uvLnAPRLL95ZhclaSw2vAxmaiGIsm7WGhjnRQ2Vntgd6fNgY9 +-----END RSA PRIVATE KEY-----""" + +# Other certificates and keys used in tests. +ALT_EXT_CRT = """-----BEGIN CERTIFICATE----- +MIIGqjCCBZKgAwIBAgIJAIApBg8slSSiMA0GCSqGSIb3DQEBBQUAMIGLMQswCQYD +VQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxFDASBgNVBAcMC1NhbiBBbnRvbmlvMR4w +HAYDVQQKDBVPcGVuU3RhY2sgRXhwZXJpbWVudHMxFjAUBgNVBAsMDU5ldXRyb24g +TGJhYXMxHjAcBgNVBAMMFXd3dy5DTkZyb21TdWJqZWN0Lm9yZzAeFw0xNTA1MjEy +MDMzMjNaFw0yNTA1MTgyMDMzMjNaMIGLMQswCQYDVQQGEwJVUzEOMAwGA1UECAwF +VGV4YXMxFDASBgNVBAcMC1NhbiBBbnRvbmlvMR4wHAYDVQQKDBVPcGVuU3RhY2sg +RXhwZXJpbWVudHMxFjAUBgNVBAsMDU5ldXRyb24gTGJhYXMxHjAcBgNVBAMMFXd3 +dy5DTkZyb21TdWJqZWN0Lm9yZzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBALL1nmbDPUDps84i1sM3rhHrc+Dlu0N/wKQWKZFeiWUtF/pot19V3o0yXDps +g7W5RkLMTFkZEcnQpyGdpAGjTjzmNXMZw99EzxsmrR3l6hUEISifVbvEuftYZT6j +PxM5ML6WAjFNaBEZPWtZi8CgX5xdjdrDNndwyHob49n7Nc/h1kVqqBqMILabTqC6 +yEcxS/B+DugVuuYbEdYYYElQUMfM+mUdULrSqIVl2n5AvvSFjWzWzfgPyp4QKn+f +7HVRT62bh/XjQ88n1tMYNAEqixRZTPgqY1LFl9VJVgRp9fdL6ttMurOR3C0STJ5q +CdKBL7LrpbY4u8dEragRC6YAyI8CAwEAAaOCAw0wggMJMAkGA1UdEwQCMAAwCwYD +VR0PBAQDAgXgMIIC7QYDVR0RBIIC5DCCAuCCGHd3dy5ob3N0RnJvbUROU05hbWUx +LmNvbYIYd3d3Lmhvc3RGcm9tRE5TTmFtZTIuY29tghh3d3cuaG9zdEZyb21ETlNO +YW1lMy5jb22CGHd3dy5ob3N0RnJvbUROU05hbWU0LmNvbYcECgECA4cQASNFZ4mr +ze/3s9WR5qLEgIYWaHR0cDovL3d3dy5leGFtcGxlLmNvbaSBjzCBjDELMAkGA1UE +BhMCVVMxDjAMBgNVBAgMBVRleGFzMRQwEgYDVQQHDAtTYW4gQW50b25pbzEeMBwG +A1UECgwVT3BlblN0YWNrIEV4cGVyaW1lbnRzMRYwFAYDVQQLDA1OZXV0cm9uIExi +YWFzMR8wHQYDVQQDDBZ3d3cuY25Gcm9tQWx0TmFtZTEub3JnpIGPMIGMMQswCQYD +VQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxFDASBgNVBAcMC1NhbiBBbnRvbmlvMR4w +HAYDVQQKDBVPcGVuU3RhY2sgRXhwZXJpbWVudHMxFjAUBgNVBAsMDU5ldXRyb24g +TGJhYXMxHzAdBgNVBAMMFnd3dy5jbkZyb21BbHROYW1lMi5vcmekgY8wgYwxCzAJ +BgNVBAYTAlVTMQ4wDAYDVQQIDAVUZXhhczEUMBIGA1UEBwwLU2FuIEFudG9uaW8x +HjAcBgNVBAoMFU9wZW5TdGFjayBFeHBlcmltZW50czEWMBQGA1UECwwNTmV1dHJv +biBMYmFhczEfMB0GA1UEAwwWd3d3LmNuRnJvbUFsdE5hbWUzLm9yZ6SBjzCBjDEL +MAkGA1UEBhMCVVMxDjAMBgNVBAgMBVRleGFzMRQwEgYDVQQHDAtTYW4gQW50b25p +bzEeMBwGA1UECgwVT3BlblN0YWNrIEV4cGVyaW1lbnRzMRYwFAYDVQQLDA1OZXV0 +cm9uIExiYWFzMR8wHQYDVQQDDBZ3d3cuY25Gcm9tQWx0TmFtZTQub3JnMA0GCSqG +SIb3DQEBBQUAA4IBAQCS6iDn6R3C+qJLZibaqrBSkM9yu5kwRsQ6lQ+DODvVYGWq +eGkkh5o2c6WbJlH44yF280+HvnJcuISD7epPHJN0vUM9+WMtXfEli9avFHgu2JxP +3P0ixK2kaJnqKQkSEdnA/v/eWP1Cd2v6rbKCIo9d2gSP0cnpdtlX9Zk3SzEh0V7s +RjSdfZoAvz0aAnpDHlTerLcz5T2aiRae2wSt/RLA3qDO1Ji05tWvQBmKuepxS6A1 +tL4Drm+OCXJwTrE7ClTMCwcrZnLl4tI+Z+X3DV92WQB8ldST/QFjz1hgs/4zrADA +elu2c/X7MR4ObOjhDfaVGQ8kMhYf5hx69qyNDsGi +-----END CERTIFICATE-----""" + +ALT_EXT_CRT_KEY = """ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAsvWeZsM9QOmzziLWwzeuEetz4OW7Q3/ApBYpkV6JZS0X+mi3 +X1XejTJcOmyDtblGQsxMWRkRydCnIZ2kAaNOPOY1cxnD30TPGyatHeXqFQQhKJ9V +u8S5+1hlPqM/EzkwvpYCMU1oERk9a1mLwKBfnF2N2sM2d3DIehvj2fs1z+HWRWqo +GowgtptOoLrIRzFL8H4O6BW65hsR1hhgSVBQx8z6ZR1QutKohWXafkC+9IWNbNbN ++A/KnhAqf5/sdVFPrZuH9eNDzyfW0xg0ASqLFFlM+CpjUsWX1UlWBGn190vq20y6 +s5HcLRJMnmoJ0oEvsuultji7x0StqBELpgDIjwIDAQABAoIBAC3DX6FZtfU+jgtd +n1vGhk3wzu4o8S0+ow2S2UhiS3JDCMmxM4s+ky26Phl2nGvBGDWGttNl9MWOBN80 +x7bfgudR20M2yH70wp1n04c8vxJmvu/7ZtogYYrjvOg6qKuKyWtDQwZGjCErOiiU +eodku25qAhd6Khh7D9kh/q9EbSteYFXsqJiNrY4ul1+cROMZpHx63xY6AzPmkvSU +garkgY4rw9E71t7it2laWkRKVsd+kEjayritdEEliNMVFFtrGEgplYkmLxGf0HLi +ROFVMCLRW/P12JpXllFPrBb8rlPL4w1c/s+yStohT0K+o4FLXhsf/inxmfc9XnZX +dJm0k/ECgYEA47FpV1caMk+TNPfu318VCGRmjwpXdmkNaUiX2Uvs3xIKQ6KJmpo3 +sj0YjQEmQVz8s6geStvU1LdPxgsWZfbDt31M6SNwylh82ABQF1bZyrcMRxM8bHhe +bhDITM1dAn6aROkS1cBpfR9NJOFD850lmJvBGR9ORVBGyucTKH5uXxkCgYEAyTU0 +zQKW2aU3J7mTCC9cp+eSD3fubJpa3ML5XfQ8YNID4PsxWglNKPcOTC4yaSfxVmyk +S0WIQUazCstszQsvwy9YyHtpkMq+0lyCPvrYnmRV0zx5zT155V2zcEh/oj64eoee +W5kvJSs/x6vT+lEN0TDEJ2gKEaJuBt6JG6P04ecCgYBSNw1CbEEZSYJt7dhi74I4 +tYgSvjk2mFgvW/b4j2HIaksqgNYO7QCPa2AiCfg2Qc09UcceYKJI7Kfxaq97wc6J +wsSyqglgBvONSw+gXcvmVpIoV9nJkO0H8SdiFAUxkWVC3KXgaMmuVE8WsgBHRsb8 +g8EFwTgR7xqgyS8xv/U6gQKBgQCdUr/dSJgAx6EPq5degAHXu0ZGWAUR38MJ+F2Y +6/5FyhCEWoRlHP66+CmywTBjbnrSk5IG1PBL8ebOmu6QiJ2o5R1rbKvHLe/0dabV +bbfwaQ1+ZDvskZP9Fr3WHqnFh3shO2dDwcvOKTnuetj9UWEXXyUQltXAohubvWbB +OPqhowKBgB3t2oUSFJI8fSNQnQNkcespJTddr0oLEwgsIl4Q7rdFHLr+/c46svjJ +kPMtpfxDQvkgK2aWpS4OP0E2vSU/IfMEDmlypfKe2SaTtFehZSUwR4R1/ZhSL3iS +iMwJYgm98P27s4TEMdhlPNVJrj1FrD+4VrgpOsoM20EkZnTvel9s +-----END RSA PRIVATE KEY-----""" + +ENCRYPTED_PKCS8_CRT_KEY_PASSPHRASE = 'test_passphrase' + +ENCRYPTED_PKCS8_CRT_KEY = """-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIE6TAbBgkqhkiG9w0BBQMwDgQIT04zko6pmJICAggABIIEyL/79sqzTQ7BsEjY +ao2Uhh3//mpNJfCDhjSZOmWL7s4+161cEqpxrfxo4bHH8fkZ60VZUQP8CjwwQUhP +4iwpv2bYbQwzlttZwTC6s28wh7FRtgVoVPTwvXJa6fl2zAjLtsjwLZ/556ez9xIJ +67hxkIK2EzGQaeEKI1+vVF5EKsgKiPEmgspOBxRPoVWTx49NooiakGnwaBoDyTob +8FMr8mF1EheNQ4kl1bPrl+csD7PPnfbWUdNVvMljEhS3cYamQDPEWyAzvaIr0rHh +/6h80L/G2+0fensrTspWJcjX+XDBwQPk+YMic0TJ3KvkC7p2iNJhjNrjhQ+APZWq +xYrjfcmdK0RaaoqN+1zeE1P2kWIJx9CQZVMeGhVzzcmPwJPDnJFpkU+8cgTWnUr/ +Fh8YtDoDzLiAUcmV1Kk7LYtYPHuU8epuz5PYm49TbWzdS7PX5wqFAFmrVt5jysm4 +D/Ox0r4KV1t7D/1gc1WRIu8oUXkIglCHWNpTyMK0kFPctAf/ua+DUFRE4eSx3rsX +ZKIymdF9v/WF1Ud0tsNeudQbVeXWS6UCR8m/rqe81W4npQm/uqUNla+6yaYUmHlk +tvw/m6pt+jKhn0XIRkMwHrTpIaMVvInMg0xpkRuc7Xj5A7vNnkypZRNZJHgy7WWC +6GpOCWJOltYaNy7tmAkSUHJ6kNjXK5a4fi30HknEaqKjFTQNGvcybulJ3MXUzds0 +MJoTpvQfLzYQbMYZ/XRGND4lgeEbs29nWLPae8D5XlDeZQMin8EukPko8u8+YGbU +eWGOvDc+4/xrWrsq1i6R0uWq+Cyoql8oh0PNBlM04S7GAbu1pOD/tPcq/GNYcv/Q +vJcIz9KA3BNepq7tC8D88ggEvFjTsHKeW/OnuCxKducSna4Mq+GebU52tKjkLjFC +eLG4Vx0BY5xPH3gd7iyuAf7S+08BbinNZWjHLpdmR3vKK5YbLPiGSfcYQdClr6BK +9vNWH4TXmZMV+rWtfSeM/cbhCHwxT5Jx6N0OFAxOblQClWnUD79nGkEgn/GoY/Aj +FPNj8u2U/mJHgFHH3ClidYL9jJUvhGpTixB8nGgMjJ0wvFcp+5OysG3TsjqYkwR6 +RRNBmM+iLEUFTrMZYb+edHvGJsMEMZ0qvjmZDsfDz6ax5M9zH/ORFcGplgIec8kj +I106+dqAVVrv1CrBf2N/pxV0OXVhgl6ECe/Ee1xYC2e2CiEgUnQtedu8ekgPgp73 +tHcAiWMamLPTwXuL7jFtvWaQfkYBmrBdEx54+eZOfH/NgV3o8gbaWNHSxbfbwlXN +MvyJidZGkXU0DJtUUnO5i2S7ftKCdOzrrSA8HDTvxFUhxretYpF3NzPYpYkM7WJX +GM7bTMn37AWYqLZmdYYdjh1ZOH/wsM/3uxGBpyEyy4Urrr1ux7X1P0cL0O2P/72h +GRd499JLrRMrmmtQ4KrN7GCHdctvujhDP8zvmnaEyGVzg88XmDg50ZF3+8DmOOgX +EMZEYHO2Wi2uyFotFtZCuqoOJmGPPeGV8QrsRs82hnL1bcd6REUTWk0KsTt13lvF +WwMJugHFk5NQuse3P4Hh9smQrRrv1dvnpt7s4yKStKolXUaFWcXJvXVaDfR5266Y +p7cuYY1cAyI7gFfl5A== +-----END ENCRYPTED PRIVATE KEY-----""" + +UNENCRYPTED_PKCS8_CRT_KEY = """-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCy9Z5mwz1A6bPO +ItbDN64R63Pg5btDf8CkFimRXollLRf6aLdfVd6NMlw6bIO1uUZCzExZGRHJ0Kch +naQBo0485jVzGcPfRM8bJq0d5eoVBCEon1W7xLn7WGU+oz8TOTC+lgIxTWgRGT1r +WYvAoF+cXY3awzZ3cMh6G+PZ+zXP4dZFaqgajCC2m06gushHMUvwfg7oFbrmGxHW +GGBJUFDHzPplHVC60qiFZdp+QL70hY1s1s34D8qeECp/n+x1UU+tm4f140PPJ9bT +GDQBKosUWUz4KmNSxZfVSVYEafX3S+rbTLqzkdwtEkyeagnSgS+y66W2OLvHRK2o +EQumAMiPAgMBAAECggEALcNfoVm19T6OC12fW8aGTfDO7ijxLT6jDZLZSGJLckMI +ybEziz6TLbo+GXaca8EYNYa202X0xY4E3zTHtt+C51HbQzbIfvTCnWfThzy/Ema+ +7/tm2iBhiuO86Dqoq4rJa0NDBkaMISs6KJR6h2S7bmoCF3oqGHsP2SH+r0RtK15g +VeyomI2tji6XX5xE4xmkfHrfFjoDM+aS9JSBquSBjivD0TvW3uK3aVpaREpWx36Q +SNrKuK10QSWI0xUUW2sYSCmViSYvEZ/QcuJE4VUwItFb8/XYmleWUU+sFvyuU8vj +DVz+z7JK2iFPQr6jgUteGx/+KfGZ9z1edld0mbST8QKBgQDjsWlXVxoyT5M09+7f +XxUIZGaPCld2aQ1pSJfZS+zfEgpDoomamjeyPRiNASZBXPyzqB5K29TUt0/GCxZl +9sO3fUzpI3DKWHzYAFAXVtnKtwxHEzxseF5uEMhMzV0CfppE6RLVwGl9H00k4UPz +nSWYm8EZH05FUEbK5xMofm5fGQKBgQDJNTTNApbZpTcnuZMIL1yn55IPd+5smlrc +wvld9Dxg0gPg+zFaCU0o9w5MLjJpJ/FWbKRLRYhBRrMKy2zNCy/DL1jIe2mQyr7S +XII++tieZFXTPHnNPXnlXbNwSH+iPrh6h55bmS8lKz/Hq9P6UQ3RMMQnaAoRom4G +3okbo/Th5wKBgFI3DUJsQRlJgm3t2GLvgji1iBK+OTaYWC9b9viPYchqSyqA1g7t +AI9rYCIJ+DZBzT1Rxx5gokjsp/Fqr3vBzonCxLKqCWAG841LD6Bdy+ZWkihX2cmQ +7QfxJ2IUBTGRZULcpeBoya5UTxayAEdGxvyDwQXBOBHvGqDJLzG/9TqBAoGBAJ1S +v91ImADHoQ+rl16AAde7RkZYBRHfwwn4XZjr/kXKEIRahGUc/rr4KbLBMGNuetKT +kgbU8Evx5s6a7pCInajlHWtsq8ct7/R1ptVtt/BpDX5kO+yRk/0WvdYeqcWHeyE7 +Z0PBy84pOe562P1RYRdfJRCW1cCiG5u9ZsE4+qGjAoGAHe3ahRIUkjx9I1CdA2Rx +6yklN12vSgsTCCwiXhDut0Ucuv79zjqy+MmQ8y2l/ENC+SArZpalLg4/QTa9JT8h +8wQOaXKl8p7ZJpO0V6FlJTBHhHX9mFIveJKIzAliCb3w/buzhMQx2GU81UmuPUWs +P7hWuCk6ygzbQSRmdO96X2w= +-----END PRIVATE KEY-----""" + +EXPECTED_IMD_TEST_SUBJS = ["IMD3", "IMD2", "IMD1"] + +TEST_X509_IMDS = """Junk +-----BEGIN CERTIFICATE----- +MIIBhDCCAS6gAwIBAgIGAUo7hO/eMA0GCSqGSIb3DQEBCwUAMA8xDTALBgNVBAMT +BElNRDIwHhcNMTQxMjExMjI0MjU1WhcNMjUxMTIzMjI0MjU1WjAPMQ0wCwYDVQQD +EwRJTUQzMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKHIPXo2pfD5dpnpVDVz4n43 +zn3VYsjz/mgOZU0WIWjPA97mvulb7mwb4/LB4ijOMzHj9XfwP75GiOFxYFs8O80C +AwEAAaNwMG4wDwYDVR0TAQH/BAUwAwEB/zA8BgNVHSMENTAzgBS6rfnABCO3oHEz +NUUtov2hfXzfVaETpBEwDzENMAsGA1UEAxMESU1EMYIGAUo7hO/DMB0GA1UdDgQW +BBRiLW10LVJiFO/JOLsQFev0ToAcpzANBgkqhkiG9w0BAQsFAANBABtdF+89WuDi +TC0FqCocb7PWdTucaItD9Zn55G8KMd93eXrOE/FQDf1ScC+7j0jIHXjhnyu6k3NV +8el/x5gUHlc= +-----END CERTIFICATE----- +Junk should be ignored by x509 splitter +-----BEGIN CERTIFICATE----- +MIIBhDCCAS6gAwIBAgIGAUo7hO/DMA0GCSqGSIb3DQEBCwUAMA8xDTALBgNVBAMT +BElNRDEwHhcNMTQxMjExMjI0MjU1WhcNMjUxMTIzMjI0MjU1WjAPMQ0wCwYDVQQD +EwRJTUQyMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAJYHqnsisVKTlwVaCSa2wdrv +CeJJzqpEVV0RVgAAF6FXjX2Tioii+HkXMR9zFgpE1w4yD7iu9JDb8yTdNh+NxysC +AwEAAaNwMG4wDwYDVR0TAQH/BAUwAwEB/zA8BgNVHSMENTAzgBQt3KvN8ncGj4/s +if1+wdvIMCoiE6ETpBEwDzENMAsGA1UEAxMEcm9vdIIGAUo7hO+mMB0GA1UdDgQW +BBS6rfnABCO3oHEzNUUtov2hfXzfVTANBgkqhkiG9w0BAQsFAANBAIlJODvtmpok +eoRPOb81MFwPTTGaIqafebVWfBlR0lmW8IwLhsOUdsQqSzoeypS3SJUBpYT1Uu2v +zEDOmgdMsBY= +-----END CERTIFICATE----- +Junk should be thrown out like junk +-----BEGIN CERTIFICATE----- +MIIBfzCCASmgAwIBAgIGAUo7hO+mMA0GCSqGSIb3DQEBCwUAMA8xDTALBgNVBAMT +BHJvb3QwHhcNMTQxMjExMjI0MjU1WhcNMjUxMTIzMjI0MjU1WjAPMQ0wCwYDVQQD +EwRJTUQxMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAI+tSJxr60ogwXFmgqbLMW7K +3fkQnh9sZBi7Qo6AzUnfe/AhXoisib651fOxKXCbp57IgzLTv7O9ygq3I+5fQqsC +AwEAAaNrMGkwDwYDVR0TAQH/BAUwAwEB/zA3BgNVHSMEMDAugBR73ZKSpjbsz9tZ +URkvFwpIO7gB4KETpBEwDzENMAsGA1UEAxMEcm9vdIIBATAdBgNVHQ4EFgQULdyr +zfJ3Bo+P7In9fsHbyDAqIhMwDQYJKoZIhvcNAQELBQADQQBenkZ2k7RgZqgj+dxA +D7BF8MN1oUAOpyYqAjkGddSEuMyNmwtHKZI1dyQ0gBIQdiU9yAG2oTbUIK4msbBV +uJIQ +-----END CERTIFICATE-----""" diff --git a/octavia/tests/unit/common/sample_configs/sample_configs.py b/octavia/tests/unit/common/sample_configs/sample_configs.py index 06582a7009..a7fa443612 100644 --- a/octavia/tests/unit/common/sample_configs/sample_configs.py +++ b/octavia/tests/unit/common/sample_configs/sample_configs.py @@ -17,6 +17,7 @@ import collections from octavia.common import constants +from octavia.tests.unit.common.sample_configs import sample_certs def sample_amphora_tuple(): @@ -427,26 +428,26 @@ def sample_listener_tuple(proto=None, monitor=True, persistence=True, tls_certificate_id='cont_id_1' if tls else '', sni_container_ids=['cont_id_2', 'cont_id_3'] if sni else [], default_tls_container=sample_tls_container_tuple( - id='cont_id_1', certificate='--imapem1--\n', - private_key='--imakey1--\n', intermediates=[ - '--imainter1--\n', '--imainter1too--\n'], - primary_cn='aFakeCN' + id='cont_id_1', certificate=sample_certs.X509_CERT, + private_key=sample_certs.X509_CERT_KEY, + intermediates=sample_certs.X509_IMDS_LIST, + primary_cn=sample_certs.X509_CERT_CN ) if tls else '', sni_containers=[ sample_tls_sni_container_tuple( tls_container_id='cont_id_2', tls_container=sample_tls_container_tuple( - id='cont_id_2', certificate='--imapem2--\n', - private_key='--imakey2--\n', intermediates=[ - '--imainter2--\n', '--imainter2too--\n' - ], primary_cn='aFakeCN')), + id='cont_id_2', certificate=sample_certs.X509_CERT_2, + private_key=sample_certs.X509_CERT_KEY_2, + intermediates=sample_certs.X509_IMDS_LIST, + primary_cn=sample_certs.X509_CERT_CN_2)), sample_tls_sni_container_tuple( tls_container_id='cont_id_3', tls_container=sample_tls_container_tuple( - id='cont_id_3', certificate='--imapem3--\n', - private_key='--imakey3--\n', intermediates=[ - '--imainter3--\n', '--imainter3too--\n' - ], primary_cn='aFakeCN'))] + id='cont_id_3', certificate=sample_certs.X509_CERT_3, + private_key=sample_certs.X509_CERT_KEY_3, + intermediates=sample_certs.X509_IMDS_LIST, + primary_cn=sample_certs.X509_CERT_CN_3))] if sni else [], pools=pools, l7policies=l7policies, diff --git a/octavia/tests/unit/common/tls_utils/test_cert_parser.py b/octavia/tests/unit/common/tls_utils/test_cert_parser.py index bba872684d..69c076e6d9 100644 --- a/octavia/tests/unit/common/tls_utils/test_cert_parser.py +++ b/octavia/tests/unit/common/tls_utils/test_cert_parser.py @@ -21,203 +21,13 @@ from octavia.common import data_models import octavia.common.exceptions as exceptions import octavia.common.tls_utils.cert_parser as cert_parser from octavia.tests.unit import base +from octavia.tests.unit.common.sample_configs import sample_certs from octavia.tests.unit.common.sample_configs import sample_configs -ALT_EXT_CRT = """-----BEGIN CERTIFICATE----- -MIIGqjCCBZKgAwIBAgIJAIApBg8slSSiMA0GCSqGSIb3DQEBBQUAMIGLMQswCQYD -VQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxFDASBgNVBAcMC1NhbiBBbnRvbmlvMR4w -HAYDVQQKDBVPcGVuU3RhY2sgRXhwZXJpbWVudHMxFjAUBgNVBAsMDU5ldXRyb24g -TGJhYXMxHjAcBgNVBAMMFXd3dy5DTkZyb21TdWJqZWN0Lm9yZzAeFw0xNTA1MjEy -MDMzMjNaFw0yNTA1MTgyMDMzMjNaMIGLMQswCQYDVQQGEwJVUzEOMAwGA1UECAwF -VGV4YXMxFDASBgNVBAcMC1NhbiBBbnRvbmlvMR4wHAYDVQQKDBVPcGVuU3RhY2sg -RXhwZXJpbWVudHMxFjAUBgNVBAsMDU5ldXRyb24gTGJhYXMxHjAcBgNVBAMMFXd3 -dy5DTkZyb21TdWJqZWN0Lm9yZzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC -ggEBALL1nmbDPUDps84i1sM3rhHrc+Dlu0N/wKQWKZFeiWUtF/pot19V3o0yXDps -g7W5RkLMTFkZEcnQpyGdpAGjTjzmNXMZw99EzxsmrR3l6hUEISifVbvEuftYZT6j -PxM5ML6WAjFNaBEZPWtZi8CgX5xdjdrDNndwyHob49n7Nc/h1kVqqBqMILabTqC6 -yEcxS/B+DugVuuYbEdYYYElQUMfM+mUdULrSqIVl2n5AvvSFjWzWzfgPyp4QKn+f -7HVRT62bh/XjQ88n1tMYNAEqixRZTPgqY1LFl9VJVgRp9fdL6ttMurOR3C0STJ5q -CdKBL7LrpbY4u8dEragRC6YAyI8CAwEAAaOCAw0wggMJMAkGA1UdEwQCMAAwCwYD -VR0PBAQDAgXgMIIC7QYDVR0RBIIC5DCCAuCCGHd3dy5ob3N0RnJvbUROU05hbWUx -LmNvbYIYd3d3Lmhvc3RGcm9tRE5TTmFtZTIuY29tghh3d3cuaG9zdEZyb21ETlNO -YW1lMy5jb22CGHd3dy5ob3N0RnJvbUROU05hbWU0LmNvbYcECgECA4cQASNFZ4mr -ze/3s9WR5qLEgIYWaHR0cDovL3d3dy5leGFtcGxlLmNvbaSBjzCBjDELMAkGA1UE -BhMCVVMxDjAMBgNVBAgMBVRleGFzMRQwEgYDVQQHDAtTYW4gQW50b25pbzEeMBwG -A1UECgwVT3BlblN0YWNrIEV4cGVyaW1lbnRzMRYwFAYDVQQLDA1OZXV0cm9uIExi -YWFzMR8wHQYDVQQDDBZ3d3cuY25Gcm9tQWx0TmFtZTEub3JnpIGPMIGMMQswCQYD -VQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxFDASBgNVBAcMC1NhbiBBbnRvbmlvMR4w -HAYDVQQKDBVPcGVuU3RhY2sgRXhwZXJpbWVudHMxFjAUBgNVBAsMDU5ldXRyb24g -TGJhYXMxHzAdBgNVBAMMFnd3dy5jbkZyb21BbHROYW1lMi5vcmekgY8wgYwxCzAJ -BgNVBAYTAlVTMQ4wDAYDVQQIDAVUZXhhczEUMBIGA1UEBwwLU2FuIEFudG9uaW8x -HjAcBgNVBAoMFU9wZW5TdGFjayBFeHBlcmltZW50czEWMBQGA1UECwwNTmV1dHJv -biBMYmFhczEfMB0GA1UEAwwWd3d3LmNuRnJvbUFsdE5hbWUzLm9yZ6SBjzCBjDEL -MAkGA1UEBhMCVVMxDjAMBgNVBAgMBVRleGFzMRQwEgYDVQQHDAtTYW4gQW50b25p -bzEeMBwGA1UECgwVT3BlblN0YWNrIEV4cGVyaW1lbnRzMRYwFAYDVQQLDA1OZXV0 -cm9uIExiYWFzMR8wHQYDVQQDDBZ3d3cuY25Gcm9tQWx0TmFtZTQub3JnMA0GCSqG -SIb3DQEBBQUAA4IBAQCS6iDn6R3C+qJLZibaqrBSkM9yu5kwRsQ6lQ+DODvVYGWq -eGkkh5o2c6WbJlH44yF280+HvnJcuISD7epPHJN0vUM9+WMtXfEli9avFHgu2JxP -3P0ixK2kaJnqKQkSEdnA/v/eWP1Cd2v6rbKCIo9d2gSP0cnpdtlX9Zk3SzEh0V7s -RjSdfZoAvz0aAnpDHlTerLcz5T2aiRae2wSt/RLA3qDO1Ji05tWvQBmKuepxS6A1 -tL4Drm+OCXJwTrE7ClTMCwcrZnLl4tI+Z+X3DV92WQB8ldST/QFjz1hgs/4zrADA -elu2c/X7MR4ObOjhDfaVGQ8kMhYf5hx69qyNDsGi ------END CERTIFICATE----- -""" - -SOME_OTHER_RSA_KEY = """ ------BEGIN RSA PRIVATE KEY----- -MIICWwIBAAKBgQDDnJL9dAdDpjoq4tksTJmdM0AjIHa7Y2yc8XwU7YkgrOR0m4Po -r7El0NwWf5i/LFudX1cOkfwemMIPwQ+67k0BVu/W3SR+g9ZzVKZtTBJnDoqMZ4RJ -jBk4gfwhnQYKPIQvdilDZReH3hFcBvPUkYWSHMn17FBTGmNzp2AnMdLpQQIDAQAB -AoGAIlew7tKaG+RpPfJJ0p84MQM4dXJTph6UiRFUiZASjSwNh/Ntu0JtRYhfu4t3 -U8kD5KNCc4ppyy1ilMV+b4E6/3ydz6syMeJ7G24/PMU8d44zDgZXdM1pf5Nlosh1 -BVv1Fvb0PBW2xs9VRlO6W62IWVtsZCGXYNayrXDiRZ50IGkCQQDkmOVEqffz3GeD -A+XWp9YrXeMqOmtPcrOuvMIO9DwrlXb8eNwvG5GxbuHGuZfOp01tiPyQrkxM0JzU -y8iD1pjrAkEA2w9topUzYS/NZt45OD9t5ZBVMfP15AwWRVv7V5uTksTqfZ9tFfh6 -pN4oWe6xK/kgKAdE9hkjubGKQBjJSC27gwJAGZlRm1XZUXKuGMrX8yjKYALcjH8M -Q1JZ8shqhtgs4MiVEYLLTW8t6ou7NtDTwi2UCx8bAWyzWKrH1UCYzMK8TwJAMngU -fz+2ra5wuUF7l1ztudUN+8tEHH04aFRvzNhYIJljmPuxCz3LK87PJyEaCpKD+RTr -q3NRSsf/nRLY1NtMdwJAVKOdUCwZKGpGyOUZPRbZZAPlojIff2CxJ6E2Pr0RbShD -31icKmhIY+e2rP6v5W7hzTGge5PA0hRfCiwyd+zLoQ== ------END RSA PRIVATE KEY----- -""" - -ALT_EXT_CRT_KEY = """ ------BEGIN RSA PRIVATE KEY----- -MIIEowIBAAKCAQEAsvWeZsM9QOmzziLWwzeuEetz4OW7Q3/ApBYpkV6JZS0X+mi3 -X1XejTJcOmyDtblGQsxMWRkRydCnIZ2kAaNOPOY1cxnD30TPGyatHeXqFQQhKJ9V -u8S5+1hlPqM/EzkwvpYCMU1oERk9a1mLwKBfnF2N2sM2d3DIehvj2fs1z+HWRWqo -GowgtptOoLrIRzFL8H4O6BW65hsR1hhgSVBQx8z6ZR1QutKohWXafkC+9IWNbNbN -+A/KnhAqf5/sdVFPrZuH9eNDzyfW0xg0ASqLFFlM+CpjUsWX1UlWBGn190vq20y6 -s5HcLRJMnmoJ0oEvsuultji7x0StqBELpgDIjwIDAQABAoIBAC3DX6FZtfU+jgtd -n1vGhk3wzu4o8S0+ow2S2UhiS3JDCMmxM4s+ky26Phl2nGvBGDWGttNl9MWOBN80 -x7bfgudR20M2yH70wp1n04c8vxJmvu/7ZtogYYrjvOg6qKuKyWtDQwZGjCErOiiU -eodku25qAhd6Khh7D9kh/q9EbSteYFXsqJiNrY4ul1+cROMZpHx63xY6AzPmkvSU -garkgY4rw9E71t7it2laWkRKVsd+kEjayritdEEliNMVFFtrGEgplYkmLxGf0HLi -ROFVMCLRW/P12JpXllFPrBb8rlPL4w1c/s+yStohT0K+o4FLXhsf/inxmfc9XnZX -dJm0k/ECgYEA47FpV1caMk+TNPfu318VCGRmjwpXdmkNaUiX2Uvs3xIKQ6KJmpo3 -sj0YjQEmQVz8s6geStvU1LdPxgsWZfbDt31M6SNwylh82ABQF1bZyrcMRxM8bHhe -bhDITM1dAn6aROkS1cBpfR9NJOFD850lmJvBGR9ORVBGyucTKH5uXxkCgYEAyTU0 -zQKW2aU3J7mTCC9cp+eSD3fubJpa3ML5XfQ8YNID4PsxWglNKPcOTC4yaSfxVmyk -S0WIQUazCstszQsvwy9YyHtpkMq+0lyCPvrYnmRV0zx5zT155V2zcEh/oj64eoee -W5kvJSs/x6vT+lEN0TDEJ2gKEaJuBt6JG6P04ecCgYBSNw1CbEEZSYJt7dhi74I4 -tYgSvjk2mFgvW/b4j2HIaksqgNYO7QCPa2AiCfg2Qc09UcceYKJI7Kfxaq97wc6J -wsSyqglgBvONSw+gXcvmVpIoV9nJkO0H8SdiFAUxkWVC3KXgaMmuVE8WsgBHRsb8 -g8EFwTgR7xqgyS8xv/U6gQKBgQCdUr/dSJgAx6EPq5degAHXu0ZGWAUR38MJ+F2Y -6/5FyhCEWoRlHP66+CmywTBjbnrSk5IG1PBL8ebOmu6QiJ2o5R1rbKvHLe/0dabV -bbfwaQ1+ZDvskZP9Fr3WHqnFh3shO2dDwcvOKTnuetj9UWEXXyUQltXAohubvWbB -OPqhowKBgB3t2oUSFJI8fSNQnQNkcespJTddr0oLEwgsIl4Q7rdFHLr+/c46svjJ -kPMtpfxDQvkgK2aWpS4OP0E2vSU/IfMEDmlypfKe2SaTtFehZSUwR4R1/ZhSL3iS -iMwJYgm98P27s4TEMdhlPNVJrj1FrD+4VrgpOsoM20EkZnTvel9s ------END RSA PRIVATE KEY----- -""" - -ENCRYPTED_PKCS8_CRT_KEY_PASSPHRASE = "test_passphrase" - -ENCRYPTED_PKCS8_CRT_KEY = """-----BEGIN ENCRYPTED PRIVATE KEY----- -MIIE6TAbBgkqhkiG9w0BBQMwDgQIT04zko6pmJICAggABIIEyL/79sqzTQ7BsEjY -ao2Uhh3//mpNJfCDhjSZOmWL7s4+161cEqpxrfxo4bHH8fkZ60VZUQP8CjwwQUhP -4iwpv2bYbQwzlttZwTC6s28wh7FRtgVoVPTwvXJa6fl2zAjLtsjwLZ/556ez9xIJ -67hxkIK2EzGQaeEKI1+vVF5EKsgKiPEmgspOBxRPoVWTx49NooiakGnwaBoDyTob -8FMr8mF1EheNQ4kl1bPrl+csD7PPnfbWUdNVvMljEhS3cYamQDPEWyAzvaIr0rHh -/6h80L/G2+0fensrTspWJcjX+XDBwQPk+YMic0TJ3KvkC7p2iNJhjNrjhQ+APZWq -xYrjfcmdK0RaaoqN+1zeE1P2kWIJx9CQZVMeGhVzzcmPwJPDnJFpkU+8cgTWnUr/ -Fh8YtDoDzLiAUcmV1Kk7LYtYPHuU8epuz5PYm49TbWzdS7PX5wqFAFmrVt5jysm4 -D/Ox0r4KV1t7D/1gc1WRIu8oUXkIglCHWNpTyMK0kFPctAf/ua+DUFRE4eSx3rsX -ZKIymdF9v/WF1Ud0tsNeudQbVeXWS6UCR8m/rqe81W4npQm/uqUNla+6yaYUmHlk -tvw/m6pt+jKhn0XIRkMwHrTpIaMVvInMg0xpkRuc7Xj5A7vNnkypZRNZJHgy7WWC -6GpOCWJOltYaNy7tmAkSUHJ6kNjXK5a4fi30HknEaqKjFTQNGvcybulJ3MXUzds0 -MJoTpvQfLzYQbMYZ/XRGND4lgeEbs29nWLPae8D5XlDeZQMin8EukPko8u8+YGbU -eWGOvDc+4/xrWrsq1i6R0uWq+Cyoql8oh0PNBlM04S7GAbu1pOD/tPcq/GNYcv/Q -vJcIz9KA3BNepq7tC8D88ggEvFjTsHKeW/OnuCxKducSna4Mq+GebU52tKjkLjFC -eLG4Vx0BY5xPH3gd7iyuAf7S+08BbinNZWjHLpdmR3vKK5YbLPiGSfcYQdClr6BK -9vNWH4TXmZMV+rWtfSeM/cbhCHwxT5Jx6N0OFAxOblQClWnUD79nGkEgn/GoY/Aj -FPNj8u2U/mJHgFHH3ClidYL9jJUvhGpTixB8nGgMjJ0wvFcp+5OysG3TsjqYkwR6 -RRNBmM+iLEUFTrMZYb+edHvGJsMEMZ0qvjmZDsfDz6ax5M9zH/ORFcGplgIec8kj -I106+dqAVVrv1CrBf2N/pxV0OXVhgl6ECe/Ee1xYC2e2CiEgUnQtedu8ekgPgp73 -tHcAiWMamLPTwXuL7jFtvWaQfkYBmrBdEx54+eZOfH/NgV3o8gbaWNHSxbfbwlXN -MvyJidZGkXU0DJtUUnO5i2S7ftKCdOzrrSA8HDTvxFUhxretYpF3NzPYpYkM7WJX -GM7bTMn37AWYqLZmdYYdjh1ZOH/wsM/3uxGBpyEyy4Urrr1ux7X1P0cL0O2P/72h -GRd499JLrRMrmmtQ4KrN7GCHdctvujhDP8zvmnaEyGVzg88XmDg50ZF3+8DmOOgX -EMZEYHO2Wi2uyFotFtZCuqoOJmGPPeGV8QrsRs82hnL1bcd6REUTWk0KsTt13lvF -WwMJugHFk5NQuse3P4Hh9smQrRrv1dvnpt7s4yKStKolXUaFWcXJvXVaDfR5266Y -p7cuYY1cAyI7gFfl5A== ------END ENCRYPTED PRIVATE KEY----- -""" - -UNENCRYPTED_PKCS8_CRT_KEY = """-----BEGIN PRIVATE KEY----- -MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCy9Z5mwz1A6bPO -ItbDN64R63Pg5btDf8CkFimRXollLRf6aLdfVd6NMlw6bIO1uUZCzExZGRHJ0Kch -naQBo0485jVzGcPfRM8bJq0d5eoVBCEon1W7xLn7WGU+oz8TOTC+lgIxTWgRGT1r -WYvAoF+cXY3awzZ3cMh6G+PZ+zXP4dZFaqgajCC2m06gushHMUvwfg7oFbrmGxHW -GGBJUFDHzPplHVC60qiFZdp+QL70hY1s1s34D8qeECp/n+x1UU+tm4f140PPJ9bT -GDQBKosUWUz4KmNSxZfVSVYEafX3S+rbTLqzkdwtEkyeagnSgS+y66W2OLvHRK2o -EQumAMiPAgMBAAECggEALcNfoVm19T6OC12fW8aGTfDO7ijxLT6jDZLZSGJLckMI -ybEziz6TLbo+GXaca8EYNYa202X0xY4E3zTHtt+C51HbQzbIfvTCnWfThzy/Ema+ -7/tm2iBhiuO86Dqoq4rJa0NDBkaMISs6KJR6h2S7bmoCF3oqGHsP2SH+r0RtK15g -VeyomI2tji6XX5xE4xmkfHrfFjoDM+aS9JSBquSBjivD0TvW3uK3aVpaREpWx36Q -SNrKuK10QSWI0xUUW2sYSCmViSYvEZ/QcuJE4VUwItFb8/XYmleWUU+sFvyuU8vj -DVz+z7JK2iFPQr6jgUteGx/+KfGZ9z1edld0mbST8QKBgQDjsWlXVxoyT5M09+7f -XxUIZGaPCld2aQ1pSJfZS+zfEgpDoomamjeyPRiNASZBXPyzqB5K29TUt0/GCxZl -9sO3fUzpI3DKWHzYAFAXVtnKtwxHEzxseF5uEMhMzV0CfppE6RLVwGl9H00k4UPz -nSWYm8EZH05FUEbK5xMofm5fGQKBgQDJNTTNApbZpTcnuZMIL1yn55IPd+5smlrc -wvld9Dxg0gPg+zFaCU0o9w5MLjJpJ/FWbKRLRYhBRrMKy2zNCy/DL1jIe2mQyr7S -XII++tieZFXTPHnNPXnlXbNwSH+iPrh6h55bmS8lKz/Hq9P6UQ3RMMQnaAoRom4G -3okbo/Th5wKBgFI3DUJsQRlJgm3t2GLvgji1iBK+OTaYWC9b9viPYchqSyqA1g7t -AI9rYCIJ+DZBzT1Rxx5gokjsp/Fqr3vBzonCxLKqCWAG841LD6Bdy+ZWkihX2cmQ -7QfxJ2IUBTGRZULcpeBoya5UTxayAEdGxvyDwQXBOBHvGqDJLzG/9TqBAoGBAJ1S -v91ImADHoQ+rl16AAde7RkZYBRHfwwn4XZjr/kXKEIRahGUc/rr4KbLBMGNuetKT -kgbU8Evx5s6a7pCInajlHWtsq8ct7/R1ptVtt/BpDX5kO+yRk/0WvdYeqcWHeyE7 -Z0PBy84pOe562P1RYRdfJRCW1cCiG5u9ZsE4+qGjAoGAHe3ahRIUkjx9I1CdA2Rx -6yklN12vSgsTCCwiXhDut0Ucuv79zjqy+MmQ8y2l/ENC+SArZpalLg4/QTa9JT8h -8wQOaXKl8p7ZJpO0V6FlJTBHhHX9mFIveJKIzAliCb3w/buzhMQx2GU81UmuPUWs -P7hWuCk6ygzbQSRmdO96X2w= ------END PRIVATE KEY----- -""" - -EXPECTED_IMD_SUBJS = ["IMD3", "IMD2", "IMD1"] - -X509_IMDS = """Junk ------BEGIN CERTIFICATE----- -MIIBhDCCAS6gAwIBAgIGAUo7hO/eMA0GCSqGSIb3DQEBCwUAMA8xDTALBgNVBAMT -BElNRDIwHhcNMTQxMjExMjI0MjU1WhcNMjUxMTIzMjI0MjU1WjAPMQ0wCwYDVQQD -EwRJTUQzMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKHIPXo2pfD5dpnpVDVz4n43 -zn3VYsjz/mgOZU0WIWjPA97mvulb7mwb4/LB4ijOMzHj9XfwP75GiOFxYFs8O80C -AwEAAaNwMG4wDwYDVR0TAQH/BAUwAwEB/zA8BgNVHSMENTAzgBS6rfnABCO3oHEz -NUUtov2hfXzfVaETpBEwDzENMAsGA1UEAxMESU1EMYIGAUo7hO/DMB0GA1UdDgQW -BBRiLW10LVJiFO/JOLsQFev0ToAcpzANBgkqhkiG9w0BAQsFAANBABtdF+89WuDi -TC0FqCocb7PWdTucaItD9Zn55G8KMd93eXrOE/FQDf1ScC+7j0jIHXjhnyu6k3NV -8el/x5gUHlc= ------END CERTIFICATE----- -Junk should be ignored by x509 splitter ------BEGIN CERTIFICATE----- -MIIBhDCCAS6gAwIBAgIGAUo7hO/DMA0GCSqGSIb3DQEBCwUAMA8xDTALBgNVBAMT -BElNRDEwHhcNMTQxMjExMjI0MjU1WhcNMjUxMTIzMjI0MjU1WjAPMQ0wCwYDVQQD -EwRJTUQyMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAJYHqnsisVKTlwVaCSa2wdrv -CeJJzqpEVV0RVgAAF6FXjX2Tioii+HkXMR9zFgpE1w4yD7iu9JDb8yTdNh+NxysC -AwEAAaNwMG4wDwYDVR0TAQH/BAUwAwEB/zA8BgNVHSMENTAzgBQt3KvN8ncGj4/s -if1+wdvIMCoiE6ETpBEwDzENMAsGA1UEAxMEcm9vdIIGAUo7hO+mMB0GA1UdDgQW -BBS6rfnABCO3oHEzNUUtov2hfXzfVTANBgkqhkiG9w0BAQsFAANBAIlJODvtmpok -eoRPOb81MFwPTTGaIqafebVWfBlR0lmW8IwLhsOUdsQqSzoeypS3SJUBpYT1Uu2v -zEDOmgdMsBY= ------END CERTIFICATE----- -Junk should be thrown out like junk ------BEGIN CERTIFICATE----- -MIIBfzCCASmgAwIBAgIGAUo7hO+mMA0GCSqGSIb3DQEBCwUAMA8xDTALBgNVBAMT -BHJvb3QwHhcNMTQxMjExMjI0MjU1WhcNMjUxMTIzMjI0MjU1WjAPMQ0wCwYDVQQD -EwRJTUQxMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAI+tSJxr60ogwXFmgqbLMW7K -3fkQnh9sZBi7Qo6AzUnfe/AhXoisib651fOxKXCbp57IgzLTv7O9ygq3I+5fQqsC -AwEAAaNrMGkwDwYDVR0TAQH/BAUwAwEB/zA3BgNVHSMEMDAugBR73ZKSpjbsz9tZ -URkvFwpIO7gB4KETpBEwDzENMAsGA1UEAxMEcm9vdIIBATAdBgNVHQ4EFgQULdyr -zfJ3Bo+P7In9fsHbyDAqIhMwDQYJKoZIhvcNAQELBQADQQBenkZ2k7RgZqgj+dxA -D7BF8MN1oUAOpyYqAjkGddSEuMyNmwtHKZI1dyQ0gBIQdiU9yAG2oTbUIK4msbBV -uJIQ ------END CERTIFICATE-----""" - - class TestTLSParseUtils(base.TestCase): def test_alt_subject_name_parses(self): - hosts = cert_parser.get_host_names(ALT_EXT_CRT) + hosts = cert_parser.get_host_names(sample_certs.ALT_EXT_CRT) self.assertIn('www.cnfromsubject.org', hosts['cn']) self.assertIn('www.hostfromdnsname1.com', hosts['dns_names']) self.assertIn('www.hostfromdnsname2.com', hosts['dns_names']) @@ -227,40 +37,100 @@ class TestTLSParseUtils(base.TestCase): def test_x509_parses(self): self.assertRaises(exceptions.UnreadableCert, cert_parser.validate_cert, "BAD CERT") - self.assertTrue(cert_parser.validate_cert(ALT_EXT_CRT)) - self.assertTrue(cert_parser.validate_cert(ALT_EXT_CRT, - private_key=UNENCRYPTED_PKCS8_CRT_KEY)) + self.assertTrue(cert_parser.validate_cert(sample_certs.X509_CERT)) + self.assertTrue(cert_parser.validate_cert(sample_certs.X509_CERT, + private_key=sample_certs.X509_CERT_KEY)) - def test_read_private_key(self): + def test_read_private_key_pkcs8(self): self.assertRaises(exceptions.NeedsPassphrase, - cert_parser._read_privatekey, - ENCRYPTED_PKCS8_CRT_KEY) - cert_parser._read_privatekey( - ENCRYPTED_PKCS8_CRT_KEY, - passphrase=ENCRYPTED_PKCS8_CRT_KEY_PASSPHRASE) + cert_parser._read_private_key, + sample_certs.ENCRYPTED_PKCS8_CRT_KEY) + cert_parser._read_private_key( + sample_certs.ENCRYPTED_PKCS8_CRT_KEY, + passphrase=sample_certs.ENCRYPTED_PKCS8_CRT_KEY_PASSPHRASE) + + def test_read_private_key_pem(self): + self.assertRaises(exceptions.NeedsPassphrase, + cert_parser._read_private_key, + sample_certs.X509_CERT_KEY_ENCRYPTED) + cert_parser._read_private_key( + sample_certs.X509_CERT_KEY_ENCRYPTED, + passphrase=sample_certs.X509_CERT_KEY_PASSPHRASE) + + def test_prepare_private_key(self): + self.assertEqual( + cert_parser.prepare_private_key( + sample_certs.X509_CERT_KEY_ENCRYPTED, + passphrase=sample_certs.X509_CERT_KEY_PASSPHRASE), + sample_certs.X509_CERT_KEY) + + def test_prepare_private_key_orig_not_encrypted(self): + self.assertEqual( + cert_parser.prepare_private_key( + sample_certs.X509_CERT_KEY), + sample_certs.X509_CERT_KEY) def test_validate_cert_and_key_match(self): self.assertTrue( cert_parser.validate_cert( - ALT_EXT_CRT, private_key=ALT_EXT_CRT_KEY)) + sample_certs.X509_CERT, + private_key=sample_certs.X509_CERT_KEY)) self.assertTrue( cert_parser.validate_cert( - ALT_EXT_CRT, private_key=ALT_EXT_CRT_KEY, - intermediates=X509_IMDS)) + sample_certs.X509_CERT, + private_key=sample_certs.X509_CERT_KEY, + intermediates=(sample_certs.TEST_X509_IMDS + + "\nParser should ignore junk\n"))) self.assertRaises(exceptions.MisMatchedKey, cert_parser.validate_cert, - ALT_EXT_CRT, private_key=SOME_OTHER_RSA_KEY) + sample_certs.X509_CERT, + private_key=sample_certs.X509_CERT_KEY_2) def test_split_x509s(self): imds = [] - for x509Pem in cert_parser._split_x509s(X509_IMDS): + for x509Pem in cert_parser._split_x509s(sample_certs.TEST_X509_IMDS): imds.append(cert_parser._get_x509_from_pem_bytes(x509Pem)) for i in range(0, len(imds)): - self.assertEqual(EXPECTED_IMD_SUBJS[i], + self.assertEqual(sample_certs.EXPECTED_IMD_TEST_SUBJS[i], imds[i].subject.get_attributes_for_oid( x509.OID_COMMON_NAME)[0].value) + def test_get_intermediates_pem_chain(self): + self.assertEqual( + sample_certs.X509_IMDS_LIST, + [c for c in + cert_parser.get_intermediates_pems(sample_certs.X509_IMDS)]) + + def test_get_intermediates_pkcs7_pem(self): + self.assertEqual( + sample_certs.X509_IMDS_LIST, + [c for c in + cert_parser.get_intermediates_pems(sample_certs.PKCS7_PEM)]) + + def test_get_intermediates_pkcs7_pem_bad(self): + self.assertRaises( + exceptions.UnreadableCert, + lambda: list(cert_parser.get_intermediates_pems( + '-----BEGIN PKCS7-----\nbad data\n-----END PKCS7-----'))) + + def test_get_intermediates_pkcs7_der(self): + self.assertEqual( + sample_certs.X509_IMDS_LIST, + [c for c in + cert_parser.get_intermediates_pems(sample_certs.PKCS7_DER)]) + + def test_get_intermediates_pkcs7_der_bad(self): + self.assertRaises( + exceptions.UnreadableCert, + lambda: list(cert_parser.get_intermediates_pems( + '\xfe\xfe\xff\xff'))) + + def test_get_x509_from_der_bytes_bad(self): + self.assertRaises( + exceptions.UnreadableCert, + cert_parser._get_x509_from_der_bytes, b'bad data') + def test_load_certificates(self): listener = sample_configs.sample_listener_tuple(tls=True, sni=True) client = mock.MagicMock() @@ -281,16 +151,18 @@ class TestTLSParseUtils(base.TestCase): @mock.patch('octavia.certificates.common.cert.Cert') def test_map_cert_tls_container(self, cert_mock): - tls = data_models.TLSContainer(primary_cn='fakeCN', - certificate='imaCert', - private_key='imaPrivateKey', - intermediates=['imainter1', - 'imainter2']) + tls = data_models.TLSContainer( + primary_cn=sample_certs.X509_CERT_CN, + certificate=sample_certs.X509_CERT, + private_key=sample_certs.X509_CERT_KEY_ENCRYPTED, + passphrase=sample_certs.X509_CERT_KEY_PASSPHRASE, + intermediates=sample_certs.X509_IMDS_LIST) cert_mock.get_private_key.return_value = tls.private_key cert_mock.get_certificate.return_value = tls.certificate cert_mock.get_intermediates.return_value = tls.intermediates + cert_mock.get_private_key_passphrase.return_value = tls.passphrase with mock.patch.object(cert_parser, 'get_host_names') as cp: - cp.return_value = {'cn': 'fakeCN'} + cp.return_value = {'cn': sample_certs.X509_CERT_CN} self.assertEqual( tls.primary_cn, cert_parser._map_cert_tls_container( cert_mock).primary_cn) @@ -298,14 +170,15 @@ class TestTLSParseUtils(base.TestCase): tls.certificate, cert_parser._map_cert_tls_container( cert_mock).certificate) self.assertEqual( - tls.private_key, cert_parser._map_cert_tls_container( + sample_certs.X509_CERT_KEY, + cert_parser._map_cert_tls_container( cert_mock).private_key) self.assertEqual( tls.intermediates, cert_parser._map_cert_tls_container( cert_mock).intermediates) def test_build_pem(self): - expected = 'imacert\nimakey\nimainter\nimainter2' + expected = 'imacert\nimakey\nimainter\nimainter2\n' tls_tupe = sample_configs.sample_tls_container_tuple( certificate='imacert', private_key='imakey', intermediates=['imainter', 'imainter2']) @@ -320,8 +193,8 @@ class TestTLSParseUtils(base.TestCase): self.assertEqual('fakeCN', cn) def test_get_cert_expiration(self): - exp_date = cert_parser.get_cert_expiration(ALT_EXT_CRT) - self.assertEqual(datetime.datetime(2025, 5, 18, 20, 33, 23), exp_date) + exp_date = cert_parser.get_cert_expiration(sample_certs.X509_EXPIRED) + self.assertEqual(datetime.datetime(2016, 9, 25, 18, 1, 54), exp_date) # test the exception self.assertRaises(exceptions.UnreadableCert, diff --git a/releasenotes/notes/support-pkcs7-intermediate-ca-bundles-279c12bad974bff7.yaml b/releasenotes/notes/support-pkcs7-intermediate-ca-bundles-279c12bad974bff7.yaml new file mode 100644 index 0000000000..493be007ac --- /dev/null +++ b/releasenotes/notes/support-pkcs7-intermediate-ca-bundles-279c12bad974bff7.yaml @@ -0,0 +1,6 @@ +--- +features: + - Adds support for PKCS7 PEM or DER encoded intermediate certificate bundles + for TERMINATED_HTTPS listeners. +fixes: + - Resolves an issue with using encrypted TLS private keys. diff --git a/requirements.txt b/requirements.txt index 182817df4e..16952875ee 100644 --- a/requirements.txt +++ b/requirements.txt @@ -25,6 +25,8 @@ oslo.middleware>=3.0.0 # Apache-2.0 oslo.reports>=0.6.0 # Apache-2.0 oslo.service>=1.10.0 # Apache-2.0 oslo.utils>=3.16.0 # Apache-2.0 +pyasn1 # BSD +pyasn1-modules # BSD PyMySQL!=0.7.7,>=0.6.2 # MIT License python-barbicanclient>=4.0.0 # Apache-2.0 python-glanceclient>=2.5.0 # Apache-2.0 diff --git a/tools/pkcs7_to_pem.py b/tools/pkcs7_to_pem.py new file mode 100755 index 0000000000..1834fea8e7 --- /dev/null +++ b/tools/pkcs7_to_pem.py @@ -0,0 +1,113 @@ +#!/usr/bin/python +# +# Copyright 2016 IBM. All rights reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +# Converts a PKCS7 certificate bundle in DER or PEM format into +# a sequence of PEM-encoded certificates. + +import base64 +import sys + +from cryptography.hazmat import backends +from cryptography.hazmat.primitives import serialization +from cryptography import x509 +from pyasn1.codec.der import decoder as der_decoder +from pyasn1.codec.der import encoder as der_encoder +from pyasn1_modules import rfc2315 +import six + + +PKCS7_BEG = """-----BEGIN PKCS7-----""" +PKCS7_END = """-----END PKCS7-----""" + + +# Based on pyasn1-modules.pem.readPemBlocksFromFile, but eliminates the need +# to operate on a file handle. +def _read_pem_blocks(data, *markers): + stSpam, stHam, stDump = 0, 1, 2 + + startMarkers = dict(map(lambda x: (x[1], x[0]), + enumerate(map(lambda x: x[0], markers)))) + stopMarkers = dict(map(lambda x: (x[1], x[0]), + enumerate(map(lambda x: x[1], markers)))) + idx = -1 + state = stSpam + if six.PY3: + data = str(data, encoding="UTF-8") + for certLine in data.replace('\r', '').split('\n'): + if not certLine: + break + certLine = certLine.strip() + if state == stSpam: + if certLine in startMarkers: + certLines = [] + idx = startMarkers[certLine] + state = stHam + continue + if state == stHam: + if certLine in stopMarkers and stopMarkers[certLine] == idx: + state = stDump + else: + certLines.append(certLine) + if state == stDump: + if six.PY2: + yield ''.join([ + base64.b64decode(x) for x in certLines]) + elif six.PY3: + yield ''.encode().join([ + base64.b64decode(x) for x in certLines]) + state = stSpam + + +def _process_pkcs7_substrate(substrate): + contentInfo, _ = der_decoder.decode(substrate, + asn1Spec=rfc2315.ContentInfo()) + + contentType = contentInfo.getComponentByName('contentType') + + if contentType != rfc2315.signedData: + raise Exception + + content, _ = der_decoder.decode( + contentInfo.getComponentByName('content'), + asn1Spec=rfc2315.SignedData()) + + for blob in content.getComponentByName('certificates'): + cert = x509.load_der_x509_certificate(der_encoder.encode(blob), + backends.default_backend()) + six.print_(cert.public_bytes( + encoding=serialization.Encoding.PEM).decode( + 'unicode_escape'), end='') + + +# Main program code +if len(sys.argv) != 1: + six.print_('Usage: cat | %s' % sys.argv[0]) + sys.exit(-1) + +# Need to read in binary bytes in case DER encoding of PKCS7 bundle +if six.PY2: + data = sys.stdin.read() +elif six.PY3: + data = sys.stdin.buffer.read() + +# Look for PEM encoding +if PKCS7_BEG in str(data): + for substrate in _read_pem_blocks(data, (PKCS7_BEG, PKCS7_END)): + _process_pkcs7_substrate(substrate) + +# If no PEM encoding, assume this is DER encoded and try to decode +else: + _process_pkcs7_substrate(data)