Merge "Add support for PKCS7 bundles and encrypted keys"

This commit is contained in:
Jenkins
2016-10-07 20:59:44 +00:00
committed by Gerrit Code Review
12 changed files with 1323 additions and 307 deletions

View File

@@ -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

View File

@@ -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:

View File

@@ -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())

View File

@@ -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)

View File

@@ -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(),

View File

@@ -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)

View File

@@ -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-----
MIIJKwIBAAKCAgEA3WLWYLlM0aaISfBiNvygl2cXehc5mbaqys9SYWcUleeloWJ2
JY80IEbD1/JcVFgv50ptd3I4rXyhv07wzU7Om2/be1ZGDNJNHsLwK3DUUkkf2Bzt
K+31r+15ME77d8hbqVpHjQWqgwoWmb5W5XEMTVIFyN6/3TiQ4TpX84RdCzC+RSCB
4JIEAhwFEOtjny5Jl6jT2ACmvSv0xOFBrRvS2E9HctwMClKb135yiUhIK0XfDbpe
0aegYGFqCYOqWQ+BixYqk9m8aYSSWgGLpYLRdYwx3mYMrp5u1q8Gb8TwvACHBXJL
ETMQeVzhKPDwV2hh8WyI5fjwsOlqReYO2TveZHmKJ6UPJyvVXJ+wYFnEFGOUrmQm
jE5O/R7P5A9bqXNtiB4P/7dasvtItkjU8Y+5+5pMpwR6Ckq5Vb4lJMmR0GJ4rgBn
ImGCk30ctBdS2jN0DlBd1wPY/toNYwlqJTD1qc1hUzpAcKApgrNDF+xWmL7AHi8e
Kw1S0UFs7ymShk7mwwVkvMAbAKtIQ7+G96+JOnCM/WuD2DRQEbJ0oZCh8JZ/ZTOR
4zx1DUmeqGmiHYuMBH9McIqTIhIvsYd1JM7auqQRpTW3cXZQT9z1WqdACPNWpbMC
1qgi+cgR879DUKfse/xDgyQ3LxOrCj0ZgVU8zvWNtCjJR63HZT4jWwQ4SfkCAwEA
AQKCAgEA2q4m1KQ1HWJCfcbVPTuN5gAPUKpgW1X0nyDrXwtTaj/HfAKmcbNi6f78
tPLSAP6bUvxR5QsOsU/K9g4kDqkprKBxTQOLbl7NjvVAB6kMEbvpmK/6FsqXRZBt
hSp/e3KOGFr1EnfmVkpAyN0bOMjSPg4naKOfIgYeFlxrREAbKFKdn+rcX9fb3bmP
x4a8gSBX0VcS6uq5yWMCBPf8x+IUA1dMXEjAG/I9vj9JJBIiN5xtGEJgJvhNkuam
t383ZYHLlHfw1trdId2yMvYT2wm9nT8+g1CKdnJJSgbZdM40fYCH3vlm7TZjr33v
a2GUBsM0/CUZlRCxsA7gyurVAAADS6UtfOF2lcLIxeC8FDdL/p4ytF3sYND4f0kp
+gQn5+vTnfijfEqWbHWnkn9V8MSZd3ihVn74d2544SOLJ5j+i1vYfieBj4gXhOiA
TMudpGh7wKOy/ajLRtSxtM1uYGtycA1e1jaBX8eXwfPyJemYlurK7DEyH2BlVbJY
EUCGYvR96VNpDLpvBwB0+G4E1LJOpt+h4si03mQIfnX3um6hBmUGzGwyr14i7Qh6
mPT2i/xdZtUFD1Hp2cFCwVvkGzhorgM+ICgLOFF2FOuzBrC+zrQNj6Aom1bWakdw
x/lNKSYmzypsCQC20lCme4SRyRfn/Tz/ylN95uvZvU5gr7Lhf4ECggEBAPwKNCGI
45B16n7CqnTziO3nZHO2xeKEyzLXbKK2f/AvYiQnZxRG8aY+DqoqnnBbXCXnd7LW
nDjoOYI3l75n/F33+2IiyJUNSF2noLEu1T1gQVaFuU6p8bwCJ5rShuxXMVAGkw3z
/bcTuaZIJU4KTNCP4A9wgyB40uwRrYiEQMaYXjeph71PTOEA7XseuiOhHnaiCaeg
KVivOD9vR532Ai2mCmi/6oBtT/AjnbWLXXNJRp0OfPZ2nZ/Z8j0zmCMmbhMtpQe0
Utk5LaABCqRh6ZRp4bvoqgR7yrOAH1NUPPJhdrQywAl0UiXgnjhNixDp4kP/TLvE
70Z2i+u3ayssEnMCggEBAODdVUYSw3F+CQ8T+IGaTAU8lzE5fgxGZcxsnzrnzbCQ
PLSuzx9FJJ8+gYCkD3FUg8vduN8uyy3YY13LFjOiJ6+TczglgmGgZM+2qxQ5mFyT
9FVijPUNtUuhJm3SBvHpJlzmcR/uNiIws55M+RbGSKB7kotf5FchZ2YBhZzpr7lG
jn6x15ts6iSlxHnR5QAPvqgCOhUJnk8CiDaypx12MXRP/A/KZX8XAeRFIMmKSC6f
O7kRY/xpSKxuyvACDybxhXbGP86t07ZXpXU8PmgU6yjnsGxQOg4iLlReI3jiaa7m
TTeiNjW3Ra2pOBd5BWn3ecVvf4UHJsJs59euYWha2uMCggEBAMbLlYrN2hBbsXYC
PUi5vktHs+fBRhwA+fVEDZ/Zqtfbx+EUYy2PN5MUZ6S4sPeWV/xdsgARXm9UW+fl
yX6Zrmi/7Dvfi65lJ6sXSJv4gKFEhsSj/SGa0dylJm/rlhhcPb0NMnhS9s+sc0ZA
qYwAe84VbXlAGW1HX7ZryyBekGkUTVxCD5q2LcFbZfUyq0bnEowoCs14eqREsGz4
bNie7eDrklJE7cYWcnLK5N4I6tC//z5p6w7LSFCJK5QyWdF/wlrGKeEFzkMf4mjN
6YL257H0QeRhA5k9uwgSCqNDUj8ruOExFl9erFzL6oAmSYYxtBJGEFQaZVCCuKJX
reQDgxkCggEBANjfn6b94dJMJKCOOleFYVyLm6a2NIPSQcxv1wMRHqjz9BivoMQg
A7oypuRTVzGOua6eIfffQcbttKh5fug9tNj59V5pjt5pu9E59LaE9hYT/Mt9SUXv
+rL+sfmpX1lh7MYc225YaY2AOzyqMHNuug1OIYCa87e1V+xh+2PjXr/q9PPswm39
FbZSyrRTY/IzPUb9Hte7dxvs7UMT+2nG3Nu5aPox0sJIhmKK6Zx36jZNDWTpCO4g
/R6RnNjuo36D4p0zh8bmkBKFZec0O1xXEJdbHiTZG6UWAmkMglnMxPES3daSdIZK
RMHBO4AoELirHp71cp/yzccnElRKs1faiNECggEBAJg1b53r259txjDUxY922imF
JXySNhRHlrQ6BYyfHJWMMEVNasd86wrc8VKgIqQcxtdfIL1dGxOu31xzmxsSmfjR
0aG51uHi/erTKeV0C3/bdZ8TgeTKhxXAVZXLuJ4i6HvdF1lAZmE0stXd7vA0bedJ
7RYKKnuW16qo05oNx/ADdjfCaOHA0cCfyPv294CQn0z4hyEVAbBykU/j6v0WbzS5
r187A8Q9L5pB57JnuY9nO7MvrINJVNbLPYjanqrkqvwDjiPkzETVm50mVtFYLWgw
8m7OLXEaFVJ4XA3C8e78bzDhSMvQTc8QVYmwj24gQ/uolftqdM4lEKpUucw/ECs=
-----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-----"""

View File

@@ -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,

View File

@@ -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,

View File

@@ -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.

View File

@@ -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

113
tools/pkcs7_to_pem.py Executable file
View File

@@ -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 <pkcs7_bundle.p7b> | %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)