Remove PyOpenSSL backends (except for openssl_pkcs12) (#273)

* Remove Ubuntu 16.04 (Xenial Xerus) from CI.

* Removing PyOpenSSL backend from everywhere but openssl_pkcs12.

* Remove PyOpenSSL support from module_utils that's not needed for openssl_pkcs12.

* Add changelog fragment.
pull/288/head
Felix Fontein 2021-09-28 17:46:35 +02:00 committed by GitHub
parent 24e7d07973
commit f644db3c79
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
72 changed files with 227 additions and 2638 deletions

View File

@ -177,8 +177,6 @@ stages:
test: centos6 test: centos6
- name: Fedora 31 - name: Fedora 31
test: fedora31 test: fedora31
- name: Ubuntu 16.04
test: ubuntu1604
- stage: Docker_2_9 - stage: Docker_2_9
displayName: Docker 2.9 displayName: Docker 2.9
dependsOn: [] dependsOn: []
@ -193,8 +191,8 @@ stages:
test: centos7 test: centos7
- name: Fedora 31 - name: Fedora 31
test: fedora31 test: fedora31
- name: Ubuntu 16.04 - name: openSUSE 15 py3
test: ubuntu1604 test: opensuse15
- name: Ubuntu 18.04 - name: Ubuntu 18.04
test: ubuntu1804 test: ubuntu1804

View File

@ -0,0 +1,17 @@
removed_features:
- "get_certificate - removed the ``pyopenssl`` backend (https://github.com/ansible-collections/community.crypto/pull/273)."
- "openssl_csr - removed the ``pyopenssl`` backend (https://github.com/ansible-collections/community.crypto/pull/273)."
- "openssl_csr_info - removed the ``pyopenssl`` backend (https://github.com/ansible-collections/community.crypto/pull/273)."
- "openssl_csr_pipe - removed the ``pyopenssl`` backend (https://github.com/ansible-collections/community.crypto/pull/273)."
- "openssl_privatekey - removed the ``pyopenssl`` backend (https://github.com/ansible-collections/community.crypto/pull/273)."
- "openssl_privatekey_info - removed the ``pyopenssl`` backend (https://github.com/ansible-collections/community.crypto/pull/273)."
- "openssl_privatekey_pipe - removed the ``pyopenssl`` backend (https://github.com/ansible-collections/community.crypto/pull/273)."
- "openssl_publickey - removed the ``pyopenssl`` backend (https://github.com/ansible-collections/community.crypto/pull/273)."
- "openssl_publickey_info - removed the ``pyopenssl`` backend (https://github.com/ansible-collections/community.crypto/pull/273)."
- "openssl_signature - removed the ``pyopenssl`` backend (https://github.com/ansible-collections/community.crypto/pull/273)."
- "openssl_signature_info - removed the ``pyopenssl`` backend (https://github.com/ansible-collections/community.crypto/pull/273)."
- "x509_certificate - removed the ``pyopenssl`` backend (https://github.com/ansible-collections/community.crypto/pull/273)."
- "x509_certificate_info - removed the ``pyopenssl`` backend (https://github.com/ansible-collections/community.crypto/pull/273)."
- "x509_certificate_pipe - removed the ``pyopenssl`` backend (https://github.com/ansible-collections/community.crypto/pull/273)."
breaking_changes:
- "module_utils - removed various PyOpenSSL support functions and default backend values that are not needed for the openssl_pkcs12 module (https://github.com/ansible-collections/community.crypto/pull/273)."

View File

@ -33,7 +33,7 @@ options:
key." key."
- "Private keys can be created with the - "Private keys can be created with the
M(community.crypto.openssl_privatekey) or M(community.crypto.openssl_privatekey_pipe) M(community.crypto.openssl_privatekey) or M(community.crypto.openssl_privatekey_pipe)
modules. If the requisites (pyOpenSSL or cryptography) are not available, modules. If the requisite (cryptography) is not available,
keys can also be created directly with the C(openssl) command line tool: keys can also be created directly with the C(openssl) command line tool:
RSA keys can be created with C(openssl genrsa ...). Elliptic curve keys RSA keys can be created with C(openssl genrsa ...). Elliptic curve keys
can be created with C(openssl ecparam -genkey ...). Any other tool creating can be created with C(openssl ecparam -genkey ...). Any other tool creating

View File

@ -14,12 +14,9 @@ class ModuleDocFragment(object):
DOCUMENTATION = r''' DOCUMENTATION = r'''
description: description:
- This module allows one to (re)generate OpenSSL certificates. - This module allows one to (re)generate OpenSSL certificates.
- It uses the pyOpenSSL or cryptography python library to interact with OpenSSL. - It uses the 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)).
Please note that the PyOpenSSL backend was deprecated in Ansible 2.9 and will be removed in community.crypto 2.0.0.
requirements: requirements:
- PyOpenSSL >= 0.15 or cryptography >= 1.6 (if using C(selfsigned), C(ownca) or C(assertonly) provider) - cryptography >= 1.6 (if using C(selfsigned), C(ownca) or C(assertonly) provider)
options: options:
force: force:
description: description:
@ -58,14 +55,11 @@ options:
select_crypto_backend: select_crypto_backend:
description: description:
- Determines which crypto backend to use. - 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). - The default choice is C(auto), which tries to use C(cryptography) if available.
- 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. - If set to C(cryptography), will try to use the L(cryptography,https://cryptography.io/) library.
- Please note that the C(pyopenssl) backend has been deprecated in Ansible 2.9, and will be removed in community.crypto 2.0.0.
From that point on, only the C(cryptography) backend will be available.
type: str type: str
default: auto default: auto
choices: [ auto, cryptography, pyopenssl ] choices: [ auto, cryptography ]
notes: notes:
- All ASN.1 TIME values should be specified following the YYYYMMDDHHMMSSZ pattern. - All ASN.1 TIME values should be specified following the YYYYMMDDHHMMSSZ pattern.

View File

@ -15,13 +15,8 @@ description:
- This module allows one to (re)generate OpenSSL certificate signing requests. - This module allows one to (re)generate OpenSSL certificate signing requests.
- This module supports the subjectAltName, keyUsage, extendedKeyUsage, basicConstraints and OCSP Must Staple - This module supports the subjectAltName, keyUsage, extendedKeyUsage, basicConstraints and OCSP Must Staple
extensions. extensions.
- "The module can use the cryptography Python library, or the pyOpenSSL Python
library. By default, it tries to detect which one is available. This can be
overridden with the I(select_crypto_backend) option. Please note that the
PyOpenSSL backend was deprecated in Ansible 2.9 and will be removed in community.crypto 2.0.0."
requirements: requirements:
- Either cryptography >= 1.3 - cryptography >= 1.3
- Or pyOpenSSL >= 0.15
options: options:
digest: digest:
description: description:
@ -196,14 +191,11 @@ options:
select_crypto_backend: select_crypto_backend:
description: description:
- Determines which crypto backend to use. - 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). - The default choice is C(auto), which tries to use C(cryptography) if available.
- 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. - If set to C(cryptography), will try to use the L(cryptography,https://cryptography.io/) library.
- Please note that the C(pyopenssl) backend has been deprecated in Ansible 2.9, and will be removed in community.crypto 2.0.0.
From that point on, only the C(cryptography) backend will be available.
type: str type: str
default: auto default: auto
choices: [ auto, cryptography, pyopenssl ] choices: [ auto, cryptography ]
create_subject_key_identifier: create_subject_key_identifier:
description: description:
- Create the Subject Key Identifier from the public key. - Create the Subject Key Identifier from the public key.

View File

@ -22,13 +22,8 @@ description:
(or specify none), change the keysize, etc., the private key will be (or specify none), change the keysize, etc., the private key will be
regenerated. If you are concerned that this could B(overwrite your private key), regenerated. If you are concerned that this could B(overwrite your private key),
consider using the I(backup) option." consider using the I(backup) option."
- "The module can use the cryptography Python library, or the pyOpenSSL Python
library. By default, it tries to detect which one is available. This can be
overridden with the I(select_crypto_backend) option. Please note that the
PyOpenSSL backend was deprecated in Ansible 2.9 and will be removed in community.crypto 2.0.0."
requirements: requirements:
- Either cryptography >= 1.2.3 (older versions might work as well) - cryptography >= 1.2.3 (older versions might work as well)
- Or pyOpenSSL
options: options:
size: size:
description: description:
@ -80,22 +75,16 @@ options:
type: str type: str
cipher: cipher:
description: description:
- The cipher to encrypt the private key. (Valid values can be found by - The cipher to encrypt the private key. Must be C(auto).
running `openssl list -cipher-algorithms` or `openssl list-cipher-algorithms`,
depending on your OpenSSL version.)
- When using the C(cryptography) backend, use C(auto).
type: str type: str
select_crypto_backend: select_crypto_backend:
description: description:
- Determines which crypto backend to use. - 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). - The default choice is C(auto), which tries to use C(cryptography) if available.
- 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. - If set to C(cryptography), will try to use the L(cryptography,https://cryptography.io/) library.
- Please note that the C(pyopenssl) backend has been deprecated in Ansible 2.9, and will be removed in community.crypto 2.0.0.
From that point on, only the C(cryptography) backend will be available.
type: str type: str
default: auto default: auto
choices: [ auto, cryptography, pyopenssl ] choices: [ auto, cryptography ]
format: format:
description: description:
- Determines which format the private key is written in. By default, PKCS1 (traditional OpenSSL format) - Determines which format the private key is written in. By default, PKCS1 (traditional OpenSSL format)
@ -105,8 +94,6 @@ options:
selected one for generation. selected one for generation.
- Note that if the format for an existing private key mismatches, the key is B(regenerated) by default. - Note that if the format for an existing private key mismatches, the key is B(regenerated) by default.
To change this behavior, use the I(format_mismatch) option. To change this behavior, use the I(format_mismatch) option.
- The I(format) option is only supported by the C(cryptography) backend. The C(pyopenssl) backend will
fail if a value different from C(auto_ignore) is used.
type: str type: str
default: auto_ignore default: auto_ignore
choices: [ pkcs1, pkcs8, raw, auto, auto_ignore ] choices: [ pkcs1, pkcs8, raw, auto, auto_ignore ]

View File

@ -25,7 +25,6 @@ __metaclass__ = type
# please stop doing so. # please stop doing so.
from .basic import ( from .basic import (
HAS_PYOPENSSL,
CRYPTOGRAPHY_HAS_X25519, CRYPTOGRAPHY_HAS_X25519,
CRYPTOGRAPHY_HAS_X25519_FULL, CRYPTOGRAPHY_HAS_X25519_FULL,
CRYPTOGRAPHY_HAS_X448, CRYPTOGRAPHY_HAS_X448,
@ -74,12 +73,6 @@ from ._objects import OID_LOOKUP as _OID_LOOKUP
from ._objects import NORMALIZE_NAMES as _NORMALIZE_NAMES from ._objects import NORMALIZE_NAMES as _NORMALIZE_NAMES
from ._objects import NORMALIZE_NAMES_SHORT as _NORMALIZE_NAMES_SHORT from ._objects import NORMALIZE_NAMES_SHORT as _NORMALIZE_NAMES_SHORT
from .pyopenssl_support import (
pyopenssl_normalize_name,
pyopenssl_get_extensions_from_cert,
pyopenssl_get_extensions_from_csr,
)
from .support import ( from .support import (
get_fingerprint_of_bytes, get_fingerprint_of_bytes,
get_fingerprint, get_fingerprint,

View File

@ -22,14 +22,6 @@ __metaclass__ = type
from distutils.version import LooseVersion from distutils.version import LooseVersion
try:
import OpenSSL # noqa
from OpenSSL import crypto # noqa
HAS_PYOPENSSL = True
except ImportError:
# Error handled in the calling module.
HAS_PYOPENSSL = False
try: try:
import cryptography import cryptography
from cryptography import x509 from cryptography import x509

View File

@ -38,18 +38,6 @@ from ansible_collections.community.crypto.plugins.module_utils.crypto.module_bac
) )
MINIMAL_CRYPTOGRAPHY_VERSION = '1.6' 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
else:
PYOPENSSL_FOUND = True
CRYPTOGRAPHY_IMP_ERR = None CRYPTOGRAPHY_IMP_ERR = None
CRYPTOGRAPHY_VERSION = None CRYPTOGRAPHY_VERSION = None
@ -173,43 +161,12 @@ class CertificateBackend(object):
def _check_privatekey(self): def _check_privatekey(self):
"""Check whether provided parameters match, assuming self.existing_certificate and self.privatekey have been populated.""" """Check whether provided parameters match, assuming self.existing_certificate and self.privatekey have been populated."""
if self.backend == 'pyopenssl': if self.backend == 'cryptography':
ctx = OpenSSL.SSL.Context(OpenSSL.SSL.TLSv1_2_METHOD)
ctx.use_privatekey(self.privatekey)
ctx.use_certificate(self.existing_certificate)
try:
ctx.check_privatekey()
return True
except OpenSSL.SSL.Error:
return False
elif self.backend == 'cryptography':
return cryptography_compare_public_keys(self.existing_certificate.public_key(), self.privatekey.public_key()) return cryptography_compare_public_keys(self.existing_certificate.public_key(), self.privatekey.public_key())
def _check_csr(self): def _check_csr(self):
"""Check whether provided parameters match, assuming self.existing_certificate and self.csr have been populated.""" """Check whether provided parameters match, assuming self.existing_certificate and self.csr have been populated."""
if self.backend == 'pyopenssl': if self.backend == 'cryptography':
# Verify that CSR is signed by certificate's private key
try:
self.csr.verify(self.existing_certificate.get_pubkey())
except OpenSSL.crypto.Error:
return False
# Check subject
if self.check_csr_subject and self.csr.get_subject() != self.existing_certificate.get_subject():
return False
# Check extensions
if not self.check_csr_extensions:
return True
csr_extensions = self.csr.get_extensions()
cert_extension_count = self.existing_certificate.get_extension_count()
if len(csr_extensions) != cert_extension_count:
return False
for extension_number in range(0, cert_extension_count):
cert_extension = self.existing_certificate.get_extension(extension_number)
csr_extension = filter(lambda extension: extension.get_short_name() == cert_extension.get_short_name(), csr_extensions)
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 # Verify that CSR is signed by certificate's private key
if not self.csr.is_signature_valid: if not self.csr.is_signature_valid:
return False return False
@ -244,10 +201,6 @@ class CertificateBackend(object):
def _check_subject_key_identifier(self): def _check_subject_key_identifier(self):
"""Check whether Subject Key Identifier matches, assuming self.existing_certificate has been populated.""" """Check whether Subject Key Identifier matches, assuming self.existing_certificate has been populated."""
if self.backend != 'cryptography':
# We do not support SKI with pyOpenSSL backend
return True
# Get hold of certificate's SKI # Get hold of certificate's SKI
try: try:
ext = self.existing_certificate.extensions.get_extension_for_class(x509.SubjectKeyIdentifier) ext = self.existing_certificate.extensions.get_extension_for_class(x509.SubjectKeyIdentifier)
@ -328,10 +281,6 @@ class CertificateProvider(object):
def needs_version_two_certs(self, module): def needs_version_two_certs(self, module):
"""Whether the provider needs to create a version 2 certificate.""" """Whether the provider needs to create a version 2 certificate."""
def needs_pyopenssl_get_extensions(self, module):
"""Whether the provider needs to use get_extensions() with pyOpenSSL."""
return True
@abc.abstractmethod @abc.abstractmethod
def create_backend(self, module, backend): def create_backend(self, module, backend):
"""Create an implementation for a backend. """Create an implementation for a backend.
@ -352,45 +301,22 @@ def select_backend(module, backend, provider):
if backend == 'auto': if backend == 'auto':
# Detect what backend we can use # Detect what backend we can use
can_use_cryptography = CRYPTOGRAPHY_FOUND and CRYPTOGRAPHY_VERSION >= LooseVersion(MINIMAL_CRYPTOGRAPHY_VERSION) 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 cryptography is available we'll use it
if can_use_cryptography: if can_use_cryptography:
backend = 'cryptography' backend = 'cryptography'
elif can_use_pyopenssl:
backend = 'pyopenssl'
if provider.needs_version_two_certs(module):
module.warn('crypto backend forced to pyopenssl. The cryptography library does not support v2 certificates')
backend = 'pyopenssl'
# Fail if no backend has been found # Fail if no backend has been found
if backend == 'auto': if backend == 'auto':
module.fail_json(msg=("Can't detect any of the required Python libraries " module.fail_json(msg=("Can't detect the required Python library "
"cryptography (>= {0}) or PyOpenSSL (>= {1})").format( "cryptography (>= {0})").format(MINIMAL_CRYPTOGRAPHY_VERSION))
MINIMAL_CRYPTOGRAPHY_VERSION,
MINIMAL_PYOPENSSL_VERSION))
if backend == 'pyopenssl': if backend == 'cryptography':
module.deprecate('The module is using the PyOpenSSL backend. This backend has been deprecated',
version='2.0.0', collection_name='community.crypto')
if not PYOPENSSL_FOUND:
module.fail_json(msg=missing_required_lib('pyOpenSSL >= {0}'.format(MINIMAL_PYOPENSSL_VERSION)),
exception=PYOPENSSL_IMP_ERR)
if provider.needs_pyopenssl_get_extensions(module):
try:
getattr(crypto.X509Req, 'get_extensions')
except AttributeError:
module.fail_json(msg='You need to have PyOpenSSL>=0.15')
elif backend == 'cryptography':
if not CRYPTOGRAPHY_FOUND: if not CRYPTOGRAPHY_FOUND:
module.fail_json(msg=missing_required_lib('cryptography >= {0}'.format(MINIMAL_CRYPTOGRAPHY_VERSION)), module.fail_json(msg=missing_required_lib('cryptography >= {0}'.format(MINIMAL_CRYPTOGRAPHY_VERSION)),
exception=CRYPTOGRAPHY_IMP_ERR) exception=CRYPTOGRAPHY_IMP_ERR)
if provider.needs_version_two_certs(module): if provider.needs_version_two_certs(module):
module.fail_json(msg='The cryptography backend does not support v2 certificates, ' module.fail_json(msg='The cryptography backend does not support v2 certificates')
'use select_crypto_backend=pyopenssl for v2 certificates')
return provider.create_backend(module, backend) return provider.create_backend(module, backend)
@ -402,7 +328,7 @@ def get_certificate_argument_spec():
force=dict(type='bool', default=False,), force=dict(type='bool', default=False,),
csr_path=dict(type='path'), csr_path=dict(type='path'),
csr_content=dict(type='str'), csr_content=dict(type='str'),
select_crypto_backend=dict(type='str', default='auto', choices=['auto', 'cryptography', 'pyopenssl']), select_crypto_backend=dict(type='str', default='auto', choices=['auto', 'cryptography']),
# General properties of a certificate # General properties of a certificate
privatekey_path=dict(type='path'), privatekey_path=dict(type='path'),

View File

@ -25,10 +25,6 @@ from ansible_collections.community.crypto.plugins.module_utils.crypto.cryptograp
cryptography_parse_key_usage_params, cryptography_parse_key_usage_params,
) )
from ansible_collections.community.crypto.plugins.module_utils.crypto.pyopenssl_support import (
pyopenssl_normalize_name_attribute,
)
from ansible_collections.community.crypto.plugins.module_utils.crypto.module_backends.certificate import ( from ansible_collections.community.crypto.plugins.module_utils.crypto.module_backends.certificate import (
CertificateBackend, CertificateBackend,
CertificateProvider, CertificateProvider,
@ -460,164 +456,6 @@ class AssertOnlyCertificateBackendCryptography(AssertOnlyCertificateBackend):
return self.existing_certificate.not_valid_before, valid_in_date, self.existing_certificate.not_valid_after return self.existing_certificate.not_valid_before, valid_in_date, self.existing_certificate.not_valid_after
class AssertOnlyCertificateBackendPyOpenSSL(AssertOnlyCertificateBackend):
"""validate the supplied certificate."""
def __init__(self, module):
super(AssertOnlyCertificateBackendPyOpenSSL, self).__init__(module, 'pyopenssl')
# Ensure inputs are properly sanitized before comparison.
for param in ['signature_algorithms', 'key_usage', 'extended_key_usage',
'subject_alt_name', 'subject', 'issuer', 'not_before',
'not_after', 'valid_at', 'invalid_at']:
attr = getattr(self, param)
if isinstance(attr, list) and attr:
if isinstance(attr[0], str):
setattr(self, param, [to_bytes(item) for item in attr])
elif isinstance(attr[0], tuple):
setattr(self, param, [(to_bytes(item[0]), to_bytes(item[1])) for item in attr])
elif isinstance(attr, tuple):
setattr(self, param, dict((to_bytes(k), to_bytes(v)) for (k, v) in attr.items()))
elif isinstance(attr, dict):
setattr(self, param, dict((to_bytes(k), to_bytes(v)) for (k, v) in attr.items()))
elif isinstance(attr, str):
setattr(self, param, to_bytes(attr))
def _validate_privatekey(self):
ctx = OpenSSL.SSL.Context(OpenSSL.SSL.TLSv1_2_METHOD)
ctx.use_privatekey(self.privatekey)
ctx.use_certificate(self.existing_certificate)
try:
ctx.check_privatekey()
return True
except OpenSSL.SSL.Error:
return False
def _validate_csr_signature(self):
try:
self.csr.verify(self.existing_certificate.get_pubkey())
except OpenSSL.crypto.Error:
return False
def _validate_csr_subject(self):
if self.csr.get_subject() != self.existing_certificate.get_subject():
return False
def _validate_csr_extensions(self):
csr_extensions = self.csr.get_extensions()
cert_extension_count = self.existing_certificate.get_extension_count()
if len(csr_extensions) != cert_extension_count:
return False
for extension_number in range(0, cert_extension_count):
cert_extension = self.existing_certificate.get_extension(extension_number)
csr_extension = filter(lambda extension: extension.get_short_name() == cert_extension.get_short_name(), csr_extensions)
if cert_extension.get_data() != list(csr_extension)[0].get_data():
return False
return True
def _validate_signature_algorithms(self):
if self.existing_certificate.get_signature_algorithm() not in self.signature_algorithms:
return self.existing_certificate.get_signature_algorithm()
def _validate_subject(self):
expected_subject = [(OpenSSL._util.lib.OBJ_txt2nid(sub[0]), sub[1]) for sub in self.subject]
cert_subject = self.existing_certificate.get_subject().get_components()
current_subject = [(OpenSSL._util.lib.OBJ_txt2nid(sub[0]), sub[1]) for sub in cert_subject]
if not compare_sets(expected_subject, current_subject, self.subject_strict):
return expected_subject, current_subject
def _validate_issuer(self):
expected_issuer = [(OpenSSL._util.lib.OBJ_txt2nid(iss[0]), iss[1]) for iss in self.issuer]
cert_issuer = self.existing_certificate.get_issuer().get_components()
current_issuer = [(OpenSSL._util.lib.OBJ_txt2nid(iss[0]), iss[1]) for iss in cert_issuer]
if not compare_sets(expected_issuer, current_issuer, self.issuer_strict):
return self.issuer, cert_issuer
def _validate_has_expired(self):
# The following 3 lines are the same as the current PyOpenSSL code for cert.has_expired().
# Older version of PyOpenSSL have a buggy implementation,
# to avoid issues with those we added the code from a more recent release here.
time_string = to_native(self.existing_certificate.get_notAfter())
not_after = datetime.datetime.strptime(time_string, "%Y%m%d%H%M%SZ")
cert_expired = not_after < datetime.datetime.utcnow()
return cert_expired
def _validate_version(self):
# Version numbers in certs are off by one:
# v1: 0, v2: 1, v3: 2 ...
return self.existing_certificate.get_version() + 1
def _validate_key_usage(self):
found = False
for extension_idx in range(0, self.existing_certificate.get_extension_count()):
extension = self.existing_certificate.get_extension(extension_idx)
if extension.get_short_name() == b'keyUsage':
found = True
expected_extension = crypto.X509Extension(b"keyUsage", False, b', '.join(self.key_usage))
key_usage = [usage.strip() for usage in to_text(expected_extension, errors='surrogate_or_strict').split(',')]
current_ku = [usage.strip() for usage in to_text(extension, errors='surrogate_or_strict').split(',')]
if not compare_sets(key_usage, current_ku, self.key_usage_strict):
return self.key_usage, str(extension).split(', ')
if not found:
# This is only bad if the user specified a non-empty list
if self.key_usage:
return NO_EXTENSION
def _validate_extended_key_usage(self):
found = False
for extension_idx in range(0, self.existing_certificate.get_extension_count()):
extension = self.existing_certificate.get_extension(extension_idx)
if extension.get_short_name() == b'extendedKeyUsage':
found = True
extKeyUsage = [OpenSSL._util.lib.OBJ_txt2nid(keyUsage) for keyUsage in self.extended_key_usage]
current_xku = [OpenSSL._util.lib.OBJ_txt2nid(usage.strip()) for usage in
to_bytes(extension, errors='surrogate_or_strict').split(b',')]
if not compare_sets(extKeyUsage, current_xku, self.extended_key_usage_strict):
return self.extended_key_usage, str(extension).split(', ')
if not found:
# This is only bad if the user specified a non-empty list
if self.extended_key_usage:
return NO_EXTENSION
def _validate_subject_alt_name(self):
found = False
for extension_idx in range(0, self.existing_certificate.get_extension_count()):
extension = self.existing_certificate.get_extension(extension_idx)
if extension.get_short_name() == b'subjectAltName':
found = True
l_altnames = [pyopenssl_normalize_name_attribute(altname.strip()) for altname in
to_text(extension, errors='surrogate_or_strict').split(', ')]
sans = [pyopenssl_normalize_name_attribute(to_text(san, errors='surrogate_or_strict')) for san in self.subject_alt_name]
if not compare_sets(sans, l_altnames, self.subject_alt_name_strict):
return self.subject_alt_name, l_altnames
if not found:
# This is only bad if the user specified a non-empty list
if self.subject_alt_name:
return NO_EXTENSION
def _validate_not_before(self):
return self.existing_certificate.get_notBefore()
def _validate_not_after(self):
return self.existing_certificate.get_notAfter()
def _validate_valid_at(self):
rt = get_relative_time_option(self.valid_at, "valid_at", backend=self.backend)
rt = to_bytes(rt, errors='surrogate_or_strict')
return self.existing_certificate.get_notBefore(), rt, self.existing_certificate.get_notAfter()
def _validate_invalid_at(self):
rt = get_relative_time_option(self.invalid_at, "invalid_at", backend=self.backend)
rt = to_bytes(rt, errors='surrogate_or_strict')
return self.existing_certificate.get_notBefore(), rt, self.existing_certificate.get_notAfter()
def _validate_valid_in(self):
valid_in_asn1 = get_relative_time_option(self.valid_in, "valid_in", backend=self.backend)
valid_in_date = to_bytes(valid_in_asn1, errors='surrogate_or_strict')
return self.existing_certificate.get_notBefore(), valid_in_date, self.existing_certificate.get_notAfter()
class AssertOnlyCertificateProvider(CertificateProvider): class AssertOnlyCertificateProvider(CertificateProvider):
def validate_module_args(self, module): def validate_module_args(self, module):
module.deprecate("The 'assertonly' provider is deprecated; please see the examples of " module.deprecate("The 'assertonly' provider is deprecated; please see the examples of "
@ -630,8 +468,6 @@ class AssertOnlyCertificateProvider(CertificateProvider):
def create_backend(self, module, backend): def create_backend(self, module, backend):
if backend == 'cryptography': if backend == 'cryptography':
return AssertOnlyCertificateBackendCryptography(module) return AssertOnlyCertificateBackendCryptography(module)
if backend == 'pyopenssl':
return AssertOnlyCertificateBackendPyOpenSSL(module)
def add_assertonly_provider_to_argument_spec(argument_spec): def add_assertonly_provider_to_argument_spec(argument_spec):

View File

@ -58,18 +58,7 @@ class EntrustCertificateBackend(CertificateBackend):
# We want to always force behavior of trying to use the organization provided in the CSR. # We want to always force behavior of trying to use the organization provided in the CSR.
# To that end we need to parse out the organization from the CSR. # To that end we need to parse out the organization from the CSR.
self.csr_org = None self.csr_org = None
if self.backend == 'pyopenssl': if self.backend == 'cryptography':
csr_subject = self.csr.get_subject()
csr_subject_components = csr_subject.get_components()
for k, v in csr_subject_components:
if k.upper() == 'O':
# Entrust does not support multiple validated organizations in a single certificate
if self.csr_org is not None:
self.module.fail_json(msg=("Entrust provider does not currently support multiple validated organizations. Multiple organizations "
"found in Subject DN: '{0}'. ".format(csr_subject)))
else:
self.csr_org = v
elif self.backend == 'cryptography':
csr_subject_orgs = self.csr.subject.get_attributes_for_oid(NameOID.ORGANIZATION_NAME) csr_subject_orgs = self.csr.subject.get_attributes_for_oid(NameOID.ORGANIZATION_NAME)
if len(csr_subject_orgs) == 1: if len(csr_subject_orgs) == 1:
self.csr_org = csr_subject_orgs[0].value self.csr_org = csr_subject_orgs[0].value
@ -162,11 +151,7 @@ class EntrustCertificateBackend(CertificateBackend):
if self.existing_certificate: if self.existing_certificate:
serial_number = None serial_number = None
expiry = None expiry = None
if self.backend == 'pyopenssl': if self.backend == 'cryptography':
serial_number = "{0:X}".format(self.existing_certificate.get_serial_number())
time_string = to_native(self.existing_certificate.get_notAfter())
expiry = datetime.datetime.strptime(time_string, "%Y%m%d%H%M%SZ")
elif self.backend == 'cryptography':
serial_number = "{0:X}".format(cryptography_serial_number_of_cert(self.existing_certificate)) serial_number = "{0:X}".format(cryptography_serial_number_of_cert(self.existing_certificate))
expiry = self.existing_certificate.not_valid_after expiry = self.existing_certificate.not_valid_after

View File

@ -33,37 +33,11 @@ from ansible_collections.community.crypto.plugins.module_utils.crypto.cryptograp
cryptography_serial_number_of_cert, cryptography_serial_number_of_cert,
) )
from ansible_collections.community.crypto.plugins.module_utils.crypto.pyopenssl_support import (
pyopenssl_get_extensions_from_cert,
pyopenssl_normalize_name,
pyopenssl_normalize_name_attribute,
)
from ansible_collections.community.crypto.plugins.module_utils.crypto.module_backends.publickey_info import ( from ansible_collections.community.crypto.plugins.module_utils.crypto.module_backends.publickey_info import (
get_publickey_info, get_publickey_info,
) )
MINIMAL_CRYPTOGRAPHY_VERSION = '1.6' 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__)
if OpenSSL.SSL.OPENSSL_VERSION_NUMBER >= 0x10100000:
# OpenSSL 1.1.0 or newer
OPENSSL_MUST_STAPLE_NAME = b"tlsfeature"
OPENSSL_MUST_STAPLE_VALUE = b"status_request"
else:
# OpenSSL 1.0.x or older
OPENSSL_MUST_STAPLE_NAME = b"1.3.6.1.5.5.7.1.24"
OPENSSL_MUST_STAPLE_VALUE = b"DER:30:03:02:01:05"
except ImportError:
PYOPENSSL_IMP_ERR = traceback.format_exc()
PYOPENSSL_FOUND = False
else:
PYOPENSSL_FOUND = True
CRYPTOGRAPHY_IMP_ERR = None CRYPTOGRAPHY_IMP_ERR = None
try: try:
@ -209,20 +183,19 @@ class CertificateInfoRetrieval(object):
result['fingerprints'] = get_fingerprint_of_bytes( result['fingerprints'] = get_fingerprint_of_bytes(
self._get_der_bytes(), prefer_one=prefer_one_fingerprint) self._get_der_bytes(), prefer_one=prefer_one_fingerprint)
if self.backend != 'pyopenssl': ski = self._get_subject_key_identifier()
ski = self._get_subject_key_identifier() if ski is not None:
if ski is not None: ski = to_native(binascii.hexlify(ski))
ski = to_native(binascii.hexlify(ski)) ski = ':'.join([ski[i:i + 2] for i in range(0, len(ski), 2)])
ski = ':'.join([ski[i:i + 2] for i in range(0, len(ski), 2)]) result['subject_key_identifier'] = ski
result['subject_key_identifier'] = ski
aki, aci, acsn = self._get_authority_key_identifier() aki, aci, acsn = self._get_authority_key_identifier()
if aki is not None: if aki is not None:
aki = to_native(binascii.hexlify(aki)) aki = to_native(binascii.hexlify(aki))
aki = ':'.join([aki[i:i + 2] for i in range(0, len(aki), 2)]) aki = ':'.join([aki[i:i + 2] for i in range(0, len(aki), 2)])
result['authority_key_identifier'] = aki result['authority_key_identifier'] = aki
result['authority_cert_issuer'] = aci result['authority_cert_issuer'] = aci
result['authority_cert_serial_number'] = acsn result['authority_cert_serial_number'] = acsn
result['serial_number'] = self._get_serial_number() result['serial_number'] = self._get_serial_number()
result['extensions_by_oid'] = self._get_all_extensions() result['extensions_by_oid'] = self._get_all_extensions()
@ -392,136 +365,9 @@ class CertificateInfoRetrievalCryptography(CertificateInfoRetrieval):
return None return None
class CertificateInfoRetrievalPyOpenSSL(CertificateInfoRetrieval):
"""validate the supplied certificate."""
def __init__(self, module, content):
super(CertificateInfoRetrievalPyOpenSSL, self).__init__(module, 'pyopenssl', content)
def _get_der_bytes(self):
return crypto.dump_certificate(crypto.FILETYPE_ASN1, self.cert)
def _get_signature_algorithm(self):
return to_text(self.cert.get_signature_algorithm())
def __get_name(self, name):
result = []
for sub in name.get_components():
result.append([pyopenssl_normalize_name(sub[0]), to_text(sub[1])])
return result
def _get_subject_ordered(self):
return self.__get_name(self.cert.get_subject())
def _get_issuer_ordered(self):
return self.__get_name(self.cert.get_issuer())
def _get_version(self):
# Version numbers in certs are off by one:
# v1: 0, v2: 1, v3: 2 ...
return self.cert.get_version() + 1
def _get_extension(self, short_name):
for extension_idx in range(0, self.cert.get_extension_count()):
extension = self.cert.get_extension(extension_idx)
if extension.get_short_name() == short_name:
result = [
pyopenssl_normalize_name(usage.strip()) for usage in to_text(extension, errors='surrogate_or_strict').split(',')
]
return sorted(result), bool(extension.get_critical())
return None, False
def _get_key_usage(self):
return self._get_extension(b'keyUsage')
def _get_extended_key_usage(self):
return self._get_extension(b'extendedKeyUsage')
def _get_basic_constraints(self):
return self._get_extension(b'basicConstraints')
def _get_ocsp_must_staple(self):
extensions = [self.cert.get_extension(i) for i in range(0, self.cert.get_extension_count())]
oms_ext = [
ext for ext in extensions
if to_bytes(ext.get_short_name()) == OPENSSL_MUST_STAPLE_NAME and to_bytes(ext) == OPENSSL_MUST_STAPLE_VALUE
]
if OpenSSL.SSL.OPENSSL_VERSION_NUMBER < 0x10100000:
# Older versions of libssl don't know about OCSP Must Staple
oms_ext.extend([ext for ext in extensions if ext.get_short_name() == b'UNDEF' and ext.get_data() == b'\x30\x03\x02\x01\x05'])
if oms_ext:
return True, bool(oms_ext[0].get_critical())
else:
return None, False
def _get_subject_alt_name(self):
for extension_idx in range(0, self.cert.get_extension_count()):
extension = self.cert.get_extension(extension_idx)
if extension.get_short_name() == b'subjectAltName':
result = [pyopenssl_normalize_name_attribute(altname.strip()) for altname in
to_text(extension, errors='surrogate_or_strict').split(', ')]
return result, bool(extension.get_critical())
return None, False
def get_not_before(self):
time_string = to_native(self.cert.get_notBefore())
return datetime.datetime.strptime(time_string, "%Y%m%d%H%M%SZ")
def get_not_after(self):
time_string = to_native(self.cert.get_notAfter())
return datetime.datetime.strptime(time_string, "%Y%m%d%H%M%SZ")
def _get_public_key_pem(self):
try:
return crypto.dump_publickey(
crypto.FILETYPE_PEM,
self.cert.get_pubkey(),
)
except AttributeError:
try:
# pyOpenSSL < 16.0:
bio = crypto._new_mem_buf()
rc = crypto._lib.PEM_write_bio_PUBKEY(bio, self.cert.get_pubkey()._pkey)
if rc != 1:
crypto._raise_current_error()
return crypto._bio_to_string(bio)
except AttributeError:
self.module.warn('Your pyOpenSSL version does not support dumping public keys. '
'Please upgrade to version 16.0 or newer, or use the cryptography backend.')
def _get_public_key_object(self):
return self.cert.get_pubkey()
def _get_subject_key_identifier(self):
# Won't be implemented
return None
def _get_authority_key_identifier(self):
# Won't be implemented
return None, None, None
def _get_serial_number(self):
return self.cert.get_serial_number()
def _get_all_extensions(self):
return pyopenssl_get_extensions_from_cert(self.cert)
def _get_ocsp_uri(self):
for i in range(self.cert.get_extension_count()):
ext = self.cert.get_extension(i)
if ext.get_short_name() == b'authorityInfoAccess':
v = str(ext)
m = re.search('^OCSP - URI:(.*)$', v, flags=re.MULTILINE)
if m:
return m.group(1)
return None
def get_certificate_info(module, backend, content, prefer_one_fingerprint=False): def get_certificate_info(module, backend, content, prefer_one_fingerprint=False):
if backend == 'cryptography': if backend == 'cryptography':
info = CertificateInfoRetrievalCryptography(module, content) info = CertificateInfoRetrievalCryptography(module, content)
elif backend == 'pyopenssl':
info = CertificateInfoRetrievalPyOpenSSL(module, content)
return info.get_info(prefer_one_fingerprint=prefer_one_fingerprint) return info.get_info(prefer_one_fingerprint=prefer_one_fingerprint)
@ -529,34 +375,17 @@ def select_backend(module, backend, content):
if backend == 'auto': if backend == 'auto':
# Detection what is possible # Detection what is possible
can_use_cryptography = CRYPTOGRAPHY_FOUND and CRYPTOGRAPHY_VERSION >= LooseVersion(MINIMAL_CRYPTOGRAPHY_VERSION) can_use_cryptography = CRYPTOGRAPHY_FOUND and CRYPTOGRAPHY_VERSION >= LooseVersion(MINIMAL_CRYPTOGRAPHY_VERSION)
can_use_pyopenssl = PYOPENSSL_FOUND and PYOPENSSL_VERSION >= LooseVersion(MINIMAL_PYOPENSSL_VERSION)
# First try cryptography, then pyOpenSSL # Try cryptography
if can_use_cryptography: if can_use_cryptography:
backend = 'cryptography' backend = 'cryptography'
elif can_use_pyopenssl:
backend = 'pyopenssl'
# Success? # Success?
if backend == 'auto': if backend == 'auto':
module.fail_json(msg=("Can't detect any of the required Python libraries " module.fail_json(msg=("Can't detect any of the required Python libraries "
"cryptography (>= {0}) or PyOpenSSL (>= {1})").format( "cryptography (>= {0})").format(MINIMAL_CRYPTOGRAPHY_VERSION))
MINIMAL_CRYPTOGRAPHY_VERSION,
MINIMAL_PYOPENSSL_VERSION))
if backend == 'pyopenssl': if backend == 'cryptography':
if not PYOPENSSL_FOUND:
module.fail_json(msg=missing_required_lib('pyOpenSSL >= {0}'.format(MINIMAL_PYOPENSSL_VERSION)),
exception=PYOPENSSL_IMP_ERR)
try:
getattr(crypto.X509Req, 'get_extensions')
except AttributeError:
module.fail_json(msg='You need to have PyOpenSSL>=0.15 to generate CSRs')
module.deprecate('The module is using the PyOpenSSL backend. This backend has been deprecated',
version='2.0.0', collection_name='community.crypto')
return backend, CertificateInfoRetrievalPyOpenSSL(module, content)
elif backend == 'cryptography':
if not CRYPTOGRAPHY_FOUND: if not CRYPTOGRAPHY_FOUND:
module.fail_json(msg=missing_required_lib('cryptography >= {0}'.format(MINIMAL_CRYPTOGRAPHY_VERSION)), module.fail_json(msg=missing_required_lib('cryptography >= {0}'.format(MINIMAL_CRYPTOGRAPHY_VERSION)),
exception=CRYPTOGRAPHY_IMP_ERR) exception=CRYPTOGRAPHY_IMP_ERR)

View File

@ -227,100 +227,6 @@ def generate_serial_number():
return result return result
class OwnCACertificateBackendPyOpenSSL(CertificateBackend):
def __init__(self, module):
super(OwnCACertificateBackendPyOpenSSL, self).__init__(module, 'pyopenssl')
self.notBefore = get_relative_time_option(self.module.params['ownca_not_before'], 'ownca_not_before', backend=self.backend)
self.notAfter = get_relative_time_option(self.module.params['ownca_not_after'], 'ownca_not_after', backend=self.backend)
self.digest = self.module.params['ownca_digest']
self.version = self.module.params['ownca_version']
self.serial_number = generate_serial_number()
if self.module.params['ownca_create_subject_key_identifier'] != 'create_if_not_provided':
self.module.fail_json(msg='ownca_create_subject_key_identifier cannot be used with the pyOpenSSL backend!')
if self.module.params['ownca_create_authority_key_identifier']:
self.module.warn('ownca_create_authority_key_identifier is ignored by the pyOpenSSL backend!')
self.ca_cert_path = self.module.params['ownca_path']
self.ca_cert_content = self.module.params['ownca_content']
if self.ca_cert_content is not None:
self.ca_cert_content = self.ca_cert_content.encode('utf-8')
self.ca_privatekey_path = self.module.params['ownca_privatekey_path']
self.ca_privatekey_content = self.module.params['ownca_privatekey_content']
if self.ca_privatekey_content is not None:
self.ca_privatekey_content = self.ca_privatekey_content.encode('utf-8')
self.ca_privatekey_passphrase = self.module.params['ownca_privatekey_passphrase']
if self.csr_content is None and not os.path.exists(self.csr_path):
raise CertificateError(
'The certificate signing request file {0} does not exist'.format(self.csr_path)
)
if self.ca_cert_content is None and not os.path.exists(self.ca_cert_path):
raise CertificateError(
'The CA certificate file {0} does not exist'.format(self.ca_cert_path)
)
if self.ca_privatekey_content is None and not os.path.exists(self.ca_privatekey_path):
raise CertificateError(
'The CA private key file {0} does not exist'.format(self.ca_privatekey_path)
)
self._ensure_csr_loaded()
self.ca_cert = load_certificate(
path=self.ca_cert_path,
content=self.ca_cert_content,
)
try:
self.ca_privatekey = load_privatekey(
path=self.ca_privatekey_path,
content=self.ca_privatekey_content,
passphrase=self.ca_privatekey_passphrase
)
except OpenSSLBadPassphraseError as exc:
self.module.fail_json(msg=str(exc))
def generate_certificate(self):
"""(Re-)Generate certificate."""
cert = crypto.X509()
cert.set_serial_number(self.serial_number)
cert.set_notBefore(to_bytes(self.notBefore))
cert.set_notAfter(to_bytes(self.notAfter))
cert.set_subject(self.csr.get_subject())
cert.set_issuer(self.ca_cert.get_subject())
cert.set_version(self.version - 1)
cert.set_pubkey(self.csr.get_pubkey())
cert.add_extensions(self.csr.get_extensions())
cert.sign(self.ca_privatekey, self.digest)
self.cert = cert
def get_certificate_data(self):
"""Return bytes for self.cert."""
return crypto.dump_certificate(crypto.FILETYPE_PEM, self.cert)
def dump(self, include_certificate):
result = super(OwnCACertificateBackendPyOpenSSL, self).dump(include_certificate)
result.update({
'ca_cert': self.ca_cert_path,
'ca_privatekey': self.ca_privatekey_path,
})
if self.module.check_mode:
result.update({
'notBefore': self.notBefore,
'notAfter': self.notAfter,
'serial_number': self.serial_number,
})
else:
if self.cert is None:
self.cert = self.existing_certificate
result.update({
'notBefore': self.cert.get_notBefore(),
'notAfter': self.cert.get_notAfter(),
'serial_number': self.cert.get_serial_number(),
})
return result
class OwnCACertificateProvider(CertificateProvider): class OwnCACertificateProvider(CertificateProvider):
def validate_module_args(self, module): def validate_module_args(self, module):
if module.params['ownca_path'] is None and module.params['ownca_content'] is None: if module.params['ownca_path'] is None and module.params['ownca_content'] is None:
@ -334,8 +240,6 @@ class OwnCACertificateProvider(CertificateProvider):
def create_backend(self, module, backend): def create_backend(self, module, backend):
if backend == 'cryptography': if backend == 'cryptography':
return OwnCACertificateBackendCryptography(module) return OwnCACertificateBackendCryptography(module)
if backend == 'pyopenssl':
return OwnCACertificateBackendPyOpenSSL(module)
def add_ownca_provider_to_argument_spec(argument_spec): def add_ownca_provider_to_argument_spec(argument_spec):

View File

@ -163,76 +163,6 @@ def generate_serial_number():
return result return result
class SelfSignedCertificateBackendPyOpenSSL(CertificateBackend):
def __init__(self, module):
super(SelfSignedCertificateBackendPyOpenSSL, self).__init__(module, 'pyopenssl')
if module.params['selfsigned_create_subject_key_identifier'] != 'create_if_not_provided':
module.fail_json(msg='selfsigned_create_subject_key_identifier cannot be used with the pyOpenSSL backend!')
self.notBefore = get_relative_time_option(module.params['selfsigned_not_before'], 'selfsigned_not_before', backend=self.backend)
self.notAfter = get_relative_time_option(module.params['selfsigned_not_after'], 'selfsigned_not_after', backend=self.backend)
self.digest = module.params['selfsigned_digest']
self.version = module.params['selfsigned_version']
self.serial_number = generate_serial_number()
if self.csr_path is not None and not os.path.exists(self.csr_path):
raise CertificateError(
'The certificate signing request file {0} does not exist'.format(self.csr_path)
)
if self.privatekey_content is None and not os.path.exists(self.privatekey_path):
raise CertificateError(
'The private key file {0} does not exist'.format(self.privatekey_path)
)
self._ensure_private_key_loaded()
self._ensure_csr_loaded()
if self.csr is None:
# Create empty CSR on the fly
self.csr = crypto.X509Req()
self.csr.set_pubkey(self.privatekey)
self.csr.sign(self.privatekey, self.digest)
def generate_certificate(self):
"""(Re-)Generate certificate."""
cert = crypto.X509()
cert.set_serial_number(self.serial_number)
cert.set_notBefore(to_bytes(self.notBefore))
cert.set_notAfter(to_bytes(self.notAfter))
cert.set_subject(self.csr.get_subject())
cert.set_issuer(self.csr.get_subject())
cert.set_version(self.version - 1)
cert.set_pubkey(self.csr.get_pubkey())
cert.add_extensions(self.csr.get_extensions())
cert.sign(self.privatekey, self.digest)
self.cert = cert
def get_certificate_data(self):
"""Return bytes for self.cert."""
return crypto.dump_certificate(crypto.FILETYPE_PEM, self.cert)
def dump(self, include_certificate):
result = super(SelfSignedCertificateBackendPyOpenSSL, self).dump(include_certificate)
if self.module.check_mode:
result.update({
'notBefore': self.notBefore,
'notAfter': self.notAfter,
'serial_number': self.serial_number,
})
else:
if self.cert is None:
self.cert = self.existing_certificate
result.update({
'notBefore': self.cert.get_notBefore(),
'notAfter': self.cert.get_notAfter(),
'serial_number': self.cert.get_serial_number(),
})
return result
class SelfSignedCertificateProvider(CertificateProvider): class SelfSignedCertificateProvider(CertificateProvider):
def validate_module_args(self, module): def validate_module_args(self, module):
if module.params['privatekey_path'] is None and module.params['privatekey_content'] is None: if module.params['privatekey_path'] is None and module.params['privatekey_content'] is None:
@ -244,8 +174,6 @@ class SelfSignedCertificateProvider(CertificateProvider):
def create_backend(self, module, backend): def create_backend(self, module, backend):
if backend == 'cryptography': if backend == 'cryptography':
return SelfSignedCertificateBackendCryptography(module) return SelfSignedCertificateBackendCryptography(module)
if backend == 'pyopenssl':
return SelfSignedCertificateBackendPyOpenSSL(module)
def add_selfsigned_provider_to_argument_spec(argument_spec): def add_selfsigned_provider_to_argument_spec(argument_spec):

View File

@ -43,11 +43,6 @@ from ansible_collections.community.crypto.plugins.module_utils.crypto.cryptograp
REVOCATION_REASON_MAP, REVOCATION_REASON_MAP,
) )
from ansible_collections.community.crypto.plugins.module_utils.crypto.pyopenssl_support import (
pyopenssl_normalize_name_attribute,
pyopenssl_parse_name_constraints,
)
from ansible_collections.community.crypto.plugins.module_utils.crypto.module_backends.csr_info import ( from ansible_collections.community.crypto.plugins.module_utils.crypto.module_backends.csr_info import (
get_csr_info, get_csr_info,
) )
@ -55,28 +50,8 @@ from ansible_collections.community.crypto.plugins.module_utils.crypto.module_bac
from ansible_collections.community.crypto.plugins.module_utils.crypto.module_backends.common import ArgumentSpec from ansible_collections.community.crypto.plugins.module_utils.crypto.module_backends.common import ArgumentSpec
MINIMAL_PYOPENSSL_VERSION = '0.15'
MINIMAL_CRYPTOGRAPHY_VERSION = '1.3' MINIMAL_CRYPTOGRAPHY_VERSION = '1.3'
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
else:
PYOPENSSL_FOUND = True
if OpenSSL.SSL.OPENSSL_VERSION_NUMBER >= 0x10100000:
# OpenSSL 1.1.0 or newer
OPENSSL_MUST_STAPLE_NAME = b"tlsfeature"
OPENSSL_MUST_STAPLE_VALUE = b"status_request"
else:
# OpenSSL 1.0.x or older
OPENSSL_MUST_STAPLE_NAME = b"1.3.6.1.5.5.7.1.24"
OPENSSL_MUST_STAPLE_VALUE = b"DER:30:03:02:01:05"
CRYPTOGRAPHY_IMP_ERR = None CRYPTOGRAPHY_IMP_ERR = None
try: try:
import cryptography import cryptography
@ -273,174 +248,6 @@ class CertificateSigningRequestBackend(object):
return result return result
# Implementation with using pyOpenSSL
class CertificateSigningRequestPyOpenSSLBackend(CertificateSigningRequestBackend):
def __init__(self, module):
for o in ('create_subject_key_identifier', ):
if module.params[o]:
module.fail_json(msg='You cannot use {0} with the pyOpenSSL backend!'.format(o))
for o in ('subject_key_identifier', 'authority_key_identifier', 'authority_cert_issuer', 'authority_cert_serial_number', 'crl_distribution_points'):
if module.params[o] is not None:
module.fail_json(msg='You cannot use {0} with the pyOpenSSL backend!'.format(o))
super(CertificateSigningRequestPyOpenSSLBackend, self).__init__(module, 'pyopenssl')
def generate_csr(self):
"""(Re-)Generate CSR."""
self._ensure_private_key_loaded()
req = crypto.X509Req()
req.set_version(self.version - 1)
subject = req.get_subject()
for entry in self.subject:
if entry[1] is not None:
# Workaround for https://github.com/pyca/pyopenssl/issues/165
nid = OpenSSL._util.lib.OBJ_txt2nid(to_bytes(entry[0]))
if nid == 0:
raise CertificateSigningRequestError('Unknown subject field identifier "{0}"'.format(entry[0]))
res = OpenSSL._util.lib.X509_NAME_add_entry_by_NID(subject._name, nid, OpenSSL._util.lib.MBSTRING_UTF8, to_bytes(entry[1]), -1, -1, 0)
if res == 0:
raise CertificateSigningRequestError('Invalid value for subject field identifier "{0}": {1}'.format(entry[0], entry[1]))
extensions = []
if self.subjectAltName:
altnames = ', '.join(self.subjectAltName)
try:
extensions.append(crypto.X509Extension(b"subjectAltName", self.subjectAltName_critical, altnames.encode('ascii')))
except OpenSSL.crypto.Error as e:
raise CertificateSigningRequestError(
'Error while parsing Subject Alternative Names {0} (check for missing type prefix, such as "DNS:"!): {1}'.format(
', '.join(["{0}".format(san) for san in self.subjectAltName]), str(e)
)
)
if self.keyUsage:
usages = ', '.join(self.keyUsage)
extensions.append(crypto.X509Extension(b"keyUsage", self.keyUsage_critical, usages.encode('ascii')))
if self.extendedKeyUsage:
usages = ', '.join(self.extendedKeyUsage)
extensions.append(crypto.X509Extension(b"extendedKeyUsage", self.extendedKeyUsage_critical, usages.encode('ascii')))
if self.basicConstraints:
usages = ', '.join(self.basicConstraints)
extensions.append(crypto.X509Extension(b"basicConstraints", self.basicConstraints_critical, usages.encode('ascii')))
if self.name_constraints_permitted or self.name_constraints_excluded:
usages = ', '.join(
['permitted;{0}'.format(name) for name in self.name_constraints_permitted] +
['excluded;{0}'.format(name) for name in self.name_constraints_excluded]
)
extensions.append(crypto.X509Extension(b"nameConstraints", self.name_constraints_critical, usages.encode('ascii')))
if self.ocspMustStaple:
extensions.append(crypto.X509Extension(OPENSSL_MUST_STAPLE_NAME, self.ocspMustStaple_critical, OPENSSL_MUST_STAPLE_VALUE))
if extensions:
req.add_extensions(extensions)
req.set_pubkey(self.privatekey)
req.sign(self.privatekey, self.digest)
self.csr = req
def get_csr_data(self):
"""Return bytes for self.csr."""
return crypto.dump_certificate_request(crypto.FILETYPE_PEM, self.csr)
def _check_csr(self):
def _check_subject(csr):
subject = [(OpenSSL._util.lib.OBJ_txt2nid(to_bytes(sub[0])), to_bytes(sub[1])) for sub in self.subject]
current_subject = [(OpenSSL._util.lib.OBJ_txt2nid(to_bytes(sub[0])), to_bytes(sub[1])) for sub in csr.get_subject().get_components()]
if not set(subject) == set(current_subject):
return False
return True
def _check_subjectAltName(extensions):
altnames_ext = next((ext for ext in extensions if ext.get_short_name() == b'subjectAltName'), '')
altnames = [pyopenssl_normalize_name_attribute(altname.strip()) for altname in
to_text(altnames_ext, errors='surrogate_or_strict').split(',') if altname.strip()]
if self.subjectAltName:
if (set(altnames) != set([pyopenssl_normalize_name_attribute(to_text(name)) for name in self.subjectAltName]) or
altnames_ext.get_critical() != self.subjectAltName_critical):
return False
else:
if altnames:
return False
return True
def _check_keyUsage_(extensions, extName, expected, critical):
usages_ext = [ext for ext in extensions if ext.get_short_name() == extName]
if (not usages_ext and expected) or (usages_ext and not expected):
return False
elif not usages_ext and not expected:
return True
else:
current = [OpenSSL._util.lib.OBJ_txt2nid(to_bytes(usage.strip())) for usage in str(usages_ext[0]).split(',')]
expected = [OpenSSL._util.lib.OBJ_txt2nid(to_bytes(usage)) for usage in expected]
return set(current) == set(expected) and usages_ext[0].get_critical() == critical
def _check_keyUsage(extensions):
usages_ext = [ext for ext in extensions if ext.get_short_name() == b'keyUsage']
if (not usages_ext and self.keyUsage) or (usages_ext and not self.keyUsage):
return False
elif not usages_ext and not self.keyUsage:
return True
else:
# OpenSSL._util.lib.OBJ_txt2nid() always returns 0 for all keyUsage values
# (since keyUsage has a fixed bitfield for these values and is not extensible).
# Therefore, we create an extension for the wanted values, and compare the
# data of the extensions (which is the serialized bitfield).
expected_ext = crypto.X509Extension(b"keyUsage", False, ', '.join(self.keyUsage).encode('ascii'))
return usages_ext[0].get_data() == expected_ext.get_data() and usages_ext[0].get_critical() == self.keyUsage_critical
def _check_extenededKeyUsage(extensions):
return _check_keyUsage_(extensions, b'extendedKeyUsage', self.extendedKeyUsage, self.extendedKeyUsage_critical)
def _check_basicConstraints(extensions):
return _check_keyUsage_(extensions, b'basicConstraints', self.basicConstraints, self.basicConstraints_critical)
def _check_nameConstraints(extensions):
nc_ext = next((ext for ext in extensions if ext.get_short_name() == b'nameConstraints'), '')
permitted, excluded = pyopenssl_parse_name_constraints(nc_ext)
if self.name_constraints_permitted or self.name_constraints_excluded:
if set(permitted) != set([pyopenssl_normalize_name_attribute(to_text(name)) for name in self.name_constraints_permitted]):
return False
if set(excluded) != set([pyopenssl_normalize_name_attribute(to_text(name)) for name in self.name_constraints_excluded]):
return False
if nc_ext.get_critical() != self.name_constraints_critical:
return False
else:
if permitted or excluded:
return False
return True
def _check_ocspMustStaple(extensions):
oms_ext = [ext for ext in extensions if to_bytes(ext.get_short_name()) == OPENSSL_MUST_STAPLE_NAME and to_bytes(ext) == OPENSSL_MUST_STAPLE_VALUE]
if OpenSSL.SSL.OPENSSL_VERSION_NUMBER < 0x10100000:
# Older versions of libssl don't know about OCSP Must Staple
oms_ext.extend([ext for ext in extensions if ext.get_short_name() == b'UNDEF' and ext.get_data() == b'\x30\x03\x02\x01\x05'])
if self.ocspMustStaple:
return len(oms_ext) > 0 and oms_ext[0].get_critical() == self.ocspMustStaple_critical
else:
return len(oms_ext) == 0
def _check_extensions(csr):
extensions = csr.get_extensions()
return (_check_subjectAltName(extensions) and _check_keyUsage(extensions) and
_check_extenededKeyUsage(extensions) and _check_basicConstraints(extensions) and
_check_ocspMustStaple(extensions) and _check_nameConstraints(extensions))
def _check_signature(csr):
try:
return csr.verify(self.privatekey)
except crypto.Error:
return False
return _check_subject(self.existing_csr) and _check_extensions(self.existing_csr) and _check_signature(self.existing_csr)
def parse_crl_distribution_points(module, crl_distribution_points): def parse_crl_distribution_points(module, crl_distribution_points):
result = [] result = []
for index, parse_crl_distribution_point in enumerate(crl_distribution_points): for index, parse_crl_distribution_point in enumerate(crl_distribution_points):
@ -762,34 +569,17 @@ def select_backend(module, backend):
if backend == 'auto': if backend == 'auto':
# Detection what is possible # Detection what is possible
can_use_cryptography = CRYPTOGRAPHY_FOUND and CRYPTOGRAPHY_VERSION >= LooseVersion(MINIMAL_CRYPTOGRAPHY_VERSION) can_use_cryptography = CRYPTOGRAPHY_FOUND and CRYPTOGRAPHY_VERSION >= LooseVersion(MINIMAL_CRYPTOGRAPHY_VERSION)
can_use_pyopenssl = PYOPENSSL_FOUND and PYOPENSSL_VERSION >= LooseVersion(MINIMAL_PYOPENSSL_VERSION)
# First try cryptography, then pyOpenSSL # Try cryptography
if can_use_cryptography: if can_use_cryptography:
backend = 'cryptography' backend = 'cryptography'
elif can_use_pyopenssl:
backend = 'pyopenssl'
# Success? # Success?
if backend == 'auto': if backend == 'auto':
module.fail_json(msg=("Can't detect any of the required Python libraries " module.fail_json(msg=("Can't detect any of the required Python libraries "
"cryptography (>= {0}) or PyOpenSSL (>= {1})").format( "cryptography (>= {0})").format(MINIMAL_CRYPTOGRAPHY_VERSION))
MINIMAL_CRYPTOGRAPHY_VERSION,
MINIMAL_PYOPENSSL_VERSION))
if backend == 'pyopenssl': if backend == 'cryptography':
if not PYOPENSSL_FOUND:
module.fail_json(msg=missing_required_lib('pyOpenSSL >= {0}'.format(MINIMAL_PYOPENSSL_VERSION)),
exception=PYOPENSSL_IMP_ERR)
try:
getattr(crypto.X509Req, 'get_extensions')
except AttributeError:
module.fail_json(msg='You need to have PyOpenSSL>=0.15 to generate CSRs')
module.deprecate('The module is using the PyOpenSSL backend. This backend has been deprecated',
version='2.0.0', collection_name='community.crypto')
return backend, CertificateSigningRequestPyOpenSSLBackend(module)
elif backend == 'cryptography':
if not CRYPTOGRAPHY_FOUND: if not CRYPTOGRAPHY_FOUND:
module.fail_json(msg=missing_required_lib('cryptography >= {0}'.format(MINIMAL_CRYPTOGRAPHY_VERSION)), module.fail_json(msg=missing_required_lib('cryptography >= {0}'.format(MINIMAL_CRYPTOGRAPHY_VERSION)),
exception=CRYPTOGRAPHY_IMP_ERR) exception=CRYPTOGRAPHY_IMP_ERR)
@ -853,7 +643,7 @@ def get_csr_argument_spec():
), ),
mutually_exclusive=[('full_name', 'relative_name')] mutually_exclusive=[('full_name', 'relative_name')]
), ),
select_crypto_backend=dict(type='str', default='auto', choices=['auto', 'cryptography', 'pyopenssl']), select_crypto_backend=dict(type='str', default='auto', choices=['auto', 'cryptography']),
), ),
required_together=[ required_together=[
['authority_cert_issuer', 'authority_cert_serial_number'], ['authority_cert_issuer', 'authority_cert_serial_number'],

View File

@ -30,38 +30,11 @@ from ansible_collections.community.crypto.plugins.module_utils.crypto.cryptograp
cryptography_oid_to_name, cryptography_oid_to_name,
) )
from ansible_collections.community.crypto.plugins.module_utils.crypto.pyopenssl_support import (
pyopenssl_get_extensions_from_csr,
pyopenssl_normalize_name,
pyopenssl_normalize_name_attribute,
pyopenssl_parse_name_constraints,
)
from ansible_collections.community.crypto.plugins.module_utils.crypto.module_backends.publickey_info import ( from ansible_collections.community.crypto.plugins.module_utils.crypto.module_backends.publickey_info import (
get_publickey_info, get_publickey_info,
) )
MINIMAL_CRYPTOGRAPHY_VERSION = '1.3' MINIMAL_CRYPTOGRAPHY_VERSION = '1.3'
MINIMAL_PYOPENSSL_VERSION = '0.15'
PYOPENSSL_IMP_ERR = None
try:
import OpenSSL
from OpenSSL import crypto
PYOPENSSL_VERSION = LooseVersion(OpenSSL.__version__)
if OpenSSL.SSL.OPENSSL_VERSION_NUMBER >= 0x10100000:
# OpenSSL 1.1.0 or newer
OPENSSL_MUST_STAPLE_NAME = b"tlsfeature"
OPENSSL_MUST_STAPLE_VALUE = b"status_request"
else:
# OpenSSL 1.0.x or older
OPENSSL_MUST_STAPLE_NAME = b"1.3.6.1.5.5.7.1.24"
OPENSSL_MUST_STAPLE_VALUE = b"DER:30:03:02:01:05"
except ImportError:
PYOPENSSL_IMP_ERR = traceback.format_exc()
PYOPENSSL_FOUND = False
else:
PYOPENSSL_FOUND = True
CRYPTOGRAPHY_IMP_ERR = None CRYPTOGRAPHY_IMP_ERR = None
try: try:
@ -173,20 +146,19 @@ class CSRInfoRetrieval(object):
'public_key_fingerprints': public_key_info['fingerprints'], 'public_key_fingerprints': public_key_info['fingerprints'],
}) })
if self.backend != 'pyopenssl': ski = self._get_subject_key_identifier()
ski = self._get_subject_key_identifier() if ski is not None:
if ski is not None: ski = to_native(binascii.hexlify(ski))
ski = to_native(binascii.hexlify(ski)) ski = ':'.join([ski[i:i + 2] for i in range(0, len(ski), 2)])
ski = ':'.join([ski[i:i + 2] for i in range(0, len(ski), 2)]) result['subject_key_identifier'] = ski
result['subject_key_identifier'] = ski
aki, aci, acsn = self._get_authority_key_identifier() aki, aci, acsn = self._get_authority_key_identifier()
if aki is not None: if aki is not None:
aki = to_native(binascii.hexlify(aki)) aki = to_native(binascii.hexlify(aki))
aki = ':'.join([aki[i:i + 2] for i in range(0, len(aki), 2)]) aki = ':'.join([aki[i:i + 2] for i in range(0, len(aki), 2)])
result['authority_key_identifier'] = aki result['authority_key_identifier'] = aki
result['authority_cert_issuer'] = aci result['authority_cert_issuer'] = aci
result['authority_cert_serial_number'] = acsn result['authority_cert_serial_number'] = acsn
result['extensions_by_oid'] = self._get_all_extensions() result['extensions_by_oid'] = self._get_all_extensions()
@ -332,112 +304,9 @@ class CSRInfoRetrievalCryptography(CSRInfoRetrieval):
return self.csr.is_signature_valid return self.csr.is_signature_valid
class CSRInfoRetrievalPyOpenSSL(CSRInfoRetrieval):
"""validate the supplied CSR."""
def __init__(self, module, content, validate_signature):
super(CSRInfoRetrievalPyOpenSSL, self).__init__(module, 'pyopenssl', content, validate_signature)
def __get_name(self, name):
result = []
for sub in name.get_components():
result.append([pyopenssl_normalize_name(sub[0]), to_text(sub[1])])
return result
def _get_subject_ordered(self):
return self.__get_name(self.csr.get_subject())
def _get_extension(self, short_name):
for extension in self.csr.get_extensions():
if extension.get_short_name() == short_name:
result = [
pyopenssl_normalize_name(usage.strip()) for usage in to_text(extension, errors='surrogate_or_strict').split(',')
]
return sorted(result), bool(extension.get_critical())
return None, False
def _get_key_usage(self):
return self._get_extension(b'keyUsage')
def _get_extended_key_usage(self):
return self._get_extension(b'extendedKeyUsage')
def _get_basic_constraints(self):
return self._get_extension(b'basicConstraints')
def _get_ocsp_must_staple(self):
extensions = self.csr.get_extensions()
oms_ext = [
ext for ext in extensions
if to_bytes(ext.get_short_name()) == OPENSSL_MUST_STAPLE_NAME and to_bytes(ext) == OPENSSL_MUST_STAPLE_VALUE
]
if OpenSSL.SSL.OPENSSL_VERSION_NUMBER < 0x10100000:
# Older versions of libssl don't know about OCSP Must Staple
oms_ext.extend([ext for ext in extensions if ext.get_short_name() == b'UNDEF' and ext.get_data() == b'\x30\x03\x02\x01\x05'])
if oms_ext:
return True, bool(oms_ext[0].get_critical())
else:
return None, False
def _get_subject_alt_name(self):
for extension in self.csr.get_extensions():
if extension.get_short_name() == b'subjectAltName':
result = [pyopenssl_normalize_name_attribute(altname.strip()) for altname in
to_text(extension, errors='surrogate_or_strict').split(', ')]
return result, bool(extension.get_critical())
return None, False
def _get_name_constraints(self):
for extension in self.csr.get_extensions():
if extension.get_short_name() == b'nameConstraints':
permitted, excluded = pyopenssl_parse_name_constraints(extension)
return permitted, excluded, bool(extension.get_critical())
return None, None, False
def _get_public_key_pem(self):
try:
return crypto.dump_publickey(
crypto.FILETYPE_PEM,
self.csr.get_pubkey(),
)
except AttributeError:
try:
bio = crypto._new_mem_buf()
rc = crypto._lib.PEM_write_bio_PUBKEY(bio, self.csr.get_pubkey()._pkey)
if rc != 1:
crypto._raise_current_error()
return crypto._bio_to_string(bio)
except AttributeError:
self.module.warn('Your pyOpenSSL version does not support dumping public keys. '
'Please upgrade to version 16.0 or newer, or use the cryptography backend.')
def _get_public_key_object(self):
return self.csr.get_pubkey()
def _get_subject_key_identifier(self):
# Won't be implemented
return None
def _get_authority_key_identifier(self):
# Won't be implemented
return None, None, None
def _get_all_extensions(self):
return pyopenssl_get_extensions_from_csr(self.csr)
def _is_signature_valid(self):
try:
return bool(self.csr.verify(self.csr.get_pubkey()))
except crypto.Error:
# OpenSSL error means that key is not consistent
return False
def get_csr_info(module, backend, content, validate_signature=True, prefer_one_fingerprint=False): def get_csr_info(module, backend, content, validate_signature=True, prefer_one_fingerprint=False):
if backend == 'cryptography': if backend == 'cryptography':
info = CSRInfoRetrievalCryptography(module, content, validate_signature=validate_signature) info = CSRInfoRetrievalCryptography(module, content, validate_signature=validate_signature)
elif backend == 'pyopenssl':
info = CSRInfoRetrievalPyOpenSSL(module, content, validate_signature=validate_signature)
return info.get_info(prefer_one_fingerprint=prefer_one_fingerprint) return info.get_info(prefer_one_fingerprint=prefer_one_fingerprint)
@ -445,34 +314,17 @@ def select_backend(module, backend, content, validate_signature=True):
if backend == 'auto': if backend == 'auto':
# Detection what is possible # Detection what is possible
can_use_cryptography = CRYPTOGRAPHY_FOUND and CRYPTOGRAPHY_VERSION >= LooseVersion(MINIMAL_CRYPTOGRAPHY_VERSION) can_use_cryptography = CRYPTOGRAPHY_FOUND and CRYPTOGRAPHY_VERSION >= LooseVersion(MINIMAL_CRYPTOGRAPHY_VERSION)
can_use_pyopenssl = PYOPENSSL_FOUND and PYOPENSSL_VERSION >= LooseVersion(MINIMAL_PYOPENSSL_VERSION)
# First try cryptography, then pyOpenSSL # Try cryptography
if can_use_cryptography: if can_use_cryptography:
backend = 'cryptography' backend = 'cryptography'
elif can_use_pyopenssl:
backend = 'pyopenssl'
# Success? # Success?
if backend == 'auto': if backend == 'auto':
module.fail_json(msg=("Can't detect any of the required Python libraries " module.fail_json(msg=("Can't detect the required Python library "
"cryptography (>= {0}) or PyOpenSSL (>= {1})").format( "cryptography (>= {0})").format(MINIMAL_CRYPTOGRAPHY_VERSION))
MINIMAL_CRYPTOGRAPHY_VERSION,
MINIMAL_PYOPENSSL_VERSION))
if backend == 'pyopenssl': if backend == 'cryptography':
if not PYOPENSSL_FOUND:
module.fail_json(msg=missing_required_lib('pyOpenSSL >= {0}'.format(MINIMAL_PYOPENSSL_VERSION)),
exception=PYOPENSSL_IMP_ERR)
try:
getattr(crypto.X509Req, 'get_extensions')
except AttributeError:
module.fail_json(msg='You need to have PyOpenSSL>=0.15 to generate CSRs')
module.deprecate('The module is using the PyOpenSSL backend. This backend has been deprecated',
version='2.0.0', collection_name='community.crypto')
return backend, CSRInfoRetrievalPyOpenSSL(module, content, validate_signature=validate_signature)
elif backend == 'cryptography':
if not CRYPTOGRAPHY_FOUND: if not CRYPTOGRAPHY_FOUND:
module.fail_json(msg=missing_required_lib('cryptography >= {0}'.format(MINIMAL_CRYPTOGRAPHY_VERSION)), module.fail_json(msg=missing_required_lib('cryptography >= {0}'.format(MINIMAL_CRYPTOGRAPHY_VERSION)),
exception=CRYPTOGRAPHY_IMP_ERR) exception=CRYPTOGRAPHY_IMP_ERR)

View File

@ -46,20 +46,8 @@ from ansible_collections.community.crypto.plugins.module_utils.crypto.module_bac
from ansible_collections.community.crypto.plugins.module_utils.crypto.module_backends.common import ArgumentSpec from ansible_collections.community.crypto.plugins.module_utils.crypto.module_backends.common import ArgumentSpec
MINIMAL_PYOPENSSL_VERSION = '0.6'
MINIMAL_CRYPTOGRAPHY_VERSION = '1.2.3' MINIMAL_CRYPTOGRAPHY_VERSION = '1.2.3'
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
else:
PYOPENSSL_FOUND = True
CRYPTOGRAPHY_IMP_ERR = None CRYPTOGRAPHY_IMP_ERR = None
try: try:
import cryptography import cryptography
@ -263,61 +251,6 @@ class PrivateKeyBackend:
return result return result
# Implementation with using pyOpenSSL
class PrivateKeyPyOpenSSLBackend(PrivateKeyBackend):
def __init__(self, module):
super(PrivateKeyPyOpenSSLBackend, self).__init__(module=module, backend='pyopenssl')
if self.type == 'RSA':
self.openssl_type = crypto.TYPE_RSA
elif self.type == 'DSA':
self.openssl_type = crypto.TYPE_DSA
else:
self.module.fail_json(msg="PyOpenSSL backend only supports RSA and DSA keys.")
if self.format != 'auto_ignore':
self.module.fail_json(msg="PyOpenSSL backend only supports auto_ignore format.")
def generate_private_key(self):
"""(Re-)Generate private key."""
self.private_key = crypto.PKey()
try:
self.private_key.generate_key(self.openssl_type, self.size)
except (TypeError, ValueError) as exc:
raise PrivateKeyError(exc)
def _ensure_existing_private_key_loaded(self):
if self.existing_private_key is None and self.has_existing():
try:
self.existing_private_key = load_privatekey(
None, self.passphrase, content=self.existing_private_key_bytes, backend=self.backend)
except OpenSSLBadPassphraseError as exc:
raise PrivateKeyError(exc)
def get_private_key_data(self):
"""Return bytes for self.private_key"""
if self.cipher and self.passphrase:
return crypto.dump_privatekey(crypto.FILETYPE_PEM, self.private_key,
self.cipher, to_bytes(self.passphrase))
else:
return crypto.dump_privatekey(crypto.FILETYPE_PEM, self.private_key)
def _check_passphrase(self):
try:
load_privatekey(None, self.passphrase, content=self.existing_private_key_bytes, backend=self.backend)
return True
except Exception as dummy:
return False
def _check_size_and_type(self):
return self.size == self.existing_private_key.bits() and self.openssl_type == self.existing_private_key.type()
def _check_format(self):
# Not supported by this backend
return True
# Implementation with using cryptography # Implementation with using cryptography
class PrivateKeyCryptographyBackend(PrivateKeyBackend): class PrivateKeyCryptographyBackend(PrivateKeyBackend):
@ -550,36 +483,16 @@ def select_backend(module, backend):
if backend == 'auto': if backend == 'auto':
# Detection what is possible # Detection what is possible
can_use_cryptography = CRYPTOGRAPHY_FOUND and CRYPTOGRAPHY_VERSION >= LooseVersion(MINIMAL_CRYPTOGRAPHY_VERSION) can_use_cryptography = CRYPTOGRAPHY_FOUND and CRYPTOGRAPHY_VERSION >= LooseVersion(MINIMAL_CRYPTOGRAPHY_VERSION)
can_use_pyopenssl = PYOPENSSL_FOUND and PYOPENSSL_VERSION >= LooseVersion(MINIMAL_PYOPENSSL_VERSION)
# Decision # Decision
if module.params['cipher'] and module.params['passphrase'] and module.params['cipher'] != 'auto': if can_use_cryptography:
# First try pyOpenSSL, then cryptography backend = 'cryptography'
if can_use_pyopenssl:
backend = 'pyopenssl'
elif can_use_cryptography:
backend = 'cryptography'
else:
# First try cryptography, then pyOpenSSL
if can_use_cryptography:
backend = 'cryptography'
elif can_use_pyopenssl:
backend = 'pyopenssl'
# Success? # Success?
if backend == 'auto': if backend == 'auto':
module.fail_json(msg=("Can't detect any of the required Python libraries " module.fail_json(msg=("Can't detect the required Python library "
"cryptography (>= {0}) or PyOpenSSL (>= {1})").format( "cryptography (>= {0})").format(MINIMAL_CRYPTOGRAPHY_VERSION))
MINIMAL_CRYPTOGRAPHY_VERSION, if backend == 'cryptography':
MINIMAL_PYOPENSSL_VERSION))
if backend == 'pyopenssl':
if not PYOPENSSL_FOUND:
module.fail_json(msg=missing_required_lib('pyOpenSSL >= {0}'.format(MINIMAL_PYOPENSSL_VERSION)),
exception=PYOPENSSL_IMP_ERR)
module.deprecate('The module is using the PyOpenSSL backend. This backend has been deprecated',
version='2.0.0', collection_name='community.crypto')
return backend, PrivateKeyPyOpenSSLBackend(module)
elif backend == 'cryptography':
if not CRYPTOGRAPHY_FOUND: if not CRYPTOGRAPHY_FOUND:
module.fail_json(msg=missing_required_lib('cryptography >= {0}'.format(MINIMAL_CRYPTOGRAPHY_VERSION)), module.fail_json(msg=missing_required_lib('cryptography >= {0}'.format(MINIMAL_CRYPTOGRAPHY_VERSION)),
exception=CRYPTOGRAPHY_IMP_ERR) exception=CRYPTOGRAPHY_IMP_ERR)
@ -605,7 +518,7 @@ def get_privatekey_argument_spec():
cipher=dict(type='str'), cipher=dict(type='str'),
format=dict(type='str', default='auto_ignore', choices=['pkcs1', 'pkcs8', 'raw', 'auto', 'auto_ignore']), format=dict(type='str', default='auto_ignore', choices=['pkcs1', 'pkcs8', 'raw', 'auto', 'auto_ignore']),
format_mismatch=dict(type='str', default='regenerate', choices=['regenerate', 'convert']), format_mismatch=dict(type='str', default='regenerate', choices=['regenerate', 'convert']),
select_crypto_backend=dict(type='str', choices=['auto', 'pyopenssl', 'cryptography'], default='auto'), select_crypto_backend=dict(type='str', choices=['auto', 'cryptography'], default='auto'),
regenerate=dict( regenerate=dict(
type='str', type='str',
default='full_idempotence', default='full_idempotence',

View File

@ -36,24 +36,10 @@ from ansible_collections.community.crypto.plugins.module_utils.crypto.math impor
from ansible_collections.community.crypto.plugins.module_utils.crypto.module_backends.publickey_info import ( from ansible_collections.community.crypto.plugins.module_utils.crypto.module_backends.publickey_info import (
_get_cryptography_public_key_info, _get_cryptography_public_key_info,
_bigint_to_int,
_get_pyopenssl_public_key_info,
) )
MINIMAL_CRYPTOGRAPHY_VERSION = '1.2.3' MINIMAL_CRYPTOGRAPHY_VERSION = '1.2.3'
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
else:
PYOPENSSL_FOUND = True
CRYPTOGRAPHY_IMP_ERR = None CRYPTOGRAPHY_IMP_ERR = None
try: try:
@ -265,135 +251,10 @@ class PrivateKeyInfoRetrievalCryptography(PrivateKeyInfoRetrieval):
return _is_cryptography_key_consistent(self.key, key_public_data, key_private_data) return _is_cryptography_key_consistent(self.key, key_public_data, key_private_data)
class PrivateKeyInfoRetrievalPyOpenSSL(PrivateKeyInfoRetrieval):
"""validate the supplied private key."""
def __init__(self, module, content, **kwargs):
super(PrivateKeyInfoRetrievalPyOpenSSL, self).__init__(module, 'pyopenssl', content, **kwargs)
def _get_public_key(self, binary):
try:
return crypto.dump_publickey(
crypto.FILETYPE_ASN1 if binary else crypto.FILETYPE_PEM,
self.key
)
except AttributeError:
try:
# pyOpenSSL < 16.0:
bio = crypto._new_mem_buf()
if binary:
rc = crypto._lib.i2d_PUBKEY_bio(bio, self.key._pkey)
else:
rc = crypto._lib.PEM_write_bio_PUBKEY(bio, self.key._pkey)
if rc != 1:
crypto._raise_current_error()
return crypto._bio_to_string(bio)
except AttributeError:
self.module.warn('Your pyOpenSSL version does not support dumping public keys. '
'Please upgrade to version 16.0 or newer, or use the cryptography backend.')
def _get_key_info(self):
key_type, key_public_data, try_fallback = _get_pyopenssl_public_key_info(self.key)
key_private_data = dict()
openssl_key_type = self.key.type()
if crypto.TYPE_RSA == openssl_key_type:
try:
# Use OpenSSL directly to extract key data
key = OpenSSL._util.lib.EVP_PKEY_get1_RSA(self.key._pkey)
key = OpenSSL._util.ffi.gc(key, OpenSSL._util.lib.RSA_free)
# OpenSSL 1.1 and newer have functions to extract the parameters
# from the EVP PKEY data structures. Older versions didn't have
# these getters, and it was common use to simply access the values
# directly. Since there's no guarantee that these data structures
# will still be accessible in the future, we use the getters for
# 1.1 and later, and directly access the values for 1.0.x and
# earlier.
if OpenSSL.SSL.OPENSSL_VERSION_NUMBER >= 0x10100000:
# Get modulus and exponents
n = OpenSSL._util.ffi.new("BIGNUM **")
e = OpenSSL._util.ffi.new("BIGNUM **")
d = OpenSSL._util.ffi.new("BIGNUM **")
OpenSSL._util.lib.RSA_get0_key(key, n, e, d)
key_private_data['exponent'] = _bigint_to_int(d[0])
# Get factors
p = OpenSSL._util.ffi.new("BIGNUM **")
q = OpenSSL._util.ffi.new("BIGNUM **")
OpenSSL._util.lib.RSA_get0_factors(key, p, q)
key_private_data['p'] = _bigint_to_int(p[0])
key_private_data['q'] = _bigint_to_int(q[0])
else:
# Get private exponent
key_private_data['exponent'] = _bigint_to_int(key.d)
# Get factors
key_private_data['p'] = _bigint_to_int(key.p)
key_private_data['q'] = _bigint_to_int(key.q)
except AttributeError:
try_fallback = True
elif crypto.TYPE_DSA == openssl_key_type:
try:
# Use OpenSSL directly to extract key data
key = OpenSSL._util.lib.EVP_PKEY_get1_DSA(self.key._pkey)
key = OpenSSL._util.ffi.gc(key, OpenSSL._util.lib.DSA_free)
# OpenSSL 1.1 and newer have functions to extract the parameters
# from the EVP PKEY data structures. Older versions didn't have
# these getters, and it was common use to simply access the values
# directly. Since there's no guarantee that these data structures
# will still be accessible in the future, we use the getters for
# 1.1 and later, and directly access the values for 1.0.x and
# earlier.
if OpenSSL.SSL.OPENSSL_VERSION_NUMBER >= 0x10100000:
# Get private key exponents
y = OpenSSL._util.ffi.new("BIGNUM **")
x = OpenSSL._util.ffi.new("BIGNUM **")
OpenSSL._util.lib.DSA_get0_key(key, y, x)
key_private_data['x'] = _bigint_to_int(x[0])
else:
# Get private key exponents
key_private_data['x'] = _bigint_to_int(key.priv_key)
except AttributeError:
try_fallback = True
else:
# Return 'unknown'
key_type = 'unknown ({0})'.format(self.key.type())
# If needed and if possible, fall back to cryptography
if try_fallback and PYOPENSSL_VERSION >= LooseVersion('16.1.0') and CRYPTOGRAPHY_FOUND:
return _get_cryptography_private_key_info(self.key.to_cryptography_key())
return key_type, key_public_data, key_private_data
def _is_key_consistent(self, key_public_data, key_private_data):
openssl_key_type = self.key.type()
if crypto.TYPE_RSA == openssl_key_type:
try:
return self.key.check()
except crypto.Error:
# OpenSSL error means that key is not consistent
return False
if crypto.TYPE_DSA == openssl_key_type:
result = _check_dsa_consistency(key_public_data, key_private_data)
if result is not None:
return result
signature = crypto.sign(self.key, SIGNATURE_TEST_DATA, 'sha256')
# Verify wants a cert (where it can get the public key from)
cert = crypto.X509()
cert.set_pubkey(self.key)
try:
crypto.verify(cert, signature, SIGNATURE_TEST_DATA, 'sha256')
return True
except crypto.Error:
return False
# If needed and if possible, fall back to cryptography
if PYOPENSSL_VERSION >= LooseVersion('16.1.0') and CRYPTOGRAPHY_FOUND:
return _is_cryptography_key_consistent(self.key.to_cryptography_key(), key_public_data, key_private_data)
return None
def get_privatekey_info(module, backend, content, passphrase=None, return_private_key_data=False, prefer_one_fingerprint=False): def get_privatekey_info(module, backend, content, passphrase=None, return_private_key_data=False, prefer_one_fingerprint=False):
if backend == 'cryptography': if backend == 'cryptography':
info = PrivateKeyInfoRetrievalCryptography( info = PrivateKeyInfoRetrievalCryptography(
module, content, passphrase=passphrase, return_private_key_data=return_private_key_data) module, content, passphrase=passphrase, return_private_key_data=return_private_key_data)
elif backend == 'pyopenssl':
info = PrivateKeyInfoRetrievalPyOpenSSL(
module, content, passphrase=passphrase, return_private_key_data=return_private_key_data)
return info.get_info(prefer_one_fingerprint=prefer_one_fingerprint) return info.get_info(prefer_one_fingerprint=prefer_one_fingerprint)
@ -401,30 +262,17 @@ def select_backend(module, backend, content, passphrase=None, return_private_key
if backend == 'auto': if backend == 'auto':
# Detection what is possible # Detection what is possible
can_use_cryptography = CRYPTOGRAPHY_FOUND and CRYPTOGRAPHY_VERSION >= LooseVersion(MINIMAL_CRYPTOGRAPHY_VERSION) can_use_cryptography = CRYPTOGRAPHY_FOUND and CRYPTOGRAPHY_VERSION >= LooseVersion(MINIMAL_CRYPTOGRAPHY_VERSION)
can_use_pyopenssl = PYOPENSSL_FOUND and PYOPENSSL_VERSION >= LooseVersion(MINIMAL_PYOPENSSL_VERSION)
# First try cryptography, then pyOpenSSL # Try cryptography
if can_use_cryptography: if can_use_cryptography:
backend = 'cryptography' backend = 'cryptography'
elif can_use_pyopenssl:
backend = 'pyopenssl'
# Success? # Success?
if backend == 'auto': if backend == 'auto':
module.fail_json(msg=("Can't detect any of the required Python libraries " module.fail_json(msg=("Can't detect the required Python library "
"cryptography (>= {0}) or PyOpenSSL (>= {1})").format( "cryptography (>= {0})").format(MINIMAL_CRYPTOGRAPHY_VERSION))
MINIMAL_CRYPTOGRAPHY_VERSION,
MINIMAL_PYOPENSSL_VERSION))
if backend == 'pyopenssl': if backend == 'cryptography':
if not PYOPENSSL_FOUND:
module.fail_json(msg=missing_required_lib('pyOpenSSL >= {0}'.format(MINIMAL_PYOPENSSL_VERSION)),
exception=PYOPENSSL_IMP_ERR)
module.deprecate('The module is using the PyOpenSSL backend. This backend has been deprecated',
version='2.0.0', collection_name='community.crypto')
return backend, PrivateKeyInfoRetrievalPyOpenSSL(
module, content, passphrase=passphrase, return_private_key_data=return_private_key_data)
elif backend == 'cryptography':
if not CRYPTOGRAPHY_FOUND: if not CRYPTOGRAPHY_FOUND:
module.fail_json(msg=missing_required_lib('cryptography >= {0}'.format(MINIMAL_CRYPTOGRAPHY_VERSION)), module.fail_json(msg=missing_required_lib('cryptography >= {0}'.format(MINIMAL_CRYPTOGRAPHY_VERSION)),
exception=CRYPTOGRAPHY_IMP_ERR) exception=CRYPTOGRAPHY_IMP_ERR)

View File

@ -31,18 +31,6 @@ from ansible_collections.community.crypto.plugins.module_utils.crypto.support im
MINIMAL_CRYPTOGRAPHY_VERSION = '1.2.3' MINIMAL_CRYPTOGRAPHY_VERSION = '1.2.3'
MINIMAL_PYOPENSSL_VERSION = '16.0.0' # when working with public key objects, the minimal required version is 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
else:
PYOPENSSL_FOUND = True
CRYPTOGRAPHY_IMP_ERR = None CRYPTOGRAPHY_IMP_ERR = None
try: try:
@ -93,98 +81,6 @@ def _get_cryptography_public_key_info(key):
return key_type, key_public_data return key_type, key_public_data
def _bigint_to_int(bn):
'''Convert OpenSSL BIGINT to Python integer'''
if bn == OpenSSL._util.ffi.NULL:
return None
hexstr = OpenSSL._util.lib.BN_bn2hex(bn)
try:
return int(OpenSSL._util.ffi.string(hexstr), 16)
finally:
OpenSSL._util.lib.OPENSSL_free(hexstr)
def _get_pyopenssl_public_key_info(key):
key_public_data = dict()
try_fallback = True
openssl_key_type = key.type()
if crypto.TYPE_RSA == openssl_key_type:
key_type = 'RSA'
key_public_data['size'] = key.bits()
try:
# Use OpenSSL directly to extract key data
key = OpenSSL._util.lib.EVP_PKEY_get1_RSA(key._pkey)
key = OpenSSL._util.ffi.gc(key, OpenSSL._util.lib.RSA_free)
# OpenSSL 1.1 and newer have functions to extract the parameters
# from the EVP PKEY data structures. Older versions didn't have
# these getters, and it was common use to simply access the values
# directly. Since there's no guarantee that these data structures
# will still be accessible in the future, we use the getters for
# 1.1 and later, and directly access the values for 1.0.x and
# earlier.
if OpenSSL.SSL.OPENSSL_VERSION_NUMBER >= 0x10100000:
# Get modulus and exponents
n = OpenSSL._util.ffi.new("BIGNUM **")
e = OpenSSL._util.ffi.new("BIGNUM **")
d = OpenSSL._util.ffi.new("BIGNUM **")
OpenSSL._util.lib.RSA_get0_key(key, n, e, d)
key_public_data['modulus'] = _bigint_to_int(n[0])
key_public_data['exponent'] = _bigint_to_int(e[0])
else:
# Get modulus and exponents
key_public_data['modulus'] = _bigint_to_int(key.n)
key_public_data['exponent'] = _bigint_to_int(key.e)
try_fallback = False
except AttributeError:
# Use fallback if available
pass
elif crypto.TYPE_DSA == openssl_key_type:
key_type = 'DSA'
key_public_data['size'] = key.bits()
try:
# Use OpenSSL directly to extract key data
key = OpenSSL._util.lib.EVP_PKEY_get1_DSA(key._pkey)
key = OpenSSL._util.ffi.gc(key, OpenSSL._util.lib.DSA_free)
# OpenSSL 1.1 and newer have functions to extract the parameters
# from the EVP PKEY data structures. Older versions didn't have
# these getters, and it was common use to simply access the values
# directly. Since there's no guarantee that these data structures
# will still be accessible in the future, we use the getters for
# 1.1 and later, and directly access the values for 1.0.x and
# earlier.
if OpenSSL.SSL.OPENSSL_VERSION_NUMBER >= 0x10100000:
# Get public parameters (primes and group element)
p = OpenSSL._util.ffi.new("BIGNUM **")
q = OpenSSL._util.ffi.new("BIGNUM **")
g = OpenSSL._util.ffi.new("BIGNUM **")
OpenSSL._util.lib.DSA_get0_pqg(key, p, q, g)
key_public_data['p'] = _bigint_to_int(p[0])
key_public_data['q'] = _bigint_to_int(q[0])
key_public_data['g'] = _bigint_to_int(g[0])
# Get public key exponents
y = OpenSSL._util.ffi.new("BIGNUM **")
x = OpenSSL._util.ffi.new("BIGNUM **")
OpenSSL._util.lib.DSA_get0_key(key, y, x)
key_public_data['y'] = _bigint_to_int(y[0])
else:
# Get public parameters (primes and group element)
key_public_data['p'] = _bigint_to_int(key.p)
key_public_data['q'] = _bigint_to_int(key.q)
key_public_data['g'] = _bigint_to_int(key.g)
# Get public key exponents
key_public_data['y'] = _bigint_to_int(key.pub_key)
try_fallback = False
except AttributeError:
# Use fallback if available
pass
else:
# Return 'unknown'
key_type = 'unknown ({0})'.format(key.type())
return key_type, key_public_data, try_fallback
class PublicKeyParseError(OpenSSLObjectError): class PublicKeyParseError(OpenSSLObjectError):
def __init__(self, msg, result): def __init__(self, msg, result):
super(PublicKeyParseError, self).__init__(msg) super(PublicKeyParseError, self).__init__(msg)
@ -242,46 +138,9 @@ class PublicKeyInfoRetrievalCryptography(PublicKeyInfoRetrieval):
return _get_cryptography_public_key_info(self.key) return _get_cryptography_public_key_info(self.key)
class PublicKeyInfoRetrievalPyOpenSSL(PublicKeyInfoRetrieval):
"""validate the supplied public key."""
def __init__(self, module, content=None, key=None):
super(PublicKeyInfoRetrievalPyOpenSSL, self).__init__(module, 'pyopenssl', content=content, key=key)
def _get_public_key(self, binary):
try:
return crypto.dump_publickey(
crypto.FILETYPE_ASN1 if binary else crypto.FILETYPE_PEM,
self.key
)
except AttributeError:
try:
# pyOpenSSL < 16.0:
bio = crypto._new_mem_buf()
if binary:
rc = crypto._lib.i2d_PUBKEY_bio(bio, self.key._pkey)
else:
rc = crypto._lib.PEM_write_bio_PUBKEY(bio, self.key._pkey)
if rc != 1:
crypto._raise_current_error()
return crypto._bio_to_string(bio)
except AttributeError:
self.module.warn('Your pyOpenSSL version does not support dumping public keys. '
'Please upgrade to version 16.0 or newer, or use the cryptography backend.')
def _get_key_info(self):
key_type, key_public_data, try_fallback = _get_pyopenssl_public_key_info(self.key)
# If needed and if possible, fall back to cryptography
if try_fallback and PYOPENSSL_VERSION >= LooseVersion('16.1.0') and CRYPTOGRAPHY_FOUND:
return _get_cryptography_public_key_info(self.key.to_cryptography_key())
return key_type, key_public_data
def get_publickey_info(module, backend, content=None, key=None, prefer_one_fingerprint=False): def get_publickey_info(module, backend, content=None, key=None, prefer_one_fingerprint=False):
if backend == 'cryptography': if backend == 'cryptography':
info = PublicKeyInfoRetrievalCryptography(module, content=content, key=key) info = PublicKeyInfoRetrievalCryptography(module, content=content, key=key)
elif backend == 'pyopenssl':
info = PublicKeyInfoRetrievalPyOpenSSL(module, content=content, key=key)
return info.get_info(prefer_one_fingerprint=prefer_one_fingerprint) return info.get_info(prefer_one_fingerprint=prefer_one_fingerprint)
@ -289,29 +148,17 @@ def select_backend(module, backend, content=None, key=None):
if backend == 'auto': if backend == 'auto':
# Detection what is possible # Detection what is possible
can_use_cryptography = CRYPTOGRAPHY_FOUND and CRYPTOGRAPHY_VERSION >= LooseVersion(MINIMAL_CRYPTOGRAPHY_VERSION) can_use_cryptography = CRYPTOGRAPHY_FOUND and CRYPTOGRAPHY_VERSION >= LooseVersion(MINIMAL_CRYPTOGRAPHY_VERSION)
can_use_pyopenssl = PYOPENSSL_FOUND and PYOPENSSL_VERSION >= LooseVersion(MINIMAL_PYOPENSSL_VERSION)
# First try cryptography, then pyOpenSSL # Try cryptography
if can_use_cryptography: if can_use_cryptography:
backend = 'cryptography' backend = 'cryptography'
elif can_use_pyopenssl:
backend = 'pyopenssl'
# Success? # Success?
if backend == 'auto': if backend == 'auto':
module.fail_json(msg=("Can't detect any of the required Python libraries " module.fail_json(msg=("Can't detect any of the required Python libraries "
"cryptography (>= {0}) or PyOpenSSL (>= {1})").format( "cryptography (>= {0})").format(MINIMAL_CRYPTOGRAPHY_VERSION))
MINIMAL_CRYPTOGRAPHY_VERSION,
MINIMAL_PYOPENSSL_VERSION))
if backend == 'pyopenssl': if backend == 'cryptography':
if not PYOPENSSL_FOUND:
module.fail_json(msg=missing_required_lib('pyOpenSSL >= {0}'.format(MINIMAL_PYOPENSSL_VERSION)),
exception=PYOPENSSL_IMP_ERR)
module.deprecate('The module is using the PyOpenSSL backend. This backend has been deprecated',
version='2.0.0', collection_name='community.crypto')
return backend, PublicKeyInfoRetrievalPyOpenSSL(module, content=content, key=key)
elif backend == 'cryptography':
if not CRYPTOGRAPHY_FOUND: if not CRYPTOGRAPHY_FOUND:
module.fail_json(msg=missing_required_lib('cryptography >= {0}'.format(MINIMAL_CRYPTOGRAPHY_VERSION)), module.fail_json(msg=missing_required_lib('cryptography >= {0}'.format(MINIMAL_CRYPTOGRAPHY_VERSION)),
exception=CRYPTOGRAPHY_IMP_ERR) exception=CRYPTOGRAPHY_IMP_ERR)

View File

@ -1,154 +0,0 @@
# -*- coding: utf-8 -*-
#
# (c) 2019, Felix Fontein <felix@fontein.de>
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
from __future__ import absolute_import, division, print_function
__metaclass__ = type
import base64
from ansible.module_utils.common.text.converters import to_bytes, to_text
from ansible_collections.community.crypto.plugins.module_utils.compat import ipaddress as compat_ipaddress
try:
import OpenSSL
except ImportError:
# Error handled in the calling module.
pass
from ._objects import (
NORMALIZE_NAMES_SHORT,
NORMALIZE_NAMES,
)
from ._obj2txt import obj2txt
from .basic import (
OpenSSLObjectError,
)
def pyopenssl_normalize_name(name, short=False):
nid = OpenSSL._util.lib.OBJ_txt2nid(to_bytes(name))
if nid != 0:
b_name = OpenSSL._util.lib.OBJ_nid2ln(nid)
name = to_text(OpenSSL._util.ffi.string(b_name))
if short:
return NORMALIZE_NAMES_SHORT.get(name, name)
else:
return NORMALIZE_NAMES.get(name, name)
def pyopenssl_normalize_name_attribute(san):
# apparently openssl returns 'IP address' not 'IP' as specifier when converting the subjectAltName to string
# although it won't accept this specifier when generating the CSR. (https://github.com/openssl/openssl/issues/4004)
if san.startswith('IP Address:'):
san = 'IP:' + san[len('IP Address:'):]
if san.startswith('IP:'):
address = san[3:]
if '/' in address:
ip = compat_ipaddress.ip_network(address)
san = 'IP:{0}/{1}'.format(ip.network_address.compressed, ip.prefixlen)
else:
ip = compat_ipaddress.ip_address(address)
san = 'IP:{0}'.format(ip.compressed)
if san.startswith('Registered ID:'):
san = 'RID:' + san[len('Registered ID:'):]
# Some versions of OpenSSL apparently forgot the colon. Happens in CI with Ubuntu 16.04 and FreeBSD 11.1
if san.startswith('Registered ID'):
san = 'RID:' + san[len('Registered ID'):]
return san
def pyopenssl_get_extensions_from_cert(cert):
# While pyOpenSSL allows us to get an extension's DER value, it won't
# give us the dotted string for an OID. So we have to do some magic to
# get hold of it.
result = dict()
ext_count = cert.get_extension_count()
for i in range(0, ext_count):
ext = cert.get_extension(i)
entry = dict(
critical=bool(ext.get_critical()),
value=base64.b64encode(ext.get_data()),
)
oid = obj2txt(
OpenSSL._util.lib,
OpenSSL._util.ffi,
OpenSSL._util.lib.X509_EXTENSION_get_object(ext._extension)
)
# This could also be done a bit simpler:
#
# oid = obj2txt(OpenSSL._util.lib, OpenSSL._util.ffi, OpenSSL._util.lib.OBJ_nid2obj(ext._nid))
#
# Unfortunately this gives the wrong result in case the linked OpenSSL
# doesn't know the OID. That's why we have to get the OID dotted string
# similarly to how cryptography does it.
result[oid] = entry
return result
def pyopenssl_get_extensions_from_csr(csr):
# While pyOpenSSL allows us to get an extension's DER value, it won't
# give us the dotted string for an OID. So we have to do some magic to
# get hold of it.
result = dict()
for ext in csr.get_extensions():
entry = dict(
critical=bool(ext.get_critical()),
value=base64.b64encode(ext.get_data()),
)
oid = obj2txt(
OpenSSL._util.lib,
OpenSSL._util.ffi,
OpenSSL._util.lib.X509_EXTENSION_get_object(ext._extension)
)
# This could also be done a bit simpler:
#
# oid = obj2txt(OpenSSL._util.lib, OpenSSL._util.ffi, OpenSSL._util.lib.OBJ_nid2obj(ext._nid))
#
# Unfortunately this gives the wrong result in case the linked OpenSSL
# doesn't know the OID. That's why we have to get the OID dotted string
# similarly to how cryptography does it.
result[oid] = entry
return result
def pyopenssl_parse_name_constraints(name_constraints_extension):
lines = to_text(name_constraints_extension, errors='surrogate_or_strict').splitlines()
exclude = None
excluded = []
permitted = []
for line in lines:
if line.startswith(' ') or line.startswith('\t'):
name = pyopenssl_normalize_name_attribute(line.strip())
if exclude is True:
excluded.append(name)
elif exclude is False:
permitted.append(name)
else:
raise OpenSSLObjectError('Unexpected nameConstraint line: "{0}"'.format(line))
else:
line_lc = line.lower()
if line_lc.startswith('exclud'):
exclude = True
elif line_lc.startswith('includ') or line_lc.startswith('permitt'):
exclude = False
else:
raise OpenSSLObjectError('Cannot parse nameConstraint line: "{0}"'.format(line))
return permitted, excluded

View File

@ -98,25 +98,10 @@ def get_fingerprint_of_bytes(source, prefer_one=False):
return fingerprint return fingerprint
def get_fingerprint_of_privatekey(privatekey, backend='pyopenssl', prefer_one=False): def get_fingerprint_of_privatekey(privatekey, backend='cryptography', prefer_one=False):
"""Generate the fingerprint of the public key. """ """Generate the fingerprint of the public key. """
if backend == 'pyopenssl': if backend == 'cryptography':
try:
publickey = crypto.dump_publickey(crypto.FILETYPE_ASN1, privatekey)
except AttributeError:
# If PyOpenSSL < 16.0 crypto.dump_publickey() will fail.
try:
bio = crypto._new_mem_buf()
rc = crypto._lib.i2d_PUBKEY_bio(bio, privatekey._pkey)
if rc != 1:
crypto._raise_current_error()
publickey = crypto._bio_to_string(bio)
except AttributeError:
# By doing this we prevent the code from raising an error
# yet we return no value in the fingerprint hash.
return None
elif backend == 'cryptography':
publickey = privatekey.public_key().public_bytes( publickey = privatekey.public_key().public_bytes(
serialization.Encoding.DER, serialization.Encoding.DER,
serialization.PublicFormat.SubjectPublicKeyInfo serialization.PublicFormat.SubjectPublicKeyInfo
@ -125,7 +110,7 @@ def get_fingerprint_of_privatekey(privatekey, backend='pyopenssl', prefer_one=Fa
return get_fingerprint_of_bytes(publickey, prefer_one=prefer_one) return get_fingerprint_of_bytes(publickey, prefer_one=prefer_one)
def get_fingerprint(path, passphrase=None, content=None, backend='pyopenssl', prefer_one=False): def get_fingerprint(path, passphrase=None, content=None, backend='cryptography', prefer_one=False):
"""Generate the fingerprint of the public key. """ """Generate the fingerprint of the public key. """
privatekey = load_privatekey(path, passphrase=passphrase, content=content, check_passphrase=False, backend=backend) privatekey = load_privatekey(path, passphrase=passphrase, content=content, check_passphrase=False, backend=backend)
@ -133,7 +118,7 @@ def get_fingerprint(path, passphrase=None, content=None, backend='pyopenssl', pr
return get_fingerprint_of_privatekey(privatekey, backend=backend, prefer_one=prefer_one) return get_fingerprint_of_privatekey(privatekey, backend=backend, prefer_one=prefer_one)
def load_privatekey(path, passphrase=None, check_passphrase=True, content=None, backend='pyopenssl'): def load_privatekey(path, passphrase=None, check_passphrase=True, content=None, backend='cryptography'):
"""Load the specified OpenSSL private key. """Load the specified OpenSSL private key.
The content can also be specified via content; in that case, The content can also be specified via content; in that case,
@ -213,14 +198,9 @@ def load_publickey(path=None, content=None, backend=None):
return serialization.load_pem_public_key(content, backend=cryptography_backend()) return serialization.load_pem_public_key(content, backend=cryptography_backend())
except Exception as e: except Exception as e:
raise OpenSSLObjectError('Error while deserializing key: {0}'.format(e)) raise OpenSSLObjectError('Error while deserializing key: {0}'.format(e))
else:
try:
return crypto.load_publickey(crypto.FILETYPE_PEM, content)
except crypto.Error as e:
raise OpenSSLObjectError('Error while deserializing key: {0}'.format(e))
def load_certificate(path, content=None, backend='pyopenssl'): def load_certificate(path, content=None, backend='cryptography'):
"""Load the specified certificate.""" """Load the specified certificate."""
try: try:
@ -240,7 +220,7 @@ def load_certificate(path, content=None, backend='pyopenssl'):
raise OpenSSLObjectError(exc) raise OpenSSLObjectError(exc)
def load_certificate_request(path, content=None, backend='pyopenssl'): def load_certificate_request(path, content=None, backend='cryptography'):
"""Load the specified certificate signing request.""" """Load the specified certificate signing request."""
try: try:
if content is None: if content is None:
@ -250,9 +230,7 @@ def load_certificate_request(path, content=None, backend='pyopenssl'):
csr_content = content csr_content = content
except (IOError, OSError) as exc: except (IOError, OSError) as exc:
raise OpenSSLObjectError(exc) raise OpenSSLObjectError(exc)
if backend == 'pyopenssl': if backend == 'cryptography':
return crypto.load_certificate_request(crypto.FILETYPE_PEM, csr_content)
elif backend == 'cryptography':
try: try:
return x509.load_pem_x509_csr(csr_content, cryptography_backend()) return x509.load_pem_x509_csr(csr_content, cryptography_backend())
except ValueError as exc: except ValueError as exc:
@ -322,9 +300,7 @@ def get_relative_time_option(input_string, input_name, backend='cryptography'):
elif backend == 'cryptography': elif backend == 'cryptography':
return result_datetime return result_datetime
# Absolute time # Absolute time
if backend == 'pyopenssl': if backend == 'cryptography':
return input_string
elif 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']: 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: try:
return datetime.datetime.strptime(result, date_fmt) return datetime.datetime.strptime(result, date_fmt)

View File

@ -14,10 +14,7 @@ author: "John Westcott IV (@john-westcott-iv)"
short_description: Get a certificate from a host:port short_description: Get a certificate from a host:port
description: description:
- Makes a secure connection and returns information about the presented certificate - Makes a secure connection and returns information about the presented certificate
- "The module can use the cryptography Python library, or the pyOpenSSL Python - The module uses the cryptography Python library.
library. By default, it tries to detect which one is available. This can be
overridden with the I(select_crypto_backend) option. Please note that the PyOpenSSL
backend was deprecated in Ansible 2.9 and will be removed in community.crypto 2.0.0."
- Support SNI (L(Server Name Indication,https://en.wikipedia.org/wiki/Server_Name_Indication)) only with python >= 2.7. - Support SNI (L(Server Name Indication,https://en.wikipedia.org/wiki/Server_Name_Indication)) only with python >= 2.7.
options: options:
host: host:
@ -66,19 +63,18 @@ options:
select_crypto_backend: select_crypto_backend:
description: description:
- Determines which crypto backend to use. - 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). - The default choice is C(auto), which tries to use C(cryptography) if available.
- 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. - If set to C(cryptography), will try to use the L(cryptography,https://cryptography.io/) library.
type: str type: str
default: auto default: auto
choices: [ auto, cryptography, pyopenssl ] choices: [ auto, cryptography ]
notes: notes:
- When using ca_cert on OS X it has been reported that in some conditions the validate will always succeed. - When using ca_cert on OS X it has been reported that in some conditions the validate will always succeed.
requirements: requirements:
- "python >= 2.7 when using C(proxy_host)" - "python >= 2.7 when using C(proxy_host)"
- "cryptography >= 1.6 or pyOpenSSL >= 0.15" - "cryptography >= 1.6"
''' '''
RETURN = ''' RETURN = '''
@ -180,7 +176,6 @@ from ansible_collections.community.crypto.plugins.module_utils.crypto.cryptograp
cryptography_get_extensions_from_cert, cryptography_get_extensions_from_cert,
) )
MINIMAL_PYOPENSSL_VERSION = '0.15'
MINIMAL_CRYPTOGRAPHY_VERSION = '1.6' MINIMAL_CRYPTOGRAPHY_VERSION = '1.6'
CREATE_DEFAULT_CONTEXT_IMP_ERR = None CREATE_DEFAULT_CONTEXT_IMP_ERR = None
@ -192,17 +187,6 @@ except ImportError:
else: else:
HAS_CREATE_DEFAULT_CONTEXT = True HAS_CREATE_DEFAULT_CONTEXT = True
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
else:
PYOPENSSL_FOUND = True
CRYPTOGRAPHY_IMP_ERR = None CRYPTOGRAPHY_IMP_ERR = None
try: try:
import cryptography import cryptography
@ -241,7 +225,7 @@ def main():
proxy_port=dict(type='int', default=8080), proxy_port=dict(type='int', default=8080),
server_name=dict(type='str'), server_name=dict(type='str'),
timeout=dict(type='int', default=10), timeout=dict(type='int', default=10),
select_crypto_backend=dict(type='str', choices=['auto', 'pyopenssl', 'cryptography'], default='auto'), select_crypto_backend=dict(type='str', choices=['auto', 'cryptography'], default='auto'),
starttls=dict(type='str', choices=['mysql']), starttls=dict(type='str', choices=['mysql']),
), ),
) )
@ -259,28 +243,17 @@ def main():
if backend == 'auto': if backend == 'auto':
# Detection what is possible # Detection what is possible
can_use_cryptography = CRYPTOGRAPHY_FOUND and CRYPTOGRAPHY_VERSION >= LooseVersion(MINIMAL_CRYPTOGRAPHY_VERSION) can_use_cryptography = CRYPTOGRAPHY_FOUND and CRYPTOGRAPHY_VERSION >= LooseVersion(MINIMAL_CRYPTOGRAPHY_VERSION)
can_use_pyopenssl = PYOPENSSL_FOUND and PYOPENSSL_VERSION >= LooseVersion(MINIMAL_PYOPENSSL_VERSION)
# First try cryptography, then pyOpenSSL # Try cryptography
if can_use_cryptography: if can_use_cryptography:
backend = 'cryptography' backend = 'cryptography'
elif can_use_pyopenssl:
backend = 'pyopenssl'
# Success? # Success?
if backend == 'auto': if backend == 'auto':
module.fail_json(msg=("Can't detect any of the required Python libraries " module.fail_json(msg=("Can't detect the required Python library "
"cryptography (>= {0}) or PyOpenSSL (>= {1})").format( "cryptography (>= {0})").format(MINIMAL_CRYPTOGRAPHY_VERSION))
MINIMAL_CRYPTOGRAPHY_VERSION,
MINIMAL_PYOPENSSL_VERSION))
if backend == 'pyopenssl': if backend == 'cryptography':
if not PYOPENSSL_FOUND:
module.fail_json(msg=missing_required_lib('pyOpenSSL >= {0}'.format(MINIMAL_PYOPENSSL_VERSION)),
exception=PYOPENSSL_IMP_ERR)
module.deprecate('The module is using the PyOpenSSL backend. This backend has been deprecated',
version='2.0.0', collection_name='community.crypto')
elif backend == 'cryptography':
if not CRYPTOGRAPHY_FOUND: if not CRYPTOGRAPHY_FOUND:
module.fail_json(msg=missing_required_lib('cryptography >= {0}'.format(MINIMAL_CRYPTOGRAPHY_VERSION)), module.fail_json(msg=missing_required_lib('cryptography >= {0}'.format(MINIMAL_CRYPTOGRAPHY_VERSION)),
exception=CRYPTOGRAPHY_IMP_ERR) exception=CRYPTOGRAPHY_IMP_ERR)
@ -343,37 +316,7 @@ def main():
result['cert'] = cert result['cert'] = cert
if backend == 'pyopenssl': if backend == 'cryptography':
x509 = crypto.load_certificate(crypto.FILETYPE_PEM, cert)
result['subject'] = {}
for component in x509.get_subject().get_components():
result['subject'][component[0]] = component[1]
result['expired'] = x509.has_expired()
result['extensions'] = []
extension_count = x509.get_extension_count()
for index in range(0, extension_count):
extension = x509.get_extension(index)
result['extensions'].append({
'critical': extension.get_critical(),
'asn1_data': extension.get_data(),
'name': extension.get_short_name(),
})
result['issuer'] = {}
for component in x509.get_issuer().get_components():
result['issuer'][component[0]] = component[1]
result['not_after'] = x509.get_notAfter()
result['not_before'] = x509.get_notBefore()
result['serial_number'] = x509.get_serial_number()
result['signature_algorithm'] = x509.get_signature_algorithm()
result['version'] = x509.get_version()
elif backend == 'cryptography':
x509 = cryptography.x509.load_pem_x509_certificate(to_bytes(cert), cryptography_backend()) x509 = cryptography.x509.load_pem_x509_certificate(to_bytes(cert), cryptography_backend())
result['subject'] = {} result['subject'] = {}
for attribute in x509.subject: for attribute in x509.subject:

View File

@ -17,13 +17,9 @@ description:
- This module allows one to query information on OpenSSL Certificate Signing Requests (CSR). - This module allows one to query information on OpenSSL Certificate Signing Requests (CSR).
- In case the CSR signature cannot be validated, the module will fail. In this case, all return - In case the CSR signature cannot be validated, the module will fail. In this case, all return
variables are still returned. variables are still returned.
- It uses the pyOpenSSL or cryptography python library to interact with OpenSSL. If both the - It uses the cryptography python library to interact with OpenSSL.
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)). Please note that the PyOpenSSL backend was deprecated in Ansible 2.9
and will be removed in community.crypto 2.0.0.
requirements: requirements:
- PyOpenSSL >= 0.15 or cryptography >= 1.3 - cryptography >= 1.3
author: author:
- Felix Fontein (@felixfontein) - Felix Fontein (@felixfontein)
- Yanis Guenane (@Spredzy) - Yanis Guenane (@Spredzy)
@ -42,14 +38,11 @@ options:
select_crypto_backend: select_crypto_backend:
description: description:
- Determines which crypto backend to use. - 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). - The default choice is C(auto), which tries to use C(cryptography) if available.
- 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. - If set to C(cryptography), will try to use the L(cryptography,https://cryptography.io/) library.
- Please note that the C(pyopenssl) backend has been deprecated in Ansible 2.9, and will be removed in community.crypto 2.0.0.
From that point on, only the C(cryptography) backend will be available.
type: str type: str
default: auto default: auto
choices: [ auto, cryptography, pyopenssl ] choices: [ auto, cryptography ]
seealso: seealso:
- module: community.crypto.openssl_csr - module: community.crypto.openssl_csr
@ -267,7 +260,7 @@ subject_key_identifier:
- The CSR's subject key identifier. - The CSR's subject key identifier.
- The identifier is returned in hexadecimal, with C(:) used to separate bytes. - The identifier is returned in hexadecimal, with C(:) used to separate bytes.
- Is C(none) if the C(SubjectKeyIdentifier) extension is not present. - Is C(none) if the C(SubjectKeyIdentifier) extension is not present.
returned: success and if the pyOpenSSL backend is I(not) used returned: success
type: str type: str
sample: '00:11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff:00:11:22:33' sample: '00:11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff:00:11:22:33'
authority_key_identifier: authority_key_identifier:
@ -275,14 +268,14 @@ authority_key_identifier:
- The CSR's authority key identifier. - The CSR's authority key identifier.
- The identifier is returned in hexadecimal, with C(:) used to separate bytes. - The identifier is returned in hexadecimal, with C(:) used to separate bytes.
- Is C(none) if the C(AuthorityKeyIdentifier) extension is not present. - Is C(none) if the C(AuthorityKeyIdentifier) extension is not present.
returned: success and if the pyOpenSSL backend is I(not) used returned: success
type: str type: str
sample: '00:11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff:00:11:22:33' sample: '00:11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff:00:11:22:33'
authority_cert_issuer: authority_cert_issuer:
description: description:
- The CSR's authority cert issuer as a list of general names. - The CSR's authority cert issuer as a list of general names.
- Is C(none) if the C(AuthorityKeyIdentifier) extension is not present. - Is C(none) if the C(AuthorityKeyIdentifier) extension is not present.
returned: success and if the pyOpenSSL backend is I(not) used returned: success
type: list type: list
elements: str elements: str
sample: "[DNS:www.ansible.com, IP:1.2.3.4]" sample: "[DNS:www.ansible.com, IP:1.2.3.4]"
@ -290,7 +283,7 @@ authority_cert_serial_number:
description: description:
- The CSR's authority cert serial number. - The CSR's authority cert serial number.
- Is C(none) if the C(AuthorityKeyIdentifier) extension is not present. - Is C(none) if the C(AuthorityKeyIdentifier) extension is not present.
returned: success and if the pyOpenSSL backend is I(not) used returned: success
type: int type: int
sample: '12345' sample: '12345'
''' '''
@ -313,7 +306,7 @@ def main():
argument_spec=dict( argument_spec=dict(
path=dict(type='path'), path=dict(type='path'),
content=dict(type='str'), content=dict(type='str'),
select_crypto_backend=dict(type='str', default='auto', choices=['auto', 'cryptography', 'pyopenssl']), select_crypto_backend=dict(type='str', default='auto', choices=['auto', 'cryptography']),
), ),
required_one_of=( required_one_of=(
['path', 'content'], ['path', 'content'],

View File

@ -117,7 +117,6 @@ filename:
fingerprint: fingerprint:
description: description:
- The fingerprint of the public key. Fingerprint will be generated for each C(hashlib.algorithms) available. - The fingerprint of the public key. Fingerprint will be generated for each C(hashlib.algorithms) available.
- The PyOpenSSL backend requires PyOpenSSL >= 16.0 for meaningful output.
returned: changed or success returned: changed or success
type: dict type: dict
sample: sample:

View File

@ -19,13 +19,9 @@ description:
private key. In this case, all return variables are still returned. Note that key consistency private key. In this case, all return variables are still returned. Note that key consistency
checks are not available all key types; if none is available, C(none) is returned for checks are not available all key types; if none is available, C(none) is returned for
C(key_is_consistent). C(key_is_consistent).
- It uses the pyOpenSSL or cryptography python library to interact with OpenSSL. If both the - It uses the cryptography python library to interact with OpenSSL.
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)). Please note that the PyOpenSSL backend was deprecated in Ansible 2.9
and will be removed in community.crypto 2.0.0.
requirements: requirements:
- PyOpenSSL >= 0.15 or cryptography >= 1.2.3 - cryptography >= 1.2.3
author: author:
- Felix Fontein (@felixfontein) - Felix Fontein (@felixfontein)
- Yanis Guenane (@Spredzy) - Yanis Guenane (@Spredzy)
@ -56,14 +52,11 @@ options:
select_crypto_backend: select_crypto_backend:
description: description:
- Determines which crypto backend to use. - 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). - The default choice is C(auto), which tries to use C(cryptography) if available.
- 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. - If set to C(cryptography), will try to use the L(cryptography,https://cryptography.io/) library.
- Please note that the C(pyopenssl) backend has been deprecated in Ansible 2.9, and will be removed in community.crypto 2.0.0.
From that point on, only the C(cryptography) backend will be available.
type: str type: str
default: auto default: auto
choices: [ auto, cryptography, pyopenssl ] choices: [ auto, cryptography ]
notes: notes:
- Supports C(check_mode). - Supports C(check_mode).
@ -215,7 +208,7 @@ def main():
content=dict(type='str', no_log=True), content=dict(type='str', no_log=True),
passphrase=dict(type='str', no_log=True), passphrase=dict(type='str', no_log=True),
return_private_key_data=dict(type='bool', default=False), return_private_key_data=dict(type='bool', default=False),
select_crypto_backend=dict(type='str', default='auto', choices=['auto', 'cryptography', 'pyopenssl']), select_crypto_backend=dict(type='str', default='auto', choices=['auto', 'cryptography']),
), ),
required_one_of=( required_one_of=(
['path', 'content'], ['path', 'content'],

View File

@ -97,7 +97,6 @@ curve:
fingerprint: fingerprint:
description: description:
- The fingerprint of the public key. Fingerprint will be generated for each C(hashlib.algorithms) available. - The fingerprint of the public key. Fingerprint will be generated for each C(hashlib.algorithms) available.
- The PyOpenSSL backend requires PyOpenSSL >= 16.0 for meaningful output.
returned: changed or success returned: changed or success
type: dict type: dict
sample: sample:

View File

@ -15,14 +15,9 @@ short_description: Generate an OpenSSL public key from its private key.
description: description:
- This module allows one to (re)generate OpenSSL public keys from their private keys. - This module allows one to (re)generate OpenSSL public keys from their private keys.
- Keys are generated in PEM or OpenSSH format. - Keys are generated in PEM or OpenSSH format.
- "The module can use the cryptography Python library, or the pyOpenSSL Python - The module uses the cryptography Python library.
library. By default, it tries to detect which one is available. This can be
overridden with the I(select_crypto_backend) option. When I(format) is C(OpenSSH),
the C(cryptography) backend has to be used. Please note that the PyOpenSSL backend
was deprecated in Ansible 2.9 and will be removed in community.crypto 2.0.0."
requirements: requirements:
- Either cryptography >= 1.2.3 (older versions might work as well) - cryptography >= 1.2.3 (older versions might work as well)
- Or pyOpenSSL >= 16.0.0
- Needs cryptography >= 1.4 if I(format) is C(OpenSSH) - Needs cryptography >= 1.4 if I(format) is C(OpenSSH)
author: author:
- Yanis Guenane (@Spredzy) - Yanis Guenane (@Spredzy)
@ -76,12 +71,11 @@ options:
select_crypto_backend: select_crypto_backend:
description: description:
- Determines which crypto backend to use. - 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). - The default choice is C(auto), which tries to use C(cryptography) if available.
- 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. - If set to C(cryptography), will try to use the L(cryptography,https://cryptography.io/) library.
type: str type: str
default: auto default: auto
choices: [ auto, cryptography, pyopenssl ] choices: [ auto, cryptography ]
return_content: return_content:
description: description:
- If set to C(yes), will return the (current or generated) public key's content as I(publickey). - If set to C(yes), will return the (current or generated) public key's content as I(publickey).
@ -157,7 +151,6 @@ filename:
fingerprint: fingerprint:
description: description:
- The fingerprint of the public key. Fingerprint will be generated for each hashlib.algorithms available. - The fingerprint of the public key. Fingerprint will be generated for each hashlib.algorithms available.
- Requires PyOpenSSL >= 16.0 for meaningful output.
returned: changed or success returned: changed or success
type: dict type: dict
sample: sample:
@ -208,21 +201,9 @@ from ansible_collections.community.crypto.plugins.module_utils.crypto.module_bac
get_publickey_info, get_publickey_info,
) )
MINIMAL_PYOPENSSL_VERSION = '16.0.0'
MINIMAL_CRYPTOGRAPHY_VERSION = '1.2.3' MINIMAL_CRYPTOGRAPHY_VERSION = '1.2.3'
MINIMAL_CRYPTOGRAPHY_VERSION_OPENSSH = '1.4' MINIMAL_CRYPTOGRAPHY_VERSION_OPENSSH = '1.4'
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
else:
PYOPENSSL_FOUND = True
CRYPTOGRAPHY_IMP_ERR = None CRYPTOGRAPHY_IMP_ERR = None
try: try:
import cryptography import cryptography
@ -300,11 +281,6 @@ class PublicKey(OpenSSLObject):
crypto_serialization.Encoding.PEM, crypto_serialization.Encoding.PEM,
crypto_serialization.PublicFormat.SubjectPublicKeyInfo crypto_serialization.PublicFormat.SubjectPublicKeyInfo
) )
else:
try:
return crypto.dump_publickey(crypto.FILETYPE_PEM, self.privatekey)
except AttributeError as dummy:
raise PublicKeyError('You need to have PyOpenSSL>=16.0.0 to generate public keys')
def generate(self, module): def generate(self, module):
"""Generate the public key.""" """Generate the public key."""
@ -372,11 +348,6 @@ class PublicKey(OpenSSLObject):
crypto_serialization.Encoding.PEM, crypto_serialization.Encoding.PEM,
crypto_serialization.PublicFormat.SubjectPublicKeyInfo crypto_serialization.PublicFormat.SubjectPublicKeyInfo
) )
else:
publickey_content = crypto.dump_publickey(
crypto.FILETYPE_PEM,
crypto.load_publickey(crypto.FILETYPE_PEM, publickey_content)
)
except Exception as dummy: except Exception as dummy:
return False return False
@ -434,7 +405,7 @@ def main():
format=dict(type='str', default='PEM', choices=['OpenSSH', 'PEM']), format=dict(type='str', default='PEM', choices=['OpenSSH', 'PEM']),
privatekey_passphrase=dict(type='str', no_log=True), privatekey_passphrase=dict(type='str', no_log=True),
backup=dict(type='bool', default=False), backup=dict(type='bool', default=False),
select_crypto_backend=dict(type='str', choices=['auto', 'pyopenssl', 'cryptography'], default='auto'), select_crypto_backend=dict(type='str', choices=['auto', 'cryptography'], default='auto'),
return_content=dict(type='bool', default=False), return_content=dict(type='bool', default=False),
), ),
supports_check_mode=True, supports_check_mode=True,
@ -453,36 +424,20 @@ def main():
if backend == 'auto': if backend == 'auto':
# Detection what is possible # Detection what is possible
can_use_cryptography = CRYPTOGRAPHY_FOUND and CRYPTOGRAPHY_VERSION >= LooseVersion(minimal_cryptography_version) can_use_cryptography = CRYPTOGRAPHY_FOUND and CRYPTOGRAPHY_VERSION >= LooseVersion(minimal_cryptography_version)
can_use_pyopenssl = PYOPENSSL_FOUND and PYOPENSSL_VERSION >= LooseVersion(MINIMAL_PYOPENSSL_VERSION)
# Decision # Decision
if can_use_cryptography: if can_use_cryptography:
backend = 'cryptography' backend = 'cryptography'
elif can_use_pyopenssl:
if module.params['format'] == 'OpenSSH':
module.fail_json(
msg=missing_required_lib('cryptography >= {0}'.format(MINIMAL_CRYPTOGRAPHY_VERSION_OPENSSH)),
exception=CRYPTOGRAPHY_IMP_ERR
)
backend = 'pyopenssl'
# Success? # Success?
if backend == 'auto': if backend == 'auto':
module.fail_json(msg=("Can't detect any of the required Python libraries " module.fail_json(msg=("Can't detect the required Python library "
"cryptography (>= {0}) or PyOpenSSL (>= {1})").format( "cryptography (>= {0})").format(minimal_cryptography_version))
minimal_cryptography_version,
MINIMAL_PYOPENSSL_VERSION))
if module.params['format'] == 'OpenSSH' and backend != 'cryptography': if module.params['format'] == 'OpenSSH' and backend != 'cryptography':
module.fail_json(msg="Format OpenSSH requires the cryptography backend.") module.fail_json(msg="Format OpenSSH requires the cryptography backend.")
if backend == 'pyopenssl': if backend == 'cryptography':
if not PYOPENSSL_FOUND:
module.fail_json(msg=missing_required_lib('pyOpenSSL >= {0}'.format(MINIMAL_PYOPENSSL_VERSION)),
exception=PYOPENSSL_IMP_ERR)
module.deprecate('The module is using the PyOpenSSL backend. This backend has been deprecated',
version='2.0.0', collection_name='community.crypto')
elif backend == 'cryptography':
if not CRYPTOGRAPHY_FOUND: if not CRYPTOGRAPHY_FOUND:
module.fail_json(msg=missing_required_lib('cryptography >= {0}'.format(minimal_cryptography_version)), module.fail_json(msg=missing_required_lib('cryptography >= {0}'.format(minimal_cryptography_version)),
exception=CRYPTOGRAPHY_IMP_ERR) exception=CRYPTOGRAPHY_IMP_ERR)

View File

@ -14,14 +14,10 @@ module: openssl_publickey_info
short_description: Provide information for OpenSSL public keys short_description: Provide information for OpenSSL public keys
description: description:
- This module allows one to query information on OpenSSL public keys. - This module allows one to query information on OpenSSL public keys.
- It uses the pyOpenSSL or cryptography python library to interact with OpenSSL. If both the - It uses the cryptography python library to interact with OpenSSL.
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)). Please note that the PyOpenSSL backend was deprecated in Ansible 2.9
and will be removed in community.crypto 2.0.0.
version_added: 1.7.0 version_added: 1.7.0
requirements: requirements:
- PyOpenSSL >= 0.15 or cryptography >= 1.2.3 - cryptography >= 1.2.3
author: author:
- Felix Fontein (@felixfontein) - Felix Fontein (@felixfontein)
options: options:
@ -38,14 +34,11 @@ options:
select_crypto_backend: select_crypto_backend:
description: description:
- Determines which crypto backend to use. - 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). - The default choice is C(auto), which tries to use C(cryptography) if available.
- 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. - If set to C(cryptography), will try to use the L(cryptography,https://cryptography.io/) library.
- Please note that the C(pyopenssl) backend has been deprecated in Ansible 2.9, and will be removed in community.crypto 2.0.0.
From that point on, only the C(cryptography) backend will be available.
type: str type: str
default: auto default: auto
choices: [ auto, cryptography, pyopenssl ] choices: [ auto, cryptography ]
notes: notes:
- Supports C(check_mode). - Supports C(check_mode).
@ -174,7 +167,7 @@ def main():
argument_spec=dict( argument_spec=dict(
path=dict(type='path'), path=dict(type='path'),
content=dict(type='str', no_log=True), content=dict(type='str', no_log=True),
select_crypto_backend=dict(type='str', default='auto', choices=['auto', 'cryptography', 'pyopenssl']), select_crypto_backend=dict(type='str', default='auto', choices=['auto', 'cryptography']),
), ),
required_one_of=( required_one_of=(
['path', 'content'], ['path', 'content'],

View File

@ -15,13 +15,9 @@ version_added: 1.1.0
short_description: Sign data with openssl short_description: Sign data with openssl
description: description:
- This module allows one to sign data using a private key. - This module allows one to sign data using a private key.
- The module can use the cryptography Python library, or the pyOpenSSL Python - The module uses the cryptography Python library.
library. By default, it tries to detect which one is available. This can be
overridden with the I(select_crypto_backend) option. Please note that the PyOpenSSL backend
was deprecated in Ansible 2.9 and will be removed in community.crypto 2.0.0.
requirements: requirements:
- Either cryptography >= 1.4 (some key types require newer versions) - cryptography >= 1.4 (some key types require newer versions)
- Or pyOpenSSL >= 0.11 (Ed25519 and Ed448 keys are not supported with this backend)
author: author:
- Patrick Pichler (@aveexy) - Patrick Pichler (@aveexy)
- Markus Teufelberger (@MarkusTeufelberger) - Markus Teufelberger (@MarkusTeufelberger)
@ -50,12 +46,11 @@ options:
select_crypto_backend: select_crypto_backend:
description: description:
- Determines which crypto backend to use. - 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). - The default choice is C(auto), which tries to use C(cryptography) if available.
- 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. - If set to C(cryptography), will try to use the L(cryptography,https://cryptography.io/) library.
type: str type: str
default: auto default: auto
choices: [ auto, cryptography, pyopenssl ] choices: [ auto, cryptography ]
notes: notes:
- | - |
When using the C(cryptography) backend, the following key types require at least the following C(cryptography) version: When using the C(cryptography) backend, the following key types require at least the following C(cryptography) version:
@ -99,20 +94,8 @@ import traceback
from distutils.version import LooseVersion from distutils.version import LooseVersion
import base64 import base64
MINIMAL_PYOPENSSL_VERSION = '0.11'
MINIMAL_CRYPTOGRAPHY_VERSION = '1.4' MINIMAL_CRYPTOGRAPHY_VERSION = '1.4'
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
else:
PYOPENSSL_FOUND = True
CRYPTOGRAPHY_IMP_ERR = None CRYPTOGRAPHY_IMP_ERR = None
try: try:
import cryptography import cryptography
@ -170,34 +153,6 @@ class SignatureBase(OpenSSLObject):
pass pass
# Implementation with using pyOpenSSL
class SignaturePyOpenSSL(SignatureBase):
def __init__(self, module, backend):
super(SignaturePyOpenSSL, self).__init__(module, backend)
def run(self):
result = dict()
try:
with open(self.path, "rb") as f:
_in = f.read()
private_key = load_privatekey(
path=self.privatekey_path,
content=self.privatekey_content,
passphrase=self.privatekey_passphrase,
backend=self.backend,
)
signature = OpenSSL.crypto.sign(private_key, _in, "sha256")
result['signature'] = base64.b64encode(signature)
return result
except Exception as e:
raise OpenSSLObjectError(e)
# Implementation with using cryptography # Implementation with using cryptography
class SignatureCryptography(SignatureBase): class SignatureCryptography(SignatureBase):
@ -262,7 +217,7 @@ def main():
privatekey_content=dict(type='str', no_log=True), privatekey_content=dict(type='str', no_log=True),
privatekey_passphrase=dict(type='str', no_log=True), privatekey_passphrase=dict(type='str', no_log=True),
path=dict(type='path', required=True), path=dict(type='path', required=True),
select_crypto_backend=dict(type='str', choices=['auto', 'pyopenssl', 'cryptography'], default='auto'), select_crypto_backend=dict(type='str', choices=['auto', 'cryptography'], default='auto'),
), ),
mutually_exclusive=( mutually_exclusive=(
['privatekey_path', 'privatekey_content'], ['privatekey_path', 'privatekey_content'],
@ -283,29 +238,17 @@ def main():
if backend == 'auto': if backend == 'auto':
# Detection what is possible # Detection what is possible
can_use_cryptography = CRYPTOGRAPHY_FOUND and CRYPTOGRAPHY_VERSION >= LooseVersion(MINIMAL_CRYPTOGRAPHY_VERSION) can_use_cryptography = CRYPTOGRAPHY_FOUND and CRYPTOGRAPHY_VERSION >= LooseVersion(MINIMAL_CRYPTOGRAPHY_VERSION)
can_use_pyopenssl = PYOPENSSL_FOUND and PYOPENSSL_VERSION >= LooseVersion(MINIMAL_PYOPENSSL_VERSION)
# Decision # Decision
if can_use_cryptography: if can_use_cryptography:
backend = 'cryptography' backend = 'cryptography'
elif can_use_pyopenssl:
backend = 'pyopenssl'
# Success? # Success?
if backend == 'auto': if backend == 'auto':
module.fail_json(msg=("Can't detect any of the required Python libraries " module.fail_json(msg=("Can't detect the required Python library "
"cryptography (>= {0}) or PyOpenSSL (>= {1})").format( "cryptography (>= {0})").format(MINIMAL_CRYPTOGRAPHY_VERSION))
MINIMAL_CRYPTOGRAPHY_VERSION,
MINIMAL_PYOPENSSL_VERSION))
try: try:
if backend == 'pyopenssl': if backend == 'cryptography':
if not PYOPENSSL_FOUND:
module.fail_json(msg=missing_required_lib('pyOpenSSL >= {0}'.format(MINIMAL_PYOPENSSL_VERSION)),
exception=PYOPENSSL_IMP_ERR)
module.deprecate('The module is using the PyOpenSSL backend. This backend has been deprecated',
version='2.0.0', collection_name='community.crypto')
_sign = SignaturePyOpenSSL(module, backend)
elif backend == 'cryptography':
if not CRYPTOGRAPHY_FOUND: if not CRYPTOGRAPHY_FOUND:
module.fail_json(msg=missing_required_lib('cryptography >= {0}'.format(MINIMAL_CRYPTOGRAPHY_VERSION)), module.fail_json(msg=missing_required_lib('cryptography >= {0}'.format(MINIMAL_CRYPTOGRAPHY_VERSION)),
exception=CRYPTOGRAPHY_IMP_ERR) exception=CRYPTOGRAPHY_IMP_ERR)

View File

@ -15,13 +15,9 @@ version_added: 1.1.0
short_description: Verify signatures with openssl short_description: Verify signatures with openssl
description: description:
- This module allows one to verify a signature for a file by a certificate. - This module allows one to verify a signature for a file by a certificate.
- The module can use the cryptography Python library, or the pyOpenSSL Python - The module uses the cryptography Python library.
library. By default, it tries to detect which one is available. This can be
overridden with the I(select_crypto_backend) option. Please note that the PyOpenSSL backend
was deprecated in Ansible 2.9 and will be removed in community.crypto 2.0.0.
requirements: requirements:
- Either cryptography >= 1.4 (some key types require newer versions) - cryptography >= 1.4 (some key types require newer versions)
- Or pyOpenSSL >= 0.11 (Ed25519 and Ed448 keys are not supported with this backend)
author: author:
- Patrick Pichler (@aveexy) - Patrick Pichler (@aveexy)
- Markus Teufelberger (@MarkusTeufelberger) - Markus Teufelberger (@MarkusTeufelberger)
@ -49,12 +45,11 @@ options:
select_crypto_backend: select_crypto_backend:
description: description:
- Determines which crypto backend to use. - 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). - The default choice is C(auto), which tries to use C(cryptography) if available.
- 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. - If set to C(cryptography), will try to use the L(cryptography,https://cryptography.io/) library.
type: str type: str
default: auto default: auto
choices: [ auto, cryptography, pyopenssl ] choices: [ auto, cryptography ]
notes: notes:
- | - |
When using the C(cryptography) backend, the following key types require at least the following C(cryptography) version: When using the C(cryptography) backend, the following key types require at least the following C(cryptography) version:
@ -99,20 +94,8 @@ import traceback
from distutils.version import LooseVersion from distutils.version import LooseVersion
import base64 import base64
MINIMAL_PYOPENSSL_VERSION = '0.11'
MINIMAL_CRYPTOGRAPHY_VERSION = '1.4' MINIMAL_CRYPTOGRAPHY_VERSION = '1.4'
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
else:
PYOPENSSL_FOUND = True
CRYPTOGRAPHY_IMP_ERR = None CRYPTOGRAPHY_IMP_ERR = None
try: try:
import cryptography import cryptography
@ -170,37 +153,6 @@ class SignatureInfoBase(OpenSSLObject):
pass pass
# Implementation with using pyOpenSSL
class SignatureInfoPyOpenSSL(SignatureInfoBase):
def __init__(self, module, backend):
super(SignatureInfoPyOpenSSL, self).__init__(module, backend)
def run(self):
result = dict()
try:
with open(self.path, "rb") as f:
_in = f.read()
_signature = base64.b64decode(self.signature)
certificate = load_certificate(
path=self.certificate_path,
content=self.certificate_content,
backend=self.backend,
)
try:
OpenSSL.crypto.verify(certificate, _signature, _in, 'sha256')
result['valid'] = True
except Exception:
result['valid'] = False
return result
except Exception as e:
raise OpenSSLObjectError(e)
# Implementation with using cryptography # Implementation with using cryptography
class SignatureInfoCryptography(SignatureInfoBase): class SignatureInfoCryptography(SignatureInfoBase):
@ -295,7 +247,7 @@ def main():
certificate_content=dict(type='str'), certificate_content=dict(type='str'),
path=dict(type='path', required=True), path=dict(type='path', required=True),
signature=dict(type='str', required=True), signature=dict(type='str', required=True),
select_crypto_backend=dict(type='str', choices=['auto', 'pyopenssl', 'cryptography'], default='auto'), select_crypto_backend=dict(type='str', choices=['auto', 'cryptography'], default='auto'),
), ),
mutually_exclusive=( mutually_exclusive=(
['certificate_path', 'certificate_content'], ['certificate_path', 'certificate_content'],
@ -316,29 +268,17 @@ def main():
if backend == 'auto': if backend == 'auto':
# Detection what is possible # Detection what is possible
can_use_cryptography = CRYPTOGRAPHY_FOUND and CRYPTOGRAPHY_VERSION >= LooseVersion(MINIMAL_CRYPTOGRAPHY_VERSION) can_use_cryptography = CRYPTOGRAPHY_FOUND and CRYPTOGRAPHY_VERSION >= LooseVersion(MINIMAL_CRYPTOGRAPHY_VERSION)
can_use_pyopenssl = PYOPENSSL_FOUND and PYOPENSSL_VERSION >= LooseVersion(MINIMAL_PYOPENSSL_VERSION)
# Decision # Decision
if can_use_cryptography: if can_use_cryptography:
backend = 'cryptography' backend = 'cryptography'
elif can_use_pyopenssl:
backend = 'pyopenssl'
# Success? # Success?
if backend == 'auto': if backend == 'auto':
module.fail_json(msg=("Can't detect any of the required Python libraries " module.fail_json(msg=("Can't detect any of the required Python libraries "
"cryptography (>= {0}) or PyOpenSSL (>= {1})").format( "cryptography (>= {0})").format(MINIMAL_CRYPTOGRAPHY_VERSION))
MINIMAL_CRYPTOGRAPHY_VERSION,
MINIMAL_PYOPENSSL_VERSION))
try: try:
if backend == 'pyopenssl': if backend == 'cryptography':
if not PYOPENSSL_FOUND:
module.fail_json(msg=missing_required_lib('pyOpenSSL >= {0}'.format(MINIMAL_PYOPENSSL_VERSION)),
exception=PYOPENSSL_IMP_ERR)
module.deprecate('The module is using the PyOpenSSL backend. This backend has been deprecated',
version='2.0.0', collection_name='community.crypto')
_sign = SignatureInfoPyOpenSSL(module, backend)
elif backend == 'cryptography':
if not CRYPTOGRAPHY_FOUND: if not CRYPTOGRAPHY_FOUND:
module.fail_json(msg=missing_required_lib('cryptography >= {0}'.format(MINIMAL_CRYPTOGRAPHY_VERSION)), module.fail_json(msg=missing_required_lib('cryptography >= {0}'.format(MINIMAL_CRYPTOGRAPHY_VERSION)),
exception=CRYPTOGRAPHY_IMP_ERR) exception=CRYPTOGRAPHY_IMP_ERR)

View File

@ -15,11 +15,7 @@ module: x509_certificate_info
short_description: Provide information of OpenSSL X.509 certificates short_description: Provide information of OpenSSL X.509 certificates
description: description:
- This module allows one to query information on OpenSSL certificates. - This module allows one to query information on OpenSSL certificates.
- It uses the pyOpenSSL or cryptography python library to interact with OpenSSL. If both the - It uses the cryptography python library to interact with OpenSSL.
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)). Please note that the PyOpenSSL backend was deprecated in Ansible 2.9
and will be removed in community.crypto 2.0.0.
- Note that this module was called C(openssl_certificate_info) when included directly in Ansible - Note that this module was called C(openssl_certificate_info) when included directly in Ansible
up to version 2.9. When moved to the collection C(community.crypto), it was renamed to up to version 2.9. When moved to the collection C(community.crypto), it was renamed to
M(community.crypto.x509_certificate_info). From Ansible 2.10 on, it can still be used by the M(community.crypto.x509_certificate_info). From Ansible 2.10 on, it can still be used by the
@ -29,7 +25,7 @@ description:
keyword, the new name M(community.crypto.x509_certificate_info) should be used to avoid keyword, the new name M(community.crypto.x509_certificate_info) should be used to avoid
a deprecation warning. a deprecation warning.
requirements: requirements:
- PyOpenSSL >= 0.15 or cryptography >= 1.6 - cryptography >= 1.6
author: author:
- Felix Fontein (@felixfontein) - Felix Fontein (@felixfontein)
- Yanis Guenane (@Spredzy) - Yanis Guenane (@Spredzy)
@ -60,14 +56,11 @@ options:
select_crypto_backend: select_crypto_backend:
description: description:
- Determines which crypto backend to use. - 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). - The default choice is C(auto), which tries to use C(cryptography) if available.
- 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. - If set to C(cryptography), will try to use the L(cryptography,https://cryptography.io/) library.
- Please note that the C(pyopenssl) backend has been deprecated in Ansible 2.9, and will be removed in community.crypto 2.0.0.
From that point on, only the C(cryptography) backend will be available.
type: str type: str
default: auto default: auto
choices: [ auto, cryptography, pyopenssl ] choices: [ auto, cryptography ]
notes: notes:
- All timestamp values are provided in ASN.1 TIME format, in other words, following the C(YYYYMMDDHHMMSSZ) pattern. - All timestamp values are provided in ASN.1 TIME format, in other words, following the C(YYYYMMDDHHMMSSZ) pattern.
@ -341,7 +334,7 @@ subject_key_identifier:
- The certificate's subject key identifier. - The certificate's subject key identifier.
- The identifier is returned in hexadecimal, with C(:) used to separate bytes. - The identifier is returned in hexadecimal, with C(:) used to separate bytes.
- Is C(none) if the C(SubjectKeyIdentifier) extension is not present. - Is C(none) if the C(SubjectKeyIdentifier) extension is not present.
returned: success and if the pyOpenSSL backend is I(not) used returned: success
type: str type: str
sample: '00:11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff:00:11:22:33' sample: '00:11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff:00:11:22:33'
authority_key_identifier: authority_key_identifier:
@ -349,14 +342,14 @@ authority_key_identifier:
- The certificate's authority key identifier. - The certificate's authority key identifier.
- The identifier is returned in hexadecimal, with C(:) used to separate bytes. - The identifier is returned in hexadecimal, with C(:) used to separate bytes.
- Is C(none) if the C(AuthorityKeyIdentifier) extension is not present. - Is C(none) if the C(AuthorityKeyIdentifier) extension is not present.
returned: success and if the pyOpenSSL backend is I(not) used returned: success
type: str type: str
sample: '00:11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff:00:11:22:33' sample: '00:11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff:00:11:22:33'
authority_cert_issuer: authority_cert_issuer:
description: description:
- The certificate's authority cert issuer as a list of general names. - The certificate's authority cert issuer as a list of general names.
- Is C(none) if the C(AuthorityKeyIdentifier) extension is not present. - Is C(none) if the C(AuthorityKeyIdentifier) extension is not present.
returned: success and if the pyOpenSSL backend is I(not) used returned: success
type: list type: list
elements: str elements: str
sample: "[DNS:www.ansible.com, IP:1.2.3.4]" sample: "[DNS:www.ansible.com, IP:1.2.3.4]"
@ -364,7 +357,7 @@ authority_cert_serial_number:
description: description:
- The certificate's authority cert serial number. - The certificate's authority cert serial number.
- Is C(none) if the C(AuthorityKeyIdentifier) extension is not present. - Is C(none) if the C(AuthorityKeyIdentifier) extension is not present.
returned: success and if the pyOpenSSL backend is I(not) used returned: success
type: int type: int
sample: '12345' sample: '12345'
ocsp_uri: ocsp_uri:
@ -398,7 +391,7 @@ def main():
path=dict(type='path'), path=dict(type='path'),
content=dict(type='str'), content=dict(type='str'),
valid_at=dict(type='dict'), valid_at=dict(type='dict'),
select_crypto_backend=dict(type='str', default='auto', choices=['auto', 'cryptography', 'pyopenssl']), select_crypto_backend=dict(type='str', default='auto', choices=['auto', 'cryptography']),
), ),
required_one_of=( required_one_of=(
['path', 'content'], ['path', 'content'],

View File

@ -1,4 +1,3 @@
dependencies: dependencies:
- setup_openssl - setup_openssl
- setup_pyopenssl
- prepare_http_tests - prepare_http_tests

View File

@ -30,17 +30,7 @@
that: that:
- result is success or skip_tests - result is success or skip_tests
when: | when: cryptography_version.stdout is version('1.6', '>=')
pyopenssl_version.stdout is version('0.15', '>=') or
cryptography_version.stdout is version('1.6', '>=')
- block:
- include_tasks: ../tests/validate.yml
vars:
select_crypto_backend: pyopenssl
when: pyopenssl_version.stdout is version('0.15', '>=') and not skip_tests
- block: - block:

View File

@ -1,4 +1,3 @@
dependencies: dependencies:
- setup_openssl - setup_openssl
- setup_pyopenssl
- setup_remote_tmp_dir - setup_remote_tmp_dir

View File

@ -363,7 +363,6 @@
commonName: www.ansible.com commonName: www.ansible.com
subject_key_identifier: "00:11:22:33" subject_key_identifier: "00:11:22:33"
select_crypto_backend: '{{ select_crypto_backend }}' select_crypto_backend: '{{ select_crypto_backend }}'
when: select_crypto_backend != 'pyopenssl'
register: subject_key_identifier_1 register: subject_key_identifier_1
- name: "({{ select_crypto_backend }}) Generate CSR with subject key identifier (idempotency)" - name: "({{ select_crypto_backend }}) Generate CSR with subject key identifier (idempotency)"
@ -374,7 +373,6 @@
commonName: www.ansible.com commonName: www.ansible.com
subject_key_identifier: "00:11:22:33" subject_key_identifier: "00:11:22:33"
select_crypto_backend: '{{ select_crypto_backend }}' select_crypto_backend: '{{ select_crypto_backend }}'
when: select_crypto_backend != 'pyopenssl'
register: subject_key_identifier_2 register: subject_key_identifier_2
- name: "({{ select_crypto_backend }}) Generate CSR with subject key identifier (change)" - name: "({{ select_crypto_backend }}) Generate CSR with subject key identifier (change)"
@ -385,7 +383,6 @@
commonName: www.ansible.com commonName: www.ansible.com
subject_key_identifier: "44:55:66:77:88" subject_key_identifier: "44:55:66:77:88"
select_crypto_backend: '{{ select_crypto_backend }}' select_crypto_backend: '{{ select_crypto_backend }}'
when: select_crypto_backend != 'pyopenssl'
register: subject_key_identifier_3 register: subject_key_identifier_3
- name: "({{ select_crypto_backend }}) Generate CSR with subject key identifier (auto-create)" - name: "({{ select_crypto_backend }}) Generate CSR with subject key identifier (auto-create)"
@ -396,7 +393,6 @@
commonName: www.ansible.com commonName: www.ansible.com
create_subject_key_identifier: yes create_subject_key_identifier: yes
select_crypto_backend: '{{ select_crypto_backend }}' select_crypto_backend: '{{ select_crypto_backend }}'
when: select_crypto_backend != 'pyopenssl'
register: subject_key_identifier_4 register: subject_key_identifier_4
- name: "({{ select_crypto_backend }}) Generate CSR with subject key identifier (auto-create idempotency)" - name: "({{ select_crypto_backend }}) Generate CSR with subject key identifier (auto-create idempotency)"
@ -407,7 +403,6 @@
commonName: www.ansible.com commonName: www.ansible.com
create_subject_key_identifier: yes create_subject_key_identifier: yes
select_crypto_backend: '{{ select_crypto_backend }}' select_crypto_backend: '{{ select_crypto_backend }}'
when: select_crypto_backend != 'pyopenssl'
register: subject_key_identifier_5 register: subject_key_identifier_5
- name: "({{ select_crypto_backend }}) Generate CSR with subject key identifier (remove)" - name: "({{ select_crypto_backend }}) Generate CSR with subject key identifier (remove)"
@ -417,7 +412,6 @@
subject: subject:
commonName: www.ansible.com commonName: www.ansible.com
select_crypto_backend: '{{ select_crypto_backend }}' select_crypto_backend: '{{ select_crypto_backend }}'
when: select_crypto_backend != 'pyopenssl'
register: subject_key_identifier_6 register: subject_key_identifier_6
- name: "({{ select_crypto_backend }}) Generate CSR with authority key identifier" - name: "({{ select_crypto_backend }}) Generate CSR with authority key identifier"
@ -428,7 +422,6 @@
commonName: www.ansible.com commonName: www.ansible.com
authority_key_identifier: "00:11:22:33" authority_key_identifier: "00:11:22:33"
select_crypto_backend: '{{ select_crypto_backend }}' select_crypto_backend: '{{ select_crypto_backend }}'
when: select_crypto_backend != 'pyopenssl'
register: authority_key_identifier_1 register: authority_key_identifier_1
- name: "({{ select_crypto_backend }}) Generate CSR with authority key identifier (idempotency)" - name: "({{ select_crypto_backend }}) Generate CSR with authority key identifier (idempotency)"
@ -439,7 +432,6 @@
commonName: www.ansible.com commonName: www.ansible.com
authority_key_identifier: "00:11:22:33" authority_key_identifier: "00:11:22:33"
select_crypto_backend: '{{ select_crypto_backend }}' select_crypto_backend: '{{ select_crypto_backend }}'
when: select_crypto_backend != 'pyopenssl'
register: authority_key_identifier_2 register: authority_key_identifier_2
- name: "({{ select_crypto_backend }}) Generate CSR with authority key identifier (change)" - name: "({{ select_crypto_backend }}) Generate CSR with authority key identifier (change)"
@ -450,7 +442,6 @@
commonName: www.ansible.com commonName: www.ansible.com
authority_key_identifier: "44:55:66:77:88" authority_key_identifier: "44:55:66:77:88"
select_crypto_backend: '{{ select_crypto_backend }}' select_crypto_backend: '{{ select_crypto_backend }}'
when: select_crypto_backend != 'pyopenssl'
register: authority_key_identifier_3 register: authority_key_identifier_3
- name: "({{ select_crypto_backend }}) Generate CSR with authority key identifier (remove)" - name: "({{ select_crypto_backend }}) Generate CSR with authority key identifier (remove)"
@ -460,7 +451,6 @@
subject: subject:
commonName: www.ansible.com commonName: www.ansible.com
select_crypto_backend: '{{ select_crypto_backend }}' select_crypto_backend: '{{ select_crypto_backend }}'
when: select_crypto_backend != 'pyopenssl'
register: authority_key_identifier_4 register: authority_key_identifier_4
- name: "({{ select_crypto_backend }}) Generate CSR with authority cert issuer / serial number" - name: "({{ select_crypto_backend }}) Generate CSR with authority cert issuer / serial number"
@ -474,7 +464,6 @@
- "IP:1.2.3.4" - "IP:1.2.3.4"
authority_cert_serial_number: 12345 authority_cert_serial_number: 12345
select_crypto_backend: '{{ select_crypto_backend }}' select_crypto_backend: '{{ select_crypto_backend }}'
when: select_crypto_backend != 'pyopenssl'
register: authority_cert_issuer_sn_1 register: authority_cert_issuer_sn_1
- name: "({{ select_crypto_backend }}) Generate CSR with authority cert issuer / serial number (idempotency)" - name: "({{ select_crypto_backend }}) Generate CSR with authority cert issuer / serial number (idempotency)"
@ -488,7 +477,6 @@
- "IP:1.2.3.4" - "IP:1.2.3.4"
authority_cert_serial_number: 12345 authority_cert_serial_number: 12345
select_crypto_backend: '{{ select_crypto_backend }}' select_crypto_backend: '{{ select_crypto_backend }}'
when: select_crypto_backend != 'pyopenssl'
register: authority_cert_issuer_sn_2 register: authority_cert_issuer_sn_2
- name: "({{ select_crypto_backend }}) Generate CSR with authority cert issuer / serial number (change issuer)" - name: "({{ select_crypto_backend }}) Generate CSR with authority cert issuer / serial number (change issuer)"
@ -502,7 +490,6 @@
- "DNS:ca.example.org" - "DNS:ca.example.org"
authority_cert_serial_number: 12345 authority_cert_serial_number: 12345
select_crypto_backend: '{{ select_crypto_backend }}' select_crypto_backend: '{{ select_crypto_backend }}'
when: select_crypto_backend != 'pyopenssl'
register: authority_cert_issuer_sn_3 register: authority_cert_issuer_sn_3
- name: "({{ select_crypto_backend }}) Generate CSR with authority cert issuer / serial number (change serial number)" - name: "({{ select_crypto_backend }}) Generate CSR with authority cert issuer / serial number (change serial number)"
@ -516,7 +503,6 @@
- "DNS:ca.example.org" - "DNS:ca.example.org"
authority_cert_serial_number: 54321 authority_cert_serial_number: 54321
select_crypto_backend: '{{ select_crypto_backend }}' select_crypto_backend: '{{ select_crypto_backend }}'
when: select_crypto_backend != 'pyopenssl'
register: authority_cert_issuer_sn_4 register: authority_cert_issuer_sn_4
- name: "({{ select_crypto_backend }}) Generate CSR with authority cert issuer / serial number (remove)" - name: "({{ select_crypto_backend }}) Generate CSR with authority cert issuer / serial number (remove)"
@ -525,7 +511,6 @@
privatekey_path: '{{ remote_tmp_dir }}/privatekey.pem' privatekey_path: '{{ remote_tmp_dir }}/privatekey.pem'
subject: subject:
commonName: www.ansible.com commonName: www.ansible.com
when: select_crypto_backend != 'pyopenssl'
register: authority_cert_issuer_sn_5 register: authority_cert_issuer_sn_5
- name: "({{ select_crypto_backend }}) Generate CSR with everything" - name: "({{ select_crypto_backend }}) Generate CSR with everything"
@ -561,37 +546,24 @@
- Encipher Only - Encipher Only
- decipherOnly - decipherOnly
key_usage_critical: yes key_usage_critical: yes
extended_key_usage: '{{ value_for_extended_key_usage if select_crypto_backend != "pyopenssl" else value_for_extended_key_usage_pyopenssl }}' extended_key_usage: '{{ value_for_extended_key_usage }}'
subject_alt_name: '{{ value_for_san if select_crypto_backend != "pyopenssl" else value_for_san_pyopenssl }}' subject_alt_name: '{{ value_for_san }}'
basic_constraints: basic_constraints:
- "CA:TRUE" - "CA:TRUE"
- "pathlen:23" - "pathlen:23"
basic_constraints_critical: yes basic_constraints_critical: yes
name_constraints_permitted: '{{ value_for_name_constraints_permitted if select_crypto_backend != "pyopenssl" else value_for_name_constraints_permitted_pyopenssl }}' name_constraints_permitted: '{{ value_for_name_constraints_permitted }}'
name_constraints_excluded: name_constraints_excluded:
- "DNS:.example.com" - "DNS:.example.com"
- "DNS:.org" - "DNS:.org"
name_constraints_critical: yes name_constraints_critical: yes
ocsp_must_staple: yes ocsp_must_staple: yes
subject_key_identifier: '{{ "00:11:22:33" if select_crypto_backend != "pyopenssl" else omit }}' subject_key_identifier: 00:11:22:33
authority_key_identifier: '{{ "44:55:66:77" if select_crypto_backend != "pyopenssl" else omit }}' authority_key_identifier: 44:55:66:77
authority_cert_issuer: '{{ value_for_authority_cert_issuer if select_crypto_backend != "pyopenssl" else omit }}' authority_cert_issuer: '{{ value_for_authority_cert_issuer }}'
authority_cert_serial_number: '{{ 12345 if select_crypto_backend != "pyopenssl" else omit }}' authority_cert_serial_number: 12345
select_crypto_backend: '{{ select_crypto_backend }}' select_crypto_backend: '{{ select_crypto_backend }}'
vars: vars:
value_for_extended_key_usage_pyopenssl:
- serverAuth # the same as "TLS Web Server Authentication"
- TLS Web Server Authentication
- TLS Web Client Authentication
- Code Signing
- E-mail Protection
- timeStamping
- OCSPSigning
- Any Extended Key Usage
- qcStatements
- DVCS
- IPSec User
- biometricInfo
value_for_extended_key_usage: value_for_extended_key_usage:
- serverAuth # the same as "TLS Web Server Authentication" - serverAuth # the same as "TLS Web Server Authentication"
- TLS Web Server Authentication - TLS Web Server Authentication
@ -609,13 +581,6 @@
value_for_authority_cert_issuer: value_for_authority_cert_issuer:
- "DNS:ca.example.org" - "DNS:ca.example.org"
- "IP:1.2.3.4" - "IP:1.2.3.4"
value_for_san_pyopenssl:
- "DNS:www.ansible.com"
- "IP:1.2.3.4"
- "IP:::1"
- "email:test@example.org"
- "URI:https://example.org/test/index.html"
- "RID:1.2.3.4"
value_for_san: value_for_san:
- "DNS:www.ansible.com" - "DNS:www.ansible.com"
- "IP:1.2.3.4" - "IP:1.2.3.4"
@ -631,9 +596,6 @@
- "DNS:www.example.com" - "DNS:www.example.com"
- "IP:1.2.3.0/24" - "IP:1.2.3.0/24"
- "IP:::1:0:0/112" - "IP:::1:0:0/112"
value_for_name_constraints_permitted_pyopenssl:
- "DNS:www.example.com"
- "IP:1.2.3.0/255.255.255.0"
register: everything_1 register: everything_1
- name: "({{ select_crypto_backend }}) Generate CSR with everything (idempotent, check mode)" - name: "({{ select_crypto_backend }}) Generate CSR with everything (idempotent, check mode)"
@ -669,37 +631,24 @@
- Encipher Only - Encipher Only
- decipherOnly - decipherOnly
key_usage_critical: yes key_usage_critical: yes
extended_key_usage: '{{ value_for_extended_key_usage if select_crypto_backend != "pyopenssl" else value_for_extended_key_usage_pyopenssl }}' extended_key_usage: '{{ value_for_extended_key_usage }}'
subject_alt_name: '{{ value_for_san if select_crypto_backend != "pyopenssl" else value_for_san_pyopenssl }}' subject_alt_name: '{{ value_for_san }}'
basic_constraints: basic_constraints:
- "CA:TRUE" - "CA:TRUE"
- "pathlen:23" - "pathlen:23"
basic_constraints_critical: yes basic_constraints_critical: yes
name_constraints_permitted: '{{ value_for_name_constraints_permitted if select_crypto_backend != "pyopenssl" else value_for_name_constraints_permitted_pyopenssl }}' name_constraints_permitted: '{{ value_for_name_constraints_permitted }}'
name_constraints_excluded: name_constraints_excluded:
- "DNS:.org" - "DNS:.org"
- "DNS:.example.com" - "DNS:.example.com"
name_constraints_critical: yes name_constraints_critical: yes
ocsp_must_staple: yes ocsp_must_staple: yes
subject_key_identifier: '{{ "00:11:22:33" if select_crypto_backend != "pyopenssl" else omit }}' subject_key_identifier: 00:11:22:33
authority_key_identifier: '{{ "44:55:66:77" if select_crypto_backend != "pyopenssl" else omit }}' authority_key_identifier: 44:55:66:77
authority_cert_issuer: '{{ value_for_authority_cert_issuer if select_crypto_backend != "pyopenssl" else omit }}' authority_cert_issuer: '{{ value_for_authority_cert_issuer }}'
authority_cert_serial_number: '{{ 12345 if select_crypto_backend != "pyopenssl" else omit }}' authority_cert_serial_number: 12345
select_crypto_backend: '{{ select_crypto_backend }}' select_crypto_backend: '{{ select_crypto_backend }}'
vars: vars:
value_for_extended_key_usage_pyopenssl:
- serverAuth # the same as "TLS Web Server Authentication"
- TLS Web Server Authentication
- TLS Web Client Authentication
- Code Signing
- E-mail Protection
- timeStamping
- OCSPSigning
- Any Extended Key Usage
- qcStatements
- DVCS
- IPSec User
- biometricInfo
value_for_extended_key_usage: value_for_extended_key_usage:
- serverAuth # the same as "TLS Web Server Authentication" - serverAuth # the same as "TLS Web Server Authentication"
- TLS Web Server Authentication - TLS Web Server Authentication
@ -717,13 +666,6 @@
value_for_authority_cert_issuer: value_for_authority_cert_issuer:
- "DNS:ca.example.org" - "DNS:ca.example.org"
- "IP:1.2.3.4" - "IP:1.2.3.4"
value_for_san_pyopenssl:
- "DNS:www.ansible.com"
- "IP:1.2.3.4"
- "IP:::1"
- "email:test@example.org"
- "URI:https://example.org/test/index.html"
- "RID:1.2.3.4"
value_for_san: value_for_san:
- "DNS:www.ansible.com" - "DNS:www.ansible.com"
- "IP:1.2.3.4" - "IP:1.2.3.4"
@ -739,9 +681,6 @@
- "DNS:www.example.com" - "DNS:www.example.com"
- "IP:1.2.3.0/255.255.255.0" - "IP:1.2.3.0/255.255.255.0"
- "IP:0::0:1:0:0/112" - "IP:0::0:1:0:0/112"
value_for_name_constraints_permitted_pyopenssl:
- "DNS:www.example.com"
- "IP:1.2.3.0/255.255.255.0"
check_mode: yes check_mode: yes
register: everything_2 register: everything_2
@ -778,37 +717,24 @@
- Encipher Only - Encipher Only
- decipherOnly - decipherOnly
key_usage_critical: yes key_usage_critical: yes
extended_key_usage: '{{ value_for_extended_key_usage if select_crypto_backend != "pyopenssl" else value_for_extended_key_usage_pyopenssl }}' extended_key_usage: '{{ value_for_extended_key_usage }}'
subject_alt_name: '{{ value_for_san if select_crypto_backend != "pyopenssl" else value_for_san_pyopenssl }}' subject_alt_name: '{{ value_for_san }}'
basic_constraints: basic_constraints:
- "CA:TRUE" - "CA:TRUE"
- "pathlen:23" - "pathlen:23"
basic_constraints_critical: yes basic_constraints_critical: yes
name_constraints_permitted: '{{ value_for_name_constraints_permitted if select_crypto_backend != "pyopenssl" else value_for_name_constraints_permitted_pyopenssl }}' name_constraints_permitted: '{{ value_for_name_constraints_permitted }}'
name_constraints_excluded: name_constraints_excluded:
- "DNS:.org" - "DNS:.org"
- "DNS:.example.com" - "DNS:.example.com"
name_constraints_critical: yes name_constraints_critical: yes
ocsp_must_staple: yes ocsp_must_staple: yes
subject_key_identifier: '{{ "00:11:22:33" if select_crypto_backend != "pyopenssl" else omit }}' subject_key_identifier: 00:11:22:33
authority_key_identifier: '{{ "44:55:66:77" if select_crypto_backend != "pyopenssl" else omit }}' authority_key_identifier: 44:55:66:77
authority_cert_issuer: '{{ value_for_authority_cert_issuer if select_crypto_backend != "pyopenssl" else omit }}' authority_cert_issuer: '{{ value_for_authority_cert_issuer }}'
authority_cert_serial_number: '{{ 12345 if select_crypto_backend != "pyopenssl" else omit }}' authority_cert_serial_number: 12345
select_crypto_backend: '{{ select_crypto_backend }}' select_crypto_backend: '{{ select_crypto_backend }}'
vars: vars:
value_for_extended_key_usage_pyopenssl:
- serverAuth # the same as "TLS Web Server Authentication"
- TLS Web Server Authentication
- TLS Web Client Authentication
- Code Signing
- E-mail Protection
- timeStamping
- OCSPSigning
- Any Extended Key Usage
- qcStatements
- DVCS
- IPSec User
- biometricInfo
value_for_extended_key_usage: value_for_extended_key_usage:
- serverAuth # the same as "TLS Web Server Authentication" - serverAuth # the same as "TLS Web Server Authentication"
- TLS Web Server Authentication - TLS Web Server Authentication
@ -826,13 +752,6 @@
value_for_authority_cert_issuer: value_for_authority_cert_issuer:
- "DNS:ca.example.org" - "DNS:ca.example.org"
- "IP:1.2.3.4" - "IP:1.2.3.4"
value_for_san_pyopenssl:
- "DNS:www.ansible.com"
- "IP:1.2.3.4"
- "IP:::1"
- "email:test@example.org"
- "URI:https://example.org/test/index.html"
- "RID:1.2.3.4"
value_for_san: value_for_san:
- "DNS:www.ansible.com" - "DNS:www.ansible.com"
- "IP:1.2.3.4" - "IP:1.2.3.4"
@ -848,9 +767,6 @@
- "DNS:www.example.com" - "DNS:www.example.com"
- "IP:1.2.3.0/255.255.255.0" - "IP:1.2.3.0/255.255.255.0"
- "IP:0::0:1:0:0/112" - "IP:0::0:1:0:0/112"
value_for_name_constraints_permitted_pyopenssl:
- "DNS:www.example.com"
- "IP:1.2.3.0/255.255.255.0"
register: everything_3 register: everything_3
- name: "({{ select_crypto_backend }}) Get info from CSR with everything" - name: "({{ select_crypto_backend }}) Get info from CSR with everything"

View File

@ -4,40 +4,18 @@
# and should not be used as examples of how to write Ansible roles # # and should not be used as examples of how to write Ansible roles #
#################################################################### ####################################################################
- name: Prepare private key for backend autodetection test
openssl_privatekey:
path: '{{ remote_tmp_dir }}/privatekey_backend_selection.pem'
size: '{{ default_rsa_key_size }}'
- name: Run module with backend autodetection
openssl_csr:
path: '{{ remote_tmp_dir }}/csr_backend_selection.csr'
privatekey_path: '{{ remote_tmp_dir }}/privatekey_backend_selection.pem'
subject:
commonName: www.ansible.com
- block: - block:
- name: Running tests with pyOpenSSL backend - name: Prepare private key for backend autodetection test
include_tasks: impl.yml openssl_privatekey:
vars: path: '{{ remote_tmp_dir }}/privatekey_backend_selection.pem'
select_crypto_backend: pyopenssl size: '{{ default_rsa_key_size }}'
- name: Run module with backend autodetection
openssl_csr:
path: '{{ remote_tmp_dir }}/csr_backend_selection.csr'
privatekey_path: '{{ remote_tmp_dir }}/privatekey_backend_selection.pem'
subject:
commonName: www.ansible.com
- import_tasks: ../tests/validate.yml
vars:
select_crypto_backend: pyopenssl
when: pyopenssl_version.stdout is version('0.15', '>=')
- name: Remove output directory
file:
path: "{{ remote_tmp_dir }}"
state: absent
- name: Re-create output directory
file:
path: "{{ remote_tmp_dir }}"
state: directory
- block:
- name: Running tests with cryptography backend - name: Running tests with cryptography backend
include_tasks: impl.yml include_tasks: impl.yml
vars: vars:

View File

@ -74,13 +74,13 @@
- "'Subject Alternative Name' in generate_csr_invalid_san.msg" - "'Subject Alternative Name' in generate_csr_invalid_san.msg"
- name: "({{ select_crypto_backend }}) Validate invalid SAN (2/2)" - name: "({{ select_crypto_backend }}) Validate invalid SAN (2/2)"
# Note that pyOpenSSL simply accepts this name, and modern cryptography versions do so as well. # Note that modern cryptography versions simply accept this name.
# The error has been observed with cryptography 1.7.2 and 1.9, but not with 2.3 and newer. # The error has been observed with cryptography 1.7.2 and 1.9, but not with 2.3 and newer.
assert: assert:
that: that:
- generate_csr_invalid_san_2 is failed - generate_csr_invalid_san_2 is failed
- "'The label system:kube-controller-manager is not a valid A-label' in generate_csr_invalid_san_2.msg" - "'The label system:kube-controller-manager is not a valid A-label' in generate_csr_invalid_san_2.msg"
when: select_crypto_backend == 'cryptography' and cryptography_version.stdout is version('2.0', '<') when: cryptography_version.stdout is version('2.0', '<')
- name: "({{ select_crypto_backend }}) Validate OCSP Must Staple CSR (test - everything)" - name: "({{ select_crypto_backend }}) Validate OCSP Must Staple CSR (test - everything)"
shell: "{{ openssl_binary }} req -noout -in {{ remote_tmp_dir }}/csr_ocsp.csr -text" shell: "{{ openssl_binary }} req -noout -in {{ remote_tmp_dir }}/csr_ocsp.csr -text"
@ -156,7 +156,6 @@
- subject_key_identifier_4 is changed - subject_key_identifier_4 is changed
- subject_key_identifier_5 is not changed - subject_key_identifier_5 is not changed
- subject_key_identifier_6 is changed - subject_key_identifier_6 is changed
when: select_crypto_backend != 'pyopenssl'
- name: "({{ select_crypto_backend }}) Verify that authority key identifier handling works" - name: "({{ select_crypto_backend }}) Verify that authority key identifier handling works"
assert: assert:
@ -165,7 +164,6 @@
- authority_key_identifier_2 is not changed - authority_key_identifier_2 is not changed
- authority_key_identifier_3 is changed - authority_key_identifier_3 is changed
- authority_key_identifier_4 is changed - authority_key_identifier_4 is changed
when: select_crypto_backend != 'pyopenssl'
- name: "({{ select_crypto_backend }}) Verify that authority cert issuer / serial number handling works" - name: "({{ select_crypto_backend }}) Verify that authority cert issuer / serial number handling works"
assert: assert:
@ -175,7 +173,6 @@
- authority_cert_issuer_sn_3 is changed - authority_cert_issuer_sn_3 is changed
- authority_cert_issuer_sn_4 is changed - authority_cert_issuer_sn_4 is changed
- authority_cert_issuer_sn_5 is changed - authority_cert_issuer_sn_5 is changed
when: select_crypto_backend != 'pyopenssl'
- name: "({{ select_crypto_backend }}) Check backup" - name: "({{ select_crypto_backend }}) Check backup"
assert: assert:
@ -243,38 +240,7 @@
] ]
- everything_info.name_constraints_critical == true - everything_info.name_constraints_critical == true
- name: "({{ select_crypto_backend }}) Check CSR with everything (pyOpenSSL specific)" - name: "({{ select_crypto_backend }}) Check CSR with everything"
assert:
that:
- everything_info.subject_alt_name == [
"DNS:www.ansible.com",
"IP:1.2.3.4",
"IP:::1",
"email:test@example.org",
"URI:https://example.org/test/index.html",
"RID:1.2.3.4",
]
- everything_info.extended_key_usage == [
"Any Extended Key Usage",
"Biometric Info",
"Code Signing",
"E-mail Protection",
"IPSec User",
"OCSP Signing",
"TLS Web Client Authentication",
"TLS Web Server Authentication",
"TLS Web Server Authentication",
"Time Stamping",
"dvcs",
"qcStatements",
]
- everything_info.name_constraints_permitted == [
"DNS:www.example.com",
"IP:1.2.3.0/24",
]
when: select_crypto_backend == 'pyopenssl'
- name: "({{ select_crypto_backend }}) Check CSR with everything (non-pyOpenSSL specific)"
assert: assert:
that: that:
- everything_info.authority_cert_issuer == [ - everything_info.authority_cert_issuer == [
@ -316,7 +282,6 @@
"IP:1.2.3.0/24", "IP:1.2.3.0/24",
"IP:::1:0:0/112", "IP:::1:0:0/112",
] ]
when: select_crypto_backend != 'pyopenssl'
- name: "({{ select_crypto_backend }}) Verify Ed25519 and Ed448 tests (for cryptography >= 2.6, < 2.8)" - name: "({{ select_crypto_backend }}) Verify Ed25519 and Ed448 tests (for cryptography >= 2.6, < 2.8)"
assert: assert:

View File

@ -1,5 +1,4 @@
dependencies: dependencies:
- setup_openssl - setup_openssl
- setup_pyopenssl
- setup_remote_tmp_dir - setup_remote_tmp_dir
- prepare_jinja2_compat - prepare_jinja2_compat

View File

@ -28,11 +28,7 @@
expected_authority_cert_issuer: expected_authority_cert_issuer:
- "DNS:ca.example.org" - "DNS:ca.example.org"
- "IP:1.2.3.4" - "IP:1.2.3.4"
when: select_crypto_backend != 'pyopenssl' and cryptography_version.stdout is version('1.3', '>=') when: cryptography_version.stdout is version('1.3', '>=')
- name: "({{ select_crypto_backend }}) Update result list"
set_fact:
info_results: "{{ info_results + [result] }}"
- name: "({{ select_crypto_backend }}) Read CSR" - name: "({{ select_crypto_backend }}) Read CSR"
slurp: slurp:
@ -56,10 +52,6 @@
select_crypto_backend: '{{ select_crypto_backend }}' select_crypto_backend: '{{ select_crypto_backend }}'
register: result register: result
- name: "({{ select_crypto_backend }}) Update result list"
set_fact:
info_results: "{{ info_results + [result] }}"
- name: "({{ select_crypto_backend }}) Get CSR info" - name: "({{ select_crypto_backend }}) Get CSR info"
openssl_csr_info: openssl_csr_info:
path: '{{ remote_tmp_dir }}/csr_3.csr' path: '{{ remote_tmp_dir }}/csr_3.csr'
@ -76,11 +68,7 @@
expected_authority_cert_issuer: expected_authority_cert_issuer:
- "DNS:ca.example.org" - "DNS:ca.example.org"
- "IP:1.2.3.4" - "IP:1.2.3.4"
when: select_crypto_backend != 'pyopenssl' and cryptography_version.stdout is version('1.3', '>=') when: cryptography_version.stdout is version('1.3', '>=')
- name: "({{ select_crypto_backend }}) Update result list"
set_fact:
info_results: "{{ info_results + [result] }}"
- name: "({{ select_crypto_backend }}) Get CSR info" - name: "({{ select_crypto_backend }}) Get CSR info"
openssl_csr_info: openssl_csr_info:
@ -94,8 +82,4 @@
- result.authority_key_identifier == "44:55:66:77" - result.authority_key_identifier == "44:55:66:77"
- result.authority_cert_issuer is none - result.authority_cert_issuer is none
- result.authority_cert_serial_number is none - result.authority_cert_serial_number is none
when: select_crypto_backend != 'pyopenssl' and cryptography_version.stdout is version('1.3', '>=') when: cryptography_version.stdout is version('1.3', '>=')
- name: "({{ select_crypto_backend }}) Update result list"
set_fact:
info_results: "{{ info_results + [result] }}"

View File

@ -119,50 +119,8 @@
useCommonNameForSAN: no useCommonNameForSAN: no
authority_key_identifier: '{{ "44:55:66:77" if cryptography_version.stdout is version("1.3", ">=") else omit }}' authority_key_identifier: '{{ "44:55:66:77" if cryptography_version.stdout is version("1.3", ">=") else omit }}'
- name: Prepare result list
set_fact:
info_results: []
- name: Running tests with pyOpenSSL backend
include_tasks: impl.yml
vars:
select_crypto_backend: pyopenssl
when: pyopenssl_version.stdout is version('0.15', '>=')
- name: Prepare result list
set_fact:
pyopenssl_info_results: "{{ info_results }}"
info_results: []
- name: Running tests with cryptography backend - name: Running tests with cryptography backend
include_tasks: impl.yml include_tasks: impl.yml
vars: vars:
select_crypto_backend: cryptography select_crypto_backend: cryptography
when: cryptography_version.stdout is version('1.3', '>=') when: cryptography_version.stdout is version('1.3', '>=')
- name: Prepare result list
set_fact:
cryptography_info_results: "{{ info_results }}"
- block:
- name: Dump pyOpenSSL results
debug:
var: pyopenssl_info_results
- name: Dump cryptography results
debug:
var: cryptography_info_results
- name: Compare results
assert:
that:
- ' (item.0 | dict2items | rejectattr("key", "in", keys_to_ignore) | list | items2dict)
== (item.1 | dict2items | rejectattr("key", "in", keys_to_ignore) | list | items2dict)'
quiet: yes
loop: "{{ pyopenssl_info_results | zip(cryptography_info_results) | list }}"
when: pyopenssl_version.stdout is version('0.15', '>=') and cryptography_version.stdout is version('1.3', '>=')
vars:
keys_to_ignore:
- deprecations
- subject_key_identifier
- authority_key_identifier
- authority_cert_issuer
- authority_cert_serial_number

View File

@ -1,4 +1,3 @@
dependencies: dependencies:
- setup_openssl - setup_openssl
- setup_pyopenssl
- setup_remote_tmp_dir - setup_remote_tmp_dir

View File

@ -14,24 +14,6 @@
subject: subject:
commonName: www.ansible.com commonName: www.ansible.com
- block:
- 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: "{{ remote_tmp_dir }}"
state: absent
- name: Re-create output directory
file:
path: "{{ remote_tmp_dir }}"
state: directory
- block: - block:
- name: Running tests with cryptography backend - name: Running tests with cryptography backend
include_tasks: impl.yml include_tasks: impl.yml

View File

@ -1,4 +1,3 @@
dependencies: dependencies:
- setup_openssl - setup_openssl
- setup_pyopenssl
- setup_remote_tmp_dir - setup_remote_tmp_dir

View File

@ -67,7 +67,7 @@
openssl_privatekey: openssl_privatekey:
path: '{{ remote_tmp_dir }}/privatekey5.pem' path: '{{ remote_tmp_dir }}/privatekey5.pem'
passphrase: ansible passphrase: ansible
cipher: "{{ 'aes256' if select_crypto_backend == 'pyopenssl' else 'auto' }}" cipher: auto
size: '{{ default_rsa_key_size }}' size: '{{ default_rsa_key_size }}'
select_crypto_backend: '{{ select_crypto_backend }}' select_crypto_backend: '{{ select_crypto_backend }}'
@ -75,7 +75,7 @@
openssl_privatekey: openssl_privatekey:
path: '{{ remote_tmp_dir }}/privatekey5.pem' path: '{{ remote_tmp_dir }}/privatekey5.pem'
passphrase: ansible passphrase: ansible
cipher: "{{ 'aes256' if select_crypto_backend == 'pyopenssl' else 'auto' }}" cipher: auto
size: '{{ default_rsa_key_size }}' size: '{{ default_rsa_key_size }}'
select_crypto_backend: '{{ select_crypto_backend }}' select_crypto_backend: '{{ select_crypto_backend }}'
register: privatekey5_idempotence register: privatekey5_idempotence
@ -84,13 +84,10 @@
openssl_privatekey: openssl_privatekey:
path: '{{ remote_tmp_dir }}/privatekey6.pem' path: '{{ remote_tmp_dir }}/privatekey6.pem'
passphrase: ànsïblé passphrase: ànsïblé
cipher: "{{ 'aes256' if select_crypto_backend == 'pyopenssl' else 'auto' }}" cipher: auto
size: '{{ default_rsa_key_size }}' size: '{{ default_rsa_key_size }}'
select_crypto_backend: '{{ select_crypto_backend }}' select_crypto_backend: '{{ select_crypto_backend }}'
- set_fact:
ecc_types: []
when: select_crypto_backend == 'pyopenssl'
- set_fact: - set_fact:
ecc_types: ecc_types:
- curve: secp384r1 - curve: secp384r1
@ -150,7 +147,6 @@
- curve: sect163r2 - curve: sect163r2
openssl_name: sect163r2 openssl_name: sect163r2
min_cryptography_version: "0.5" min_cryptography_version: "0.5"
when: select_crypto_backend == 'cryptography'
- name: "({{ select_crypto_backend }}) Test ECC key generation" - name: "({{ select_crypto_backend }}) Test ECC key generation"
openssl_privatekey: openssl_privatekey:
@ -221,7 +217,7 @@
openssl_privatekey: openssl_privatekey:
path: '{{ remote_tmp_dir }}/privatekeypw.pem' path: '{{ remote_tmp_dir }}/privatekeypw.pem'
passphrase: hunter2 passphrase: hunter2
cipher: "{{ 'aes256' if select_crypto_backend == 'pyopenssl' else 'auto' }}" cipher: auto
size: '{{ default_rsa_key_size }}' size: '{{ default_rsa_key_size }}'
select_crypto_backend: '{{ select_crypto_backend }}' select_crypto_backend: '{{ select_crypto_backend }}'
backup: yes backup: yes
@ -231,7 +227,7 @@
openssl_privatekey: openssl_privatekey:
path: '{{ remote_tmp_dir }}/privatekeypw.pem' path: '{{ remote_tmp_dir }}/privatekeypw.pem'
passphrase: hunter2 passphrase: hunter2
cipher: "{{ 'aes256' if select_crypto_backend == 'pyopenssl' else 'auto' }}" cipher: auto
size: '{{ default_rsa_key_size }}' size: '{{ default_rsa_key_size }}'
select_crypto_backend: '{{ select_crypto_backend }}' select_crypto_backend: '{{ select_crypto_backend }}'
backup: yes backup: yes
@ -257,7 +253,7 @@
openssl_privatekey: openssl_privatekey:
path: '{{ remote_tmp_dir }}/privatekeypw.pem' path: '{{ remote_tmp_dir }}/privatekeypw.pem'
passphrase: hunter2 passphrase: hunter2
cipher: "{{ 'aes256' if select_crypto_backend == 'pyopenssl' else 'auto' }}" cipher: auto
size: '{{ default_rsa_key_size }}' size: '{{ default_rsa_key_size }}'
select_crypto_backend: '{{ select_crypto_backend }}' select_crypto_backend: '{{ select_crypto_backend }}'
backup: yes backup: yes
@ -278,7 +274,7 @@
openssl_privatekey: openssl_privatekey:
path: '{{ remote_tmp_dir }}/privatekeypw.pem' path: '{{ remote_tmp_dir }}/privatekeypw.pem'
passphrase: hunter2 passphrase: hunter2
cipher: "{{ 'aes256' if select_crypto_backend == 'pyopenssl' else 'auto' }}" cipher: auto
size: '{{ default_rsa_key_size }}' size: '{{ default_rsa_key_size }}'
select_crypto_backend: '{{ select_crypto_backend }}' select_crypto_backend: '{{ select_crypto_backend }}'
backup: yes backup: yes
@ -289,7 +285,7 @@
openssl_privatekey: openssl_privatekey:
path: '{{ remote_tmp_dir }}/privatekeypw.pem' path: '{{ remote_tmp_dir }}/privatekeypw.pem'
passphrase: hunter2 passphrase: hunter2
cipher: "{{ 'aes256' if select_crypto_backend == 'pyopenssl' else 'auto' }}" cipher: auto
size: '{{ default_rsa_key_size }}' size: '{{ default_rsa_key_size }}'
select_crypto_backend: '{{ select_crypto_backend }}' select_crypto_backend: '{{ select_crypto_backend }}'
backup: yes backup: yes
@ -551,7 +547,7 @@
type: RSA type: RSA
size: '{{ default_rsa_key_size }}' size: '{{ default_rsa_key_size }}'
passphrase: hunter2 passphrase: hunter2
cipher: "{{ 'aes256' if select_crypto_backend == 'pyopenssl' else 'auto' }}" cipher: auto
select_crypto_backend: '{{ select_crypto_backend }}' select_crypto_backend: '{{ select_crypto_backend }}'
loop: "{{ regenerate_values }}" loop: "{{ regenerate_values }}"
- name: "({{ select_crypto_backend }}) Regenerate - setup broken keys" - name: "({{ select_crypto_backend }}) Regenerate - setup broken keys"

View File

@ -36,29 +36,6 @@
path: '{{ remote_tmp_dir }}/privatekey_backend_selection.pem' path: '{{ remote_tmp_dir }}/privatekey_backend_selection.pem'
size: '{{ default_rsa_key_size }}' size: '{{ default_rsa_key_size }}'
- block:
- name: Running tests with pyOpenSSL backend
include_tasks: impl.yml
vars:
select_crypto_backend: pyopenssl
- import_tasks: ../tests/validate.yml
vars:
select_crypto_backend: pyopenssl
# FIXME: minimal pyOpenSSL version?!
when: pyopenssl_version.stdout is version('0.6', '>=')
- name: Remove output directory
file:
path: "{{ remote_tmp_dir }}"
state: absent
- name: Re-create output directory
file:
path: "{{ remote_tmp_dir }}"
state: directory
- block: - block:
- name: Running tests with cryptography backend - name: Running tests with cryptography backend
include_tasks: impl.yml include_tasks: impl.yml
@ -70,45 +47,3 @@
select_crypto_backend: cryptography select_crypto_backend: cryptography
when: cryptography_version.stdout is version('0.5', '>=') when: cryptography_version.stdout is version('0.5', '>=')
- name: Check that fingerprints do not depend on the backend
block:
- name: "Fingerprint comparison: pyOpenSSL"
openssl_privatekey:
path: '{{ remote_tmp_dir }}/fingerprint-{{ item }}.pem'
type: "{{ item }}"
size: '{{ default_rsa_key_size }}'
select_crypto_backend: pyopenssl
loop:
- RSA
- DSA
register: fingerprint_pyopenssl
- name: "Fingerprint comparison: cryptography"
openssl_privatekey:
path: '{{ remote_tmp_dir }}/fingerprint-{{ item }}.pem'
type: "{{ item }}"
size: '{{ default_rsa_key_size }}'
select_crypto_backend: cryptography
loop:
- RSA
- DSA
register: fingerprint_cryptography
- name: Verify that keys were not regenerated
assert:
that:
- fingerprint_cryptography is not changed
- name: Verify that fingerprints match
assert:
that: item.0.fingerprint[item.2] == item.1.fingerprint[item.2]
when: item.0 is not skipped and item.1 is not skipped
loop: |
{{ query('nested',
fingerprint_pyopenssl.results | zip(fingerprint_cryptography.results),
fingerprint_pyopenssl.results[0].fingerprint.keys()
) if fingerprint_pyopenssl.results[0].fingerprint else [] }}
loop_control:
label: "{{ [item.0.item, item.2] }}"
when: pyopenssl_version.stdout is version('0.6', '>=') and cryptography_version.stdout is version('0.5', '>=')

View File

@ -1,5 +1,4 @@
dependencies: dependencies:
- setup_openssl - setup_openssl
- setup_pyopenssl
- setup_remote_tmp_dir - setup_remote_tmp_dir
- prepare_jinja2_compat - prepare_jinja2_compat

View File

@ -20,10 +20,6 @@
- "result.public_data.exponent > 5" - "result.public_data.exponent > 5"
- "'private_data' not in result" - "'private_data' not in result"
- name: Update result list
set_fact:
info_results: "{{ info_results | combine({'key1': result}) }}"
- name: ({{select_crypto_backend}}) Read private key - name: ({{select_crypto_backend}}) Read private key
slurp: slurp:
src: '{{ remote_tmp_dir }}/privatekey_1.pem' src: '{{ remote_tmp_dir }}/privatekey_1.pem'
@ -62,10 +58,6 @@
- "result.public_data.modulus == result.private_data.p * result.private_data.q" - "result.public_data.modulus == result.private_data.p * result.private_data.q"
- "result.private_data.exponent > 5" - "result.private_data.exponent > 5"
- name: Update result list
set_fact:
info_results: "{{ info_results | combine({'key2': result}) }}"
- name: ({{select_crypto_backend}}) Get key 3 info (without passphrase) - name: ({{select_crypto_backend}}) Get key 3 info (without passphrase)
openssl_privatekey_info: openssl_privatekey_info:
path: '{{ remote_tmp_dir }}/privatekey_3.pem' path: '{{ remote_tmp_dir }}/privatekey_3.pem'
@ -113,10 +105,6 @@
- "result.public_data.modulus == result.private_data.p * result.private_data.q" - "result.public_data.modulus == result.private_data.p * result.private_data.q"
- "result.private_data.exponent > 5" - "result.private_data.exponent > 5"
- name: Update result list
set_fact:
info_results: "{{ info_results | combine({'key3': result}) }}"
- name: ({{select_crypto_backend}}) Get key 4 info - name: ({{select_crypto_backend}}) Get key 4 info
openssl_privatekey_info: openssl_privatekey_info:
path: '{{ remote_tmp_dir }}/privatekey_4.pem' path: '{{ remote_tmp_dir }}/privatekey_4.pem'
@ -124,37 +112,20 @@
select_crypto_backend: '{{ select_crypto_backend }}' select_crypto_backend: '{{ select_crypto_backend }}'
register: result register: result
- block:
- name: Check that ECC key info is ok
assert:
that:
- "'public_key' in result"
- "'public_key_fingerprints' in result"
- "'type' in result"
- "result.type == 'ECC'"
- "'public_data' in result"
- "result.public_data.curve is string"
- "result.public_data.x != 0"
- "result.public_data.y != 0"
- "result.public_data.exponent_size == (521 if (ansible_distribution == 'CentOS' and ansible_distribution_major_version == '6') else 256)"
- "'private_data' in result"
- "result.private_data.multiplier > 1024"
- name: Update result list
set_fact:
info_results: "{{ info_results | combine({'key4': result}) }}"
when: select_crypto_backend != 'pyopenssl' or (pyopenssl_version.stdout is version('16.1.0', '>=') and cryptography_version.stdout is version('0.0', '>'))
- name: Check that ECC key info is ok - name: Check that ECC key info is ok
assert: assert:
that: that:
- "'public_key' in result" - "'public_key' in result"
- "'public_key_fingerprints' in result" - "'public_key_fingerprints' in result"
- "'type' in result" - "'type' in result"
- "result.type.startswith('unknown ')" - "result.type == 'ECC'"
- "'public_data' in result" - "'public_data' in result"
- "result.public_data.curve is string"
- "result.public_data.x != 0"
- "result.public_data.y != 0"
- "result.public_data.exponent_size == (521 if (ansible_distribution == 'CentOS' and ansible_distribution_major_version == '6') else 256)"
- "'private_data' in result" - "'private_data' in result"
when: select_crypto_backend == 'pyopenssl' and not (pyopenssl_version.stdout is version('16.1.0', '>=') and cryptography_version.stdout is version('0.0', '>')) - "result.private_data.multiplier > 1024"
- name: ({{select_crypto_backend}}) Get key 5 info - name: ({{select_crypto_backend}}) Get key 5 info
openssl_privatekey_info: openssl_privatekey_info:
@ -177,7 +148,3 @@
- "result.public_data.y > 2" - "result.public_data.y > 2"
- "'private_data' in result" - "'private_data' in result"
- "result.private_data.x > 2" - "result.private_data.x > 2"
- name: Update result list
set_fact:
info_results: "{{ info_results | combine({'key5': result}) }}"

View File

@ -36,42 +36,8 @@
type: DSA type: DSA
size: 1024 size: 1024
- name: Prepare result list
set_fact:
info_results: {}
- name: Running tests with pyOpenSSL backend
include_tasks: impl.yml
vars:
select_crypto_backend: pyopenssl
when: pyopenssl_version.stdout is version('0.15', '>=')
- name: Prepare result list
set_fact:
pyopenssl_info_results: "{{ info_results }}"
info_results: {}
- name: Running tests with cryptography backend - name: Running tests with cryptography backend
include_tasks: impl.yml include_tasks: impl.yml
vars: vars:
select_crypto_backend: cryptography select_crypto_backend: cryptography
when: cryptography_version.stdout is version('1.2.3', '>=') when: cryptography_version.stdout is version('1.2.3', '>=')
- name: Prepare result list
set_fact:
cryptography_info_results: "{{ info_results }}"
- block:
- name: Dump pyOpenSSL results
debug:
var: pyopenssl_info_results
- name: Dump cryptography results
debug:
var: cryptography_info_results
- name: Compare results
assert:
that:
- ' (pyopenssl_info_results[item] | dict2items | rejectattr("key", "equalto", "deprecations") | list | items2dict)
== (cryptography_info_results[item] | dict2items | rejectattr("key", "equalto", "deprecations") | list | items2dict)'
loop: "{{ pyopenssl_info_results.keys() | intersect(cryptography_info_results.keys()) | list }}"
when: pyopenssl_version.stdout is version('0.15', '>=') and cryptography_version.stdout is version('1.2.3', '>=')

View File

@ -1,4 +1,3 @@
dependencies: dependencies:
- setup_openssl - setup_openssl
- setup_pyopenssl
- setup_remote_tmp_dir - setup_remote_tmp_dir

View File

@ -8,25 +8,6 @@
openssl_privatekey_pipe: openssl_privatekey_pipe:
size: '{{ default_rsa_key_size }}' size: '{{ default_rsa_key_size }}'
- block:
- name: Running tests with pyOpenSSL backend
include_tasks: impl.yml
vars:
select_crypto_backend: pyopenssl
# FIXME: minimal pyOpenSSL version?!
when: pyopenssl_version.stdout is version('0.6', '>=')
- name: Remove output directory
file:
path: "{{ remote_tmp_dir }}"
state: absent
- name: Re-create output directory
file:
path: "{{ remote_tmp_dir }}"
state: directory
- block: - block:
- name: Running tests with cryptography backend - name: Running tests with cryptography backend
include_tasks: impl.yml include_tasks: impl.yml

View File

@ -1,4 +1,3 @@
dependencies: dependencies:
- setup_openssl - setup_openssl
- setup_pyopenssl
- setup_remote_tmp_dir - setup_remote_tmp_dir

View File

@ -90,7 +90,7 @@
openssl_privatekey: openssl_privatekey:
path: '{{ remote_tmp_dir }}/privatekey3.pem' path: '{{ remote_tmp_dir }}/privatekey3.pem'
passphrase: ansible passphrase: ansible
cipher: aes256 cipher: auto
size: '{{ default_rsa_key_size }}' size: '{{ default_rsa_key_size }}'
- name: "({{ select_crypto_backend }}) Generate publickey3 - with passphrase protected privatekey" - name: "({{ select_crypto_backend }}) Generate publickey3 - with passphrase protected privatekey"

View File

@ -15,33 +15,6 @@
path: '{{ remote_tmp_dir }}/privatekey_autodetect_public.pem' path: '{{ remote_tmp_dir }}/privatekey_autodetect_public.pem'
privatekey_path: '{{ remote_tmp_dir }}/privatekey_autodetect.pem' privatekey_path: '{{ remote_tmp_dir }}/privatekey_autodetect.pem'
when: |
pyopenssl_version.stdout is version('16.0.0', '>=') or
cryptography_version.stdout is version('1.2.3', '>=')
- block:
- name: Running tests with pyOpenSSL backend
include_tasks: impl.yml
vars:
select_crypto_backend: pyopenssl
- import_tasks: ../tests/validate.yml
vars:
select_crypto_backend: pyopenssl
when: pyopenssl_version.stdout is version('16.0.0', '>=')
- name: Remove output directory
file:
path: "{{ remote_tmp_dir }}"
state: absent
- name: Re-create output directory
file:
path: "{{ remote_tmp_dir }}"
state: directory
- block:
- name: Running tests with cryptography backend - name: Running tests with cryptography backend
include_tasks: impl.yml include_tasks: impl.yml
vars: vars:

View File

@ -1,5 +1,4 @@
dependencies: dependencies:
- setup_openssl - setup_openssl
- setup_pyopenssl
- setup_remote_tmp_dir - setup_remote_tmp_dir
- prepare_jinja2_compat - prepare_jinja2_compat

View File

@ -18,10 +18,6 @@
- "2 ** (result.public_data.size - 1) < result.public_data.modulus < 2 ** result.public_data.size" - "2 ** (result.public_data.size - 1) < result.public_data.modulus < 2 ** result.public_data.size"
- "result.public_data.exponent > 5" - "result.public_data.exponent > 5"
- name: Update result list
set_fact:
info_results: "{{ info_results | combine({'key1': result}) }}"
- name: ({{select_crypto_backend}}) Read file - name: ({{select_crypto_backend}}) Read file
slurp: slurp:
src: '{{ remote_tmp_dir }}/publickey_1.pem' src: '{{ remote_tmp_dir }}/publickey_1.pem'
@ -55,42 +51,23 @@
- "2 ** (result.public_data.size - 1) < result.public_data.modulus < 2 ** result.public_data.size" - "2 ** (result.public_data.size - 1) < result.public_data.modulus < 2 ** result.public_data.size"
- "result.public_data.exponent > 5" - "result.public_data.exponent > 5"
- name: Update result list
set_fact:
info_results: "{{ info_results | combine({'key2': result}) }}"
- name: ({{select_crypto_backend}}) Get key 3 info - name: ({{select_crypto_backend}}) Get key 3 info
openssl_publickey_info: openssl_publickey_info:
path: '{{ remote_tmp_dir }}/publickey_3.pem' path: '{{ remote_tmp_dir }}/publickey_3.pem'
select_crypto_backend: '{{ select_crypto_backend }}' select_crypto_backend: '{{ select_crypto_backend }}'
register: result register: result
- block:
- name: Check that ECC key info is ok
assert:
that:
- "'fingerprints' in result"
- "'type' in result"
- "result.type == 'ECC'"
- "'public_data' in result"
- "result.public_data.curve is string"
- "result.public_data.x != 0"
- "result.public_data.y != 0"
- "result.public_data.exponent_size == (521 if (ansible_distribution == 'CentOS' and ansible_distribution_major_version == '6') else 256)"
- name: Update result list
set_fact:
info_results: "{{ info_results | combine({'key3': result}) }}"
when: select_crypto_backend != 'pyopenssl' or (pyopenssl_version.stdout is version('16.1.0', '>=') and cryptography_version.stdout is version('0.0', '>'))
- name: Check that ECC key info is ok - name: Check that ECC key info is ok
assert: assert:
that: that:
- "'fingerprints' in result" - "'fingerprints' in result"
- "'type' in result" - "'type' in result"
- "result.type.startswith('unknown ')" - "result.type == 'ECC'"
- "'public_data' in result" - "'public_data' in result"
when: select_crypto_backend == 'pyopenssl' and not (pyopenssl_version.stdout is version('16.1.0', '>=') and cryptography_version.stdout is version('0.0', '>')) - "result.public_data.curve is string"
- "result.public_data.x != 0"
- "result.public_data.y != 0"
- "result.public_data.exponent_size == (521 if (ansible_distribution == 'CentOS' and ansible_distribution_major_version == '6') else 256)"
- name: ({{select_crypto_backend}}) Get key 4 info - name: ({{select_crypto_backend}}) Get key 4 info
openssl_publickey_info: openssl_publickey_info:
@ -109,7 +86,3 @@
- "result.public_data.q > 2" - "result.public_data.q > 2"
- "result.public_data.g >= 2" - "result.public_data.g >= 2"
- "result.public_data.y > 2" - "result.public_data.y > 2"
- name: Update result list
set_fact:
info_results: "{{ info_results | combine({'key4': result}) }}"

View File

@ -42,38 +42,8 @@
set_fact: set_fact:
info_results: {} info_results: {}
- name: Running tests with pyOpenSSL backend
include_tasks: impl.yml
vars:
select_crypto_backend: pyopenssl
when: pyopenssl_version.stdout is version('16.0.0', '>=')
- name: Prepare result list
set_fact:
pyopenssl_info_results: "{{ info_results }}"
info_results: {}
- name: Running tests with cryptography backend - name: Running tests with cryptography backend
include_tasks: impl.yml include_tasks: impl.yml
vars: vars:
select_crypto_backend: cryptography select_crypto_backend: cryptography
when: cryptography_version.stdout is version('1.2.3', '>=') when: cryptography_version.stdout is version('1.2.3', '>=')
- name: Prepare result list
set_fact:
cryptography_info_results: "{{ info_results }}"
- block:
- name: Dump pyOpenSSL results
debug:
var: pyopenssl_info_results
- name: Dump cryptography results
debug:
var: cryptography_info_results
- name: Compare results
assert:
that:
- ' (pyopenssl_info_results[item] | dict2items | rejectattr("key", "equalto", "deprecations") | list | items2dict)
== (cryptography_info_results[item] | dict2items | rejectattr("key", "equalto", "deprecations") | list | items2dict)'
loop: "{{ pyopenssl_info_results.keys() | intersect(cryptography_info_results.keys()) | list }}"
when: pyopenssl_version.stdout is version('16.0.0', '>=') and cryptography_version.stdout is version('1.2.3', '>=')

View File

@ -1,4 +1,3 @@
dependencies: dependencies:
- setup_openssl - setup_openssl
- setup_pyopenssl
- setup_remote_tmp_dir - setup_remote_tmp_dir

View File

@ -5,7 +5,7 @@
#################################################################### ####################################################################
# Test matrix: # Test matrix:
# * pyopenssl or cryptography # * cryptography
# * DSA or ECC or ... # * DSA or ECC or ...
# * password protected private key or not # * password protected private key or not
@ -25,11 +25,6 @@
backends: "{{ backends + [ { 'backend': 'cryptography' } ] }}" backends: "{{ backends + [ { 'backend': 'cryptography' } ] }}"
when: cryptography_version.stdout is version('1.4', '>=') when: cryptography_version.stdout is version('1.4', '>=')
- name: Add pyopenssl backend
set_fact:
backends: "{{ backends + [ { 'backend': 'pyopenssl' } ] }}"
when: pyopenssl_version.stdout is version('0.11', '>=')
- name: Add RSA tests - name: Add RSA tests
set_fact: set_fact:
key_types: "{{ key_types + [ { 'type': 'RSA', 'size': default_rsa_key_size } ] }}" key_types: "{{ key_types + [ { 'type': 'RSA', 'size': default_rsa_key_size } ] }}"
@ -58,14 +53,11 @@
all_tests: >- all_tests: >-
[ [
{% for b in backends %} {% for b in backends %}
{% for kt in key_types %} {% for kt in key_types %}
{% for kp in key_password %} {% for kp in key_password %}
{# Exclude Ed25519 and Ed448 tests on pyopenssl #} {{ b | combine (kt) | combine(kp) }},
{% if not (b.backend == 'pyopenssl' and (kt.type == 'Ed25519' or kt.type == 'Ed448')) %} {% endfor %}
{{ b | combine (kt) | combine(kp) }}, {% endfor %}
{% endif %}
{% endfor %}
{% endfor %}
{% endfor %} {% endfor %}
] ]

View File

@ -1,4 +1,3 @@
dependencies: dependencies:
- setup_openssl - setup_openssl
- setup_pyopenssl
- setup_remote_tmp_dir - setup_remote_tmp_dir

View File

@ -12,20 +12,8 @@
commonName: www.example.com commonName: www.example.com
- name: (Expired, {{select_crypto_backend}}) Generate expired selfsigned certificate - name: (Expired, {{select_crypto_backend}}) Generate expired selfsigned certificate
x509_certificate: # Cryptography won't allow creating expired certificates; so we create it with 'command'
path: '{{ remote_tmp_dir }}/has_expired_cert.pem'
csr_path: '{{ remote_tmp_dir }}/has_expired_csr.csr'
privatekey_path: '{{ remote_tmp_dir }}/has_expired_privatekey.pem'
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: (Expired, {{select_crypto_backend}}) Generate expired selfsigned certificate
command: "{{ openssl_binary }} x509 -req -days -1 -in {{ remote_tmp_dir }}/has_expired_csr.csr -signkey {{ remote_tmp_dir }}/has_expired_privatekey.pem -out {{ remote_tmp_dir }}/has_expired_cert.pem" command: "{{ openssl_binary }} x509 -req -days -1 -in {{ remote_tmp_dir }}/has_expired_csr.csr -signkey {{ remote_tmp_dir }}/has_expired_privatekey.pem -out {{ remote_tmp_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)" - name: "(Expired) Check task fails because cert is expired (has_expired: false)"
x509_certificate: x509_certificate:

View File

@ -4,22 +4,6 @@
# and should not be used as examples of how to write Ansible roles # # and should not be used as examples of how to write Ansible roles #
#################################################################### ####################################################################
- 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: "{{ remote_tmp_dir }}"
state: absent
- name: Re-create output directory
file:
path: "{{ remote_tmp_dir }}"
state: directory
- name: Running tests with cryptography backend - name: Running tests with cryptography backend
include_tasks: impl.yml include_tasks: impl.yml
vars: vars:

View File

@ -340,7 +340,6 @@
ownca_digest: sha256 ownca_digest: sha256
ownca_create_subject_key_identifier: always_create ownca_create_subject_key_identifier: always_create
select_crypto_backend: '{{ select_crypto_backend }}' select_crypto_backend: '{{ select_crypto_backend }}'
when: select_crypto_backend != 'pyopenssl'
register: ownca_subject_key_identifier_1 register: ownca_subject_key_identifier_1
- name: (OwnCA, {{select_crypto_backend}}) Create subject key identifier (idempotency) - name: (OwnCA, {{select_crypto_backend}}) Create subject key identifier (idempotency)
@ -353,7 +352,6 @@
ownca_digest: sha256 ownca_digest: sha256
ownca_create_subject_key_identifier: always_create ownca_create_subject_key_identifier: always_create
select_crypto_backend: '{{ select_crypto_backend }}' select_crypto_backend: '{{ select_crypto_backend }}'
when: select_crypto_backend != 'pyopenssl'
register: ownca_subject_key_identifier_2 register: ownca_subject_key_identifier_2
- name: (OwnCA, {{select_crypto_backend}}) Create subject key identifier (remove) - name: (OwnCA, {{select_crypto_backend}}) Create subject key identifier (remove)
@ -366,7 +364,6 @@
ownca_digest: sha256 ownca_digest: sha256
ownca_create_subject_key_identifier: never_create ownca_create_subject_key_identifier: never_create
select_crypto_backend: '{{ select_crypto_backend }}' select_crypto_backend: '{{ select_crypto_backend }}'
when: select_crypto_backend != 'pyopenssl'
register: ownca_subject_key_identifier_3 register: ownca_subject_key_identifier_3
- name: (OwnCA, {{select_crypto_backend}}) Create subject key identifier (remove idempotency) - name: (OwnCA, {{select_crypto_backend}}) Create subject key identifier (remove idempotency)
@ -379,7 +376,6 @@
ownca_digest: sha256 ownca_digest: sha256
ownca_create_subject_key_identifier: never_create ownca_create_subject_key_identifier: never_create
select_crypto_backend: '{{ select_crypto_backend }}' select_crypto_backend: '{{ select_crypto_backend }}'
when: select_crypto_backend != 'pyopenssl'
register: ownca_subject_key_identifier_4 register: ownca_subject_key_identifier_4
- name: (OwnCA, {{select_crypto_backend}}) Create subject key identifier (re-enable) - name: (OwnCA, {{select_crypto_backend}}) Create subject key identifier (re-enable)
@ -392,7 +388,6 @@
ownca_digest: sha256 ownca_digest: sha256
ownca_create_subject_key_identifier: always_create ownca_create_subject_key_identifier: always_create
select_crypto_backend: '{{ select_crypto_backend }}' select_crypto_backend: '{{ select_crypto_backend }}'
when: select_crypto_backend != 'pyopenssl'
register: ownca_subject_key_identifier_5 register: ownca_subject_key_identifier_5
- name: (OwnCA, {{select_crypto_backend}}) Create authority key identifier - name: (OwnCA, {{select_crypto_backend}}) Create authority key identifier
@ -405,7 +400,6 @@
ownca_digest: sha256 ownca_digest: sha256
ownca_create_authority_key_identifier: yes ownca_create_authority_key_identifier: yes
select_crypto_backend: '{{ select_crypto_backend }}' select_crypto_backend: '{{ select_crypto_backend }}'
when: select_crypto_backend != 'pyopenssl'
register: ownca_authority_key_identifier_1 register: ownca_authority_key_identifier_1
- name: (OwnCA, {{select_crypto_backend}}) Create authority key identifier (idempotency) - name: (OwnCA, {{select_crypto_backend}}) Create authority key identifier (idempotency)
@ -418,7 +412,6 @@
ownca_digest: sha256 ownca_digest: sha256
ownca_create_authority_key_identifier: yes ownca_create_authority_key_identifier: yes
select_crypto_backend: '{{ select_crypto_backend }}' select_crypto_backend: '{{ select_crypto_backend }}'
when: select_crypto_backend != 'pyopenssl'
register: ownca_authority_key_identifier_2 register: ownca_authority_key_identifier_2
- name: (OwnCA, {{select_crypto_backend}}) Create authority key identifier (remove) - name: (OwnCA, {{select_crypto_backend}}) Create authority key identifier (remove)
@ -431,7 +424,6 @@
ownca_digest: sha256 ownca_digest: sha256
ownca_create_authority_key_identifier: no ownca_create_authority_key_identifier: no
select_crypto_backend: '{{ select_crypto_backend }}' select_crypto_backend: '{{ select_crypto_backend }}'
when: select_crypto_backend != 'pyopenssl'
register: ownca_authority_key_identifier_3 register: ownca_authority_key_identifier_3
- name: (OwnCA, {{select_crypto_backend}}) Create authority key identifier (remove idempotency) - name: (OwnCA, {{select_crypto_backend}}) Create authority key identifier (remove idempotency)
@ -444,7 +436,6 @@
ownca_digest: sha256 ownca_digest: sha256
ownca_create_authority_key_identifier: no ownca_create_authority_key_identifier: no
select_crypto_backend: '{{ select_crypto_backend }}' select_crypto_backend: '{{ select_crypto_backend }}'
when: select_crypto_backend != 'pyopenssl'
register: ownca_authority_key_identifier_4 register: ownca_authority_key_identifier_4
- name: (OwnCA, {{select_crypto_backend}}) Create authority key identifier (re-add) - name: (OwnCA, {{select_crypto_backend}}) Create authority key identifier (re-add)
@ -457,7 +448,6 @@
ownca_digest: sha256 ownca_digest: sha256
ownca_create_authority_key_identifier: yes ownca_create_authority_key_identifier: yes
select_crypto_backend: '{{ select_crypto_backend }}' select_crypto_backend: '{{ select_crypto_backend }}'
when: select_crypto_backend != 'pyopenssl'
register: ownca_authority_key_identifier_5 register: ownca_authority_key_identifier_5
- name: (OwnCA, {{select_crypto_backend}}) Ed25519 and Ed448 tests (for cryptography >= 2.6) - name: (OwnCA, {{select_crypto_backend}}) Ed25519 and Ed448 tests (for cryptography >= 2.6)

View File

@ -353,7 +353,6 @@
selfsigned_digest: sha256 selfsigned_digest: sha256
selfsigned_create_subject_key_identifier: always_create selfsigned_create_subject_key_identifier: always_create
select_crypto_backend: '{{ select_crypto_backend }}' select_crypto_backend: '{{ select_crypto_backend }}'
when: select_crypto_backend != 'pyopenssl'
register: selfsigned_subject_key_identifier_1 register: selfsigned_subject_key_identifier_1
- name: (Selfsigned, {{select_crypto_backend}}) Create subject key identifier test (idempotency) - name: (Selfsigned, {{select_crypto_backend}}) Create subject key identifier test (idempotency)
@ -365,7 +364,6 @@
selfsigned_digest: sha256 selfsigned_digest: sha256
selfsigned_create_subject_key_identifier: always_create selfsigned_create_subject_key_identifier: always_create
select_crypto_backend: '{{ select_crypto_backend }}' select_crypto_backend: '{{ select_crypto_backend }}'
when: select_crypto_backend != 'pyopenssl'
register: selfsigned_subject_key_identifier_2 register: selfsigned_subject_key_identifier_2
- name: (Selfsigned, {{select_crypto_backend}}) Create subject key identifier test (remove) - name: (Selfsigned, {{select_crypto_backend}}) Create subject key identifier test (remove)
@ -377,7 +375,6 @@
selfsigned_digest: sha256 selfsigned_digest: sha256
selfsigned_create_subject_key_identifier: never_create selfsigned_create_subject_key_identifier: never_create
select_crypto_backend: '{{ select_crypto_backend }}' select_crypto_backend: '{{ select_crypto_backend }}'
when: select_crypto_backend != 'pyopenssl'
register: selfsigned_subject_key_identifier_3 register: selfsigned_subject_key_identifier_3
- name: (Selfsigned, {{select_crypto_backend}}) Create subject key identifier test (remove idempotency) - name: (Selfsigned, {{select_crypto_backend}}) Create subject key identifier test (remove idempotency)
@ -389,7 +386,6 @@
selfsigned_digest: sha256 selfsigned_digest: sha256
selfsigned_create_subject_key_identifier: never_create selfsigned_create_subject_key_identifier: never_create
select_crypto_backend: '{{ select_crypto_backend }}' select_crypto_backend: '{{ select_crypto_backend }}'
when: select_crypto_backend != 'pyopenssl'
register: selfsigned_subject_key_identifier_4 register: selfsigned_subject_key_identifier_4
- name: (Selfsigned, {{select_crypto_backend}}) Create subject key identifier test (re-enable) - name: (Selfsigned, {{select_crypto_backend}}) Create subject key identifier test (re-enable)
@ -401,7 +397,6 @@
selfsigned_digest: sha256 selfsigned_digest: sha256
selfsigned_create_subject_key_identifier: always_create selfsigned_create_subject_key_identifier: always_create
select_crypto_backend: '{{ select_crypto_backend }}' select_crypto_backend: '{{ select_crypto_backend }}'
when: select_crypto_backend != 'pyopenssl'
register: selfsigned_subject_key_identifier_5 register: selfsigned_subject_key_identifier_5
- name: (Selfsigned, {{select_crypto_backend}}) Ed25519 and Ed448 tests (for cryptography >= 2.6) - name: (Selfsigned, {{select_crypto_backend}}) Ed25519 and Ed448 tests (for cryptography >= 2.6)

View File

@ -140,7 +140,6 @@
- ownca_subject_key_identifier_3 is changed - ownca_subject_key_identifier_3 is changed
- ownca_subject_key_identifier_4 is not changed - ownca_subject_key_identifier_4 is not changed
- ownca_subject_key_identifier_5 is changed - ownca_subject_key_identifier_5 is changed
when: select_crypto_backend != 'pyopenssl'
- name: (OwnCA validation, {{select_crypto_backend}}) Check create authority key identifier - name: (OwnCA validation, {{select_crypto_backend}}) Check create authority key identifier
assert: assert:
@ -150,7 +149,6 @@
- ownca_authority_key_identifier_3 is changed - ownca_authority_key_identifier_3 is changed
- ownca_authority_key_identifier_4 is not changed - ownca_authority_key_identifier_4 is not changed
- ownca_authority_key_identifier_5 is changed - ownca_authority_key_identifier_5 is changed
when: select_crypto_backend != 'pyopenssl'
- name: (OwnCA validation, {{select_crypto_backend}}) Verify Ed25519 and Ed448 tests (for cryptography >= 2.6, < 2.8) - name: (OwnCA validation, {{select_crypto_backend}}) Verify Ed25519 and Ed448 tests (for cryptography >= 2.6, < 2.8)
assert: assert:

View File

@ -185,7 +185,6 @@
- selfsigned_subject_key_identifier_3 is changed - selfsigned_subject_key_identifier_3 is changed
- selfsigned_subject_key_identifier_4 is not changed - selfsigned_subject_key_identifier_4 is not changed
- selfsigned_subject_key_identifier_5 is changed - selfsigned_subject_key_identifier_5 is changed
when: select_crypto_backend != 'pyopenssl'
- name: (Selfsigned validation, {{select_crypto_backend}}) Verify Ed25519 and Ed448 tests (for cryptography >= 2.6, < 2.8) - name: (Selfsigned validation, {{select_crypto_backend}}) Verify Ed25519 and Ed448 tests (for cryptography >= 2.6, < 2.8)
assert: assert:

View File

@ -1,5 +1,4 @@
dependencies: dependencies:
- setup_openssl - setup_openssl
- setup_pyopenssl
- setup_remote_tmp_dir - setup_remote_tmp_dir
- prepare_jinja2_compat - prepare_jinja2_compat

View File

@ -31,11 +31,7 @@
expected_authority_cert_issuer: expected_authority_cert_issuer:
- "DNS:ca.example.org" - "DNS:ca.example.org"
- "IP:1.2.3.4" - "IP:1.2.3.4"
when: select_crypto_backend != 'pyopenssl' and cryptography_version.stdout is version('1.3', '>=') when: cryptography_version.stdout is version('1.3', '>=')
- name: Update result list
set_fact:
info_results: "{{ info_results + [result] }}"
- name: ({{select_crypto_backend}}) Read file - name: ({{select_crypto_backend}}) Read file
slurp: slurp:
@ -68,10 +64,6 @@
- not result.valid_at.past - not result.valid_at.past
- not result.valid_at.twentydays - not result.valid_at.twentydays
- name: Update result list
set_fact:
info_results: "{{ info_results + [result] }}"
- name: ({{select_crypto_backend}}) Get certificate info - name: ({{select_crypto_backend}}) Get certificate info
x509_certificate_info: x509_certificate_info:
path: '{{ remote_tmp_dir }}/cert_3.pem' path: '{{ remote_tmp_dir }}/cert_3.pem'
@ -88,11 +80,7 @@
expected_authority_cert_issuer: expected_authority_cert_issuer:
- "DNS:ca.example.org" - "DNS:ca.example.org"
- "IP:1.2.3.4" - "IP:1.2.3.4"
when: select_crypto_backend != 'pyopenssl' and cryptography_version.stdout is version('1.3', '>=') when: cryptography_version.stdout is version('1.3', '>=')
- name: Update result list
set_fact:
info_results: "{{ info_results + [result] }}"
- name: ({{select_crypto_backend}}) Get certificate info - name: ({{select_crypto_backend}}) Get certificate info
x509_certificate_info: x509_certificate_info:
@ -106,11 +94,7 @@
- result.authority_key_identifier == "44:55:66:77" - result.authority_key_identifier == "44:55:66:77"
- result.authority_cert_issuer is none - result.authority_cert_issuer is none
- result.authority_cert_serial_number is none - result.authority_cert_serial_number is none
when: select_crypto_backend != 'pyopenssl' and cryptography_version.stdout is version('1.3', '>=') when: cryptography_version.stdout is version('1.3', '>=')
- name: Update result list
set_fact:
info_results: "{{ info_results + [result] }}"
- name: Copy packed cert 1 to remote - name: Copy packed cert 1 to remote
copy: copy:
@ -131,7 +115,3 @@
that: that:
- (result.fingerprints.sha256 == '57:7c:f1:f5:dd:cc:6e:e9:f3:17:28:73:17:e4:25:c7:69:74:3e:f7:9a:df:58:20:7a:5a:e4:aa:de:bf:24:5b' if result.fingerprints.sha256 is defined else true) - (result.fingerprints.sha256 == '57:7c:f1:f5:dd:cc:6e:e9:f3:17:28:73:17:e4:25:c7:69:74:3e:f7:9a:df:58:20:7a:5a:e4:aa:de:bf:24:5b' if result.fingerprints.sha256 is defined else true)
- (result.fingerprints.sha1 == 'b7:79:64:f4:2b:e0:ae:45:74:d4:f3:08:f6:53:cb:39:26:fa:52:6b' if result.fingerprints.sha1 is defined else true) - (result.fingerprints.sha1 == 'b7:79:64:f4:2b:e0:ae:45:74:d4:f3:08:f6:53:cb:39:26:fa:52:6b' if result.fingerprints.sha1 is defined else true)
- name: Update result list
set_fact:
info_results: "{{ info_results + [result] }}"

View File

@ -134,50 +134,8 @@
- 3 - 3
- 4 - 4
- name: Prepare result list
set_fact:
info_results: []
- name: Running tests with pyOpenSSL backend
include_tasks: impl.yml
vars:
select_crypto_backend: pyopenssl
when: pyopenssl_version.stdout is version('0.15', '>=')
- name: Prepare result list
set_fact:
pyopenssl_info_results: "{{ info_results }}"
info_results: []
- name: Running tests with cryptography backend - name: Running tests with cryptography backend
include_tasks: impl.yml include_tasks: impl.yml
vars: vars:
select_crypto_backend: cryptography select_crypto_backend: cryptography
when: cryptography_version.stdout is version('1.6', '>=') when: cryptography_version.stdout is version('1.6', '>=')
- name: Prepare result list
set_fact:
cryptography_info_results: "{{ info_results }}"
- block:
- name: Dump pyOpenSSL results
debug:
var: pyopenssl_info_results
- name: Dump cryptography results
debug:
var: cryptography_info_results
- name: Compare results
assert:
that:
- ' (item.0 | dict2items | rejectattr("key", "in", keys_to_ignore) | list | items2dict)
== (item.1 | dict2items | rejectattr("key", "in", keys_to_ignore) | list | items2dict)'
quiet: yes
loop: "{{ pyopenssl_info_results | zip(cryptography_info_results) | list }}"
when: pyopenssl_version.stdout is version('0.15', '>=') and cryptography_version.stdout is version('1.6', '>=')
vars:
keys_to_ignore:
- deprecations
- subject_key_identifier
- authority_key_identifier
- authority_cert_issuer
- authority_cert_serial_number

View File

@ -1,4 +1,3 @@
dependencies: dependencies:
- setup_openssl - setup_openssl
- setup_pyopenssl
- setup_remote_tmp_dir - setup_remote_tmp_dir

View File

@ -13,24 +13,6 @@
provider: selfsigned provider: selfsigned
privatekey_path: '{{ remote_tmp_dir }}/privatekey_backend_selection.pem' privatekey_path: '{{ remote_tmp_dir }}/privatekey_backend_selection.pem'
- block:
- 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: "{{ remote_tmp_dir }}"
state: absent
- name: Re-create output directory
file:
path: "{{ remote_tmp_dir }}"
state: directory
- block: - block:
- name: Running tests with cryptography backend - name: Running tests with cryptography backend
include_tasks: impl.yml include_tasks: impl.yml

View File

@ -1,4 +1,3 @@
dependencies: dependencies:
- setup_openssl - setup_openssl
- setup_pyopenssl # the x509_crl* modules don't need this, but the other modules using during the tests do in some situations
- setup_remote_tmp_dir - setup_remote_tmp_dir

View File

@ -1,7 +1,6 @@
bcrypt
cryptography cryptography
ipaddress ; python_version < '3.0' ipaddress ; python_version < '3.0'
pyopenssl
bcrypt
unittest2 ; python_version < '2.7' unittest2 ; python_version < '2.7'
importlib ; python_version < '2.7' importlib ; python_version < '2.7'