Merge "Support ICA signed certificate installation"

This commit is contained in:
Zuul 2020-08-05 12:11:40 +00:00 committed by Gerrit Code Review
commit 273a5b5fe5
8 changed files with 583 additions and 54 deletions

View File

@ -348,12 +348,43 @@ class CertificateController(rest.RestController):
LOG.info(msg)
return dict(success="", error=msg)
for cert in certs:
hash_issuers = []
for index, cert in enumerate(certs):
msg = self._check_cert_validity(cert)
if msg is not True:
return dict(success="", error=msg)
if mode == constants.CERT_MODE_OPENSTACK:
# validation checking for ssl, tpm_mode, docker_registry
# and openstack certficcates
if mode in [constants.CERT_MODE_SSL,
constants.CERT_MODE_TPM,
constants.CERT_MODE_DOCKER_REGISTRY,
constants.CERT_MODE_OPENSTACK,
]:
try:
hash_issuers.append(cutils.get_cert_issuer_hash(cert))
if index == 0:
if cutils.is_ca_cert(cert):
msg = "The first cert in the file should not be " \
"a CA cert"
return dict(success="", error=msg)
else:
if not cutils.is_ca_cert(cert):
msg = "Number %s cert in the file should be a " \
"CA cert" % (index + 1)
return dict(success="", error=msg)
hash_subject = cutils.get_cert_subject_hash(cert)
if hash_subject != hash_issuers[index - 1]:
msg = "Number %s cert in the file is not " \
"signing cert of the preceding one. Check " \
"certs order in the file." % (index + 1)
return dict(success="", error=msg)
except Exception as e:
msg = "No certificates have been added, exception " \
"occured on cert %s: %s" % (index, e)
return dict(success="", error=msg)
if mode == constants.CERT_MODE_OPENSTACK and index == 0:
domain, msg = _check_endpoint_domain_exists()
if domain:
msg = _check_cert_dns_name(cert, domain)
@ -412,6 +443,16 @@ class CertificateController(rest.RestController):
# information returned from conductor manager.
certificate_dicts = []
for inv_cert in inv_certs:
# for ssl, tmp_mode, docker_registry and openstack certs, if the
# cert is ICA signed cert (ie, the pem_contents contains
# intermediate CA certs), skip these intermediate CA certs.
if mode in [constants.CERT_MODE_SSL,
constants.CERT_MODE_TPM,
constants.CERT_MODE_DOCKER_REGISTRY,
constants.CERT_MODE_OPENSTACK] \
and inv_cert.get('is_ca', None):
continue
values = {
'certtype': mode,
'signature': inv_cert.get('signature'),

View File

@ -31,6 +31,8 @@ import collections
import contextlib
from cryptography import x509
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization
from OpenSSL import crypto
import datetime
import errno
import functools
@ -2210,10 +2212,70 @@ def extract_certs_from_pem(pem_contents):
"Failed to load pem x509 certificate"))
certs.append(cert)
start = start + index + len(marker)
start = index + len(marker)
return certs
def is_ca_cert(cert):
"""
Check if the certificate is a CA certficate
:param cert: the certificate to be checked
:return: True is the certificate is a CA certificate, otherwise
False
"""
# extract "ca" value from cert extensions
is_ca = False
try:
basic_constraints = cert.extensions.get_extension_for_oid(
x509.ExtensionOID.BASIC_CONSTRAINTS)
value = getattr(basic_constraints, 'value', None)
if value:
is_ca = getattr(value, 'ca', False)
except x509.ExtensionNotFound:
LOG.debug("The cert doesn't have BASIC_CONSTRAINTS extension")
pass
return is_ca
def get_cert_issuer_hash(cert):
"""
Get the hash value of the cert's issuer DN
:param cert: the certificate to get issuer from
:return: The hash value of the cert's issuer DN
"""
try:
public_bytes = cert.public_bytes(encoding=serialization.Encoding.PEM)
cert_c = crypto.load_certificate(crypto.FILETYPE_PEM, public_bytes)
hash_issuer = cert_c.get_issuer().hash()
except Exception:
LOG.exception()
raise exception.SysinvException(_(
"Failed to get certificate issuer hash."))
return hash_issuer
def get_cert_subject_hash(cert):
"""
Get the hash value of the cert's subject DN
:param cert: the certificate to get subject from
:return: The hash value of the cert's subject DN
"""
try:
public_bytes = cert.public_bytes(encoding=serialization.Encoding.PEM)
cert_c = crypto.load_certificate(crypto.FILETYPE_PEM, public_bytes)
hash_subject = cert_c.get_subject().hash()
except Exception:
LOG.exception()
raise exception.SysinvException(_(
"Failed to get certificate subject hash."))
return hash_subject
def format_image_filename(device_image):
""" Format device image filename """
return "{}-{}-{}-{}.bit".format(device_image.bitstream_type,

View File

@ -10311,7 +10311,7 @@ class ConductorManager(service.PeriodicService):
:param cert_format: serialization.PrivateFormat
:param passphrase: passphrase for PEM file
:returns: A list of {cert, private_bytes, public_bytes, signature}
:returns: A list of {cert, public_bytes, signature}, and private key.
"""
if passphrase:
@ -10353,7 +10353,7 @@ class ConductorManager(service.PeriodicService):
% e))
certs = cutils.extract_certs_from_pem(temp_pem_contents)
key_list = []
cert_list = []
for cert in certs:
# format=serialization.PrivateFormat.TraditionalOpenSSL,
try:
@ -10364,40 +10364,49 @@ class ConductorManager(service.PeriodicService):
"bytes from PEM data: %s"
% e))
# check if the cert is a CA cert
is_ca = cutils.is_ca_cert(cert)
signature = mode + '_' + str(cert.serial_number)
if len(signature) > 255:
LOG.info("Truncating certificate serial no %s" % signature)
signature = signature[:255]
LOG.info("config_certificate signature=%s" % signature)
key_list.append({'cert': cert,
'private_bytes': private_bytes,
cert_list.append({'cert': cert,
'is_ca': is_ca,
'public_bytes': public_bytes,
'signature': signature})
return key_list
return cert_list, private_bytes
@staticmethod
def _get_public_bytes_one(key_list):
"""Get exactly one public bytes entry from key list"""
def _get_public_bytes(cert_list):
"""Get all public bytes from cert list"""
if len(key_list) != 1:
msg = "There should be exactly one certificate " \
"(ie, public_bytes) in the pem contents."
if len(cert_list) < 1:
msg = "There should be at least one certificate " \
"in the pem contents."
LOG.error(msg)
raise exception.SysinvException(_(msg))
return key_list[0].get('public_bytes')
# Concatenate all the public bytes together, as the pem contents
# may contain intermediate CA certs in it.
public_bytes = ''
for cert in cert_list:
public_bytes += cert.get('public_bytes', '')
return public_bytes
@staticmethod
def _get_private_bytes_one(key_list):
"""Get exactly one private bytes entry from key list"""
def _get_private_bytes_one(private_key):
"""Get exactly one private bytes entry from private key"""
if len(key_list) != 1:
msg = "There should be exactly one private key " \
"(ie, private_bytes) in the pem contents."
if not private_key:
msg = "No private key found in the pem contents."
LOG.error(msg)
raise exception.SysinvException(_(msg))
return key_list[0].get('private_bytes')
return private_key
@staticmethod
def _consolidate_cert_files():
@ -10486,7 +10495,7 @@ class ConductorManager(service.PeriodicService):
LOG.info("config_certificate mode=%s" % mode)
key_list = \
cert_list, private_key = \
self._extract_keys_from_pem(mode, pem_contents,
serialization.PrivateFormat.PKCS8,
passphrase)
@ -10499,8 +10508,8 @@ class ConductorManager(service.PeriodicService):
pass
if mode == constants.CERT_MODE_TPM:
private_bytes = self._get_private_bytes_one(key_list)
public_bytes = self._get_public_bytes_one(key_list)
private_bytes = self._get_private_bytes_one(private_key)
public_bytes = self._get_public_bytes(cert_list)
self._perform_config_certificate_tpm_mode(
context, tpm, private_bytes, public_bytes)
@ -10514,8 +10523,8 @@ class ConductorManager(service.PeriodicService):
elif mode == constants.CERT_MODE_SSL:
config_uuid = self._config_update_hosts(context, personalities)
private_bytes = self._get_private_bytes_one(key_list)
public_bytes = self._get_public_bytes_one(key_list)
private_bytes = self._get_private_bytes_one(private_key)
public_bytes = self._get_public_bytes(cert_list)
file_content = private_bytes + public_bytes
config_dict = {
'personalities': personalities,
@ -10557,23 +10566,23 @@ class ConductorManager(service.PeriodicService):
# The list of the actual CA certs as files in FS
certs_file = os.listdir(constants.SSL_CERT_CA_LIST_SHARED_DIR)
# Remove these already installed from the key list
key_list_c = key_list[:]
for key in key_list_c:
if key.get('signature') in certs_inv \
and key.get('signature') in certs_file:
key_list.remove(key)
# Remove these already installed from the cert list
cert_list_c = cert_list[:]
for cert in cert_list_c:
if cert.get('signature') in certs_inv \
and cert.get('signature') in certs_file:
cert_list.remove(cert)
# Save certs in files and cat them into ca-cert.pem to apply to the
# system.
if key_list:
if cert_list:
# Save each cert in a separate file with signature as its name
try:
for key in key_list:
file_content = key.get('public_bytes')
for cert in cert_list:
file_content = cert.get('public_bytes')
file_name = \
os.path.join(constants.SSL_CERT_CA_LIST_SHARED_DIR,
key.get('signature'))
cert.get('signature'))
with os.fdopen(
os.open(file_name,
os.O_CREAT | os.O_TRUNC | os.O_WRONLY,
@ -10583,7 +10592,7 @@ class ConductorManager(service.PeriodicService):
except Exception as e:
msg = "Failed to save cert file: %s" % str(e)
LOG.warn(msg)
raise exception.SysinvException(_(msg))
raise exception.SysinvException(msg)
# consolidate the CA cert files into ca-cert.pem to update
# system CA certs.
@ -10604,11 +10613,12 @@ class ConductorManager(service.PeriodicService):
elif mode == constants.CERT_MODE_DOCKER_REGISTRY:
LOG.info("Docker registry certificate install")
# docker registry requires a PKCS1 key for the token server
key_list_pkcs1 = \
_, private_key_pkcs1 = \
self._extract_keys_from_pem(mode, pem_contents,
serialization.PrivateFormat
.TraditionalOpenSSL, passphrase)
pkcs1_private_bytes = self._get_private_bytes_one(key_list_pkcs1)
pkcs1_private_bytes = \
self._get_private_bytes_one(private_key_pkcs1)
# install certificate, key, and pkcs1 key to controllers
config_uuid = self._config_update_hosts(context, personalities)
@ -10616,8 +10626,8 @@ class ConductorManager(service.PeriodicService):
cert_path = constants.DOCKER_REGISTRY_CERT_FILE
pkcs1_key_path = constants.DOCKER_REGISTRY_PKCS1_KEY_FILE
private_bytes = self._get_private_bytes_one(key_list)
public_bytes = self._get_public_bytes_one(key_list)
private_bytes = self._get_private_bytes_one(private_key)
public_bytes = self._get_public_bytes(cert_list)
config_dict = {
'personalities': personalities,
@ -10677,8 +10687,8 @@ class ConductorManager(service.PeriodicService):
config_uuid = self._config_update_hosts(context, personalities)
key_path = constants.OPENSTACK_CERT_KEY_FILE
cert_path = constants.OPENSTACK_CERT_FILE
private_bytes = self._get_private_bytes_one(key_list)
public_bytes = self._get_public_bytes_one(key_list)
private_bytes = self._get_private_bytes_one(private_key)
public_bytes = self._get_public_bytes(cert_list)
config_dict = {
'personalities': personalities,
@ -10716,9 +10726,7 @@ class ConductorManager(service.PeriodicService):
elif mode == constants.CERT_MODE_OPENSTACK_CA:
config_uuid = self._config_update_hosts(context, personalities)
file_content = ''
for key in key_list:
file_content += key.get('public_bytes', '')
file_content = self._get_public_bytes(cert_list)
config_dict = {
'personalities': personalities,
'file_names': [constants.OPENSTACK_CERT_CA_FILE],
@ -10749,10 +10757,11 @@ class ConductorManager(service.PeriodicService):
raise exception.SysinvException(_(msg))
inv_certs = []
for key in key_list:
inv_cert = {'signature': key.get('signature'),
'not_valid_before': key.get('cert').not_valid_before,
'not_valid_after': key.get('cert').not_valid_after}
for cert in cert_list:
inv_cert = {'signature': cert.get('signature'),
'is_ca': cert.get('is_ca'),
'not_valid_before': cert.get('cert').not_valid_before,
'not_valid_after': cert.get('cert').not_valid_after}
inv_certs.append(inv_cert)
return inv_certs
@ -10775,7 +10784,7 @@ class ConductorManager(service.PeriodicService):
LOG.info("_config_selfsigned_certificate mode=%s file=%s" % (mode, certificate_file))
key_list = \
cert_list, private_key = \
self._extract_keys_from_pem(mode, pem_contents,
serialization.PrivateFormat.PKCS8,
passphrase)
@ -10783,8 +10792,8 @@ class ConductorManager(service.PeriodicService):
personalities = [constants.CONTROLLER]
config_uuid = self._config_update_hosts(context, personalities)
private_bytes = self._get_private_bytes_one(key_list)
public_bytes = self._get_public_bytes_one(key_list)
private_bytes = self._get_private_bytes_one(private_key)
public_bytes = self._get_public_bytes(cert_list)
file_content = private_bytes + public_bytes
config_dict = {
'personalities': personalities,
@ -10802,7 +10811,7 @@ class ConductorManager(service.PeriodicService):
'wb') as f:
f.write(file_content)
return key_list[0].get('signature')
return cert_list[0].get('signature')
def delete_certificate(self, context, mode, signature):
"""Delete a certificate by its mode and signature.

View File

@ -0,0 +1,64 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAxKPRDBjPM/vnwuKojBwzL0KG7jvdm7XWzbafGR3kM2kXPMQB
OtCaIHesVn573VZdfAA1w20p/l2uX+ArQEuqt3qgI1aq1+cPVg0QLkrbeYCQYz/S
/wbTIRU4jYXOFEbotvqNrfhOo1thevhSZB+9nmjx/fPv3smhzsEGyzmLunN60zaj
9zkBpJJkGtuyczn11qe9yARUvRlSqUjaXUF/DmP7vAGPTpQttD93xoPOb14onC3W
K8DC5kCpCHbhYNNNWq+KYjsvmdd6/ZzIw9rHTjXuj39nyp80rqk/jFknJWASt/Qu
q18Fkm/i4BB5QocSJ0dnV2GvA7W48n84+INhEQIDAQABAoIBADhDp00OzDrTmTzq
Hc/5QawEHReNiZtELHIwDtXg96q+Jyf77V4m0fL4Sxd0mXx7ecRBSyDf22Qs6fgE
7Fs5S1+0dp2l7rxYIMs+caLNSH87ihTb7kOKcF7G86eK4axrnaq9hkyA//895maJ
Jwn/CwKIhLVlVv0oNGQ0Vv1POevfHlbP+KSK8dDRgkStfP+mM3T9yyWttr37RHmv
uvg+R0tL9eZetc8QvEmCy3WnCslfJ9g3NxBjKgxWyQIw99w0IDtQv+uPtNHOvM4w
08zNW//foV0QvhhHbejrjyTTp2LVQ97wOz0S5GfnbmwsfYQagaP735DXzoTWC1mm
SWESkAECgYEA7+Chlop04bf+xLxAPE49tca1+I8ESu4FsEvHs1k1/58BlopGOqiD
/Uf/N0aySNmzv+/CU3klsmnpTZYmY/Su+ELjhY9GC1KLFghHBRVU+cIX53VYY7ca
q69FjxcA8BZjTdYe5hPTHbiWXvzUYOK22XjaI6Hm03atdcd6oW6hUtkCgYEA0ds7
uNSwmP4Bc2FzP1uMlxAlUWhHS513NG1keXk3MKtofrgA1/Sb1FLs9lYQbHWjnvZD
HF5e2opPM8nExqKV9vUNnObW/kBClX3i189Fdt5OgJgoZH/Imc/cXvqMeLepPL4s
TFeScDjsbqaUpUDnu8KOatpLP6M/9aEqjrNwrPkCgYEAoKy8T9PqOo8+TZUzCbH2
z92MYjOZ0n7gvJfl6hIlg1WEgMuaAwyBbJRfNS1bmkSIjFYSukr5nyomJdwfVvC8
inpzYDD53/eoRlfBCnyhcLI7xMPrMs+hQ8KhOxlTfX3hgm+cPsykqtHLl1CPCV6S
z9SNeTSnIpnp0myNjbhGwvECgYAJL9TEeaMlf3Em0OxPqL1V26IWlz8Pw1v1kphk
bPcKjWZsrbdzvMSZneu7uPbNGLVJ/zPe6Q6z+HwRI1MW8wUno4CuDrj4tm1zx+gH
VI6Q7ph3LrIN/D4W5a+bBNLjcf7ZpuWfjfHeMc+/5FD7jwTWBmVfriC/L3FcWmrX
hml3WQKBgGUH5mJIVEbxxe6HaMXlJ6T+Z6Vtx98Q5DC2NvukGiH35WCB/itwy8Se
UGZ9t+gXvSIyktQcQHDHKOIHdQvWPSLCN2jBWhRN8LtxTP1HaBRn6QX99PQ/K7AF
V9O/MHuLI0Ph1brfvSlPTjxXwtqHLZe3dUlfXkaiBWYwFFgrAk0C
-----END RSA PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
MIIC2jCCAcKgAwIBAgIRAJx+0xv3wyTCGC/hqxY6aMYwDQYJKoZIhvcNAQELBQAw
EjEQMA4GA1UEAwwHcm9vdF9jYTAeFw0yMDA2MjUxNTEwMDBaFw0yMTA2MjUxNTEw
MDBaMBoxGDAWBgNVBAMMD2ludGVybWVkaWF0ZV9jYTCCASIwDQYJKoZIhvcNAQEB
BQADggEPADCCAQoCggEBAMk0swFLA2iI4MI6QWyi6K3QzPSVUNIw8UHtSi6U3O5s
q750DY2NLfBJwKpROjqwKbG7lwcYG3qDKFK3E1YR5H4q66WT+8J6DyP5poouOwvP
Gn9GYqb+nGbePyL/woFPFmaXGUZpTQTUn5UwyNFX9APkoGqJR0RpW5o5E1Hhx2Nn
8DEzuUc4r45YCChK0NaeW2EobNhGWsNxOypBcXSDwj+zXNdxoToR7By6uScPdO1l
qNetjGq+RCUCYj70+oYDwiIPetF/kkj0rjLO2GxVvLJMAYDO5lBwQRfaIMJF2wNB
yHecVJHAq5CZLHbkIqnilRiPQaJWdTuqn5YPGUzRejECAwEAAaMjMCEwDgYDVR0P
AQH/BAQDAgKkMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAEbA
gaZW06bMjPMKyZ0tRDryQ5ctdRBTOs1SQtu/24DUsKU7wkX9Is06v45RpLlVoSG8
vRz9mLxDOf7T21aKgVs58lq4FAdi+zVfba5vs9e3WLKpvj/C3fSN3KROSEXRxbHR
bBeBZYD+xjKvKd0Zvkyaqg6JdwhUWwdegbAZkD/LDRIuktmO4kUXJtsN5TPp2GTJ
H/Hw9zu36kGXBxXN+l/KVkqgltENlw/pvlDNKa4IPER3uNOLcJgXvBVCLs80haq2
ZQaESInzOXjaN3P93H6Qw+ob+7+/s6h6qEuTl5X/awjeEYCcufBUzpAi42wFL/Nk
DA0ylFDBpdwidpPo2hQ=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDFzCCAf+gAwIBAgIQNr/QDnePc/YH4DjC6I0osjANBgkqhkiG9w0BAQsFADAa
MRgwFgYDVQQDDA9pbnRlcm1lZGlhdGVfY2EwHhcNMjAwNjI1MTgwMjQyWhcNMjAx
MjIyMTgwMjQyWjAaMRgwFgYDVQQDEw8xMjguMjI0LjE1MS4yNDMwggEiMA0GCSqG
SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDEo9EMGM8z++fC4qiMHDMvQobuO92btdbN
tp8ZHeQzaRc8xAE60Jogd6xWfnvdVl18ADXDbSn+Xa5f4CtAS6q3eqAjVqrX5w9W
DRAuStt5gJBjP9L/BtMhFTiNhc4URui2+o2t+E6jW2F6+FJkH72eaPH98+/eyaHO
wQbLOYu6c3rTNqP3OQGkkmQa27JzOfXWp73IBFS9GVKpSNpdQX8OY/u8AY9OlC20
P3fGg85vXiicLdYrwMLmQKkIduFg001ar4piOy+Z13r9nMjD2sdONe6Pf2fKnzSu
qT+MWSclYBK39C6rXwWSb+LgEHlChxInR2dXYa8Dtbjyfzj4g2ERAgMBAAGjWTBX
MA4GA1UdDwEB/wQEAwIFoDAMBgNVHRMBAf8EAjAAMDcGA1UdEQQwMC6CDnJlZ2lz
dHJ5LmxvY2FsghByZWdpc3RyeS5jZW50cmFshwTAqMwBhwSA4JfzMA0GCSqGSIb3
DQEBCwUAA4IBAQC4GwZR1hg6NHOcRRhpoDaqTrMl1wtUCb//b2tr3h/vyw319hiW
yVdgQNob9D+VdRo9moX2bOCiMbXHUaabk0fFxPXwaGVkotC/RDLQS+Hol60kdTNu
Ph1TBsuszu9qZyEp+9qyqyOXpiIlpN8RehOuc8L3qUoix/eb9EqvYS9/L27YXkne
qVisVfbnsPivyU9mm/fERvgdrTua2Fi3JvdyJOr4HNO7T8SCGPOnWg+sz4+35CCS
HFK/FNoFnfDgeCj0sHl3Sv2/nZDSG+N98zUaqNTrkXLeUREiaEf9cO0FLEoNBo2R
nJl8zUCPJ8OzOMRD7/fTuOTtmOcWzlFHuTzJ
-----END CERTIFICATE-----

View File

@ -0,0 +1,64 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAxKPRDBjPM/vnwuKojBwzL0KG7jvdm7XWzbafGR3kM2kXPMQB
OtCaIHesVn573VZdfAA1w20p/l2uX+ArQEuqt3qgI1aq1+cPVg0QLkrbeYCQYz/S
/wbTIRU4jYXOFEbotvqNrfhOo1thevhSZB+9nmjx/fPv3smhzsEGyzmLunN60zaj
9zkBpJJkGtuyczn11qe9yARUvRlSqUjaXUF/DmP7vAGPTpQttD93xoPOb14onC3W
K8DC5kCpCHbhYNNNWq+KYjsvmdd6/ZzIw9rHTjXuj39nyp80rqk/jFknJWASt/Qu
q18Fkm/i4BB5QocSJ0dnV2GvA7W48n84+INhEQIDAQABAoIBADhDp00OzDrTmTzq
Hc/5QawEHReNiZtELHIwDtXg96q+Jyf77V4m0fL4Sxd0mXx7ecRBSyDf22Qs6fgE
7Fs5S1+0dp2l7rxYIMs+caLNSH87ihTb7kOKcF7G86eK4axrnaq9hkyA//895maJ
Jwn/CwKIhLVlVv0oNGQ0Vv1POevfHlbP+KSK8dDRgkStfP+mM3T9yyWttr37RHmv
uvg+R0tL9eZetc8QvEmCy3WnCslfJ9g3NxBjKgxWyQIw99w0IDtQv+uPtNHOvM4w
08zNW//foV0QvhhHbejrjyTTp2LVQ97wOz0S5GfnbmwsfYQagaP735DXzoTWC1mm
SWESkAECgYEA7+Chlop04bf+xLxAPE49tca1+I8ESu4FsEvHs1k1/58BlopGOqiD
/Uf/N0aySNmzv+/CU3klsmnpTZYmY/Su+ELjhY9GC1KLFghHBRVU+cIX53VYY7ca
q69FjxcA8BZjTdYe5hPTHbiWXvzUYOK22XjaI6Hm03atdcd6oW6hUtkCgYEA0ds7
uNSwmP4Bc2FzP1uMlxAlUWhHS513NG1keXk3MKtofrgA1/Sb1FLs9lYQbHWjnvZD
HF5e2opPM8nExqKV9vUNnObW/kBClX3i189Fdt5OgJgoZH/Imc/cXvqMeLepPL4s
TFeScDjsbqaUpUDnu8KOatpLP6M/9aEqjrNwrPkCgYEAoKy8T9PqOo8+TZUzCbH2
z92MYjOZ0n7gvJfl6hIlg1WEgMuaAwyBbJRfNS1bmkSIjFYSukr5nyomJdwfVvC8
inpzYDD53/eoRlfBCnyhcLI7xMPrMs+hQ8KhOxlTfX3hgm+cPsykqtHLl1CPCV6S
z9SNeTSnIpnp0myNjbhGwvECgYAJL9TEeaMlf3Em0OxPqL1V26IWlz8Pw1v1kphk
bPcKjWZsrbdzvMSZneu7uPbNGLVJ/zPe6Q6z+HwRI1MW8wUno4CuDrj4tm1zx+gH
VI6Q7ph3LrIN/D4W5a+bBNLjcf7ZpuWfjfHeMc+/5FD7jwTWBmVfriC/L3FcWmrX
hml3WQKBgGUH5mJIVEbxxe6HaMXlJ6T+Z6Vtx98Q5DC2NvukGiH35WCB/itwy8Se
UGZ9t+gXvSIyktQcQHDHKOIHdQvWPSLCN2jBWhRN8LtxTP1HaBRn6QX99PQ/K7AF
V9O/MHuLI0Ph1brfvSlPTjxXwtqHLZe3dUlfXkaiBWYwFFgrAk0C
-----END RSA PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
MIIDFzCCAf+gAwIBAgIQNr/QDnePc/YH4DjC6I0osjANBgkqhkiG9w0BAQsFADAa
MRgwFgYDVQQDDA9pbnRlcm1lZGlhdGVfY2EwHhcNMjAwNjI1MTgwMjQyWhcNMjAx
MjIyMTgwMjQyWjAaMRgwFgYDVQQDEw8xMjguMjI0LjE1MS4yNDMwggEiMA0GCSqG
SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDEo9EMGM8z++fC4qiMHDMvQobuO92btdbN
tp8ZHeQzaRc8xAE60Jogd6xWfnvdVl18ADXDbSn+Xa5f4CtAS6q3eqAjVqrX5w9W
DRAuStt5gJBjP9L/BtMhFTiNhc4URui2+o2t+E6jW2F6+FJkH72eaPH98+/eyaHO
wQbLOYu6c3rTNqP3OQGkkmQa27JzOfXWp73IBFS9GVKpSNpdQX8OY/u8AY9OlC20
P3fGg85vXiicLdYrwMLmQKkIduFg001ar4piOy+Z13r9nMjD2sdONe6Pf2fKnzSu
qT+MWSclYBK39C6rXwWSb+LgEHlChxInR2dXYa8Dtbjyfzj4g2ERAgMBAAGjWTBX
MA4GA1UdDwEB/wQEAwIFoDAMBgNVHRMBAf8EAjAAMDcGA1UdEQQwMC6CDnJlZ2lz
dHJ5LmxvY2FsghByZWdpc3RyeS5jZW50cmFshwTAqMwBhwSA4JfzMA0GCSqGSIb3
DQEBCwUAA4IBAQC4GwZR1hg6NHOcRRhpoDaqTrMl1wtUCb//b2tr3h/vyw319hiW
yVdgQNob9D+VdRo9moX2bOCiMbXHUaabk0fFxPXwaGVkotC/RDLQS+Hol60kdTNu
Ph1TBsuszu9qZyEp+9qyqyOXpiIlpN8RehOuc8L3qUoix/eb9EqvYS9/L27YXkne
qVisVfbnsPivyU9mm/fERvgdrTua2Fi3JvdyJOr4HNO7T8SCGPOnWg+sz4+35CCS
HFK/FNoFnfDgeCj0sHl3Sv2/nZDSG+N98zUaqNTrkXLeUREiaEf9cO0FLEoNBo2R
nJl8zUCPJ8OzOMRD7/fTuOTtmOcWzlFHuTzJ
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIC2jCCAcKgAwIBAgIRAJx+0xv3wyTCGC/hqxY6aMYwDQYJKoZIhvcNAQELBQAw
EjEQMA4GA1UEAwwHcm9vdF9jYTAeFw0yMDA2MjUxNTEwMDBaFw0yMTA2MjUxNTEw
MDBaMBoxGDAWBgNVBAMMD2ludGVybWVkaWF0ZV9jYTCCASIwDQYJKoZIhvcNAQEB
BQADggEPADCCAQoCggEBAMk0swFLA2iI4MI6QWyi6K3QzPSVUNIw8UHtSi6U3O5s
q750DY2NLfBJwKpROjqwKbG7lwcYG3qDKFK3E1YR5H4q66WT+8J6DyP5poouOwvP
Gn9GYqb+nGbePyL/woFPFmaXGUZpTQTUn5UwyNFX9APkoGqJR0RpW5o5E1Hhx2Nn
8DEzuUc4r45YCChK0NaeW2EobNhGWsNxOypBcXSDwj+zXNdxoToR7By6uScPdO1l
qNetjGq+RCUCYj70+oYDwiIPetF/kkj0rjLO2GxVvLJMAYDO5lBwQRfaIMJF2wNB
yHecVJHAq5CZLHbkIqnilRiPQaJWdTuqn5YPGUzRejECAwEAAaMjMCEwDgYDVR0P
AQH/BAQDAgKkMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAEbA
gaZW06bMjPMKyZ0tRDryQ5ctdRBTOs1SQtu/24DUsKU7wkX9Is06v45RpLlVoSG8
vRz9mLxDOf7T21aKgVs58lq4FAdi+zVfba5vs9e3WLKpvj/C3fSN3KROSEXRxbHR
bBeBZYD+xjKvKd0Zvkyaqg6JdwhUWwdegbAZkD/LDRIuktmO4kUXJtsN5TPp2GTJ
H/Hw9zu36kGXBxXN+l/KVkqgltENlw/pvlDNKa4IPER3uNOLcJgXvBVCLs80haq2
ZQaESInzOXjaN3P93H6Qw+ob+7+/s6h6qEuTl5X/awjeEYCcufBUzpAi42wFL/Nk
DA0ylFDBpdwidpPo2hQ=
-----END CERTIFICATE-----

View File

@ -0,0 +1,63 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEAkKELpZl5mPAgALbxnL7ywdlCUp8TpGJklozGkTcDaDJ3umOS
S8Eu0lQJmTRb8FUaYXAgi5BtTj1rx9Z7HyxbZa58cMB842ZrmXhX0rzNReTdGLhr
hJxmd7UBLcw9CGtHSUkwlsjQzWxAlDXLp9t+vj1pScJQEumxNftqIWYIuX5Ck67X
Q0SSq4zdqdNhaGwCxWkQvDwIoUVX6nYwtFfxPju4WhjnG14AubNlxCU47DK7l6Bc
147cywhu0hCFGGkpBjROoD3JDR0L4hwkvzH/2uMe9zfL1MV13JV6cK/FeCuhc/O2
S3SzI1FY5H8I013rvyBwYU0zNtpQ86l1OzhLuwIDAQABAoIBAC2c7OxsS+JFEQLI
r8isnCkw2O7vxqUYdvrYKSPRQtzQ1dvN3WzQ33BsGp7b2Ychf/FtCIabpS2Ax4Y+
CZSaS7T048A3pOue+J5tSt1muCKr/GL4fshS/yoPxvBnsnP+SXw+ffmAGgI/etqD
YNhurDlZauRdSR294CrTQPSJloTRaNSyfr71HfcZ5tmDKvz9/2W3Tz1e+Gsyid57
HzgQPaPxAnupZkLvQEMEVbGhg/ywhVax+XbNV/vhkIgwUcNmsmxjj23rhR5lrnRm
10/BsEVVGCwSsjeQQEDh9YidfyoCFnCHR8IHWXZaeAGj2Jiv8sds4Q9DUbkov80U
iDJUVfkCgYEAwMK6Qa6jX7ATCFQHmgaDtr1USu8Vjro672UphZGaA3Yv3wPz4FME
DLnr/rgB2QBN3dwPUkWVwOZyiwEBCI/6fUVNmEf9iuzBrCNf+AWrGx0p+ySdHn6l
lpUKYJaYTNa2nyIJD2a8VQONaaCdm3ZL1KIF8uRkjGO5he2BjoQdVDUCgYEAwBPr
vESVJhk3MuqVzuLeEu/iFBuS7Y7eDeTxJFPVR5Anag4ZNiQyhuo7mKo6BT5D1t5K
zM41BW9SxEX4j6XEsRc3pIc0rIiVxj+BaHpyUg6Y5/Xkh4g02xdo76JkkAI2BbGT
J+L8H63Xsp+ZNzTo0+d9cic7c0HTW7Ds/c3NPi8CgYBOkdDLnvx2r1VCqsWicY9U
eB2YoZU+5QfKtohTKkMFsDHZDEpmoE/hRBM+cxBFvEsA6IbSOQzOYOBFanZaYB1w
3EPBk250JwbWPGEMvnq9BYksFgN4/5RNKZjeP+P6RfaQLJZW0cBoQpdse4xiZriI
Bja4h4G4e5mOTn3lclJmFQKBgGXkq3NONojla4DZLOt5MjF9i8L69bCLz4QzZTDO
QCAceqwqGnm8LgmgDElqVCkf5MEBX1DSOxJNAe0Y6UjauULwfBYNm4XDVyC5455X
Bno9QCQdL0Qun0tyWbp1vT8fzSSsFNJd+T2c7QXW4GKG8NQow0VhRopX6xWC/9WL
7UsFAoGAY8agTn3py7aNgK05rW3m/yQ7418nEWT2DxIMc97EAVsXyyEXkLeqiqfB
rUwWqyeLrUVNLa/E1PuK+KEqQQUNuj/Rczo8Y2qSi4GIl5HAocz72jdfCk9L+2NM
/V5umqflAPn0m4Le/qZXP6AsW792HW4oeAiQEsD1xW15kPa0c1Q=
-----END RSA PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
MIIC2jCCAcKgAwIBAgIRAJx+0xv3wyTCGC/hqxY6aMYwDQYJKoZIhvcNAQELBQAw
EjEQMA4GA1UEAwwHcm9vdF9jYTAeFw0yMDA2MjUxNTEwMDBaFw0yMTA2MjUxNTEw
MDBaMBoxGDAWBgNVBAMMD2ludGVybWVkaWF0ZV9jYTCCASIwDQYJKoZIhvcNAQEB
BQADggEPADCCAQoCggEBAMk0swFLA2iI4MI6QWyi6K3QzPSVUNIw8UHtSi6U3O5s
q750DY2NLfBJwKpROjqwKbG7lwcYG3qDKFK3E1YR5H4q66WT+8J6DyP5poouOwvP
Gn9GYqb+nGbePyL/woFPFmaXGUZpTQTUn5UwyNFX9APkoGqJR0RpW5o5E1Hhx2Nn
8DEzuUc4r45YCChK0NaeW2EobNhGWsNxOypBcXSDwj+zXNdxoToR7By6uScPdO1l
qNetjGq+RCUCYj70+oYDwiIPetF/kkj0rjLO2GxVvLJMAYDO5lBwQRfaIMJF2wNB
yHecVJHAq5CZLHbkIqnilRiPQaJWdTuqn5YPGUzRejECAwEAAaMjMCEwDgYDVR0P
AQH/BAQDAgKkMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAEbA
gaZW06bMjPMKyZ0tRDryQ5ctdRBTOs1SQtu/24DUsKU7wkX9Is06v45RpLlVoSG8
vRz9mLxDOf7T21aKgVs58lq4FAdi+zVfba5vs9e3WLKpvj/C3fSN3KROSEXRxbHR
bBeBZYD+xjKvKd0Zvkyaqg6JdwhUWwdegbAZkD/LDRIuktmO4kUXJtsN5TPp2GTJ
H/Hw9zu36kGXBxXN+l/KVkqgltENlw/pvlDNKa4IPER3uNOLcJgXvBVCLs80haq2
ZQaESInzOXjaN3P93H6Qw+ob+7+/s6h6qEuTl5X/awjeEYCcufBUzpAi42wFL/Nk
DA0ylFDBpdwidpPo2hQ=
-----BEGIN CERTIFICATE-----
MIIC3jCCAcagAwIBAgIQWzdMuPwZZbFPButb9XkiazANBgkqhkiG9w0BAQsFADAa
MRgwFgYDVQQDDA9pbnRlcm1lZGlhdGVfY2EwHhcNMjAwNjI1MTUxMDAxWhcNMjAx
MjIyMTUxMDAxWjAaMRgwFgYDVQQDEw8xMjguMjI0LjE1MS4yNDMwggEiMA0GCSqG
SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCQoQulmXmY8CAAtvGcvvLB2UJSnxOkYmSW
jMaRNwNoMne6Y5JLwS7SVAmZNFvwVRphcCCLkG1OPWvH1nsfLFtlrnxwwHzjZmuZ
eFfSvM1F5N0YuGuEnGZ3tQEtzD0Ia0dJSTCWyNDNbECUNcun236+PWlJwlAS6bE1
+2ohZgi5fkKTrtdDRJKrjN2p02FobALFaRC8PAihRVfqdjC0V/E+O7haGOcbXgC5
s2XEJTjsMruXoFzXjtzLCG7SEIUYaSkGNE6gPckNHQviHCS/Mf/a4x73N8vUxXXc
lXpwr8V4K6Fz87ZLdLMjUVjkfwjTXeu/IHBhTTM22lDzqXU7OEu7AgMBAAGjIDAe
MA4GA1UdDwEB/wQEAwIFoDAMBgNVHRMBAf8EAjAAMA0GCSqGSIb3DQEBCwUAA4IB
AQBu0Uzfpw/pX5IgkKMW+Dq3cUWr/tyJen9BLQQ0ryH6AqBSdjOt0Dfof/S9RyBQ
Pj1xPCMRjGz8qcQYRUPg6pgnq/k3kNIgbZf4V+p+9y/bWsEICwinyWDFWH260MIA
Kckeq6Jf3gLWCrPzZ4hASVMcS+0YA4BaxS/EuX0lRn4O2sf2fMb1VKdPqDmWg/Se
IOm6fKO6zQmnvTFKOPwHUX0ZgUiTEEDqzgeeVcmZhM8ZBb8IQbZIwtvgOOeAg98B
tQcpipGjv4Oq1FsoQ395qasf562ZittlaX7NLbMQef8KiiuYieXYAMqQID+hf3sH
DQih77e2tY88orfCKunGt++R
-----END CERTIFICATE-----
-----END CERTIFICATE-----

View File

@ -0,0 +1,63 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEAkKELpZl5mPAgALbxnL7ywdlCUp8TpGJklozGkTcDaDJ3umOS
S8Eu0lQJmTRb8FUaYXAgi5BtTj1rx9Z7HyxbZa58cMB842ZrmXhX0rzNReTdGLhr
hJxmd7UBLcw9CGtHSUkwlsjQzWxAlDXLp9t+vj1pScJQEumxNftqIWYIuX5Ck67X
Q0SSq4zdqdNhaGwCxWkQvDwIoUVX6nYwtFfxPju4WhjnG14AubNlxCU47DK7l6Bc
147cywhu0hCFGGkpBjROoD3JDR0L4hwkvzH/2uMe9zfL1MV13JV6cK/FeCuhc/O2
S3SzI1FY5H8I013rvyBwYU0zNtpQ86l1OzhLuwIDAQABAoIBAC2c7OxsS+JFEQLI
r8isnCkw2O7vxqUYdvrYKSPRQtzQ1dvN3WzQ33BsGp7b2Ychf/FtCIabpS2Ax4Y+
CZSaS7T048A3pOue+J5tSt1muCKr/GL4fshS/yoPxvBnsnP+SXw+ffmAGgI/etqD
YNhurDlZauRdSR294CrTQPSJloTRaNSyfr71HfcZ5tmDKvz9/2W3Tz1e+Gsyid57
HzgQPaPxAnupZkLvQEMEVbGhg/ywhVax+XbNV/vhkIgwUcNmsmxjj23rhR5lrnRm
10/BsEVVGCwSsjeQQEDh9YidfyoCFnCHR8IHWXZaeAGj2Jiv8sds4Q9DUbkov80U
iDJUVfkCgYEAwMK6Qa6jX7ATCFQHmgaDtr1USu8Vjro672UphZGaA3Yv3wPz4FME
DLnr/rgB2QBN3dwPUkWVwOZyiwEBCI/6fUVNmEf9iuzBrCNf+AWrGx0p+ySdHn6l
lpUKYJaYTNa2nyIJD2a8VQONaaCdm3ZL1KIF8uRkjGO5he2BjoQdVDUCgYEAwBPr
vESVJhk3MuqVzuLeEu/iFBuS7Y7eDeTxJFPVR5Anag4ZNiQyhuo7mKo6BT5D1t5K
zM41BW9SxEX4j6XEsRc3pIc0rIiVxj+BaHpyUg6Y5/Xkh4g02xdo76JkkAI2BbGT
J+L8H63Xsp+ZNzTo0+d9cic7c0HTW7Ds/c3NPi8CgYBOkdDLnvx2r1VCqsWicY9U
eB2YoZU+5QfKtohTKkMFsDHZDEpmoE/hRBM+cxBFvEsA6IbSOQzOYOBFanZaYB1w
3EPBk250JwbWPGEMvnq9BYksFgN4/5RNKZjeP+P6RfaQLJZW0cBoQpdse4xiZriI
Bja4h4G4e5mOTn3lclJmFQKBgGXkq3NONojla4DZLOt5MjF9i8L69bCLz4QzZTDO
QCAceqwqGnm8LgmgDElqVCkf5MEBX1DSOxJNAe0Y6UjauULwfBYNm4XDVyC5455X
Bno9QCQdL0Qun0tyWbp1vT8fzSSsFNJd+T2c7QXW4GKG8NQow0VhRopX6xWC/9WL
7UsFAoGAY8agTn3py7aNgK05rW3m/yQ7418nEWT2DxIMc97EAVsXyyEXkLeqiqfB
rUwWqyeLrUVNLa/E1PuK+KEqQQUNuj/Rczo8Y2qSi4GIl5HAocz72jdfCk9L+2NM
/V5umqflAPn0m4Le/qZXP6AsW792HW4oeAiQEsD1xW15kPa0c1Q=
-----END RSA PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
MIIC3jCCAcagAwIBAgIQWzdMuPwZZbFPButb9XkiazANBgkqhkiG9w0BAQsFADAa
MRgwFgYDVQQDDA9pbnRlcm1lZGlhdGVfY2EwHhcNMjAwNjI1MTUxMDAxWhcNMjAx
MjIyMTUxMDAxWjAaMRgwFgYDVQQDEw8xMjguMjI0LjE1MS4yNDMwggEiMA0GCSqG
SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCQoQulmXmY8CAAtvGcvvLB2UJSnxOkYmSW
jMaRNwNoMne6Y5JLwS7SVAmZNFvwVRphcCCLkG1OPWvH1nsfLFtlrnxwwHzjZmuZ
eFfSvM1F5N0YuGuEnGZ3tQEtzD0Ia0dJSTCWyNDNbECUNcun236+PWlJwlAS6bE1
+2ohZgi5fkKTrtdDRJKrjN2p02FobALFaRC8PAihRVfqdjC0V/E+O7haGOcbXgC5
s2XEJTjsMruXoFzXjtzLCG7SEIUYaSkGNE6gPckNHQviHCS/Mf/a4x73N8vUxXXc
lXpwr8V4K6Fz87ZLdLMjUVjkfwjTXeu/IHBhTTM22lDzqXU7OEu7AgMBAAGjIDAe
MA4GA1UdDwEB/wQEAwIFoDAMBgNVHRMBAf8EAjAAMA0GCSqGSIb3DQEBCwUAA4IB
AQBu0Uzfpw/pX5IgkKMW+Dq3cUWr/tyJen9BLQQ0ryH6AqBSdjOt0Dfof/S9RyBQ
Pj1xPCMRjGz8qcQYRUPg6pgnq/k3kNIgbZf4V+p+9y/bWsEICwinyWDFWH260MIA
Kckeq6Jf3gLWCrPzZ4hASVMcS+0YA4BaxS/EuX0lRn4O2sf2fMb1VKdPqDmWg/Se
IOm6fKO6zQmnvTFKOPwHUX0ZgUiTEEDqzgeeVcmZhM8ZBb8IQbZIwtvgOOeAg98B
tQcpipGjv4Oq1FsoQ395qasf562ZittlaX7NLbMQef8KiiuYieXYAMqQID+hf3sH
DQih77e2tY88orfCKunGt++R
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIC2jCCAcKgAwIBAgIRAJx+0xv3wyTCGC/hqxY6aMYwDQYJKoZIhvcNAQELBQAw
EjEQMA4GA1UEAwwHcm9vdF9jYTAeFw0yMDA2MjUxNTEwMDBaFw0yMTA2MjUxNTEw
MDBaMBoxGDAWBgNVBAMMD2ludGVybWVkaWF0ZV9jYTCCASIwDQYJKoZIhvcNAQEB
BQADggEPADCCAQoCggEBAMk0swFLA2iI4MI6QWyi6K3QzPSVUNIw8UHtSi6U3O5s
q750DY2NLfBJwKpROjqwKbG7lwcYG3qDKFK3E1YR5H4q66WT+8J6DyP5poouOwvP
Gn9GYqb+nGbePyL/woFPFmaXGUZpTQTUn5UwyNFX9APkoGqJR0RpW5o5E1Hhx2Nn
8DEzuUc4r45YCChK0NaeW2EobNhGWsNxOypBcXSDwj+zXNdxoToR7By6uScPdO1l
qNetjGq+RCUCYj70+oYDwiIPetF/kkj0rjLO2GxVvLJMAYDO5lBwQRfaIMJF2wNB
yHecVJHAq5CZLHbkIqnilRiPQaJWdTuqn5YPGUzRejECAwEAAaMjMCEwDgYDVR0P
AQH/BAQDAgKkMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAEbA
gaZW06bMjPMKyZ0tRDryQ5ctdRBTOs1SQtu/24DUsKU7wkX9Is06v45RpLlVoSG8
vRz9mLxDOf7T21aKgVs58lq4FAdi+zVfba5vs9e3WLKpvj/C3fSN3KROSEXRxbHR
bBeBZYD+xjKvKd0Zvkyaqg6JdwhUWwdegbAZkD/LDRIuktmO4kUXJtsN5TPp2GTJ
H/Hw9zu36kGXBxXN+l/KVkqgltENlw/pvlDNKa4IPER3uNOLcJgXvBVCLs80haq2
ZQaESInzOXjaN3P93H6Qw+ob+7+/s6h6qEuTl5X/awjeEYCcufBUzpAi42wFL/Nk
DA0ylFDBpdwidpPo2hQ=
-----END CERTIFICATE-----

View File

@ -216,7 +216,7 @@ class ApiCertificateTestCaseMixin(object):
cert = x509.load_pem_x509_certificate(pem_contents[index::],
default_backend())
certs.append(cert)
start = start + index + len(marker)
start = index + len(marker)
return certs
@staticmethod
@ -351,6 +351,169 @@ class ApiCertificatePostTestSuite(ApiCertificateTestCaseMixin,
found_match = True
self.assertTrue(found_match)
# Test successful POST operation to install ssl certificate signed by
# intermediate CA
def test_install_2xcert_1xkey_ssl_certificate(self):
mode = 'ssl'
certfile = os.path.join(os.path.dirname(__file__), "data",
'ssl-cert-2xcert-1xkey-with-key.pem')
in_certs = self.extract_certs_from_pem_file(certfile)
fake_config_certificate_return = []
for index, in_cert in enumerate(in_certs):
is_ca = False if index == 0 else True
fake_config_certificate_return.append(
{'signature': self.get_cert_signature(mode, in_cert),
'not_valid_before': in_cert.not_valid_before,
'not_valid_after': in_cert.not_valid_after,
'is_ca': is_ca})
self.fake_conductor_api.\
setup_config_certificate(fake_config_certificate_return)
data = {'mode': mode}
files = [('file', certfile)]
response = self.post_with_files('%s/%s' % (self.API_PREFIX, 'certificate_install'),
data,
upload_files=files,
headers=self.API_HEADERS,
expect_errors=False)
self.assertEqual(response.status_code, http_client.OK)
resp = json.loads(response.body)
self.assertIn('certificates', resp)
ret_certs = resp.get('certificates')
# The installed cert contains the server cert and the intermediate
# CA cert but the API returns only the server cert, which should match
# the server cert in the cert file (the first one).
self.assertEqual(len(ret_certs), 1)
ret_cert = ret_certs[0]
in_cert = in_certs[0]
self.assertIn('certtype', ret_cert)
self.assertEqual(ret_cert.get('certtype'), mode)
self.assertIn('signature', ret_cert)
self.assertIn('start_date', ret_cert)
self.assertIn('expiry_date', ret_cert)
ret_cert_start_date = str(ret_cert.get('start_date'))
ret_cert_start_date = ret_cert_start_date.replace('+00:00', '')
ret_cert_expiry_date = str(ret_cert.get('expiry_date'))
ret_cert_expiry_date = ret_cert_expiry_date.replace('+00:00', '')
found_match = False
if ret_cert.get('signature') == \
self.get_cert_signature(mode, in_cert) and \
ret_cert_start_date == \
str(in_cert.not_valid_before) and \
ret_cert_expiry_date == \
str(in_cert.not_valid_after):
found_match = True
self.assertTrue(found_match)
# Test POST operation to install ssl certificate signed by intermediate CA,
# but the server cert and intermediate cert in the file is in wrong order.
def test_install_2xcert_1xkey_ssl_certificate_wrong_order(self):
mode = 'ssl'
certfile = os.path.join(os.path.dirname(__file__), "data",
'ssl-cert-2xcert-1xkey-with-key-wrong-order.pem')
data = {'mode': mode}
files = [('file', certfile)]
response = self.post_with_files('%s/%s' % (self.API_PREFIX, 'certificate_install'),
data,
upload_files=files,
headers=self.API_HEADERS,
expect_errors=True)
self.assertTrue(response.body)
resp = json.loads(response.body)
self.assertTrue(resp.get('error'))
fault_string_expected = 'The first cert in the file should not be a ' \
'CA cert'
self.assertIn(fault_string_expected, str(resp.get('error')))
# Test successful POST operation to install docker_registry certificate
# signed by intermediate CA
def test_install_2xcert_1xkey_docker_registry_certificate(self):
mode = 'docker_registry'
certfile = os.path.join(os.path.dirname(__file__), "data",
'docker_registry-cert-2xcert-1xkey-with-key.pem')
in_certs = self.extract_certs_from_pem_file(certfile)
fake_config_certificate_return = []
for index, in_cert in enumerate(in_certs):
is_ca = False if index == 0 else True
fake_config_certificate_return.append(
{'signature': self.get_cert_signature(mode, in_cert),
'not_valid_before': in_cert.not_valid_before,
'not_valid_after': in_cert.not_valid_after,
'is_ca': is_ca})
self.fake_conductor_api.\
setup_config_certificate(fake_config_certificate_return)
data = {'mode': mode}
files = [('file', certfile)]
response = self.post_with_files('%s/%s' % (self.API_PREFIX, 'certificate_install'),
data,
upload_files=files,
headers=self.API_HEADERS,
expect_errors=False)
self.assertEqual(response.status_code, http_client.OK)
resp = json.loads(response.body)
self.assertIn('certificates', resp)
ret_certs = resp.get('certificates')
# The installed cert contains the server cert and the intermediate
# CA cert but the API returns only the server cert, which should match
# the server cert in the cert file (the first one).
self.assertEqual(len(ret_certs), 1)
ret_cert = ret_certs[0]
in_cert = in_certs[0]
self.assertIn('certtype', ret_cert)
self.assertEqual(ret_cert.get('certtype'), mode)
self.assertIn('signature', ret_cert)
self.assertIn('start_date', ret_cert)
self.assertIn('expiry_date', ret_cert)
ret_cert_start_date = str(ret_cert.get('start_date'))
ret_cert_start_date = ret_cert_start_date.replace('+00:00', '')
ret_cert_expiry_date = str(ret_cert.get('expiry_date'))
ret_cert_expiry_date = ret_cert_expiry_date.replace('+00:00', '')
found_match = False
if ret_cert.get('signature') == \
self.get_cert_signature(mode, in_cert) and \
ret_cert_start_date == \
str(in_cert.not_valid_before) and \
ret_cert_expiry_date == \
str(in_cert.not_valid_after):
found_match = True
self.assertTrue(found_match)
# Test POST operation to install docker_registry certificate signed by
# intermediate CA, but the server cert and intermediate cert in the file
# is in wrong order.
def test_install_2xcert_1xkey_docker_registry_certificate_wrong_order(self):
mode = 'docker_registry'
certfile = os.path.join(os.path.dirname(__file__), "data",
'docker_registry-cert-2xcert-1xkey-with-key-wrong-order.pem')
data = {'mode': mode}
files = [('file', certfile)]
response = self.post_with_files('%s/%s' % (self.API_PREFIX, 'certificate_install'),
data,
upload_files=files,
headers=self.API_HEADERS,
expect_errors=True)
self.assertTrue(response.body)
resp = json.loads(response.body)
self.assertTrue(resp.get('error'))
fault_string_expected = 'The first cert in the file should not be a ' \
'CA cert'
self.assertIn(fault_string_expected, str(resp.get('error')))
class ApiCertificateDeleteTestSuite(ApiCertificateTestCaseMixin,
base.FunctionalTest):