Add support for PKCS7 bundles and encrypted keys

This commit adds the ability for Octavia to make use of PKCS7
intermediate certificate bundles. These PKCS7 bundles may be in PEM or
DER format. This feature is being added since barbican specifies that
this is the preferred format for intermediate bundles in secret
containers.

This commit also re-arranges and/or strengthens several of our existing
tests of TLS / SNI functionality and in the process also fixes a bug
where encrypted private keys were not uploaded to amphorae in a format
that haproxy can readily parse. I have also added several sample or
dummy certificates which can be used for an up-coming scenario test
which exercises TLS-termination capabilities of Octavia.

Change-Id: I14e394bbf48456d2e2a7bbefcc777a1b6f4b83e4
Closes-Bug: #1627356
Closes-Bug: #1627367
This commit is contained in:
Stephen Balukoff 2016-09-30 01:42:40 -07:00
parent a26d76b144
commit a27b5c418a
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-----
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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-----"""

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)