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:
parent
90c092a104
commit
36a790dcde
11 changed files with 1034 additions and 200 deletions
|
@ -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."
|
||||
|
|
|
@ -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):
|
||||
|
||||
|
|
|
@ -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':
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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', '>=')
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue