New cryptography backend for openssl_certificate (#53924)

* New cryptography backend for openssl_certificate

load_* functions in module_utils/crypto.py now have a backend paramter
which when set to 'cryptography' will return cryptography objects so
they can be used for both pyopenssl and cryptography backends.
Added a select_message_digest function too returning a cryptography
digest hash from `cryptography.hazmat.primitives.hashes`
Added new classes for Cryptography backend

* Run test with various backends.

* Prefixing tests.

* Make sure we have the correct backend available.

* Linting (flake8).

* Moved cryptography import to separate try/except

* Make sure certificate is actually valid at some time in the past.

* Improve error handling.

* Trying to fix validation for cryptography backend.

* Fixed issue with keyUsage test in assertonly

* Fixed CI/Lint issues

* Fix private key problem for OwnCA.

* Cryptography backend doesn't support v2 certs.

* issue an expired cert with command when using cryptography backend

* Added warning when backend is auto and v2 cert is requested

* Bumped min cryptography version to  1.6

* Correctly check for failure when backend is cryptography and cert is v2

* Use self.backend where possible

* Use secp521r1 EC when testing on CentOS6

* Fixed pylint issue

* AcmeCertificate support for both backends

* Review fixes

* Fixed missing '(' when raising error

* Fixed date_fmt loop

* Updated docs and requirements with cryptography

* Add openssl_certificate to changelog.
This commit is contained in:
Andrea Tartaglia 2019-03-22 13:21:23 +00:00 committed by John R Barker
parent 90c092a104
commit 36a790dcde
11 changed files with 1034 additions and 200 deletions

View file

@ -1,3 +1,4 @@
minor_changes:
- "openssl_certificate - now works with both PyOpenSSL and cryptography Python libraries. Autodetection can be overridden with ``select_crypto_backend`` option."
- "openssl_csr - now works with both PyOpenSSL and cryptography Python libraries. Autodetection can be overridden with ``select_crypto_backend`` option."
- "openssl_privatekey - now works with both PyOpenSSL and cryptography Python libraries. Autodetection can be overridden with ``select_crypto_backend`` option."

View file

@ -23,6 +23,16 @@ except ImportError:
# user know that OpenSSL couldn't be found.
pass
try:
from cryptography import x509
from cryptography.hazmat.backends import default_backend as cryptography_backend
from cryptography.hazmat.primitives.serialization import load_pem_private_key
from cryptography.hazmat.primitives import hashes
except ImportError:
# Error handled in the calling module.
pass
import abc
import datetime
import errno
@ -82,70 +92,87 @@ def get_fingerprint(path, passphrase=None):
return None
def load_privatekey(path, passphrase=None, check_passphrase=True):
def load_privatekey(path, passphrase=None, check_passphrase=True, backend='pyopenssl'):
"""Load the specified OpenSSL private key."""
try:
with open(path, 'rb') as b_priv_key_fh:
priv_key_detail = b_priv_key_fh.read()
# First try: try to load with real passphrase (resp. empty string)
# Will work if this is the correct passphrase, or the key is not
# password-protected.
try:
result = crypto.load_privatekey(crypto.FILETYPE_PEM,
priv_key_detail,
to_bytes(passphrase or ''))
except crypto.Error as e:
if len(e.args) > 0 and len(e.args[0]) > 0 and e.args[0][0][2] == 'bad decrypt':
# This happens in case we have the wrong passphrase.
if passphrase is not None:
raise OpenSSLBadPassphraseError('Wrong passphrase provided for private key!')
else:
raise OpenSSLBadPassphraseError('No passphrase provided, but private key is password-protected!')
raise
if check_passphrase:
# Next we want to make sure that the key is actually protected by
# a passphrase (in case we did try the empty string before, make
# sure that the key is not protected by the empty string)
if backend == 'pyopenssl':
# First try: try to load with real passphrase (resp. empty string)
# Will work if this is the correct passphrase, or the key is not
# password-protected.
try:
crypto.load_privatekey(crypto.FILETYPE_PEM,
priv_key_detail,
to_bytes('y' if passphrase == 'x' else 'x'))
if passphrase is not None:
# Since we can load the key without an exception, the
# key isn't password-protected
raise OpenSSLBadPassphraseError('Passphrase provided, but private key is not password-protected!')
except crypto.Error:
if passphrase is None and len(e.args) > 0 and len(e.args[0]) > 0 and e.args[0][0][2] == 'bad decrypt':
# The key is obviously protected by the empty string.
# Don't do this at home (if it's possible at all)...
raise OpenSSLBadPassphraseError('No passphrase provided, but private key is password-protected!')
result = crypto.load_privatekey(crypto.FILETYPE_PEM,
priv_key_detail,
to_bytes(passphrase or ''))
except crypto.Error as e:
if len(e.args) > 0 and len(e.args[0]) > 0 and e.args[0][0][2] == 'bad decrypt':
# This happens in case we have the wrong passphrase.
if passphrase is not None:
raise OpenSSLBadPassphraseError('Wrong passphrase provided for private key!')
else:
raise OpenSSLBadPassphraseError('No passphrase provided, but private key is password-protected!')
raise
if check_passphrase:
# Next we want to make sure that the key is actually protected by
# a passphrase (in case we did try the empty string before, make
# sure that the key is not protected by the empty string)
try:
crypto.load_privatekey(crypto.FILETYPE_PEM,
priv_key_detail,
to_bytes('y' if passphrase == 'x' else 'x'))
if passphrase is not None:
# Since we can load the key without an exception, the
# key isn't password-protected
raise OpenSSLBadPassphraseError('Passphrase provided, but private key is not password-protected!')
except crypto.Error:
if passphrase is None and len(e.args) > 0 and len(e.args[0]) > 0 and e.args[0][0][2] == 'bad decrypt':
# The key is obviously protected by the empty string.
# Don't do this at home (if it's possible at all)...
raise OpenSSLBadPassphraseError('No passphrase provided, but private key is password-protected!')
elif backend == 'cryptography':
try:
result = load_pem_private_key(priv_key_detail,
passphrase,
cryptography_backend())
except TypeError as e:
raise OpenSSLBadPassphraseError('Wrong or empty passphrase provided for private key')
except ValueError as e:
raise OpenSSLBadPassphraseError('Wrong passphrase provided for private key')
return result
except (IOError, OSError) as exc:
raise OpenSSLObjectError(exc)
def load_certificate(path):
def load_certificate(path, backend='pyopenssl'):
"""Load the specified certificate."""
try:
with open(path, 'rb') as cert_fh:
cert_content = cert_fh.read()
return crypto.load_certificate(crypto.FILETYPE_PEM, cert_content)
if backend == 'pyopenssl':
return crypto.load_certificate(crypto.FILETYPE_PEM, cert_content)
elif backend == 'cryptography':
return x509.load_pem_x509_certificate(cert_content, cryptography_backend())
except (IOError, OSError) as exc:
raise OpenSSLObjectError(exc)
def load_certificate_request(path):
def load_certificate_request(path, backend='pyopenssl'):
"""Load the specified certificate signing request."""
try:
with open(path, 'rb') as csr_fh:
csr_content = csr_fh.read()
return crypto.load_certificate_request(crypto.FILETYPE_PEM, csr_content)
except (IOError, OSError) as exc:
raise OpenSSLObjectError(exc)
if backend == 'pyopenssl':
return crypto.load_certificate_request(crypto.FILETYPE_PEM, csr_content)
elif backend == 'cryptography':
return x509.load_pem_x509_csr(csr_content, cryptography_backend())
def parse_name_field(input_dict):
@ -192,6 +219,21 @@ def convert_relative_to_datetime(relative_time_string):
return datetime.datetime.utcnow() - offset
def select_message_digest(digest_string):
digest = None
if digest_string == 'sha256':
digest = hashes.SHA256()
elif digest_string == 'sha384':
digest = hashes.SHA384()
elif digest_string == 'sha512':
digest = hashes.SHA512()
elif digest_string == 'sha1':
digest = hashes.SHA1()
elif digest_string == 'md5':
digest = hashes.MD5()
return digest
@six.add_metaclass(abc.ABCMeta)
class OpenSSLObject(object):

View file

@ -28,9 +28,11 @@ description:
- Many properties that can be specified in this module are for validation of an
existing or newly generated certificate. The proper place to specify them, if you
want to receive a certificate with these properties is a CSR (Certificate Signing Request).
- It uses the pyOpenSSL python library to interact with OpenSSL.
- It uses the pyOpenSSL or cryptography python library to interact with OpenSSL.
- If both the cryptography and PyOpenSSL libraries are available (and meet the minimum version requirements)
cryptography will be preferred as a backend over PyOpenSSL (unless the backend is forced with C(select_crypto_backend))
requirements:
- python-pyOpenSSL >= 0.15 (if using C(selfsigned) or C(assertonly) provider)
- PyOpenSSL >= 0.15 or cryptography >= 1.6 (if using C(selfsigned) or C(assertonly) provider)
- acme-tiny (if using the C(acme) provider)
author:
- Yanis Guenane (@Spredzy)
@ -346,6 +348,17 @@ options:
default: no
aliases: [ subjectAltName_strict ]
select_crypto_backend:
description:
- Determines which crypto backend to use.
- The default choice is C(auto), which tries to use C(cryptography) if available, and falls back to C(pyopenssl).
- If set to C(pyopenssl), will try to use the L(pyOpenSSL,https://pypi.org/project/pyOpenSSL/) library.
- If set to C(cryptography), will try to use the L(cryptography,https://cryptography.io/) library.
type: str
default: auto
choices: [ auto, cryptography, pyopenssl ]
version_added: "2.8"
extends_documentation_fragment: files
notes:
- All ASN.1 TIME values should be specified following the YYYYMMDDHHMMSSZ pattern.
@ -504,20 +517,39 @@ from random import randint
import datetime
import os
import traceback
from distutils.version import LooseVersion
from ansible.module_utils import crypto as crypto_utils
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
from ansible.module_utils._text import to_native, to_bytes
from ansible.module_utils._text import to_native, to_bytes, to_text
MINIMAL_CRYPTOGRAPHY_VERSION = '1.6'
MINIMAL_PYOPENSSL_VERSION = '0.15'
PYOPENSSL_IMP_ERR = None
try:
import OpenSSL
from OpenSSL import crypto
PYOPENSSL_VERSION = LooseVersion(OpenSSL.__version__)
except ImportError:
PYOPENSSL_IMP_ERR = traceback.format_exc()
pyopenssl_found = False
PYOPENSSL_FOUND = False
else:
pyopenssl_found = True
PYOPENSSL_FOUND = True
CRYPTOGRAPHY_IMP_ERR = None
try:
import cryptography
from cryptography import x509
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.serialization import Encoding
from cryptography.x509 import NameAttribute, Name
CRYPTOGRAPHY_VERSION = LooseVersion(cryptography.__version__)
except ImportError:
CRYPTOGRAPHY_IMP_ERR = traceback.format_exc()
CRYPTOGRAPHY_FOUND = False
else:
CRYPTOGRAPHY_FOUND = True
class CertificateError(crypto_utils.OpenSSLObjectError):
@ -526,7 +558,7 @@ class CertificateError(crypto_utils.OpenSSLObjectError):
class Certificate(crypto_utils.OpenSSLObject):
def __init__(self, module):
def __init__(self, module, backend):
super(Certificate, self).__init__(
module.params['path'],
module.params['state'],
@ -541,6 +573,7 @@ class Certificate(crypto_utils.OpenSSLObject):
self.cert = None
self.privatekey = None
self.csr = None
self.backend = backend
self.module = module
def get_relative_time_option(self, input_string, input_name):
@ -548,37 +581,55 @@ class Certificate(crypto_utils.OpenSSLObject):
or an ASN1 formatted string is provided."""
result = input_string
if result.startswith("+") or result.startswith("-"):
result = crypto_utils.convert_relative_to_datetime(
result).strftime("%Y%m%d%H%M%SZ")
result_datetime = crypto_utils.convert_relative_to_datetime(
result)
if self.backend == 'pyopenssl':
return result_datetime.strftime("%Y%m%d%H%M%SZ")
elif self.backend == 'cryptography':
return result_datetime
if result is None:
raise CertificateError(
'The timespec "%s" for %s is not valid' %
input_string, input_name)
if self.backend == 'cryptography':
for date_fmt in ['%Y%m%d%H%M%SZ', '%Y%m%d%H%MZ', '%Y%m%d%H%M%S%z', '%Y%m%d%H%M%z']:
try:
result = datetime.datetime.strptime(input_string, date_fmt)
break
except ValueError:
pass
if not isinstance(result, datetime.datetime):
raise CertificateError(
'The time spec "%s" for %s is invalid' %
(input_string, input_name)
)
return result
def check(self, module, perms_required=True):
"""Ensure the resource is in its desired state."""
def _validate_privatekey(self):
if self.backend == 'pyopenssl':
ctx = OpenSSL.SSL.Context(OpenSSL.SSL.TLSv1_2_METHOD)
ctx.use_privatekey(self.privatekey)
ctx.use_certificate(self.cert)
try:
ctx.check_privatekey()
return True
except OpenSSL.SSL.Error:
return False
elif self.backend == 'cryptography':
return self.cert.public_key().public_numbers() == self.privatekey.public_key().public_numbers()
state_and_perms = super(Certificate, self).check(module, perms_required)
def _validate_privatekey():
if self.privatekey_path:
ctx = OpenSSL.SSL.Context(OpenSSL.SSL.TLSv1_2_METHOD)
ctx.use_privatekey(self.privatekey)
ctx.use_certificate(self.cert)
try:
ctx.check_privatekey()
return True
except OpenSSL.SSL.Error:
return False
def _validate_csr():
def _validate_csr(self):
if self.backend == 'pyopenssl':
# Verify that CSR is signed by certificate's private key
try:
self.csr.verify(self.cert.get_pubkey())
except OpenSSL.crypto.Error:
return False
# Check subject
if self.csr.get_subject() != self.cert.get_subject():
return False
# Check extensions
csr_extensions = self.csr.get_extensions()
cert_extension_count = self.cert.get_extension_count()
if len(csr_extensions) != cert_extension_count:
@ -589,35 +640,156 @@ class Certificate(crypto_utils.OpenSSLObject):
if cert_extension.get_data() != list(csr_extension)[0].get_data():
return False
return True
elif self.backend == 'cryptography':
# Verify that CSR is signed by certificate's private key
if not self.csr.is_signature_valid:
return False
if self.csr.public_key().public_numbers() != self.cert.public_key().public_numbers():
return False
# Check subject
if self.csr.subject != self.cert.subject:
return False
# Check extensions
cert_exts = self.cert.extensions
csr_exts = self.csr.extensions
if len(cert_exts) != len(csr_exts):
return False
for cert_ext in cert_exts:
try:
csr_ext = csr_exts.get_extension_for_oid(cert_ext.oid)
if cert_ext != csr_ext:
return False
except cryptography.x509.ExtensionNotFound as e:
return False
return True
def check(self, module, perms_required=True):
"""Ensure the resource is in its desired state."""
state_and_perms = super(Certificate, self).check(module, perms_required)
if not state_and_perms:
return False
self.cert = crypto_utils.load_certificate(self.path)
self.cert = crypto_utils.load_certificate(self.path, backend=self.backend)
if self.privatekey_path:
try:
self.privatekey = crypto_utils.load_privatekey(
self.privatekey_path,
self.privatekey_passphrase
self.privatekey_passphrase,
backend=self.backend
)
except crypto_utils.OpenSSLBadPassphraseError as exc:
raise CertificateError(exc)
return _validate_privatekey()
return self._validate_privatekey()
if self.csr_path:
self.csr = crypto_utils.load_certificate_request(self.csr_path)
if not _validate_csr():
self.csr = crypto_utils.load_certificate_request(self.csr_path, backend=self.backend)
if not self._validate_csr():
return False
return True
class SelfSignedCertificateCryptography(Certificate):
"""Generate the self-signed certificate, using the cryptography backend"""
def __init__(self, module):
super(SelfSignedCertificateCryptography, self).__init__(module, 'cryptography')
self.notBefore = self.get_relative_time_option(module.params['selfsigned_not_before'], 'selfsigned_not_before')
self.notAfter = self.get_relative_time_option(module.params['selfsigned_not_after'], 'selfsigned_not_after')
self.digest = crypto_utils.select_message_digest(module.params['selfsigned_digest'])
self.version = module.params['selfsigned_version']
self.serial_number = x509.random_serial_number()
self.csr = crypto_utils.load_certificate_request(self.csr_path, backend=self.backend)
self._module = module
try:
self.privatekey = crypto_utils.load_privatekey(
self.privatekey_path, self.privatekey_passphrase, backend=self.backend
)
except crypto_utils.OpenSSLBadPassphraseError as exc:
module.fail_json(msg=to_native(exc))
if self.digest is None:
raise CertificateError(
'The digest %s is not supported with the cryptography backend' % module.params['selfsigned_digest']
)
def generate(self, module):
if not os.path.exists(self.privatekey_path):
raise CertificateError(
'The private key %s does not exist' % self.privatekey_path
)
if not os.path.exists(self.csr_path):
raise CertificateError(
'The certificate signing request file %s does not exist' % self.csr_path
)
if not self.check(module, perms_required=False) or self.force:
try:
cert_builder = x509.CertificateBuilder()
cert_builder = cert_builder.subject_name(self.csr.subject)
cert_builder = cert_builder.issuer_name(self.csr.subject)
cert_builder = cert_builder.serial_number(self.serial_number)
cert_builder = cert_builder.not_valid_before(self.notBefore)
cert_builder = cert_builder.not_valid_after(self.notAfter)
cert_builder = cert_builder.public_key(self.privatekey.public_key())
for extension in self.csr.extensions:
cert_builder = cert_builder.add_extension(extension.value, critical=extension.critical)
except ValueError as e:
raise CertificateError(str(e))
certificate = cert_builder.sign(
private_key=self.privatekey, algorithm=self.digest,
backend=default_backend()
)
self.cert = certificate
try:
with open(self.path, 'wb') as cert_file:
cert_file.write(certificate.public_bytes(Encoding.PEM))
except Exception as exc:
raise CertificateError(exc)
self.changed = True
else:
self.cert = crypto_utils.load_certificate(self.path, backend=self.backend)
file_args = module.load_file_common_arguments(module.params)
if module.set_fs_attributes_if_different(file_args, False):
self.changed = True
def dump(self, check_mode=False):
result = {
'changed': self.changed,
'filename': self.path,
'privatekey': self.privatekey_path,
'csr': self.csr_path
}
if check_mode:
result.update({
'notBefore': self.notBefore.strftime("%Y%m%d%H%M%SZ"),
'notAfter': self.notAfter.strftime("%Y%m%d%H%M%SZ"),
'serial_number': self.serial_number,
})
else:
result.update({
'notBefore': self.cert.not_valid_before.strftime("%Y%m%d%H%M%SZ"),
'notAfter': self.cert.not_valid_after.strftime("%Y%m%d%H%M%SZ"),
'serial_number': self.cert.serial_number,
})
return result
class SelfSignedCertificate(Certificate):
"""Generate the self-signed certificate."""
def __init__(self, module):
super(SelfSignedCertificate, self).__init__(module)
super(SelfSignedCertificate, self).__init__(module, 'pyopenssl')
self.notBefore = self.get_relative_time_option(module.params['selfsigned_not_before'], 'selfsigned_not_before')
self.notAfter = self.get_relative_time_option(module.params['selfsigned_not_after'], 'selfsigned_not_after')
self.digest = module.params['selfsigned_digest']
@ -694,11 +866,108 @@ class SelfSignedCertificate(Certificate):
return result
class OwnCACertificateCryptography(Certificate):
"""Generate the own CA certificate. Using the cryptography backend"""
def __init__(self, module):
super(OwnCACertificateCryptography, self).__init__(module, 'cryptography')
self.notBefore = self.get_relative_time_option(module.params['ownca_not_before'], 'ownca_not_before')
self.notAfter = self.get_relative_time_option(module.params['ownca_not_after'], 'ownca_not_after')
self.digest = crypto_utils.select_message_digest(module.params['ownca_digest'])
self.version = module.params['ownca_version']
self.serial_number = x509.random_serial_number()
self.ca_cert_path = module.params['ownca_path']
self.ca_privatekey_path = module.params['ownca_privatekey_path']
self.ca_privatekey_passphrase = module.params['ownca_privatekey_passphrase']
self.csr = crypto_utils.load_certificate_request(self.csr_path, backend=self.backend)
self.ca_cert = crypto_utils.load_certificate(self.ca_cert_path, backend=self.backend)
try:
self.ca_private_key = crypto_utils.load_privatekey(
self.ca_privatekey_path, self.ca_privatekey_passphrase, backend=self.backend
)
except crypto_utils.OpenSSLBadPassphraseError as exc:
module.fail_json(msg=str(exc))
def generate(self, module):
if not os.path.exists(self.ca_cert_path):
raise CertificateError(
'The CA certificate %s does not exist' % self.ca_cert_path
)
if not os.path.exists(self.ca_privatekey_path):
raise CertificateError(
'The CA private key %s does not exist' % self.ca_privatekey_path
)
if not os.path.exists(self.csr_path):
raise CertificateError(
'The certificate signing request file %s does not exist' % self.csr_path
)
if not self.check(module, perms_required=False) or self.force:
cert_builder = x509.CertificateBuilder()
cert_builder = cert_builder.subject_name(self.csr.subject)
cert_builder = cert_builder.issuer_name(self.ca_cert.subject)
cert_builder = cert_builder.serial_number(self.serial_number)
cert_builder = cert_builder.not_valid_before(self.notBefore)
cert_builder = cert_builder.not_valid_after(self.notAfter)
cert_builder = cert_builder.public_key(self.csr.public_key())
for extension in self.csr.extensions:
cert_builder = cert_builder.add_extension(extension.value, critical=extension.critical)
certificate = cert_builder.sign(
private_key=self.ca_private_key, algorithm=self.digest,
backend=default_backend()
)
self.cert = certificate
try:
with open(self.path, 'wb') as cert_file:
cert_file.write(certificate.public_bytes(Encoding.PEM))
except Exception as exc:
raise CertificateError(exc)
self.changed = True
else:
self.cert = crypto_utils.load_certificate(self.path, backend=self.backend)
file_args = module.load_file_common_arguments(module.params)
if module.set_fs_attributes_if_different(file_args, False):
self.changed = True
def dump(self, check_mode=False):
result = {
'changed': self.changed,
'filename': self.path,
'privatekey': self.privatekey_path,
'csr': self.csr_path,
'ca_cert': self.ca_cert_path,
'ca_privatekey': self.ca_privatekey_path
}
if check_mode:
result.update({
'notBefore': self.notBefore.strftime("%Y%m%d%H%M%SZ"),
'notAfter': self.notAfter.strftime("%Y%m%d%H%M%SZ"),
'serial_number': self.serial_number,
})
else:
result.update({
'notBefore': self.cert.not_valid_before.strftime("%Y%m%d%H%M%SZ"),
'notAfter': self.cert.not_valid_after.strftime("%Y%m%d%H%M%SZ"),
'serial_number': self.cert.serial_number,
})
return result
class OwnCACertificate(Certificate):
"""Generate the own CA certificate."""
def __init__(self, module):
super(OwnCACertificate, self).__init__(module)
super(OwnCACertificate, self).__init__(module, 'pyopenssl')
self.notBefore = self.get_relative_time_option(module.params['ownca_not_before'], 'ownca_not_before')
self.notAfter = self.get_relative_time_option(module.params['ownca_not_after'], 'ownca_not_after')
self.digest = module.params['ownca_digest']
@ -786,11 +1055,408 @@ class OwnCACertificate(Certificate):
return result
class AssertOnlyCertificateCryptography(Certificate):
"""Validate the supplied cert, using the cryptography backend"""
def __init__(self, module):
super(AssertOnlyCertificateCryptography, self).__init__(module, 'cryptography')
self.signature_algorithms = module.params['signature_algorithms']
if module.params['subject']:
self.subject = crypto_utils.parse_name_field(module.params['subject'])
else:
self.subject = []
self.subject_strict = module.params['subject_strict']
if module.params['issuer']:
self.issuer = crypto_utils.parse_name_field(module.params['issuer'])
else:
self.issuer = []
self.issuer_strict = module.params['issuer_strict']
self.has_expired = module.params['has_expired']
self.version = module.params['version']
self.keyUsage = module.params['key_usage']
self.keyUsage_strict = module.params['key_usage_strict']
self.extendedKeyUsage = module.params['extended_key_usage']
self.extendedKeyUsage_strict = module.params['extended_key_usage_strict']
self.subjectAltName = module.params['subject_alt_name']
self.subjectAltName_strict = module.params['subject_alt_name_strict']
self.notBefore = module.params['not_before'],
self.notAfter = module.params['not_after'],
self.valid_at = module.params['valid_at'],
self.invalid_at = module.params['invalid_at'],
self.valid_in = module.params['valid_in'],
self.message = []
def _get_name_oid(self, id):
if id in ('CN', 'commonName'):
return cryptography.x509.oid.NameOID.COMMON_NAME
if id in ('C', 'countryName'):
return cryptography.x509.oid.NameOID.COUNTRY_NAME
if id in ('L', 'localityName'):
return cryptography.x509.oid.NameOID.LOCALITY_NAME
if id in ('ST', 'stateOrProvinceName'):
return cryptography.x509.oid.NameOID.STATE_OR_PROVINCE_NAME
if id in ('street', 'streetAddress'):
return cryptography.x509.oid.NameOID.STREET_ADDRESS
if id in ('O', 'organizationName'):
return cryptography.x509.oid.NameOID.ORGANIZATION_NAME
if id in ('OU', 'organizationalUnitName'):
return cryptography.x509.oid.NameOID.ORGANIZATIONAL_UNIT_NAME
if id in ('serialNumber', ):
return cryptography.x509.oid.NameOID.SERIAL_NUMBER
if id in ('SN', 'surname'):
return cryptography.x509.oid.NameOID.SURNAME
if id in ('GN', 'givenName'):
return cryptography.x509.oid.NameOID.GIVEN_NAME
if id in ('title', ):
return cryptography.x509.oid.NameOID.TITLE
if id in ('generationQualifier', ):
return cryptography.x509.oid.NameOID.GENERATION_QUALIFIER
if id in ('x500UniqueIdentifier', ):
return cryptography.x509.oid.NameOID.X500_UNIQUE_IDENTIFIER
if id in ('dnQualifier', ):
return cryptography.x509.oid.NameOID.DN_QUALIFIER
if id in ('pseudonym', ):
return cryptography.x509.oid.NameOID.PSEUDONYM
if id in ('UID', 'userId'):
return cryptography.x509.oid.NameOID.USER_ID
if id in ('DC', 'domainComponent'):
return cryptography.x509.oid.NameOID.DOMAIN_COMPONENT
if id in ('emailAddress', ):
return cryptography.x509.oid.NameOID.EMAIL_ADDRESS
if id in ('jurisdictionC', 'jurisdictionCountryName'):
return cryptography.x509.oid.NameOID.JURISDICTION_COUNTRY_NAME
if id in ('jurisdictionL', 'jurisdictionLocalityName'):
return cryptography.x509.oid.NameOID.JURISDICTION_LOCALITY_NAME
if id in ('jurisdictionST', 'jurisdictionStateOrProvinceName'):
return cryptography.x509.oid.NameOID.JURISDICTION_STATE_OR_PROVINCE_NAME
if id in ('businessCategory', ):
return cryptography.x509.oid.NameOID.BUSINESS_CATEGORY
if id in ('postalAddress', ):
return cryptography.x509.oid.NameOID.POSTAL_ADDRESS
if id in ('postalCode', ):
return cryptography.x509.oid.NameOID.POSTAL_CODE
def _get_san(self, name):
if name.startswith('DNS:'):
return cryptography.x509.DNSName(to_native(name[4:]))
if name.startswith('IP:'):
return cryptography.x509.IPAddress(to_native(name[3:]))
if name.startswith('email:'):
return cryptography.x509.RFC822Name(to_native(name[6:]))
if name.startswith('URI:'):
return cryptography.x509.UniformResourceIdentifier(to_native(name[4:]))
if name.startswith('DirName:'):
return cryptography.x509.DirectoryName(to_native(name[8:]))
if ':' not in name:
raise CertificateError('Cannot parse Subject Alternative Name "{0}" (forgot "DNS:" prefix?)'.format(name))
raise CertificateError('Cannot parse Subject Alternative Name "{0}" (potentially unsupported by cryptography backend)'.format(name))
def _get_keyusage(self, usage):
if usage in ('Digital Signature', 'digitalSignature'):
return 'digital_signature'
if usage in ('Non Repudiation', 'nonRepudiation'):
return 'content_commitment'
if usage in ('Key Encipherment', 'keyEncipherment'):
return 'key_encipherment'
if usage in ('Data Encipherment', 'dataEncipherment'):
return 'data_encipherment'
if usage in ('Key Agreement', 'keyAgreement'):
return 'key_agreement'
if usage in ('Certificate Sign', 'keyCertSign'):
return 'key_cert_sign'
if usage in ('CRL Sign', 'cRLSign'):
return 'crl_sign'
if usage in ('Encipher Only', 'encipherOnly'):
return 'encipher_only'
if usage in ('Decipher Only', 'decipherOnly'):
return 'decipher_only'
raise CertificateError('Unknown key usage "{0}"'.format(usage))
def _get_ext_keyusage(self, usage):
if usage in ('serverAuth', 'TLS Web Server Authentication'):
return cryptography.x509.oid.ExtendedKeyUsageOID.SERVER_AUTH
if usage in ('clientAuth', 'TLS Web Client Authentication'):
return cryptography.x509.oid.ExtendedKeyUsageOID.CLIENT_AUTH
if usage in ('codeSigning', 'Code Signing'):
return cryptography.x509.oid.ExtendedKeyUsageOID.CODE_SIGNING
if usage in ('emailProtection', 'E-mail Protection'):
return cryptography.x509.oid.ExtendedKeyUsageOID.EMAIL_PROTECTION
if usage in ('timeStamping', 'Time Stamping'):
return cryptography.x509.oid.ExtendedKeyUsageOID.TIME_STAMPING
if usage in ('OCSPSigning', 'OCSP Signing'):
return cryptography.x509.oid.ExtendedKeyUsageOID.OCSP_SIGNING
if usage in ('anyExtendedKeyUsage', 'Any Extended Key Usage'):
return cryptography.x509.oid.ExtendedKeyUsageOID.ANY_EXTENDED_KEY_USAGE
if usage in ('qcStatements', ):
return cryptography.x509.oid.ObjectIdentifier("1.3.6.1.5.5.7.1.3")
if usage in ('DVCS', ):
return cryptography.x509.oid.ObjectIdentifier("1.3.6.1.5.5.7.3.10")
if usage in ('IPSec User', 'ipsecUser'):
return cryptography.x509.oid.ObjectIdentifier("1.3.6.1.5.5.7.3.7")
if usage in ('Biometric Info', 'biometricInfo'):
return cryptography.x509.oid.ObjectIdentifier("1.3.6.1.5.5.7.1.2")
# FIXME need some more, probably all from https://www.iana.org/assignments/smi-numbers/smi-numbers.xhtml#smi-numbers-1.3.6.1.5.5.7.3
raise CertificateError('Unknown extended key usage "{0}"'.format(usage))
def _get_basic_constraints(self, constraints):
ca = False
path_length = None
if constraints:
for constraint in constraints:
if constraint.startswith('CA:'):
if constraint == 'CA:TRUE':
ca = True
elif constraint == 'CA:FALSE':
ca = False
else:
raise CertificateError('Unknown basic constraint value "{0}" for CA'.format(constraint[3:]))
elif constraint.startswith('pathlen:'):
v = constraint[len('pathlen:'):]
try:
path_length = int(v)
except Exception as e:
raise CertificateError('Cannot parse path length constraint "{0}" ({1})'.format(v, e))
else:
raise CertificateError('Unknown basic constraint "{0}"'.format(constraint))
return ca, path_length
def _parse_key_usage(self):
params = dict(
digital_signature=False,
content_commitment=False,
key_encipherment=False,
data_encipherment=False,
key_agreement=False,
key_cert_sign=False,
crl_sign=False,
encipher_only=False,
decipher_only=False,
)
for usage in self.keyUsage:
params[self._get_keyusage(usage)] = True
return params
def assertonly(self):
self.cert = crypto_utils.load_certificate(self.path, backend=self.backend)
def _validate_signature_algorithms():
if self.signature_algorithms:
if self.cert.signature_algorithm_oid._name not in self.signature_algorithms:
self.message.append(
'Invalid signature algorithm (got %s, expected one of %s)' %
(self.cert.signature_algorithm_oid._name, self.signature_algorithms)
)
def _validate_subject():
if self.subject:
expected_subject = Name([NameAttribute(oid=self._get_name_oid(sub[0]), value=to_text(sub[1]))
for sub in self.subject])
cert_subject = self.cert.subject
if (not self.subject_strict and not all(x in cert_subject for x in expected_subject)) or \
(self.subject_strict and not set(expected_subject) == set(cert_subject)):
self.message.append(
'Invalid subject component (got %s, expected all of %s to be present)' %
(cert_subject, expected_subject)
)
def _validate_issuer():
if self.issuer:
expected_issuer = Name([NameAttribute(oid=self._get_name_oid(iss[0]), value=to_text(iss[1]))
for iss in self.issuer])
cert_issuer = self.cert.issuer
if (not self.issuer_strict and not all(x in cert_issuer for x in expected_issuer)) or \
(self.issuer_strict and not set(expected_issuer) == set(cert_issuer)):
self.message.append(
'Invalid issuer component (got %s, expected all of %s to be present)' % (cert_issuer, self.issuer)
)
def _validate_has_expired():
cert_not_after = self.cert.not_valid_after
cert_expired = cert_not_after < datetime.datetime.utcnow()
if self.has_expired != cert_expired:
self.message.append(
'Certificate expiration check failed (certificate expiration is %s, expected %s)' % (cert_expired, self.has_expired)
)
def _validate_version():
# FIXME
if self.version:
expected_version = x509.Version(int(self.version) - 1)
if expected_version != self.cert.version:
self.message.append(
'Invalid certificate version number (got %s, expected %s)' % (self.cert.version, self.version)
)
def _validate_keyUsage():
if self.keyUsage:
try:
current_keyusage = self.cert.extensions.get_extension_for_class(x509.KeyUsage).value
expected_keyusage = x509.KeyUsage(**self._parse_key_usage())
test_keyusage = dict(
digital_signature=current_keyusage.digital_signature,
content_commitment=current_keyusage.content_commitment,
key_encipherment=current_keyusage.key_encipherment,
data_encipherment=current_keyusage.data_encipherment,
key_agreement=current_keyusage.key_agreement,
key_cert_sign=current_keyusage.key_cert_sign,
crl_sign=current_keyusage.crl_sign,
)
if test_keyusage['key_agreement']:
test_keyusage.update(dict(
encipher_only=current_keyusage.encipher_only,
decipher_only=current_keyusage.decipher_only
))
else:
test_keyusage.update(dict(
encipher_only=False,
decipher_only=False
))
if (not self.keyUsage_strict and not all(self._parse_key_usage()[x] == test_keyusage[x] for x in self._parse_key_usage())) or \
(self.keyUsage_strict and current_keyusage != expected_keyusage):
self.message.append(
'Invalid keyUsage components (got %s, expected all of %s to be present)' %
([x for x in test_keyusage if x is True], [x for x in self.keyUsage if x is True])
)
except cryptography.x509.ExtensionNotFound:
self.message.append('Found no keyUsage extension')
def _validate_extendedKeyUsage():
if self.extendedKeyUsage:
try:
current_ext_keyusage = self.cert.extensions.get_extension_for_class(x509.ExtendedKeyUsage).value
usages = [self._get_ext_keyusage(usage) for usage in self.extendedKeyUsage]
expected_ext_keyusage = x509.ExtendedKeyUsage(usages)
if (not self.extendedKeyUsage_strict and not all(x in expected_ext_keyusage for x in current_ext_keyusage)) or \
(self.extendedKeyUsage_strict and not current_ext_keyusage == expected_ext_keyusage):
self.message.append(
'Invalid extendedKeyUsage component (got %s, expected all of %s to be present)' % ([xku.value for xku in current_ext_keyusage],
[exku.value for exku in expected_ext_keyusage])
)
except cryptography.x509.ExtensionNotFound:
self.message.append('Found no extendedKeyUsage extension')
def _validate_subjectAltName():
if self.subjectAltName:
try:
current_san = self.cert.extensions.get_extension_for_class(x509.SubjectAlternativeName).value
expected_san = [self._get_san(san) for san in self.subjectAltName]
if (not self.subjectAltName_strict and not all(x in current_san for x in expected_san)) or \
(self.subjectAltName_strict and not set(current_san) == set(expected_san)):
self.message.append(
'Invalid subjectAltName component (got %s, expected all of %s to be present)' %
(current_san, self.subjectAltName)
)
except cryptography.x509.ExtensionNotFound:
self.message.append('Found no subjectAltName extension')
def _validate_notBefore():
if self.notBefore[0]:
# try:
if self.cert.not_valid_before != self.get_relative_time_option(self.notBefore[0], 'not_before'):
self.message.append(
'Invalid notBefore component (got %s, expected %s to be present)' % (self.cert.not_valid_before, self.notBefore)
)
# except AttributeError:
# self.message.append(str(self.notBefore))
def _validate_notAfter():
if self.notAfter[0]:
if self.cert.not_valid_after != self.get_relative_time_option(self.notAfter[0], 'not_after'):
self.message.append(
'Invalid notAfter component (got %s, expected %s to be present)' % (self.cert.not_valid_after, self.notAfter)
)
def _validate_valid_at():
if self.valid_at[0]:
rt = self.get_relative_time_option(self.valid_at[0], 'valid_at')
if not (self.cert.not_valid_before <= rt <= self.cert.not_valid_after):
self.message.append(
'Certificate is not valid for the specified date (%s) - notBefore: %s - notAfter: %s' % (self.valid_at,
self.cert.not_valid_before,
self.cert.not_valid_after)
)
def _validate_invalid_at():
if self.invalid_at[0]:
if (self.get_relative_time_option(self.invalid_at[0], 'invalid_at') > self.cert.not_valid_before) \
or (self.get_relative_time_option(self.invalid_at, 'invalid_at') >= self.cert.not_valid_after):
self.message.append(
'Certificate is not invalid for the specified date (%s) - notBefore: %s - notAfter: %s' % (self.invalid_at,
self.cert.not_valid_before,
self.cert.not_valid_after)
)
def _validate_valid_in():
if self.valid_in[0]:
if not self.valid_in[0].startswith("+") and not self.valid_in[0].startswith("-"):
try:
int(self.valid_in[0])
except ValueError:
raise CertificateError(
'The supplied value for "valid_in" (%s) is not an integer or a valid timespec' % self.valid_in)
self.valid_in = "+" + self.valid_in + "s"
valid_in_date = self.get_relative_time_option(self.valid_in[0], "valid_in")
if not self.cert.not_valid_before <= valid_in_date <= self.cert.not_valid_after:
self.message.append(
'Certificate is not valid in %s from now (that would be %s) - notBefore: %s - notAfter: %s'
% (self.valid_in, valid_in_date,
self.cert.not_valid_before,
self.cert.not_valid_after))
for validation in ['signature_algorithms', 'subject', 'issuer',
'has_expired', 'version', 'keyUsage',
'extendedKeyUsage', 'subjectAltName',
'notBefore', 'notAfter', 'valid_at', 'valid_in', 'invalid_at']:
f_name = locals()['_validate_%s' % validation]
f_name()
def generate(self, module):
"""Don't generate anything - only assert"""
self.assertonly()
try:
if self.privatekey_path and \
not super(AssertOnlyCertificateCryptography, self).check(module, perms_required=False):
self.message.append(
'Certificate %s and private key %s do not match' % (self.path, self.privatekey_path)
)
except CertificateError as e:
self.message.append(
'Error while reading private key %s: %s' % (self.privatekey_path, str(e))
)
if len(self.message):
module.fail_json(msg=' | '.join(self.message))
def check(self, module, perms_required=False):
"""Ensure the resource is in its desired state."""
parent_check = super(AssertOnlyCertificateCryptography, self).check(module, perms_required)
self.assertonly()
assertonly_check = not len(self.message)
self.message = []
return parent_check and assertonly_check
def dump(self, check_mode=False):
result = {
'changed': self.changed,
'filename': self.path,
'privatekey': self.privatekey_path,
'csr': self.csr_path,
}
return result
class AssertOnlyCertificate(Certificate):
"""validate the supplied certificate."""
def __init__(self, module):
super(AssertOnlyCertificate, self).__init__(module)
super(AssertOnlyCertificate, self).__init__(module, 'pyopenssl')
self.signature_algorithms = module.params['signature_algorithms']
if module.params['subject']:
self.subject = crypto_utils.parse_name_field(module.params['subject'])
@ -991,8 +1657,7 @@ class AssertOnlyCertificate(Certificate):
self.valid_in = "+" + self.valid_in + "s"
valid_in_asn1 = self.get_relative_time_option(self.valid_in, "valid_in")
valid_in_date = to_bytes(valid_in_asn1, errors='surrogate_or_strict')
if not (self.cert.get_notBefore() <= valid_in_date <=
self.cert.get_notAfter()):
if not (self.cert.get_notBefore() <= valid_in_date <= self.cert.get_notAfter()):
self.message.append(
'Certificate is not valid in %s from now (that would be %s) - notBefore: %s - notAfter: %s'
% (self.valid_in, valid_in_date,
@ -1051,8 +1716,11 @@ class AssertOnlyCertificate(Certificate):
class AcmeCertificate(Certificate):
"""Retrieve a certificate using the ACME protocol."""
def __init__(self, module):
super(AcmeCertificate, self).__init__(module)
# Since there's no real use of the backend,
# other than the 'self.check' function, we just pass the backend to the constructor
def __init__(self, module, backend):
super(AcmeCertificate, self).__init__(module, backend)
self.accountkey_path = module.params['acme_accountkey_path']
self.challenge_path = module.params['acme_challenge_path']
self.use_chain = module.params['acme_chain']
@ -1123,6 +1791,7 @@ def main():
provider=dict(type='str', choices=['acme', 'assertonly', 'ownca', 'selfsigned']),
force=dict(type='bool', default=False,),
csr_path=dict(type='path'),
select_crypto_backend=dict(type='str', default='auto', choices=['auto', 'cryptography', 'pyopenssl']),
# General properties of a certificate
privatekey_path=dict(type='path'),
@ -1172,14 +1841,6 @@ def main():
add_file_common_args=True,
)
if not pyopenssl_found:
module.fail_json(msg=missing_required_lib('pyOpenSSL'), exception=PYOPENSSL_IMP_ERR)
if module.params['provider'] in ['selfsigned', 'ownca', 'assertonly']:
try:
getattr(crypto.X509Req, 'get_extensions')
except AttributeError:
module.fail_json(msg='You need to have PyOpenSSL>=0.15')
if module.params['provider'] != 'assertonly' and module.params['csr_path'] is None:
module.fail_json(msg='csr_path is required when provider is not assertonly')
@ -1192,14 +1853,60 @@ def main():
provider = module.params['provider']
if provider == 'selfsigned':
certificate = SelfSignedCertificate(module)
elif provider == 'acme':
certificate = AcmeCertificate(module)
elif provider == 'ownca':
certificate = OwnCACertificate(module)
else:
certificate = AssertOnlyCertificate(module)
backend = module.params['select_crypto_backend']
if backend == 'auto':
# Detect what backend we can use
can_use_cryptography = CRYPTOGRAPHY_FOUND and CRYPTOGRAPHY_VERSION >= LooseVersion(MINIMAL_CRYPTOGRAPHY_VERSION)
can_use_pyopenssl = PYOPENSSL_FOUND and PYOPENSSL_VERSION >= LooseVersion(MINIMAL_PYOPENSSL_VERSION)
# If cryptography is available we'll use it
if can_use_cryptography:
backend = 'cryptography'
elif can_use_pyopenssl:
backend = 'pyopenssl'
if module.params['selfsigned_version'] == 2 or module.params['ownca_version'] == 2:
module.warn('crypto backend forced to pyopenssl. The cryptography library does not support v2 certificates')
backend = 'pyopenssl'
# Fail if no backend has been found
if backend == 'auto':
module.fail_json(msg=("Can't detect none of the required Python libraries "
"cryptography (>= {0}) or PyOpenSSL (>= {1})").format(
MINIMAL_CRYPTOGRAPHY_VERSION,
MINIMAL_PYOPENSSL_VERSION))
if backend == 'pyopenssl':
if not PYOPENSSL_FOUND:
module.fail_json(msg=missing_required_lib('pyOpenSSL'), exception=PYOPENSSL_IMP_ERR)
if module.params['provider'] in ['selfsigned', 'ownca', 'assertonly']:
try:
getattr(crypto.X509Req, 'get_extensions')
except AttributeError:
module.fail_json(msg='You need to have PyOpenSSL>=0.15')
if provider == 'selfsigned':
certificate = SelfSignedCertificate(module)
elif provider == 'acme':
certificate = AcmeCertificate(module, 'pyopenssl')
elif provider == 'ownca':
certificate = OwnCACertificate(module)
else:
certificate = AssertOnlyCertificate(module)
elif backend == 'cryptography':
if not CRYPTOGRAPHY_FOUND:
module.fail_json(msg=missing_required_lib('cryptography'), exception=CRYPTOGRAPHY_IMP_ERR)
if module.params['selfsigned_version'] == 2 or module.params['ownca_version'] == 2:
module.fail_json(msg='The cryptography backend does not support v2 certificates, '
'use select_crypto_backend=pyopenssl for v2 certificates')
if provider == 'selfsigned':
certificate = SelfSignedCertificateCryptography(module)
elif provider == 'acme':
certificate = AcmeCertificate(module, 'cryptography')
elif provider == 'ownca':
certificate = OwnCACertificateCryptography(module)
else:
certificate = AssertOnlyCertificateCryptography(module)
if module.params['state'] == 'present':

View file

@ -1,16 +1,16 @@
---
- name: Generate privatekey
- name: (Assertonly, {{select_crypto_backend}}) - Generate privatekey
openssl_privatekey:
path: '{{ output_dir }}/privatekey.pem'
- name: Generate privatekey with password
- name: (Assertonly, {{select_crypto_backend}}) - Generate privatekey with password
openssl_privatekey:
path: '{{ output_dir }}/privatekeypw.pem'
passphrase: hunter2
cipher: auto
select_crypto_backend: cryptography
- name: Generate CSR (no extensions)
- name: (Assertonly, {{select_crypto_backend}}) - Generate CSR (no extensions)
openssl_csr:
path: '{{ output_dir }}/csr_noext.csr'
privatekey_path: '{{ output_dir }}/privatekey.pem'
@ -18,38 +18,42 @@
commonName: www.example.com
useCommonNameForSAN: no
- name: Generate selfsigned certificate (no extensions)
- name: (Assertonly, {{select_crypto_backend}}) - Generate selfsigned certificate (no extensions)
openssl_certificate:
path: '{{ output_dir }}/cert_noext.pem'
csr_path: '{{ output_dir }}/csr_noext.csr'
privatekey_path: '{{ output_dir }}/privatekey.pem'
provider: selfsigned
selfsigned_digest: sha256
select_crypto_backend: '{{ select_crypto_backend }}'
- name: Assert that subject_alt_name is there (should fail)
- name: (Assertonly, {{select_crypto_backend}}) - Assert that subject_alt_name is there (should fail)
openssl_certificate:
path: '{{ output_dir }}/cert_noext.pem'
provider: assertonly
subject_alt_name:
- "DNS:example.com"
select_crypto_backend: '{{ select_crypto_backend }}'
ignore_errors: yes
register: extension_missing_san
- name: Assert that key_usage is there (should fail)
- name: (Assertonly, {{select_crypto_backend}}) - Assert that key_usage is there (should fail)
openssl_certificate:
path: '{{ output_dir }}/cert_noext.pem'
provider: assertonly
key_usage:
- digitalSignature
select_crypto_backend: '{{ select_crypto_backend }}'
ignore_errors: yes
register: extension_missing_ku
- name: Assert that extended_key_usage is there (should fail)
- name: (Assertonly, {{select_crypto_backend}}) - Assert that extended_key_usage is there (should fail)
openssl_certificate:
path: '{{ output_dir }}/cert_noext.pem'
provider: assertonly
extended_key_usage:
- biometricInfo
select_crypto_backend: '{{ select_crypto_backend }}'
ignore_errors: yes
register: extension_missing_eku
@ -62,33 +66,36 @@
- extension_missing_eku is failed
- "'Found no extendedKeyUsage extension' in extension_missing_eku.msg"
- name: Check private key passphrase fail 1
- name: (Assertonly, {{select_crypto_backend}}) - Check private key passphrase fail 1
openssl_certificate:
path: '{{ output_dir }}/cert_noext.pem'
privatekey_path: '{{ output_dir }}/privatekey.pem'
privatekey_passphrase: hunter2
provider: assertonly
select_crypto_backend: '{{ select_crypto_backend }}'
ignore_errors: yes
register: passphrase_error_1
- name: Check private key passphrase fail 2
- name: (Assertonly, {{select_crypto_backend}}) - Check private key passphrase fail 2
openssl_certificate:
path: '{{ output_dir }}/cert_noext.pem'
privatekey_path: '{{ output_dir }}/privatekeypw.pem'
privatekey_passphrase: wrong_password
provider: assertonly
select_crypto_backend: '{{ select_crypto_backend }}'
ignore_errors: yes
register: passphrase_error_2
- name: Check private key passphrase fail 3
- name: (Assertonly, {{select_crypto_backend}}) - Check private key passphrase fail 3
openssl_certificate:
path: '{{ output_dir }}/cert_noext.pem'
privatekey_path: '{{ output_dir }}/privatekeypw.pem'
provider: assertonly
select_crypto_backend: '{{ select_crypto_backend }}'
ignore_errors: yes
register: passphrase_error_3
- name:
- name: (Assertonly, {{select_crypto_backend}}) -
assert:
that:
- passphrase_error_1 is failed

View file

@ -1,16 +1,16 @@
---
- name: Generate privatekey
- name: (Expired, {{select_crypto_backend}}) Generate privatekey
openssl_privatekey:
path: '{{ output_dir }}/has_expired_privatekey.pem'
- name: Generate CSR
- name: (Expired, {{select_crypto_backend}}) Generate CSR
openssl_csr:
path: '{{ output_dir }}/has_expired_csr.csr'
privatekey_path: '{{ output_dir }}/has_expired_privatekey.pem'
subject:
commonName: www.example.com
- name: Generate expired selfsigned certificate
- name: (Expired, {{select_crypto_backend}}) Generate expired selfsigned certificate
openssl_certificate:
path: '{{ output_dir }}/has_expired_cert.pem'
csr_path: '{{ output_dir }}/has_expired_csr.csr'
@ -18,22 +18,31 @@
provider: selfsigned
selfsigned_digest: sha256
selfsigned_not_after: "-1s"
selfsigned_not_before: "-100s"
select_crypto_backend: '{{ select_crypto_backend }}'
when: select_crypto_backend == 'pyopenssl' # cryptography won't allow creating expired certificates
- name: "Check task fails because cert is expired (has_expired: false)"
- name: (Expired, {{select_crypto_backend}}) Generate expired selfsigned certificate
command: "openssl x509 -req -days -1 -in {{ output_dir }}/has_expired_csr.csr -signkey {{ output_dir }}/has_expired_privatekey.pem -out {{ output_dir }}/has_expired_cert.pem"
when: select_crypto_backend == 'cryptography' # So we create it with 'command'
- name: "(Expired) Check task fails because cert is expired (has_expired: false)"
openssl_certificate:
provider: assertonly
path: "{{ output_dir }}/has_expired_cert.pem"
has_expired: false
select_crypto_backend: '{{ select_crypto_backend }}'
ignore_errors: true
register: expired_cert_check
- name: Ensure previous task failed
- name: (Expired, {{select_crypto_backend}}) Ensure previous task failed
assert:
that: expired_cert_check is failed
- name: "Check expired cert check is ignored (has_expired: true)"
- name: "(Expired) Check expired cert check is ignored (has_expired: true)"
openssl_certificate:
provider: assertonly
path: "{{ output_dir }}/has_expired_cert.pem"
has_expired: true
select_crypto_backend: '{{ select_crypto_backend }}'
register: expired_cert_skip

View file

@ -0,0 +1,7 @@
---
- debug:
msg: "Executing tests with backend {{ select_crypto_backend }}"
- import_tasks: assertonly.yml
- import_tasks: expired.yml
- import_tasks: selfsigned.yml
- import_tasks: ownca.yml

View file

@ -1,12 +1,22 @@
---
- block:
- import_tasks: assertonly.yml
- import_tasks: expired.yml
- import_tasks: selfsigned.yml
- import_tasks: ownca.yml
- name: Running tests with pyOpenSSL backend
include_tasks: impl.yml
vars:
select_crypto_backend: pyopenssl
when: pyopenssl_version.stdout is version('0.15', '>=')
- name: Remove output directory
file:
path: "{{ output_dir }}"
state: absent
- name: Re-create output directory
file:
path: "{{ output_dir }}"
state: directory
- name: Running tests with cryptography backend
include_tasks: impl.yml
vars:
select_crypto_backend: cryptography
when: cryptography_version.stdout is version('1.6', '>=')

View file

@ -1,9 +1,9 @@
---
- name: Generate CA privatekey
- name: (OwnCA, {{select_crypto_backend}}) Generate CA privatekey
openssl_privatekey:
path: '{{ output_dir }}/ca_privatekey.pem'
- name: Generate CA CSR
- name: (OwnCA, {{select_crypto_backend}}) Generate CA CSR
openssl_csr:
path: '{{ output_dir }}/ca_csr.csr'
privatekey_path: '{{ output_dir }}/ca_privatekey.pem'
@ -14,15 +14,16 @@
- 'CA:TRUE'
basic_constraints_critical: yes
- name: Generate selfsigned CA certificate
- name: (OwnCA, {{select_crypto_backend}}) Generate selfsigned CA certificate
openssl_certificate:
path: '{{ output_dir }}/ca_cert.pem'
csr_path: '{{ output_dir }}/ca_csr.csr'
privatekey_path: '{{ output_dir }}/ca_privatekey.pem'
provider: selfsigned
selfsigned_digest: sha256
select_crypto_backend: '{{ select_crypto_backend }}'
- name: Generate ownca certificate
- name: (OwnCA, {{select_crypto_backend}}) Generate ownca certificate
openssl_certificate:
path: '{{ output_dir }}/ownca_cert.pem'
csr_path: '{{ output_dir }}/csr.csr'
@ -31,9 +32,10 @@
ownca_privatekey_path: '{{ output_dir }}/ca_privatekey.pem'
provider: ownca
ownca_digest: sha256
select_crypto_backend: '{{ select_crypto_backend }}'
register: ownca_certificate
- name: Generate ownca certificate
- name: (OwnCA, {{select_crypto_backend}}) Generate ownca certificate
openssl_certificate:
path: '{{ output_dir }}/ownca_cert.pem'
csr_path: '{{ output_dir }}/csr.csr'
@ -42,9 +44,10 @@
ownca_privatekey_path: '{{ output_dir }}/ca_privatekey.pem'
provider: ownca
ownca_digest: sha256
select_crypto_backend: '{{ select_crypto_backend }}'
register: ownca_certificate_idempotence
- name: Generate ownca certificate (check mode)
- name: (OwnCA, {{select_crypto_backend}}) Generate ownca certificate (check mode)
openssl_certificate:
path: '{{ output_dir }}/ownca_cert.pem'
csr_path: '{{ output_dir }}/csr.csr'
@ -53,9 +56,10 @@
ownca_privatekey_path: '{{ output_dir }}/ca_privatekey.pem'
provider: ownca
ownca_digest: sha256
select_crypto_backend: '{{ select_crypto_backend }}'
check_mode: yes
- name: Check ownca certificate
- name: (OwnCA, {{select_crypto_backend}}) Check ownca certificate
openssl_certificate:
path: '{{ output_dir }}/ownca_cert.pem'
privatekey_path: '{{ output_dir }}/privatekey.pem'
@ -69,8 +73,9 @@
commonName: www.example.com
issuer:
commonName: Example CA
select_crypto_backend: '{{ select_crypto_backend }}'
- name: Generate ownca v2 certificate
- name: (OwnCA, {{select_crypto_backend}}) Generate ownca v2 certificate
openssl_certificate:
path: '{{ output_dir }}/ownca_cert_v2.pem'
csr_path: '{{ output_dir }}/csr.csr'
@ -80,8 +85,11 @@
provider: ownca
ownca_digest: sha256
ownca_version: 2
select_crypto_backend: '{{ select_crypto_backend }}'
register: ownca_v2_certificate
ignore_errors: true
- name: Generate ownca certificate2
- name: (OwnCA, {{select_crypto_backend}}) Generate ownca certificate2
openssl_certificate:
path: '{{ output_dir }}/ownca_cert2.pem'
csr_path: '{{ output_dir }}/csr2.csr'
@ -90,8 +98,9 @@
ownca_privatekey_path: '{{ output_dir }}/ca_privatekey.pem'
provider: ownca
ownca_digest: sha256
select_crypto_backend: '{{ select_crypto_backend }}'
- name: Check ownca certificate2
- name: (OwnCA, {{select_crypto_backend}}) Check ownca certificate2
openssl_certificate:
path: '{{ output_dir }}/ownca_cert2.pem'
privatekey_path: '{{ output_dir }}/privatekey2.pem'
@ -117,8 +126,9 @@
- biometricInfo
issuer:
commonName: Example CA
select_crypto_backend: '{{ select_crypto_backend }}'
- name: Create ownca certificate with notBefore and notAfter
- name: (OwnCA, {{select_crypto_backend}}) Create ownca certificate with notBefore and notAfter
openssl_certificate:
provider: ownca
ownca_not_before: 20181023133742Z
@ -128,8 +138,9 @@
privatekey_path: "{{ output_dir }}/privatekey3.pem"
ownca_path: '{{ output_dir }}/ca_cert.pem'
ownca_privatekey_path: '{{ output_dir }}/ca_privatekey.pem'
select_crypto_backend: '{{ select_crypto_backend }}'
- name: Create ownca certificate with relative notBefore and notAfter
- name: (OwnCA, {{select_crypto_backend}}) Create ownca certificate with relative notBefore and notAfter
openssl_certificate:
provider: ownca
ownca_not_before: +1s
@ -139,8 +150,9 @@
privatekey_path: "{{ output_dir }}/privatekey3.pem"
ownca_path: '{{ output_dir }}/ca_cert.pem'
ownca_privatekey_path: '{{ output_dir }}/ca_privatekey.pem'
select_crypto_backend: '{{ select_crypto_backend }}'
- name: Generate ownca ECC certificate
- name: (OwnCA, {{select_crypto_backend}}) Generate ownca ECC certificate
openssl_certificate:
path: '{{ output_dir }}/ownca_cert_ecc.pem'
csr_path: '{{ output_dir }}/csr_ecc.csr'
@ -149,9 +161,10 @@
ownca_privatekey_path: '{{ output_dir }}/ca_privatekey.pem'
provider: ownca
ownca_digest: sha256
select_crypto_backend: '{{ select_crypto_backend }}'
register: ownca_certificate_ecc
- name: Generate ownca certificate (failed passphrase 1)
- name: (OwnCA, {{select_crypto_backend}}) Generate ownca certificate (failed passphrase 1)
openssl_certificate:
path: '{{ output_dir }}/ownca_cert_pw1.pem'
csr_path: '{{ output_dir }}/csr_ecc.csr'
@ -160,10 +173,11 @@
ownca_privatekey_passphrase: hunter2
provider: ownca
ownca_digest: sha256
select_crypto_backend: '{{ select_crypto_backend }}'
ignore_errors: yes
register: passphrase_error_1
- name: Generate ownca certificate (failed passphrase 2)
- name: (OwnCA, {{select_crypto_backend}}) Generate ownca certificate (failed passphrase 2)
openssl_certificate:
path: '{{ output_dir }}/ownca_cert_pw1.pem'
csr_path: '{{ output_dir }}/csr_ecc.csr'
@ -172,10 +186,11 @@
ownca_privatekey_passphrase: wrong_password
provider: ownca
ownca_digest: sha256
select_crypto_backend: '{{ select_crypto_backend }}'
ignore_errors: yes
register: passphrase_error_2
- name: Generate ownca certificate (failed passphrase 3)
- name: (OwnCA, {{select_crypto_backend}}) Generate ownca certificate (failed passphrase 3)
openssl_certificate:
path: '{{ output_dir }}/ownca_cert_pw3.pem'
csr_path: '{{ output_dir }}/csr_ecc.csr'
@ -183,6 +198,7 @@
ownca_privatekey_path: '{{ output_dir }}/privatekeypw.pem'
provider: ownca
ownca_digest: sha256
select_crypto_backend: '{{ select_crypto_backend }}'
ignore_errors: yes
register: passphrase_error_3

View file

@ -1,50 +1,53 @@
---
- name: Generate privatekey
- name: (Selfsigned, {{select_crypto_backend}}) Generate privatekey
openssl_privatekey:
path: '{{ output_dir }}/privatekey.pem'
- name: Generate privatekey with password
- name: (Selfsigned, {{select_crypto_backend}}) Generate privatekey with password
openssl_privatekey:
path: '{{ output_dir }}/privatekeypw.pem'
passphrase: hunter2
cipher: auto
select_crypto_backend: cryptography
- name: Generate CSR
- name: (Selfsigned, {{select_crypto_backend}}) Generate CSR
openssl_csr:
path: '{{ output_dir }}/csr.csr'
privatekey_path: '{{ output_dir }}/privatekey.pem'
subject:
commonName: www.example.com
- name: Generate selfsigned certificate
- name: (Selfsigned, {{select_crypto_backend}}) Generate selfsigned certificate
openssl_certificate:
path: '{{ output_dir }}/cert.pem'
csr_path: '{{ output_dir }}/csr.csr'
privatekey_path: '{{ output_dir }}/privatekey.pem'
provider: selfsigned
selfsigned_digest: sha256
select_crypto_backend: '{{ select_crypto_backend }}'
register: selfsigned_certificate
- name: Generate selfsigned certificate
- name: (Selfsigned, {{select_crypto_backend}}) Generate selfsigned certificate - idempotency
openssl_certificate:
path: '{{ output_dir }}/cert.pem'
csr_path: '{{ output_dir }}/csr.csr'
privatekey_path: '{{ output_dir }}/privatekey.pem'
provider: selfsigned
selfsigned_digest: sha256
select_crypto_backend: '{{ select_crypto_backend }}'
register: selfsigned_certificate_idempotence
- name: Generate selfsigned certificate (check mode)
- name: (Selfsigned, {{select_crypto_backend}}) Generate selfsigned certificate (check mode)
openssl_certificate:
path: '{{ output_dir }}/cert.pem'
csr_path: '{{ output_dir }}/csr.csr'
privatekey_path: '{{ output_dir }}/privatekey.pem'
provider: selfsigned
selfsigned_digest: sha256
select_crypto_backend: '{{ select_crypto_backend }}'
check_mode: yes
- name: Check selfsigned certificate
- name: (Selfsigned, {{select_crypto_backend}}) Check selfsigned certificate
openssl_certificate:
path: '{{ output_dir }}/cert.pem'
privatekey_path: '{{ output_dir }}/privatekey.pem'
@ -56,8 +59,9 @@
- sha256WithECDSAEncryption
subject:
commonName: www.example.com
select_crypto_backend: '{{ select_crypto_backend }}'
- name: Generate selfsigned v2 certificate
- name: (Selfsigned, {{select_crypto_backend}}) Generate selfsigned v2 certificate
openssl_certificate:
path: '{{ output_dir }}/cert_v2.pem'
csr_path: '{{ output_dir }}/csr.csr'
@ -65,12 +69,15 @@
provider: selfsigned
selfsigned_digest: sha256
selfsigned_version: 2
select_crypto_backend: "{{ select_crypto_backend }}"
register: selfsigned_v2_cert
ignore_errors: true
- name: Generate privatekey2
- name: (Selfsigned, {{select_crypto_backend}}) Generate privatekey2
openssl_privatekey:
path: '{{ output_dir }}/privatekey2.pem'
- name: Generate CSR2
- name: (Selfsigned, {{select_crypto_backend}}) Generate CSR2
openssl_csr:
subject:
CN: www.example.com
@ -89,15 +96,16 @@
- ipsecUser
- biometricInfo
- name: Generate selfsigned certificate2
- name: (Selfsigned, {{select_crypto_backend}}) Generate selfsigned certificate2
openssl_certificate:
path: '{{ output_dir }}/cert2.pem'
csr_path: '{{ output_dir }}/csr2.csr'
privatekey_path: '{{ output_dir }}/privatekey2.pem'
provider: selfsigned
selfsigned_digest: sha256
select_crypto_backend: '{{ select_crypto_backend }}'
- name: Check selfsigned certificate2
- name: (Selfsigned, {{select_crypto_backend}}) Check selfsigned certificate2
openssl_certificate:
path: '{{ output_dir }}/cert2.pem'
privatekey_path: '{{ output_dir }}/privatekey2.pem'
@ -121,19 +129,20 @@
extendedKeyUsage:
- ipsecUser
- biometricInfo
select_crypto_backend: '{{ select_crypto_backend }}'
- name: Create private key 3
- name: (Selfsigned, {{select_crypto_backend}}) Create private key 3
openssl_privatekey:
path: "{{ output_dir }}/privatekey3.pem"
- name: Create CSR 3
- name: (Selfsigned, {{select_crypto_backend}}) Create CSR 3
openssl_csr:
subject:
CN: www.example.com
privatekey_path: "{{ output_dir }}/privatekey3.pem"
path: "{{ output_dir }}/csr3.pem"
- name: Create certificate3 with notBefore and notAfter
- name: (Selfsigned, {{select_crypto_backend}}) Create certificate3 with notBefore and notAfter
openssl_certificate:
provider: selfsigned
selfsigned_not_before: 20181023133742Z
@ -141,30 +150,33 @@
path: "{{ output_dir }}/cert3.pem"
csr_path: "{{ output_dir }}/csr3.pem"
privatekey_path: "{{ output_dir }}/privatekey3.pem"
select_crypto_backend: '{{ select_crypto_backend }}'
- name: Generate privatekey
- name: (Selfsigned, {{select_crypto_backend}}) Generate privatekey
openssl_privatekey:
path: '{{ output_dir }}/privatekey_ecc.pem'
type: ECC
curve: secp256k1
curve: "{{ (ansible_distribution == 'CentOS' and ansible_distribution_major_version == '6') | ternary('secp521r1', 'secp256k1') }}"
# ^ cryptography on CentOS6 doesn't support secp256k1, so we use secp521r1 instead
- name: Generate CSR
- name: (Selfsigned, {{select_crypto_backend}}) Generate CSR
openssl_csr:
path: '{{ output_dir }}/csr_ecc.csr'
privatekey_path: '{{ output_dir }}/privatekey_ecc.pem'
subject:
commonName: www.example.com
- name: Generate selfsigned certificate
- name: (Selfsigned, {{select_crypto_backend}}) Generate selfsigned certificate
openssl_certificate:
path: '{{ output_dir }}/cert_ecc.pem'
csr_path: '{{ output_dir }}/csr_ecc.csr'
privatekey_path: '{{ output_dir }}/privatekey_ecc.pem'
provider: selfsigned
selfsigned_digest: sha256
select_crypto_backend: '{{ select_crypto_backend }}'
register: selfsigned_certificate_ecc
- name: Generate selfsigned certificate (failed passphrase 1)
- name: (Selfsigned, {{select_crypto_backend}}) Generate selfsigned certificate (failed passphrase 1)
openssl_certificate:
path: '{{ output_dir }}/cert_pw1.pem'
csr_path: '{{ output_dir }}/csr_ecc.csr'
@ -172,10 +184,11 @@
privatekey_passphrase: hunter2
provider: selfsigned
selfsigned_digest: sha256
select_crypto_backend: '{{ select_crypto_backend }}'
ignore_errors: yes
register: passphrase_error_1
- name: Generate selfsigned certificate (failed passphrase 2)
- name: (Selfsigned, {{select_crypto_backend}}) Generate selfsigned certificate (failed passphrase 2)
openssl_certificate:
path: '{{ output_dir }}/cert_pw2.pem'
csr_path: '{{ output_dir }}/csr_ecc.csr'
@ -183,16 +196,18 @@
privatekey_passphrase: wrong_password
provider: selfsigned
selfsigned_digest: sha256
select_crypto_backend: '{{ select_crypto_backend }}'
ignore_errors: yes
register: passphrase_error_2
- name: Generate selfsigned certificate (failed passphrase 3)
- name: (Selfsigned, {{select_crypto_backend}}) Generate selfsigned certificate (failed passphrase 3)
openssl_certificate:
path: '{{ output_dir }}/cert_pw3.pem'
csr_path: '{{ output_dir }}/csr_ecc.csr'
privatekey_path: '{{ output_dir }}/privatekeypw.pem'
provider: selfsigned
selfsigned_digest: sha256
select_crypto_backend: '{{ select_crypto_backend }}'
ignore_errors: yes
register: passphrase_error_3

View file

@ -1,21 +1,21 @@
---
- name: Validate ownca certificate (test - verify CA)
- name: (OwnCA validation, {{select_crypto_backend}}) Validate ownca certificate (test - verify CA)
shell: 'openssl verify -CAfile {{ output_dir }}/ca_cert.pem {{ output_dir }}/ownca_cert.pem | sed "s/.*: \(.*\)/\1/g"'
register: ownca_verify_ca
- name: Validate ownca certificate (test - ownca certificate modulus)
- name: (OwnCA validation, {{select_crypto_backend}}) Validate ownca certificate (test - ownca certificate modulus)
shell: 'openssl x509 -noout -modulus -in {{ output_dir }}/ownca_cert.pem'
register: ownca_cert_modulus
- name: Validate ownca certificate (test - ownca issuer value)
- name: (OwnCA validation, {{select_crypto_backend}}) Validate ownca certificate (test - ownca issuer value)
shell: 'openssl x509 -noout -in {{ output_dir}}/ownca_cert.pem -text | grep "Issuer" | sed "s/.*: \(.*\)/\1/g"'
register: ownca_cert_issuer
- name: Validate ownca certificate (test - ownca certficate version == default == 3)
- name: (OwnCA validation, {{select_crypto_backend}}) Validate ownca certificate (test - ownca certficate version == default == 3)
shell: 'openssl x509 -noout -in {{ output_dir}}/ownca_cert.pem -text | grep "Version" | sed "s/.*: \(.*\) .*/\1/g"'
register: ownca_cert_version
- name: Validate ownca certificate (assert)
- name: (OwnCA validation, {{select_crypto_backend}}) Validate ownca certificate (assert)
assert:
that:
- ownca_verify_ca.stdout == 'OK'
@ -24,65 +24,75 @@
# openssl 1.1.x adds a space between the output
- ownca_cert_issuer.stdout in ['CN=Example CA', 'CN = Example CA']
- name: Validate ownca certificate idempotence
- name: (OwnCA validation, {{select_crypto_backend}}) Validate ownca certificate idempotence
assert:
that:
- ownca_certificate.serial_number == ownca_certificate_idempotence.serial_number
- ownca_certificate.notBefore == ownca_certificate_idempotence.notBefore
- ownca_certificate.notAfter == ownca_certificate_idempotence.notAfter
- name: Validate ownca certificate v2 (test - ownca certificate version == 2)
shell: 'openssl x509 -noout -in {{ output_dir}}/ownca_cert_v2.pem -text | grep "Version" | sed "s/.*: \(.*\) .*/\1/g"'
register: ownca_cert_v2_version
- block:
- name: (OwnCA validation, {{select_crypto_backend}}) Validate ownca certificate v2 (test - ownca certificate version == 2)
shell: 'openssl x509 -noout -in {{ output_dir}}/ownca_cert_v2.pem -text | grep "Version" | sed "s/.*: \(.*\) .*/\1/g"'
register: ownca_cert_v2_version
- name: Validate ownca certificate version 2 (assert)
- name: (OwnCA validation, {{select_crypto_backend}}) Validate ownca certificate version 2 (assert)
assert:
that:
- ownca_cert_v2_version.stdout == '2'
when: "select_crypto_backend != 'cryptography'"
- name: (OwnCA validation, {{select_crypto_backend}}) Validate ownca certificate v2 (test - ownca certificate version == 2)
assert:
that:
- ownca_cert_v2_version.stdout == '2'
- ownca_v2_certificate is failed
- "'The cryptography backend does not support v2 certificates' in ownca_v2_certificate.msg"
when: "select_crypto_backend == 'cryptography'"
- name: Validate ownca certificate2 (test - ownca certificate modulus)
- name: (OwnCA validation, {{select_crypto_backend}}) Validate ownca certificate2 (test - ownca certificate modulus)
shell: 'openssl x509 -noout -modulus -in {{ output_dir }}/ownca_cert2.pem'
register: ownca_cert2_modulus
- name: Validate ownca certificate2 (assert)
- name: (OwnCA validation, {{select_crypto_backend}}) Validate ownca certificate2 (assert)
assert:
that:
- ownca_cert2_modulus.stdout == privatekey2_modulus.stdout
- name: Validate owncal certificate3 (test - notBefore)
- name: (OwnCA validation, {{select_crypto_backend}}) Validate owncal certificate3 (test - notBefore)
shell: 'openssl x509 -noout -in {{ output_dir }}/ownca_cert3.pem -text | grep "Not Before" | sed "s/.*: \(.*\) .*/\1/g"'
register: ownca_cert3_notBefore
- name: Validate ownca certificate3 (test - notAfter)
- name: (OwnCA validation, {{select_crypto_backend}}) Validate ownca certificate3 (test - notAfter)
shell: 'openssl x509 -noout -in {{ output_dir }}/ownca_cert3.pem -text | grep "Not After" | sed "s/.*: \(.*\) .*/\1/g"'
register: ownca_cert3_notAfter
- name: Validate ownca certificate3 (assert - notBefore)
- name: (OwnCA validation, {{select_crypto_backend}}) Validate ownca certificate3 (assert - notBefore)
assert:
that:
- ownca_cert3_notBefore.stdout == 'Oct 23 13:37:42 2018'
- name: Validate ownca certificate3 (assert - notAfter)
- name: (OwnCA validation, {{select_crypto_backend}}) Validate ownca certificate3 (assert - notAfter)
assert:
that:
- ownca_cert3_notAfter.stdout == 'Oct 23 13:37:42 2019'
- name: Validate ownca ECC certificate (test - ownca certificate pubkey)
- name: (OwnCA validation, {{select_crypto_backend}}) Validate ownca ECC certificate (test - ownca certificate pubkey)
shell: 'openssl x509 -noout -pubkey -in {{ output_dir }}/ownca_cert_ecc.pem'
register: ownca_cert_ecc_pubkey
- name: Validate ownca ECC certificate (test - ownca issuer value)
- name: (OwnCA validation, {{select_crypto_backend}}) Validate ownca ECC certificate (test - ownca issuer value)
shell: 'openssl x509 -noout -in {{ output_dir}}/ownca_cert_ecc.pem -text | grep "Issuer" | sed "s/.*: \(.*\)/\1/g"'
register: ownca_cert_ecc_issuer
- name: Validate ownca ECC certificate (assert)
- name: (OwnCA validation, {{select_crypto_backend}}) Validate ownca ECC certificate (assert)
assert:
that:
- ownca_cert_ecc_pubkey.stdout == privatekey_ecc_pubkey.stdout
# openssl 1.1.x adds a space between the output
- ownca_cert_ecc_issuer.stdout in ['CN=Example CA', 'CN = Example CA']
- name:
- name: (OwnCA validation, {{select_crypto_backend}})
assert:
that:
- passphrase_error_1 is failed

View file

@ -1,89 +1,99 @@
---
- name: Validate certificate (test - privatekey modulus)
- name: (Selfsigned validation, {{select_crypto_backend}}) Validate certificate (test - privatekey modulus)
shell: 'openssl rsa -noout -modulus -in {{ output_dir }}/privatekey.pem'
register: privatekey_modulus
- name: Validate certificate (test - certificate modulus)
- name: (Selfsigned validation, {{select_crypto_backend}}) Validate certificate (test - certificate modulus)
shell: 'openssl x509 -noout -modulus -in {{ output_dir }}/cert.pem'
register: cert_modulus
- name: Validate certificate (test - issuer value)
- name: (Selfsigned validation, {{select_crypto_backend}}) Validate certificate (test - issuer value)
shell: 'openssl x509 -noout -in {{ output_dir}}/cert.pem -text | grep "Issuer" | sed "s/.*: \(.*\)/\1/g; s/ //g;"'
register: cert_issuer
- name: Validate certificate (test - certficate version == default == 3)
- name: (Selfsigned validation, {{select_crypto_backend}}) Validate certificate (test - certficate version == default == 3)
shell: 'openssl x509 -noout -in {{ output_dir}}/cert.pem -text | grep "Version" | sed "s/.*: \(.*\) .*/\1/g"'
register: cert_version
- name: Validate certificate (assert)
- name: (Selfsigned validation, {{select_crypto_backend}}) Validate certificate (assert)
assert:
that:
- cert_modulus.stdout == privatekey_modulus.stdout
- cert_version.stdout == '3'
- cert_issuer.stdout == 'CN=www.example.com'
- name: Validate certificate idempotence
- name: (Selfsigned validation, {{select_crypto_backend}}) Validate certificate idempotence
assert:
that:
- selfsigned_certificate.serial_number == selfsigned_certificate_idempotence.serial_number
- selfsigned_certificate.notBefore == selfsigned_certificate_idempotence.notBefore
- selfsigned_certificate.notAfter == selfsigned_certificate_idempotence.notAfter
- name: Validate certificate v2 (test - certificate version == 2)
shell: 'openssl x509 -noout -in {{ output_dir}}/cert_v2.pem -text | grep "Version" | sed "s/.*: \(.*\) .*/\1/g"'
register: cert_v2_version
- block:
- name: (Selfsigned validation, {{select_crypto_backend}}) Validate certificate v2 (test - certificate version == 2)
shell: 'openssl x509 -noout -in {{ output_dir}}/cert_v2.pem -text | grep "Version" | sed "s/.*: \(.*\) .*/\1/g"'
register: cert_v2_version
- name: Validate certificate version 2 (assert)
assert:
that:
- cert_v2_version.stdout == '2'
- name: (Selfsigned validation, {{select_crypto_backend}}) Validate certificate version 2 (assert)
assert:
that:
- cert_v2_version.stdout == '2'
when: select_crypto_backend != 'cryptography'
- name: Validate certificate2 (test - privatekey modulus)
- block:
- name: (Selfsigned validateion, {{ select_crypto_backend }} Validate certificate v2 is failed
assert:
that:
- selfsigned_v2_cert is failed
- "'The cryptography backend does not support v2 certificates' in selfsigned_v2_cert.msg"
when: select_crypto_backend == 'cryptography'
- name: (Selfsigned validation, {{select_crypto_backend}}) Validate certificate2 (test - privatekey modulus)
shell: 'openssl rsa -noout -modulus -in {{ output_dir }}/privatekey2.pem'
register: privatekey2_modulus
- name: Validate certificate2 (test - certificate modulus)
- name: (Selfsigned validation, {{select_crypto_backend}}) Validate certificate2 (test - certificate modulus)
shell: 'openssl x509 -noout -modulus -in {{ output_dir }}/cert2.pem'
register: cert2_modulus
- name: Validate certificate2 (assert)
- name: (Selfsigned validation, {{select_crypto_backend}}) Validate certificate2 (assert)
assert:
that:
- cert2_modulus.stdout == privatekey2_modulus.stdout
- name: Validate certificate3 (test - notBefore)
- name: (Selfsigned validation, {{select_crypto_backend}}) Validate certificate3 (test - notBefore)
shell: 'openssl x509 -noout -in {{ output_dir }}/cert3.pem -text | grep "Not Before" | sed "s/.*: \(.*\) .*/\1/g"'
register: cert3_notBefore
- name: Validate certificate3 (test - notAfter)
- name: (Selfsigned validation, {{select_crypto_backend}}) Validate certificate3 (test - notAfter)
shell: 'openssl x509 -noout -in {{ output_dir }}/cert3.pem -text | grep "Not After" | sed "s/.*: \(.*\) .*/\1/g"'
register: cert3_notAfter
- name: Validate certificate3 (assert - notBefore)
- name: (Selfsigned validation, {{select_crypto_backend}}) Validate certificate3 (assert - notBefore)
assert:
that:
- cert3_notBefore.stdout == 'Oct 23 13:37:42 2018'
- name: Validate certificate3 (assert - notAfter)
- name: (Selfsigned validation, {{select_crypto_backend}}) Validate certificate3 (assert - notAfter)
assert:
that:
- cert3_notAfter.stdout == 'Oct 23 13:37:42 2019'
- name: Validate ECC certificate (test - privatekey's pubkey)
- name: (Selfsigned validation, {{select_crypto_backend}}) Validate ECC certificate (test - privatekey's pubkey)
shell: 'openssl ec -pubout -in {{ output_dir }}/privatekey_ecc.pem'
register: privatekey_ecc_pubkey
- name: Validate ECC certificate (test - certificate pubkey)
- name: (Selfsigned validation, {{select_crypto_backend}}) Validate ECC certificate (test - certificate pubkey)
shell: 'openssl x509 -noout -pubkey -in {{ output_dir }}/cert_ecc.pem'
register: cert_ecc_pubkey
- name: Validate ECC certificate (assert)
- name: (Selfsigned validation, {{select_crypto_backend}}) Validate ECC certificate (assert)
assert:
that:
- cert_ecc_pubkey.stdout == privatekey_ecc_pubkey.stdout
- name:
- name: (Selfsigned validation, {{select_crypto_backend}})
assert:
that:
- passphrase_error_1 is failed