summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnsible Core Team <info@ansible.com>2020-03-09 09:40:30 +0000
committerAnsible Core Team <info@ansible.com>2020-03-09 09:40:30 +0000
commitef24d794eedb4b947bcbaa2681c7fc9cdfe8ff23 (patch)
treef02583d93085b8bfc9343dd60937b395ebb92254
parent9d2d1370382f0790b0d9049640e19781497e1456 (diff)
downloadansible-ef24d794eedb4b947bcbaa2681c7fc9cdfe8ff23.tar.gz
Migrated to community.crypto
-rw-r--r--lib/ansible/module_utils/acme.py1028
-rw-r--r--lib/ansible/module_utils/crypto.py2125
-rw-r--r--lib/ansible/module_utils/ecs/api.py364
l---------lib/ansible/modules/crypto/acme/_acme_account_facts.py1
-rw-r--r--lib/ansible/modules/crypto/acme/acme_account.py278
-rw-r--r--lib/ansible/modules/crypto/acme/acme_account_info.py301
-rw-r--r--lib/ansible/modules/crypto/acme/acme_certificate.py1265
-rw-r--r--lib/ansible/modules/crypto/acme/acme_certificate_revoke.py223
-rw-r--r--lib/ansible/modules/crypto/acme/acme_challenge_cert_helper.py299
-rw-r--r--lib/ansible/modules/crypto/acme/acme_inspect.py320
-rw-r--r--lib/ansible/modules/crypto/certificate_complete_chain.py350
-rw-r--r--lib/ansible/modules/crypto/entrust/ecs_certificate.py952
-rw-r--r--lib/ansible/modules/crypto/entrust/ecs_domain.py409
-rw-r--r--lib/ansible/modules/crypto/get_certificate.py371
-rw-r--r--lib/ansible/modules/crypto/luks_device.py794
-rw-r--r--lib/ansible/modules/crypto/openssh_cert.py590
-rw-r--r--lib/ansible/modules/crypto/openssh_keypair.py493
-rw-r--r--lib/ansible/modules/crypto/openssl_certificate.py2756
-rw-r--r--lib/ansible/modules/crypto/openssl_certificate_info.py863
-rw-r--r--lib/ansible/modules/crypto/openssl_csr.py1159
-rw-r--r--lib/ansible/modules/crypto/openssl_csr_info.py667
-rw-r--r--lib/ansible/modules/crypto/openssl_dhparam.py418
-rw-r--r--lib/ansible/modules/crypto/openssl_pkcs12.py470
-rw-r--r--lib/ansible/modules/crypto/openssl_privatekey.py943
-rw-r--r--lib/ansible/modules/crypto/openssl_privatekey_info.py651
-rw-r--r--lib/ansible/modules/crypto/openssl_publickey.py478
-rw-r--r--lib/ansible/modules/crypto/x509_crl.py783
-rw-r--r--lib/ansible/modules/crypto/x509_crl_info.py281
-rw-r--r--lib/ansible/plugins/doc_fragments/acme.py118
-rw-r--r--lib/ansible/plugins/doc_fragments/ecs_credential.py43
-rw-r--r--test/integration/targets/acme_account/aliases2
-rw-r--r--test/integration/targets/acme_account/meta/main.yml2
-rw-r--r--test/integration/targets/acme_account/tasks/impl.yml244
-rw-r--r--test/integration/targets/acme_account/tasks/main.yml31
-rw-r--r--test/integration/targets/acme_account/tests/validate.yml129
-rw-r--r--test/integration/targets/acme_account_info/aliases2
-rw-r--r--test/integration/targets/acme_account_info/meta/main.yml2
-rw-r--r--test/integration/targets/acme_account_info/tasks/impl.yml82
-rw-r--r--test/integration/targets/acme_account_info/tasks/main.yml31
-rw-r--r--test/integration/targets/acme_account_info/tests/validate.yml40
-rw-r--r--test/integration/targets/acme_certificate/aliases2
-rw-r--r--test/integration/targets/acme_certificate/meta/main.yml2
-rw-r--r--test/integration/targets/acme_certificate/tasks/impl.yml451
-rw-r--r--test/integration/targets/acme_certificate/tasks/main.yml102
l---------test/integration/targets/acme_certificate/tasks/obtain-cert.yml1
-rw-r--r--test/integration/targets/acme_certificate/tests/validate.yml144
-rw-r--r--test/integration/targets/acme_certificate_revoke/aliases2
-rw-r--r--test/integration/targets/acme_certificate_revoke/meta/main.yml2
-rw-r--r--test/integration/targets/acme_certificate_revoke/tasks/impl.yml89
-rw-r--r--test/integration/targets/acme_certificate_revoke/tasks/main.yml31
l---------test/integration/targets/acme_certificate_revoke/tasks/obtain-cert.yml1
-rw-r--r--test/integration/targets/acme_certificate_revoke/tests/validate.yml16
-rw-r--r--test/integration/targets/acme_challenge_cert_helper/aliases2
-rw-r--r--test/integration/targets/acme_challenge_cert_helper/meta/main.yml2
-rw-r--r--test/integration/targets/acme_challenge_cert_helper/tasks/main.yml25
l---------test/integration/targets/acme_challenge_cert_helper/tasks/obtain-cert.yml1
-rw-r--r--test/integration/targets/acme_inspect/aliases2
-rw-r--r--test/integration/targets/acme_inspect/meta/main.yml2
-rw-r--r--test/integration/targets/acme_inspect/tasks/impl.yml151
-rw-r--r--test/integration/targets/acme_inspect/tasks/main.yml31
-rw-r--r--test/integration/targets/acme_inspect/tests/validate.yml131
-rw-r--r--test/integration/targets/certificate_complete_chain/aliases2
-rw-r--r--test/integration/targets/certificate_complete_chain/files/cert1-chain.pem22
-rw-r--r--test/integration/targets/certificate_complete_chain/files/cert1-fullchain.pem51
-rw-r--r--test/integration/targets/certificate_complete_chain/files/cert1-root.pem16
-rw-r--r--test/integration/targets/certificate_complete_chain/files/cert1.pem29
-rw-r--r--test/integration/targets/certificate_complete_chain/files/cert2-altchain.pem32
-rw-r--r--test/integration/targets/certificate_complete_chain/files/cert2-altroot.pem31
-rw-r--r--test/integration/targets/certificate_complete_chain/files/cert2-chain.pem27
-rw-r--r--test/integration/targets/certificate_complete_chain/files/cert2-fullchain.pem72
-rw-r--r--test/integration/targets/certificate_complete_chain/files/cert2-root.pem20
-rw-r--r--test/integration/targets/certificate_complete_chain/files/cert2.pem45
-rw-r--r--test/integration/targets/certificate_complete_chain/files/roots.pem3733
-rw-r--r--test/integration/targets/certificate_complete_chain/files/roots/COMODO_Certification_Authority.pem25
-rw-r--r--test/integration/targets/certificate_complete_chain/files/roots/COMODO_ECC_Certification_Authority.pem16
-rw-r--r--test/integration/targets/certificate_complete_chain/files/roots/COMODO_RSA_Certification_Authority.pem34
-rw-r--r--test/integration/targets/certificate_complete_chain/files/roots/DST_Root_CA_X3.pem20
-rw-r--r--test/integration/targets/certificate_complete_chain/files/roots/ISRG_Root_X1.pem31
-rw-r--r--test/integration/targets/certificate_complete_chain/meta/main.yml2
-rw-r--r--test/integration/targets/certificate_complete_chain/tasks/main.yml97
-rw-r--r--test/integration/targets/ecs_certificate/aliases15
-rw-r--r--test/integration/targets/ecs_certificate/defaults/main.yml2
-rw-r--r--test/integration/targets/ecs_certificate/meta/main.yml3
-rw-r--r--test/integration/targets/ecs_certificate/tasks/main.yml215
-rw-r--r--test/integration/targets/ecs_certificate/vars/main.yml52
-rw-r--r--test/integration/targets/ecs_domain/aliases15
-rw-r--r--test/integration/targets/ecs_domain/defaults/main.yml2
-rw-r--r--test/integration/targets/ecs_domain/meta/main.yml2
-rw-r--r--test/integration/targets/ecs_domain/tasks/main.yml270
-rw-r--r--test/integration/targets/ecs_domain/vars/main.yml15
-rw-r--r--test/integration/targets/get_certificate/aliases4
-rw-r--r--test/integration/targets/get_certificate/files/bogus_ca.pem18
-rw-r--r--test/integration/targets/get_certificate/files/process_certs.py28
-rw-r--r--test/integration/targets/get_certificate/meta/main.yml3
-rw-r--r--test/integration/targets/get_certificate/tasks/main.yml42
-rw-r--r--test/integration/targets/get_certificate/tests/validate.yml106
-rw-r--r--test/integration/targets/incidental_x509_crl/aliases4
-rw-r--r--test/integration/targets/incidental_x509_crl/meta/main.yml2
-rw-r--r--test/integration/targets/incidental_x509_crl/tasks/impl.yml289
-rw-r--r--test/integration/targets/incidental_x509_crl/tasks/main.yml83
-rw-r--r--test/integration/targets/incidental_x509_crl/tests/validate.yml61
-rw-r--r--test/integration/targets/luks_device/aliases7
-rw-r--r--test/integration/targets/luks_device/files/keyfile11
-rw-r--r--test/integration/targets/luks_device/files/keyfile21
-rw-r--r--test/integration/targets/luks_device/tasks/main.yml36
-rw-r--r--test/integration/targets/luks_device/tasks/run-test.yml8
-rw-r--r--test/integration/targets/luks_device/tasks/tests/create-destroy.yml187
-rw-r--r--test/integration/targets/luks_device/tasks/tests/device-check.yml48
-rw-r--r--test/integration/targets/luks_device/tasks/tests/key-management.yml168
-rw-r--r--test/integration/targets/luks_device/tasks/tests/options.yml41
-rw-r--r--test/integration/targets/luks_device/tasks/tests/passphrase.yml181
-rw-r--r--test/integration/targets/openssh_cert/aliases3
-rw-r--r--test/integration/targets/openssh_cert/meta/main.yml2
-rw-r--r--test/integration/targets/openssh_cert/tasks/main.yml408
-rw-r--r--test/integration/targets/openssh_keypair/aliases3
-rw-r--r--test/integration/targets/openssh_keypair/meta/main.yml2
-rw-r--r--test/integration/targets/openssh_keypair/tasks/main.yml375
-rw-r--r--test/integration/targets/openssh_keypair/tests/validate.yml132
-rw-r--r--test/integration/targets/openssh_keypair/vars/main.yml7
-rw-r--r--test/integration/targets/openssl_certificate/aliases3
-rw-r--r--test/integration/targets/openssl_certificate/meta/main.yml2
-rw-r--r--test/integration/targets/openssl_certificate/tasks/assertonly.yml152
-rw-r--r--test/integration/targets/openssl_certificate/tasks/expired.yml48
-rw-r--r--test/integration/targets/openssl_certificate/tasks/impl.yml8
-rw-r--r--test/integration/targets/openssl_certificate/tasks/main.yml22
-rw-r--r--test/integration/targets/openssl_certificate/tasks/ownca.yml578
-rw-r--r--test/integration/targets/openssl_certificate/tasks/removal.yml52
-rw-r--r--test/integration/targets/openssl_certificate/tasks/selfsigned.yml431
-rw-r--r--test/integration/targets/openssl_certificate/tests/validate_ownca.yml178
-rw-r--r--test/integration/targets/openssl_certificate/tests/validate_selfsigned.yml164
-rw-r--r--test/integration/targets/openssl_certificate_info/aliases3
-rw-r--r--test/integration/targets/openssl_certificate_info/files/cert1.pem45
-rw-r--r--test/integration/targets/openssl_certificate_info/meta/main.yml2
-rw-r--r--test/integration/targets/openssl_certificate_info/tasks/impl.yml120
-rw-r--r--test/integration/targets/openssl_certificate_info/tasks/main.yml176
-rw-r--r--test/integration/targets/openssl_certificate_info/test_plugins/jinja_compatibility.py15
-rw-r--r--test/integration/targets/openssl_csr/aliases3
-rw-r--r--test/integration/targets/openssl_csr/meta/main.yml2
-rw-r--r--test/integration/targets/openssl_csr/tasks/impl.yml767
-rw-r--r--test/integration/targets/openssl_csr/tasks/main.yml44
-rw-r--r--test/integration/targets/openssl_csr/tests/validate.yml208
-rw-r--r--test/integration/targets/openssl_csr_info/aliases3
-rw-r--r--test/integration/targets/openssl_csr_info/meta/main.yml2
-rw-r--r--test/integration/targets/openssl_csr_info/tasks/impl.yml94
-rw-r--r--test/integration/targets/openssl_csr_info/tasks/main.yml161
-rw-r--r--test/integration/targets/openssl_csr_info/test_plugins/jinja_compatibility.py15
-rw-r--r--test/integration/targets/openssl_dhparam/aliases3
-rw-r--r--test/integration/targets/openssl_dhparam/meta/main.yml2
-rw-r--r--test/integration/targets/openssl_dhparam/tasks/impl.yml101
-rw-r--r--test/integration/targets/openssl_dhparam/tasks/main.yml38
-rw-r--r--test/integration/targets/openssl_dhparam/tests/validate.yml58
-rw-r--r--test/integration/targets/openssl_pkcs12/aliases4
-rw-r--r--test/integration/targets/openssl_pkcs12/meta/main.yml2
-rw-r--r--test/integration/targets/openssl_pkcs12/tasks/impl.yml254
-rw-r--r--test/integration/targets/openssl_pkcs12/tasks/main.yml4
-rw-r--r--test/integration/targets/openssl_pkcs12/tests/validate.yml57
-rw-r--r--test/integration/targets/openssl_privatekey/aliases3
-rw-r--r--test/integration/targets/openssl_privatekey/meta/main.yml2
-rw-r--r--test/integration/targets/openssl_privatekey/tasks/impl.yml820
-rw-r--r--test/integration/targets/openssl_privatekey/tasks/main.yml108
-rw-r--r--test/integration/targets/openssl_privatekey/tests/validate.yml215
-rw-r--r--test/integration/targets/openssl_privatekey/vars/main.yml7
-rw-r--r--test/integration/targets/openssl_privatekey_info/aliases3
-rw-r--r--test/integration/targets/openssl_privatekey_info/meta/main.yml2
-rw-r--r--test/integration/targets/openssl_privatekey_info/tasks/impl.yml178
-rw-r--r--test/integration/targets/openssl_privatekey_info/tasks/main.yml71
-rw-r--r--test/integration/targets/openssl_publickey/aliases3
-rw-r--r--test/integration/targets/openssl_publickey/meta/main.yml2
-rw-r--r--test/integration/targets/openssl_publickey/tasks/impl.yml186
-rw-r--r--test/integration/targets/openssl_publickey/tasks/main.yml48
-rw-r--r--test/integration/targets/openssl_publickey/tests/validate.yml146
-rw-r--r--test/integration/targets/x509_crl/aliases4
-rw-r--r--test/integration/targets/x509_crl/meta/main.yml2
-rw-r--r--test/integration/targets/x509_crl/tasks/impl.yml289
-rw-r--r--test/integration/targets/x509_crl/tasks/main.yml83
-rw-r--r--test/integration/targets/x509_crl/tests/validate.yml61
-rw-r--r--test/sanity/ignore.txt2
-rw-r--r--test/units/module_utils/acme/fixtures/cert_1.pem11
-rw-r--r--test/units/module_utils/acme/fixtures/csr_1.pem9
-rw-r--r--test/units/module_utils/acme/fixtures/csr_1.txt28
-rw-r--r--test/units/module_utils/acme/fixtures/csr_2.pem27
-rw-r--r--test/units/module_utils/acme/fixtures/csr_2.txt78
-rw-r--r--test/units/module_utils/acme/fixtures/privatekey_1.pem5
-rw-r--r--test/units/module_utils/acme/fixtures/privatekey_1.txt14
-rw-r--r--test/units/module_utils/acme/test_acme.py216
-rw-r--r--test/units/modules/crypto/test_luks_device.py305
186 files changed, 0 insertions, 35443 deletions
diff --git a/lib/ansible/module_utils/acme.py b/lib/ansible/module_utils/acme.py
deleted file mode 100644
index 215ce899ee..0000000000
--- a/lib/ansible/module_utils/acme.py
+++ /dev/null
@@ -1,1028 +0,0 @@
-# -*- coding: utf-8 -*-
-
-# This code is part of Ansible, but is an independent component.
-# This particular file snippet, and this file snippet only, is BSD licensed.
-# Modules you write using this snippet, which is embedded dynamically by Ansible
-# still belong to the author of the module, and may assign their own license
-# to the complete work.
-#
-# Copyright (c), Michael Gruener <michael.gruener@chaosmoon.net>, 2016
-#
-# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-
-import base64
-import binascii
-import copy
-import datetime
-import hashlib
-import json
-import locale
-import os
-import re
-import shutil
-import sys
-import tempfile
-import traceback
-
-from ansible.module_utils.basic import missing_required_lib
-from ansible.module_utils._text import to_native, to_text, to_bytes
-from ansible.module_utils.urls import fetch_url
-from ansible.module_utils.compat import ipaddress as compat_ipaddress
-from ansible.module_utils.six.moves.urllib.parse import unquote
-
-try:
- import cryptography
- import cryptography.hazmat.backends
- import cryptography.hazmat.primitives.serialization
- import cryptography.hazmat.primitives.asymmetric.rsa
- import cryptography.hazmat.primitives.asymmetric.ec
- import cryptography.hazmat.primitives.asymmetric.padding
- import cryptography.hazmat.primitives.hashes
- import cryptography.hazmat.primitives.asymmetric.utils
- import cryptography.x509
- import cryptography.x509.oid
- from distutils.version import LooseVersion
- CRYPTOGRAPHY_VERSION = cryptography.__version__
- HAS_CURRENT_CRYPTOGRAPHY = (LooseVersion(CRYPTOGRAPHY_VERSION) >= LooseVersion('1.5'))
- if HAS_CURRENT_CRYPTOGRAPHY:
- _cryptography_backend = cryptography.hazmat.backends.default_backend()
-except Exception as dummy:
- HAS_CURRENT_CRYPTOGRAPHY = False
-
-
-class ModuleFailException(Exception):
- '''
- If raised, module.fail_json() will be called with the given parameters after cleanup.
- '''
- def __init__(self, msg, **args):
- super(ModuleFailException, self).__init__(self, msg)
- self.msg = msg
- self.module_fail_args = args
-
- def do_fail(self, module, **arguments):
- module.fail_json(msg=self.msg, other=self.module_fail_args, **arguments)
-
-
-def nopad_b64(data):
- return base64.urlsafe_b64encode(data).decode('utf8').replace("=", "")
-
-
-def read_file(fn, mode='b'):
- try:
- with open(fn, 'r' + mode) as f:
- return f.read()
- except Exception as e:
- raise ModuleFailException('Error while reading file "{0}": {1}'.format(fn, e))
-
-
-# function source: network/basics/uri.py
-def write_file(module, dest, content):
- '''
- Write content to destination file dest, only if the content
- has changed.
- '''
- changed = False
- # create a tempfile
- fd, tmpsrc = tempfile.mkstemp(text=False)
- f = os.fdopen(fd, 'wb')
- try:
- f.write(content)
- except Exception as err:
- try:
- f.close()
- except Exception as dummy:
- pass
- os.remove(tmpsrc)
- raise ModuleFailException("failed to create temporary content file: %s" % to_native(err), exception=traceback.format_exc())
- f.close()
- checksum_src = None
- checksum_dest = None
- # raise an error if there is no tmpsrc file
- if not os.path.exists(tmpsrc):
- try:
- os.remove(tmpsrc)
- except Exception as dummy:
- pass
- raise ModuleFailException("Source %s does not exist" % (tmpsrc))
- if not os.access(tmpsrc, os.R_OK):
- os.remove(tmpsrc)
- raise ModuleFailException("Source %s not readable" % (tmpsrc))
- checksum_src = module.sha1(tmpsrc)
- # check if there is no dest file
- if os.path.exists(dest):
- # raise an error if copy has no permission on dest
- if not os.access(dest, os.W_OK):
- os.remove(tmpsrc)
- raise ModuleFailException("Destination %s not writable" % (dest))
- if not os.access(dest, os.R_OK):
- os.remove(tmpsrc)
- raise ModuleFailException("Destination %s not readable" % (dest))
- checksum_dest = module.sha1(dest)
- else:
- dirname = os.path.dirname(dest) or '.'
- if not os.access(dirname, os.W_OK):
- os.remove(tmpsrc)
- raise ModuleFailException("Destination dir %s not writable" % (dirname))
- if checksum_src != checksum_dest:
- try:
- shutil.copyfile(tmpsrc, dest)
- changed = True
- except Exception as err:
- os.remove(tmpsrc)
- raise ModuleFailException("failed to copy %s to %s: %s" % (tmpsrc, dest, to_native(err)), exception=traceback.format_exc())
- os.remove(tmpsrc)
- return changed
-
-
-def pem_to_der(pem_filename):
- '''
- Load PEM file, and convert to DER.
-
- If PEM contains multiple entities, the first entity will be used.
- '''
- certificate_lines = []
- try:
- with open(pem_filename, "rt") as f:
- header_line_count = 0
- for line in f:
- if line.startswith('-----'):
- header_line_count += 1
- if header_line_count == 2:
- # If certificate file contains other certs appended
- # (like intermediate certificates), ignore these.
- break
- continue
- certificate_lines.append(line.strip())
- except Exception as err:
- raise ModuleFailException("cannot load PEM file {0}: {1}".format(pem_filename, to_native(err)), exception=traceback.format_exc())
- return base64.b64decode(''.join(certificate_lines))
-
-
-def _parse_key_openssl(openssl_binary, module, key_file=None, key_content=None):
- '''
- Parses an RSA or Elliptic Curve key file in PEM format and returns a pair
- (error, key_data).
- '''
- # If key_file isn't given, but key_content, write that to a temporary file
- if key_file is None:
- fd, tmpsrc = tempfile.mkstemp()
- module.add_cleanup_file(tmpsrc) # Ansible will delete the file on exit
- f = os.fdopen(fd, 'wb')
- try:
- f.write(key_content.encode('utf-8'))
- key_file = tmpsrc
- except Exception as err:
- try:
- f.close()
- except Exception as dummy:
- pass
- raise ModuleFailException("failed to create temporary content file: %s" % to_native(err), exception=traceback.format_exc())
- f.close()
- # Parse key
- account_key_type = None
- with open(key_file, "rt") as f:
- for line in f:
- m = re.match(r"^\s*-{5,}BEGIN\s+(EC|RSA)\s+PRIVATE\s+KEY-{5,}\s*$", line)
- if m is not None:
- account_key_type = m.group(1).lower()
- break
- if account_key_type is None:
- # This happens for example if openssl_privatekey created this key
- # (as opposed to the OpenSSL binary). For now, we assume this is
- # an RSA key.
- # FIXME: add some kind of auto-detection
- account_key_type = "rsa"
- if account_key_type not in ("rsa", "ec"):
- return 'unknown key type "%s"' % account_key_type, {}
-
- openssl_keydump_cmd = [openssl_binary, account_key_type, "-in", key_file, "-noout", "-text"]
- dummy, out, dummy = module.run_command(openssl_keydump_cmd, check_rc=True)
-
- if account_key_type == 'rsa':
- pub_hex, pub_exp = re.search(
- r"modulus:\n\s+00:([a-f0-9\:\s]+?)\npublicExponent: ([0-9]+)",
- to_text(out, errors='surrogate_or_strict'), re.MULTILINE | re.DOTALL).groups()
- pub_exp = "{0:x}".format(int(pub_exp))
- if len(pub_exp) % 2:
- pub_exp = "0{0}".format(pub_exp)
-
- return None, {
- 'key_file': key_file,
- 'type': 'rsa',
- 'alg': 'RS256',
- 'jwk': {
- "kty": "RSA",
- "e": nopad_b64(binascii.unhexlify(pub_exp.encode("utf-8"))),
- "n": nopad_b64(binascii.unhexlify(re.sub(r"(\s|:)", "", pub_hex).encode("utf-8"))),
- },
- 'hash': 'sha256',
- }
- elif account_key_type == 'ec':
- pub_data = re.search(
- r"pub:\s*\n\s+04:([a-f0-9\:\s]+?)\nASN1 OID: (\S+)(?:\nNIST CURVE: (\S+))?",
- to_text(out, errors='surrogate_or_strict'), re.MULTILINE | re.DOTALL)
- if pub_data is None:
- return 'cannot parse elliptic curve key', {}
- pub_hex = binascii.unhexlify(re.sub(r"(\s|:)", "", pub_data.group(1)).encode("utf-8"))
- asn1_oid_curve = pub_data.group(2).lower()
- nist_curve = pub_data.group(3).lower() if pub_data.group(3) else None
- if asn1_oid_curve == 'prime256v1' or nist_curve == 'p-256':
- bits = 256
- alg = 'ES256'
- hashalg = 'sha256'
- point_size = 32
- curve = 'P-256'
- elif asn1_oid_curve == 'secp384r1' or nist_curve == 'p-384':
- bits = 384
- alg = 'ES384'
- hashalg = 'sha384'
- point_size = 48
- curve = 'P-384'
- elif asn1_oid_curve == 'secp521r1' or nist_curve == 'p-521':
- # Not yet supported on Let's Encrypt side, see
- # https://github.com/letsencrypt/boulder/issues/2217
- bits = 521
- alg = 'ES512'
- hashalg = 'sha512'
- point_size = 66
- curve = 'P-521'
- else:
- return 'unknown elliptic curve: %s / %s' % (asn1_oid_curve, nist_curve), {}
- num_bytes = (bits + 7) // 8
- if len(pub_hex) != 2 * num_bytes:
- return 'bad elliptic curve point (%s / %s)' % (asn1_oid_curve, nist_curve), {}
- return None, {
- 'key_file': key_file,
- 'type': 'ec',
- 'alg': alg,
- 'jwk': {
- "kty": "EC",
- "crv": curve,
- "x": nopad_b64(pub_hex[:num_bytes]),
- "y": nopad_b64(pub_hex[num_bytes:]),
- },
- 'hash': hashalg,
- 'point_size': point_size,
- }
-
-
-def _sign_request_openssl(openssl_binary, module, payload64, protected64, key_data):
- openssl_sign_cmd = [openssl_binary, "dgst", "-{0}".format(key_data['hash']), "-sign", key_data['key_file']]
- sign_payload = "{0}.{1}".format(protected64, payload64).encode('utf8')
- dummy, out, dummy = module.run_command(openssl_sign_cmd, data=sign_payload, check_rc=True, binary_data=True)
-
- if key_data['type'] == 'ec':
- dummy, der_out, dummy = module.run_command(
- [openssl_binary, "asn1parse", "-inform", "DER"],
- data=out, binary_data=True)
- expected_len = 2 * key_data['point_size']
- sig = re.findall(
- r"prim:\s+INTEGER\s+:([0-9A-F]{1,%s})\n" % expected_len,
- to_text(der_out, errors='surrogate_or_strict'))
- if len(sig) != 2:
- raise ModuleFailException(
- "failed to generate Elliptic Curve signature; cannot parse DER output: {0}".format(
- to_text(der_out, errors='surrogate_or_strict')))
- sig[0] = (expected_len - len(sig[0])) * '0' + sig[0]
- sig[1] = (expected_len - len(sig[1])) * '0' + sig[1]
- out = binascii.unhexlify(sig[0]) + binascii.unhexlify(sig[1])
-
- return {
- "protected": protected64,
- "payload": payload64,
- "signature": nopad_b64(to_bytes(out)),
- }
-
-
-if sys.version_info[0] >= 3:
- # Python 3 (and newer)
- def _count_bytes(n):
- return (n.bit_length() + 7) // 8 if n > 0 else 0
-
- def _convert_int_to_bytes(count, no):
- return no.to_bytes(count, byteorder='big')
-
- def _pad_hex(n, digits):
- res = hex(n)[2:]
- if len(res) < digits:
- res = '0' * (digits - len(res)) + res
- return res
-else:
- # Python 2
- def _count_bytes(n):
- if n <= 0:
- return 0
- h = '%x' % n
- return (len(h) + 1) // 2
-
- def _convert_int_to_bytes(count, n):
- h = '%x' % n
- if len(h) > 2 * count:
- raise Exception('Number {1} needs more than {0} bytes!'.format(count, n))
- return ('0' * (2 * count - len(h)) + h).decode('hex')
-
- def _pad_hex(n, digits):
- h = '%x' % n
- if len(h) < digits:
- h = '0' * (digits - len(h)) + h
- return h
-
-
-def _parse_key_cryptography(module, key_file=None, key_content=None):
- '''
- Parses an RSA or Elliptic Curve key file in PEM format and returns a pair
- (error, key_data).
- '''
- # If key_content isn't given, read key_file
- if key_content is None:
- key_content = read_file(key_file)
- else:
- key_content = to_bytes(key_content)
- # Parse key
- try:
- key = cryptography.hazmat.primitives.serialization.load_pem_private_key(key_content, password=None, backend=_cryptography_backend)
- except Exception as e:
- return 'error while loading key: {0}'.format(e), None
- if isinstance(key, cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateKey):
- pk = key.public_key().public_numbers()
- return None, {
- 'key_obj': key,
- 'type': 'rsa',
- 'alg': 'RS256',
- 'jwk': {
- "kty": "RSA",
- "e": nopad_b64(_convert_int_to_bytes(_count_bytes(pk.e), pk.e)),
- "n": nopad_b64(_convert_int_to_bytes(_count_bytes(pk.n), pk.n)),
- },
- 'hash': 'sha256',
- }
- elif isinstance(key, cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePrivateKey):
- pk = key.public_key().public_numbers()
- if pk.curve.name == 'secp256r1':
- bits = 256
- alg = 'ES256'
- hashalg = 'sha256'
- point_size = 32
- curve = 'P-256'
- elif pk.curve.name == 'secp384r1':
- bits = 384
- alg = 'ES384'
- hashalg = 'sha384'
- point_size = 48
- curve = 'P-384'
- elif pk.curve.name == 'secp521r1':
- # Not yet supported on Let's Encrypt side, see
- # https://github.com/letsencrypt/boulder/issues/2217
- bits = 521
- alg = 'ES512'
- hashalg = 'sha512'
- point_size = 66
- curve = 'P-521'
- else:
- return 'unknown elliptic curve: {0}'.format(pk.curve.name), {}
- num_bytes = (bits + 7) // 8
- return None, {
- 'key_obj': key,
- 'type': 'ec',
- 'alg': alg,
- 'jwk': {
- "kty": "EC",
- "crv": curve,
- "x": nopad_b64(_convert_int_to_bytes(num_bytes, pk.x)),
- "y": nopad_b64(_convert_int_to_bytes(num_bytes, pk.y)),
- },
- 'hash': hashalg,
- 'point_size': point_size,
- }
- else:
- return 'unknown key type "{0}"'.format(type(key)), {}
-
-
-def _sign_request_cryptography(module, payload64, protected64, key_data):
- sign_payload = "{0}.{1}".format(protected64, payload64).encode('utf8')
- if isinstance(key_data['key_obj'], cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateKey):
- padding = cryptography.hazmat.primitives.asymmetric.padding.PKCS1v15()
- hashalg = cryptography.hazmat.primitives.hashes.SHA256
- signature = key_data['key_obj'].sign(sign_payload, padding, hashalg())
- elif isinstance(key_data['key_obj'], cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePrivateKey):
- if key_data['hash'] == 'sha256':
- hashalg = cryptography.hazmat.primitives.hashes.SHA256
- elif key_data['hash'] == 'sha384':
- hashalg = cryptography.hazmat.primitives.hashes.SHA384
- elif key_data['hash'] == 'sha512':
- hashalg = cryptography.hazmat.primitives.hashes.SHA512
- ecdsa = cryptography.hazmat.primitives.asymmetric.ec.ECDSA(hashalg())
- r, s = cryptography.hazmat.primitives.asymmetric.utils.decode_dss_signature(key_data['key_obj'].sign(sign_payload, ecdsa))
- rr = _pad_hex(r, 2 * key_data['point_size'])
- ss = _pad_hex(s, 2 * key_data['point_size'])
- signature = binascii.unhexlify(rr) + binascii.unhexlify(ss)
-
- return {
- "protected": protected64,
- "payload": payload64,
- "signature": nopad_b64(signature),
- }
-
-
-def _assert_fetch_url_success(response, info, allow_redirect=False, allow_client_error=True, allow_server_error=True):
- if info['status'] < 0:
- raise ModuleFailException(msg="Failure downloading %s, %s" % (info['url'], info['msg']))
-
- if (300 <= info['status'] < 400 and not allow_redirect) or \
- (400 <= info['status'] < 500 and not allow_client_error) or \
- (info['status'] >= 500 and not allow_server_error):
- raise ModuleFailException("ACME request failed: CODE: {0} MGS: {1} RESULT: {2}".format(info['status'], info['msg'], response))
-
-
-class ACMEDirectory(object):
- '''
- The ACME server directory. Gives access to the available resources,
- and allows to obtain a Replay-Nonce. The acme_directory URL
- needs to support unauthenticated GET requests; ACME endpoints
- requiring authentication are not supported.
- https://tools.ietf.org/html/rfc8555#section-7.1.1
- '''
-
- def __init__(self, module, account):
- self.module = module
- self.directory_root = module.params['acme_directory']
- self.version = module.params['acme_version']
-
- self.directory, dummy = account.get_request(self.directory_root, get_only=True)
-
- # Check whether self.version matches what we expect
- if self.version == 1:
- for key in ('new-reg', 'new-authz', 'new-cert'):
- if key not in self.directory:
- raise ModuleFailException("ACME directory does not seem to follow protocol ACME v1")
- if self.version == 2:
- for key in ('newNonce', 'newAccount', 'newOrder'):
- if key not in self.directory:
- raise ModuleFailException("ACME directory does not seem to follow protocol ACME v2")
-
- def __getitem__(self, key):
- return self.directory[key]
-
- def get_nonce(self, resource=None):
- url = self.directory_root if self.version == 1 else self.directory['newNonce']
- if resource is not None:
- url = resource
- dummy, info = fetch_url(self.module, url, method='HEAD')
- if info['status'] not in (200, 204):
- raise ModuleFailException("Failed to get replay-nonce, got status {0}".format(info['status']))
- return info['replay-nonce']
-
-
-class ACMEAccount(object):
- '''
- ACME account object. Handles the authorized communication with the
- ACME server. Provides access to account bound information like
- the currently active authorizations and valid certificates
- '''
-
- def __init__(self, module):
- # Set to true to enable logging of all signed requests
- self._debug = False
-
- self.module = module
- self.version = module.params['acme_version']
- # account_key path and content are mutually exclusive
- self.key = module.params['account_key_src']
- self.key_content = module.params['account_key_content']
-
- # Grab account URI from module parameters.
- # Make sure empty string is treated as None.
- self.uri = module.params.get('account_uri') or None
-
- self._openssl_bin = module.get_bin_path('openssl', True)
-
- if self.key is not None or self.key_content is not None:
- error, self.key_data = self.parse_key(self.key, self.key_content)
- if error:
- raise ModuleFailException("error while parsing account key: %s" % error)
- self.jwk = self.key_data['jwk']
- self.jws_header = {
- "alg": self.key_data['alg'],
- "jwk": self.jwk,
- }
- if self.uri:
- # Make sure self.jws_header is updated
- self.set_account_uri(self.uri)
-
- self.directory = ACMEDirectory(module, self)
-
- def get_keyauthorization(self, token):
- '''
- Returns the key authorization for the given token
- https://tools.ietf.org/html/rfc8555#section-8.1
- '''
- accountkey_json = json.dumps(self.jwk, sort_keys=True, separators=(',', ':'))
- thumbprint = nopad_b64(hashlib.sha256(accountkey_json.encode('utf8')).digest())
- return "{0}.{1}".format(token, thumbprint)
-
- def parse_key(self, key_file=None, key_content=None):
- '''
- Parses an RSA or Elliptic Curve key file in PEM format and returns a pair
- (error, key_data).
- '''
- if key_file is None and key_content is None:
- raise AssertionError('One of key_file and key_content must be specified!')
- if HAS_CURRENT_CRYPTOGRAPHY:
- return _parse_key_cryptography(self.module, key_file, key_content)
- else:
- return _parse_key_openssl(self._openssl_bin, self.module, key_file, key_content)
-
- def sign_request(self, protected, payload, key_data, encode_payload=True):
- try:
- if payload is None:
- # POST-as-GET
- payload64 = ''
- else:
- # POST
- if encode_payload:
- payload = self.module.jsonify(payload).encode('utf8')
- payload64 = nopad_b64(to_bytes(payload))
- protected64 = nopad_b64(self.module.jsonify(protected).encode('utf8'))
- except Exception as e:
- raise ModuleFailException("Failed to encode payload / headers as JSON: {0}".format(e))
-
- if HAS_CURRENT_CRYPTOGRAPHY:
- return _sign_request_cryptography(self.module, payload64, protected64, key_data)
- else:
- return _sign_request_openssl(self._openssl_bin, self.module, payload64, protected64, key_data)
-
- def _log(self, msg, data=None):
- '''
- Write arguments to acme.log when logging is enabled.
- '''
- if self._debug:
- with open('acme.log', 'ab') as f:
- f.write('[{0}] {1}\n'.format(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S.%s'), msg).encode('utf-8'))
- if data is not None:
- f.write('{0}\n\n'.format(json.dumps(data, indent=2, sort_keys=True)).encode('utf-8'))
-
- def send_signed_request(self, url, payload, key_data=None, jws_header=None, parse_json_result=True, encode_payload=True):
- '''
- Sends a JWS signed HTTP POST request to the ACME server and returns
- the response as dictionary
- https://tools.ietf.org/html/rfc8555#section-6.2
-
- If payload is None, a POST-as-GET is performed.
- (https://tools.ietf.org/html/rfc8555#section-6.3)
- '''
- key_data = key_data or self.key_data
- jws_header = jws_header or self.jws_header
- failed_tries = 0
- while True:
- protected = copy.deepcopy(jws_header)
- protected["nonce"] = self.directory.get_nonce()
- if self.version != 1:
- protected["url"] = url
-
- self._log('URL', url)
- self._log('protected', protected)
- self._log('payload', payload)
- data = self.sign_request(protected, payload, key_data, encode_payload=encode_payload)
- if self.version == 1:
- data["header"] = jws_header.copy()
- for k, v in protected.items():
- hv = data["header"].pop(k, None)
- self._log('signed request', data)
- data = self.module.jsonify(data)
-
- headers = {
- 'Content-Type': 'application/jose+json',
- }
- resp, info = fetch_url(self.module, url, data=data, headers=headers, method='POST')
- _assert_fetch_url_success(resp, info)
- result = {}
- try:
- content = resp.read()
- except AttributeError:
- content = info.pop('body', None)
-
- if content or not parse_json_result:
- if (parse_json_result and info['content-type'].startswith('application/json')) or 400 <= info['status'] < 600:
- try:
- decoded_result = self.module.from_json(content.decode('utf8'))
- self._log('parsed result', decoded_result)
- # In case of badNonce error, try again (up to 5 times)
- # (https://tools.ietf.org/html/rfc8555#section-6.7)
- if (400 <= info['status'] < 600 and
- decoded_result.get('type') == 'urn:ietf:params:acme:error:badNonce' and
- failed_tries <= 5):
- failed_tries += 1
- continue
- if parse_json_result:
- result = decoded_result
- else:
- result = content
- except ValueError:
- raise ModuleFailException("Failed to parse the ACME response: {0} {1}".format(url, content))
- else:
- result = content
-
- return result, info
-
- def get_request(self, uri, parse_json_result=True, headers=None, get_only=False, fail_on_error=True):
- '''
- Perform a GET-like request. Will try POST-as-GET for ACMEv2, with fallback
- to GET if server replies with a status code of 405.
- '''
- if not get_only and self.version != 1:
- # Try POST-as-GET
- content, info = self.send_signed_request(uri, None, parse_json_result=False)
- if info['status'] == 405:
- # Instead, do unauthenticated GET
- get_only = True
- else:
- # Do unauthenticated GET
- get_only = True
-
- if get_only:
- # Perform unauthenticated GET
- resp, info = fetch_url(self.module, uri, method='GET', headers=headers)
-
- _assert_fetch_url_success(resp, info)
-
- try:
- content = resp.read()
- except AttributeError:
- content = info.pop('body', None)
-
- # Process result
- if parse_json_result:
- result = {}
- if content:
- if info['content-type'].startswith('application/json'):
- try:
- result = self.module.from_json(content.decode('utf8'))
- except ValueError:
- raise ModuleFailException("Failed to parse the ACME response: {0} {1}".format(uri, content))
- else:
- result = content
- else:
- result = content
-
- if fail_on_error and (info['status'] < 200 or info['status'] >= 400):
- raise ModuleFailException("ACME request failed: CODE: {0} RESULT: {1}".format(info['status'], result))
- return result, info
-
- def set_account_uri(self, uri):
- '''
- Set account URI. For ACME v2, it needs to be used to sending signed
- requests.
- '''
- self.uri = uri
- if self.version != 1:
- self.jws_header.pop('jwk')
- self.jws_header['kid'] = self.uri
-
- def _new_reg(self, contact=None, agreement=None, terms_agreed=False, allow_creation=True):
- '''
- Registers a new ACME account. Returns a pair ``(created, data)``.
- Here, ``created`` is ``True`` if the account was created and
- ``False`` if it already existed (e.g. it was not newly created),
- or does not exist. In case the account was created or exists,
- ``data`` contains the account data; otherwise, it is ``None``.
- https://tools.ietf.org/html/rfc8555#section-7.3
- '''
- contact = contact or []
-
- if self.version == 1:
- new_reg = {
- 'resource': 'new-reg',
- 'contact': contact
- }
- if agreement:
- new_reg['agreement'] = agreement
- else:
- new_reg['agreement'] = self.directory['meta']['terms-of-service']
- url = self.directory['new-reg']
- else:
- new_reg = {
- 'contact': contact
- }
- if not allow_creation:
- # https://tools.ietf.org/html/rfc8555#section-7.3.1
- new_reg['onlyReturnExisting'] = True
- if terms_agreed:
- new_reg['termsOfServiceAgreed'] = True
- url = self.directory['newAccount']
-
- result, info = self.send_signed_request(url, new_reg)
-
- if info['status'] in ([200, 201] if self.version == 1 else [201]):
- # Account did not exist
- if 'location' in info:
- self.set_account_uri(info['location'])
- return True, result
- elif info['status'] == (409 if self.version == 1 else 200):
- # Account did exist
- if result.get('status') == 'deactivated':
- # A bug in Pebble (https://github.com/letsencrypt/pebble/issues/179) and
- # Boulder (https://github.com/letsencrypt/boulder/issues/3971): this should
- # not return a valid account object according to
- # https://tools.ietf.org/html/rfc8555#section-7.3.6:
- # "Once an account is deactivated, the server MUST NOT accept further
- # requests authorized by that account's key."
- if not allow_creation:
- return False, None
- else:
- raise ModuleFailException("Account is deactivated")
- if 'location' in info:
- self.set_account_uri(info['location'])
- return False, result
- elif info['status'] == 400 and result['type'] == 'urn:ietf:params:acme:error:accountDoesNotExist' and not allow_creation:
- # Account does not exist (and we didn't try to create it)
- return False, None
- elif info['status'] == 403 and result['type'] == 'urn:ietf:params:acme:error:unauthorized' and 'deactivated' in (result.get('detail') or ''):
- # Account has been deactivated; currently works for Pebble; hasn't been
- # implemented for Boulder (https://github.com/letsencrypt/boulder/issues/3971),
- # might need adjustment in error detection.
- if not allow_creation:
- return False, None
- else:
- raise ModuleFailException("Account is deactivated")
- else:
- raise ModuleFailException("Error registering: {0} {1}".format(info['status'], result))
-
- def get_account_data(self):
- '''
- Retrieve account information. Can only be called when the account
- URI is already known (such as after calling setup_account).
- Return None if the account was deactivated, or a dict otherwise.
- '''
- if self.uri is None:
- raise ModuleFailException("Account URI unknown")
- if self.version == 1:
- data = {}
- data['resource'] = 'reg'
- result, info = self.send_signed_request(self.uri, data)
- else:
- # try POST-as-GET first (draft-15 or newer)
- data = None
- result, info = self.send_signed_request(self.uri, data)
- # check whether that failed with a malformed request error
- if info['status'] >= 400 and result.get('type') == 'urn:ietf:params:acme:error:malformed':
- # retry as a regular POST (with no changed data) for pre-draft-15 ACME servers
- data = {}
- result, info = self.send_signed_request(self.uri, data)
- if info['status'] in (400, 403) and result.get('type') == 'urn:ietf:params:acme:error:unauthorized':
- # Returned when account is deactivated
- return None
- if info['status'] in (400, 404) and result.get('type') == 'urn:ietf:params:acme:error:accountDoesNotExist':
- # Returned when account does not exist
- return None
- if info['status'] < 200 or info['status'] >= 300:
- raise ModuleFailException("Error getting account data from {2}: {0} {1}".format(info['status'], result, self.uri))
- return result
-
- def setup_account(self, contact=None, agreement=None, terms_agreed=False, allow_creation=True, remove_account_uri_if_not_exists=False):
- '''
- Detect or create an account on the ACME server. For ACME v1,
- as the only way (without knowing an account URI) to test if an
- account exists is to try and create one with the provided account
- key, this method will always result in an account being present
- (except on error situations). For ACME v2, a new account will
- only be created if ``allow_creation`` is set to True.
-
- For ACME v2, ``check_mode`` is fully respected. For ACME v1, the
- account might be created if it does not yet exist.
-
- Return a pair ``(created, account_data)``. Here, ``created`` will
- be ``True`` in case the account was created or would be created
- (check mode). ``account_data`` will be the current account data,
- or ``None`` if the account does not exist.
-
- The account URI will be stored in ``self.uri``; if it is ``None``,
- the account does not exist.
-
- https://tools.ietf.org/html/rfc8555#section-7.3
- '''
-
- if self.uri is not None:
- created = False
- # Verify that the account key belongs to the URI.
- # (If update_contact is True, this will be done below.)
- account_data = self.get_account_data()
- if account_data is None:
- if remove_account_uri_if_not_exists and not allow_creation:
- self.uri = None
- else:
- raise ModuleFailException("Account is deactivated or does not exist!")
- else:
- created, account_data = self._new_reg(
- contact,
- agreement=agreement,
- terms_agreed=terms_agreed,
- allow_creation=allow_creation and not self.module.check_mode
- )
- if self.module.check_mode and self.uri is None and allow_creation:
- created = True
- account_data = {
- 'contact': contact or []
- }
- return created, account_data
-
- def update_account(self, account_data, contact=None):
- '''
- Update an account on the ACME server. Check mode is fully respected.
-
- The current account data must be provided as ``account_data``.
-
- Return a pair ``(updated, account_data)``, where ``updated`` is
- ``True`` in case something changed (contact info updated) or
- would be changed (check mode), and ``account_data`` the updated
- account data.
-
- https://tools.ietf.org/html/rfc8555#section-7.3.2
- '''
- # Create request
- update_request = {}
- if contact is not None and account_data.get('contact', []) != contact:
- update_request['contact'] = list(contact)
-
- # No change?
- if not update_request:
- return False, dict(account_data)
-
- # Apply change
- if self.module.check_mode:
- account_data = dict(account_data)
- account_data.update(update_request)
- else:
- if self.version == 1:
- update_request['resource'] = 'reg'
- account_data, dummy = self.send_signed_request(self.uri, update_request)
- return True, account_data
-
-
-def _normalize_ip(ip):
- try:
- return to_native(compat_ipaddress.ip_address(to_text(ip)).compressed)
- except ValueError:
- # We don't want to error out on something IPAddress() can't parse
- return ip
-
-
-def openssl_get_csr_identifiers(openssl_binary, module, csr_filename):
- '''
- Return a set of requested identifiers (CN and SANs) for the CSR.
- Each identifier is a pair (type, identifier), where type is either
- 'dns' or 'ip'.
- '''
- openssl_csr_cmd = [openssl_binary, "req", "-in", csr_filename, "-noout", "-text"]
- dummy, out, dummy = module.run_command(openssl_csr_cmd, check_rc=True)
-
- identifiers = set([])
- common_name = re.search(r"Subject:.* CN\s?=\s?([^\s,;/]+)", to_text(out, errors='surrogate_or_strict'))
- if common_name is not None:
- identifiers.add(('dns', common_name.group(1)))
- subject_alt_names = re.search(
- r"X509v3 Subject Alternative Name: (?:critical)?\n +([^\n]+)\n",
- to_text(out, errors='surrogate_or_strict'), re.MULTILINE | re.DOTALL)
- if subject_alt_names is not None:
- for san in subject_alt_names.group(1).split(", "):
- if san.lower().startswith("dns:"):
- identifiers.add(('dns', san[4:]))
- elif san.lower().startswith("ip:"):
- identifiers.add(('ip', _normalize_ip(san[3:])))
- elif san.lower().startswith("ip address:"):
- identifiers.add(('ip', _normalize_ip(san[11:])))
- else:
- raise ModuleFailException('Found unsupported SAN identifier "{0}"'.format(san))
- return identifiers
-
-
-def cryptography_get_csr_identifiers(module, csr_filename):
- '''
- Return a set of requested identifiers (CN and SANs) for the CSR.
- Each identifier is a pair (type, identifier), where type is either
- 'dns' or 'ip'.
- '''
- identifiers = set([])
- csr = cryptography.x509.load_pem_x509_csr(read_file(csr_filename), _cryptography_backend)
- for sub in csr.subject:
- if sub.oid == cryptography.x509.oid.NameOID.COMMON_NAME:
- identifiers.add(('dns', sub.value))
- for extension in csr.extensions:
- if extension.oid == cryptography.x509.oid.ExtensionOID.SUBJECT_ALTERNATIVE_NAME:
- for name in extension.value:
- if isinstance(name, cryptography.x509.DNSName):
- identifiers.add(('dns', name.value))
- elif isinstance(name, cryptography.x509.IPAddress):
- identifiers.add(('ip', name.value.compressed))
- else:
- raise ModuleFailException('Found unsupported SAN identifier {0}'.format(name))
- return identifiers
-
-
-def cryptography_get_cert_days(module, cert_file, now=None):
- '''
- Return the days the certificate in cert_file remains valid and -1
- if the file was not found. If cert_file contains more than one
- certificate, only the first one will be considered.
- '''
- if not os.path.exists(cert_file):
- return -1
-
- try:
- cert = cryptography.x509.load_pem_x509_certificate(read_file(cert_file), _cryptography_backend)
- except Exception as e:
- raise ModuleFailException('Cannot parse certificate {0}: {1}'.format(cert_file, e))
- if now is None:
- now = datetime.datetime.now()
- return (cert.not_valid_after - now).days
-
-
-def set_crypto_backend(module):
- '''
- Sets which crypto backend to use (default: auto detection).
-
- Does not care whether a new enough cryptoraphy is available or not. Must
- be called before any real stuff is done which might evaluate
- ``HAS_CURRENT_CRYPTOGRAPHY``.
- '''
- global HAS_CURRENT_CRYPTOGRAPHY
- # Choose backend
- backend = module.params['select_crypto_backend']
- if backend == 'auto':
- pass
- elif backend == 'openssl':
- HAS_CURRENT_CRYPTOGRAPHY = False
- elif backend == 'cryptography':
- try:
- cryptography.__version__
- except Exception as dummy:
- module.fail_json(msg=missing_required_lib('cryptography'))
- HAS_CURRENT_CRYPTOGRAPHY = True
- else:
- module.fail_json(msg='Unknown crypto backend "{0}"!'.format(backend))
- # Inform about choices
- if HAS_CURRENT_CRYPTOGRAPHY:
- module.debug('Using cryptography backend (library version {0})'.format(CRYPTOGRAPHY_VERSION))
- return 'cryptography'
- else:
- module.debug('Using OpenSSL binary backend')
- return 'openssl'
-
-
-def process_links(info, callback):
- '''
- Process link header, calls callback for every link header with the URL and relation as options.
- '''
- if 'link' in info:
- link = info['link']
- for url, relation in re.findall(r'<([^>]+)>;\s*rel="(\w+)"', link):
- callback(unquote(url), relation)
-
-
-def get_default_argspec():
- '''
- Provides default argument spec for the options documented in the acme doc fragment.
- '''
- return dict(
- account_key_src=dict(type='path', aliases=['account_key']),
- account_key_content=dict(type='str', no_log=True),
- account_uri=dict(type='str'),
- acme_directory=dict(type='str'),
- acme_version=dict(type='int', choices=[1, 2]),
- validate_certs=dict(type='bool', default=True),
- select_crypto_backend=dict(type='str', default='auto', choices=['auto', 'openssl', 'cryptography']),
- )
-
-
-def handle_standard_module_arguments(module, needs_acme_v2=False):
- '''
- Do standard module setup, argument handling and warning emitting.
- '''
- backend = set_crypto_backend(module)
-
- if not module.params['validate_certs']:
- module.warn(
- 'Disabling certificate validation for communications with ACME endpoint. '
- 'This should only be done for testing against a local ACME server for '
- 'development purposes, but *never* for production purposes.'
- )
-
- if module.params['acme_version'] is None:
- module.params['acme_version'] = 1
- module.deprecate("The option 'acme_version' will be required from Ansible 2.14 on", version='2.14')
-
- if module.params['acme_directory'] is None:
- module.params['acme_directory'] = 'https://acme-staging.api.letsencrypt.org/directory'
- module.deprecate("The option 'acme_directory' will be required from Ansible 2.14 on", version='2.14')
-
- if needs_acme_v2 and module.params['acme_version'] < 2:
- module.fail_json(msg='The {0} module requires the ACME v2 protocol!'.format(module._name))
-
- # AnsibleModule() changes the locale, so change it back to C because we rely on time.strptime() when parsing certificate dates.
- module.run_command_environ_update = dict(LANG='C', LC_ALL='C', LC_MESSAGES='C', LC_CTYPE='C')
- locale.setlocale(locale.LC_ALL, 'C')
-
- return backend
diff --git a/lib/ansible/module_utils/crypto.py b/lib/ansible/module_utils/crypto.py
deleted file mode 100644
index e67eeff1b4..0000000000
--- a/lib/ansible/module_utils/crypto.py
+++ /dev/null
@@ -1,2125 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# (c) 2016, Yanis Guenane <yanis+ansible@guenane.org>
-#
-# 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/>.
-#
-# ----------------------------------------------------------------------
-# A clearly marked portion of this file is licensed under the BSD license
-# Copyright (c) 2015, 2016 Paul Kehrer (@reaperhulk)
-# Copyright (c) 2017 Fraser Tweedale (@frasertweedale)
-# For more details, search for the function _obj2txt().
-# ---------------------------------------------------------------------
-# A clearly marked portion of this file is extracted from a project that
-# is licensed under the Apache License 2.0
-# Copyright (c) the OpenSSL contributors
-# For more details, search for the function _OID_MAP.
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-
-import sys
-from distutils.version import LooseVersion
-
-try:
- import OpenSSL
- from OpenSSL import crypto
-except ImportError:
- # An error will be raised in the calling class to let the end
- # user know that OpenSSL couldn't be found.
- pass
-
-try:
- import cryptography
- from cryptography import x509
- from cryptography.hazmat.backends import default_backend as cryptography_backend
- from cryptography.hazmat.primitives.serialization import load_pem_private_key
- from cryptography.hazmat.primitives import hashes
- from cryptography.hazmat.primitives import serialization
- import ipaddress
-
- # Older versions of cryptography (< 2.1) do not have __hash__ functions for
- # general name objects (DNSName, IPAddress, ...), while providing overloaded
- # equality and string representation operations. This makes it impossible to
- # use them in hash-based data structures such as set or dict. Since we are
- # actually doing that in openssl_certificate, and potentially in other code,
- # we need to monkey-patch __hash__ for these classes to make sure our code
- # works fine.
- if LooseVersion(cryptography.__version__) < LooseVersion('2.1'):
- # A very simply hash function which relies on the representation
- # of an object to be implemented. This is the case since at least
- # cryptography 1.0, see
- # https://github.com/pyca/cryptography/commit/7a9abce4bff36c05d26d8d2680303a6f64a0e84f
- def simple_hash(self):
- return hash(repr(self))
-
- # The hash functions for the following types were added for cryptography 2.1:
- # https://github.com/pyca/cryptography/commit/fbfc36da2a4769045f2373b004ddf0aff906cf38
- x509.DNSName.__hash__ = simple_hash
- x509.DirectoryName.__hash__ = simple_hash
- x509.GeneralName.__hash__ = simple_hash
- x509.IPAddress.__hash__ = simple_hash
- x509.OtherName.__hash__ = simple_hash
- x509.RegisteredID.__hash__ = simple_hash
-
- if LooseVersion(cryptography.__version__) < LooseVersion('1.2'):
- # The hash functions for the following types were added for cryptography 1.2:
- # https://github.com/pyca/cryptography/commit/b642deed88a8696e5f01ce6855ccf89985fc35d0
- # https://github.com/pyca/cryptography/commit/d1b5681f6db2bde7a14625538bd7907b08dfb486
- x509.RFC822Name.__hash__ = simple_hash
- x509.UniformResourceIdentifier.__hash__ = simple_hash
-
- # Test whether we have support for X25519, X448, Ed25519 and/or Ed448
- try:
- import cryptography.hazmat.primitives.asymmetric.x25519
- CRYPTOGRAPHY_HAS_X25519 = True
- try:
- cryptography.hazmat.primitives.asymmetric.x25519.X25519PrivateKey.private_bytes
- CRYPTOGRAPHY_HAS_X25519_FULL = True
- except AttributeError:
- CRYPTOGRAPHY_HAS_X25519_FULL = False
- except ImportError:
- CRYPTOGRAPHY_HAS_X25519 = False
- CRYPTOGRAPHY_HAS_X25519_FULL = False
- try:
- import cryptography.hazmat.primitives.asymmetric.x448
- CRYPTOGRAPHY_HAS_X448 = True
- except ImportError:
- CRYPTOGRAPHY_HAS_X448 = False
- try:
- import cryptography.hazmat.primitives.asymmetric.ed25519
- CRYPTOGRAPHY_HAS_ED25519 = True
- except ImportError:
- CRYPTOGRAPHY_HAS_ED25519 = False
- try:
- import cryptography.hazmat.primitives.asymmetric.ed448
- CRYPTOGRAPHY_HAS_ED448 = True
- except ImportError:
- CRYPTOGRAPHY_HAS_ED448 = False
-
- HAS_CRYPTOGRAPHY = True
-except ImportError:
- # Error handled in the calling module.
- CRYPTOGRAPHY_HAS_X25519 = False
- CRYPTOGRAPHY_HAS_X25519_FULL = False
- CRYPTOGRAPHY_HAS_X448 = False
- CRYPTOGRAPHY_HAS_ED25519 = False
- CRYPTOGRAPHY_HAS_ED448 = False
- HAS_CRYPTOGRAPHY = False
-
-
-import abc
-import base64
-import binascii
-import datetime
-import errno
-import hashlib
-import os
-import re
-import tempfile
-
-from ansible.module_utils import six
-from ansible.module_utils._text import to_native, to_bytes, to_text
-
-
-class OpenSSLObjectError(Exception):
- pass
-
-
-class OpenSSLBadPassphraseError(OpenSSLObjectError):
- pass
-
-
-def get_fingerprint_of_bytes(source):
- """Generate the fingerprint of the given bytes."""
-
- fingerprint = {}
-
- try:
- algorithms = hashlib.algorithms
- except AttributeError:
- try:
- algorithms = hashlib.algorithms_guaranteed
- except AttributeError:
- return None
-
- for algo in algorithms:
- f = getattr(hashlib, algo)
- try:
- h = f(source)
- except ValueError:
- # This can happen for hash algorithms not supported in FIPS mode
- # (https://github.com/ansible/ansible/issues/67213)
- continue
- try:
- # Certain hash functions have a hexdigest() which expects a length parameter
- pubkey_digest = h.hexdigest()
- except TypeError:
- pubkey_digest = h.hexdigest(32)
- fingerprint[algo] = ':'.join(pubkey_digest[i:i + 2] for i in range(0, len(pubkey_digest), 2))
-
- return fingerprint
-
-
-def get_fingerprint(path, passphrase=None, content=None, backend='pyopenssl'):
- """Generate the fingerprint of the public key. """
-
- privatekey = load_privatekey(path, passphrase=passphrase, content=content, check_passphrase=False, backend=backend)
-
- if backend == 'pyopenssl':
- 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(
- serialization.Encoding.DER,
- serialization.PublicFormat.SubjectPublicKeyInfo
- )
-
- return get_fingerprint_of_bytes(publickey)
-
-
-def load_file_if_exists(path, module=None, ignore_errors=False):
- try:
- with open(path, 'rb') as f:
- return f.read()
- except EnvironmentError as exc:
- if exc.errno == errno.ENOENT:
- return None
- if ignore_errors:
- return None
- if module is None:
- raise
- module.fail_json('Error while loading {0} - {1}'.format(path, str(exc)))
- except Exception as exc:
- if ignore_errors:
- return None
- if module is None:
- raise
- module.fail_json('Error while loading {0} - {1}'.format(path, str(exc)))
-
-
-def load_privatekey(path, passphrase=None, check_passphrase=True, content=None, backend='pyopenssl'):
- """Load the specified OpenSSL private key.
-
- The content can also be specified via content; in that case,
- this function will not load the key from disk.
- """
-
- try:
- if content is None:
- with open(path, 'rb') as b_priv_key_fh:
- priv_key_detail = b_priv_key_fh.read()
- else:
- priv_key_detail = content
-
- if backend == 'pyopenssl':
-
- # First try: try to load with real passphrase (resp. empty string)
- # Will work if this is the correct passphrase, or the key is not
- # password-protected.
- try:
- result = crypto.load_privatekey(crypto.FILETYPE_PEM,
- priv_key_detail,
- to_bytes(passphrase or ''))
- except crypto.Error as e:
- if len(e.args) > 0 and len(e.args[0]) > 0:
- if e.args[0][0][2] in ('bad decrypt', 'bad password read'):
- # This happens in case we have the wrong passphrase.
- if passphrase is not None:
- raise OpenSSLBadPassphraseError('Wrong passphrase provided for private key!')
- else:
- raise OpenSSLBadPassphraseError('No passphrase provided, but private key is password-protected!')
- raise OpenSSLObjectError('Error while deserializing key: {0}'.format(e))
- if check_passphrase:
- # Next we want to make sure that the key is actually protected by
- # a passphrase (in case we did try the empty string before, make
- # sure that the key is not protected by the empty string)
- try:
- crypto.load_privatekey(crypto.FILETYPE_PEM,
- priv_key_detail,
- to_bytes('y' if passphrase == 'x' else 'x'))
- if passphrase is not None:
- # Since we can load the key without an exception, the
- # key isn't password-protected
- raise OpenSSLBadPassphraseError('Passphrase provided, but private key is not password-protected!')
- except crypto.Error as e:
- if passphrase is None and len(e.args) > 0 and len(e.args[0]) > 0:
- if e.args[0][0][2] in ('bad decrypt', 'bad password read'):
- # The key is obviously protected by the empty string.
- # Don't do this at home (if it's possible at all)...
- raise OpenSSLBadPassphraseError('No passphrase provided, but private key is password-protected!')
- elif backend == 'cryptography':
- try:
- result = load_pem_private_key(priv_key_detail,
- None if passphrase is None else to_bytes(passphrase),
- cryptography_backend())
- except TypeError as dummy:
- raise OpenSSLBadPassphraseError('Wrong or empty passphrase provided for private key')
- except ValueError as dummy:
- raise OpenSSLBadPassphraseError('Wrong passphrase provided for private key')
-
- return result
- except (IOError, OSError) as exc:
- raise OpenSSLObjectError(exc)
-
-
-def load_certificate(path, content=None, backend='pyopenssl'):
- """Load the specified certificate."""
-
- try:
- if content is None:
- with open(path, 'rb') as cert_fh:
- cert_content = cert_fh.read()
- else:
- cert_content = content
- if backend == 'pyopenssl':
- return crypto.load_certificate(crypto.FILETYPE_PEM, cert_content)
- elif backend == 'cryptography':
- return x509.load_pem_x509_certificate(cert_content, cryptography_backend())
- except (IOError, OSError) as exc:
- raise OpenSSLObjectError(exc)
-
-
-def load_certificate_request(path, content=None, backend='pyopenssl'):
- """Load the specified certificate signing request."""
- try:
- if content is None:
- with open(path, 'rb') as csr_fh:
- csr_content = csr_fh.read()
- else:
- csr_content = content
- except (IOError, OSError) as exc:
- raise OpenSSLObjectError(exc)
- if backend == 'pyopenssl':
- return crypto.load_certificate_request(crypto.FILETYPE_PEM, csr_content)
- elif backend == 'cryptography':
- return x509.load_pem_x509_csr(csr_content, cryptography_backend())
-
-
-def parse_name_field(input_dict):
- """Take a dict with key: value or key: list_of_values mappings and return a list of tuples"""
-
- result = []
- for key in input_dict:
- if isinstance(input_dict[key], list):
- for entry in input_dict[key]:
- result.append((key, entry))
- else:
- result.append((key, input_dict[key]))
- return result
-
-
-def convert_relative_to_datetime(relative_time_string):
- """Get a datetime.datetime or None from a string in the time format described in sshd_config(5)"""
-
- parsed_result = re.match(
- r"^(?P<prefix>[+-])((?P<weeks>\d+)[wW])?((?P<days>\d+)[dD])?((?P<hours>\d+)[hH])?((?P<minutes>\d+)[mM])?((?P<seconds>\d+)[sS]?)?$",
- relative_time_string)
-
- if parsed_result is None or len(relative_time_string) == 1:
- # not matched or only a single "+" or "-"
- return None
-
- offset = datetime.timedelta(0)
- if parsed_result.group("weeks") is not None:
- offset += datetime.timedelta(weeks=int(parsed_result.group("weeks")))
- if parsed_result.group("days") is not None:
- offset += datetime.timedelta(days=int(parsed_result.group("days")))
- if parsed_result.group("hours") is not None:
- offset += datetime.timedelta(hours=int(parsed_result.group("hours")))
- if parsed_result.group("minutes") is not None:
- offset += datetime.timedelta(
- minutes=int(parsed_result.group("minutes")))
- if parsed_result.group("seconds") is not None:
- offset += datetime.timedelta(
- seconds=int(parsed_result.group("seconds")))
-
- if parsed_result.group("prefix") == "+":
- return datetime.datetime.utcnow() + offset
- else:
- return datetime.datetime.utcnow() - offset
-
-
-def get_relative_time_option(input_string, input_name, backend='cryptography'):
- """Return an absolute timespec if a relative timespec or an ASN1 formatted
- string is provided.
-
- The return value will be a datetime object for the cryptography backend,
- and a ASN1 formatted string for the pyopenssl backend."""
- result = to_native(input_string)
- if result is None:
- raise OpenSSLObjectError(
- 'The timespec "%s" for %s is not valid' %
- input_string, input_name)
- # Relative time
- if result.startswith("+") or result.startswith("-"):
- result_datetime = convert_relative_to_datetime(result)
- if backend == 'pyopenssl':
- return result_datetime.strftime("%Y%m%d%H%M%SZ")
- elif backend == 'cryptography':
- return result_datetime
- # Absolute time
- if backend == 'pyopenssl':
- 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']:
- try:
- return datetime.datetime.strptime(result, date_fmt)
- except ValueError:
- pass
-
- raise OpenSSLObjectError(
- 'The time spec "%s" for %s is invalid' %
- (input_string, input_name)
- )
-
-
-def select_message_digest(digest_string):
- digest = None
- if digest_string == 'sha256':
- digest = hashes.SHA256()
- elif digest_string == 'sha384':
- digest = hashes.SHA384()
- elif digest_string == 'sha512':
- digest = hashes.SHA512()
- elif digest_string == 'sha1':
- digest = hashes.SHA1()
- elif digest_string == 'md5':
- digest = hashes.MD5()
- return digest
-
-
-def write_file(module, content, default_mode=None, path=None):
- '''
- Writes content into destination file as securely as possible.
- Uses file arguments from module.
- '''
- # Find out parameters for file
- file_args = module.load_file_common_arguments(module.params, path=path)
- if file_args['mode'] is None:
- file_args['mode'] = default_mode
- # Create tempfile name
- tmp_fd, tmp_name = tempfile.mkstemp(prefix=b'.ansible_tmp')
- try:
- os.close(tmp_fd)
- except Exception as dummy:
- pass
- module.add_cleanup_file(tmp_name) # if we fail, let Ansible try to remove the file
- try:
- try:
- # Create tempfile
- file = os.open(tmp_name, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o600)
- os.write(file, content)
- os.close(file)
- except Exception as e:
- try:
- os.remove(tmp_name)
- except Exception as dummy:
- pass
- module.fail_json(msg='Error while writing result into temporary file: {0}'.format(e))
- # Update destination to wanted permissions
- if os.path.exists(file_args['path']):
- module.set_fs_attributes_if_different(file_args, False)
- # Move tempfile to final destination
- module.atomic_move(tmp_name, file_args['path'])
- # Try to update permissions again
- module.set_fs_attributes_if_different(file_args, False)
- except Exception as e:
- try:
- os.remove(tmp_name)
- except Exception as dummy:
- pass
- module.fail_json(msg='Error while writing result: {0}'.format(e))
-
-
-@six.add_metaclass(abc.ABCMeta)
-class OpenSSLObject(object):
-
- def __init__(self, path, state, force, check_mode):
- self.path = path
- self.state = state
- self.force = force
- self.name = os.path.basename(path)
- self.changed = False
- self.check_mode = check_mode
-
- def check(self, module, perms_required=True):
- """Ensure the resource is in its desired state."""
-
- def _check_state():
- return os.path.exists(self.path)
-
- def _check_perms(module):
- file_args = module.load_file_common_arguments(module.params)
- return not module.set_fs_attributes_if_different(file_args, False)
-
- if not perms_required:
- return _check_state()
-
- return _check_state() and _check_perms(module)
-
- @abc.abstractmethod
- def dump(self):
- """Serialize the object into a dictionary."""
-
- pass
-
- @abc.abstractmethod
- def generate(self):
- """Generate the resource."""
-
- pass
-
- def remove(self, module):
- """Remove the resource from the filesystem."""
-
- try:
- os.remove(self.path)
- self.changed = True
- except OSError as exc:
- if exc.errno != errno.ENOENT:
- raise OpenSSLObjectError(exc)
- else:
- pass
-
-
-# #####################################################################################
-# #####################################################################################
-# This has been extracted from the OpenSSL project's objects.txt:
-# https://github.com/openssl/openssl/blob/9537fe5757bb07761fa275d779bbd40bcf5530e4/crypto/objects/objects.txt
-# Extracted with https://gist.github.com/felixfontein/376748017ad65ead093d56a45a5bf376
-#
-# In case the following data structure has any copyrightable content, note that it is licensed as follows:
-# Copyright (c) the OpenSSL contributors
-# Licensed under the Apache License 2.0
-# https://github.com/openssl/openssl/blob/master/LICENSE
-_OID_MAP = {
- '0': ('itu-t', 'ITU-T', 'ccitt'),
- '0.3.4401.5': ('ntt-ds', ),
- '0.3.4401.5.3.1.9': ('camellia', ),
- '0.3.4401.5.3.1.9.1': ('camellia-128-ecb', 'CAMELLIA-128-ECB'),
- '0.3.4401.5.3.1.9.3': ('camellia-128-ofb', 'CAMELLIA-128-OFB'),
- '0.3.4401.5.3.1.9.4': ('camellia-128-cfb', 'CAMELLIA-128-CFB'),
- '0.3.4401.5.3.1.9.6': ('camellia-128-gcm', 'CAMELLIA-128-GCM'),
- '0.3.4401.5.3.1.9.7': ('camellia-128-ccm', 'CAMELLIA-128-CCM'),
- '0.3.4401.5.3.1.9.9': ('camellia-128-ctr', 'CAMELLIA-128-CTR'),
- '0.3.4401.5.3.1.9.10': ('camellia-128-cmac', 'CAMELLIA-128-CMAC'),
- '0.3.4401.5.3.1.9.21': ('camellia-192-ecb', 'CAMELLIA-192-ECB'),
- '0.3.4401.5.3.1.9.23': ('camellia-192-ofb', 'CAMELLIA-192-OFB'),
- '0.3.4401.5.3.1.9.24': ('camellia-192-cfb', 'CAMELLIA-192-CFB'),
- '0.3.4401.5.3.1.9.26': ('camellia-192-gcm', 'CAMELLIA-192-GCM'),
- '0.3.4401.5.3.1.9.27': ('camellia-192-ccm', 'CAMELLIA-192-CCM'),
- '0.3.4401.5.3.1.9.29': ('camellia-192-ctr', 'CAMELLIA-192-CTR'),
- '0.3.4401.5.3.1.9.30': ('camellia-192-cmac', 'CAMELLIA-192-CMAC'),
- '0.3.4401.5.3.1.9.41': ('camellia-256-ecb', 'CAMELLIA-256-ECB'),
- '0.3.4401.5.3.1.9.43': ('camellia-256-ofb', 'CAMELLIA-256-OFB'),
- '0.3.4401.5.3.1.9.44': ('camellia-256-cfb', 'CAMELLIA-256-CFB'),
- '0.3.4401.5.3.1.9.46': ('camellia-256-gcm', 'CAMELLIA-256-GCM'),
- '0.3.4401.5.3.1.9.47': ('camellia-256-ccm', 'CAMELLIA-256-CCM'),
- '0.3.4401.5.3.1.9.49': ('camellia-256-ctr', 'CAMELLIA-256-CTR'),
- '0.3.4401.5.3.1.9.50': ('camellia-256-cmac', 'CAMELLIA-256-CMAC'),
- '0.9': ('data', ),
- '0.9.2342': ('pss', ),
- '0.9.2342.19200300': ('ucl', ),
- '0.9.2342.19200300.100': ('pilot', ),
- '0.9.2342.19200300.100.1': ('pilotAttributeType', ),
- '0.9.2342.19200300.100.1.1': ('userId', 'UID'),
- '0.9.2342.19200300.100.1.2': ('textEncodedORAddress', ),
- '0.9.2342.19200300.100.1.3': ('rfc822Mailbox', 'mail'),
- '0.9.2342.19200300.100.1.4': ('info', ),
- '0.9.2342.19200300.100.1.5': ('favouriteDrink', ),
- '0.9.2342.19200300.100.1.6': ('roomNumber', ),
- '0.9.2342.19200300.100.1.7': ('photo', ),
- '0.9.2342.19200300.100.1.8': ('userClass', ),
- '0.9.2342.19200300.100.1.9': ('host', ),
- '0.9.2342.19200300.100.1.10': ('manager', ),
- '0.9.2342.19200300.100.1.11': ('documentIdentifier', ),
- '0.9.2342.19200300.100.1.12': ('documentTitle', ),
- '0.9.2342.19200300.100.1.13': ('documentVersion', ),
- '0.9.2342.19200300.100.1.14': ('documentAuthor', ),
- '0.9.2342.19200300.100.1.15': ('documentLocation', ),
- '0.9.2342.19200300.100.1.20': ('homeTelephoneNumber', ),
- '0.9.2342.19200300.100.1.21': ('secretary', ),
- '0.9.2342.19200300.100.1.22': ('otherMailbox', ),
- '0.9.2342.19200300.100.1.23': ('lastModifiedTime', ),
- '0.9.2342.19200300.100.1.24': ('lastModifiedBy', ),
- '0.9.2342.19200300.100.1.25': ('domainComponent', 'DC'),
- '0.9.2342.19200300.100.1.26': ('aRecord', ),
- '0.9.2342.19200300.100.1.27': ('pilotAttributeType27', ),
- '0.9.2342.19200300.100.1.28': ('mXRecord', ),
- '0.9.2342.19200300.100.1.29': ('nSRecord', ),
- '0.9.2342.19200300.100.1.30': ('sOARecord', ),
- '0.9.2342.19200300.100.1.31': ('cNAMERecord', ),
- '0.9.2342.19200300.100.1.37': ('associatedDomain', ),
- '0.9.2342.19200300.100.1.38': ('associatedName', ),
- '0.9.2342.19200300.100.1.39': ('homePostalAddress', ),
- '0.9.2342.19200300.100.1.40': ('personalTitle', ),
- '0.9.2342.19200300.100.1.41': ('mobileTelephoneNumber', ),
- '0.9.2342.19200300.100.1.42': ('pagerTelephoneNumber', ),
- '0.9.2342.19200300.100.1.43': ('friendlyCountryName', ),
- '0.9.2342.19200300.100.1.44': ('uniqueIdentifier', 'uid'),
- '0.9.2342.19200300.100.1.45': ('organizationalStatus', ),
- '0.9.2342.19200300.100.1.46': ('janetMailbox', ),
- '0.9.2342.19200300.100.1.47': ('mailPreferenceOption', ),
- '0.9.2342.19200300.100.1.48': ('buildingName', ),
- '0.9.2342.19200300.100.1.49': ('dSAQuality', ),
- '0.9.2342.19200300.100.1.50': ('singleLevelQuality', ),
- '0.9.2342.19200300.100.1.51': ('subtreeMinimumQuality', ),
- '0.9.2342.19200300.100.1.52': ('subtreeMaximumQuality', ),
- '0.9.2342.19200300.100.1.53': ('personalSignature', ),
- '0.9.2342.19200300.100.1.54': ('dITRedirect', ),
- '0.9.2342.19200300.100.1.55': ('audio', ),
- '0.9.2342.19200300.100.1.56': ('documentPublisher', ),
- '0.9.2342.19200300.100.3': ('pilotAttributeSyntax', ),
- '0.9.2342.19200300.100.3.4': ('iA5StringSyntax', ),
- '0.9.2342.19200300.100.3.5': ('caseIgnoreIA5StringSyntax', ),
- '0.9.2342.19200300.100.4': ('pilotObjectClass', ),
- '0.9.2342.19200300.100.4.3': ('pilotObject', ),
- '0.9.2342.19200300.100.4.4': ('pilotPerson', ),
- '0.9.2342.19200300.100.4.5': ('account', ),
- '0.9.2342.19200300.100.4.6': ('document', ),
- '0.9.2342.19200300.100.4.7': ('room', ),
- '0.9.2342.19200300.100.4.9': ('documentSeries', ),
- '0.9.2342.19200300.100.4.13': ('Domain', 'domain'),
- '0.9.2342.19200300.100.4.14': ('rFC822localPart', ),
- '0.9.2342.19200300.100.4.15': ('dNSDomain', ),
- '0.9.2342.19200300.100.4.17': ('domainRelatedObject', ),
- '0.9.2342.19200300.100.4.18': ('friendlyCountry', ),
- '0.9.2342.19200300.100.4.19': ('simpleSecurityObject', ),
- '0.9.2342.19200300.100.4.20': ('pilotOrganization', ),
- '0.9.2342.19200300.100.4.21': ('pilotDSA', ),
- '0.9.2342.19200300.100.4.22': ('qualityLabelledData', ),
- '0.9.2342.19200300.100.10': ('pilotGroups', ),
- '1': ('iso', 'ISO'),
- '1.0.9797.3.4': ('gmac', 'GMAC'),
- '1.0.10118.3.0.55': ('whirlpool', ),
- '1.2': ('ISO Member Body', 'member-body'),
- '1.2.156': ('ISO CN Member Body', 'ISO-CN'),
- '1.2.156.10197': ('oscca', ),
- '1.2.156.10197.1': ('sm-scheme', ),
- '1.2.156.10197.1.104.1': ('sm4-ecb', 'SM4-ECB'),
- '1.2.156.10197.1.104.2': ('sm4-cbc', 'SM4-CBC'),
- '1.2.156.10197.1.104.3': ('sm4-ofb', 'SM4-OFB'),
- '1.2.156.10197.1.104.4': ('sm4-cfb', 'SM4-CFB'),
- '1.2.156.10197.1.104.5': ('sm4-cfb1', 'SM4-CFB1'),
- '1.2.156.10197.1.104.6': ('sm4-cfb8', 'SM4-CFB8'),
- '1.2.156.10197.1.104.7': ('sm4-ctr', 'SM4-CTR'),
- '1.2.156.10197.1.301': ('sm2', 'SM2'),
- '1.2.156.10197.1.401': ('sm3', 'SM3'),
- '1.2.156.10197.1.501': ('SM2-with-SM3', 'SM2-SM3'),
- '1.2.156.10197.1.504': ('sm3WithRSAEncryption', 'RSA-SM3'),
- '1.2.392.200011.61.1.1.1.2': ('camellia-128-cbc', 'CAMELLIA-128-CBC'),
- '1.2.392.200011.61.1.1.1.3': ('camellia-192-cbc', 'CAMELLIA-192-CBC'),
- '1.2.392.200011.61.1.1.1.4': ('camellia-256-cbc', 'CAMELLIA-256-CBC'),
- '1.2.392.200011.61.1.1.3.2': ('id-camellia128-wrap', ),
- '1.2.392.200011.61.1.1.3.3': ('id-camellia192-wrap', ),
- '1.2.392.200011.61.1.1.3.4': ('id-camellia256-wrap', ),
- '1.2.410.200004': ('kisa', 'KISA'),
- '1.2.410.200004.1.3': ('seed-ecb', 'SEED-ECB'),
- '1.2.410.200004.1.4': ('seed-cbc', 'SEED-CBC'),
- '1.2.410.200004.1.5': ('seed-cfb', 'SEED-CFB'),
- '1.2.410.200004.1.6': ('seed-ofb', 'SEED-OFB'),
- '1.2.410.200046.1.1': ('aria', ),
- '1.2.410.200046.1.1.1': ('aria-128-ecb', 'ARIA-128-ECB'),
- '1.2.410.200046.1.1.2': ('aria-128-cbc', 'ARIA-128-CBC'),
- '1.2.410.200046.1.1.3': ('aria-128-cfb', 'ARIA-128-CFB'),
- '1.2.410.200046.1.1.4': ('aria-128-ofb', 'ARIA-128-OFB'),
- '1.2.410.200046.1.1.5': ('aria-128-ctr', 'ARIA-128-CTR'),
- '1.2.410.200046.1.1.6': ('aria-192-ecb', 'ARIA-192-ECB'),
- '1.2.410.200046.1.1.7': ('aria-192-cbc', 'ARIA-192-CBC'),
- '1.2.410.200046.1.1.8': ('aria-192-cfb', 'ARIA-192-CFB'),
- '1.2.410.200046.1.1.9': ('aria-192-ofb', 'ARIA-192-OFB'),
- '1.2.410.200046.1.1.10': ('aria-192-ctr', 'ARIA-192-CTR'),
- '1.2.410.200046.1.1.11': ('aria-256-ecb', 'ARIA-256-ECB'),
- '1.2.410.200046.1.1.12': ('aria-256-cbc', 'ARIA-256-CBC'),
- '1.2.410.200046.1.1.13': ('aria-256-cfb', 'ARIA-256-CFB'),
- '1.2.410.200046.1.1.14': ('aria-256-ofb', 'ARIA-256-OFB'),
- '1.2.410.200046.1.1.15': ('aria-256-ctr', 'ARIA-256-CTR'),
- '1.2.410.200046.1.1.34': ('aria-128-gcm', 'ARIA-128-GCM'),
- '1.2.410.200046.1.1.35': ('aria-192-gcm', 'ARIA-192-GCM'),
- '1.2.410.200046.1.1.36': ('aria-256-gcm', 'ARIA-256-GCM'),
- '1.2.410.200046.1.1.37': ('aria-128-ccm', 'ARIA-128-CCM'),
- '1.2.410.200046.1.1.38': ('aria-192-ccm', 'ARIA-192-CCM'),
- '1.2.410.200046.1.1.39': ('aria-256-ccm', 'ARIA-256-CCM'),
- '1.2.643.2.2': ('cryptopro', ),
- '1.2.643.2.2.3': ('GOST R 34.11-94 with GOST R 34.10-2001', 'id-GostR3411-94-with-GostR3410-2001'),
- '1.2.643.2.2.4': ('GOST R 34.11-94 with GOST R 34.10-94', 'id-GostR3411-94-with-GostR3410-94'),
- '1.2.643.2.2.9': ('GOST R 34.11-94', 'md_gost94'),
- '1.2.643.2.2.10': ('HMAC GOST 34.11-94', 'id-HMACGostR3411-94'),
- '1.2.643.2.2.14.0': ('id-Gost28147-89-None-KeyMeshing', ),
- '1.2.643.2.2.14.1': ('id-Gost28147-89-CryptoPro-KeyMeshing', ),
- '1.2.643.2.2.19': ('GOST R 34.10-2001', 'gost2001'),
- '1.2.643.2.2.20': ('GOST R 34.10-94', 'gost94'),
- '1.2.643.2.2.20.1': ('id-GostR3410-94-a', ),
- '1.2.643.2.2.20.2': ('id-GostR3410-94-aBis', ),
- '1.2.643.2.2.20.3': ('id-GostR3410-94-b', ),
- '1.2.643.2.2.20.4': ('id-GostR3410-94-bBis', ),
- '1.2.643.2.2.21': ('GOST 28147-89', 'gost89'),
- '1.2.643.2.2.22': ('GOST 28147-89 MAC', 'gost-mac'),
- '1.2.643.2.2.23': ('GOST R 34.11-94 PRF', 'prf-gostr3411-94'),
- '1.2.643.2.2.30.0': ('id-GostR3411-94-TestParamSet', ),
- '1.2.643.2.2.30.1': ('id-GostR3411-94-CryptoProParamSet', ),
- '1.2.643.2.2.31.0': ('id-Gost28147-89-TestParamSet', ),
- '1.2.643.2.2.31.1': ('id-Gost28147-89-CryptoPro-A-ParamSet', ),
- '1.2.643.2.2.31.2': ('id-Gost28147-89-CryptoPro-B-ParamSet', ),
- '1.2.643.2.2.31.3': ('id-Gost28147-89-CryptoPro-C-ParamSet', ),
- '1.2.643.2.2.31.4': ('id-Gost28147-89-CryptoPro-D-ParamSet', ),
- '1.2.643.2.2.31.5': ('id-Gost28147-89-CryptoPro-Oscar-1-1-ParamSet', ),
- '1.2.643.2.2.31.6': ('id-Gost28147-89-CryptoPro-Oscar-1-0-ParamSet', ),
- '1.2.643.2.2.31.7': ('id-Gost28147-89-CryptoPro-RIC-1-ParamSet', ),
- '1.2.643.2.2.32.0': ('id-GostR3410-94-TestParamSet', ),
- '1.2.643.2.2.32.2': ('id-GostR3410-94-CryptoPro-A-ParamSet', ),
- '1.2.643.2.2.32.3': ('id-GostR3410-94-CryptoPro-B-ParamSet', ),
- '1.2.643.2.2.32.4': ('id-GostR3410-94-CryptoPro-C-ParamSet', ),
- '1.2.643.2.2.32.5': ('id-GostR3410-94-CryptoPro-D-ParamSet', ),
- '1.2.643.2.2.33.1': ('id-GostR3410-94-CryptoPro-XchA-ParamSet', ),
- '1.2.643.2.2.33.2': ('id-GostR3410-94-CryptoPro-XchB-ParamSet', ),
- '1.2.643.2.2.33.3': ('id-GostR3410-94-CryptoPro-XchC-ParamSet', ),
- '1.2.643.2.2.35.0': ('id-GostR3410-2001-TestParamSet', ),
- '1.2.643.2.2.35.1': ('id-GostR3410-2001-CryptoPro-A-ParamSet', ),
- '1.2.643.2.2.35.2': ('id-GostR3410-2001-CryptoPro-B-ParamSet', ),
- '1.2.643.2.2.35.3': ('id-GostR3410-2001-CryptoPro-C-ParamSet', ),
- '1.2.643.2.2.36.0': ('id-GostR3410-2001-CryptoPro-XchA-ParamSet', ),
- '1.2.643.2.2.36.1': ('id-GostR3410-2001-CryptoPro-XchB-ParamSet', ),
- '1.2.643.2.2.98': ('GOST R 34.10-2001 DH', 'id-GostR3410-2001DH'),
- '1.2.643.2.2.99': ('GOST R 34.10-94 DH', 'id-GostR3410-94DH'),
- '1.2.643.2.9': ('cryptocom', ),
- '1.2.643.2.9.1.3.3': ('GOST R 34.11-94 with GOST R 34.10-94 Cryptocom', 'id-GostR3411-94-with-GostR3410-94-cc'),
- '1.2.643.2.9.1.3.4': ('GOST R 34.11-94 with GOST R 34.10-2001 Cryptocom', 'id-GostR3411-94-with-GostR3410-2001-cc'),
- '1.2.643.2.9.1.5.3': ('GOST 34.10-94 Cryptocom', 'gost94cc'),
- '1.2.643.2.9.1.5.4': ('GOST 34.10-2001 Cryptocom', 'gost2001cc'),
- '1.2.643.2.9.1.6.1': ('GOST 28147-89 Cryptocom ParamSet', 'id-Gost28147-89-cc'),
- '1.2.643.2.9.1.8.1': ('GOST R 3410-2001 Parameter Set Cryptocom', 'id-GostR3410-2001-ParamSet-cc'),
- '1.2.643.3.131.1.1': ('INN', 'INN'),
- '1.2.643.7.1': ('id-tc26', ),
- '1.2.643.7.1.1': ('id-tc26-algorithms', ),
- '1.2.643.7.1.1.1': ('id-tc26-sign', ),
- '1.2.643.7.1.1.1.1': ('GOST R 34.10-2012 with 256 bit modulus', 'gost2012_256'),
- '1.2.643.7.1.1.1.2': ('GOST R 34.10-2012 with 512 bit modulus', 'gost2012_512'),
- '1.2.643.7.1.1.2': ('id-tc26-digest', ),
- '1.2.643.7.1.1.2.2': ('GOST R 34.11-2012 with 256 bit hash', 'md_gost12_256'),
- '1.2.643.7.1.1.2.3': ('GOST R 34.11-2012 with 512 bit hash', 'md_gost12_512'),
- '1.2.643.7.1.1.3': ('id-tc26-signwithdigest', ),
- '1.2.643.7.1.1.3.2': ('GOST R 34.10-2012 with GOST R 34.11-2012 (256 bit)', 'id-tc26-signwithdigest-gost3410-2012-256'),
- '1.2.643.7.1.1.3.3': ('GOST R 34.10-2012 with GOST R 34.11-2012 (512 bit)', 'id-tc26-signwithdigest-gost3410-2012-512'),
- '1.2.643.7.1.1.4': ('id-tc26-mac', ),
- '1.2.643.7.1.1.4.1': ('HMAC GOST 34.11-2012 256 bit', 'id-tc26-hmac-gost-3411-2012-256'),
- '1.2.643.7.1.1.4.2': ('HMAC GOST 34.11-2012 512 bit', 'id-tc26-hmac-gost-3411-2012-512'),
- '1.2.643.7.1.1.5': ('id-tc26-cipher', ),
- '1.2.643.7.1.1.5.1': ('id-tc26-cipher-gostr3412-2015-magma', ),
- '1.2.643.7.1.1.5.1.1': ('id-tc26-cipher-gostr3412-2015-magma-ctracpkm', ),
- '1.2.643.7.1.1.5.1.2': ('id-tc26-cipher-gostr3412-2015-magma-ctracpkm-omac', ),
- '1.2.643.7.1.1.5.2': ('id-tc26-cipher-gostr3412-2015-kuznyechik', ),
- '1.2.643.7.1.1.5.2.1': ('id-tc26-cipher-gostr3412-2015-kuznyechik-ctracpkm', ),
- '1.2.643.7.1.1.5.2.2': ('id-tc26-cipher-gostr3412-2015-kuznyechik-ctracpkm-omac', ),
- '1.2.643.7.1.1.6': ('id-tc26-agreement', ),
- '1.2.643.7.1.1.6.1': ('id-tc26-agreement-gost-3410-2012-256', ),
- '1.2.643.7.1.1.6.2': ('id-tc26-agreement-gost-3410-2012-512', ),
- '1.2.643.7.1.1.7': ('id-tc26-wrap', ),
- '1.2.643.7.1.1.7.1': ('id-tc26-wrap-gostr3412-2015-magma', ),
- '1.2.643.7.1.1.7.1.1': ('id-tc26-wrap-gostr3412-2015-magma-kexp15', 'id-tc26-wrap-gostr3412-2015-kuznyechik-kexp15'),
- '1.2.643.7.1.1.7.2': ('id-tc26-wrap-gostr3412-2015-kuznyechik', ),
- '1.2.643.7.1.2': ('id-tc26-constants', ),
- '1.2.643.7.1.2.1': ('id-tc26-sign-constants', ),
- '1.2.643.7.1.2.1.1': ('id-tc26-gost-3410-2012-256-constants', ),
- '1.2.643.7.1.2.1.1.1': ('GOST R 34.10-2012 (256 bit) ParamSet A', 'id-tc26-gost-3410-2012-256-paramSetA'),
- '1.2.643.7.1.2.1.1.2': ('GOST R 34.10-2012 (256 bit) ParamSet B', 'id-tc26-gost-3410-2012-256-paramSetB'),
- '1.2.643.7.1.2.1.1.3': ('GOST R 34.10-2012 (256 bit) ParamSet C', 'id-tc26-gost-3410-2012-256-paramSetC'),
- '1.2.643.7.1.2.1.1.4': ('GOST R 34.10-2012 (256 bit) ParamSet D', 'id-tc26-gost-3410-2012-256-paramSetD'),
- '1.2.643.7.1.2.1.2': ('id-tc26-gost-3410-2012-512-constants', ),
- '1.2.643.7.1.2.1.2.0': ('GOST R 34.10-2012 (512 bit) testing parameter set', 'id-tc26-gost-3410-2012-512-paramSetTest'),
- '1.2.643.7.1.2.1.2.1': ('GOST R 34.10-2012 (512 bit) ParamSet A', 'id-tc26-gost-3410-2012-512-paramSetA'),
- '1.2.643.7.1.2.1.2.2': ('GOST R 34.10-2012 (512 bit) ParamSet B', 'id-tc26-gost-3410-2012-512-paramSetB'),
- '1.2.643.7.1.2.1.2.3': ('GOST R 34.10-2012 (512 bit) ParamSet C', 'id-tc26-gost-3410-2012-512-paramSetC'),
- '1.2.643.7.1.2.2': ('id-tc26-digest-constants', ),
- '1.2.643.7.1.2.5': ('id-tc26-cipher-constants', ),
- '1.2.643.7.1.2.5.1': ('id-tc26-gost-28147-constants', ),
- '1.2.643.7.1.2.5.1.1': ('GOST 28147-89 TC26 parameter set', 'id-tc26-gost-28147-param-Z'),
- '1.2.643.100.1': ('OGRN', 'OGRN'),
- '1.2.643.100.3': ('SNILS', 'SNILS'),
- '1.2.643.100.111': ('Signing Tool of Subject', 'subjectSignTool'),
- '1.2.643.100.112': ('Signing Tool of Issuer', 'issuerSignTool'),
- '1.2.804': ('ISO-UA', ),
- '1.2.804.2.1.1.1': ('ua-pki', ),
- '1.2.804.2.1.1.1.1.1.1': ('DSTU Gost 28147-2009', 'dstu28147'),
- '1.2.804.2.1.1.1.1.1.1.2': ('DSTU Gost 28147-2009 OFB mode', 'dstu28147-ofb'),
- '1.2.804.2.1.1.1.1.1.1.3': ('DSTU Gost 28147-2009 CFB mode', 'dstu28147-cfb'),
- '1.2.804.2.1.1.1.1.1.1.5': ('DSTU Gost 28147-2009 key wrap', 'dstu28147-wrap'),
- '1.2.804.2.1.1.1.1.1.2': ('HMAC DSTU Gost 34311-95', 'hmacWithDstu34311'),
- '1.2.804.2.1.1.1.1.2.1': ('DSTU Gost 34311-95', 'dstu34311'),
- '1.2.804.2.1.1.1.1.3.1.1': ('DSTU 4145-2002 little endian', 'dstu4145le'),
- '1.2.804.2.1.1.1.1.3.1.1.1.1': ('DSTU 4145-2002 big endian', 'dstu4145be'),
- '1.2.804.2.1.1.1.1.3.1.1.2.0': ('DSTU curve 0', 'uacurve0'),
- '1.2.804.2.1.1.1.1.3.1.1.2.1': ('DSTU curve 1', 'uacurve1'),
- '1.2.804.2.1.1.1.1.3.1.1.2.2': ('DSTU curve 2', 'uacurve2'),
- '1.2.804.2.1.1.1.1.3.1.1.2.3': ('DSTU curve 3', 'uacurve3'),
- '1.2.804.2.1.1.1.1.3.1.1.2.4': ('DSTU curve 4', 'uacurve4'),
- '1.2.804.2.1.1.1.1.3.1.1.2.5': ('DSTU curve 5', 'uacurve5'),
- '1.2.804.2.1.1.1.1.3.1.1.2.6': ('DSTU curve 6', 'uacurve6'),
- '1.2.804.2.1.1.1.1.3.1.1.2.7': ('DSTU curve 7', 'uacurve7'),
- '1.2.804.2.1.1.1.1.3.1.1.2.8': ('DSTU curve 8', 'uacurve8'),
- '1.2.804.2.1.1.1.1.3.1.1.2.9': ('DSTU curve 9', 'uacurve9'),
- '1.2.840': ('ISO US Member Body', 'ISO-US'),
- '1.2.840.10040': ('X9.57', 'X9-57'),
- '1.2.840.10040.2': ('holdInstruction', ),
- '1.2.840.10040.2.1': ('Hold Instruction None', 'holdInstructionNone'),
- '1.2.840.10040.2.2': ('Hold Instruction Call Issuer', 'holdInstructionCallIssuer'),
- '1.2.840.10040.2.3': ('Hold Instruction Reject', 'holdInstructionReject'),
- '1.2.840.10040.4': ('X9.57 CM ?', 'X9cm'),
- '1.2.840.10040.4.1': ('dsaEncryption', 'DSA'),
- '1.2.840.10040.4.3': ('dsaWithSHA1', 'DSA-SHA1'),
- '1.2.840.10045': ('ANSI X9.62', 'ansi-X9-62'),
- '1.2.840.10045.1': ('id-fieldType', ),
- '1.2.840.10045.1.1': ('prime-field', ),
- '1.2.840.10045.1.2': ('characteristic-two-field', ),
- '1.2.840.10045.1.2.3': ('id-characteristic-two-basis', ),
- '1.2.840.10045.1.2.3.1': ('onBasis', ),
- '1.2.840.10045.1.2.3.2': ('tpBasis', ),
- '1.2.840.10045.1.2.3.3': ('ppBasis', ),
- '1.2.840.10045.2': ('id-publicKeyType', ),
- '1.2.840.10045.2.1': ('id-ecPublicKey', ),
- '1.2.840.10045.3': ('ellipticCurve', ),
- '1.2.840.10045.3.0': ('c-TwoCurve', ),
- '1.2.840.10045.3.0.1': ('c2pnb163v1', ),
- '1.2.840.10045.3.0.2': ('c2pnb163v2', ),
- '1.2.840.10045.3.0.3': ('c2pnb163v3', ),
- '1.2.840.10045.3.0.4': ('c2pnb176v1', ),
- '1.2.840.10045.3.0.5': ('c2tnb191v1', ),
- '1.2.840.10045.3.0.6': ('c2tnb191v2', ),
- '1.2.840.10045.3.0.7': ('c2tnb191v3', ),
- '1.2.840.10045.3.0.8': ('c2onb191v4', ),
- '1.2.840.10045.3.0.9': ('c2onb191v5', ),
- '1.2.840.10045.3.0.10': ('c2pnb208w1', ),
- '1.2.840.10045.3.0.11': ('c2tnb239v1', ),
- '1.2.840.10045.3.0.12': ('c2tnb239v2', ),
- '1.2.840.10045.3.0.13': ('c2tnb239v3', ),
- '1.2.840.10045.3.0.14': ('c2onb239v4', ),
- '1.2.840.10045.3.0.15': ('c2onb239v5', ),
- '1.2.840.10045.3.0.16': ('c2pnb272w1', ),
- '1.2.840.10045.3.0.17': ('c2pnb304w1', ),
- '1.2.840.10045.3.0.18': ('c2tnb359v1', ),
- '1.2.840.10045.3.0.19': ('c2pnb368w1', ),
- '1.2.840.10045.3.0.20': ('c2tnb431r1', ),
- '1.2.840.10045.3.1': ('primeCurve', ),
- '1.2.840.10045.3.1.1': ('prime192v1', ),
- '1.2.840.10045.3.1.2': ('prime192v2', ),
- '1.2.840.10045.3.1.3': ('prime192v3', ),
- '1.2.840.10045.3.1.4': ('prime239v1', ),
- '1.2.840.10045.3.1.5': ('prime239v2', ),
- '1.2.840.10045.3.1.6': ('prime239v3', ),
- '1.2.840.10045.3.1.7': ('prime256v1', ),
- '1.2.840.10045.4': ('id-ecSigType', ),
- '1.2.840.10045.4.1': ('ecdsa-with-SHA1', ),
- '1.2.840.10045.4.2': ('ecdsa-with-Recommended', ),
- '1.2.840.10045.4.3': ('ecdsa-with-Specified', ),
- '1.2.840.10045.4.3.1': ('ecdsa-with-SHA224', ),
- '1.2.840.10045.4.3.2': ('ecdsa-with-SHA256', ),
- '1.2.840.10045.4.3.3': ('ecdsa-with-SHA384', ),
- '1.2.840.10045.4.3.4': ('ecdsa-with-SHA512', ),
- '1.2.840.10046.2.1': ('X9.42 DH', 'dhpublicnumber'),
- '1.2.840.113533.7.66.10': ('cast5-cbc', 'CAST5-CBC'),
- '1.2.840.113533.7.66.12': ('pbeWithMD5AndCast5CBC', ),
- '1.2.840.113533.7.66.13': ('password based MAC', 'id-PasswordBasedMAC'),
- '1.2.840.113533.7.66.30': ('Diffie-Hellman based MAC', 'id-DHBasedMac'),
- '1.2.840.113549': ('RSA Data Security, Inc.', 'rsadsi'),
- '1.2.840.113549.1': ('RSA Data Security, Inc. PKCS', 'pkcs'),
- '1.2.840.113549.1.1': ('pkcs1', ),
- '1.2.840.113549.1.1.1': ('rsaEncryption', ),
- '1.2.840.113549.1.1.2': ('md2WithRSAEncryption', 'RSA-MD2'),
- '1.2.840.113549.1.1.3': ('md4WithRSAEncryption', 'RSA-MD4'),
- '1.2.840.113549.1.1.4': ('md5WithRSAEncryption', 'RSA-MD5'),
- '1.2.840.113549.1.1.5': ('sha1WithRSAEncryption', 'RSA-SHA1'),
- '1.2.840.113549.1.1.6': ('rsaOAEPEncryptionSET', ),
- '1.2.840.113549.1.1.7': ('rsaesOaep', 'RSAES-OAEP'),
- '1.2.840.113549.1.1.8': ('mgf1', 'MGF1'),
- '1.2.840.113549.1.1.9': ('pSpecified', 'PSPECIFIED'),
- '1.2.840.113549.1.1.10': ('rsassaPss', 'RSASSA-PSS'),
- '1.2.840.113549.1.1.11': ('sha256WithRSAEncryption', 'RSA-SHA256'),
- '1.2.840.113549.1.1.12': ('sha384WithRSAEncryption', 'RSA-SHA384'),
- '1.2.840.113549.1.1.13': ('sha512WithRSAEncryption', 'RSA-SHA512'),
- '1.2.840.113549.1.1.14': ('sha224WithRSAEncryption', 'RSA-SHA224'),
- '1.2.840.113549.1.1.15': ('sha512-224WithRSAEncryption', 'RSA-SHA512/224'),
- '1.2.840.113549.1.1.16': ('sha512-256WithRSAEncryption', 'RSA-SHA512/256'),
- '1.2.840.113549.1.3': ('pkcs3', ),
- '1.2.840.113549.1.3.1': ('dhKeyAgreement', ),
- '1.2.840.113549.1.5': ('pkcs5', ),
- '1.2.840.113549.1.5.1': ('pbeWithMD2AndDES-CBC', 'PBE-MD2-DES'),
- '1.2.840.113549.1.5.3': ('pbeWithMD5AndDES-CBC', 'PBE-MD5-DES'),
- '1.2.840.113549.1.5.4': ('pbeWithMD2AndRC2-CBC', 'PBE-MD2-RC2-64'),
- '1.2.840.113549.1.5.6': ('pbeWithMD5AndRC2-CBC', 'PBE-MD5-RC2-64'),
- '1.2.840.113549.1.5.10': ('pbeWithSHA1AndDES-CBC', 'PBE-SHA1-DES'),
- '1.2.840.113549.1.5.11': ('pbeWithSHA1AndRC2-CBC', 'PBE-SHA1-RC2-64'),
- '1.2.840.113549.1.5.12': ('PBKDF2', ),
- '1.2.840.113549.1.5.13': ('PBES2', ),
- '1.2.840.113549.1.5.14': ('PBMAC1', ),
- '1.2.840.113549.1.7': ('pkcs7', ),
- '1.2.840.113549.1.7.1': ('pkcs7-data', ),
- '1.2.840.113549.1.7.2': ('pkcs7-signedData', ),
- '1.2.840.113549.1.7.3': ('pkcs7-envelopedData', ),
- '1.2.840.113549.1.7.4': ('pkcs7-signedAndEnvelopedData', ),
- '1.2.840.113549.1.7.5': ('pkcs7-digestData', ),
- '1.2.840.113549.1.7.6': ('pkcs7-encryptedData', ),
- '1.2.840.113549.1.9': ('pkcs9', ),
- '1.2.840.113549.1.9.1': ('emailAddress', ),
- '1.2.840.113549.1.9.2': ('unstructuredName', ),
- '1.2.840.113549.1.9.3': ('contentType', ),
- '1.2.840.113549.1.9.4': ('messageDigest', ),
- '1.2.840.113549.1.9.5': ('signingTime', ),
- '1.2.840.113549.1.9.6': ('countersignature', ),
- '1.2.840.113549.1.9.7': ('challengePassword', ),
- '1.2.840.113549.1.9.8': ('unstructuredAddress', ),
- '1.2.840.113549.1.9.9': ('extendedCertificateAttributes', ),
- '1.2.840.113549.1.9.14': ('Extension Request', 'extReq'),
- '1.2.840.113549.1.9.15': ('S/MIME Capabilities', 'SMIME-CAPS'),
- '1.2.840.113549.1.9.16': ('S/MIME', 'SMIME'),
- '1.2.840.113549.1.9.16.0': ('id-smime-mod', ),
- '1.2.840.113549.1.9.16.0.1': ('id-smime-mod-cms', ),
- '1.2.840.113549.1.9.16.0.2': ('id-smime-mod-ess', ),
- '1.2.840.113549.1.9.16.0.3': ('id-smime-mod-oid', ),
- '1.2.840.113549.1.9.16.0.4': ('id-smime-mod-msg-v3', ),
- '1.2.840.113549.1.9.16.0.5': ('id-smime-mod-ets-eSignature-88', ),
- '1.2.840.113549.1.9.16.0.6': ('id-smime-mod-ets-eSignature-97', ),
- '1.2.840.113549.1.9.16.0.7': ('id-smime-mod-ets-eSigPolicy-88', ),
- '1.2.840.113549.1.9.16.0.8': ('id-smime-mod-ets-eSigPolicy-97', ),
- '1.2.840.113549.1.9.16.1': ('id-smime-ct', ),
- '1.2.840.113549.1.9.16.1.1': ('id-smime-ct-receipt', ),
- '1.2.840.113549.1.9.16.1.2': ('id-smime-ct-authData', ),
- '1.2.840.113549.1.9.16.1.3': ('id-smime-ct-publishCert', ),
- '1.2.840.113549.1.9.16.1.4': ('id-smime-ct-TSTInfo', ),
- '1.2.840.113549.1.9.16.1.5': ('id-smime-ct-TDTInfo', ),
- '1.2.840.113549.1.9.16.1.6': ('id-smime-ct-contentInfo', ),
- '1.2.840.113549.1.9.16.1.7': ('id-smime-ct-DVCSRequestData', ),
- '1.2.840.113549.1.9.16.1.8': ('id-smime-ct-DVCSResponseData', ),
- '1.2.840.113549.1.9.16.1.9': ('id-smime-ct-compressedData', ),
- '1.2.840.113549.1.9.16.1.19': ('id-smime-ct-contentCollection', ),
- '1.2.840.113549.1.9.16.1.23': ('id-smime-ct-authEnvelopedData', ),
- '1.2.840.113549.1.9.16.1.27': ('id-ct-asciiTextWithCRLF', ),
- '1.2.840.113549.1.9.16.1.28': ('id-ct-xml', ),
- '1.2.840.113549.1.9.16.2': ('id-smime-aa', ),
- '1.2.840.113549.1.9.16.2.1': ('id-smime-aa-receiptRequest', ),
- '1.2.840.113549.1.9.16.2.2': ('id-smime-aa-securityLabel', ),
- '1.2.840.113549.1.9.16.2.3': ('id-smime-aa-mlExpandHistory', ),
- '1.2.840.113549.1.9.16.2.4': ('id-smime-aa-contentHint', ),
- '1.2.840.113549.1.9.16.2.5': ('id-smime-aa-msgSigDigest', ),
- '1.2.840.113549.1.9.16.2.6': ('id-smime-aa-encapContentType', ),
- '1.2.840.113549.1.9.16.2.7': ('id-smime-aa-contentIdentifier', ),
- '1.2.840.113549.1.9.16.2.8': ('id-smime-aa-macValue', ),
- '1.2.840.113549.1.9.16.2.9': ('id-smime-aa-equivalentLabels', ),
- '1.2.840.113549.1.9.16.2.10': ('id-smime-aa-contentReference', ),
- '1.2.840.113549.1.9.16.2.11': ('id-smime-aa-encrypKeyPref', ),
- '1.2.840.113549.1.9.16.2.12': ('id-smime-aa-signingCertificate', ),
- '1.2.840.113549.1.9.16.2.13': ('id-smime-aa-smimeEncryptCerts', ),
- '1.2.840.113549.1.9.16.2.14': ('id-smime-aa-timeStampToken', ),
- '1.2.840.113549.1.9.16.2.15': ('id-smime-aa-ets-sigPolicyId', ),
- '1.2.840.113549.1.9.16.2.16': ('id-smime-aa-ets-commitmentType', ),
- '1.2.840.113549.1.9.16.2.17': ('id-smime-aa-ets-signerLocation', ),
- '1.2.840.113549.1.9.16.2.18': ('id-smime-aa-ets-signerAttr', ),
- '1.2.840.113549.1.9.16.2.19': ('id-smime-aa-ets-otherSigCert', ),
- '1.2.840.113549.1.9.16.2.20': ('id-smime-aa-ets-contentTimestamp', ),
- '1.2.840.113549.1.9.16.2.21': ('id-smime-aa-ets-CertificateRefs', ),
- '1.2.840.113549.1.9.16.2.22': ('id-smime-aa-ets-RevocationRefs', ),
- '1.2.840.113549.1.9.16.2.23': ('id-smime-aa-ets-certValues', ),
- '1.2.840.113549.1.9.16.2.24': ('id-smime-aa-ets-revocationValues', ),
- '1.2.840.113549.1.9.16.2.25': ('id-smime-aa-ets-escTimeStamp', ),
- '1.2.840.113549.1.9.16.2.26': ('id-smime-aa-ets-certCRLTimestamp', ),
- '1.2.840.113549.1.9.16.2.27': ('id-smime-aa-ets-archiveTimeStamp', ),
- '1.2.840.113549.1.9.16.2.28': ('id-smime-aa-signatureType', ),
- '1.2.840.113549.1.9.16.2.29': ('id-smime-aa-dvcs-dvc', ),
- '1.2.840.113549.1.9.16.2.47': ('id-smime-aa-signingCertificateV2', ),
- '1.2.840.113549.1.9.16.3': ('id-smime-alg', ),
- '1.2.840.113549.1.9.16.3.1': ('id-smime-alg-ESDHwith3DES', ),
- '1.2.840.113549.1.9.16.3.2': ('id-smime-alg-ESDHwithRC2', ),
- '1.2.840.113549.1.9.16.3.3': ('id-smime-alg-3DESwrap', ),
- '1.2.840.113549.1.9.16.3.4': ('id-smime-alg-RC2wrap', ),
- '1.2.840.113549.1.9.16.3.5': ('id-smime-alg-ESDH', ),
- '1.2.840.113549.1.9.16.3.6': ('id-smime-alg-CMS3DESwrap', ),
- '1.2.840.113549.1.9.16.3.7': ('id-smime-alg-CMSRC2wrap', ),
- '1.2.840.113549.1.9.16.3.8': ('zlib compression', 'ZLIB'),
- '1.2.840.113549.1.9.16.3.9': ('id-alg-PWRI-KEK', ),
- '1.2.840.113549.1.9.16.4': ('id-smime-cd', ),
- '1.2.840.113549.1.9.16.4.1': ('id-smime-cd-ldap', ),
- '1.2.840.113549.1.9.16.5': ('id-smime-spq', ),
- '1.2.840.113549.1.9.16.5.1': ('id-smime-spq-ets-sqt-uri', ),
- '1.2.840.113549.1.9.16.5.2': ('id-smime-spq-ets-sqt-unotice', ),
- '1.2.840.113549.1.9.16.6': ('id-smime-cti', ),
- '1.2.840.113549.1.9.16.6.1': ('id-smime-cti-ets-proofOfOrigin', ),
- '1.2.840.113549.1.9.16.6.2': ('id-smime-cti-ets-proofOfReceipt', ),
- '1.2.840.113549.1.9.16.6.3': ('id-smime-cti-ets-proofOfDelivery', ),
- '1.2.840.113549.1.9.16.6.4': ('id-smime-cti-ets-proofOfSender', ),
- '1.2.840.113549.1.9.16.6.5': ('id-smime-cti-ets-proofOfApproval', ),
- '1.2.840.113549.1.9.16.6.6': ('id-smime-cti-ets-proofOfCreation', ),
- '1.2.840.113549.1.9.20': ('friendlyName', ),
- '1.2.840.113549.1.9.21': ('localKeyID', ),
- '1.2.840.113549.1.9.22': ('certTypes', ),
- '1.2.840.113549.1.9.22.1': ('x509Certificate', ),
- '1.2.840.113549.1.9.22.2': ('sdsiCertificate', ),
- '1.2.840.113549.1.9.23': ('crlTypes', ),
- '1.2.840.113549.1.9.23.1': ('x509Crl', ),
- '1.2.840.113549.1.12': ('pkcs12', ),
- '1.2.840.113549.1.12.1': ('pkcs12-pbeids', ),
- '1.2.840.113549.1.12.1.1': ('pbeWithSHA1And128BitRC4', 'PBE-SHA1-RC4-128'),
- '1.2.840.113549.1.12.1.2': ('pbeWithSHA1And40BitRC4', 'PBE-SHA1-RC4-40'),
- '1.2.840.113549.1.12.1.3': ('pbeWithSHA1And3-KeyTripleDES-CBC', 'PBE-SHA1-3DES'),
- '1.2.840.113549.1.12.1.4': ('pbeWithSHA1And2-KeyTripleDES-CBC', 'PBE-SHA1-2DES'),
- '1.2.840.113549.1.12.1.5': ('pbeWithSHA1And128BitRC2-CBC', 'PBE-SHA1-RC2-128'),
- '1.2.840.113549.1.12.1.6': ('pbeWithSHA1And40BitRC2-CBC', 'PBE-SHA1-RC2-40'),
- '1.2.840.113549.1.12.10': ('pkcs12-Version1', ),
- '1.2.840.113549.1.12.10.1': ('pkcs12-BagIds', ),
- '1.2.840.113549.1.12.10.1.1': ('keyBag', ),
- '1.2.840.113549.1.12.10.1.2': ('pkcs8ShroudedKeyBag', ),
- '1.2.840.113549.1.12.10.1.3': ('certBag', ),
- '1.2.840.113549.1.12.10.1.4': ('crlBag', ),
- '1.2.840.113549.1.12.10.1.5': ('secretBag', ),
- '1.2.840.113549.1.12.10.1.6': ('safeContentsBag', ),
- '1.2.840.113549.2.2': ('md2', 'MD2'),
- '1.2.840.113549.2.4': ('md4', 'MD4'),
- '1.2.840.113549.2.5': ('md5', 'MD5'),
- '1.2.840.113549.2.6': ('hmacWithMD5', ),
- '1.2.840.113549.2.7': ('hmacWithSHA1', ),
- '1.2.840.113549.2.8': ('hmacWithSHA224', ),
- '1.2.840.113549.2.9': ('hmacWithSHA256', ),
- '1.2.840.113549.2.10': ('hmacWithSHA384', ),
- '1.2.840.113549.2.11': ('hmacWithSHA512', ),
- '1.2.840.113549.2.12': ('hmacWithSHA512-224', ),
- '1.2.840.113549.2.13': ('hmacWithSHA512-256', ),
- '1.2.840.113549.3.2': ('rc2-cbc', 'RC2-CBC'),
- '1.2.840.113549.3.4': ('rc4', 'RC4'),
- '1.2.840.113549.3.7': ('des-ede3-cbc', 'DES-EDE3-CBC'),
- '1.2.840.113549.3.8': ('rc5-cbc', 'RC5-CBC'),
- '1.2.840.113549.3.10': ('des-cdmf', 'DES-CDMF'),
- '1.3': ('identified-organization', 'org', 'ORG'),
- '1.3.6': ('dod', 'DOD'),
- '1.3.6.1': ('iana', 'IANA', 'internet'),
- '1.3.6.1.1': ('Directory', 'directory'),
- '1.3.6.1.2': ('Management', 'mgmt'),
- '1.3.6.1.3': ('Experimental', 'experimental'),
- '1.3.6.1.4': ('Private', 'private'),
- '1.3.6.1.4.1': ('Enterprises', 'enterprises'),
- '1.3.6.1.4.1.188.7.1.1.2': ('idea-cbc', 'IDEA-CBC'),
- '1.3.6.1.4.1.311.2.1.14': ('Microsoft Extension Request', 'msExtReq'),
- '1.3.6.1.4.1.311.2.1.21': ('Microsoft Individual Code Signing', 'msCodeInd'),
- '1.3.6.1.4.1.311.2.1.22': ('Microsoft Commercial Code Signing', 'msCodeCom'),
- '1.3.6.1.4.1.311.10.3.1': ('Microsoft Trust List Signing', 'msCTLSign'),
- '1.3.6.1.4.1.311.10.3.3': ('Microsoft Server Gated Crypto', 'msSGC'),
- '1.3.6.1.4.1.311.10.3.4': ('Microsoft Encrypted File System', 'msEFS'),
- '1.3.6.1.4.1.311.17.1': ('Microsoft CSP Name', 'CSPName'),
- '1.3.6.1.4.1.311.17.2': ('Microsoft Local Key set', 'LocalKeySet'),
- '1.3.6.1.4.1.311.20.2.2': ('Microsoft Smartcardlogin', 'msSmartcardLogin'),
- '1.3.6.1.4.1.311.20.2.3': ('Microsoft Universal Principal Name', 'msUPN'),
- '1.3.6.1.4.1.311.60.2.1.1': ('jurisdictionLocalityName', 'jurisdictionL'),
- '1.3.6.1.4.1.311.60.2.1.2': ('jurisdictionStateOrProvinceName', 'jurisdictionST'),
- '1.3.6.1.4.1.311.60.2.1.3': ('jurisdictionCountryName', 'jurisdictionC'),
- '1.3.6.1.4.1.1466.344': ('dcObject', 'dcobject'),
- '1.3.6.1.4.1.1722.12.2.1.16': ('blake2b512', 'BLAKE2b512'),
- '1.3.6.1.4.1.1722.12.2.2.8': ('blake2s256', 'BLAKE2s256'),
- '1.3.6.1.4.1.3029.1.2': ('bf-cbc', 'BF-CBC'),
- '1.3.6.1.4.1.11129.2.4.2': ('CT Precertificate SCTs', 'ct_precert_scts'),
- '1.3.6.1.4.1.11129.2.4.3': ('CT Precertificate Poison', 'ct_precert_poison'),
- '1.3.6.1.4.1.11129.2.4.4': ('CT Precertificate Signer', 'ct_precert_signer'),
- '1.3.6.1.4.1.11129.2.4.5': ('CT Certificate SCTs', 'ct_cert_scts'),
- '1.3.6.1.4.1.11591.4.11': ('scrypt', 'id-scrypt'),
- '1.3.6.1.5': ('Security', 'security'),
- '1.3.6.1.5.2.3': ('id-pkinit', ),
- '1.3.6.1.5.2.3.4': ('PKINIT Client Auth', 'pkInitClientAuth'),
- '1.3.6.1.5.2.3.5': ('Signing KDC Response', 'pkInitKDC'),
- '1.3.6.1.5.5.7': ('PKIX', ),
- '1.3.6.1.5.5.7.0': ('id-pkix-mod', ),
- '1.3.6.1.5.5.7.0.1': ('id-pkix1-explicit-88', ),
- '1.3.6.1.5.5.7.0.2': ('id-pkix1-implicit-88', ),
- '1.3.6.1.5.5.7.0.3': ('id-pkix1-explicit-93', ),
- '1.3.6.1.5.5.7.0.4': ('id-pkix1-implicit-93', ),
- '1.3.6.1.5.5.7.0.5': ('id-mod-crmf', ),
- '1.3.6.1.5.5.7.0.6': ('id-mod-cmc', ),
- '1.3.6.1.5.5.7.0.7': ('id-mod-kea-profile-88', ),
- '1.3.6.1.5.5.7.0.8': ('id-mod-kea-profile-93', ),
- '1.3.6.1.5.5.7.0.9': ('id-mod-cmp', ),
- '1.3.6.1.5.5.7.0.10': ('id-mod-qualified-cert-88', ),
- '1.3.6.1.5.5.7.0.11': ('id-mod-qualified-cert-93', ),
- '1.3.6.1.5.5.7.0.12': ('id-mod-attribute-cert', ),
- '1.3.6.1.5.5.7.0.13': ('id-mod-timestamp-protocol', ),
- '1.3.6.1.5.5.7.0.14': ('id-mod-ocsp', ),
- '1.3.6.1.5.5.7.0.15': ('id-mod-dvcs', ),
- '1.3.6.1.5.5.7.0.16': ('id-mod-cmp2000', ),
- '1.3.6.1.5.5.7.1': ('id-pe', ),
- '1.3.6.1.5.5.7.1.1': ('Authority Information Access', 'authorityInfoAccess'),
- '1.3.6.1.5.5.7.1.2': ('Biometric Info', 'biometricInfo'),
- '1.3.6.1.5.5.7.1.3': ('qcStatements', ),
- '1.3.6.1.5.5.7.1.4': ('ac-auditEntity', ),
- '1.3.6.1.5.5.7.1.5': ('ac-targeting', ),
- '1.3.6.1.5.5.7.1.6': ('aaControls', ),
- '1.3.6.1.5.5.7.1.7': ('sbgp-ipAddrBlock', ),
- '1.3.6.1.5.5.7.1.8': ('sbgp-autonomousSysNum', ),
- '1.3.6.1.5.5.7.1.9': ('sbgp-routerIdentifier', ),
- '1.3.6.1.5.5.7.1.10': ('ac-proxying', ),
- '1.3.6.1.5.5.7.1.11': ('Subject Information Access', 'subjectInfoAccess'),
- '1.3.6.1.5.5.7.1.14': ('Proxy Certificate Information', 'proxyCertInfo'),
- '1.3.6.1.5.5.7.1.24': ('TLS Feature', 'tlsfeature'),
- '1.3.6.1.5.5.7.2': ('id-qt', ),
- '1.3.6.1.5.5.7.2.1': ('Policy Qualifier CPS', 'id-qt-cps'),
- '1.3.6.1.5.5.7.2.2': ('Policy Qualifier User Notice', 'id-qt-unotice'),
- '1.3.6.1.5.5.7.2.3': ('textNotice', ),
- '1.3.6.1.5.5.7.3': ('id-kp', ),
- '1.3.6.1.5.5.7.3.1': ('TLS Web Server Authentication', 'serverAuth'),
- '1.3.6.1.5.5.7.3.2': ('TLS Web Client Authentication', 'clientAuth'),
- '1.3.6.1.5.5.7.3.3': ('Code Signing', 'codeSigning'),
- '1.3.6.1.5.5.7.3.4': ('E-mail Protection', 'emailProtection'),
- '1.3.6.1.5.5.7.3.5': ('IPSec End System', 'ipsecEndSystem'),
- '1.3.6.1.5.5.7.3.6': ('IPSec Tunnel', 'ipsecTunnel'),
- '1.3.6.1.5.5.7.3.7': ('IPSec User', 'ipsecUser'),
- '1.3.6.1.5.5.7.3.8': ('Time Stamping', 'timeStamping'),
- '1.3.6.1.5.5.7.3.9': ('OCSP Signing', 'OCSPSigning'),
- '1.3.6.1.5.5.7.3.10': ('dvcs', 'DVCS'),
- '1.3.6.1.5.5.7.3.17': ('ipsec Internet Key Exchange', 'ipsecIKE'),
- '1.3.6.1.5.5.7.3.18': ('Ctrl/provision WAP Access', 'capwapAC'),
- '1.3.6.1.5.5.7.3.19': ('Ctrl/Provision WAP Termination', 'capwapWTP'),
- '1.3.6.1.5.5.7.3.21': ('SSH Client', 'secureShellClient'),
- '1.3.6.1.5.5.7.3.22': ('SSH Server', 'secureShellServer'),
- '1.3.6.1.5.5.7.3.23': ('Send Router', 'sendRouter'),
- '1.3.6.1.5.5.7.3.24': ('Send Proxied Router', 'sendProxiedRouter'),
- '1.3.6.1.5.5.7.3.25': ('Send Owner', 'sendOwner'),
- '1.3.6.1.5.5.7.3.26': ('Send Proxied Owner', 'sendProxiedOwner'),
- '1.3.6.1.5.5.7.3.27': ('CMC Certificate Authority', 'cmcCA'),
- '1.3.6.1.5.5.7.3.28': ('CMC Registration Authority', 'cmcRA'),
- '1.3.6.1.5.5.7.4': ('id-it', ),
- '1.3.6.1.5.5.7.4.1': ('id-it-caProtEncCert', ),
- '1.3.6.1.5.5.7.4.2': ('id-it-signKeyPairTypes', ),
- '1.3.6.1.5.5.7.4.3': ('id-it-encKeyPairTypes', ),
- '1.3.6.1.5.5.7.4.4': ('id-it-preferredSymmAlg', ),
- '1.3.6.1.5.5.7.4.5': ('id-it-caKeyUpdateInfo', ),
- '1.3.6.1.5.5.7.4.6': ('id-it-currentCRL', ),
- '1.3.6.1.5.5.7.4.7': ('id-it-unsupportedOIDs', ),
- '1.3.6.1.5.5.7.4.8': ('id-it-subscriptionRequest', ),
- '1.3.6.1.5.5.7.4.9': ('id-it-subscriptionResponse', ),
- '1.3.6.1.5.5.7.4.10': ('id-it-keyPairParamReq', ),
- '1.3.6.1.5.5.7.4.11': ('id-it-keyPairParamRep', ),
- '1.3.6.1.5.5.7.4.12': ('id-it-revPassphrase', ),
- '1.3.6.1.5.5.7.4.13': ('id-it-implicitConfirm', ),
- '1.3.6.1.5.5.7.4.14': ('id-it-confirmWaitTime', ),
- '1.3.6.1.5.5.7.4.15': ('id-it-origPKIMessage', ),
- '1.3.6.1.5.5.7.4.16': ('id-it-suppLangTags', ),
- '1.3.6.1.5.5.7.5': ('id-pkip', ),
- '1.3.6.1.5.5.7.5.1': ('id-regCtrl', ),
- '1.3.6.1.5.5.7.5.1.1': ('id-regCtrl-regToken', ),
- '1.3.6.1.5.5.7.5.1.2': ('id-regCtrl-authenticator', ),
- '1.3.6.1.5.5.7.5.1.3': ('id-regCtrl-pkiPublicationInfo', ),
- '1.3.6.1.5.5.7.5.1.4': ('id-regCtrl-pkiArchiveOptions', ),
- '1.3.6.1.5.5.7.5.1.5': ('id-regCtrl-oldCertID', ),
- '1.3.6.1.5.5.7.5.1.6': ('id-regCtrl-protocolEncrKey', ),
- '1.3.6.1.5.5.7.5.2': ('id-regInfo', ),
- '1.3.6.1.5.5.7.5.2.1': ('id-regInfo-utf8Pairs', ),
- '1.3.6.1.5.5.7.5.2.2': ('id-regInfo-certReq', ),
- '1.3.6.1.5.5.7.6': ('id-alg', ),
- '1.3.6.1.5.5.7.6.1': ('id-alg-des40', ),
- '1.3.6.1.5.5.7.6.2': ('id-alg-noSignature', ),
- '1.3.6.1.5.5.7.6.3': ('id-alg-dh-sig-hmac-sha1', ),
- '1.3.6.1.5.5.7.6.4': ('id-alg-dh-pop', ),
- '1.3.6.1.5.5.7.7': ('id-cmc', ),
- '1.3.6.1.5.5.7.7.1': ('id-cmc-statusInfo', ),
- '1.3.6.1.5.5.7.7.2': ('id-cmc-identification', ),
- '1.3.6.1.5.5.7.7.3': ('id-cmc-identityProof', ),
- '1.3.6.1.5.5.7.7.4': ('id-cmc-dataReturn', ),
- '1.3.6.1.5.5.7.7.5': ('id-cmc-transactionId', ),
- '1.3.6.1.5.5.7.7.6': ('id-cmc-senderNonce', ),
- '1.3.6.1.5.5.7.7.7': ('id-cmc-recipientNonce', ),
- '1.3.6.1.5.5.7.7.8': ('id-cmc-addExtensions', ),
- '1.3.6.1.5.5.7.7.9': ('id-cmc-encryptedPOP', ),
- '1.3.6.1.5.5.7.7.10': ('id-cmc-decryptedPOP', ),
- '1.3.6.1.5.5.7.7.11': ('id-cmc-lraPOPWitness', ),
- '1.3.6.1.5.5.7.7.15': ('id-cmc-getCert', ),
- '1.3.6.1.5.5.7.7.16': ('id-cmc-getCRL', ),
- '1.3.6.1.5.5.7.7.17': ('id-cmc-revokeRequest', ),
- '1.3.6.1.5.5.7.7.18': ('id-cmc-regInfo', ),
- '1.3.6.1.5.5.7.7.19': ('id-cmc-responseInfo', ),
- '1.3.6.1.5.5.7.7.21': ('id-cmc-queryPending', ),
- '1.3.6.1.5.5.7.7.22': ('id-cmc-popLinkRandom', ),
- '1.3.6.1.5.5.7.7.23': ('id-cmc-popLinkWitness', ),
- '1.3.6.1.5.5.7.7.24': ('id-cmc-confirmCertAcceptance', ),
- '1.3.6.1.5.5.7.8': ('id-on', ),
- '1.3.6.1.5.5.7.8.1': ('id-on-personalData', ),
- '1.3.6.1.5.5.7.8.3': ('Permanent Identifier', 'id-on-permanentIdentifier'),
- '1.3.6.1.5.5.7.9': ('id-pda', ),
- '1.3.6.1.5.5.7.9.1': ('id-pda-dateOfBirth', ),
- '1.3.6.1.5.5.7.9.2': ('id-pda-placeOfBirth', ),
- '1.3.6.1.5.5.7.9.3': ('id-pda-gender', ),
- '1.3.6.1.5.5.7.9.4': ('id-pda-countryOfCitizenship', ),
- '1.3.6.1.5.5.7.9.5': ('id-pda-countryOfResidence', ),
- '1.3.6.1.5.5.7.10': ('id-aca', ),
- '1.3.6.1.5.5.7.10.1': ('id-aca-authenticationInfo', ),
- '1.3.6.1.5.5.7.10.2': ('id-aca-accessIdentity', ),
- '1.3.6.1.5.5.7.10.3': ('id-aca-chargingIdentity', ),
- '1.3.6.1.5.5.7.10.4': ('id-aca-group', ),
- '1.3.6.1.5.5.7.10.5': ('id-aca-role', ),
- '1.3.6.1.5.5.7.10.6': ('id-aca-encAttrs', ),
- '1.3.6.1.5.5.7.11': ('id-qcs', ),
- '1.3.6.1.5.5.7.11.1': ('id-qcs-pkixQCSyntax-v1', ),
- '1.3.6.1.5.5.7.12': ('id-cct', ),
- '1.3.6.1.5.5.7.12.1': ('id-cct-crs', ),
- '1.3.6.1.5.5.7.12.2': ('id-cct-PKIData', ),
- '1.3.6.1.5.5.7.12.3': ('id-cct-PKIResponse', ),
- '1.3.6.1.5.5.7.21': ('id-ppl', ),
- '1.3.6.1.5.5.7.21.0': ('Any language', 'id-ppl-anyLanguage'),
- '1.3.6.1.5.5.7.21.1': ('Inherit all', 'id-ppl-inheritAll'),
- '1.3.6.1.5.5.7.21.2': ('Independent', 'id-ppl-independent'),
- '1.3.6.1.5.5.7.48': ('id-ad', ),
- '1.3.6.1.5.5.7.48.1': ('OCSP', 'OCSP', 'id-pkix-OCSP'),
- '1.3.6.1.5.5.7.48.1.1': ('Basic OCSP Response', 'basicOCSPResponse'),
- '1.3.6.1.5.5.7.48.1.2': ('OCSP Nonce', 'Nonce'),
- '1.3.6.1.5.5.7.48.1.3': ('OCSP CRL ID', 'CrlID'),
- '1.3.6.1.5.5.7.48.1.4': ('Acceptable OCSP Responses', 'acceptableResponses'),
- '1.3.6.1.5.5.7.48.1.5': ('OCSP No Check', 'noCheck'),
- '1.3.6.1.5.5.7.48.1.6': ('OCSP Archive Cutoff', 'archiveCutoff'),
- '1.3.6.1.5.5.7.48.1.7': ('OCSP Service Locator', 'serviceLocator'),
- '1.3.6.1.5.5.7.48.1.8': ('Extended OCSP Status', 'extendedStatus'),
- '1.3.6.1.5.5.7.48.1.9': ('valid', ),
- '1.3.6.1.5.5.7.48.1.10': ('path', ),
- '1.3.6.1.5.5.7.48.1.11': ('Trust Root', 'trustRoot'),
- '1.3.6.1.5.5.7.48.2': ('CA Issuers', 'caIssuers'),
- '1.3.6.1.5.5.7.48.3': ('AD Time Stamping', 'ad_timestamping'),
- '1.3.6.1.5.5.7.48.4': ('ad dvcs', 'AD_DVCS'),
- '1.3.6.1.5.5.7.48.5': ('CA Repository', 'caRepository'),
- '1.3.6.1.5.5.8.1.1': ('hmac-md5', 'HMAC-MD5'),
- '1.3.6.1.5.5.8.1.2': ('hmac-sha1', 'HMAC-SHA1'),
- '1.3.6.1.6': ('SNMPv2', 'snmpv2'),
- '1.3.6.1.7': ('Mail', ),
- '1.3.6.1.7.1': ('MIME MHS', 'mime-mhs'),
- '1.3.6.1.7.1.1': ('mime-mhs-headings', 'mime-mhs-headings'),
- '1.3.6.1.7.1.1.1': ('id-hex-partial-message', 'id-hex-partial-message'),
- '1.3.6.1.7.1.1.2': ('id-hex-multipart-message', 'id-hex-multipart-message'),
- '1.3.6.1.7.1.2': ('mime-mhs-bodies', 'mime-mhs-bodies'),
- '1.3.14.3.2': ('algorithm', 'algorithm'),
- '1.3.14.3.2.3': ('md5WithRSA', 'RSA-NP-MD5'),
- '1.3.14.3.2.6': ('des-ecb', 'DES-ECB'),
- '1.3.14.3.2.7': ('des-cbc', 'DES-CBC'),
- '1.3.14.3.2.8': ('des-ofb', 'DES-OFB'),
- '1.3.14.3.2.9': ('des-cfb', 'DES-CFB'),
- '1.3.14.3.2.11': ('rsaSignature', ),
- '1.3.14.3.2.12': ('dsaEncryption-old', 'DSA-old'),
- '1.3.14.3.2.13': ('dsaWithSHA', 'DSA-SHA'),
- '1.3.14.3.2.15': ('shaWithRSAEncryption', 'RSA-SHA'),
- '1.3.14.3.2.17': ('des-ede', 'DES-EDE'),
- '1.3.14.3.2.18': ('sha', 'SHA'),
- '1.3.14.3.2.26': ('sha1', 'SHA1'),
- '1.3.14.3.2.27': ('dsaWithSHA1-old', 'DSA-SHA1-old'),
- '1.3.14.3.2.29': ('sha1WithRSA', 'RSA-SHA1-2'),
- '1.3.36.3.2.1': ('ripemd160', 'RIPEMD160'),
- '1.3.36.3.3.1.2': ('ripemd160WithRSA', 'RSA-RIPEMD160'),
- '1.3.36.3.3.2.8.1.1.1': ('brainpoolP160r1', ),
- '1.3.36.3.3.2.8.1.1.2': ('brainpoolP160t1', ),
- '1.3.36.3.3.2.8.1.1.3': ('brainpoolP192r1', ),
- '1.3.36.3.3.2.8.1.1.4': ('brainpoolP192t1', ),
- '1.3.36.3.3.2.8.1.1.5': ('brainpoolP224r1', ),
- '1.3.36.3.3.2.8.1.1.6': ('brainpoolP224t1', ),
- '1.3.36.3.3.2.8.1.1.7': ('brainpoolP256r1', ),
- '1.3.36.3.3.2.8.1.1.8': ('brainpoolP256t1', ),
- '1.3.36.3.3.2.8.1.1.9': ('brainpoolP320r1', ),
- '1.3.36.3.3.2.8.1.1.10': ('brainpoolP320t1', ),
- '1.3.36.3.3.2.8.1.1.11': ('brainpoolP384r1', ),
- '1.3.36.3.3.2.8.1.1.12': ('brainpoolP384t1', ),
- '1.3.36.3.3.2.8.1.1.13': ('brainpoolP512r1', ),
- '1.3.36.3.3.2.8.1.1.14': ('brainpoolP512t1', ),
- '1.3.36.8.3.3': ('Professional Information or basis for Admission', 'x509ExtAdmission'),
- '1.3.101.1.4.1': ('Strong Extranet ID', 'SXNetID'),
- '1.3.101.110': ('X25519', ),
- '1.3.101.111': ('X448', ),
- '1.3.101.112': ('ED25519', ),
- '1.3.101.113': ('ED448', ),
- '1.3.111': ('ieee', ),
- '1.3.111.2.1619': ('IEEE Security in Storage Working Group', 'ieee-siswg'),
- '1.3.111.2.1619.0.1.1': ('aes-128-xts', 'AES-128-XTS'),
- '1.3.111.2.1619.0.1.2': ('aes-256-xts', 'AES-256-XTS'),
- '1.3.132': ('certicom-arc', ),
- '1.3.132.0': ('secg_ellipticCurve', ),
- '1.3.132.0.1': ('sect163k1', ),
- '1.3.132.0.2': ('sect163r1', ),
- '1.3.132.0.3': ('sect239k1', ),
- '1.3.132.0.4': ('sect113r1', ),
- '1.3.132.0.5': ('sect113r2', ),
- '1.3.132.0.6': ('secp112r1', ),
- '1.3.132.0.7': ('secp112r2', ),
- '1.3.132.0.8': ('secp160r1', ),
- '1.3.132.0.9': ('secp160k1', ),
- '1.3.132.0.10': ('secp256k1', ),
- '1.3.132.0.15': ('sect163r2', ),
- '1.3.132.0.16': ('sect283k1', ),
- '1.3.132.0.17': ('sect283r1', ),
- '1.3.132.0.22': ('sect131r1', ),
- '1.3.132.0.23': ('sect131r2', ),
- '1.3.132.0.24': ('sect193r1', ),
- '1.3.132.0.25': ('sect193r2', ),
- '1.3.132.0.26': ('sect233k1', ),
- '1.3.132.0.27': ('sect233r1', ),
- '1.3.132.0.28': ('secp128r1', ),
- '1.3.132.0.29': ('secp128r2', ),
- '1.3.132.0.30': ('secp160r2', ),
- '1.3.132.0.31': ('secp192k1', ),
- '1.3.132.0.32': ('secp224k1', ),
- '1.3.132.0.33': ('secp224r1', ),
- '1.3.132.0.34': ('secp384r1', ),
- '1.3.132.0.35': ('secp521r1', ),
- '1.3.132.0.36': ('sect409k1', ),
- '1.3.132.0.37': ('sect409r1', ),
- '1.3.132.0.38': ('sect571k1', ),
- '1.3.132.0.39': ('sect571r1', ),
- '1.3.132.1': ('secg-scheme', ),
- '1.3.132.1.11.0': ('dhSinglePass-stdDH-sha224kdf-scheme', ),
- '1.3.132.1.11.1': ('dhSinglePass-stdDH-sha256kdf-scheme', ),
- '1.3.132.1.11.2': ('dhSinglePass-stdDH-sha384kdf-scheme', ),
- '1.3.132.1.11.3': ('dhSinglePass-stdDH-sha512kdf-scheme', ),
- '1.3.132.1.14.0': ('dhSinglePass-cofactorDH-sha224kdf-scheme', ),
- '1.3.132.1.14.1': ('dhSinglePass-cofactorDH-sha256kdf-scheme', ),
- '1.3.132.1.14.2': ('dhSinglePass-cofactorDH-sha384kdf-scheme', ),
- '1.3.132.1.14.3': ('dhSinglePass-cofactorDH-sha512kdf-scheme', ),
- '1.3.133.16.840.63.0': ('x9-63-scheme', ),
- '1.3.133.16.840.63.0.2': ('dhSinglePass-stdDH-sha1kdf-scheme', ),
- '1.3.133.16.840.63.0.3': ('dhSinglePass-cofactorDH-sha1kdf-scheme', ),
- '2': ('joint-iso-itu-t', 'JOINT-ISO-ITU-T', 'joint-iso-ccitt'),
- '2.5': ('directory services (X.500)', 'X500'),
- '2.5.1.5': ('Selected Attribute Types', 'selected-attribute-types'),
- '2.5.1.5.55': ('clearance', ),
- '2.5.4': ('X509', ),
- '2.5.4.3': ('commonName', 'CN'),
- '2.5.4.4': ('surname', 'SN'),
- '2.5.4.5': ('serialNumber', ),
- '2.5.4.6': ('countryName', 'C'),
- '2.5.4.7': ('localityName', 'L'),
- '2.5.4.8': ('stateOrProvinceName', 'ST'),
- '2.5.4.9': ('streetAddress', 'street'),
- '2.5.4.10': ('organizationName', 'O'),
- '2.5.4.11': ('organizationalUnitName', 'OU'),
- '2.5.4.12': ('title', 'title'),
- '2.5.4.13': ('description', ),
- '2.5.4.14': ('searchGuide', ),
- '2.5.4.15': ('businessCategory', ),
- '2.5.4.16': ('postalAddress', ),
- '2.5.4.17': ('postalCode', ),
- '2.5.4.18': ('postOfficeBox', ),
- '2.5.4.19': ('physicalDeliveryOfficeName', ),
- '2.5.4.20': ('telephoneNumber', ),
- '2.5.4.21': ('telexNumber', ),
- '2.5.4.22': ('teletexTerminalIdentifier', ),
- '2.5.4.23': ('facsimileTelephoneNumber', ),
- '2.5.4.24': ('x121Address', ),
- '2.5.4.25': ('internationaliSDNNumber', ),
- '2.5.4.26': ('registeredAddress', ),
- '2.5.4.27': ('destinationIndicator', ),
- '2.5.4.28': ('preferredDeliveryMethod', ),
- '2.5.4.29': ('presentationAddress', ),
- '2.5.4.30': ('supportedApplicationContext', ),
- '2.5.4.31': ('member', ),
- '2.5.4.32': ('owner', ),
- '2.5.4.33': ('roleOccupant', ),
- '2.5.4.34': ('seeAlso', ),
- '2.5.4.35': ('userPassword', ),
- '2.5.4.36': ('userCertificate', ),
- '2.5.4.37': ('cACertificate', ),
- '2.5.4.38': ('authorityRevocationList', ),
- '2.5.4.39': ('certificateRevocationList', ),
- '2.5.4.40': ('crossCertificatePair', ),
- '2.5.4.41': ('name', 'name'),
- '2.5.4.42': ('givenName', 'GN'),
- '2.5.4.43': ('initials', 'initials'),
- '2.5.4.44': ('generationQualifier', ),
- '2.5.4.45': ('x500UniqueIdentifier', ),
- '2.5.4.46': ('dnQualifier', 'dnQualifier'),
- '2.5.4.47': ('enhancedSearchGuide', ),
- '2.5.4.48': ('protocolInformation', ),
- '2.5.4.49': ('distinguishedName', ),
- '2.5.4.50': ('uniqueMember', ),
- '2.5.4.51': ('houseIdentifier', ),
- '2.5.4.52': ('supportedAlgorithms', ),
- '2.5.4.53': ('deltaRevocationList', ),
- '2.5.4.54': ('dmdName', ),
- '2.5.4.65': ('pseudonym', ),
- '2.5.4.72': ('role', 'role'),
- '2.5.4.97': ('organizationIdentifier', ),
- '2.5.4.98': ('countryCode3c', 'c3'),
- '2.5.4.99': ('countryCode3n', 'n3'),
- '2.5.4.100': ('dnsName', ),
- '2.5.8': ('directory services - algorithms', 'X500algorithms'),
- '2.5.8.1.1': ('rsa', 'RSA'),
- '2.5.8.3.100': ('mdc2WithRSA', 'RSA-MDC2'),
- '2.5.8.3.101': ('mdc2', 'MDC2'),
- '2.5.29': ('id-ce', ),
- '2.5.29.9': ('X509v3 Subject Directory Attributes', 'subjectDirectoryAttributes'),
- '2.5.29.14': ('X509v3 Subject Key Identifier', 'subjectKeyIdentifier'),
- '2.5.29.15': ('X509v3 Key Usage', 'keyUsage'),
- '2.5.29.16': ('X509v3 Private Key Usage Period', 'privateKeyUsagePeriod'),
- '2.5.29.17': ('X509v3 Subject Alternative Name', 'subjectAltName'),
- '2.5.29.18': ('X509v3 Issuer Alternative Name', 'issuerAltName'),
- '2.5.29.19': ('X509v3 Basic Constraints', 'basicConstraints'),
- '2.5.29.20': ('X509v3 CRL Number', 'crlNumber'),
- '2.5.29.21': ('X509v3 CRL Reason Code', 'CRLReason'),
- '2.5.29.23': ('Hold Instruction Code', 'holdInstructionCode'),
- '2.5.29.24': ('Invalidity Date', 'invalidityDate'),
- '2.5.29.27': ('X509v3 Delta CRL Indicator', 'deltaCRL'),
- '2.5.29.28': ('X509v3 Issuing Distribution Point', 'issuingDistributionPoint'),
- '2.5.29.29': ('X509v3 Certificate Issuer', 'certificateIssuer'),
- '2.5.29.30': ('X509v3 Name Constraints', 'nameConstraints'),
- '2.5.29.31': ('X509v3 CRL Distribution Points', 'crlDistributionPoints'),
- '2.5.29.32': ('X509v3 Certificate Policies', 'certificatePolicies'),
- '2.5.29.32.0': ('X509v3 Any Policy', 'anyPolicy'),
- '2.5.29.33': ('X509v3 Policy Mappings', 'policyMappings'),
- '2.5.29.35': ('X509v3 Authority Key Identifier', 'authorityKeyIdentifier'),
- '2.5.29.36': ('X509v3 Policy Constraints', 'policyConstraints'),
- '2.5.29.37': ('X509v3 Extended Key Usage', 'extendedKeyUsage'),
- '2.5.29.37.0': ('Any Extended Key Usage', 'anyExtendedKeyUsage'),
- '2.5.29.46': ('X509v3 Freshest CRL', 'freshestCRL'),
- '2.5.29.54': ('X509v3 Inhibit Any Policy', 'inhibitAnyPolicy'),
- '2.5.29.55': ('X509v3 AC Targeting', 'targetInformation'),
- '2.5.29.56': ('X509v3 No Revocation Available', 'noRevAvail'),
- '2.16.840.1.101.3': ('csor', ),
- '2.16.840.1.101.3.4': ('nistAlgorithms', ),
- '2.16.840.1.101.3.4.1': ('aes', ),
- '2.16.840.1.101.3.4.1.1': ('aes-128-ecb', 'AES-128-ECB'),
- '2.16.840.1.101.3.4.1.2': ('aes-128-cbc', 'AES-128-CBC'),
- '2.16.840.1.101.3.4.1.3': ('aes-128-ofb', 'AES-128-OFB'),
- '2.16.840.1.101.3.4.1.4': ('aes-128-cfb', 'AES-128-CFB'),
- '2.16.840.1.101.3.4.1.5': ('id-aes128-wrap', ),
- '2.16.840.1.101.3.4.1.6': ('aes-128-gcm', 'id-aes128-GCM'),
- '2.16.840.1.101.3.4.1.7': ('aes-128-ccm', 'id-aes128-CCM'),
- '2.16.840.1.101.3.4.1.8': ('id-aes128-wrap-pad', ),
- '2.16.840.1.101.3.4.1.21': ('aes-192-ecb', 'AES-192-ECB'),
- '2.16.840.1.101.3.4.1.22': ('aes-192-cbc', 'AES-192-CBC'),
- '2.16.840.1.101.3.4.1.23': ('aes-192-ofb', 'AES-192-OFB'),
- '2.16.840.1.101.3.4.1.24': ('aes-192-cfb', 'AES-192-CFB'),
- '2.16.840.1.101.3.4.1.25': ('id-aes192-wrap', ),
- '2.16.840.1.101.3.4.1.26': ('aes-192-gcm', 'id-aes192-GCM'),
- '2.16.840.1.101.3.4.1.27': ('aes-192-ccm', 'id-aes192-CCM'),
- '2.16.840.1.101.3.4.1.28': ('id-aes192-wrap-pad', ),
- '2.16.840.1.101.3.4.1.41': ('aes-256-ecb', 'AES-256-ECB'),
- '2.16.840.1.101.3.4.1.42': ('aes-256-cbc', 'AES-256-CBC'),
- '2.16.840.1.101.3.4.1.43': ('aes-256-ofb', 'AES-256-OFB'),
- '2.16.840.1.101.3.4.1.44': ('aes-256-cfb', 'AES-256-CFB'),
- '2.16.840.1.101.3.4.1.45': ('id-aes256-wrap', ),
- '2.16.840.1.101.3.4.1.46': ('aes-256-gcm', 'id-aes256-GCM'),
- '2.16.840.1.101.3.4.1.47': ('aes-256-ccm', 'id-aes256-CCM'),
- '2.16.840.1.101.3.4.1.48': ('id-aes256-wrap-pad', ),
- '2.16.840.1.101.3.4.2': ('nist_hashalgs', ),
- '2.16.840.1.101.3.4.2.1': ('sha256', 'SHA256'),
- '2.16.840.1.101.3.4.2.2': ('sha384', 'SHA384'),
- '2.16.840.1.101.3.4.2.3': ('sha512', 'SHA512'),
- '2.16.840.1.101.3.4.2.4': ('sha224', 'SHA224'),
- '2.16.840.1.101.3.4.2.5': ('sha512-224', 'SHA512-224'),
- '2.16.840.1.101.3.4.2.6': ('sha512-256', 'SHA512-256'),
- '2.16.840.1.101.3.4.2.7': ('sha3-224', 'SHA3-224'),
- '2.16.840.1.101.3.4.2.8': ('sha3-256', 'SHA3-256'),
- '2.16.840.1.101.3.4.2.9': ('sha3-384', 'SHA3-384'),
- '2.16.840.1.101.3.4.2.10': ('sha3-512', 'SHA3-512'),
- '2.16.840.1.101.3.4.2.11': ('shake128', 'SHAKE128'),
- '2.16.840.1.101.3.4.2.12': ('shake256', 'SHAKE256'),
- '2.16.840.1.101.3.4.2.13': ('hmac-sha3-224', 'id-hmacWithSHA3-224'),
- '2.16.840.1.101.3.4.2.14': ('hmac-sha3-256', 'id-hmacWithSHA3-256'),
- '2.16.840.1.101.3.4.2.15': ('hmac-sha3-384', 'id-hmacWithSHA3-384'),
- '2.16.840.1.101.3.4.2.16': ('hmac-sha3-512', 'id-hmacWithSHA3-512'),
- '2.16.840.1.101.3.4.3': ('dsa_with_sha2', 'sigAlgs'),
- '2.16.840.1.101.3.4.3.1': ('dsa_with_SHA224', ),
- '2.16.840.1.101.3.4.3.2': ('dsa_with_SHA256', ),
- '2.16.840.1.101.3.4.3.3': ('dsa_with_SHA384', 'id-dsa-with-sha384'),
- '2.16.840.1.101.3.4.3.4': ('dsa_with_SHA512', 'id-dsa-with-sha512'),
- '2.16.840.1.101.3.4.3.5': ('dsa_with_SHA3-224', 'id-dsa-with-sha3-224'),
- '2.16.840.1.101.3.4.3.6': ('dsa_with_SHA3-256', 'id-dsa-with-sha3-256'),
- '2.16.840.1.101.3.4.3.7': ('dsa_with_SHA3-384', 'id-dsa-with-sha3-384'),
- '2.16.840.1.101.3.4.3.8': ('dsa_with_SHA3-512', 'id-dsa-with-sha3-512'),
- '2.16.840.1.101.3.4.3.9': ('ecdsa_with_SHA3-224', 'id-ecdsa-with-sha3-224'),
- '2.16.840.1.101.3.4.3.10': ('ecdsa_with_SHA3-256', 'id-ecdsa-with-sha3-256'),
- '2.16.840.1.101.3.4.3.11': ('ecdsa_with_SHA3-384', 'id-ecdsa-with-sha3-384'),
- '2.16.840.1.101.3.4.3.12': ('ecdsa_with_SHA3-512', 'id-ecdsa-with-sha3-512'),
- '2.16.840.1.101.3.4.3.13': ('RSA-SHA3-224', 'id-rsassa-pkcs1-v1_5-with-sha3-224'),
- '2.16.840.1.101.3.4.3.14': ('RSA-SHA3-256', 'id-rsassa-pkcs1-v1_5-with-sha3-256'),
- '2.16.840.1.101.3.4.3.15': ('RSA-SHA3-384', 'id-rsassa-pkcs1-v1_5-with-sha3-384'),
- '2.16.840.1.101.3.4.3.16': ('RSA-SHA3-512', 'id-rsassa-pkcs1-v1_5-with-sha3-512'),
- '2.16.840.1.113730': ('Netscape Communications Corp.', 'Netscape'),
- '2.16.840.1.113730.1': ('Netscape Certificate Extension', 'nsCertExt'),
- '2.16.840.1.113730.1.1': ('Netscape Cert Type', 'nsCertType'),
- '2.16.840.1.113730.1.2': ('Netscape Base Url', 'nsBaseUrl'),
- '2.16.840.1.113730.1.3': ('Netscape Revocation Url', 'nsRevocationUrl'),
- '2.16.840.1.113730.1.4': ('Netscape CA Revocation Url', 'nsCaRevocationUrl'),
- '2.16.840.1.113730.1.7': ('Netscape Renewal Url', 'nsRenewalUrl'),
- '2.16.840.1.113730.1.8': ('Netscape CA Policy Url', 'nsCaPolicyUrl'),
- '2.16.840.1.113730.1.12': ('Netscape SSL Server Name', 'nsSslServerName'),
- '2.16.840.1.113730.1.13': ('Netscape Comment', 'nsComment'),
- '2.16.840.1.113730.2': ('Netscape Data Type', 'nsDataType'),
- '2.16.840.1.113730.2.5': ('Netscape Certificate Sequence', 'nsCertSequence'),
- '2.16.840.1.113730.4.1': ('Netscape Server Gated Crypto', 'nsSGC'),
- '2.23': ('International Organizations', 'international-organizations'),
- '2.23.42': ('Secure Electronic Transactions', 'id-set'),
- '2.23.42.0': ('content types', 'set-ctype'),
- '2.23.42.0.0': ('setct-PANData', ),
- '2.23.42.0.1': ('setct-PANToken', ),
- '2.23.42.0.2': ('setct-PANOnly', ),
- '2.23.42.0.3': ('setct-OIData', ),
- '2.23.42.0.4': ('setct-PI', ),
- '2.23.42.0.5': ('setct-PIData', ),
- '2.23.42.0.6': ('setct-PIDataUnsigned', ),
- '2.23.42.0.7': ('setct-HODInput', ),
- '2.23.42.0.8': ('setct-AuthResBaggage', ),
- '2.23.42.0.9': ('setct-AuthRevReqBaggage', ),
- '2.23.42.0.10': ('setct-AuthRevResBaggage', ),
- '2.23.42.0.11': ('setct-CapTokenSeq', ),
- '2.23.42.0.12': ('setct-PInitResData', ),
- '2.23.42.0.13': ('setct-PI-TBS', ),
- '2.23.42.0.14': ('setct-PResData', ),
- '2.23.42.0.16': ('setct-AuthReqTBS', ),
- '2.23.42.0.17': ('setct-AuthResTBS', ),
- '2.23.42.0.18': ('setct-AuthResTBSX', ),
- '2.23.42.0.19': ('setct-AuthTokenTBS', ),
- '2.23.42.0.20': ('setct-CapTokenData', ),
- '2.23.42.0.21': ('setct-CapTokenTBS', ),
- '2.23.42.0.22': ('setct-AcqCardCodeMsg', ),
- '2.23.42.0.23': ('setct-AuthRevReqTBS', ),
- '2.23.42.0.24': ('setct-AuthRevResData', ),
- '2.23.42.0.25': ('setct-AuthRevResTBS', ),
- '2.23.42.0.26': ('setct-CapReqTBS', ),
- '2.23.42.0.27': ('setct-CapReqTBSX', ),
- '2.23.42.0.28': ('setct-CapResData', ),
- '2.23.42.0.29': ('setct-CapRevReqTBS', ),
- '2.23.42.0.30': ('setct-CapRevReqTBSX', ),
- '2.23.42.0.31': ('setct-CapRevResData', ),
- '2.23.42.0.32': ('setct-CredReqTBS', ),
- '2.23.42.0.33': ('setct-CredReqTBSX', ),
- '2.23.42.0.34': ('setct-CredResData', ),
- '2.23.42.0.35': ('setct-CredRevReqTBS', ),
- '2.23.42.0.36': ('setct-CredRevReqTBSX', ),
- '2.23.42.0.37': ('setct-CredRevResData', ),
- '2.23.42.0.38': ('setct-PCertReqData', ),
- '2.23.42.0.39': ('setct-PCertResTBS', ),
- '2.23.42.0.40': ('setct-BatchAdminReqData', ),
- '2.23.42.0.41': ('setct-BatchAdminResData', ),
- '2.23.42.0.42': ('setct-CardCInitResTBS', ),
- '2.23.42.0.43': ('setct-MeAqCInitResTBS', ),
- '2.23.42.0.44': ('setct-RegFormResTBS', ),
- '2.23.42.0.45': ('setct-CertReqData', ),
- '2.23.42.0.46': ('setct-CertReqTBS', ),
- '2.23.42.0.47': ('setct-CertResData', ),
- '2.23.42.0.48': ('setct-CertInqReqTBS', ),
- '2.23.42.0.49': ('setct-ErrorTBS', ),
- '2.23.42.0.50': ('setct-PIDualSignedTBE', ),
- '2.23.42.0.51': ('setct-PIUnsignedTBE', ),
- '2.23.42.0.52': ('setct-AuthReqTBE', ),
- '2.23.42.0.53': ('setct-AuthResTBE', ),
- '2.23.42.0.54': ('setct-AuthResTBEX', ),
- '2.23.42.0.55': ('setct-AuthTokenTBE', ),
- '2.23.42.0.56': ('setct-CapTokenTBE', ),
- '2.23.42.0.57': ('setct-CapTokenTBEX', ),
- '2.23.42.0.58': ('setct-AcqCardCodeMsgTBE', ),
- '2.23.42.0.59': ('setct-AuthRevReqTBE', ),
- '2.23.42.0.60': ('setct-AuthRevResTBE', ),
- '2.23.42.0.61': ('setct-AuthRevResTBEB', ),
- '2.23.42.0.62': ('setct-CapReqTBE', ),
- '2.23.42.0.63': ('setct-CapReqTBEX', ),
- '2.23.42.0.64': ('setct-CapResTBE', ),
- '2.23.42.0.65': ('setct-CapRevReqTBE', ),
- '2.23.42.0.66': ('setct-CapRevReqTBEX', ),
- '2.23.42.0.67': ('setct-CapRevResTBE', ),
- '2.23.42.0.68': ('setct-CredReqTBE', ),
- '2.23.42.0.69': ('setct-CredReqTBEX', ),
- '2.23.42.0.70': ('setct-CredResTBE', ),
- '2.23.42.0.71': ('setct-CredRevReqTBE', ),
- '2.23.42.0.72': ('setct-CredRevReqTBEX', ),
- '2.23.42.0.73': ('setct-CredRevResTBE', ),
- '2.23.42.0.74': ('setct-BatchAdminReqTBE', ),
- '2.23.42.0.75': ('setct-BatchAdminResTBE', ),
- '2.23.42.0.76': ('setct-RegFormReqTBE', ),
- '2.23.42.0.77': ('setct-CertReqTBE', ),
- '2.23.42.0.78': ('setct-CertReqTBEX', ),
- '2.23.42.0.79': ('setct-CertResTBE', ),
- '2.23.42.0.80': ('setct-CRLNotificationTBS', ),
- '2.23.42.0.81': ('setct-CRLNotificationResTBS', ),
- '2.23.42.0.82': ('setct-BCIDistributionTBS', ),
- '2.23.42.1': ('message extensions', 'set-msgExt'),
- '2.23.42.1.1': ('generic cryptogram', 'setext-genCrypt'),
- '2.23.42.1.3': ('merchant initiated auth', 'setext-miAuth'),
- '2.23.42.1.4': ('setext-pinSecure', ),
- '2.23.42.1.5': ('setext-pinAny', ),
- '2.23.42.1.7': ('setext-track2', ),
- '2.23.42.1.8': ('additional verification', 'setext-cv'),
- '2.23.42.3': ('set-attr', ),
- '2.23.42.3.0': ('setAttr-Cert', ),
- '2.23.42.3.0.0': ('set-rootKeyThumb', ),
- '2.23.42.3.0.1': ('set-addPolicy', ),
- '2.23.42.3.1': ('payment gateway capabilities', 'setAttr-PGWYcap'),
- '2.23.42.3.2': ('setAttr-TokenType', ),
- '2.23.42.3.2.1': ('setAttr-Token-EMV', ),
- '2.23.42.3.2.2': ('setAttr-Token-B0Prime', ),
- '2.23.42.3.3': ('issuer capabilities', 'setAttr-IssCap'),
- '2.23.42.3.3.3': ('setAttr-IssCap-CVM', ),
- '2.23.42.3.3.3.1': ('generate cryptogram', 'setAttr-GenCryptgrm'),
- '2.23.42.3.3.4': ('setAttr-IssCap-T2', ),
- '2.23.42.3.3.4.1': ('encrypted track 2', 'setAttr-T2Enc'),
- '2.23.42.3.3.4.2': ('cleartext track 2', 'setAttr-T2cleartxt'),
- '2.23.42.3.3.5': ('setAttr-IssCap-Sig', ),
- '2.23.42.3.3.5.1': ('ICC or token signature', 'setAttr-TokICCsig'),
- '2.23.42.3.3.5.2': ('secure device signature', 'setAttr-SecDevSig'),
- '2.23.42.5': ('set-policy', ),
- '2.23.42.5.0': ('set-policy-root', ),
- '2.23.42.7': ('certificate extensions', 'set-certExt'),
- '2.23.42.7.0': ('setCext-hashedRoot', ),
- '2.23.42.7.1': ('setCext-certType', ),
- '2.23.42.7.2': ('setCext-merchData', ),
- '2.23.42.7.3': ('setCext-cCertRequired', ),
- '2.23.42.7.4': ('setCext-tunneling', ),
- '2.23.42.7.5': ('setCext-setExt', ),
- '2.23.42.7.6': ('setCext-setQualf', ),
- '2.23.42.7.7': ('setCext-PGWYcapabilities', ),
- '2.23.42.7.8': ('setCext-TokenIdentifier', ),
- '2.23.42.7.9': ('setCext-Track2Data', ),
- '2.23.42.7.10': ('setCext-TokenType', ),
- '2.23.42.7.11': ('setCext-IssuerCapabilities', ),
- '2.23.42.8': ('set-brand', ),
- '2.23.42.8.1': ('set-brand-IATA-ATA', ),
- '2.23.42.8.4': ('set-brand-Visa', ),
- '2.23.42.8.5': ('set-brand-MasterCard', ),
- '2.23.42.8.30': ('set-brand-Diners', ),
- '2.23.42.8.34': ('set-brand-AmericanExpress', ),
- '2.23.42.8.35': ('set-brand-JCB', ),
- '2.23.42.8.6011': ('set-brand-Novus', ),
- '2.23.43': ('wap', ),
- '2.23.43.1': ('wap-wsg', ),
- '2.23.43.1.4': ('wap-wsg-idm-ecid', ),
- '2.23.43.1.4.1': ('wap-wsg-idm-ecid-wtls1', ),
- '2.23.43.1.4.3': ('wap-wsg-idm-ecid-wtls3', ),
- '2.23.43.1.4.4': ('wap-wsg-idm-ecid-wtls4', ),
- '2.23.43.1.4.5': ('wap-wsg-idm-ecid-wtls5', ),
- '2.23.43.1.4.6': ('wap-wsg-idm-ecid-wtls6', ),
- '2.23.43.1.4.7': ('wap-wsg-idm-ecid-wtls7', ),
- '2.23.43.1.4.8': ('wap-wsg-idm-ecid-wtls8', ),
- '2.23.43.1.4.9': ('wap-wsg-idm-ecid-wtls9', ),
- '2.23.43.1.4.10': ('wap-wsg-idm-ecid-wtls10', ),
- '2.23.43.1.4.11': ('wap-wsg-idm-ecid-wtls11', ),
- '2.23.43.1.4.12': ('wap-wsg-idm-ecid-wtls12', ),
-}
-# #####################################################################################
-# #####################################################################################
-
-_OID_LOOKUP = dict()
-_NORMALIZE_NAMES = dict()
-_NORMALIZE_NAMES_SHORT = dict()
-
-for dotted, names in _OID_MAP.items():
- for name in names:
- if name in _NORMALIZE_NAMES and _OID_LOOKUP[name] != dotted:
- raise AssertionError(
- 'Name collision during setup: "{0}" for OIDs {1} and {2}'
- .format(name, dotted, _OID_LOOKUP[name])
- )
- _NORMALIZE_NAMES[name] = names[0]
- _NORMALIZE_NAMES_SHORT[name] = names[-1]
- _OID_LOOKUP[name] = dotted
-for alias, original in [('userID', 'userId')]:
- if alias in _NORMALIZE_NAMES:
- raise AssertionError(
- 'Name collision during adding aliases: "{0}" (alias for "{1}") is already mapped to OID {2}'
- .format(alias, original, _OID_LOOKUP[alias])
- )
- _NORMALIZE_NAMES[alias] = original
- _NORMALIZE_NAMES_SHORT[alias] = _NORMALIZE_NAMES_SHORT[original]
- _OID_LOOKUP[alias] = _OID_LOOKUP[original]
-
-
-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)
-
-
-# #####################################################################################
-# #####################################################################################
-# # This excerpt is dual licensed under the terms of the Apache License, Version
-# # 2.0, and the BSD License. See the LICENSE file at
-# # https://github.com/pyca/cryptography/blob/master/LICENSE for complete details.
-# #
-# # Adapted from cryptography's hazmat/backends/openssl/decode_asn1.py
-# #
-# # Copyright (c) 2015, 2016 Paul Kehrer (@reaperhulk)
-# # Copyright (c) 2017 Fraser Tweedale (@frasertweedale)
-# #
-# # Relevant commits from cryptography project (https://github.com/pyca/cryptography):
-# # pyca/cryptography@719d536dd691e84e208534798f2eb4f82aaa2e07
-# # pyca/cryptography@5ab6d6a5c05572bd1c75f05baf264a2d0001894a
-# # pyca/cryptography@2e776e20eb60378e0af9b7439000d0e80da7c7e3
-# # pyca/cryptography@fb309ed24647d1be9e319b61b1f2aa8ebb87b90b
-# # pyca/cryptography@2917e460993c475c72d7146c50dc3bbc2414280d
-# # pyca/cryptography@3057f91ea9a05fb593825006d87a391286a4d828
-# # pyca/cryptography@d607dd7e5bc5c08854ec0c9baff70ba4a35be36f
-def _obj2txt(openssl_lib, openssl_ffi, obj):
- # Set to 80 on the recommendation of
- # https://www.openssl.org/docs/crypto/OBJ_nid2ln.html#return_values
- #
- # But OIDs longer than this occur in real life (e.g. Active
- # Directory makes some very long OIDs). So we need to detect
- # and properly handle the case where the default buffer is not
- # big enough.
- #
- buf_len = 80
- buf = openssl_ffi.new("char[]", buf_len)
-
- # 'res' is the number of bytes that *would* be written if the
- # buffer is large enough. If 'res' > buf_len - 1, we need to
- # alloc a big-enough buffer and go again.
- res = openssl_lib.OBJ_obj2txt(buf, buf_len, obj, 1)
- if res > buf_len - 1: # account for terminating null byte
- buf_len = res + 1
- buf = openssl_ffi.new("char[]", buf_len)
- res = openssl_lib.OBJ_obj2txt(buf, buf_len, obj, 1)
- return openssl_ffi.buffer(buf, res)[:].decode()
-# #####################################################################################
-# #####################################################################################
-
-
-def cryptography_get_extensions_from_cert(cert):
- # Since cryptography won't give us the DER value for an extension
- # (that is only stored for unrecognized extensions), we have to re-do
- # the extension parsing outselves.
- result = dict()
- backend = cert._backend
- x509_obj = cert._x509
-
- for i in range(backend._lib.X509_get_ext_count(x509_obj)):
- ext = backend._lib.X509_get_ext(x509_obj, i)
- if ext == backend._ffi.NULL:
- continue
- crit = backend._lib.X509_EXTENSION_get_critical(ext)
- data = backend._lib.X509_EXTENSION_get_data(ext)
- backend.openssl_assert(data != backend._ffi.NULL)
- der = backend._ffi.buffer(data.data, data.length)[:]
- entry = dict(
- critical=(crit == 1),
- value=base64.b64encode(der),
- )
- oid = _obj2txt(backend._lib, backend._ffi, backend._lib.X509_EXTENSION_get_object(ext))
- result[oid] = entry
- return result
-
-
-def cryptography_get_extensions_from_csr(csr):
- # Since cryptography won't give us the DER value for an extension
- # (that is only stored for unrecognized extensions), we have to re-do
- # the extension parsing outselves.
- result = dict()
- backend = csr._backend
-
- extensions = backend._lib.X509_REQ_get_extensions(csr._x509_req)
- extensions = backend._ffi.gc(
- extensions,
- lambda ext: backend._lib.sk_X509_EXTENSION_pop_free(
- ext,
- backend._ffi.addressof(backend._lib._original_lib, "X509_EXTENSION_free")
- )
- )
-
- for i in range(backend._lib.sk_X509_EXTENSION_num(extensions)):
- ext = backend._lib.sk_X509_EXTENSION_value(extensions, i)
- if ext == backend._ffi.NULL:
- continue
- crit = backend._lib.X509_EXTENSION_get_critical(ext)
- data = backend._lib.X509_EXTENSION_get_data(ext)
- backend.openssl_assert(data != backend._ffi.NULL)
- der = backend._ffi.buffer(data.data, data.length)[:]
- entry = dict(
- critical=(crit == 1),
- value=base64.b64encode(der),
- )
- oid = _obj2txt(backend._lib, backend._ffi, backend._lib.X509_EXTENSION_get_object(ext))
- result[oid] = entry
- return result
-
-
-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 cryptography_name_to_oid(name):
- dotted = _OID_LOOKUP.get(name)
- if dotted is None:
- raise OpenSSLObjectError('Cannot find OID for "{0}"'.format(name))
- return x509.oid.ObjectIdentifier(dotted)
-
-
-def cryptography_oid_to_name(oid, short=False):
- dotted_string = oid.dotted_string
- names = _OID_MAP.get(dotted_string)
- name = names[0] if names else oid._name
- if short:
- return _NORMALIZE_NAMES_SHORT.get(name, name)
- else:
- return _NORMALIZE_NAMES.get(name, name)
-
-
-def cryptography_get_name(name):
- '''
- Given a name string, returns a cryptography x509.Name object.
- Raises an OpenSSLObjectError if the name is unknown or cannot be parsed.
- '''
- try:
- if name.startswith('DNS:'):
- return x509.DNSName(to_text(name[4:]))
- if name.startswith('IP:'):
- return x509.IPAddress(ipaddress.ip_address(to_text(name[3:])))
- if name.startswith('email:'):
- return x509.RFC822Name(to_text(name[6:]))
- if name.startswith('URI:'):
- return x509.UniformResourceIdentifier(to_text(name[4:]))
- except Exception as e:
- raise OpenSSLObjectError('Cannot parse Subject Alternative Name "{0}": {1}'.format(name, e))
- if ':' not in name:
- raise OpenSSLObjectError('Cannot parse Subject Alternative Name "{0}" (forgot "DNS:" prefix?)'.format(name))
- raise OpenSSLObjectError('Cannot parse Subject Alternative Name "{0}" (potentially unsupported by cryptography backend)'.format(name))
-
-
-def _get_hex(bytesstr):
- if bytesstr is None:
- return bytesstr
- data = binascii.hexlify(bytesstr)
- data = to_text(b':'.join(data[i:i + 2] for i in range(0, len(data), 2)))
- return data
-
-
-def cryptography_decode_name(name):
- '''
- Given a cryptography x509.Name object, returns a string.
- Raises an OpenSSLObjectError if the name is not supported.
- '''
- if isinstance(name, x509.DNSName):
- return 'DNS:{0}'.format(name.value)
- if isinstance(name, x509.IPAddress):
- return 'IP:{0}'.format(name.value.compressed)
- if isinstance(name, x509.RFC822Name):
- return 'email:{0}'.format(name.value)
- if isinstance(name, x509.UniformResourceIdentifier):
- return 'URI:{0}'.format(name.value)
- if isinstance(name, x509.DirectoryName):
- # FIXME: test
- return 'DirName:' + ''.join(['/{0}:{1}'.format(attribute.oid._name, attribute.value) for attribute in name.value])
- if isinstance(name, x509.RegisteredID):
- # FIXME: test
- return 'RegisteredID:{0}'.format(name.value)
- if isinstance(name, x509.OtherName):
- # FIXME: test
- return '{0}:{1}'.format(name.type_id.dotted_string, _get_hex(name.value))
- raise OpenSSLObjectError('Cannot decode name "{0}"'.format(name))
-
-
-def _cryptography_get_keyusage(usage):
- '''
- Given a key usage identifier string, returns the parameter name used by cryptography's x509.KeyUsage().
- Raises an OpenSSLObjectError if the identifier is unknown.
- '''
- if usage in ('Digital Signature', 'digitalSignature'):
- return 'digital_signature'
- if usage in ('Non Repudiation', 'nonRepudiation'):
- return 'content_commitment'
- if usage in ('Key Encipherment', 'keyEncipherment'):
- return 'key_encipherment'
- if usage in ('Data Encipherment', 'dataEncipherment'):
- return 'data_encipherment'
- if usage in ('Key Agreement', 'keyAgreement'):
- return 'key_agreement'
- if usage in ('Certificate Sign', 'keyCertSign'):
- return 'key_cert_sign'
- if usage in ('CRL Sign', 'cRLSign'):
- return 'crl_sign'
- if usage in ('Encipher Only', 'encipherOnly'):
- return 'encipher_only'
- if usage in ('Decipher Only', 'decipherOnly'):
- return 'decipher_only'
- raise OpenSSLObjectError('Unknown key usage "{0}"'.format(usage))
-
-
-def cryptography_parse_key_usage_params(usages):
- '''
- Given a list of key usage identifier strings, returns the parameters for cryptography's x509.KeyUsage().
- Raises an OpenSSLObjectError if an identifier is unknown.
- '''
- params = dict(
- digital_signature=False,
- content_commitment=False,
- key_encipherment=False,
- data_encipherment=False,
- key_agreement=False,
- key_cert_sign=False,
- crl_sign=False,
- encipher_only=False,
- decipher_only=False,
- )
- for usage in usages:
- params[_cryptography_get_keyusage(usage)] = True
- return params
-
-
-def cryptography_get_basic_constraints(constraints):
- '''
- Given a list of constraints, returns a tuple (ca, path_length).
- Raises an OpenSSLObjectError if a constraint is unknown or cannot be parsed.
- '''
- ca = False
- path_length = None
- if constraints:
- for constraint in constraints:
- if constraint.startswith('CA:'):
- if constraint == 'CA:TRUE':
- ca = True
- elif constraint == 'CA:FALSE':
- ca = False
- else:
- raise OpenSSLObjectError('Unknown basic constraint value "{0}" for CA'.format(constraint[3:]))
- elif constraint.startswith('pathlen:'):
- v = constraint[len('pathlen:'):]
- try:
- path_length = int(v)
- except Exception as e:
- raise OpenSSLObjectError('Cannot parse path length constraint "{0}" ({1})'.format(v, e))
- else:
- raise OpenSSLObjectError('Unknown basic constraint "{0}"'.format(constraint))
- return ca, path_length
-
-
-def binary_exp_mod(f, e, m):
- '''Computes f^e mod m in O(log e) multiplications modulo m.'''
- # Compute len_e = floor(log_2(e))
- len_e = -1
- x = e
- while x > 0:
- x >>= 1
- len_e += 1
- # Compute f**e mod m
- result = 1
- for k in range(len_e, -1, -1):
- result = (result * result) % m
- if ((e >> k) & 1) != 0:
- result = (result * f) % m
- return result
-
-
-def simple_gcd(a, b):
- '''Compute GCD of its two inputs.'''
- while b != 0:
- a, b = b, a % b
- return a
-
-
-def quick_is_not_prime(n):
- '''Does some quick checks to see if we can poke a hole into the primality of n.
-
- A result of `False` does **not** mean that the number is prime; it just means
- that we couldn't detect quickly whether it is not prime.
- '''
- if n <= 2:
- return True
- # The constant in the next line is the product of all primes < 200
- if simple_gcd(n, 7799922041683461553249199106329813876687996789903550945093032474868511536164700810) > 1:
- return True
- # TODO: maybe do some iterations of Miller-Rabin to increase confidence
- # (https://en.wikipedia.org/wiki/Miller%E2%80%93Rabin_primality_test)
- return False
-
-
-python_version = (sys.version_info[0], sys.version_info[1])
-if python_version >= (2, 7) or python_version >= (3, 1):
- # Ansible still supports Python 2.6 on remote nodes
- def count_bits(no):
- no = abs(no)
- if no == 0:
- return 0
- return no.bit_length()
-else:
- # Slow, but works
- def count_bits(no):
- no = abs(no)
- count = 0
- while no > 0:
- no >>= 1
- count += 1
- return count
-
-
-PEM_START = '-----BEGIN '
-PEM_END = '-----'
-PKCS8_PRIVATEKEY_NAMES = ('PRIVATE KEY', 'ENCRYPTED PRIVATE KEY')
-PKCS1_PRIVATEKEY_SUFFIX = ' PRIVATE KEY'
-
-
-def identify_private_key_format(content):
- '''Given the contents of a private key file, identifies its format.'''
- # See https://github.com/openssl/openssl/blob/master/crypto/pem/pem_pkey.c#L40-L85
- # (PEM_read_bio_PrivateKey)
- # and https://github.com/openssl/openssl/blob/master/include/openssl/pem.h#L46-L47
- # (PEM_STRING_PKCS8, PEM_STRING_PKCS8INF)
- try:
- lines = content.decode('utf-8').splitlines(False)
- if lines[0].startswith(PEM_START) and lines[0].endswith(PEM_END) and len(lines[0]) > len(PEM_START) + len(PEM_END):
- name = lines[0][len(PEM_START):-len(PEM_END)]
- if name in PKCS8_PRIVATEKEY_NAMES:
- return 'pkcs8'
- if len(name) > len(PKCS1_PRIVATEKEY_SUFFIX) and name.endswith(PKCS1_PRIVATEKEY_SUFFIX):
- return 'pkcs1'
- return 'unknown-pem'
- except UnicodeDecodeError:
- pass
- return 'raw'
-
-
-def cryptography_key_needs_digest_for_signing(key):
- '''Tests whether the given private key requires a digest algorithm for signing.
-
- Ed25519 and Ed448 keys do not; they need None to be passed as the digest algorithm.
- '''
- if CRYPTOGRAPHY_HAS_ED25519 and isinstance(key, cryptography.hazmat.primitives.asymmetric.ed25519.Ed25519PrivateKey):
- return False
- if CRYPTOGRAPHY_HAS_ED448 and isinstance(key, cryptography.hazmat.primitives.asymmetric.ed448.Ed448PrivateKey):
- return False
- return True
-
-
-def cryptography_compare_public_keys(key1, key2):
- '''Tests whether two public keys are the same.
-
- Needs special logic for Ed25519 and Ed448 keys, since they do not have public_numbers().
- '''
- if CRYPTOGRAPHY_HAS_ED25519:
- a = isinstance(key1, cryptography.hazmat.primitives.asymmetric.ed25519.Ed25519PublicKey)
- b = isinstance(key2, cryptography.hazmat.primitives.asymmetric.ed25519.Ed25519PublicKey)
- if a or b:
- if not a or not b:
- return False
- a = key1.public_bytes(serialization.Encoding.Raw, serialization.PublicFormat.Raw)
- b = key2.public_bytes(serialization.Encoding.Raw, serialization.PublicFormat.Raw)
- return a == b
- if CRYPTOGRAPHY_HAS_ED448:
- a = isinstance(key1, cryptography.hazmat.primitives.asymmetric.ed448.Ed448PublicKey)
- b = isinstance(key2, cryptography.hazmat.primitives.asymmetric.ed448.Ed448PublicKey)
- if a or b:
- if not a or not b:
- return False
- a = key1.public_bytes(serialization.Encoding.Raw, serialization.PublicFormat.Raw)
- b = key2.public_bytes(serialization.Encoding.Raw, serialization.PublicFormat.Raw)
- return a == b
- return key1.public_numbers() == key2.public_numbers()
-
-
-if HAS_CRYPTOGRAPHY:
- REVOCATION_REASON_MAP = {
- 'unspecified': x509.ReasonFlags.unspecified,
- 'key_compromise': x509.ReasonFlags.key_compromise,
- 'ca_compromise': x509.ReasonFlags.ca_compromise,
- 'affiliation_changed': x509.ReasonFlags.affiliation_changed,
- 'superseded': x509.ReasonFlags.superseded,
- 'cessation_of_operation': x509.ReasonFlags.cessation_of_operation,
- 'certificate_hold': x509.ReasonFlags.certificate_hold,
- 'privilege_withdrawn': x509.ReasonFlags.privilege_withdrawn,
- 'aa_compromise': x509.ReasonFlags.aa_compromise,
- 'remove_from_crl': x509.ReasonFlags.remove_from_crl,
- }
- REVOCATION_REASON_MAP_INVERSE = dict()
- for k, v in REVOCATION_REASON_MAP.items():
- REVOCATION_REASON_MAP_INVERSE[v] = k
-
-
-def cryptography_decode_revoked_certificate(cert):
- result = {
- 'serial_number': cert.serial_number,
- 'revocation_date': cert.revocation_date,
- 'issuer': None,
- 'issuer_critical': False,
- 'reason': None,
- 'reason_critical': False,
- 'invalidity_date': None,
- 'invalidity_date_critical': False,
- }
- try:
- ext = cert.extensions.get_extension_for_class(x509.CertificateIssuer)
- result['issuer'] = list(ext.value)
- result['issuer_critical'] = ext.critical
- except x509.ExtensionNotFound:
- pass
- try:
- ext = cert.extensions.get_extension_for_class(x509.CRLReason)
- result['reason'] = ext.value.reason
- result['reason_critical'] = ext.critical
- except x509.ExtensionNotFound:
- pass
- try:
- ext = cert.extensions.get_extension_for_class(x509.InvalidityDate)
- result['invalidity_date'] = ext.value.invalidity_date
- result['invalidity_date_critical'] = ext.critical
- except x509.ExtensionNotFound:
- pass
- return result
diff --git a/lib/ansible/module_utils/ecs/api.py b/lib/ansible/module_utils/ecs/api.py
deleted file mode 100644
index d89b03330b..0000000000
--- a/lib/ansible/module_utils/ecs/api.py
+++ /dev/null
@@ -1,364 +0,0 @@
-# -*- coding: utf-8 -*-
-
-# This code is part of Ansible, but is an independent component.
-# This particular file snippet, and this file snippet only, is licensed under the
-# Modified BSD License. Modules you write using this snippet, which is embedded
-# dynamically by Ansible, still belong to the author of the module, and may assign
-# their own license to the complete work.
-#
-# Copyright (c), Entrust Datacard Corporation, 2019
-# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
-
-# Redistribution and use in source and binary forms, with or without modification,
-# are permitted provided that the following conditions are met:
-# 1. Redistributions of source code must retain the above copyright notice,
-# this list of conditions and the following disclaimer.
-# 2. Redistributions in binary form must reproduce the above copyright notice,
-# this list of conditions and the following disclaimer in the documentation
-# and/or other materials provided with the distribution.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
-# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
-# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
-# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-from __future__ import absolute_import, division, print_function
-
-__metaclass__ = type
-
-import json
-import os
-import re
-import time
-import traceback
-
-from ansible.module_utils._text import to_text, to_native
-from ansible.module_utils.basic import missing_required_lib
-from ansible.module_utils.six.moves.urllib.parse import urlencode
-from ansible.module_utils.six.moves.urllib.error import HTTPError
-from ansible.module_utils.urls import Request
-
-YAML_IMP_ERR = None
-try:
- import yaml
-except ImportError:
- YAML_FOUND = False
- YAML_IMP_ERR = traceback.format_exc()
-else:
- YAML_FOUND = True
-
-valid_file_format = re.compile(r".*(\.)(yml|yaml|json)$")
-
-
-def ecs_client_argument_spec():
- return dict(
- entrust_api_user=dict(type='str', required=True),
- entrust_api_key=dict(type='str', required=True, no_log=True),
- entrust_api_client_cert_path=dict(type='path', required=True),
- entrust_api_client_cert_key_path=dict(type='path', required=True, no_log=True),
- entrust_api_specification_path=dict(type='path', default='https://cloud.entrust.net/EntrustCloud/documentation/cms-api-2.1.0.yaml'),
- )
-
-
-class SessionConfigurationException(Exception):
- """ Raised if we cannot configure a session with the API """
-
- pass
-
-
-class RestOperationException(Exception):
- """ Encapsulate a REST API error """
-
- def __init__(self, error):
- self.status = to_native(error.get("status", None))
- self.errors = [to_native(err.get("message")) for err in error.get("errors", {})]
- self.message = to_native(" ".join(self.errors))
-
-
-def generate_docstring(operation_spec):
- """Generate a docstring for an operation defined in operation_spec (swagger)"""
- # Description of the operation
- docs = operation_spec.get("description", "No Description")
- docs += "\n\n"
-
- # Parameters of the operation
- parameters = operation_spec.get("parameters", [])
- if len(parameters) != 0:
- docs += "\tArguments:\n\n"
- for parameter in parameters:
- docs += "{0} ({1}:{2}): {3}\n".format(
- parameter.get("name"),
- parameter.get("type", "No Type"),
- "Required" if parameter.get("required", False) else "Not Required",
- parameter.get("description"),
- )
-
- return docs
-
-
-def bind(instance, method, operation_spec):
- def binding_scope_fn(*args, **kwargs):
- return method(instance, *args, **kwargs)
-
- # Make sure we don't confuse users; add the proper name and documentation to the function.
- # Users can use !help(<function>) to get help on the function from interactive python or pdb
- operation_name = operation_spec.get("operationId").split("Using")[0]
- binding_scope_fn.__name__ = str(operation_name)
- binding_scope_fn.__doc__ = generate_docstring(operation_spec)
-
- return binding_scope_fn
-
-
-class RestOperation(object):
- def __init__(self, session, uri, method, parameters=None):
- self.session = session
- self.method = method
- if parameters is None:
- self.parameters = {}
- else:
- self.parameters = parameters
- self.url = "{scheme}://{host}{base_path}{uri}".format(scheme="https", host=session._spec.get("host"), base_path=session._spec.get("basePath"), uri=uri)
-
- def restmethod(self, *args, **kwargs):
- """Do the hard work of making the request here"""
-
- # gather named path parameters and do substitution on the URL
- if self.parameters:
- path_parameters = {}
- body_parameters = {}
- query_parameters = {}
- for x in self.parameters:
- expected_location = x.get("in")
- key_name = x.get("name", None)
- key_value = kwargs.get(key_name, None)
- if expected_location == "path" and key_name and key_value:
- path_parameters.update({key_name: key_value})
- elif expected_location == "body" and key_name and key_value:
- body_parameters.update({key_name: key_value})
- elif expected_location == "query" and key_name and key_value:
- query_parameters.update({key_name: key_value})
-
- if len(body_parameters.keys()) >= 1:
- body_parameters = body_parameters.get(list(body_parameters.keys())[0])
- else:
- body_parameters = None
- else:
- path_parameters = {}
- query_parameters = {}
- body_parameters = None
-
- # This will fail if we have not set path parameters with a KeyError
- url = self.url.format(**path_parameters)
- if query_parameters:
- # modify the URL to add path parameters
- url = url + "?" + urlencode(query_parameters)
-
- try:
- if body_parameters:
- body_parameters_json = json.dumps(body_parameters)
- response = self.session.request.open(method=self.method, url=url, data=body_parameters_json)
- else:
- response = self.session.request.open(method=self.method, url=url)
- request_error = False
- except HTTPError as e:
- # An HTTPError has the same methods available as a valid response from request.open
- response = e
- request_error = True
-
- # Return the result if JSON and success ({} for empty responses)
- # Raise an exception if there was a failure.
- try:
- result_code = response.getcode()
- result = json.loads(response.read())
- except ValueError:
- result = {}
-
- if result or result == {}:
- if result_code and result_code < 400:
- return result
- else:
- raise RestOperationException(result)
-
- # Raise a generic RestOperationException if this fails
- raise RestOperationException({"status": result_code, "errors": [{"message": "REST Operation Failed"}]})
-
-
-class Resource(object):
- """ Implement basic CRUD operations against a path. """
-
- def __init__(self, session):
- self.session = session
- self.parameters = {}
-
- for url in session._spec.get("paths").keys():
- methods = session._spec.get("paths").get(url)
- for method in methods.keys():
- operation_spec = methods.get(method)
- operation_name = operation_spec.get("operationId", None)
- parameters = operation_spec.get("parameters")
-
- if not operation_name:
- if method.lower() == "post":
- operation_name = "Create"
- elif method.lower() == "get":
- operation_name = "Get"
- elif method.lower() == "put":
- operation_name = "Update"
- elif method.lower() == "delete":
- operation_name = "Delete"
- elif method.lower() == "patch":
- operation_name = "Patch"
- else:
- raise SessionConfigurationException(to_native("Invalid REST method type {0}".format(method)))
-
- # Get the non-parameter parts of the URL and append to the operation name
- # e.g /application/version -> GetApplicationVersion
- # e.g. /application/{id} -> GetApplication
- # This may lead to duplicates, which we must prevent.
- operation_name += re.sub(r"{(.*)}", "", url).replace("/", " ").title().replace(" ", "")
- operation_spec["operationId"] = operation_name
-
- op = RestOperation(session, url, method, parameters)
- setattr(self, operation_name, bind(self, op.restmethod, operation_spec))
-
-
-# Session to encapsulate the connection parameters of the module_utils Request object, the api spec, etc
-class ECSSession(object):
- def __init__(self, name, **kwargs):
- """
- Initialize our session
- """
-
- self._set_config(name, **kwargs)
-
- def client(self):
- resource = Resource(self)
- return resource
-
- def _set_config(self, name, **kwargs):
- headers = {
- "Content-Type": "application/json",
- "Connection": "keep-alive",
- }
- self.request = Request(headers=headers, timeout=60)
-
- configurators = [self._read_config_vars]
- for configurator in configurators:
- self._config = configurator(name, **kwargs)
- if self._config:
- break
- if self._config is None:
- raise SessionConfigurationException(to_native("No Configuration Found."))
-
- # set up auth if passed
- entrust_api_user = self.get_config("entrust_api_user")
- entrust_api_key = self.get_config("entrust_api_key")
- if entrust_api_user and entrust_api_key:
- self.request.url_username = entrust_api_user
- self.request.url_password = entrust_api_key
- else:
- raise SessionConfigurationException(to_native("User and key must be provided."))
-
- # set up client certificate if passed (support all-in one or cert + key)
- entrust_api_cert = self.get_config("entrust_api_cert")
- entrust_api_cert_key = self.get_config("entrust_api_cert_key")
- if entrust_api_cert:
- self.request.client_cert = entrust_api_cert
- if entrust_api_cert_key:
- self.request.client_key = entrust_api_cert_key
- else:
- raise SessionConfigurationException(to_native("Client certificate for authentication to the API must be provided."))
-
- # set up the spec
- entrust_api_specification_path = self.get_config("entrust_api_specification_path")
-
- if not entrust_api_specification_path.startswith("http") and not os.path.isfile(entrust_api_specification_path):
- raise SessionConfigurationException(to_native("OpenAPI specification was not found at location {0}.".format(entrust_api_specification_path)))
- if not valid_file_format.match(entrust_api_specification_path):
- raise SessionConfigurationException(to_native("OpenAPI specification filename must end in .json, .yml or .yaml"))
-
- self.verify = True
-
- if entrust_api_specification_path.startswith("http"):
- try:
- http_response = Request().open(method="GET", url=entrust_api_specification_path)
- http_response_contents = http_response.read()
- if entrust_api_specification_path.endswith(".json"):
- self._spec = json.load(http_response_contents)
- elif entrust_api_specification_path.endswith(".yml") or entrust_api_specification_path.endswith(".yaml"):
- self._spec = yaml.safe_load(http_response_contents)
- except HTTPError as e:
- raise SessionConfigurationException(to_native("Error downloading specification from address '{0}', received error code '{1}'".format(
- entrust_api_specification_path, e.getcode())))
- else:
- with open(entrust_api_specification_path) as f:
- if ".json" in entrust_api_specification_path:
- self._spec = json.load(f)
- elif ".yml" in entrust_api_specification_path or ".yaml" in entrust_api_specification_path:
- self._spec = yaml.safe_load(f)
-
- def get_config(self, item):
- return self._config.get(item, None)
-
- def _read_config_vars(self, name, **kwargs):
- """ Read configuration from variables passed to the module. """
- config = {}
-
- entrust_api_specification_path = kwargs.get("entrust_api_specification_path")
- if not entrust_api_specification_path or (not entrust_api_specification_path.startswith("http") and not os.path.isfile(entrust_api_specification_path)):
- raise SessionConfigurationException(
- to_native(
- "Parameter provided for entrust_api_specification_path of value '{0}' was not a valid file path or HTTPS address.".format(
- entrust_api_specification_path
- )
- )
- )
-
- for required_file in ["entrust_api_cert", "entrust_api_cert_key"]:
- file_path = kwargs.get(required_file)
- if not file_path or not os.path.isfile(file_path):
- raise SessionConfigurationException(
- to_native("Parameter provided for {0} of value '{1}' was not a valid file path.".format(required_file, file_path))
- )
-
- for required_var in ["entrust_api_user", "entrust_api_key"]:
- if not kwargs.get(required_var):
- raise SessionConfigurationException(to_native("Parameter provided for {0} was missing.".format(required_var)))
-
- config["entrust_api_cert"] = kwargs.get("entrust_api_cert")
- config["entrust_api_cert_key"] = kwargs.get("entrust_api_cert_key")
- config["entrust_api_specification_path"] = kwargs.get("entrust_api_specification_path")
- config["entrust_api_user"] = kwargs.get("entrust_api_user")
- config["entrust_api_key"] = kwargs.get("entrust_api_key")
-
- return config
-
-
-def ECSClient(entrust_api_user=None, entrust_api_key=None, entrust_api_cert=None, entrust_api_cert_key=None, entrust_api_specification_path=None):
- """Create an ECS client"""
-
- if not YAML_FOUND:
- raise SessionConfigurationException(missing_required_lib("PyYAML"), exception=YAML_IMP_ERR)
-
- if entrust_api_specification_path is None:
- entrust_api_specification_path = "https://cloud.entrust.net/EntrustCloud/documentation/cms-api-2.1.0.yaml"
-
- # Not functionally necessary with current uses of this module_util, but better to be explicit for future use cases
- entrust_api_user = to_text(entrust_api_user)
- entrust_api_key = to_text(entrust_api_key)
- entrust_api_cert_key = to_text(entrust_api_cert_key)
- entrust_api_specification_path = to_text(entrust_api_specification_path)
-
- return ECSSession(
- "ecs",
- entrust_api_user=entrust_api_user,
- entrust_api_key=entrust_api_key,
- entrust_api_cert=entrust_api_cert,
- entrust_api_cert_key=entrust_api_cert_key,
- entrust_api_specification_path=entrust_api_specification_path,
- ).client()
diff --git a/lib/ansible/modules/crypto/acme/_acme_account_facts.py b/lib/ansible/modules/crypto/acme/_acme_account_facts.py
deleted file mode 120000
index ffd88bceb5..0000000000
--- a/lib/ansible/modules/crypto/acme/_acme_account_facts.py
+++ /dev/null
@@ -1 +0,0 @@
-acme_account_info.py \ No newline at end of file
diff --git a/lib/ansible/modules/crypto/acme/acme_account.py b/lib/ansible/modules/crypto/acme/acme_account.py
deleted file mode 100644
index ec922ce693..0000000000
--- a/lib/ansible/modules/crypto/acme/acme_account.py
+++ /dev/null
@@ -1,278 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-
-# (c) 2016 Michael Gruener <michael.gruener@chaosmoon.net>
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'community'}
-
-
-DOCUMENTATION = '''
----
-module: acme_account
-author: "Felix Fontein (@felixfontein)"
-version_added: "2.6"
-short_description: Create, modify or delete ACME accounts
-description:
- - "Allows to create, modify or delete accounts with a CA supporting the
- L(ACME protocol,https://tools.ietf.org/html/rfc8555),
- such as L(Let's Encrypt,https://letsencrypt.org/)."
- - "This module only works with the ACME v2 protocol."
-notes:
- - "The M(acme_certificate) module also allows to do basic account management.
- When using both modules, it is recommended to disable account management
- for M(acme_certificate). For that, use the C(modify_account) option of
- M(acme_certificate)."
-seealso:
- - name: Automatic Certificate Management Environment (ACME)
- description: The specification of the ACME protocol (RFC 8555).
- link: https://tools.ietf.org/html/rfc8555
- - module: acme_account_info
- description: Retrieves facts about an ACME account.
- - module: openssl_privatekey
- description: Can be used to create a private account key.
- - module: acme_inspect
- description: Allows to debug problems.
-extends_documentation_fragment:
- - acme
-options:
- state:
- description:
- - "The state of the account, to be identified by its account key."
- - "If the state is C(absent), the account will either not exist or be
- deactivated."
- - "If the state is C(changed_key), the account must exist. The account
- key will be changed; no other information will be touched."
- type: str
- required: true
- choices:
- - present
- - absent
- - changed_key
- allow_creation:
- description:
- - "Whether account creation is allowed (when state is C(present))."
- type: bool
- default: yes
- contact:
- description:
- - "A list of contact URLs."
- - "Email addresses must be prefixed with C(mailto:)."
- - "See U(https://tools.ietf.org/html/rfc8555#section-7.3)
- for what is allowed."
- - "Must be specified when state is C(present). Will be ignored
- if state is C(absent) or C(changed_key)."
- type: list
- elements: str
- default: []
- terms_agreed:
- description:
- - "Boolean indicating whether you agree to the terms of service document."
- - "ACME servers can require this to be true."
- type: bool
- default: no
- new_account_key_src:
- description:
- - "Path to a file containing the ACME account RSA or Elliptic Curve key to change to."
- - "Same restrictions apply as to C(account_key_src)."
- - "Mutually exclusive with C(new_account_key_content)."
- - "Required if C(new_account_key_content) is not used and state is C(changed_key)."
- type: path
- new_account_key_content:
- description:
- - "Content of the ACME account RSA or Elliptic Curve key to change to."
- - "Same restrictions apply as to C(account_key_content)."
- - "Mutually exclusive with C(new_account_key_src)."
- - "Required if C(new_account_key_src) is not used and state is C(changed_key)."
- type: str
-'''
-
-EXAMPLES = '''
-- name: Make sure account exists and has given contacts. We agree to TOS.
- acme_account:
- account_key_src: /etc/pki/cert/private/account.key
- state: present
- terms_agreed: yes
- contact:
- - mailto:me@example.com
- - mailto:myself@example.org
-
-- name: Make sure account has given email address. Don't create account if it doesn't exist
- acme_account:
- account_key_src: /etc/pki/cert/private/account.key
- state: present
- allow_creation: no
- contact:
- - mailto:me@example.com
-
-- name: Change account's key to the one stored in the variable new_account_key
- acme_account:
- account_key_src: /etc/pki/cert/private/account.key
- new_account_key_content: '{{ new_account_key }}'
- state: changed_key
-
-- name: Delete account (we have to use the new key)
- acme_account:
- account_key_content: '{{ new_account_key }}'
- state: absent
-'''
-
-RETURN = '''
-account_uri:
- description: ACME account URI, or None if account does not exist.
- returned: always
- type: str
-'''
-
-from ansible.module_utils.acme import (
- ModuleFailException,
- ACMEAccount,
- handle_standard_module_arguments,
- get_default_argspec,
-)
-
-from ansible.module_utils.basic import AnsibleModule
-
-
-def main():
- argument_spec = get_default_argspec()
- argument_spec.update(dict(
- terms_agreed=dict(type='bool', default=False),
- state=dict(type='str', required=True, choices=['absent', 'present', 'changed_key']),
- allow_creation=dict(type='bool', default=True),
- contact=dict(type='list', elements='str', default=[]),
- new_account_key_src=dict(type='path'),
- new_account_key_content=dict(type='str', no_log=True),
- ))
- module = AnsibleModule(
- argument_spec=argument_spec,
- required_one_of=(
- ['account_key_src', 'account_key_content'],
- ),
- mutually_exclusive=(
- ['account_key_src', 'account_key_content'],
- ['new_account_key_src', 'new_account_key_content'],
- ),
- required_if=(
- # Make sure that for state == changed_key, one of
- # new_account_key_src and new_account_key_content are specified
- ['state', 'changed_key', ['new_account_key_src', 'new_account_key_content'], True],
- ),
- supports_check_mode=True,
- )
- handle_standard_module_arguments(module, needs_acme_v2=True)
-
- try:
- account = ACMEAccount(module)
- changed = False
- state = module.params.get('state')
- diff_before = {}
- diff_after = {}
- if state == 'absent':
- created, account_data = account.setup_account(allow_creation=False)
- if account_data:
- diff_before = dict(account_data)
- diff_before['public_account_key'] = account.key_data['jwk']
- if created:
- raise AssertionError('Unwanted account creation')
- if account_data is not None:
- # Account is not yet deactivated
- if not module.check_mode:
- # Deactivate it
- payload = {
- 'status': 'deactivated'
- }
- result, info = account.send_signed_request(account.uri, payload)
- if info['status'] != 200:
- raise ModuleFailException('Error deactivating account: {0} {1}'.format(info['status'], result))
- changed = True
- elif state == 'present':
- allow_creation = module.params.get('allow_creation')
- # Make sure contact is a list of strings (unfortunately, Ansible doesn't do that for us)
- contact = [str(v) for v in module.params.get('contact')]
- terms_agreed = module.params.get('terms_agreed')
- created, account_data = account.setup_account(
- contact,
- terms_agreed=terms_agreed,
- allow_creation=allow_creation,
- )
- if account_data is None:
- raise ModuleFailException(msg='Account does not exist or is deactivated.')
- if created:
- diff_before = {}
- else:
- diff_before = dict(account_data)
- diff_before['public_account_key'] = account.key_data['jwk']
- updated = False
- if not created:
- updated, account_data = account.update_account(account_data, contact)
- changed = created or updated
- diff_after = dict(account_data)
- diff_after['public_account_key'] = account.key_data['jwk']
- elif state == 'changed_key':
- # Parse new account key
- error, new_key_data = account.parse_key(
- module.params.get('new_account_key_src'),
- module.params.get('new_account_key_content')
- )
- if error:
- raise ModuleFailException("error while parsing account key: %s" % error)
- # Verify that the account exists and has not been deactivated
- created, account_data = account.setup_account(allow_creation=False)
- if created:
- raise AssertionError('Unwanted account creation')
- if account_data is None:
- raise ModuleFailException(msg='Account does not exist or is deactivated.')
- diff_before = dict(account_data)
- diff_before['public_account_key'] = account.key_data['jwk']
- # Now we can start the account key rollover
- if not module.check_mode:
- # Compose inner signed message
- # https://tools.ietf.org/html/rfc8555#section-7.3.5
- url = account.directory['keyChange']
- protected = {
- "alg": new_key_data['alg'],
- "jwk": new_key_data['jwk'],
- "url": url,
- }
- payload = {
- "account": account.uri,
- "newKey": new_key_data['jwk'], # specified in draft 12 and older
- "oldKey": account.jwk, # specified in draft 13 and newer
- }
- data = account.sign_request(protected, payload, new_key_data)
- # Send request and verify result
- result, info = account.send_signed_request(url, data)
- if info['status'] != 200:
- raise ModuleFailException('Error account key rollover: {0} {1}'.format(info['status'], result))
- if module._diff:
- account.key_data = new_key_data
- account.jws_header['alg'] = new_key_data['alg']
- diff_after = account.get_account_data()
- elif module._diff:
- # Kind of fake diff_after
- diff_after = dict(diff_before)
- diff_after['public_account_key'] = new_key_data['jwk']
- changed = True
- result = {
- 'changed': changed,
- 'account_uri': account.uri,
- }
- if module._diff:
- result['diff'] = {
- 'before': diff_before,
- 'after': diff_after,
- }
- module.exit_json(**result)
- except ModuleFailException as e:
- e.do_fail(module)
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/crypto/acme/acme_account_info.py b/lib/ansible/modules/crypto/acme/acme_account_info.py
deleted file mode 100644
index f60eb42a29..0000000000
--- a/lib/ansible/modules/crypto/acme/acme_account_info.py
+++ /dev/null
@@ -1,301 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-
-# (c) 2018 Felix Fontein <felix@fontein.de>
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'community'}
-
-
-DOCUMENTATION = '''
----
-module: acme_account_info
-author: "Felix Fontein (@felixfontein)"
-version_added: "2.7"
-short_description: Retrieves information on ACME accounts
-description:
- - "Allows to retrieve information on accounts a CA supporting the
- L(ACME protocol,https://tools.ietf.org/html/rfc8555),
- such as L(Let's Encrypt,https://letsencrypt.org/)."
- - "This module only works with the ACME v2 protocol."
-notes:
- - "The M(acme_account) module allows to modify, create and delete ACME accounts."
- - "This module was called C(acme_account_facts) before Ansible 2.8. The usage
- did not change."
-options:
- retrieve_orders:
- description:
- - "Whether to retrieve the list of order URLs or order objects, if provided
- by the ACME server."
- - "A value of C(ignore) will not fetch the list of orders."
- - "Currently, Let's Encrypt does not return orders, so the C(orders) result
- will always be empty."
- type: str
- choices:
- - ignore
- - url_list
- - object_list
- default: ignore
- version_added: "2.9"
-seealso:
- - module: acme_account
- description: Allows to create, modify or delete an ACME account.
-extends_documentation_fragment:
- - acme
-'''
-
-EXAMPLES = '''
-- name: Check whether an account with the given account key exists
- acme_account_info:
- account_key_src: /etc/pki/cert/private/account.key
- register: account_data
-- name: Verify that account exists
- assert:
- that:
- - account_data.exists
-- name: Print account URI
- debug: var=account_data.account_uri
-- name: Print account contacts
- debug: var=account_data.account.contact
-
-- name: Check whether the account exists and is accessible with the given account key
- acme_account_info:
- account_key_content: "{{ acme_account_key }}"
- account_uri: "{{ acme_account_uri }}"
- register: account_data
-- name: Verify that account exists
- assert:
- that:
- - account_data.exists
-- name: Print account contacts
- debug: var=account_data.account.contact
-'''
-
-RETURN = '''
-exists:
- description: Whether the account exists.
- returned: always
- type: bool
-
-account_uri:
- description: ACME account URI, or None if account does not exist.
- returned: always
- type: str
-
-account:
- description: The account information, as retrieved from the ACME server.
- returned: if account exists
- type: dict
- contains:
- contact:
- description: the challenge resource that must be created for validation
- returned: always
- type: list
- elements: str
- sample: "['mailto:me@example.com', 'tel:00123456789']"
- status:
- description: the account's status
- returned: always
- type: str
- choices: ['valid', 'deactivated', 'revoked']
- sample: valid
- orders:
- description:
- - A URL where a list of orders can be retrieved for this account.
- - Use the I(retrieve_orders) option to query this URL and retrieve the
- complete list of orders.
- returned: always
- type: str
- sample: https://example.ca/account/1/orders
- public_account_key:
- description: the public account key as a L(JSON Web Key,https://tools.ietf.org/html/rfc7517).
- returned: always
- type: str
- sample: '{"kty":"EC","crv":"P-256","x":"MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4","y":"4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM"}'
-
-orders:
- description:
- - "The list of orders."
- - "If I(retrieve_orders) is C(url_list), this will be a list of URLs."
- - "If I(retrieve_orders) is C(object_list), this will be a list of objects."
- type: list
- #elements: ... depends on retrieve_orders
- returned: if account exists, I(retrieve_orders) is not C(ignore), and server supports order listing
- contains:
- status:
- description: The order's status.
- type: str
- choices:
- - pending
- - ready
- - processing
- - valid
- - invalid
- expires:
- description:
- - When the order expires.
- - Timestamp should be formatted as described in RFC3339.
- - Only required to be included in result when I(status) is C(pending) or C(valid).
- type: str
- returned: when server gives expiry date
- identifiers:
- description:
- - List of identifiers this order is for.
- type: list
- elements: dict
- contains:
- type:
- description: Type of identifier. C(dns) or C(ip).
- type: str
- value:
- description: Name of identifier. Hostname or IP address.
- type: str
- wildcard:
- description: "Whether I(value) is actually a wildcard. The wildcard
- prefix C(*.) is not included in I(value) if this is C(true)."
- type: bool
- returned: required to be included if the identifier is wildcarded
- notBefore:
- description:
- - The requested value of the C(notBefore) field in the certificate.
- - Date should be formatted as described in RFC3339.
- - Server is not required to return this.
- type: str
- returned: when server returns this
- notAfter:
- description:
- - The requested value of the C(notAfter) field in the certificate.
- - Date should be formatted as described in RFC3339.
- - Server is not required to return this.
- type: str
- returned: when server returns this
- error:
- description:
- - In case an error occurred during processing, this contains information about the error.
- - The field is structured as a problem document (RFC7807).
- type: dict
- returned: when an error occurred
- authorizations:
- description:
- - A list of URLs for authorizations for this order.
- type: list
- elements: str
- finalize:
- description:
- - A URL used for finalizing an ACME order.
- type: str
- certificate:
- description:
- - The URL for retrieving the certificate.
- type: str
- returned: when certificate was issued
-'''
-
-from ansible.module_utils.acme import (
- ModuleFailException,
- ACMEAccount,
- handle_standard_module_arguments,
- process_links,
- get_default_argspec,
-)
-
-from ansible.module_utils.basic import AnsibleModule
-
-
-def get_orders_list(module, account, orders_url):
- '''
- Retrieves orders list (handles pagination).
- '''
- orders = []
- while orders_url:
- # Get part of orders list
- res, info = account.get_request(orders_url, parse_json_result=True, fail_on_error=True)
- if not res.get('orders'):
- if orders:
- module.warn('When retrieving orders list part {0}, got empty result list'.format(orders_url))
- break
- # Add order URLs to result list
- orders.extend(res['orders'])
- # Extract URL of next part of results list
- new_orders_url = []
-
- def f(link, relation):
- if relation == 'next':
- new_orders_url.append(link)
-
- process_links(info, f)
- new_orders_url.append(None)
- previous_orders_url, orders_url = orders_url, new_orders_url.pop(0)
- if orders_url == previous_orders_url:
- # Prevent infinite loop
- orders_url = None
- return orders
-
-
-def get_order(account, order_url):
- '''
- Retrieve order data.
- '''
- return account.get_request(order_url, parse_json_result=True, fail_on_error=True)[0]
-
-
-def main():
- argument_spec = get_default_argspec()
- argument_spec.update(dict(
- retrieve_orders=dict(type='str', default='ignore', choices=['ignore', 'url_list', 'object_list']),
- ))
- module = AnsibleModule(
- argument_spec=argument_spec,
- required_one_of=(
- ['account_key_src', 'account_key_content'],
- ),
- mutually_exclusive=(
- ['account_key_src', 'account_key_content'],
- ),
- supports_check_mode=True,
- )
- if module._name == 'acme_account_facts':
- module.deprecate("The 'acme_account_facts' module has been renamed to 'acme_account_info'", version='2.12')
- handle_standard_module_arguments(module, needs_acme_v2=True)
-
- try:
- account = ACMEAccount(module)
- # Check whether account exists
- created, account_data = account.setup_account(
- [],
- allow_creation=False,
- remove_account_uri_if_not_exists=True,
- )
- if created:
- raise AssertionError('Unwanted account creation')
- result = {
- 'changed': False,
- 'exists': account.uri is not None,
- 'account_uri': account.uri,
- }
- if account.uri is not None:
- # Make sure promised data is there
- if 'contact' not in account_data:
- account_data['contact'] = []
- account_data['public_account_key'] = account.key_data['jwk']
- result['account'] = account_data
- # Retrieve orders list
- if account_data.get('orders') and module.params['retrieve_orders'] != 'ignore':
- orders = get_orders_list(module, account, account_data['orders'])
- if module.params['retrieve_orders'] == 'url_list':
- result['orders'] = orders
- else:
- result['orders'] = [get_order(account, order) for order in orders]
- module.exit_json(**result)
- except ModuleFailException as e:
- e.do_fail(module)
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/crypto/acme/acme_certificate.py b/lib/ansible/modules/crypto/acme/acme_certificate.py
deleted file mode 100644
index fe0f32b653..0000000000
--- a/lib/ansible/modules/crypto/acme/acme_certificate.py
+++ /dev/null
@@ -1,1265 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-
-# (c) 2016 Michael Gruener <michael.gruener@chaosmoon.net>
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'community'}
-
-
-DOCUMENTATION = '''
----
-module: acme_certificate
-author: "Michael Gruener (@mgruener)"
-version_added: "2.2"
-short_description: Create SSL/TLS certificates with the ACME protocol
-description:
- - "Create and renew SSL/TLS certificates with a CA supporting the
- L(ACME protocol,https://tools.ietf.org/html/rfc8555),
- such as L(Let's Encrypt,https://letsencrypt.org/) or
- L(Buypass,https://www.buypass.com/). The current implementation
- supports the C(http-01), C(dns-01) and C(tls-alpn-01) challenges."
- - "To use this module, it has to be executed twice. Either as two
- different tasks in the same run or during two runs. Note that the output
- of the first run needs to be recorded and passed to the second run as the
- module argument C(data)."
- - "Between these two tasks you have to fulfill the required steps for the
- chosen challenge by whatever means necessary. For C(http-01) that means
- creating the necessary challenge file on the destination webserver. For
- C(dns-01) the necessary dns record has to be created. For C(tls-alpn-01)
- the necessary certificate has to be created and served.
- It is I(not) the responsibility of this module to perform these steps."
- - "For details on how to fulfill these challenges, you might have to read through
- L(the main ACME specification,https://tools.ietf.org/html/rfc8555#section-8)
- and the L(TLS-ALPN-01 specification,https://www.rfc-editor.org/rfc/rfc8737.html#section-3).
- Also, consider the examples provided for this module."
- - "The module includes experimental support for IP identifiers according to
- the L(RFC 8738,https://www.rfc-editor.org/rfc/rfc8738.html)."
-notes:
- - "At least one of C(dest) and C(fullchain_dest) must be specified."
- - "This module includes basic account management functionality.
- If you want to have more control over your ACME account, use the M(acme_account)
- module and disable account management for this module using the C(modify_account)
- option."
- - "This module was called C(letsencrypt) before Ansible 2.6. The usage
- did not change."
-seealso:
- - name: The Let's Encrypt documentation
- description: Documentation for the Let's Encrypt Certification Authority.
- Provides useful information for example on rate limits.
- link: https://letsencrypt.org/docs/
- - name: Buypass Go SSL
- description: Documentation for the Buypass Certification Authority.
- Provides useful information for example on rate limits.
- link: https://www.buypass.com/ssl/products/acme
- - name: Automatic Certificate Management Environment (ACME)
- description: The specification of the ACME protocol (RFC 8555).
- link: https://tools.ietf.org/html/rfc8555
- - name: ACME TLS ALPN Challenge Extension
- description: The specification of the C(tls-alpn-01) challenge (RFC 8737).
- link: https://www.rfc-editor.org/rfc/rfc8737.html-05
- - module: acme_challenge_cert_helper
- description: Helps preparing C(tls-alpn-01) challenges.
- - module: openssl_privatekey
- description: Can be used to create private keys (both for certificates and accounts).
- - module: openssl_csr
- description: Can be used to create a Certificate Signing Request (CSR).
- - module: certificate_complete_chain
- description: Allows to find the root certificate for the returned fullchain.
- - module: acme_certificate_revoke
- description: Allows to revoke certificates.
- - module: acme_account
- description: Allows to create, modify or delete an ACME account.
- - module: acme_inspect
- description: Allows to debug problems.
-extends_documentation_fragment:
- - acme
-options:
- account_email:
- description:
- - "The email address associated with this account."
- - "It will be used for certificate expiration warnings."
- - "Note that when C(modify_account) is not set to C(no) and you also
- used the M(acme_account) module to specify more than one contact
- for your account, this module will update your account and restrict
- it to the (at most one) contact email address specified here."
- type: str
- agreement:
- description:
- - "URI to a terms of service document you agree to when using the
- ACME v1 service at C(acme_directory)."
- - Default is latest gathered from C(acme_directory) URL.
- - This option will only be used when C(acme_version) is 1.
- type: str
- terms_agreed:
- description:
- - "Boolean indicating whether you agree to the terms of service document."
- - "ACME servers can require this to be true."
- - This option will only be used when C(acme_version) is not 1.
- type: bool
- default: no
- version_added: "2.5"
- modify_account:
- description:
- - "Boolean indicating whether the module should create the account if
- necessary, and update its contact data."
- - "Set to C(no) if you want to use the M(acme_account) module to manage
- your account instead, and to avoid accidental creation of a new account
- using an old key if you changed the account key with M(acme_account)."
- - "If set to C(no), C(terms_agreed) and C(account_email) are ignored."
- type: bool
- default: yes
- version_added: "2.6"
- challenge:
- description: The challenge to be performed.
- type: str
- default: 'http-01'
- choices: [ 'http-01', 'dns-01', 'tls-alpn-01' ]
- csr:
- description:
- - "File containing the CSR for the new certificate."
- - "Can be created with C(openssl req ...)."
- - "The CSR may contain multiple Subject Alternate Names, but each one
- will lead to an individual challenge that must be fulfilled for the
- CSR to be signed."
- - "I(Note): the private key used to create the CSR I(must not) be the
- account key. This is a bad idea from a security point of view, and
- the CA should not accept the CSR. The ACME server should return an
- error in this case."
- type: path
- required: true
- aliases: ['src']
- data:
- description:
- - "The data to validate ongoing challenges. This must be specified for
- the second run of the module only."
- - "The value that must be used here will be provided by a previous use
- of this module. See the examples for more details."
- - "Note that for ACME v2, only the C(order_uri) entry of C(data) will
- be used. For ACME v1, C(data) must be non-empty to indicate the
- second stage is active; all needed data will be taken from the
- CSR."
- - "I(Note): the C(data) option was marked as C(no_log) up to
- Ansible 2.5. From Ansible 2.6 on, it is no longer marked this way
- as it causes error messages to be come unusable, and C(data) does
- not contain any information which can be used without having
- access to the account key or which are not public anyway."
- type: dict
- dest:
- description:
- - "The destination file for the certificate."
- - "Required if C(fullchain_dest) is not specified."
- type: path
- aliases: ['cert']
- fullchain_dest:
- description:
- - "The destination file for the full chain (i.e. certificate followed
- by chain of intermediate certificates)."
- - "Required if C(dest) is not specified."
- type: path
- version_added: 2.5
- aliases: ['fullchain']
- chain_dest:
- description:
- - If specified, the intermediate certificate will be written to this file.
- type: path
- version_added: 2.5
- aliases: ['chain']
- remaining_days:
- description:
- - "The number of days the certificate must have left being valid.
- If C(cert_days < remaining_days), then it will be renewed.
- If the certificate is not renewed, module return values will not
- include C(challenge_data)."
- - "To make sure that the certificate is renewed in any case, you can
- use the C(force) option."
- type: int
- default: 10
- deactivate_authzs:
- description:
- - "Deactivate authentication objects (authz) after issuing a certificate,
- or when issuing the certificate failed."
- - "Authentication objects are bound to an account key and remain valid
- for a certain amount of time, and can be used to issue certificates
- without having to re-authenticate the domain. This can be a security
- concern."
- type: bool
- default: no
- version_added: 2.6
- force:
- description:
- - Enforces the execution of the challenge and validation, even if an
- existing certificate is still valid for more than C(remaining_days).
- - This is especially helpful when having an updated CSR e.g. with
- additional domains for which a new certificate is desired.
- type: bool
- default: no
- version_added: 2.6
- retrieve_all_alternates:
- description:
- - "When set to C(yes), will retrieve all alternate trust chains offered by the ACME CA.
- These will not be written to disk, but will be returned together with the main
- chain as C(all_chains). See the documentation for the C(all_chains) return
- value for details."
- type: bool
- default: no
- version_added: "2.9"
- select_chain:
- description:
- - "Allows to specify criteria by which an (alternate) trust chain can be selected."
- - "The list of criteria will be processed one by one until a chain is found
- matching a criterium. If such a chain is found, it will be used by the
- module instead of the default chain."
- - "If a criterium matches multiple chains, the first one matching will be
- returned. The order is determined by the ordering of the C(Link) headers
- returned by the ACME server and might not be deterministic."
- - "Every criterium can consist of multiple different conditions, like I(issuer)
- and I(subject). For the criterium to match a chain, all conditions must apply
- to the same certificate in the chain."
- - "This option can only be used with the C(cryptography) backend."
- type: list
- version_added: "2.10"
- suboptions:
- test_certificates:
- description:
- - "Determines which certificates in the chain will be tested."
- - "I(all) tests all certificates in the chain (excluding the leaf, which is
- identical in all chains)."
- - "I(last) only tests the last certificate in the chain, i.e. the one furthest
- away from the leaf. Its issuer is the root certificate of this chain."
- type: str
- default: all
- choices: [last, all]
- issuer:
- description:
- - "Allows to specify parts of the issuer of a certificate in the chain must
- have to be selected."
- - "If I(issuer) is empty, any certificate will match."
- - 'An example value would be C({"commonName": "My Preferred CA Root"}).'
- type: dict
- subject:
- description:
- - "Allows to specify parts of the subject of a certificate in the chain must
- have to be selected."
- - "If I(subject) is empty, any certificate will match."
- - 'An example value would be C({"CN": "My Preferred CA Intermediate"})'
- type: dict
- subject_key_identifier:
- description:
- - "Checks for the SubjectKeyIdentifier extension. This is an identifier based
- on the private key of the intermediate certificate."
- - "The identifier must be of the form
- C(A8:4A:6A:63:04:7D:DD:BA:E6:D1:39:B7:A6:45:65:EF:F3:A8:EC:A1)."
- type: str
- authority_key_identifier:
- description:
- - "Checks for the AuthorityKeyIdentifier extension. This is an identifier based
- on the private key of the issuer of the intermediate certificate."
- - "The identifier must be of the form
- C(C4:A7:B1:A4:7B:2C:71:FA:DB:E1:4B:90:75:FF:C4:15:60:85:89:10)."
- type: str
-'''
-
-EXAMPLES = r'''
-### Example with HTTP challenge ###
-
-- name: Create a challenge for sample.com using a account key from a variable.
- acme_certificate:
- account_key_content: "{{ account_private_key }}"
- csr: /etc/pki/cert/csr/sample.com.csr
- dest: /etc/httpd/ssl/sample.com.crt
- register: sample_com_challenge
-
-# Alternative first step:
-- name: Create a challenge for sample.com using a account key from hashi vault.
- acme_certificate:
- account_key_content: "{{ lookup('hashi_vault', 'secret=secret/account_private_key:value') }}"
- csr: /etc/pki/cert/csr/sample.com.csr
- fullchain_dest: /etc/httpd/ssl/sample.com-fullchain.crt
- register: sample_com_challenge
-
-# Alternative first step:
-- name: Create a challenge for sample.com using a account key file.
- acme_certificate:
- account_key_src: /etc/pki/cert/private/account.key
- csr: /etc/pki/cert/csr/sample.com.csr
- dest: /etc/httpd/ssl/sample.com.crt
- fullchain_dest: /etc/httpd/ssl/sample.com-fullchain.crt
- register: sample_com_challenge
-
-# perform the necessary steps to fulfill the challenge
-# for example:
-#
-# - copy:
-# dest: /var/www/html/{{ sample_com_challenge['challenge_data']['sample.com']['http-01']['resource'] }}
-# content: "{{ sample_com_challenge['challenge_data']['sample.com']['http-01']['resource_value'] }}"
-# when: sample_com_challenge is changed
-
-- name: Let the challenge be validated and retrieve the cert and intermediate certificate
- acme_certificate:
- account_key_src: /etc/pki/cert/private/account.key
- csr: /etc/pki/cert/csr/sample.com.csr
- dest: /etc/httpd/ssl/sample.com.crt
- fullchain_dest: /etc/httpd/ssl/sample.com-fullchain.crt
- chain_dest: /etc/httpd/ssl/sample.com-intermediate.crt
- data: "{{ sample_com_challenge }}"
-
-### Example with DNS challenge against production ACME server ###
-
-- name: Create a challenge for sample.com using a account key file.
- acme_certificate:
- account_key_src: /etc/pki/cert/private/account.key
- account_email: myself@sample.com
- src: /etc/pki/cert/csr/sample.com.csr
- cert: /etc/httpd/ssl/sample.com.crt
- challenge: dns-01
- acme_directory: https://acme-v01.api.letsencrypt.org/directory
- # Renew if the certificate is at least 30 days old
- remaining_days: 60
- register: sample_com_challenge
-
-# perform the necessary steps to fulfill the challenge
-# for example:
-#
-# - route53:
-# zone: sample.com
-# record: "{{ sample_com_challenge.challenge_data['sample.com']['dns-01'].record }}"
-# type: TXT
-# ttl: 60
-# state: present
-# wait: yes
-# # Note: route53 requires TXT entries to be enclosed in quotes
-# value: "{{ sample_com_challenge.challenge_data['sample.com']['dns-01'].resource_value | regex_replace('^(.*)$', '\"\\1\"') }}"
-# when: sample_com_challenge is changed
-#
-# Alternative way:
-#
-# - route53:
-# zone: sample.com
-# record: "{{ item.key }}"
-# type: TXT
-# ttl: 60
-# state: present
-# wait: yes
-# # Note: item.value is a list of TXT entries, and route53
-# # requires every entry to be enclosed in quotes
-# value: "{{ item.value | map('regex_replace', '^(.*)$', '\"\\1\"' ) | list }}"
-# loop: "{{ sample_com_challenge.challenge_data_dns | dictsort }}"
-# when: sample_com_challenge is changed
-
-- name: Let the challenge be validated and retrieve the cert and intermediate certificate
- acme_certificate:
- account_key_src: /etc/pki/cert/private/account.key
- account_email: myself@sample.com
- src: /etc/pki/cert/csr/sample.com.csr
- cert: /etc/httpd/ssl/sample.com.crt
- fullchain: /etc/httpd/ssl/sample.com-fullchain.crt
- chain: /etc/httpd/ssl/sample.com-intermediate.crt
- challenge: dns-01
- acme_directory: https://acme-v01.api.letsencrypt.org/directory
- remaining_days: 60
- data: "{{ sample_com_challenge }}"
- when: sample_com_challenge is changed
-
-# Alternative second step:
-- name: Let the challenge be validated and retrieve the cert and intermediate certificate
- acme_certificate:
- account_key_src: /etc/pki/cert/private/account.key
- account_email: myself@sample.com
- src: /etc/pki/cert/csr/sample.com.csr
- cert: /etc/httpd/ssl/sample.com.crt
- fullchain: /etc/httpd/ssl/sample.com-fullchain.crt
- chain: /etc/httpd/ssl/sample.com-intermediate.crt
- challenge: tls-alpn-01
- remaining_days: 60
- data: "{{ sample_com_challenge }}"
- # We use Let's Encrypt's ACME v2 endpoint
- acme_directory: https://acme-v02.api.letsencrypt.org/directory
- acme_version: 2
- # The following makes sure that if a chain with /CN=DST Root CA X3 in its issuer is provided
- # as an alternative, it will be selected. These are the roots cross-signed by IdenTrust.
- # As long as Let's Encrypt provides alternate chains with the cross-signed root(s) when
- # switching to their own ISRG Root X1 root, this will use the chain ending with a cross-signed
- # root. This chain is more compatible with older TLS clients.
- select_chain:
- - test_certificates: last
- issuer:
- CN: DST Root CA X3
- O: Digital Signature Trust Co.
- when: sample_com_challenge is changed
-'''
-
-RETURN = '''
-cert_days:
- description: The number of days the certificate remains valid.
- returned: success
- type: int
-challenge_data:
- description:
- - Per identifier / challenge type challenge data.
- - Since Ansible 2.8.5, only challenges which are not yet valid are returned.
- returned: changed
- type: list
- elements: dict
- contains:
- resource:
- description: The challenge resource that must be created for validation.
- returned: changed
- type: str
- sample: .well-known/acme-challenge/evaGxfADs6pSRb2LAv9IZf17Dt3juxGJ-PCt92wr-oA
- resource_original:
- description:
- - The original challenge resource including type identifier for C(tls-alpn-01)
- challenges.
- returned: changed and challenge is C(tls-alpn-01)
- type: str
- sample: DNS:example.com
- version_added: "2.8"
- resource_value:
- description:
- - The value the resource has to produce for the validation.
- - For C(http-01) and C(dns-01) challenges, the value can be used as-is.
- - "For C(tls-alpn-01) challenges, note that this return value contains a
- Base64 encoded version of the correct binary blob which has to be put
- into the acmeValidation x509 extension; see
- U(https://www.rfc-editor.org/rfc/rfc8737.html#section-3)
- for details. To do this, you might need the C(b64decode) Jinja filter
- to extract the binary blob from this return value."
- returned: changed
- type: str
- sample: IlirfxKKXA...17Dt3juxGJ-PCt92wr-oA
- record:
- description: The full DNS record's name for the challenge.
- returned: changed and challenge is C(dns-01)
- type: str
- sample: _acme-challenge.example.com
- version_added: "2.5"
-challenge_data_dns:
- description:
- - List of TXT values per DNS record, in case challenge is C(dns-01).
- - Since Ansible 2.8.5, only challenges which are not yet valid are returned.
- returned: changed
- type: dict
- version_added: "2.5"
-authorizations:
- description:
- - ACME authorization data.
- - Maps an identifier to ACME authorization objects. See U(https://tools.ietf.org/html/rfc8555#section-7.1.4).
- returned: changed
- type: dict
- sample: '{"example.com":{...}}'
-order_uri:
- description: ACME order URI.
- returned: changed
- type: str
- version_added: "2.5"
-finalization_uri:
- description: ACME finalization URI.
- returned: changed
- type: str
- version_added: "2.5"
-account_uri:
- description: ACME account URI.
- returned: changed
- type: str
- version_added: "2.5"
-all_chains:
- description:
- - When I(retrieve_all_alternates) is set to C(yes), the module will query the ACME server
- for alternate chains. This return value will contain a list of all chains returned,
- the first entry being the main chain returned by the server.
- - See L(Section 7.4.2 of RFC8555,https://tools.ietf.org/html/rfc8555#section-7.4.2) for details.
- returned: when certificate was retrieved and I(retrieve_all_alternates) is set to C(yes)
- type: list
- elements: dict
- contains:
- cert:
- description:
- - The leaf certificate itself, in PEM format.
- type: str
- returned: always
- chain:
- description:
- - The certificate chain, excluding the root, as concatenated PEM certificates.
- type: str
- returned: always
- full_chain:
- description:
- - The certificate chain, excluding the root, but including the leaf certificate,
- as concatenated PEM certificates.
- type: str
- returned: always
-'''
-
-from ansible.module_utils.acme import (
- ModuleFailException,
- write_file,
- nopad_b64,
- pem_to_der,
- ACMEAccount,
- HAS_CURRENT_CRYPTOGRAPHY,
- cryptography_get_csr_identifiers,
- openssl_get_csr_identifiers,
- cryptography_get_cert_days,
- handle_standard_module_arguments,
- process_links,
- get_default_argspec,
-)
-
-import base64
-import binascii
-import hashlib
-import os
-import re
-import textwrap
-import time
-import traceback
-from datetime import datetime
-
-from ansible.module_utils.basic import AnsibleModule, missing_required_lib
-from ansible.module_utils._text import to_bytes, to_native
-from ansible.module_utils.compat import ipaddress as compat_ipaddress
-from ansible.module_utils import crypto as crypto_utils
-
-try:
- import cryptography
- import cryptography.hazmat.backends
- import cryptography.x509
-except ImportError:
- CRYPTOGRAPHY_IMP_ERR = traceback.format_exc()
- CRYPTOGRAPHY_FOUND = False
-else:
- CRYPTOGRAPHY_FOUND = True
-
-
-def get_cert_days(module, cert_file):
- '''
- Return the days the certificate in cert_file remains valid and -1
- if the file was not found. If cert_file contains more than one
- certificate, only the first one will be considered.
- '''
- if HAS_CURRENT_CRYPTOGRAPHY:
- return cryptography_get_cert_days(module, cert_file)
- if not os.path.exists(cert_file):
- return -1
-
- openssl_bin = module.get_bin_path('openssl', True)
- openssl_cert_cmd = [openssl_bin, "x509", "-in", cert_file, "-noout", "-text"]
- dummy, out, dummy = module.run_command(openssl_cert_cmd, check_rc=True, encoding=None)
- try:
- not_after_str = re.search(r"\s+Not After\s*:\s+(.*)", out.decode('utf8')).group(1)
- not_after = datetime.fromtimestamp(time.mktime(time.strptime(not_after_str, '%b %d %H:%M:%S %Y %Z')))
- except AttributeError:
- raise ModuleFailException("No 'Not after' date found in {0}".format(cert_file))
- except ValueError:
- raise ModuleFailException("Failed to parse 'Not after' date of {0}".format(cert_file))
- now = datetime.utcnow()
- return (not_after - now).days
-
-
-class ACMEClient(object):
- '''
- ACME client class. Uses an ACME account object and a CSR to
- start and validate ACME challenges and download the respective
- certificates.
- '''
-
- def __init__(self, module):
- self.module = module
- self.version = module.params['acme_version']
- self.challenge = module.params['challenge']
- self.csr = module.params['csr']
- self.dest = module.params.get('dest')
- self.fullchain_dest = module.params.get('fullchain_dest')
- self.chain_dest = module.params.get('chain_dest')
- self.account = ACMEAccount(module)
- self.directory = self.account.directory
- self.data = module.params['data']
- self.authorizations = None
- self.cert_days = -1
- self.order_uri = self.data.get('order_uri') if self.data else None
- self.finalize_uri = None
-
- # Make sure account exists
- modify_account = module.params['modify_account']
- if modify_account or self.version > 1:
- contact = []
- if module.params['account_email']:
- contact.append('mailto:' + module.params['account_email'])
- created, account_data = self.account.setup_account(
- contact,
- agreement=module.params.get('agreement'),
- terms_agreed=module.params.get('terms_agreed'),
- allow_creation=modify_account,
- )
- if account_data is None:
- raise ModuleFailException(msg='Account does not exist or is deactivated.')
- updated = False
- if not created and account_data and modify_account:
- updated, account_data = self.account.update_account(account_data, contact)
- self.changed = created or updated
- else:
- # This happens if modify_account is False and the ACME v1
- # protocol is used. In this case, we do not call setup_account()
- # to avoid accidental creation of an account. This is OK
- # since for ACME v1, the account URI is not needed to send a
- # signed ACME request.
- pass
-
- if not os.path.exists(self.csr):
- raise ModuleFailException("CSR %s not found" % (self.csr))
-
- self._openssl_bin = module.get_bin_path('openssl', True)
-
- # Extract list of identifiers from CSR
- self.identifiers = self._get_csr_identifiers()
-
- def _get_csr_identifiers(self):
- '''
- Parse the CSR and return the list of requested identifiers
- '''
- if HAS_CURRENT_CRYPTOGRAPHY:
- return cryptography_get_csr_identifiers(self.module, self.csr)
- else:
- return openssl_get_csr_identifiers(self._openssl_bin, self.module, self.csr)
-
- def _add_or_update_auth(self, identifier_type, identifier, auth):
- '''
- Add or update the given authorization in the global authorizations list.
- Return True if the auth was updated/added and False if no change was
- necessary.
- '''
- if self.authorizations.get(identifier_type + ':' + identifier) == auth:
- return False
- self.authorizations[identifier_type + ':' + identifier] = auth
- return True
-
- def _new_authz_v1(self, identifier_type, identifier):
- '''
- Create a new authorization for the given identifier.
- Return the authorization object of the new authorization
- https://tools.ietf.org/html/draft-ietf-acme-acme-02#section-6.4
- '''
- new_authz = {
- "resource": "new-authz",
- "identifier": {"type": identifier_type, "value": identifier},
- }
-
- result, info = self.account.send_signed_request(self.directory['new-authz'], new_authz)
- if info['status'] not in [200, 201]:
- raise ModuleFailException("Error requesting challenges: CODE: {0} RESULT: {1}".format(info['status'], result))
- else:
- result['uri'] = info['location']
- return result
-
- def _get_challenge_data(self, auth, identifier_type, identifier):
- '''
- Returns a dict with the data for all proposed (and supported) challenges
- of the given authorization.
- '''
-
- data = {}
- # no need to choose a specific challenge here as this module
- # is not responsible for fulfilling the challenges. Calculate
- # and return the required information for each challenge.
- for challenge in auth['challenges']:
- challenge_type = challenge['type']
- token = re.sub(r"[^A-Za-z0-9_\-]", "_", challenge['token'])
- keyauthorization = self.account.get_keyauthorization(token)
-
- if challenge_type == 'http-01':
- # https://tools.ietf.org/html/rfc8555#section-8.3
- resource = '.well-known/acme-challenge/' + token
- data[challenge_type] = {'resource': resource, 'resource_value': keyauthorization}
- elif challenge_type == 'dns-01':
- if identifier_type != 'dns':
- continue
- # https://tools.ietf.org/html/rfc8555#section-8.4
- resource = '_acme-challenge'
- value = nopad_b64(hashlib.sha256(to_bytes(keyauthorization)).digest())
- record = (resource + identifier[1:]) if identifier.startswith('*.') else (resource + '.' + identifier)
- data[challenge_type] = {'resource': resource, 'resource_value': value, 'record': record}
- elif challenge_type == 'tls-alpn-01':
- # https://www.rfc-editor.org/rfc/rfc8737.html#section-3
- if identifier_type == 'ip':
- # IPv4/IPv6 address: use reverse mapping (RFC1034, RFC3596)
- resource = compat_ipaddress.ip_address(identifier).reverse_pointer
- if not resource.endswith('.'):
- resource += '.'
- else:
- resource = identifier
- value = base64.b64encode(hashlib.sha256(to_bytes(keyauthorization)).digest())
- data[challenge_type] = {'resource': resource, 'resource_original': identifier_type + ':' + identifier, 'resource_value': value}
- else:
- continue
-
- return data
-
- def _fail_challenge(self, identifier_type, identifier, auth, error):
- '''
- Aborts with a specific error for a challenge.
- '''
- error_details = ''
- # multiple challenges could have failed at this point, gather error
- # details for all of them before failing
- for challenge in auth['challenges']:
- if challenge['status'] == 'invalid':
- error_details += ' CHALLENGE: {0}'.format(challenge['type'])
- if 'error' in challenge:
- error_details += ' DETAILS: {0};'.format(challenge['error']['detail'])
- else:
- error_details += ';'
- raise ModuleFailException("{0}: {1}".format(error.format(identifier_type + ':' + identifier), error_details))
-
- def _validate_challenges(self, identifier_type, identifier, auth):
- '''
- Validate the authorization provided in the auth dict. Returns True
- when the validation was successful and False when it was not.
- '''
- for challenge in auth['challenges']:
- if self.challenge != challenge['type']:
- continue
-
- uri = challenge['uri'] if self.version == 1 else challenge['url']
-
- challenge_response = {}
- if self.version == 1:
- token = re.sub(r"[^A-Za-z0-9_\-]", "_", challenge['token'])
- keyauthorization = self.account.get_keyauthorization(token)
- challenge_response["resource"] = "challenge"
- challenge_response["keyAuthorization"] = keyauthorization
- challenge_response["type"] = self.challenge
- result, info = self.account.send_signed_request(uri, challenge_response)
- if info['status'] not in [200, 202]:
- raise ModuleFailException("Error validating challenge: CODE: {0} RESULT: {1}".format(info['status'], result))
-
- status = ''
-
- while status not in ['valid', 'invalid', 'revoked']:
- result, dummy = self.account.get_request(auth['uri'])
- result['uri'] = auth['uri']
- if self._add_or_update_auth(identifier_type, identifier, result):
- self.changed = True
- # https://tools.ietf.org/html/draft-ietf-acme-acme-02#section-6.1.2
- # "status (required, string): ...
- # If this field is missing, then the default value is "pending"."
- if self.version == 1 and 'status' not in result:
- status = 'pending'
- else:
- status = result['status']
- time.sleep(2)
-
- if status == 'invalid':
- self._fail_challenge(identifier_type, identifier, result, 'Authorization for {0} returned invalid')
-
- return status == 'valid'
-
- def _finalize_cert(self):
- '''
- Create a new certificate based on the csr.
- Return the certificate object as dict
- https://tools.ietf.org/html/rfc8555#section-7.4
- '''
- csr = pem_to_der(self.csr)
- new_cert = {
- "csr": nopad_b64(csr),
- }
- result, info = self.account.send_signed_request(self.finalize_uri, new_cert)
- if info['status'] not in [200]:
- raise ModuleFailException("Error new cert: CODE: {0} RESULT: {1}".format(info['status'], result))
-
- status = result['status']
- while status not in ['valid', 'invalid']:
- time.sleep(2)
- result, dummy = self.account.get_request(self.order_uri)
- status = result['status']
-
- if status != 'valid':
- raise ModuleFailException("Error new cert: CODE: {0} STATUS: {1} RESULT: {2}".format(info['status'], status, result))
-
- return result['certificate']
-
- def _der_to_pem(self, der_cert):
- '''
- Convert the DER format certificate in der_cert to a PEM format
- certificate and return it.
- '''
- return """-----BEGIN CERTIFICATE-----\n{0}\n-----END CERTIFICATE-----\n""".format(
- "\n".join(textwrap.wrap(base64.b64encode(der_cert).decode('utf8'), 64)))
-
- def _download_cert(self, url):
- '''
- Download and parse the certificate chain.
- https://tools.ietf.org/html/rfc8555#section-7.4.2
- '''
- content, info = self.account.get_request(url, parse_json_result=False, headers={'Accept': 'application/pem-certificate-chain'})
-
- if not content or not info['content-type'].startswith('application/pem-certificate-chain'):
- raise ModuleFailException("Cannot download certificate chain from {0}: {1} (headers: {2})".format(url, content, info))
-
- cert = None
- chain = []
-
- # Parse data
- lines = content.decode('utf-8').splitlines(True)
- current = []
- for line in lines:
- if line.strip():
- current.append(line)
- if line.startswith('-----END CERTIFICATE-----'):
- if cert is None:
- cert = ''.join(current)
- else:
- chain.append(''.join(current))
- current = []
-
- alternates = []
-
- def f(link, relation):
- if relation == 'up':
- # Process link-up headers if there was no chain in reply
- if not chain:
- chain_result, chain_info = self.account.get_request(link, parse_json_result=False)
- if chain_info['status'] in [200, 201]:
- chain.append(self._der_to_pem(chain_result))
- elif relation == 'alternate':
- alternates.append(link)
-
- process_links(info, f)
-
- if cert is None or current:
- raise ModuleFailException("Failed to parse certificate chain download from {0}: {1} (headers: {2})".format(url, content, info))
- return {'cert': cert, 'chain': chain, 'alternates': alternates}
-
- def _new_cert_v1(self):
- '''
- Create a new certificate based on the CSR (ACME v1 protocol).
- Return the certificate object as dict
- https://tools.ietf.org/html/draft-ietf-acme-acme-02#section-6.5
- '''
- csr = pem_to_der(self.csr)
- new_cert = {
- "resource": "new-cert",
- "csr": nopad_b64(csr),
- }
- result, info = self.account.send_signed_request(self.directory['new-cert'], new_cert)
-
- chain = []
-
- def f(link, relation):
- if relation == 'up':
- chain_result, chain_info = self.account.get_request(link, parse_json_result=False)
- if chain_info['status'] in [200, 201]:
- del chain[:]
- chain.append(self._der_to_pem(chain_result))
-
- process_links(info, f)
-
- if info['status'] not in [200, 201]:
- raise ModuleFailException("Error new cert: CODE: {0} RESULT: {1}".format(info['status'], result))
- else:
- return {'cert': self._der_to_pem(result), 'uri': info['location'], 'chain': chain}
-
- def _new_order_v2(self):
- '''
- Start a new certificate order (ACME v2 protocol).
- https://tools.ietf.org/html/rfc8555#section-7.4
- '''
- identifiers = []
- for identifier_type, identifier in self.identifiers:
- identifiers.append({
- 'type': identifier_type,
- 'value': identifier,
- })
- new_order = {
- "identifiers": identifiers
- }
- result, info = self.account.send_signed_request(self.directory['newOrder'], new_order)
-
- if info['status'] not in [201]:
- raise ModuleFailException("Error new order: CODE: {0} RESULT: {1}".format(info['status'], result))
-
- for auth_uri in result['authorizations']:
- auth_data, dummy = self.account.get_request(auth_uri)
- auth_data['uri'] = auth_uri
- identifier_type = auth_data['identifier']['type']
- identifier = auth_data['identifier']['value']
- if auth_data.get('wildcard', False):
- identifier = '*.{0}'.format(identifier)
- self.authorizations[identifier_type + ':' + identifier] = auth_data
-
- self.order_uri = info['location']
- self.finalize_uri = result['finalize']
-
- def is_first_step(self):
- '''
- Return True if this is the first execution of this module, i.e. if a
- sufficient data object from a first run has not been provided.
- '''
- if self.data is None:
- return True
- if self.version == 1:
- # As soon as self.data is a non-empty object, we are in the second stage.
- return not self.data
- else:
- # We are in the second stage if data.order_uri is given (which has been
- # stored in self.order_uri by the constructor).
- return self.order_uri is None
-
- def start_challenges(self):
- '''
- Create new authorizations for all identifiers of the CSR,
- respectively start a new order for ACME v2.
- '''
- self.authorizations = {}
- if self.version == 1:
- for identifier_type, identifier in self.identifiers:
- if identifier_type != 'dns':
- raise ModuleFailException('ACME v1 only supports DNS identifiers!')
- for identifier_type, identifier in self.identifiers:
- new_auth = self._new_authz_v1(identifier_type, identifier)
- self._add_or_update_auth(identifier_type, identifier, new_auth)
- else:
- self._new_order_v2()
- self.changed = True
-
- def get_challenges_data(self):
- '''
- Get challenge details for the chosen challenge type.
- Return a tuple of generic challenge details, and specialized DNS challenge details.
- '''
- # Get general challenge data
- data = {}
- for type_identifier, auth in self.authorizations.items():
- identifier_type, identifier = type_identifier.split(':', 1)
- auth = self.authorizations[type_identifier]
- # Skip valid authentications: their challenges are already valid
- # and do not need to be returned
- if auth['status'] == 'valid':
- continue
- # We drop the type from the key to preserve backwards compatibility
- data[identifier] = self._get_challenge_data(auth, identifier_type, identifier)
- # Get DNS challenge data
- data_dns = {}
- if self.challenge == 'dns-01':
- for identifier, challenges in data.items():
- if self.challenge in challenges:
- values = data_dns.get(challenges[self.challenge]['record'])
- if values is None:
- values = []
- data_dns[challenges[self.challenge]['record']] = values
- values.append(challenges[self.challenge]['resource_value'])
- return data, data_dns
-
- def finish_challenges(self):
- '''
- Verify challenges for all identifiers of the CSR.
- '''
- self.authorizations = {}
-
- # Step 1: obtain challenge information
- if self.version == 1:
- # For ACME v1, we attempt to create new authzs. Existing ones
- # will be returned instead.
- for identifier_type, identifier in self.identifiers:
- new_auth = self._new_authz_v1(identifier_type, identifier)
- self._add_or_update_auth(identifier_type, identifier, new_auth)
- else:
- # For ACME v2, we obtain the order object by fetching the
- # order URI, and extract the information from there.
- result, info = self.account.get_request(self.order_uri)
-
- if not result:
- raise ModuleFailException("Cannot download order from {0}: {1} (headers: {2})".format(self.order_uri, result, info))
-
- if info['status'] not in [200]:
- raise ModuleFailException("Error on downloading order: CODE: {0} RESULT: {1}".format(info['status'], result))
-
- for auth_uri in result['authorizations']:
- auth_data, dummy = self.account.get_request(auth_uri)
- auth_data['uri'] = auth_uri
- identifier_type = auth_data['identifier']['type']
- identifier = auth_data['identifier']['value']
- if auth_data.get('wildcard', False):
- identifier = '*.{0}'.format(identifier)
- self.authorizations[identifier_type + ':' + identifier] = auth_data
-
- self.finalize_uri = result['finalize']
-
- # Step 2: validate challenges
- for type_identifier, auth in self.authorizations.items():
- if auth['status'] == 'pending':
- identifier_type, identifier = type_identifier.split(':', 1)
- self._validate_challenges(identifier_type, identifier, auth)
-
- def _chain_matches(self, chain, criterium):
- '''
- Check whether an alternate chain matches the specified criterium.
- '''
- if criterium['test_certificates'] == 'last':
- chain = chain[-1:]
- for cert in chain:
- try:
- x509 = cryptography.x509.load_pem_x509_certificate(to_bytes(cert), cryptography.hazmat.backends.default_backend())
- matches = True
- if criterium['subject']:
- for k, v in crypto_utils.parse_name_field(criterium['subject']):
- oid = crypto_utils.cryptography_name_to_oid(k)
- value = to_native(v)
- found = False
- for attribute in x509.subject:
- if attribute.oid == oid and value == to_native(attribute.value):
- found = True
- break
- if not found:
- matches = False
- break
- if criterium['issuer']:
- for k, v in crypto_utils.parse_name_field(criterium['issuer']):
- oid = crypto_utils.cryptography_name_to_oid(k)
- value = to_native(v)
- found = False
- for attribute in x509.issuer:
- if attribute.oid == oid and value == to_native(attribute.value):
- found = True
- break
- if not found:
- matches = False
- break
- if criterium['subject_key_identifier']:
- try:
- ext = x509.extensions.get_extension_for_class(cryptography.x509.SubjectKeyIdentifier)
- if criterium['subject_key_identifier'] != ext.value.digest:
- matches = False
- except cryptography.x509.ExtensionNotFound:
- matches = False
- if criterium['authority_key_identifier']:
- try:
- ext = x509.extensions.get_extension_for_class(cryptography.x509.AuthorityKeyIdentifier)
- if criterium['authority_key_identifier'] != ext.value.key_identifier:
- matches = False
- except cryptography.x509.ExtensionNotFound:
- matches = False
- if matches:
- return True
- except Exception as e:
- self.module.warn('Error while loading certificate {0}: {1}'.format(cert, e))
- return False
-
- def get_certificate(self):
- '''
- Request a new certificate and write it to the destination file.
- First verifies whether all authorizations are valid; if not, aborts
- with an error.
- '''
- for identifier_type, identifier in self.identifiers:
- auth = self.authorizations.get(identifier_type + ':' + identifier)
- if auth is None:
- raise ModuleFailException('Found no authorization information for "{0}"!'.format(identifier_type + ':' + identifier))
- if 'status' not in auth:
- self._fail_challenge(identifier_type, identifier, auth, 'Authorization for {0} returned no status')
- if auth['status'] != 'valid':
- self._fail_challenge(identifier_type, identifier, auth, 'Authorization for {0} returned status ' + str(auth['status']))
-
- if self.version == 1:
- cert = self._new_cert_v1()
- else:
- cert_uri = self._finalize_cert()
- cert = self._download_cert(cert_uri)
- if self.module.params['retrieve_all_alternates'] or self.module.params['select_chain']:
- # Retrieve alternate chains
- alternate_chains = []
- for alternate in cert['alternates']:
- try:
- alt_cert = self._download_cert(alternate)
- except ModuleFailException as e:
- self.module.warn('Error while downloading alternative certificate {0}: {1}'.format(alternate, e))
- continue
- alternate_chains.append(alt_cert)
-
- # Prepare return value for all alternate chains
- if self.module.params['retrieve_all_alternates']:
- self.all_chains = []
-
- def _append_all_chains(cert_data):
- self.all_chains.append(dict(
- cert=cert_data['cert'].encode('utf8'),
- chain=("\n".join(cert_data.get('chain', []))).encode('utf8'),
- full_chain=(cert_data['cert'] + "\n".join(cert_data.get('chain', []))).encode('utf8'),
- ))
-
- _append_all_chains(cert)
- for alt_chain in alternate_chains:
- _append_all_chains(alt_chain)
-
- # Try to select alternate chain depending on criteria
- if self.module.params['select_chain']:
- matching_chain = None
- all_chains = [cert] + alternate_chains
- for criterium_idx, criterium in enumerate(self.module.params['select_chain']):
- for v in ('subject_key_identifier', 'authority_key_identifier'):
- if criterium[v]:
- try:
- criterium[v] = binascii.unhexlify(criterium[v].replace(':', ''))
- except Exception:
- self.module.warn('Criterium {0} in select_chain has invalid {1} value. '
- 'Ignoring criterium.'.format(criterium_idx, v))
- continue
- for alt_chain in all_chains:
- if self._chain_matches(alt_chain.get('chain', []), criterium):
- self.module.debug('Found matching chain for criterium {0}'.format(criterium_idx))
- matching_chain = alt_chain
- break
- if matching_chain:
- break
- if matching_chain:
- cert.update(matching_chain)
- else:
- self.module.debug('Found no matching alternative chain')
-
- if cert['cert'] is not None:
- pem_cert = cert['cert']
-
- chain = [link for link in cert.get('chain', [])]
-
- if self.dest and write_file(self.module, self.dest, pem_cert.encode('utf8')):
- self.cert_days = get_cert_days(self.module, self.dest)
- self.changed = True
-
- if self.fullchain_dest and write_file(self.module, self.fullchain_dest, (pem_cert + "\n".join(chain)).encode('utf8')):
- self.cert_days = get_cert_days(self.module, self.fullchain_dest)
- self.changed = True
-
- if self.chain_dest and write_file(self.module, self.chain_dest, ("\n".join(chain)).encode('utf8')):
- self.changed = True
-
- def deactivate_authzs(self):
- '''
- Deactivates all valid authz's. Does not raise exceptions.
- https://community.letsencrypt.org/t/authorization-deactivation/19860/2
- https://tools.ietf.org/html/rfc8555#section-7.5.2
- '''
- authz_deactivate = {
- 'status': 'deactivated'
- }
- if self.version == 1:
- authz_deactivate['resource'] = 'authz'
- if self.authorizations:
- for identifier_type, identifier in self.identifiers:
- auth = self.authorizations.get(identifier_type + ':' + identifier)
- if auth is None or auth.get('status') != 'valid':
- continue
- try:
- result, info = self.account.send_signed_request(auth['uri'], authz_deactivate)
- if 200 <= info['status'] < 300 and result.get('status') == 'deactivated':
- auth['status'] = 'deactivated'
- except Exception as dummy:
- # Ignore errors on deactivating authzs
- pass
- if auth.get('status') != 'deactivated':
- self.module.warn(warning='Could not deactivate authz object {0}.'.format(auth['uri']))
-
-
-def main():
- argument_spec = get_default_argspec()
- argument_spec.update(dict(
- modify_account=dict(type='bool', default=True),
- account_email=dict(type='str'),
- agreement=dict(type='str'),
- terms_agreed=dict(type='bool', default=False),
- challenge=dict(type='str', default='http-01', choices=['http-01', 'dns-01', 'tls-alpn-01']),
- csr=dict(type='path', required=True, aliases=['src']),
- data=dict(type='dict'),
- dest=dict(type='path', aliases=['cert']),
- fullchain_dest=dict(type='path', aliases=['fullchain']),
- chain_dest=dict(type='path', aliases=['chain']),
- remaining_days=dict(type='int', default=10),
- deactivate_authzs=dict(type='bool', default=False),
- force=dict(type='bool', default=False),
- retrieve_all_alternates=dict(type='bool', default=False),
- select_chain=dict(type='list', elements='dict', options=dict(
- test_certificates=dict(type='str', default='all', choices=['last', 'all']),
- issuer=dict(type='dict'),
- subject=dict(type='dict'),
- subject_key_identifier=dict(type='str'),
- authority_key_identifier=dict(type='str'),
- )),
- ))
- module = AnsibleModule(
- argument_spec=argument_spec,
- required_one_of=(
- ['account_key_src', 'account_key_content'],
- ['dest', 'fullchain_dest'],
- ),
- mutually_exclusive=(
- ['account_key_src', 'account_key_content'],
- ),
- supports_check_mode=True,
- )
- backend = handle_standard_module_arguments(module)
- if module.params['select_chain']:
- if backend != 'cryptography':
- module.fail_json(msg="The 'select_chain' can only be used with the 'cryptography' backend.")
- elif not CRYPTOGRAPHY_FOUND:
- module.fail_json(msg=missing_required_lib('cryptography'))
-
- try:
- if module.params.get('dest'):
- cert_days = get_cert_days(module, module.params['dest'])
- else:
- cert_days = get_cert_days(module, module.params['fullchain_dest'])
-
- if module.params['force'] or cert_days < module.params['remaining_days']:
- # If checkmode is active, base the changed state solely on the status
- # of the certificate file as all other actions (accessing an account, checking
- # the authorization status...) would lead to potential changes of the current
- # state
- if module.check_mode:
- module.exit_json(changed=True, authorizations={}, challenge_data={}, cert_days=cert_days)
- else:
- client = ACMEClient(module)
- client.cert_days = cert_days
- other = dict()
- if client.is_first_step():
- # First run: start challenges / start new order
- client.start_challenges()
- else:
- # Second run: finish challenges, and get certificate
- try:
- client.finish_challenges()
- client.get_certificate()
- if module.params['retrieve_all_alternates']:
- other['all_chains'] = client.all_chains
- finally:
- if module.params['deactivate_authzs']:
- client.deactivate_authzs()
- data, data_dns = client.get_challenges_data()
- auths = dict()
- for k, v in client.authorizations.items():
- # Remove "type:" from key
- auths[k.split(':', 1)[1]] = v
- module.exit_json(
- changed=client.changed,
- authorizations=auths,
- finalize_uri=client.finalize_uri,
- order_uri=client.order_uri,
- account_uri=client.account.uri,
- challenge_data=data,
- challenge_data_dns=data_dns,
- cert_days=client.cert_days,
- **other
- )
- else:
- module.exit_json(changed=False, cert_days=cert_days)
- except ModuleFailException as e:
- e.do_fail(module)
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/crypto/acme/acme_certificate_revoke.py b/lib/ansible/modules/crypto/acme/acme_certificate_revoke.py
deleted file mode 100644
index b048e8e675..0000000000
--- a/lib/ansible/modules/crypto/acme/acme_certificate_revoke.py
+++ /dev/null
@@ -1,223 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-
-# (c) 2016 Michael Gruener <michael.gruener@chaosmoon.net>
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'community'}
-
-
-DOCUMENTATION = '''
----
-module: acme_certificate_revoke
-author: "Felix Fontein (@felixfontein)"
-version_added: "2.7"
-short_description: Revoke certificates with the ACME protocol
-description:
- - "Allows to revoke certificates issued by a CA supporting the
- L(ACME protocol,https://tools.ietf.org/html/rfc8555),
- such as L(Let's Encrypt,https://letsencrypt.org/)."
-notes:
- - "Exactly one of C(account_key_src), C(account_key_content),
- C(private_key_src) or C(private_key_content) must be specified."
- - "Trying to revoke an already revoked certificate
- should result in an unchanged status, even if the revocation reason
- was different than the one specified here. Also, depending on the
- server, it can happen that some other error is returned if the
- certificate has already been revoked."
-seealso:
- - name: The Let's Encrypt documentation
- description: Documentation for the Let's Encrypt Certification Authority.
- Provides useful information for example on rate limits.
- link: https://letsencrypt.org/docs/
- - name: Automatic Certificate Management Environment (ACME)
- description: The specification of the ACME protocol (RFC 8555).
- link: https://tools.ietf.org/html/rfc8555
- - module: acme_inspect
- description: Allows to debug problems.
-extends_documentation_fragment:
- - acme
-options:
- certificate:
- description:
- - "Path to the certificate to revoke."
- type: path
- required: yes
- account_key_src:
- description:
- - "Path to a file containing the ACME account RSA or Elliptic Curve
- key."
- - "RSA keys can be created with C(openssl rsa ...). Elliptic curve keys can
- be created with C(openssl ecparam -genkey ...). Any other tool creating
- private keys in PEM format can be used as well."
- - "Mutually exclusive with C(account_key_content)."
- - "Required if C(account_key_content) is not used."
- type: path
- account_key_content:
- description:
- - "Content of the ACME account RSA or Elliptic Curve key."
- - "Note that exactly one of C(account_key_src), C(account_key_content),
- C(private_key_src) or C(private_key_content) must be specified."
- - "I(Warning): the content will be written into a temporary file, which will
- be deleted by Ansible when the module completes. Since this is an
- important private key — it can be used to change the account key,
- or to revoke your certificates without knowing their private keys
- —, this might not be acceptable."
- - "In case C(cryptography) is used, the content is not written into a
- temporary file. It can still happen that it is written to disk by
- Ansible in the process of moving the module with its argument to
- the node where it is executed."
- type: str
- private_key_src:
- description:
- - "Path to the certificate's private key."
- - "Note that exactly one of C(account_key_src), C(account_key_content),
- C(private_key_src) or C(private_key_content) must be specified."
- type: path
- private_key_content:
- description:
- - "Content of the certificate's private key."
- - "Note that exactly one of C(account_key_src), C(account_key_content),
- C(private_key_src) or C(private_key_content) must be specified."
- - "I(Warning): the content will be written into a temporary file, which will
- be deleted by Ansible when the module completes. Since this is an
- important private key — it can be used to change the account key,
- or to revoke your certificates without knowing their private keys
- —, this might not be acceptable."
- - "In case C(cryptography) is used, the content is not written into a
- temporary file. It can still happen that it is written to disk by
- Ansible in the process of moving the module with its argument to
- the node where it is executed."
- type: str
- revoke_reason:
- description:
- - "One of the revocation reasonCodes defined in
- L(Section 5.3.1 of RFC5280,https://tools.ietf.org/html/rfc5280#section-5.3.1)."
- - "Possible values are C(0) (unspecified), C(1) (keyCompromise),
- C(2) (cACompromise), C(3) (affiliationChanged), C(4) (superseded),
- C(5) (cessationOfOperation), C(6) (certificateHold),
- C(8) (removeFromCRL), C(9) (privilegeWithdrawn),
- C(10) (aACompromise)"
- type: int
-'''
-
-EXAMPLES = '''
-- name: Revoke certificate with account key
- acme_certificate_revoke:
- account_key_src: /etc/pki/cert/private/account.key
- certificate: /etc/httpd/ssl/sample.com.crt
-
-- name: Revoke certificate with certificate's private key
- acme_certificate_revoke:
- private_key_src: /etc/httpd/ssl/sample.com.key
- certificate: /etc/httpd/ssl/sample.com.crt
-'''
-
-RETURN = '''
-'''
-
-from ansible.module_utils.acme import (
- ModuleFailException,
- ACMEAccount,
- nopad_b64,
- pem_to_der,
- handle_standard_module_arguments,
- get_default_argspec,
-)
-
-from ansible.module_utils.basic import AnsibleModule
-
-
-def main():
- argument_spec = get_default_argspec()
- argument_spec.update(dict(
- private_key_src=dict(type='path'),
- private_key_content=dict(type='str', no_log=True),
- certificate=dict(type='path', required=True),
- revoke_reason=dict(type='int'),
- ))
- module = AnsibleModule(
- argument_spec=argument_spec,
- required_one_of=(
- ['account_key_src', 'account_key_content', 'private_key_src', 'private_key_content'],
- ),
- mutually_exclusive=(
- ['account_key_src', 'account_key_content', 'private_key_src', 'private_key_content'],
- ),
- supports_check_mode=False,
- )
- handle_standard_module_arguments(module)
-
- try:
- account = ACMEAccount(module)
- # Load certificate
- certificate = pem_to_der(module.params.get('certificate'))
- certificate = nopad_b64(certificate)
- # Construct payload
- payload = {
- 'certificate': certificate
- }
- if module.params.get('revoke_reason') is not None:
- payload['reason'] = module.params.get('revoke_reason')
- # Determine endpoint
- if module.params.get('acme_version') == 1:
- endpoint = account.directory['revoke-cert']
- payload['resource'] = 'revoke-cert'
- else:
- endpoint = account.directory['revokeCert']
- # Get hold of private key (if available) and make sure it comes from disk
- private_key = module.params.get('private_key_src')
- private_key_content = module.params.get('private_key_content')
- # Revoke certificate
- if private_key or private_key_content:
- # Step 1: load and parse private key
- error, private_key_data = account.parse_key(private_key, private_key_content)
- if error:
- raise ModuleFailException("error while parsing private key: %s" % error)
- # Step 2: sign revokation request with private key
- jws_header = {
- "alg": private_key_data['alg'],
- "jwk": private_key_data['jwk'],
- }
- result, info = account.send_signed_request(endpoint, payload, key_data=private_key_data, jws_header=jws_header)
- else:
- # Step 1: get hold of account URI
- created, account_data = account.setup_account(allow_creation=False)
- if created:
- raise AssertionError('Unwanted account creation')
- if account_data is None:
- raise ModuleFailException(msg='Account does not exist or is deactivated.')
- # Step 2: sign revokation request with account key
- result, info = account.send_signed_request(endpoint, payload)
- if info['status'] != 200:
- already_revoked = False
- # Standardized error from draft 14 on (https://tools.ietf.org/html/rfc8555#section-7.6)
- if result.get('type') == 'urn:ietf:params:acme:error:alreadyRevoked':
- already_revoked = True
- else:
- # Hack for Boulder errors
- if module.params.get('acme_version') == 1:
- error_type = 'urn:acme:error:malformed'
- else:
- error_type = 'urn:ietf:params:acme:error:malformed'
- if result.get('type') == error_type and result.get('detail') == 'Certificate already revoked':
- # Fallback: boulder returns this in case the certificate was already revoked.
- already_revoked = True
- # If we know the certificate was already revoked, we don't fail,
- # but successfully terminate while indicating no change
- if already_revoked:
- module.exit_json(changed=False)
- raise ModuleFailException('Error revoking certificate: {0} {1}'.format(info['status'], result))
- module.exit_json(changed=True)
- except ModuleFailException as e:
- e.do_fail(module)
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/crypto/acme/acme_challenge_cert_helper.py b/lib/ansible/modules/crypto/acme/acme_challenge_cert_helper.py
deleted file mode 100644
index 7a355cb1c1..0000000000
--- a/lib/ansible/modules/crypto/acme/acme_challenge_cert_helper.py
+++ /dev/null
@@ -1,299 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-
-# (c) 2018 Felix Fontein <felix@fontein.de>
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'community'}
-
-
-DOCUMENTATION = '''
----
-module: acme_challenge_cert_helper
-author: "Felix Fontein (@felixfontein)"
-version_added: "2.7"
-short_description: Prepare certificates required for ACME challenges such as C(tls-alpn-01)
-description:
- - "Prepares certificates for ACME challenges such as C(tls-alpn-01)."
- - "The raw data is provided by the M(acme_certificate) module, and needs to be
- converted to a certificate to be used for challenge validation. This module
- provides a simple way to generate the required certificates."
-seealso:
- - name: Automatic Certificate Management Environment (ACME)
- description: The specification of the ACME protocol (RFC 8555).
- link: https://tools.ietf.org/html/rfc8555
- - name: ACME TLS ALPN Challenge Extension
- description: The specification of the C(tls-alpn-01) challenge (RFC 8737).
- link: https://www.rfc-editor.org/rfc/rfc8737.html
-requirements:
- - "cryptography >= 1.3"
-options:
- challenge:
- description:
- - "The challenge type."
- type: str
- required: yes
- choices:
- - tls-alpn-01
- challenge_data:
- description:
- - "The C(challenge_data) entry provided by M(acme_certificate) for the challenge."
- type: dict
- required: yes
- private_key_src:
- description:
- - "Path to a file containing the private key file to use for this challenge
- certificate."
- - "Mutually exclusive with C(private_key_content)."
- type: path
- private_key_content:
- description:
- - "Content of the private key to use for this challenge certificate."
- - "Mutually exclusive with C(private_key_src)."
- type: str
-'''
-
-EXAMPLES = '''
-- name: Create challenges for a given CRT for sample.com
- acme_certificate:
- account_key_src: /etc/pki/cert/private/account.key
- challenge: tls-alpn-01
- csr: /etc/pki/cert/csr/sample.com.csr
- dest: /etc/httpd/ssl/sample.com.crt
- register: sample_com_challenge
-
-- name: Create certificates for challenges
- acme_challenge_cert_helper:
- challenge: tls-alpn-01
- challenge_data: "{{ item.value['tls-alpn-01'] }}"
- private_key_src: /etc/pki/cert/key/sample.com.key
- loop: "{{ sample_com_challenge.challenge_data | dictsort }}"
- register: sample_com_challenge_certs
-
-- name: Install challenge certificates
- # We need to set up HTTPS such that for the domain,
- # regular_certificate is delivered for regular connections,
- # except if ALPN selects the "acme-tls/1"; then, the
- # challenge_certificate must be delivered.
- # This can for example be achieved with very new versions
- # of NGINX; search for ssl_preread and
- # ssl_preread_alpn_protocols for information on how to
- # route by ALPN protocol.
- ...:
- domain: "{{ item.domain }}"
- challenge_certificate: "{{ item.challenge_certificate }}"
- regular_certificate: "{{ item.regular_certificate }}"
- private_key: /etc/pki/cert/key/sample.com.key
- loop: "{{ sample_com_challenge_certs.results }}"
-
-- name: Create certificate for a given CSR for sample.com
- acme_certificate:
- account_key_src: /etc/pki/cert/private/account.key
- challenge: tls-alpn-01
- csr: /etc/pki/cert/csr/sample.com.csr
- dest: /etc/httpd/ssl/sample.com.crt
- data: "{{ sample_com_challenge }}"
-'''
-
-RETURN = '''
-domain:
- description:
- - "The domain the challenge is for. The certificate should be provided if
- this is specified in the request's the C(Host) header."
- returned: always
- type: str
-identifier_type:
- description:
- - "The identifier type for the actual resource identifier. Will be C(dns)
- or C(ip)."
- returned: always
- type: str
- version_added: "2.8"
-identifier:
- description:
- - "The identifier for the actual resource. Will be a domain name if the
- type is C(dns), or an IP address if the type is C(ip)."
- returned: always
- type: str
- version_added: "2.8"
-challenge_certificate:
- description:
- - "The challenge certificate in PEM format."
- returned: always
- type: str
-regular_certificate:
- description:
- - "A self-signed certificate for the challenge domain."
- - "If no existing certificate exists, can be used to set-up
- https in the first place if that is needed for providing
- the challenge."
- returned: always
- type: str
-'''
-
-from ansible.module_utils.acme import (
- ModuleFailException,
- read_file,
-)
-
-from ansible.module_utils.basic import AnsibleModule, missing_required_lib
-from ansible.module_utils._text import to_bytes, to_text
-
-import base64
-import datetime
-import sys
-import traceback
-
-CRYPTOGRAPHY_IMP_ERR = None
-try:
- import cryptography
- import cryptography.hazmat.backends
- import cryptography.hazmat.primitives.serialization
- import cryptography.hazmat.primitives.asymmetric.rsa
- import cryptography.hazmat.primitives.asymmetric.ec
- import cryptography.hazmat.primitives.asymmetric.padding
- import cryptography.hazmat.primitives.hashes
- import cryptography.hazmat.primitives.asymmetric.utils
- import cryptography.x509
- import cryptography.x509.oid
- import ipaddress
- from distutils.version import LooseVersion
- HAS_CRYPTOGRAPHY = (LooseVersion(cryptography.__version__) >= LooseVersion('1.3'))
- _cryptography_backend = cryptography.hazmat.backends.default_backend()
-except ImportError as dummy:
- CRYPTOGRAPHY_IMP_ERR = traceback.format_exc()
- HAS_CRYPTOGRAPHY = False
-
-
-# Convert byte string to ASN1 encoded octet string
-if sys.version_info[0] >= 3:
- def encode_octet_string(octet_string):
- if len(octet_string) >= 128:
- raise ModuleFailException('Cannot handle octet strings with more than 128 bytes')
- return bytes([0x4, len(octet_string)]) + octet_string
-else:
- def encode_octet_string(octet_string):
- if len(octet_string) >= 128:
- raise ModuleFailException('Cannot handle octet strings with more than 128 bytes')
- return b'\x04' + chr(len(octet_string)) + octet_string
-
-
-def main():
- module = AnsibleModule(
- argument_spec=dict(
- challenge=dict(type='str', required=True, choices=['tls-alpn-01']),
- challenge_data=dict(type='dict', required=True),
- private_key_src=dict(type='path'),
- private_key_content=dict(type='str', no_log=True),
- ),
- required_one_of=(
- ['private_key_src', 'private_key_content'],
- ),
- mutually_exclusive=(
- ['private_key_src', 'private_key_content'],
- ),
- )
- if not HAS_CRYPTOGRAPHY:
- module.fail_json(msg=missing_required_lib('cryptography >= 1.3'), exception=CRYPTOGRAPHY_IMP_ERR)
-
- try:
- # Get parameters
- challenge = module.params['challenge']
- challenge_data = module.params['challenge_data']
-
- # Get hold of private key
- private_key_content = module.params.get('private_key_content')
- if private_key_content is None:
- private_key_content = read_file(module.params['private_key_src'])
- else:
- private_key_content = to_bytes(private_key_content)
- try:
- private_key = cryptography.hazmat.primitives.serialization.load_pem_private_key(private_key_content, password=None, backend=_cryptography_backend)
- except Exception as e:
- raise ModuleFailException('Error while loading private key: {0}'.format(e))
-
- # Some common attributes
- domain = to_text(challenge_data['resource'])
- identifier_type, identifier = to_text(challenge_data.get('resource_original', 'dns:' + challenge_data['resource'])).split(':', 1)
- subject = issuer = cryptography.x509.Name([])
- not_valid_before = datetime.datetime.utcnow()
- not_valid_after = datetime.datetime.utcnow() + datetime.timedelta(days=10)
- if identifier_type == 'dns':
- san = cryptography.x509.DNSName(identifier)
- elif identifier_type == 'ip':
- san = cryptography.x509.IPAddress(ipaddress.ip_address(identifier))
- else:
- raise ModuleFailException('Unsupported identifier type "{0}"'.format(identifier_type))
-
- # Generate regular self-signed certificate
- regular_certificate = cryptography.x509.CertificateBuilder().subject_name(
- subject
- ).issuer_name(
- issuer
- ).public_key(
- private_key.public_key()
- ).serial_number(
- cryptography.x509.random_serial_number()
- ).not_valid_before(
- not_valid_before
- ).not_valid_after(
- not_valid_after
- ).add_extension(
- cryptography.x509.SubjectAlternativeName([san]),
- critical=False,
- ).sign(
- private_key,
- cryptography.hazmat.primitives.hashes.SHA256(),
- _cryptography_backend
- )
-
- # Process challenge
- if challenge == 'tls-alpn-01':
- value = base64.b64decode(challenge_data['resource_value'])
- challenge_certificate = cryptography.x509.CertificateBuilder().subject_name(
- subject
- ).issuer_name(
- issuer
- ).public_key(
- private_key.public_key()
- ).serial_number(
- cryptography.x509.random_serial_number()
- ).not_valid_before(
- not_valid_before
- ).not_valid_after(
- not_valid_after
- ).add_extension(
- cryptography.x509.SubjectAlternativeName([san]),
- critical=False,
- ).add_extension(
- cryptography.x509.UnrecognizedExtension(
- cryptography.x509.ObjectIdentifier("1.3.6.1.5.5.7.1.31"),
- encode_octet_string(value),
- ),
- critical=True,
- ).sign(
- private_key,
- cryptography.hazmat.primitives.hashes.SHA256(),
- _cryptography_backend
- )
-
- module.exit_json(
- changed=True,
- domain=domain,
- identifier_type=identifier_type,
- identifier=identifier,
- challenge_certificate=challenge_certificate.public_bytes(cryptography.hazmat.primitives.serialization.Encoding.PEM),
- regular_certificate=regular_certificate.public_bytes(cryptography.hazmat.primitives.serialization.Encoding.PEM)
- )
- except ModuleFailException as e:
- e.do_fail(module)
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/crypto/acme/acme_inspect.py b/lib/ansible/modules/crypto/acme/acme_inspect.py
deleted file mode 100644
index 05ff506b20..0000000000
--- a/lib/ansible/modules/crypto/acme/acme_inspect.py
+++ /dev/null
@@ -1,320 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-
-# (c) 2018 Felix Fontein (@felixfontein)
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'community'}
-
-
-DOCUMENTATION = r'''
----
-module: acme_inspect
-author: "Felix Fontein (@felixfontein)"
-version_added: "2.8"
-short_description: Send direct requests to an ACME server
-description:
- - "Allows to send direct requests to an ACME server with the
- L(ACME protocol,https://tools.ietf.org/html/rfc8555),
- which is supported by CAs such as L(Let's Encrypt,https://letsencrypt.org/)."
- - "This module can be used to debug failed certificate request attempts,
- for example when M(acme_certificate) fails or encounters a problem which
- you wish to investigate."
- - "The module can also be used to directly access features of an ACME servers
- which are not yet supported by the Ansible ACME modules."
-notes:
- - "The I(account_uri) option must be specified for properly authenticated
- ACME v2 requests (except a C(new-account) request)."
- - "Using the C(ansible) tool, M(acme_inspect) can be used to directly execute
- ACME requests without the need of writing a playbook. For example, the
- following command retrieves the ACME account with ID 1 from Let's Encrypt
- (assuming C(/path/to/key) is the correct private account key):
- C(ansible localhost -m acme_inspect -a \"account_key_src=/path/to/key
- acme_directory=https://acme-v02.api.letsencrypt.org/directory acme_version=2
- account_uri=https://acme-v02.api.letsencrypt.org/acme/acct/1 method=get
- url=https://acme-v02.api.letsencrypt.org/acme/acct/1\")"
-seealso:
- - name: Automatic Certificate Management Environment (ACME)
- description: The specification of the ACME protocol (RFC 8555).
- link: https://tools.ietf.org/html/rfc8555
- - name: ACME TLS ALPN Challenge Extension
- description: The specification of the C(tls-alpn-01) challenge (RFC 8737).
- link: https://www.rfc-editor.org/rfc/rfc8737.html
-extends_documentation_fragment:
- - acme
-options:
- url:
- description:
- - "The URL to send the request to."
- - "Must be specified if I(method) is not C(directory-only)."
- type: str
- method:
- description:
- - "The method to use to access the given URL on the ACME server."
- - "The value C(post) executes an authenticated POST request. The content
- must be specified in the I(content) option."
- - "The value C(get) executes an authenticated POST-as-GET request for ACME v2,
- and a regular GET request for ACME v1."
- - "The value C(directory-only) only retrieves the directory, without doing
- a request."
- type: str
- default: get
- choices:
- - get
- - post
- - directory-only
- content:
- description:
- - "An encoded JSON object which will be sent as the content if I(method)
- is C(post)."
- - "Required when I(method) is C(post), and not allowed otherwise."
- type: str
- fail_on_acme_error:
- description:
- - "If I(method) is C(post) or C(get), make the module fail in case an ACME
- error is returned."
- type: bool
- default: yes
-'''
-
-EXAMPLES = r'''
-- name: Get directory
- acme_inspect:
- acme_directory: https://acme-staging-v02.api.letsencrypt.org/directory
- acme_version: 2
- method: directory-only
- register: directory
-
-- name: Create an account
- acme_inspect:
- acme_directory: https://acme-staging-v02.api.letsencrypt.org/directory
- acme_version: 2
- account_key_src: /etc/pki/cert/private/account.key
- url: "{{ directory.newAccount}}"
- method: post
- content: '{"termsOfServiceAgreed":true}'
- register: account_creation
- # account_creation.headers.location contains the account URI
- # if creation was successful
-
-- name: Get account information
- acme_inspect:
- acme_directory: https://acme-staging-v02.api.letsencrypt.org/directory
- acme_version: 2
- account_key_src: /etc/pki/cert/private/account.key
- account_uri: "{{ account_creation.headers.location }}"
- url: "{{ account_creation.headers.location }}"
- method: get
-
-- name: Update account contacts
- acme_inspect:
- acme_directory: https://acme-staging-v02.api.letsencrypt.org/directory
- acme_version: 2
- account_key_src: /etc/pki/cert/private/account.key
- account_uri: "{{ account_creation.headers.location }}"
- url: "{{ account_creation.headers.location }}"
- method: post
- content: '{{ account_info | to_json }}'
- vars:
- account_info:
- # For valid values, see
- # https://tools.ietf.org/html/rfc8555#section-7.3
- contact:
- - mailto:me@example.com
-
-- name: Create certificate order
- acme_certificate:
- acme_directory: https://acme-staging-v02.api.letsencrypt.org/directory
- acme_version: 2
- account_key_src: /etc/pki/cert/private/account.key
- account_uri: "{{ account_creation.headers.location }}"
- csr: /etc/pki/cert/csr/sample.com.csr
- fullchain_dest: /etc/httpd/ssl/sample.com-fullchain.crt
- challenge: http-01
- register: certificate_request
-
-# Assume something went wrong. certificate_request.order_uri contains
-# the order URI.
-
-- name: Get order information
- acme_inspect:
- acme_directory: https://acme-staging-v02.api.letsencrypt.org/directory
- acme_version: 2
- account_key_src: /etc/pki/cert/private/account.key
- account_uri: "{{ account_creation.headers.location }}"
- url: "{{ certificate_request.order_uri }}"
- method: get
- register: order
-
-- name: Get first authz for order
- acme_inspect:
- acme_directory: https://acme-staging-v02.api.letsencrypt.org/directory
- acme_version: 2
- account_key_src: /etc/pki/cert/private/account.key
- account_uri: "{{ account_creation.headers.location }}"
- url: "{{ order.output_json.authorizations[0] }}"
- method: get
- register: authz
-
-- name: Get HTTP-01 challenge for authz
- acme_inspect:
- acme_directory: https://acme-staging-v02.api.letsencrypt.org/directory
- acme_version: 2
- account_key_src: /etc/pki/cert/private/account.key
- account_uri: "{{ account_creation.headers.location }}"
- url: "{{ authz.output_json.challenges | selectattr('type', 'equalto', 'http-01') }}"
- method: get
- register: http01challenge
-
-- name: Activate HTTP-01 challenge manually
- acme_inspect:
- acme_directory: https://acme-staging-v02.api.letsencrypt.org/directory
- acme_version: 2
- account_key_src: /etc/pki/cert/private/account.key
- account_uri: "{{ account_creation.headers.location }}"
- url: "{{ http01challenge.url }}"
- method: post
- content: '{}'
-'''
-
-RETURN = '''
-directory:
- description: The ACME directory's content
- returned: always
- type: dict
- sample: |
- {
- "a85k3x9f91A4": "https://community.letsencrypt.org/t/adding-random-entries-to-the-directory/33417",
- "keyChange": "https://acme-v02.api.letsencrypt.org/acme/key-change",
- "meta": {
- "caaIdentities": [
- "letsencrypt.org"
- ],
- "termsOfService": "https://letsencrypt.org/documents/LE-SA-v1.2-November-15-2017.pdf",
- "website": "https://letsencrypt.org"
- },
- "newAccount": "https://acme-v02.api.letsencrypt.org/acme/new-acct",
- "newNonce": "https://acme-v02.api.letsencrypt.org/acme/new-nonce",
- "newOrder": "https://acme-v02.api.letsencrypt.org/acme/new-order",
- "revokeCert": "https://acme-v02.api.letsencrypt.org/acme/revoke-cert"
- }
-headers:
- description: The request's HTTP headers (with lowercase keys)
- returned: always
- type: dict
- sample: |
- {
- "boulder-requester": "12345",
- "cache-control": "max-age=0, no-cache, no-store",
- "connection": "close",
- "content-length": "904",
- "content-type": "application/json",
- "cookies": {},
- "cookies_string": "",
- "date": "Wed, 07 Nov 2018 12:34:56 GMT",
- "expires": "Wed, 07 Nov 2018 12:44:56 GMT",
- "link": "<https://letsencrypt.org/documents/LE-SA-v1.2-November-15-2017.pdf>;rel=\"terms-of-service\"",
- "msg": "OK (904 bytes)",
- "pragma": "no-cache",
- "replay-nonce": "1234567890abcdefghijklmnopqrstuvwxyzABCDEFGH",
- "server": "nginx",
- "status": 200,
- "strict-transport-security": "max-age=604800",
- "url": "https://acme-v02.api.letsencrypt.org/acme/acct/46161",
- "x-frame-options": "DENY"
- }
-output_text:
- description: The raw text output
- returned: always
- type: str
- sample: "{\\n \\\"id\\\": 12345,\\n \\\"key\\\": {\\n \\\"kty\\\": \\\"RSA\\\",\\n ..."
-output_json:
- description: The output parsed as JSON
- returned: if output can be parsed as JSON
- type: dict
- sample:
- - id: 12345
- - key:
- - kty: RSA
- - ...
-'''
-
-from ansible.module_utils.acme import (
- ModuleFailException,
- ACMEAccount,
- handle_standard_module_arguments,
- get_default_argspec,
-)
-
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils._text import to_native, to_bytes
-
-import json
-
-
-def main():
- argument_spec = get_default_argspec()
- argument_spec.update(dict(
- url=dict(type='str'),
- method=dict(type='str', choices=['get', 'post', 'directory-only'], default='get'),
- content=dict(type='str'),
- fail_on_acme_error=dict(type='bool', default=True),
- ))
- module = AnsibleModule(
- argument_spec=argument_spec,
- mutually_exclusive=(
- ['account_key_src', 'account_key_content'],
- ),
- required_if=(
- ['method', 'get', ['url']],
- ['method', 'post', ['url', 'content']],
- ['method', 'get', ['account_key_src', 'account_key_content'], True],
- ['method', 'post', ['account_key_src', 'account_key_content'], True],
- ),
- )
- handle_standard_module_arguments(module)
-
- result = dict()
- changed = False
- try:
- # Get hold of ACMEAccount object (includes directory)
- account = ACMEAccount(module)
- method = module.params['method']
- result['directory'] = account.directory.directory
- # Do we have to do more requests?
- if method != 'directory-only':
- url = module.params['url']
- fail_on_acme_error = module.params['fail_on_acme_error']
- # Do request
- if method == 'get':
- data, info = account.get_request(url, parse_json_result=False, fail_on_error=False)
- elif method == 'post':
- changed = True # only POSTs can change
- data, info = account.send_signed_request(url, to_bytes(module.params['content']), parse_json_result=False, encode_payload=False)
- # Update results
- result.update(dict(
- headers=info,
- output_text=to_native(data),
- ))
- # See if we can parse the result as JSON
- try:
- result['output_json'] = json.loads(data)
- except Exception as dummy:
- pass
- # Fail if error was returned
- if fail_on_acme_error and info['status'] >= 400:
- raise ModuleFailException("ACME request failed: CODE: {0} RESULT: {1}".format(info['status'], data))
- # Done!
- module.exit_json(changed=changed, **result)
- except ModuleFailException as e:
- e.do_fail(module, **result)
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/crypto/certificate_complete_chain.py b/lib/ansible/modules/crypto/certificate_complete_chain.py
deleted file mode 100644
index 77fbe2ee52..0000000000
--- a/lib/ansible/modules/crypto/certificate_complete_chain.py
+++ /dev/null
@@ -1,350 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-#
-# (c) 2018, Felix Fontein <felix@fontein.de>
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'community'}
-
-
-DOCUMENTATION = '''
----
-module: certificate_complete_chain
-author: "Felix Fontein (@felixfontein)"
-version_added: "2.7"
-short_description: Complete certificate chain given a set of untrusted and root certificates
-description:
- - "This module completes a given chain of certificates in PEM format by finding
- intermediate certificates from a given set of certificates, until it finds a root
- certificate in another given set of certificates."
- - "This can for example be used to find the root certificate for a certificate chain
- returned by M(acme_certificate)."
- - "Note that this module does I(not) check for validity of the chains. It only
- checks that issuer and subject match, and that the signature is correct. It
- ignores validity dates and key usage completely. If you need to verify that a
- generated chain is valid, please use C(openssl verify ...)."
-requirements:
- - "cryptography >= 1.5"
-options:
- input_chain:
- description:
- - A concatenated set of certificates in PEM format forming a chain.
- - The module will try to complete this chain.
- type: str
- required: yes
- root_certificates:
- description:
- - "A list of filenames or directories."
- - "A filename is assumed to point to a file containing one or more certificates
- in PEM format. All certificates in this file will be added to the set of
- root certificates."
- - "If a directory name is given, all files in the directory and its
- subdirectories will be scanned and tried to be parsed as concatenated
- certificates in PEM format."
- - "Symbolic links will be followed."
- type: list
- elements: path
- required: yes
- intermediate_certificates:
- description:
- - "A list of filenames or directories."
- - "A filename is assumed to point to a file containing one or more certificates
- in PEM format. All certificates in this file will be added to the set of
- root certificates."
- - "If a directory name is given, all files in the directory and its
- subdirectories will be scanned and tried to be parsed as concatenated
- certificates in PEM format."
- - "Symbolic links will be followed."
- type: list
- elements: path
- default: []
-'''
-
-
-EXAMPLES = '''
-# Given a leaf certificate for www.ansible.com and one or more intermediate
-# certificates, finds the associated root certificate.
-- name: Find root certificate
- certificate_complete_chain:
- input_chain: "{{ lookup('file', '/etc/ssl/csr/www.ansible.com-fullchain.pem') }}"
- root_certificates:
- - /etc/ca-certificates/
- register: www_ansible_com
-- name: Write root certificate to disk
- copy:
- dest: /etc/ssl/csr/www.ansible.com-root.pem
- content: "{{ www_ansible_com.root }}"
-
-# Given a leaf certificate for www.ansible.com, and a list of intermediate
-# certificates, finds the associated root certificate.
-- name: Find root certificate
- certificate_complete_chain:
- input_chain: "{{ lookup('file', '/etc/ssl/csr/www.ansible.com.pem') }}"
- intermediate_certificates:
- - /etc/ssl/csr/www.ansible.com-chain.pem
- root_certificates:
- - /etc/ca-certificates/
- register: www_ansible_com
-- name: Write complete chain to disk
- copy:
- dest: /etc/ssl/csr/www.ansible.com-completechain.pem
- content: "{{ ''.join(www_ansible_com.complete_chain) }}"
-- name: Write root chain (intermediates and root) to disk
- copy:
- dest: /etc/ssl/csr/www.ansible.com-rootchain.pem
- content: "{{ ''.join(www_ansible_com.chain) }}"
-'''
-
-
-RETURN = '''
-root:
- description:
- - "The root certificate in PEM format."
- returned: success
- type: str
-chain:
- description:
- - "The chain added to the given input chain. Includes the root certificate."
- - "Returned as a list of PEM certificates."
- returned: success
- type: list
- elements: str
-complete_chain:
- description:
- - "The completed chain, including leaf, all intermediates, and root."
- - "Returned as a list of PEM certificates."
- returned: success
- type: list
- elements: str
-'''
-
-import os
-import traceback
-
-from ansible.module_utils.basic import AnsibleModule, missing_required_lib
-from ansible.module_utils._text import to_bytes
-
-CRYPTOGRAPHY_IMP_ERR = None
-try:
- import cryptography
- import cryptography.hazmat.backends
- import cryptography.hazmat.primitives.serialization
- import cryptography.hazmat.primitives.asymmetric.rsa
- import cryptography.hazmat.primitives.asymmetric.ec
- import cryptography.hazmat.primitives.asymmetric.padding
- import cryptography.hazmat.primitives.hashes
- import cryptography.hazmat.primitives.asymmetric.utils
- import cryptography.x509
- import cryptography.x509.oid
- from distutils.version import LooseVersion
- HAS_CRYPTOGRAPHY = (LooseVersion(cryptography.__version__) >= LooseVersion('1.5'))
- _cryptography_backend = cryptography.hazmat.backends.default_backend()
-except ImportError as dummy:
- CRYPTOGRAPHY_IMP_ERR = traceback.format_exc()
- HAS_CRYPTOGRAPHY = False
-
-
-class Certificate(object):
- '''
- Stores PEM with parsed certificate.
- '''
- def __init__(self, pem, cert):
- if not (pem.endswith('\n') or pem.endswith('\r')):
- pem = pem + '\n'
- self.pem = pem
- self.cert = cert
-
-
-def is_parent(module, cert, potential_parent):
- '''
- Tests whether the given certificate has been issued by the potential parent certificate.
- '''
- # Check issuer
- if cert.cert.issuer != potential_parent.cert.subject:
- return False
- # Check signature
- public_key = potential_parent.cert.public_key()
- try:
- if isinstance(public_key, cryptography.hazmat.primitives.asymmetric.rsa.RSAPublicKey):
- public_key.verify(
- cert.cert.signature,
- cert.cert.tbs_certificate_bytes,
- cryptography.hazmat.primitives.asymmetric.padding.PKCS1v15(),
- cert.cert.signature_hash_algorithm
- )
- elif isinstance(public_key, cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePublicKey):
- public_key.verify(
- cert.cert.signature,
- cert.cert.tbs_certificate_bytes,
- cryptography.hazmat.primitives.asymmetric.ec.ECDSA(cert.cert.signature_hash_algorithm),
- )
- else:
- # Unknown public key type
- module.warn('Unknown public key type "{0}"'.format(public_key))
- return False
- return True
- except cryptography.exceptions.InvalidSignature as dummy:
- return False
- except Exception as e:
- module.fail_json(msg='Unknown error on signature validation: {0}'.format(e))
-
-
-def parse_PEM_list(module, text, source, fail_on_error=True):
- '''
- Parse concatenated PEM certificates. Return list of ``Certificate`` objects.
- '''
- result = []
- lines = text.splitlines(True)
- current = None
- for line in lines:
- if line.strip():
- if line.startswith('-----BEGIN '):
- current = [line]
- elif current is not None:
- current.append(line)
- if line.startswith('-----END '):
- cert_pem = ''.join(current)
- current = None
- # Try to load PEM certificate
- try:
- cert = cryptography.x509.load_pem_x509_certificate(to_bytes(cert_pem), _cryptography_backend)
- result.append(Certificate(cert_pem, cert))
- except Exception as e:
- msg = 'Cannot parse certificate #{0} from {1}: {2}'.format(len(result) + 1, source, e)
- if fail_on_error:
- module.fail_json(msg=msg)
- else:
- module.warn(msg)
- return result
-
-
-def load_PEM_list(module, path, fail_on_error=True):
- '''
- Load concatenated PEM certificates from file. Return list of ``Certificate`` objects.
- '''
- try:
- with open(path, "rb") as f:
- return parse_PEM_list(module, f.read().decode('utf-8'), source=path, fail_on_error=fail_on_error)
- except Exception as e:
- msg = 'Cannot read certificate file {0}: {1}'.format(path, e)
- if fail_on_error:
- module.fail_json(msg=msg)
- else:
- module.warn(msg)
- return []
-
-
-class CertificateSet(object):
- '''
- Stores a set of certificates. Allows to search for parent (issuer of a certificate).
- '''
-
- def __init__(self, module):
- self.module = module
- self.certificates = set()
- self.certificate_by_issuer = dict()
-
- def _load_file(self, path):
- certs = load_PEM_list(self.module, path, fail_on_error=False)
- for cert in certs:
- self.certificates.add(cert)
- self.certificate_by_issuer[cert.cert.subject] = cert
-
- def load(self, path):
- '''
- Load lists of PEM certificates from a file or a directory.
- '''
- b_path = to_bytes(path, errors='surrogate_or_strict')
- if os.path.isdir(b_path):
- for directory, dummy, files in os.walk(b_path, followlinks=True):
- for file in files:
- self._load_file(os.path.join(directory, file))
- else:
- self._load_file(b_path)
-
- def find_parent(self, cert):
- '''
- Search for the parent (issuer) of a certificate. Return ``None`` if none was found.
- '''
- potential_parent = self.certificate_by_issuer.get(cert.cert.issuer)
- if potential_parent is not None:
- if is_parent(self.module, cert, potential_parent):
- return potential_parent
- return None
-
-
-def format_cert(cert):
- '''
- Return human readable representation of certificate for error messages.
- '''
- return str(cert.cert)
-
-
-def main():
- module = AnsibleModule(
- argument_spec=dict(
- input_chain=dict(type='str', required=True),
- root_certificates=dict(type='list', required=True, elements='path'),
- intermediate_certificates=dict(type='list', default=[], elements='path'),
- ),
- supports_check_mode=True,
- )
-
- if not HAS_CRYPTOGRAPHY:
- module.fail_json(msg=missing_required_lib('cryptography >= 1.5'), exception=CRYPTOGRAPHY_IMP_ERR)
-
- # Load chain
- chain = parse_PEM_list(module, module.params['input_chain'], source='input chain')
- if len(chain) == 0:
- module.fail_json(msg='Input chain must contain at least one certificate')
-
- # Check chain
- for i, parent in enumerate(chain):
- if i > 0:
- if not is_parent(module, chain[i - 1], parent):
- module.fail_json(msg=('Cannot verify input chain: certificate #{2}: {3} is not issuer ' +
- 'of certificate #{0}: {1}').format(i, format_cert(chain[i - 1]), i + 1, format_cert(parent)))
-
- # Load intermediate certificates
- intermediates = CertificateSet(module)
- for path in module.params['intermediate_certificates']:
- intermediates.load(path)
-
- # Load root certificates
- roots = CertificateSet(module)
- for path in module.params['root_certificates']:
- roots.load(path)
-
- # Try to complete chain
- current = chain[-1]
- completed = []
- while current:
- root = roots.find_parent(current)
- if root:
- completed.append(root)
- break
- intermediate = intermediates.find_parent(current)
- if intermediate:
- completed.append(intermediate)
- current = intermediate
- else:
- module.fail_json(msg='Cannot complete chain. Stuck at certificate {0}'.format(format_cert(current)))
-
- # Return results
- complete_chain = chain + completed
- module.exit_json(
- changed=False,
- root=complete_chain[-1].pem,
- chain=[cert.pem for cert in completed],
- complete_chain=[cert.pem for cert in complete_chain],
- )
-
-
-if __name__ == "__main__":
- main()
diff --git a/lib/ansible/modules/crypto/entrust/ecs_certificate.py b/lib/ansible/modules/crypto/entrust/ecs_certificate.py
deleted file mode 100644
index dd1330653e..0000000000
--- a/lib/ansible/modules/crypto/entrust/ecs_certificate.py
+++ /dev/null
@@ -1,952 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-
-# Copyright (c), Entrust Datacard Corporation, 2019
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'community'}
-
-DOCUMENTATION = '''
----
-module: ecs_certificate
-author:
- - Chris Trufan (@ctrufan)
-version_added: '2.9'
-short_description: Request SSL/TLS certificates with the Entrust Certificate Services (ECS) API
-description:
- - Create, reissue, and renew certificates with the Entrust Certificate Services (ECS) API.
- - Requires credentials for the L(Entrust Certificate Services,https://www.entrustdatacard.com/products/categories/ssl-certificates) (ECS) API.
- - In order to request a certificate, the domain and organization used in the certificate signing request must be already
- validated in the ECS system. It is I(not) the responsibility of this module to perform those steps.
-notes:
- - C(path) must be specified as the output location of the certificate.
-requirements:
- - cryptography >= 1.6
-options:
- backup:
- description:
- - Whether a backup should be made for the certificate in I(path).
- type: bool
- default: false
- force:
- description:
- - If force is used, a certificate is requested regardless of whether I(path) points to an existing valid certificate.
- - If C(request_type=renew), a forced renew will fail if the certificate being renewed has been issued within the past 30 days, regardless of the
- value of I(remaining_days) or the return value of I(cert_days) - the ECS API does not support the "renew" operation for certificates that are not
- at least 30 days old.
- type: bool
- default: false
- path:
- description:
- - The destination path for the generated certificate as a PEM encoded cert.
- - If the certificate at this location is not an Entrust issued certificate, a new certificate will always be requested even if the current
- certificate is technically valid.
- - If there is already an Entrust certificate at this location, whether it is replaced is depends on the I(remaining_days) calculation.
- - If an existing certificate is being replaced (see I(remaining_days), I(force), and I(tracking_id)), whether a new certificate is requested
- or the existing certificate is renewed or reissued is based on I(request_type).
- type: path
- required: true
- full_chain_path:
- description:
- - The destination path for the full certificate chain of the certificate, intermediates, and roots.
- type: path
- csr:
- description:
- - Base-64 encoded Certificate Signing Request (CSR). I(csr) is accepted with or without PEM formatting around the Base-64 string.
- - If no I(csr) is provided when C(request_type=reissue) or C(request_type=renew), the certificate will be generated with the same public key as
- the certificate being renewed or reissued.
- - If I(subject_alt_name) is specified, it will override the subject alternate names in the CSR.
- - If I(eku) is specified, it will override the extended key usage in the CSR.
- - If I(ou) is specified, it will override the organizational units "ou=" present in the subject distinguished name of the CSR, if any.
- - The organization "O=" field from the CSR will not be used. It will be replaced in the issued certificate by I(org) if present, and if not present,
- the organization tied to I(client_id).
- type: str
- tracking_id:
- description:
- - The tracking ID of the certificate to reissue or renew.
- - I(tracking_id) is invalid if C(request_type=new) or C(request_type=validate_only).
- - If there is a certificate present in I(path) and it is an ECS certificate, I(tracking_id) will be ignored.
- - If there is no certificate present in I(path) or there is but it is from another provider, the certificate represented by I(tracking_id) will
- be renewed or reissued and saved to I(path).
- - If there is no certificate present in I(path) and the I(force) and I(remaining_days) parameters do not indicate a new certificate is needed,
- the certificate referenced by I(tracking_id) certificate will be saved to I(path).
- - This can be used when a known certificate is not currently present on a server, but you want to renew or reissue it to be managed by an ansible
- playbook. For example, if you specify C(request_type=renew), I(tracking_id) of an issued certificate, and I(path) to a file that does not exist,
- the first run of a task will download the certificate specified by I(tracking_id) (assuming it is still valid). Future runs of the task will
- (if applicable - see I(force) and I(remaining_days)) renew the certificate now present in I(path).
- type: int
- remaining_days:
- description:
- - The number of days the certificate must have left being valid. If C(cert_days < remaining_days) then a new certificate will be
- obtained using I(request_type).
- - If C(request_type=renew), a renewal will fail if the certificate being renewed has been issued within the past 30 days, so do not set a
- I(remaining_days) value that is within 30 days of the full lifetime of the certificate being acted upon. (e.g. if you are requesting Certificates
- with a 90 day lifetime, do not set remaining_days to a value C(60) or higher).
- - The I(force) option may be used to ensure that a new certificate is always obtained.
- type: int
- default: 30
- request_type:
- description:
- - The operation performed if I(tracking_id) references a valid certificate to reissue, or there is already a certificate present in I(path) but
- either I(force) is specified or C(cert_days < remaining_days).
- - Specifying C(request_type=validate_only) means the request will be validated against the ECS API, but no certificate will be issued.
- - Specifying C(request_type=new) means a certificate request will always be submitted and a new certificate issued.
- - Specifying C(request_type=renew) means that an existing certificate (specified by I(tracking_id) if present, otherwise I(path)) will be renewed.
- If there is no certificate to renew, a new certificate is requested.
- - Specifying C(request_type=reissue) means that an existing certificate (specified by I(tracking_id) if present, otherwise I(path)) will be
- reissued.
- If there is no certificate to reissue, a new certificate is requested.
- - If a certificate was issued within the past 30 days, the 'renew' operation is not a valid operation and will fail.
- - Note that C(reissue) is an operation that will result in the revocation of the certificate that is reissued, be cautious with it's use.
- - I(check_mode) is only supported if C(request_type=new)
- - For example, setting C(request_type=renew) and C(remaining_days=30) and pointing to the same certificate on multiple playbook runs means that on
- the first run new certificate will be requested. It will then be left along on future runs until it is within 30 days of expiry, then the
- ECS "renew" operation will be performed.
- type: str
- choices: [ 'new', 'renew', 'reissue', 'validate_only']
- default: new
- cert_type:
- description:
- - Specify the type of certificate requested.
- - If a certificate is being reissued or renewed, this parameter is ignored, and the C(cert_type) of the initial certificate is used.
- type: str
- choices: [ 'STANDARD_SSL', 'ADVANTAGE_SSL', 'UC_SSL', 'EV_SSL', 'WILDCARD_SSL', 'PRIVATE_SSL', 'PD_SSL', 'CODE_SIGNING', 'EV_CODE_SIGNING',
- 'CDS_INDIVIDUAL', 'CDS_GROUP', 'CDS_ENT_LITE', 'CDS_ENT_PRO', 'SMIME_ENT' ]
- subject_alt_name:
- description:
- - The subject alternative name identifiers, as an array of values (applies to I(cert_type) with a value of C(STANDARD_SSL), C(ADVANTAGE_SSL),
- C(UC_SSL), C(EV_SSL), C(WILDCARD_SSL), C(PRIVATE_SSL), and C(PD_SSL)).
- - If you are requesting a new SSL certificate, and you pass a I(subject_alt_name) parameter, any SAN names in the CSR are ignored.
- If no subjectAltName parameter is passed, the SAN names in the CSR are used.
- - See I(request_type) to understand more about SANs during reissues and renewals.
- - In the case of certificates of type C(STANDARD_SSL) certificates, if the CN of the certificate is <domain>.<tld> only the www.<domain>.<tld> value
- is accepted. If the CN of the certificate is www.<domain>.<tld> only the <domain>.<tld> value is accepted.
- type: list
- elements: str
- eku:
- description:
- - If specified, overrides the key usage in the I(csr).
- type: str
- choices: [ SERVER_AUTH, CLIENT_AUTH, SERVER_AND_CLIENT_AUTH ]
- ct_log:
- description:
- - In compliance with browser requirements, this certificate may be posted to the Certificate Transparency (CT) logs. This is a best practice
- technique that helps domain owners monitor certificates issued to their domains. Note that not all certificates are eligible for CT logging.
- - If I(ct_log) is not specified, the certificate uses the account default.
- - If I(ct_log) is specified and the account settings allow it, I(ct_log) overrides the account default.
- - If I(ct_log) is set to C(false), but the account settings are set to "always log", the certificate generation will fail.
- type: bool
- client_id:
- description:
- - The client ID to submit the Certificate Signing Request under.
- - If no client ID is specified, the certificate will be submitted under the primary client with ID of 1.
- - When using a client other than the primary client, the I(org) parameter cannot be specified.
- - The issued certificate will have an organization value in the subject distinguished name represented by the client.
- type: int
- default: 1
- org:
- description:
- - Organization "O=" to include in the certificate.
- - If I(org) is not specified, the organization from the client represented by I(client_id) is used.
- - Unless the I(cert_type) is C(PD_SSL), this field may not be specified if the value of I(client_id) is not "1" (the primary client).
- non-primary clients, certificates may only be issued with the organization of that client.
- type: str
- ou:
- description:
- - Organizational unit "OU=" to include in the certificate.
- - I(ou) behavior is dependent on whether organizational units are enabled for your account. If organizational unit support is disabled for your
- account, organizational units from the I(csr) and the I(ou) parameter are ignored.
- - If both I(csr) and I(ou) are specified, the value in I(ou) will override the OU fields present in the subject distinguished name in the I(csr)
- - If neither I(csr) nor I(ou) are specified for a renew or reissue operation, the OU fields in the initial certificate are reused.
- - An invalid OU from I(csr) is ignored, but any invalid organizational units in I(ou) will result in an error indicating "Unapproved OU". The I(ou)
- parameter can be used to force failure if an unapproved organizational unit is provided.
- - A maximum of one OU may be specified for current products. Multiple OUs are reserved for future products.
- type: list
- elements: str
- end_user_key_storage_agreement:
- description:
- - The end user of the Code Signing certificate must generate and store the private key for this request on cryptographically secure
- hardware to be compliant with the Entrust CSP and Subscription agreement. If requesting a certificate of type C(CODE_SIGNING) or
- C(EV_CODE_SIGNING), you must set I(end_user_key_storage_agreement) to true if and only if you acknowledge that you will inform the user of this
- requirement.
- - Applicable only to I(cert_type) of values C(CODE_SIGNING) and C(EV_CODE_SIGNING).
- type: bool
- tracking_info:
- description: Free form tracking information to attach to the record for the certificate.
- type: str
- requester_name:
- description: The requester name to associate with certificate tracking information.
- type: str
- required: true
- requester_email:
- description: The requester email to associate with certificate tracking information and receive delivery and expiry notices for the certificate.
- type: str
- required: true
- requester_phone:
- description: The requester phone number to associate with certificate tracking information.
- type: str
- required: true
- additional_emails:
- description: A list of additional email addresses to receive the delivery notice and expiry notification for the certificate.
- type: list
- elements: str
- custom_fields:
- description:
- - Mapping of custom fields to associate with the certificate request and certificate.
- - Only supported if custom fields are enabled for your account.
- - Each custom field specified must be a custom field you have defined for your account.
- type: dict
- suboptions:
- text1:
- description: Custom text field (maximum 500 characters)
- type: str
- text2:
- description: Custom text field (maximum 500 characters)
- type: str
- text3:
- description: Custom text field (maximum 500 characters)
- type: str
- text4:
- description: Custom text field (maximum 500 characters)
- type: str
- text5:
- description: Custom text field (maximum 500 characters)
- type: str
- text6:
- description: Custom text field (maximum 500 characters)
- type: str
- text7:
- description: Custom text field (maximum 500 characters)
- type: str
- text8:
- description: Custom text field (maximum 500 characters)
- type: str
- text9:
- description: Custom text field (maximum 500 characters)
- type: str
- text10:
- description: Custom text field (maximum 500 characters)
- type: str
- text11:
- description: Custom text field (maximum 500 characters)
- type: str
- text12:
- description: Custom text field (maximum 500 characters)
- type: str
- text13:
- description: Custom text field (maximum 500 characters)
- type: str
- text14:
- description: Custom text field (maximum 500 characters)
- type: str
- text15:
- description: Custom text field (maximum 500 characters)
- type: str
- number1:
- description: Custom number field.
- type: float
- number2:
- description: Custom number field.
- type: float
- number3:
- description: Custom number field.
- type: float
- number4:
- description: Custom number field.
- type: float
- number5:
- description: Custom number field.
- type: float
- date1:
- description: Custom date field.
- type: str
- date2:
- description: Custom date field.
- type: str
- date3:
- description: Custom date field.
- type: str
- date4:
- description: Custom date field.
- type: str
- date5:
- description: Custom date field.
- type: str
- email1:
- description: Custom email field.
- type: str
- email2:
- description: Custom email field.
- type: str
- email3:
- description: Custom email field.
- type: str
- email4:
- description: Custom email field.
- type: str
- email5:
- description: Custom email field.
- type: str
- dropdown1:
- description: Custom dropdown field.
- type: str
- dropdown2:
- description: Custom dropdown field.
- type: str
- dropdown3:
- description: Custom dropdown field.
- type: str
- dropdown4:
- description: Custom dropdown field.
- type: str
- dropdown5:
- description: Custom dropdown field.
- type: str
- cert_expiry:
- description:
- - The date the certificate should be set to expire, in RFC3339 compliant date or date-time format. For example,
- C(2020-02-23), C(2020-02-23T15:00:00.05Z).
- - I(cert_expiry) is only supported for requests of C(request_type=new) or C(request_type=renew). If C(request_type=reissue),
- I(cert_expiry) will be used for the first certificate issuance, but subsequent issuances will have the same expiry as the initial
- certificate.
- - A reissued certificate will always have the same expiry as the original certificate.
- - Note that only the date (day, month, year) is supported for specifying the expiry date. If you choose to specify an expiry time with the expiry
- date, the time will be adjusted to Eastern Standard Time (EST). This could have the unintended effect of moving your expiry date to the previous
- day.
- - Applies only to accounts with a pooling inventory model.
- - Only one of I(cert_expiry) or I(cert_lifetime) may be specified.
- type: str
- cert_lifetime:
- description:
- - The lifetime of the certificate.
- - Applies to all certificates for accounts with a non-pooling inventory model.
- - I(cert_lifetime) is only supported for requests of C(request_type=new) or C(request_type=renew). If C(request_type=reissue), I(cert_lifetime) will
- be used for the first certificate issuance, but subsequent issuances will have the same expiry as the initial certificate.
- - Applies to certificates of I(cert_type)=C(CDS_INDIVIDUAL, CDS_GROUP, CDS_ENT_LITE, CDS_ENT_PRO, SMIME_ENT) for accounts with a pooling inventory
- model.
- - C(P1Y) is a certificate with a 1 year lifetime.
- - C(P2Y) is a certificate with a 2 year lifetime.
- - C(P3Y) is a certificate with a 3 year lifetime.
- - Only one of I(cert_expiry) or I(cert_lifetime) may be specified.
- type: str
- choices: [ P1Y, P2Y, P3Y ]
-seealso:
- - module: openssl_privatekey
- description: Can be used to create private keys (both for certificates and accounts).
- - module: openssl_csr
- description: Can be used to create a Certificate Signing Request (CSR).
-extends_documentation_fragment:
- - ecs_credential
-'''
-
-EXAMPLES = r'''
-- name: Request a new certificate from Entrust with bare minimum parameters.
- Will request a new certificate if current one is valid but within 30
- days of expiry. If replacing an existing file in path, will back it up.
- ecs_certificate:
- backup: true
- path: /etc/ssl/crt/ansible.com.crt
- full_chain_path: /etc/ssl/crt/ansible.com.chain.crt
- csr: /etc/ssl/csr/ansible.com.csr
- cert_type: EV_SSL
- requester_name: Jo Doe
- requester_email: jdoe@ansible.com
- requester_phone: 555-555-5555
- entrust_api_user: apiusername
- entrust_api_key: a^lv*32!cd9LnT
- entrust_api_client_cert_path: /etc/ssl/entrust/ecs-client.crt
- entrust_api_client_cert_key_path: /etc/ssl/entrust/ecs-client.key
-
-- name: If there is no certificate present in path, request a new certificate
- of type EV_SSL. Otherwise, if there is an Entrust managed certificate
- in path and it is within 63 days of expiration, request a renew of that
- certificate.
- ecs_certificate:
- path: /etc/ssl/crt/ansible.com.crt
- csr: /etc/ssl/csr/ansible.com.csr
- cert_type: EV_SSL
- cert_expiry: '2020-08-20'
- request_type: renew
- remaining_days: 63
- requester_name: Jo Doe
- requester_email: jdoe@ansible.com
- requester_phone: 555-555-5555
- entrust_api_user: apiusername
- entrust_api_key: a^lv*32!cd9LnT
- entrust_api_client_cert_path: /etc/ssl/entrust/ecs-client.crt
- entrust_api_client_cert_key_path: /etc/ssl/entrust/ecs-client.key
-
-- name: If there is no certificate present in path, download certificate
- specified by tracking_id if it is still valid. Otherwise, if the
- certificate is within 79 days of expiration, request a renew of that
- certificate and save it in path. This can be used to "migrate" a
- certificate to be Ansible managed.
- ecs_certificate:
- path: /etc/ssl/crt/ansible.com.crt
- csr: /etc/ssl/csr/ansible.com.csr
- tracking_id: 2378915
- request_type: renew
- remaining_days: 79
- entrust_api_user: apiusername
- entrust_api_key: a^lv*32!cd9LnT
- entrust_api_client_cert_path: /etc/ssl/entrust/ecs-client.crt
- entrust_api_client_cert_key_path: /etc/ssl/entrust/ecs-client.key
-
-- name: Force a reissue of the certificate specified by tracking_id.
- ecs_certificate:
- path: /etc/ssl/crt/ansible.com.crt
- force: true
- tracking_id: 2378915
- request_type: reissue
- entrust_api_user: apiusername
- entrust_api_key: a^lv*32!cd9LnT
- entrust_api_client_cert_path: /etc/ssl/entrust/ecs-client.crt
- entrust_api_client_cert_key_path: /etc/ssl/entrust/ecs-client.key
-
-- name: Request a new certificate with an alternative client. Note that the
- issued certificate will have it's Subject Distinguished Name use the
- organization details associated with that client, rather than what is
- in the CSR.
- ecs_certificate:
- path: /etc/ssl/crt/ansible.com.crt
- csr: /etc/ssl/csr/ansible.com.csr
- client_id: 2
- requester_name: Jo Doe
- requester_email: jdoe@ansible.com
- requester_phone: 555-555-5555
- entrust_api_user: apiusername
- entrust_api_key: a^lv*32!cd9LnT
- entrust_api_client_cert_path: /etc/ssl/entrust/ecs-client.crt
- entrust_api_client_cert_key_path: /etc/ssl/entrust/ecs-client.key
-
-- name: Request a new certificate with a number of CSR parameters overridden
- and tracking information
- ecs_certificate:
- path: /etc/ssl/crt/ansible.com.crt
- full_chain_path: /etc/ssl/crt/ansible.com.chain.crt
- csr: /etc/ssl/csr/ansible.com.csr
- subject_alt_name:
- - ansible.testcertificates.com
- - www.testcertificates.com
- eku: SERVER_AND_CLIENT_AUTH
- ct_log: true
- org: Test Organization Inc.
- ou:
- - Administration
- tracking_info: "Submitted via Ansible"
- additional_emails:
- - itsupport@testcertificates.com
- - jsmith@ansible.com
- custom_fields:
- text1: Admin
- text2: Invoice 25
- number1: 342
- date1: '2018-01-01'
- email1: sales@ansible.testcertificates.com
- dropdown1: red
- cert_expiry: '2020-08-15'
- requester_name: Jo Doe
- requester_email: jdoe@ansible.com
- requester_phone: 555-555-5555
- entrust_api_user: apiusername
- entrust_api_key: a^lv*32!cd9LnT
- entrust_api_client_cert_path: /etc/ssl/entrust/ecs-client.crt
- entrust_api_client_cert_key_path: /etc/ssl/entrust/ecs-client.key
-
-'''
-
-RETURN = '''
-filename:
- description: The destination path for the generated certificate.
- returned: changed or success
- type: str
- sample: /etc/ssl/crt/www.ansible.com.crt
-backup_file:
- description: Name of backup file created for the certificate.
- returned: changed and if I(backup) is C(true)
- type: str
- sample: /path/to/www.ansible.com.crt.2019-03-09@11:22~
-backup_full_chain_file:
- description: Name of the backup file created for the certificate chain.
- returned: changed and if I(backup) is C(true) and I(full_chain_path) is set.
- type: str
- sample: /path/to/ca.chain.crt.2019-03-09@11:22~
-tracking_id:
- description: The tracking ID to reference and track the certificate in ECS.
- returned: success
- type: int
- sample: 380079
-serial_number:
- description: The serial number of the issued certificate.
- returned: success
- type: int
- sample: 1235262234164342
-cert_days:
- description: The number of days the certificate remains valid.
- returned: success
- type: int
- sample: 253
-cert_status:
- description:
- - The certificate status in ECS.
- - 'Current possible values (which may be expanded in the future) are: C(ACTIVE), C(APPROVED), C(DEACTIVATED), C(DECLINED), C(EXPIRED), C(NA),
- C(PENDING), C(PENDING_QUORUM), C(READY), C(REISSUED), C(REISSUING), C(RENEWED), C(RENEWING), C(REVOKED), C(SUSPENDED)'
- returned: success
- type: str
- sample: ACTIVE
-cert_details:
- description:
- - The full response JSON from the Get Certificate call of the ECS API.
- - 'While the response contents are guaranteed to be forwards compatible with new ECS API releases, Entrust recommends that you do not make any
- playbooks take actions based on the content of this field. However it may be useful for debugging, logging, or auditing purposes.'
- returned: success
- type: dict
-
-'''
-
-from ansible.module_utils.ecs.api import (
- ecs_client_argument_spec,
- ECSClient,
- RestOperationException,
- SessionConfigurationException,
-)
-
-import datetime
-import json
-import os
-import re
-import time
-import traceback
-from distutils.version import LooseVersion
-
-from ansible.module_utils import crypto as crypto_utils
-from ansible.module_utils.basic import AnsibleModule, missing_required_lib
-from ansible.module_utils._text import to_native, to_bytes
-
-CRYPTOGRAPHY_IMP_ERR = None
-try:
- import cryptography
- CRYPTOGRAPHY_VERSION = LooseVersion(cryptography.__version__)
-except ImportError:
- CRYPTOGRAPHY_IMP_ERR = traceback.format_exc()
- CRYPTOGRAPHY_FOUND = False
-else:
- CRYPTOGRAPHY_FOUND = True
-
-MINIMAL_CRYPTOGRAPHY_VERSION = '1.6'
-
-
-def validate_cert_expiry(cert_expiry):
- search_string_partial = re.compile(r'^([0-9]+)-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])\Z')
- search_string_full = re.compile(r'^([0-9]+)-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])[Tt]([01][0-9]|2[0-3]):([0-5][0-9]):'
- r'([0-5][0-9]|60)(.[0-9]+)?(([Zz])|([+|-]([01][0-9]|2[0-3]):[0-5][0-9]))\Z')
- if search_string_partial.match(cert_expiry) or search_string_full.match(cert_expiry):
- return True
- return False
-
-
-def calculate_cert_days(expires_after):
- cert_days = 0
- if expires_after:
- expires_after_datetime = datetime.datetime.strptime(expires_after, '%Y-%m-%dT%H:%M:%SZ')
- cert_days = (expires_after_datetime - datetime.datetime.now()).days
- return cert_days
-
-
-# Populate the value of body[dict_param_name] with the JSON equivalent of
-# module parameter of param_name if that parameter is present, otherwise leave field
-# out of resulting dict
-def convert_module_param_to_json_bool(module, dict_param_name, param_name):
- body = {}
- if module.params[param_name] is not None:
- if module.params[param_name]:
- body[dict_param_name] = 'true'
- else:
- body[dict_param_name] = 'false'
- return body
-
-
-class EcsCertificate(object):
- '''
- Entrust Certificate Services certificate class.
- '''
-
- def __init__(self, module):
- self.path = module.params['path']
- self.full_chain_path = module.params['full_chain_path']
- self.force = module.params['force']
- self.backup = module.params['backup']
- self.request_type = module.params['request_type']
- self.csr = module.params['csr']
-
- # All return values
- self.changed = False
- self.filename = None
- self.tracking_id = None
- self.cert_status = None
- self.serial_number = None
- self.cert_days = None
- self.cert_details = None
- self.backup_file = None
- self.backup_full_chain_file = None
-
- self.cert = None
- self.ecs_client = None
- if self.path and os.path.exists(self.path):
- try:
- self.cert = crypto_utils.load_certificate(self.path, backend='cryptography')
- except Exception as dummy:
- self.cert = None
- # Instantiate the ECS client and then try a no-op connection to verify credentials are valid
- try:
- self.ecs_client = ECSClient(
- entrust_api_user=module.params['entrust_api_user'],
- entrust_api_key=module.params['entrust_api_key'],
- entrust_api_cert=module.params['entrust_api_client_cert_path'],
- entrust_api_cert_key=module.params['entrust_api_client_cert_key_path'],
- entrust_api_specification_path=module.params['entrust_api_specification_path']
- )
- except SessionConfigurationException as e:
- module.fail_json(msg='Failed to initialize Entrust Provider: {0}'.format(to_native(e)))
- try:
- self.ecs_client.GetAppVersion()
- except RestOperationException as e:
- module.fail_json(msg='Please verify credential information. Received exception when testing ECS connection: {0}'.format(to_native(e.message)))
-
- # Conversion of the fields that go into the 'tracking' parameter of the request object
- def convert_tracking_params(self, module):
- body = {}
- tracking = {}
- if module.params['requester_name']:
- tracking['requesterName'] = module.params['requester_name']
- if module.params['requester_email']:
- tracking['requesterEmail'] = module.params['requester_email']
- if module.params['requester_phone']:
- tracking['requesterPhone'] = module.params['requester_phone']
- if module.params['tracking_info']:
- tracking['trackingInfo'] = module.params['tracking_info']
- if module.params['custom_fields']:
- # Omit custom fields from submitted dict if not present, instead of submitting them with value of 'null'
- # The ECS API does technically accept null without error, but it complicates debugging user escalations and is unnecessary bandwidth.
- custom_fields = {}
- for k, v in module.params['custom_fields'].items():
- if v is not None:
- custom_fields[k] = v
- tracking['customFields'] = custom_fields
- if module.params['additional_emails']:
- tracking['additionalEmails'] = module.params['additional_emails']
- body['tracking'] = tracking
- return body
-
- def convert_cert_subject_params(self, module):
- body = {}
- if module.params['subject_alt_name']:
- body['subjectAltName'] = module.params['subject_alt_name']
- if module.params['org']:
- body['org'] = module.params['org']
- if module.params['ou']:
- body['ou'] = module.params['ou']
- return body
-
- def convert_general_params(self, module):
- body = {}
- if module.params['eku']:
- body['eku'] = module.params['eku']
- if self.request_type == 'new':
- body['certType'] = module.params['cert_type']
- body['clientId'] = module.params['client_id']
- body.update(convert_module_param_to_json_bool(module, 'ctLog', 'ct_log'))
- body.update(convert_module_param_to_json_bool(module, 'endUserKeyStorageAgreement', 'end_user_key_storage_agreement'))
- return body
-
- def convert_expiry_params(self, module):
- body = {}
- if module.params['cert_lifetime']:
- body['certLifetime'] = module.params['cert_lifetime']
- elif module.params['cert_expiry']:
- body['certExpiryDate'] = module.params['cert_expiry']
- # If neither cerTLifetime or certExpiryDate was specified and the request type is new, default to 365 days
- elif self.request_type != 'reissue':
- gmt_now = datetime.datetime.fromtimestamp(time.mktime(time.gmtime()))
- expiry = gmt_now + datetime.timedelta(days=365)
- body['certExpiryDate'] = expiry.strftime("%Y-%m-%dT%H:%M:%S.00Z")
- return body
-
- def set_tracking_id_by_serial_number(self, module):
- try:
- # Use serial_number to identify if certificate is an Entrust Certificate
- # with an associated tracking ID
- serial_number = "{0:X}".format(self.cert.serial_number)
- cert_results = self.ecs_client.GetCertificates(serialNumber=serial_number).get('certificates', {})
- if len(cert_results) == 1:
- self.tracking_id = cert_results[0].get('trackingId')
- except RestOperationException as dummy:
- # If we fail to find a cert by serial number, that's fine, we just don't set self.tracking_id
- return
-
- def set_cert_details(self, module):
- try:
- self.cert_details = self.ecs_client.GetCertificate(trackingId=self.tracking_id)
- self.cert_status = self.cert_details.get('status')
- self.serial_number = self.cert_details.get('serialNumber')
- self.cert_days = calculate_cert_days(self.cert_details.get('expiresAfter'))
- except RestOperationException as e:
- module.fail_json('Failed to get details of certificate with tracking_id="{0}", Error: '.format(self.tracking_id), to_native(e.message))
-
- def check(self, module):
- if self.cert:
- # We will only treat a certificate as valid if it is found as a managed entrust cert.
- # We will only set updated tracking ID based on certificate in "path" if it is managed by entrust.
- self.set_tracking_id_by_serial_number(module)
-
- if module.params['tracking_id'] and self.tracking_id and module.params['tracking_id'] != self.tracking_id:
- module.warn('tracking_id parameter of "{0}" provided, but will be ignored. Valid certificate was present in path "{1}" with '
- 'tracking_id of "{2}".'.format(module.params['tracking_id'], self.path, self.tracking_id))
-
- # If we did not end up setting tracking_id based on existing cert, get from module params
- if not self.tracking_id:
- self.tracking_id = module.params['tracking_id']
-
- if not self.tracking_id:
- return False
-
- self.set_cert_details(module)
-
- if self.cert_status == 'EXPIRED' or self.cert_status == 'SUSPENDED' or self.cert_status == 'REVOKED':
- return False
- if self.cert_days < module.params['remaining_days']:
- return False
-
- return True
-
- def request_cert(self, module):
- if not self.check(module) or self.force:
- body = {}
-
- # Read the CSR contents
- if self.csr and os.path.exists(self.csr):
- with open(self.csr, 'r') as csr_file:
- body['csr'] = csr_file.read()
-
- # Check if the path is already a cert
- # tracking_id may be set as a parameter or by get_cert_details if an entrust cert is in 'path'. If tracking ID is null
- # We will be performing a reissue operation.
- if self.request_type != 'new' and not self.tracking_id:
- module.warn('No existing Entrust certificate found in path={0} and no tracking_id was provided, setting request_type to "new" for this task'
- 'run. Future playbook runs that point to the pathination file in {1} will use request_type={2}'
- .format(self.path, self.path, self.request_type))
- self.request_type = 'new'
- elif self.request_type == 'new' and self.tracking_id:
- module.warn('Existing certificate being acted upon, but request_type is "new", so will be a new certificate issuance rather than a'
- 'reissue or renew')
- # Use cases where request type is new and no existing certificate, or where request type is reissue/renew and a valid
- # existing certificate is found, do not need warnings.
-
- body.update(self.convert_tracking_params(module))
- body.update(self.convert_cert_subject_params(module))
- body.update(self.convert_general_params(module))
- body.update(self.convert_expiry_params(module))
-
- if not module.check_mode:
- try:
- if self.request_type == 'validate_only':
- body['validateOnly'] = 'true'
- result = self.ecs_client.NewCertRequest(Body=body)
- if self.request_type == 'new':
- result = self.ecs_client.NewCertRequest(Body=body)
- elif self.request_type == 'renew':
- result = self.ecs_client.RenewCertRequest(trackingId=self.tracking_id, Body=body)
- elif self.request_type == 'reissue':
- result = self.ecs_client.ReissueCertRequest(trackingId=self.tracking_id, Body=body)
- self.tracking_id = result.get('trackingId')
- self.set_cert_details(module)
- except RestOperationException as e:
- module.fail_json(msg='Failed to request new certificate from Entrust (ECS) {0}'.format(e.message))
-
- if self.request_type != 'validate_only':
- if self.backup:
- self.backup_file = module.backup_local(self.path)
- crypto_utils.write_file(module, to_bytes(self.cert_details.get('endEntityCert')))
- if self.full_chain_path and self.cert_details.get('chainCerts'):
- if self.backup:
- self.backup_full_chain_file = module.backup_local(self.full_chain_path)
- chain_string = '\n'.join(self.cert_details.get('chainCerts')) + '\n'
- crypto_utils.write_file(module, to_bytes(chain_string), path=self.full_chain_path)
- self.changed = True
- # If there is no certificate present in path but a tracking ID was specified, save it to disk
- elif not os.path.exists(self.path) and self.tracking_id:
- if not module.check_mode:
- crypto_utils.write_file(module, to_bytes(self.cert_details.get('endEntityCert')))
- if self.full_chain_path and self.cert_details.get('chainCerts'):
- chain_string = '\n'.join(self.cert_details.get('chainCerts')) + '\n'
- crypto_utils.write_file(module, to_bytes(chain_string), path=self.full_chain_path)
- self.changed = True
-
- def dump(self):
- result = {
- 'changed': self.changed,
- 'filename': self.path,
- 'tracking_id': self.tracking_id,
- 'cert_status': self.cert_status,
- 'serial_number': self.serial_number,
- 'cert_days': self.cert_days,
- 'cert_details': self.cert_details,
- }
- if self.backup_file:
- result['backup_file'] = self.backup_file
- result['backup_full_chain_file'] = self.backup_full_chain_file
- return result
-
-
-def custom_fields_spec():
- return dict(
- text1=dict(type='str'),
- text2=dict(type='str'),
- text3=dict(type='str'),
- text4=dict(type='str'),
- text5=dict(type='str'),
- text6=dict(type='str'),
- text7=dict(type='str'),
- text8=dict(type='str'),
- text9=dict(type='str'),
- text10=dict(type='str'),
- text11=dict(type='str'),
- text12=dict(type='str'),
- text13=dict(type='str'),
- text14=dict(type='str'),
- text15=dict(type='str'),
- number1=dict(type='float'),
- number2=dict(type='float'),
- number3=dict(type='float'),
- number4=dict(type='float'),
- number5=dict(type='float'),
- date1=dict(type='str'),
- date2=dict(type='str'),
- date3=dict(type='str'),
- date4=dict(type='str'),
- date5=dict(type='str'),
- email1=dict(type='str'),
- email2=dict(type='str'),
- email3=dict(type='str'),
- email4=dict(type='str'),
- email5=dict(type='str'),
- dropdown1=dict(type='str'),
- dropdown2=dict(type='str'),
- dropdown3=dict(type='str'),
- dropdown4=dict(type='str'),
- dropdown5=dict(type='str'),
- )
-
-
-def ecs_certificate_argument_spec():
- return dict(
- backup=dict(type='bool', default=False),
- force=dict(type='bool', default=False),
- path=dict(type='path', required=True),
- full_chain_path=dict(type='path'),
- tracking_id=dict(type='int'),
- remaining_days=dict(type='int', default=30),
- request_type=dict(type='str', default='new', choices=['new', 'renew', 'reissue', 'validate_only']),
- cert_type=dict(type='str', choices=['STANDARD_SSL',
- 'ADVANTAGE_SSL',
- 'UC_SSL',
- 'EV_SSL',
- 'WILDCARD_SSL',
- 'PRIVATE_SSL',
- 'PD_SSL',
- 'CODE_SIGNING',
- 'EV_CODE_SIGNING',
- 'CDS_INDIVIDUAL',
- 'CDS_GROUP',
- 'CDS_ENT_LITE',
- 'CDS_ENT_PRO',
- 'SMIME_ENT',
- ]),
- csr=dict(type='str'),
- subject_alt_name=dict(type='list', elements='str'),
- eku=dict(type='str', choices=['SERVER_AUTH', 'CLIENT_AUTH', 'SERVER_AND_CLIENT_AUTH']),
- ct_log=dict(type='bool'),
- client_id=dict(type='int', default=1),
- org=dict(type='str'),
- ou=dict(type='list', elements='str'),
- end_user_key_storage_agreement=dict(type='bool'),
- tracking_info=dict(type='str'),
- requester_name=dict(type='str', required=True),
- requester_email=dict(type='str', required=True),
- requester_phone=dict(type='str', required=True),
- additional_emails=dict(type='list', elements='str'),
- custom_fields=dict(type='dict', default=None, options=custom_fields_spec()),
- cert_expiry=dict(type='str'),
- cert_lifetime=dict(type='str', choices=['P1Y', 'P2Y', 'P3Y']),
- )
-
-
-def main():
- ecs_argument_spec = ecs_client_argument_spec()
- ecs_argument_spec.update(ecs_certificate_argument_spec())
- module = AnsibleModule(
- argument_spec=ecs_argument_spec,
- required_if=(
- ['request_type', 'new', ['cert_type']],
- ['request_type', 'validate_only', ['cert_type']],
- ['cert_type', 'CODE_SIGNING', ['end_user_key_storage_agreement']],
- ['cert_type', 'EV_CODE_SIGNING', ['end_user_key_storage_agreement']],
- ),
- mutually_exclusive=(
- ['cert_expiry', 'cert_lifetime'],
- ),
- supports_check_mode=True,
- )
-
- if not CRYPTOGRAPHY_FOUND or CRYPTOGRAPHY_VERSION < LooseVersion(MINIMAL_CRYPTOGRAPHY_VERSION):
- module.fail_json(msg=missing_required_lib('cryptography >= {0}'.format(MINIMAL_CRYPTOGRAPHY_VERSION)),
- exception=CRYPTOGRAPHY_IMP_ERR)
-
- # If validate_only is used, pointing to an existing tracking_id is an invalid operation
- if module.params['tracking_id']:
- if module.params['request_type'] == 'new' or module.params['request_type'] == 'validate_only':
- module.fail_json(msg='The tracking_id field is invalid when request_type="{0}".'.format(module.params['request_type']))
-
- # A reissued request can not specify an expiration date or lifetime
- if module.params['request_type'] == 'reissue':
- if module.params['cert_expiry']:
- module.fail_json(msg='The cert_expiry field is invalid when request_type="reissue".')
- elif module.params['cert_lifetime']:
- module.fail_json(msg='The cert_lifetime field is invalid when request_type="reissue".')
- # Only a reissued request can omit the CSR
- else:
- module_params_csr = module.params['csr']
- if module_params_csr is None:
- module.fail_json(msg='The csr field is required when request_type={0}'.format(module.params['request_type']))
- elif not os.path.exists(module_params_csr):
- module.fail_json(msg='The csr field of {0} was not a valid path. csr is required when request_type={1}'.format(
- module_params_csr, module.params['request_type']))
-
- if module.params['ou'] and len(module.params['ou']) > 1:
- module.fail_json(msg='Multiple "ou" values are not currently supported.')
-
- if module.params['end_user_key_storage_agreement']:
- if module.params['cert_type'] != 'CODE_SIGNING' and module.params['cert_type'] != 'EV_CODE_SIGNING':
- module.fail_json(msg='Parameter "end_user_key_storage_agreement" is valid only for cert_types "CODE_SIGNING" and "EV_CODE_SIGNING"')
-
- if module.params['org'] and module.params['client_id'] != 1 and module.params['cert_type'] != 'PD_SSL':
- module.fail_json(msg='The "org" parameter is not supported when client_id parameter is set to a value other than 1, unless cert_type is "PD_SSL".')
-
- if module.params['cert_expiry']:
- if not validate_cert_expiry(module.params['cert_expiry']):
- module.fail_json(msg='The "cert_expiry" parameter of "{0}" is not a valid date or date-time'.format(module.params['cert_expiry']))
-
- certificate = EcsCertificate(module)
- certificate.request_cert(module)
- result = certificate.dump()
- module.exit_json(**result)
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/crypto/entrust/ecs_domain.py b/lib/ansible/modules/crypto/entrust/ecs_domain.py
deleted file mode 100644
index cda4dea53a..0000000000
--- a/lib/ansible/modules/crypto/entrust/ecs_domain.py
+++ /dev/null
@@ -1,409 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-
-# Copyright 2019 Entrust Datacard Corporation.
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'community'}
-
-
-DOCUMENTATION = '''
----
-module: ecs_domain
-author:
- - Chris Trufan (@ctrufan)
-version_added: '2.10'
-short_description: Request validation of a domain with the Entrust Certificate Services (ECS) API
-description:
- - Request validation or re-validation of a domain with the Entrust Certificate Services (ECS) API.
- - Requires credentials for the L(Entrust Certificate Services,https://www.entrustdatacard.com/products/categories/ssl-certificates) (ECS) API.
- - If the domain is already in the validation process, no new validation will be requested, but the validation data (if applicable) will be returned.
- - If the domain is already in the validation process but the I(verification_method) specified is different than the current I(verification_method),
- the I(verification_method) will be updated and validation data (if applicable) will be returned.
- - If the domain is an active, validated domain, the return value of I(changed) will be false, unless C(domain_status=EXPIRED), in which case a re-validation
- will be performed.
- - If C(verification_method=dns), details about the required DNS entry will be specified in the return parameters I(dns_contents), I(dns_location), and
- I(dns_resource_type).
- - If C(verification_method=web_server), details about the required file details will be specified in the return parameters I(file_contents) and
- I(file_location).
- - If C(verification_method=email), the email address(es) that the validation email(s) were sent to will be in the return parameter I(emails). This is
- purely informational. For domains requested using this module, this will always be a list of size 1.
-notes:
- - There is a small delay (typically about 5 seconds, but can be as long as 60 seconds) before obtaining the random values when requesting a validation
- while C(verification_method=dns) or C(verification_method=web_server). Be aware of that if doing many domain validation requests.
-options:
- client_id:
- description:
- - The client ID to request the domain be associated with.
- - If no client ID is specified, the domain will be added under the primary client with ID of 1.
- type: int
- default: 1
- domain_name:
- description:
- - The domain name to be verified or reverified.
- type: str
- required: true
- verification_method:
- description:
- - The verification method to be used to prove control of the domain.
- - If C(verification_method=email) and the value I(verification_email) is specified, that value is used for the email validation. If
- I(verification_email) is not provided, the first value present in WHOIS data will be used. An email will be sent to the address in
- I(verification_email) with instructions on how to verify control of the domain.
- - If C(verification_method=dns), the value I(dns_contents) must be stored in location I(dns_location), with a DNS record type of
- I(verification_dns_record_type). To prove domain ownership, update your DNS records so the text string returned by I(dns_contents) is available at
- I(dns_location).
- - If C(verification_method=web_server), the contents of return value I(file_contents) must be made available on a web server accessible at location
- I(file_location).
- - If C(verification_method=manual), the domain will be validated with a manual process. This is not recommended.
- type: str
- choices: [ 'dns', 'email', 'manual', 'web_server']
- required: true
- verification_email:
- description:
- - Email address to be used to verify domain ownership.
- - 'Email address must be either an email address present in the WHOIS data for I(domain_name), or one of the following constructed emails:
- admin@I(domain_name), administrator@I(domain_name), webmaster@I(domain_name), hostmaster@I(domain_name), postmaster@I(domain_name)'
- - 'Note that if I(domain_name) includes subdomains, the top level domain should be used. For example, if requesting validation of
- example1.ansible.com, or test.example2.ansible.com, and you want to use the "admin" preconstructed name, the email address should be
- admin@ansible.com.'
- - If using the email values from the WHOIS data for the domain or it's top level namespace, they must be exact matches.
- - If C(verification_method=email) but I(verification_email) is not provided, the first email address found in WHOIS data for the domain will be
- used.
- - To verify domain ownership, domain owner must follow the instructions in the email they receive.
- - Only allowed if C(verification_method=email)
- type: str
-seealso:
- - module: openssl_certificate
- description: Can be used to request certificates from ECS, with C(provider=entrust).
- - module: ecs_certificate
- description: Can be used to request a Certificate from ECS using a verified domain.
-extends_documentation_fragment:
- - ecs_credential
-'''
-
-EXAMPLES = r'''
-- name: Request domain validation using email validation for client ID of 2.
- ecs_domain:
- domain_name: ansible.com
- client_id: 2
- verification_method: email
- verification_email: admin@ansible.com
- entrust_api_user: apiusername
- entrust_api_key: a^lv*32!cd9LnT
- entrust_api_client_cert_path: /etc/ssl/entrust/ecs-client.crt
- entrust_api_client_cert_key_path: /etc/ssl/entrust/ecs-client.key
-
-- name: Request domain validation using DNS. If domain is already valid,
- request revalidation if expires within 90 days
- ecs_domain:
- domain_name: ansible.com
- verification_method: dns
- entrust_api_user: apiusername
- entrust_api_key: a^lv*32!cd9LnT
- entrust_api_client_cert_path: /etc/ssl/entrust/ecs-client.crt
- entrust_api_client_cert_key_path: /etc/ssl/entrust/ecs-client.key
-
-- name: Request domain validation using web server validation, and revalidate
- if fewer than 60 days remaining of EV eligibility.
- ecs_domain:
- domain_name: ansible.com
- verification_method: web_server
- entrust_api_user: apiusername
- entrust_api_key: a^lv*32!cd9LnT
- entrust_api_client_cert_path: /etc/ssl/entrust/ecs-client.crt
- entrust_api_client_cert_key_path: /etc/ssl/entrust/ecs-client.key
-
-- name: Request domain validation using manual validation.
- ecs_domain:
- domain_name: ansible.com
- verification_method: manual
- entrust_api_user: apiusername
- entrust_api_key: a^lv*32!cd9LnT
- entrust_api_client_cert_path: /etc/ssl/entrust/ecs-client.crt
- entrust_api_client_cert_key_path: /etc/ssl/entrust/ecs-client.key
-'''
-
-RETURN = '''
-domain_status:
- description: Status of the current domain. Will be one of C(APPROVED), C(DECLINED), C(CANCELLED), C(INITIAL_VERIFICATION), C(DECLINED), C(CANCELLED),
- C(RE_VERIFICATION), C(EXPIRED), C(EXPIRING)
- returned: changed or success
- type: str
- sample: APPROVED
-verification_method:
- description: Verification method used to request the domain validation. If C(changed) will be the same as I(verification_method) input parameter.
- returned: changed or success
- type: str
- sample: dns
-file_location:
- description: The location that ECS will be expecting to be able to find the file for domain verification, containing the contents of I(file_contents).
- returned: I(verification_method) is C(web_server)
- type: str
- sample: http://ansible.com/.well-known/pki-validation/abcd.txt
-file_contents:
- description: The contents of the file that ECS will be expecting to find at C(file_location).
- returned: I(verification_method) is C(web_server)
- type: str
- sample: AB23CD41432522FF2526920393982FAB
-emails:
- description:
- - The list of emails used to request validation of this domain.
- - Domains requested using this module will only have a list of size 1.
- returned: I(verification_method) is C(email)
- type: list
- sample: [ admin@ansible.com, administrator@ansible.com ]
-dns_location:
- description: The location that ECS will be expecting to be able to find the DNS entry for domain verification, containing the contents of I(dns_contents).
- returned: changed and if I(verification_method) is C(dns)
- type: str
- sample: _pki-validation.ansible.com
-dns_contents:
- description: The value that ECS will be expecting to find in the DNS record located at I(dns_location).
- returned: changed and if I(verification_method) is C(dns)
- type: str
- sample: AB23CD41432522FF2526920393982FAB
-dns_resource_type:
- description: The type of resource record that ECS will be expecting for the DNS record located at I(dns_location).
- returned: changed and if I(verification_method) is C(dns)
- type: str
- sample: TXT
-client_id:
- description: Client ID that the domain belongs to. If the input value I(client_id) is specified, this will always be the same as I(client_id)
- returned: changed or success
- type: int
- sample: 1
-ov_eligible:
- description: Whether the domain is eligible for submission of "OV" certificates. Will never be C(false) if I(ov_eligible) is C(true)
- returned: success and I(domain_status) is C(APPROVED), C(RE_VERIFICATION), C(EXPIRING), or C(EXPIRED).
- type: bool
- sample: true
-ov_days_remaining:
- description: The number of days the domain remains eligible for submission of "OV" certificates. Will never be less than the value of I(ev_days_remaining)
- returned: success and I(ov_eligible) is C(true) and I(domain_status) is C(APPROVED), C(RE_VERIFICATION) or C(EXPIRING).
- type: int
- sample: 129
-ev_eligible:
- description: Whether the domain is eligible for submission of "EV" certificates. Will never be C(true) if I(ov_eligible) is C(false)
- returned: success and I(domain_status) is C(APPROVED), C(RE_VERIFICATION) or C(EXPIRING), or C(EXPIRED).
- type: bool
- sample: true
-ev_days_remaining:
- description: The number of days the domain remains eligible for submission of "EV" certificates. Will never be greater than the value of
- I(ov_days_remaining)
- returned: success and I(ev_eligible) is C(true) and I(domain_status) is C(APPROVED), C(RE_VERIFICATION) or C(EXPIRING).
- type: int
- sample: 94
-
-'''
-
-from ansible.module_utils.ecs.api import (
- ecs_client_argument_spec,
- ECSClient,
- RestOperationException,
- SessionConfigurationException,
-)
-
-import datetime
-import time
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils._text import to_native
-
-
-def calculate_days_remaining(expiry_date):
- days_remaining = None
- if expiry_date:
- expiry_datetime = datetime.datetime.strptime(expiry_date, '%Y-%m-%dT%H:%M:%SZ')
- days_remaining = (expiry_datetime - datetime.datetime.now()).days
- return days_remaining
-
-
-class EcsDomain(object):
- '''
- Entrust Certificate Services domain class.
- '''
-
- def __init__(self, module):
- self.changed = False
- self.domain_status = None
- self.verification_method = None
- self.file_location = None
- self.file_contents = None
- self.dns_location = None
- self.dns_contents = None
- self.dns_resource_type = None
- self.emails = None
- self.ov_eligible = None
- self.ov_days_remaining = None
- self.ev_eligble = None
- self.ev_days_remaining = None
- # Note that verification_method is the 'current' verification
- # method of the domain, we'll use module.params when requesting a new
- # one, in case the verification method has changed.
- self.verification_method = None
-
- self.ecs_client = None
- # Instantiate the ECS client and then try a no-op connection to verify credentials are valid
- try:
- self.ecs_client = ECSClient(
- entrust_api_user=module.params['entrust_api_user'],
- entrust_api_key=module.params['entrust_api_key'],
- entrust_api_cert=module.params['entrust_api_client_cert_path'],
- entrust_api_cert_key=module.params['entrust_api_client_cert_key_path'],
- entrust_api_specification_path=module.params['entrust_api_specification_path']
- )
- except SessionConfigurationException as e:
- module.fail_json(msg='Failed to initialize Entrust Provider: {0}'.format(to_native(e)))
- try:
- self.ecs_client.GetAppVersion()
- except RestOperationException as e:
- module.fail_json(msg='Please verify credential information. Received exception when testing ECS connection: {0}'.format(to_native(e.message)))
-
- def set_domain_details(self, domain_details):
- if domain_details.get('verificationMethod'):
- self.verification_method = domain_details['verificationMethod'].lower()
- self.domain_status = domain_details['verificationStatus']
- self.ov_eligible = domain_details.get('ovEligible')
- self.ov_days_remaining = calculate_days_remaining(domain_details.get('ovExpiry'))
- self.ev_eligible = domain_details.get('evEligible')
- self.ev_days_remaining = calculate_days_remaining(domain_details.get('evExpiry'))
- self.client_id = domain_details['clientId']
-
- if self.verification_method == 'dns' and domain_details.get('dnsMethod'):
- self.dns_location = domain_details['dnsMethod']['recordDomain']
- self.dns_resource_type = domain_details['dnsMethod']['recordType']
- self.dns_contents = domain_details['dnsMethod']['recordValue']
- elif self.verification_method == 'web_server' and domain_details.get('webServerMethod'):
- self.file_location = domain_details['webServerMethod']['fileLocation']
- self.file_contents = domain_details['webServerMethod']['fileContents']
- elif self.verification_method == 'email' and domain_details.get('emailMethod'):
- self.emails = domain_details['emailMethod']
-
- def check(self, module):
- try:
- domain_details = self.ecs_client.GetDomain(clientId=module.params['client_id'], domain=module.params['domain_name'])
- self.set_domain_details(domain_details)
- if self.domain_status != 'APPROVED' and self.domain_status != 'INITIAL_VERIFICATION' and self.domain_status != 'RE_VERIFICATION':
- return False
-
- # If domain verification is in process, we want to return the random values and treat it as a valid.
- if self.domain_status == 'INITIAL_VERIFICATION' or self.domain_status == 'RE_VERIFICATION':
- # Unless the verification method has changed, in which case we need to do a reverify request.
- if self.verification_method != module.params['verification_method']:
- return False
-
- if self.domain_status == 'EXPIRING':
- return False
-
- return True
- except RestOperationException as dummy:
- return False
-
- def request_domain(self, module):
- if not self.check(module):
- body = {}
-
- body['verificationMethod'] = module.params['verification_method'].upper()
- if module.params['verification_method'] == 'email':
- emailMethod = {}
- if module.params['verification_email']:
- emailMethod['emailSource'] = 'SPECIFIED'
- emailMethod['email'] = module.params['verification_email']
- else:
- emailMethod['emailSource'] = 'INCLUDE_WHOIS'
- body['emailMethod'] = emailMethod
- # Only populate domain name in body if it is not an existing domain
- if not self.domain_status:
- body['domainName'] = module.params['domain_name']
- try:
- if not self.domain_status:
- self.ecs_client.AddDomain(clientId=module.params['client_id'], Body=body)
- else:
- self.ecs_client.ReverifyDomain(clientId=module.params['client_id'], domain=module.params['domain_name'], Body=body)
-
- time.sleep(5)
- result = self.ecs_client.GetDomain(clientId=module.params['client_id'], domain=module.params['domain_name'])
-
- # It takes a bit of time before the random values are available
- if module.params['verification_method'] == 'dns' or module.params['verification_method'] == 'web_server':
- for i in range(4):
- # Check both that random values are now available, and that they're different than were populated by previous 'check'
- if module.params['verification_method'] == 'dns':
- if result.get('dnsMethod') and result['dnsMethod']['recordValue'] != self.dns_contents:
- break
- elif module.params['verification_method'] == 'web_server':
- if result.get('webServerMethod') and result['webServerMethod']['fileContents'] != self.file_contents:
- break
- time.sleep(10)
- result = self.ecs_client.GetDomain(clientId=module.params['client_id'], domain=module.params['domain_name'])
- self.changed = True
- self.set_domain_details(result)
- except RestOperationException as e:
- module.fail_json(msg='Failed to request domain validation from Entrust (ECS) {0}'.format(e.message))
-
- def dump(self):
- result = {
- 'changed': self.changed,
- 'client_id': self.client_id,
- 'domain_status': self.domain_status,
- }
-
- if self.verification_method:
- result['verification_method'] = self.verification_method
- if self.ov_eligible is not None:
- result['ov_eligible'] = self.ov_eligible
- if self.ov_days_remaining:
- result['ov_days_remaining'] = self.ov_days_remaining
- if self.ev_eligible is not None:
- result['ev_eligible'] = self.ev_eligible
- if self.ev_days_remaining:
- result['ev_days_remaining'] = self.ev_days_remaining
- if self.emails:
- result['emails'] = self.emails
-
- if self.verification_method == 'dns':
- result['dns_location'] = self.dns_location
- result['dns_contents'] = self.dns_contents
- result['dns_resource_type'] = self.dns_resource_type
- elif self.verification_method == 'web_server':
- result['file_location'] = self.file_location
- result['file_contents'] = self.file_contents
- elif self.verification_method == 'email':
- result['emails'] = self.emails
-
- return result
-
-
-def ecs_domain_argument_spec():
- return dict(
- client_id=dict(type='int', default=1),
- domain_name=dict(type='str', required=True),
- verification_method=dict(type='str', required=True, choices=['dns', 'email', 'manual', 'web_server']),
- verification_email=dict(type='str'),
- )
-
-
-def main():
- ecs_argument_spec = ecs_client_argument_spec()
- ecs_argument_spec.update(ecs_domain_argument_spec())
- module = AnsibleModule(
- argument_spec=ecs_argument_spec,
- supports_check_mode=False,
- )
-
- if module.params['verification_email'] and module.params['verification_method'] != 'email':
- module.fail_json(msg='The verification_email field is invalid when verification_method="{0}".'.format(module.params['verification_method']))
-
- domain = EcsDomain(module)
- domain.request_domain(module)
- result = domain.dump()
- module.exit_json(**result)
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/crypto/get_certificate.py b/lib/ansible/modules/crypto/get_certificate.py
deleted file mode 100644
index 29b5c7ff01..0000000000
--- a/lib/ansible/modules/crypto/get_certificate.py
+++ /dev/null
@@ -1,371 +0,0 @@
-#!/usr/bin/python
-# coding: utf-8 -*-
-
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'community'}
-
-DOCUMENTATION = '''
----
-module: get_certificate
-author: "John Westcott IV (@john-westcott-iv)"
-version_added: "2.8"
-short_description: Get a certificate from a host:port
-description:
- - Makes a secure connection and returns information about the presented certificate
- - 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 Ansible 2.13."
-options:
- host:
- description:
- - The host to get the cert for (IP is fine)
- type: str
- required: true
- ca_cert:
- description:
- - A PEM file containing one or more root certificates; if present, the cert will be validated against these root certs.
- - Note that this only validates the certificate is signed by the chain; not that the cert is valid for the host presenting it.
- type: path
- port:
- description:
- - The port to connect to
- type: int
- required: true
- proxy_host:
- description:
- - Proxy host used when get a certificate.
- type: str
- version_added: 2.9
- proxy_port:
- description:
- - Proxy port used when get a certificate.
- type: int
- default: 8080
- version_added: 2.9
- timeout:
- description:
- - The timeout in seconds
- type: int
- default: 10
- select_crypto_backend:
- description:
- - Determines which crypto backend to use.
- - The default choice is C(auto), which tries to use C(cryptography) if available, and falls back to C(pyopenssl).
- - If set to C(pyopenssl), will try to use the L(pyOpenSSL,https://pypi.org/project/pyOpenSSL/) library.
- - If set to C(cryptography), will try to use the L(cryptography,https://cryptography.io/) library.
- type: str
- default: auto
- choices: [ auto, cryptography, pyopenssl ]
- version_added: "2.9"
-
-notes:
- - When using ca_cert on OS X it has been reported that in some conditions the validate will always succeed.
-
-requirements:
- - "python >= 2.7 when using C(proxy_host)"
- - "cryptography >= 1.6 or pyOpenSSL >= 0.15"
-'''
-
-RETURN = '''
-cert:
- description: The certificate retrieved from the port
- returned: success
- type: str
-expired:
- description: Boolean indicating if the cert is expired
- returned: success
- type: bool
-extensions:
- description: Extensions applied to the cert
- returned: success
- type: list
- elements: dict
- contains:
- critical:
- returned: success
- type: bool
- description: Whether the extension is critical.
- asn1_data:
- returned: success
- type: str
- description: The Base64 encoded ASN.1 content of the extnesion.
- name:
- returned: success
- type: str
- description: The extension's name.
-issuer:
- description: Information about the issuer of the cert
- returned: success
- type: dict
-not_after:
- description: Expiration date of the cert
- returned: success
- type: str
-not_before:
- description: Issue date of the cert
- returned: success
- type: str
-serial_number:
- description: The serial number of the cert
- returned: success
- type: str
-signature_algorithm:
- description: The algorithm used to sign the cert
- returned: success
- type: str
-subject:
- description: Information about the subject of the cert (OU, CN, etc)
- returned: success
- type: dict
-version:
- description: The version number of the certificate
- returned: success
- type: str
-'''
-
-EXAMPLES = '''
-- name: Get the cert from an RDP port
- get_certificate:
- host: "1.2.3.4"
- port: 3389
- delegate_to: localhost
- run_once: true
- register: cert
-
-- name: Get a cert from an https port
- get_certificate:
- host: "www.google.com"
- port: 443
- delegate_to: localhost
- run_once: true
- register: cert
-
-- name: How many days until cert expires
- debug:
- msg: "cert expires in: {{ expire_days }} days."
- vars:
- expire_days: "{{ (( cert.not_after | to_datetime('%Y%m%d%H%M%SZ')) - (ansible_date_time.iso8601 | to_datetime('%Y-%m-%dT%H:%M:%SZ')) ).days }}"
-'''
-
-from ansible.module_utils.basic import AnsibleModule, missing_required_lib
-from ansible.module_utils._text import to_bytes
-from ansible.module_utils import crypto as crypto_utils
-
-from distutils.version import LooseVersion
-from os.path import isfile
-from socket import setdefaulttimeout, socket
-from ssl import get_server_certificate, DER_cert_to_PEM_cert, CERT_NONE, CERT_OPTIONAL
-
-import atexit
-import base64
-import datetime
-import traceback
-
-MINIMAL_PYOPENSSL_VERSION = '0.15'
-MINIMAL_CRYPTOGRAPHY_VERSION = '1.6'
-
-CREATE_DEFAULT_CONTEXT_IMP_ERR = None
-try:
- from ssl import create_default_context
-except ImportError:
- CREATE_DEFAULT_CONTEXT_IMP_ERR = traceback.format_exc()
- HAS_CREATE_DEFAULT_CONTEXT = False
-else:
- 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
-try:
- import cryptography
- import cryptography.exceptions
- import cryptography.x509
- from cryptography.hazmat.backends import default_backend as cryptography_backend
- CRYPTOGRAPHY_VERSION = LooseVersion(cryptography.__version__)
-except ImportError:
- CRYPTOGRAPHY_IMP_ERR = traceback.format_exc()
- CRYPTOGRAPHY_FOUND = False
-else:
- CRYPTOGRAPHY_FOUND = True
-
-
-def main():
- module = AnsibleModule(
- argument_spec=dict(
- ca_cert=dict(type='path'),
- host=dict(type='str', required=True),
- port=dict(type='int', required=True),
- proxy_host=dict(type='str'),
- proxy_port=dict(type='int', default=8080),
- timeout=dict(type='int', default=10),
- select_crypto_backend=dict(type='str', choices=['auto', 'pyopenssl', 'cryptography'], default='auto'),
- ),
- )
-
- ca_cert = module.params.get('ca_cert')
- host = module.params.get('host')
- port = module.params.get('port')
- proxy_host = module.params.get('proxy_host')
- proxy_port = module.params.get('proxy_port')
- timeout = module.params.get('timeout')
-
- backend = module.params.get('select_crypto_backend')
- if backend == 'auto':
- # Detection what is possible
- 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
- if can_use_cryptography:
- backend = 'cryptography'
- elif can_use_pyopenssl:
- backend = 'pyopenssl'
-
- # Success?
- if backend == 'auto':
- module.fail_json(msg=("Can't detect any of the required Python libraries "
- "cryptography (>= {0}) or PyOpenSSL (>= {1})").format(
- MINIMAL_CRYPTOGRAPHY_VERSION,
- MINIMAL_PYOPENSSL_VERSION))
-
- if backend == 'pyopenssl':
- if not PYOPENSSL_FOUND:
- module.fail_json(msg=missing_required_lib('pyOpenSSL >= {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.13')
- elif backend == 'cryptography':
- if not CRYPTOGRAPHY_FOUND:
- module.fail_json(msg=missing_required_lib('cryptography >= {0}'.format(MINIMAL_CRYPTOGRAPHY_VERSION)),
- exception=CRYPTOGRAPHY_IMP_ERR)
-
- result = dict(
- changed=False,
- )
-
- if timeout:
- setdefaulttimeout(timeout)
-
- if ca_cert:
- if not isfile(ca_cert):
- module.fail_json(msg="ca_cert file does not exist")
-
- if proxy_host:
- if not HAS_CREATE_DEFAULT_CONTEXT:
- module.fail_json(msg='To use proxy_host, you must run the get_certificate module with Python 2.7 or newer.',
- exception=CREATE_DEFAULT_CONTEXT_IMP_ERR)
-
- try:
- connect = "CONNECT %s:%s HTTP/1.0\r\n\r\n" % (host, port)
- sock = socket()
- atexit.register(sock.close)
- sock.connect((proxy_host, proxy_port))
- sock.send(connect.encode())
- sock.recv(8192)
-
- ctx = create_default_context()
- ctx.check_hostname = False
- ctx.verify_mode = CERT_NONE
-
- if ca_cert:
- ctx.verify_mode = CERT_OPTIONAL
- ctx.load_verify_locations(cafile=ca_cert)
-
- cert = ctx.wrap_socket(sock, server_hostname=host).getpeercert(True)
- cert = DER_cert_to_PEM_cert(cert)
- except Exception as e:
- module.fail_json(msg="Failed to get cert from port with error: {0}".format(e))
-
- else:
- try:
- cert = get_server_certificate((host, port), ca_certs=ca_cert)
- except Exception as e:
- module.fail_json(msg="Failed to get cert from port with error: {0}".format(e))
-
- result['cert'] = cert
-
- if backend == 'pyopenssl':
- 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())
- result['subject'] = {}
- for attribute in x509.subject:
- result['subject'][crypto_utils.cryptography_oid_to_name(attribute.oid, short=True)] = attribute.value
-
- result['expired'] = x509.not_valid_after < datetime.datetime.utcnow()
-
- result['extensions'] = []
- for dotted_number, entry in crypto_utils.cryptography_get_extensions_from_cert(x509).items():
- oid = cryptography.x509.oid.ObjectIdentifier(dotted_number)
- result['extensions'].append({
- 'critical': entry['critical'],
- 'asn1_data': base64.b64decode(entry['value']),
- 'name': crypto_utils.cryptography_oid_to_name(oid, short=True),
- })
-
- result['issuer'] = {}
- for attribute in x509.issuer:
- result['issuer'][crypto_utils.cryptography_oid_to_name(attribute.oid, short=True)] = attribute.value
-
- result['not_after'] = x509.not_valid_after.strftime('%Y%m%d%H%M%SZ')
- result['not_before'] = x509.not_valid_before.strftime('%Y%m%d%H%M%SZ')
-
- result['serial_number'] = x509.serial_number
- result['signature_algorithm'] = crypto_utils.cryptography_oid_to_name(x509.signature_algorithm_oid)
-
- # We need the -1 offset to get the same values as pyOpenSSL
- if x509.version == cryptography.x509.Version.v1:
- result['version'] = 1 - 1
- elif x509.version == cryptography.x509.Version.v3:
- result['version'] = 3 - 1
- else:
- result['version'] = "unknown"
-
- module.exit_json(**result)
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/crypto/luks_device.py b/lib/ansible/modules/crypto/luks_device.py
deleted file mode 100644
index 3b895db757..0000000000
--- a/lib/ansible/modules/crypto/luks_device.py
+++ /dev/null
@@ -1,794 +0,0 @@
-#!/usr/bin/python
-# Copyright (c) 2017 Ansible Project
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'community'}
-
-DOCUMENTATION = '''
----
-module: luks_device
-
-short_description: Manage encrypted (LUKS) devices
-
-version_added: "2.8"
-
-description:
- - "Module manages L(LUKS,https://en.wikipedia.org/wiki/Linux_Unified_Key_Setup)
- on given device. Supports creating, destroying, opening and closing of
- LUKS container and adding or removing new keys and passphrases."
-
-options:
- device:
- description:
- - "Device to work with (e.g. C(/dev/sda1)). Needed in most cases.
- Can be omitted only when I(state=closed) together with I(name)
- is provided."
- type: str
- state:
- description:
- - "Desired state of the LUKS container. Based on its value creates,
- destroys, opens or closes the LUKS container on a given device."
- - "I(present) will create LUKS container unless already present.
- Requires I(device) and either I(keyfile) or I(passphrase) options
- to be provided."
- - "I(absent) will remove existing LUKS container if it exists.
- Requires I(device) or I(name) to be specified."
- - "I(opened) will unlock the LUKS container. If it does not exist
- it will be created first.
- Requires I(device) and either I(keyfile) or I(passphrase)
- to be specified. Use the I(name) option to set the name of
- the opened container. Otherwise the name will be
- generated automatically and returned as a part of the
- result."
- - "I(closed) will lock the LUKS container. However if the container
- does not exist it will be created.
- Requires I(device) and either I(keyfile) or I(passphrase)
- options to be provided. If container does already exist
- I(device) or I(name) will suffice."
- type: str
- default: present
- choices: [present, absent, opened, closed]
- name:
- description:
- - "Sets container name when I(state=opened). Can be used
- instead of I(device) when closing the existing container
- (i.e. when I(state=closed))."
- type: str
- keyfile:
- description:
- - "Used to unlock the container. Either a I(keyfile) or a
- I(passphrase) is needed for most of the operations. Parameter
- value is the path to the keyfile with the passphrase."
- - "BEWARE that working with keyfiles in plaintext is dangerous.
- Make sure that they are protected."
- type: path
- passphrase:
- description:
- - "Used to unlock the container. Either a I(passphrase) or a
- I(keyfile) is needed for most of the operations. Parameter
- value is a string with the passphrase."
- type: str
- version_added: '2.10'
- keysize:
- description:
- - "Sets the key size only if LUKS container does not exist."
- type: int
- version_added: '2.10'
- new_keyfile:
- description:
- - "Adds additional key to given container on I(device).
- Needs I(keyfile) or I(passphrase) option for authorization.
- LUKS container supports up to 8 keyslots. Parameter value
- is the path to the keyfile with the passphrase."
- - "NOTE that adding additional keys is *not idempotent*.
- A new keyslot will be used even if another keyslot already
- exists for this keyfile."
- - "BEWARE that working with keyfiles in plaintext is dangerous.
- Make sure that they are protected."
- type: path
- new_passphrase:
- description:
- - "Adds additional passphrase to given container on I(device).
- Needs I(keyfile) or I(passphrase) option for authorization. LUKS
- container supports up to 8 keyslots. Parameter value is a string
- with the new passphrase."
- - "NOTE that adding additional passphrase is *not idempotent*. A
- new keyslot will be used even if another keyslot already exists
- for this passphrase."
- type: str
- version_added: '2.10'
- remove_keyfile:
- description:
- - "Removes given key from the container on I(device). Does not
- remove the keyfile from filesystem.
- Parameter value is the path to the keyfile with the passphrase."
- - "NOTE that removing keys is *not idempotent*. Trying to remove
- a key which no longer exists results in an error."
- - "NOTE that to remove the last key from a LUKS container, the
- I(force_remove_last_key) option must be set to C(yes)."
- - "BEWARE that working with keyfiles in plaintext is dangerous.
- Make sure that they are protected."
- type: path
- remove_passphrase:
- description:
- - "Removes given passphrase from the container on I(device).
- Parameter value is a string with the passphrase to remove."
- - "NOTE that removing passphrases is I(not
- idempotent). Trying to remove a passphrase which no longer
- exists results in an error."
- - "NOTE that to remove the last keyslot from a LUKS
- container, the I(force_remove_last_key) option must be set
- to C(yes)."
- type: str
- version_added: '2.10'
- force_remove_last_key:
- description:
- - "If set to C(yes), allows removing the last key from a container."
- - "BEWARE that when the last key has been removed from a container,
- the container can no longer be opened!"
- type: bool
- default: no
- label:
- description:
- - "This option allow the user to create a LUKS2 format container
- with label support, respectively to identify the container by
- label on later usages."
- - "Will only be used on container creation, or when I(device) is
- not specified."
- - "This cannot be specified if I(type) is set to C(luks1)."
- type: str
- version_added: "2.10"
- uuid:
- description:
- - "With this option user can identify the LUKS container by UUID."
- - "Will only be used when I(device) and I(label) are not specified."
- type: str
- version_added: "2.10"
- type:
- description:
- - "This option allow the user explicit define the format of LUKS
- container that wants to work with. Options are C(luks1) or C(luks2)"
- type: str
- choices: [luks1, luks2]
- version_added: "2.10"
-
-
-
-requirements:
- - "cryptsetup"
- - "wipefs (when I(state) is C(absent))"
- - "lsblk"
- - "blkid (when I(label) or I(uuid) options are used)"
-
-author: Jan Pokorny (@japokorn)
-'''
-
-EXAMPLES = '''
-
-- name: create LUKS container (remains unchanged if it already exists)
- luks_device:
- device: "/dev/loop0"
- state: "present"
- keyfile: "/vault/keyfile"
-
-- name: create LUKS container with a passphrase
- luks_device:
- device: "/dev/loop0"
- state: "present"
- passphrase: "foo"
-
-- name: (create and) open the LUKS container; name it "mycrypt"
- luks_device:
- device: "/dev/loop0"
- state: "opened"
- name: "mycrypt"
- keyfile: "/vault/keyfile"
-
-- name: close the existing LUKS container "mycrypt"
- luks_device:
- state: "closed"
- name: "mycrypt"
-
-- name: make sure LUKS container exists and is closed
- luks_device:
- device: "/dev/loop0"
- state: "closed"
- keyfile: "/vault/keyfile"
-
-- name: create container if it does not exist and add new key to it
- luks_device:
- device: "/dev/loop0"
- state: "present"
- keyfile: "/vault/keyfile"
- new_keyfile: "/vault/keyfile2"
-
-- name: add new key to the LUKS container (container has to exist)
- luks_device:
- device: "/dev/loop0"
- keyfile: "/vault/keyfile"
- new_keyfile: "/vault/keyfile2"
-
-- name: add new passphrase to the LUKS container
- luks_device:
- device: "/dev/loop0"
- keyfile: "/vault/keyfile"
- new_passphrase: "foo"
-
-- name: remove existing keyfile from the LUKS container
- luks_device:
- device: "/dev/loop0"
- remove_keyfile: "/vault/keyfile2"
-
-- name: remove existing passphrase from the LUKS container
- luks_device:
- device: "/dev/loop0"
- remove_passphrase: "foo"
-
-- name: completely remove the LUKS container and its contents
- luks_device:
- device: "/dev/loop0"
- state: "absent"
-
-- name: create a container with label
- luks_device:
- device: "/dev/loop0"
- state: "present"
- keyfile: "/vault/keyfile"
- label: personalLabelName
-
-- name: open the LUKS container based on label without device; name it "mycrypt"
- luks_device:
- label: "personalLabelName"
- state: "opened"
- name: "mycrypt"
- keyfile: "/vault/keyfile"
-
-- name: close container based on UUID
- luks_device:
- uuid: 03ecd578-fad4-4e6c-9348-842e3e8fa340
- state: "closed"
- name: "mycrypt"
-
-- name: create a container using luks2 format
- luks_device:
- device: "/dev/loop0"
- state: "present"
- keyfile: "/vault/keyfile"
- type: luks2
-'''
-
-RETURN = '''
-name:
- description:
- When I(state=opened) returns (generated or given) name
- of LUKS container. Returns None if no name is supplied.
- returned: success
- type: str
- sample: "luks-c1da9a58-2fde-4256-9d9f-6ab008b4dd1b"
-'''
-
-import os
-import re
-import stat
-
-from ansible.module_utils.basic import AnsibleModule
-
-RETURN_CODE = 0
-STDOUT = 1
-STDERR = 2
-
-# used to get <luks-name> out of lsblk output in format 'crypt <luks-name>'
-# regex takes care of any possible blank characters
-LUKS_NAME_REGEX = re.compile(r'\s*crypt\s+([^\s]*)\s*')
-# used to get </luks/device> out of lsblk output
-# in format 'device: </luks/device>'
-LUKS_DEVICE_REGEX = re.compile(r'\s*device:\s+([^\s]*)\s*')
-
-
-class Handler(object):
-
- def __init__(self, module):
- self._module = module
- self._lsblk_bin = self._module.get_bin_path('lsblk', True)
-
- def _run_command(self, command, data=None):
- return self._module.run_command(command, data=data)
-
- def get_device_by_uuid(self, uuid):
- ''' Returns the device that holds UUID passed by user
- '''
- self._blkid_bin = self._module.get_bin_path('blkid', True)
- uuid = self._module.params['uuid']
- if uuid is None:
- return None
- result = self._run_command([self._blkid_bin, '--uuid', uuid])
- if result[RETURN_CODE] != 0:
- return None
- return result[STDOUT].strip()
-
- def get_device_by_label(self, label):
- ''' Returns the device that holds label passed by user
- '''
- self._blkid_bin = self._module.get_bin_path('blkid', True)
- label = self._module.params['label']
- if label is None:
- return None
- result = self._run_command([self._blkid_bin, '--label', label])
- if result[RETURN_CODE] != 0:
- return None
- return result[STDOUT].strip()
-
- def generate_luks_name(self, device):
- ''' Generate name for luks based on device UUID ('luks-<UUID>').
- Raises ValueError when obtaining of UUID fails.
- '''
- result = self._run_command([self._lsblk_bin, '-n', device, '-o', 'UUID'])
-
- if result[RETURN_CODE] != 0:
- raise ValueError('Error while generating LUKS name for %s: %s'
- % (device, result[STDERR]))
- dev_uuid = result[STDOUT].strip()
- return 'luks-%s' % dev_uuid
-
-
-class CryptHandler(Handler):
-
- def __init__(self, module):
- super(CryptHandler, self).__init__(module)
- self._cryptsetup_bin = self._module.get_bin_path('cryptsetup', True)
-
- def get_container_name_by_device(self, device):
- ''' obtain LUKS container name based on the device where it is located
- return None if not found
- raise ValueError if lsblk command fails
- '''
- result = self._run_command([self._lsblk_bin, device, '-nlo', 'type,name'])
- if result[RETURN_CODE] != 0:
- raise ValueError('Error while obtaining LUKS name for %s: %s'
- % (device, result[STDERR]))
-
- m = LUKS_NAME_REGEX.search(result[STDOUT])
-
- try:
- name = m.group(1)
- except AttributeError:
- name = None
- return name
-
- def get_container_device_by_name(self, name):
- ''' obtain device name based on the LUKS container name
- return None if not found
- raise ValueError if lsblk command fails
- '''
- # apparently each device can have only one LUKS container on it
- result = self._run_command([self._cryptsetup_bin, 'status', name])
- if result[RETURN_CODE] != 0:
- return None
-
- m = LUKS_DEVICE_REGEX.search(result[STDOUT])
- device = m.group(1)
- return device
-
- def is_luks(self, device):
- ''' check if the LUKS container does exist
- '''
- result = self._run_command([self._cryptsetup_bin, 'isLuks', device])
- return result[RETURN_CODE] == 0
-
- def run_luks_create(self, device, keyfile, passphrase, keysize):
- # create a new luks container; use batch mode to auto confirm
- luks_type = self._module.params['type']
- label = self._module.params['label']
-
- options = []
- if keysize is not None:
- options.append('--key-size=' + str(keysize))
- if label is not None:
- options.extend(['--label', label])
- luks_type = 'luks2'
- if luks_type is not None:
- options.extend(['--type', luks_type])
-
- args = [self._cryptsetup_bin, 'luksFormat']
- args.extend(options)
- args.extend(['-q', device])
- if keyfile:
- args.append(keyfile)
-
- result = self._run_command(args, data=passphrase)
- if result[RETURN_CODE] != 0:
- raise ValueError('Error while creating LUKS on %s: %s'
- % (device, result[STDERR]))
-
- def run_luks_open(self, device, keyfile, passphrase, name):
- args = [self._cryptsetup_bin]
- if keyfile:
- args.extend(['--key-file', keyfile])
- args.extend(['open', '--type', 'luks', device, name])
-
- result = self._run_command(args, data=passphrase)
- if result[RETURN_CODE] != 0:
- raise ValueError('Error while opening LUKS container on %s: %s'
- % (device, result[STDERR]))
-
- def run_luks_close(self, name):
- result = self._run_command([self._cryptsetup_bin, 'close', name])
- if result[RETURN_CODE] != 0:
- raise ValueError('Error while closing LUKS container %s' % (name))
-
- def run_luks_remove(self, device):
- wipefs_bin = self._module.get_bin_path('wipefs', True)
-
- name = self.get_container_name_by_device(device)
- if name is not None:
- self.run_luks_close(name)
- result = self._run_command([wipefs_bin, '--all', device])
- if result[RETURN_CODE] != 0:
- raise ValueError('Error while wiping luks container %s: %s'
- % (device, result[STDERR]))
-
- def run_luks_add_key(self, device, keyfile, passphrase, new_keyfile,
- new_passphrase):
- ''' Add new key from a keyfile or passphrase to given 'device';
- authentication done using 'keyfile' or 'passphrase'.
- Raises ValueError when command fails.
- '''
- data = []
- args = [self._cryptsetup_bin, 'luksAddKey', device]
-
- if keyfile:
- args.extend(['--key-file', keyfile])
- else:
- data.append(passphrase)
-
- if new_keyfile:
- args.append(new_keyfile)
- else:
- data.extend([new_passphrase, new_passphrase])
-
- result = self._run_command(args, data='\n'.join(data) or None)
- if result[RETURN_CODE] != 0:
- raise ValueError('Error while adding new LUKS keyslot to %s: %s'
- % (device, result[STDERR]))
-
- def run_luks_remove_key(self, device, keyfile, passphrase,
- force_remove_last_key=False):
- ''' Remove key from given device
- Raises ValueError when command fails
- '''
- if not force_remove_last_key:
- result = self._run_command([self._cryptsetup_bin, 'luksDump', device])
- if result[RETURN_CODE] != 0:
- raise ValueError('Error while dumping LUKS header from %s'
- % (device, ))
- keyslot_count = 0
- keyslot_area = False
- keyslot_re = re.compile(r'^Key Slot [0-9]+: ENABLED')
- for line in result[STDOUT].splitlines():
- if line.startswith('Keyslots:'):
- keyslot_area = True
- elif line.startswith(' '):
- # LUKS2 header dumps use human-readable indented output.
- # Thus we have to look out for 'Keyslots:' and count the
- # number of indented keyslot numbers.
- if keyslot_area and line[2] in '0123456789':
- keyslot_count += 1
- elif line.startswith('\t'):
- pass
- elif keyslot_re.match(line):
- # LUKS1 header dumps have one line per keyslot with ENABLED
- # or DISABLED in them. We count such lines with ENABLED.
- keyslot_count += 1
- else:
- keyslot_area = False
- if keyslot_count < 2:
- self._module.fail_json(msg="LUKS device %s has less than two active keyslots. "
- "To be able to remove a key, please set "
- "`force_remove_last_key` to `yes`." % device)
-
- args = [self._cryptsetup_bin, 'luksRemoveKey', device, '-q']
- if keyfile:
- args.extend(['--key-file', keyfile])
- result = self._run_command(args, data=passphrase)
- if result[RETURN_CODE] != 0:
- raise ValueError('Error while removing LUKS key from %s: %s'
- % (device, result[STDERR]))
-
-
-class ConditionsHandler(Handler):
-
- def __init__(self, module, crypthandler):
- super(ConditionsHandler, self).__init__(module)
- self._crypthandler = crypthandler
- self.device = self.get_device_name()
-
- def get_device_name(self):
- device = self._module.params.get('device')
- label = self._module.params.get('label')
- uuid = self._module.params.get('uuid')
- name = self._module.params.get('name')
-
- if device is None and label is not None:
- device = self.get_device_by_label(label)
- elif device is None and uuid is not None:
- device = self.get_device_by_uuid(uuid)
- elif device is None and name is not None:
- device = self._crypthandler.get_container_device_by_name(name)
-
- return device
-
- def luks_create(self):
- return (self.device is not None and
- (self._module.params['keyfile'] is not None or
- self._module.params['passphrase'] is not None) and
- self._module.params['state'] in ('present',
- 'opened',
- 'closed') and
- not self._crypthandler.is_luks(self.device))
-
- def opened_luks_name(self):
- ''' If luks is already opened, return its name.
- If 'name' parameter is specified and differs
- from obtained value, fail.
- Return None otherwise
- '''
- if self._module.params['state'] != 'opened':
- return None
-
- # try to obtain luks name - it may be already opened
- name = self._crypthandler.get_container_name_by_device(self.device)
-
- if name is None:
- # container is not open
- return None
-
- if self._module.params['name'] is None:
- # container is already opened
- return name
-
- if name != self._module.params['name']:
- # the container is already open but with different name:
- # suspicious. back off
- self._module.fail_json(msg="LUKS container is already opened "
- "under different name '%s'." % name)
-
- # container is opened and the names match
- return name
-
- def luks_open(self):
- if ((self._module.params['keyfile'] is None and
- self._module.params['passphrase'] is None) or
- self.device is None or
- self._module.params['state'] != 'opened'):
- # conditions for open not fulfilled
- return False
-
- name = self.opened_luks_name()
-
- if name is None:
- return True
- return False
-
- def luks_close(self):
- if ((self._module.params['name'] is None and self.device is None) or
- self._module.params['state'] != 'closed'):
- # conditions for close not fulfilled
- return False
-
- if self.device is not None:
- name = self._crypthandler.get_container_name_by_device(self.device)
- # successfully getting name based on device means that luks is open
- luks_is_open = name is not None
-
- if self._module.params['name'] is not None:
- self.device = self._crypthandler.get_container_device_by_name(
- self._module.params['name'])
- # successfully getting device based on name means that luks is open
- luks_is_open = self.device is not None
-
- return luks_is_open
-
- def luks_add_key(self):
- if (self.device is None or
- (self._module.params['keyfile'] is None and
- self._module.params['passphrase'] is None) or
- (self._module.params['new_keyfile'] is None and
- self._module.params['new_passphrase'] is None)):
- # conditions for adding a key not fulfilled
- return False
-
- if self._module.params['state'] == 'absent':
- self._module.fail_json(msg="Contradiction in setup: Asking to "
- "add a key to absent LUKS.")
-
- return True
-
- def luks_remove_key(self):
- if (self.device is None or
- (self._module.params['remove_keyfile'] is None and
- self._module.params['remove_passphrase'] is None)):
- # conditions for removing a key not fulfilled
- return False
-
- if self._module.params['state'] == 'absent':
- self._module.fail_json(msg="Contradiction in setup: Asking to "
- "remove a key from absent LUKS.")
-
- return True
-
- def luks_remove(self):
- return (self.device is not None and
- self._module.params['state'] == 'absent' and
- self._crypthandler.is_luks(self.device))
-
-
-def run_module():
- # available arguments/parameters that a user can pass
- module_args = dict(
- state=dict(type='str', default='present', choices=['present', 'absent', 'opened', 'closed']),
- device=dict(type='str'),
- name=dict(type='str'),
- keyfile=dict(type='path'),
- new_keyfile=dict(type='path'),
- remove_keyfile=dict(type='path'),
- passphrase=dict(type='str', no_log=True),
- new_passphrase=dict(type='str', no_log=True),
- remove_passphrase=dict(type='str', no_log=True),
- force_remove_last_key=dict(type='bool', default=False),
- keysize=dict(type='int'),
- label=dict(type='str'),
- uuid=dict(type='str'),
- type=dict(type='str', choices=['luks1', 'luks2']),
- )
-
- mutually_exclusive = [
- ('keyfile', 'passphrase'),
- ('new_keyfile', 'new_passphrase'),
- ('remove_keyfile', 'remove_passphrase')
- ]
-
- # seed the result dict in the object
- result = dict(
- changed=False,
- name=None
- )
-
- module = AnsibleModule(argument_spec=module_args,
- supports_check_mode=True,
- mutually_exclusive=mutually_exclusive)
-
- if module.params['device'] is not None:
- try:
- statinfo = os.stat(module.params['device'])
- mode = statinfo.st_mode
- if not stat.S_ISBLK(mode) and not stat.S_ISCHR(mode):
- raise Exception('{0} is not a device'.format(module.params['device']))
- except Exception as e:
- module.fail_json(msg=str(e))
-
- crypt = CryptHandler(module)
- conditions = ConditionsHandler(module, crypt)
-
- # conditions not allowed to run
- if module.params['label'] is not None and module.params['type'] == 'luks1':
- module.fail_json(msg='You cannot combine type luks1 with the label option.')
-
- # The conditions are in order to allow more operations in one run.
- # (e.g. create luks and add a key to it)
-
- # luks create
- if conditions.luks_create():
- if not module.check_mode:
- try:
- crypt.run_luks_create(conditions.device,
- module.params['keyfile'],
- module.params['passphrase'],
- module.params['keysize'])
- except ValueError as e:
- module.fail_json(msg="luks_device error: %s" % e)
- result['changed'] = True
- if module.check_mode:
- module.exit_json(**result)
-
- # luks open
-
- name = conditions.opened_luks_name()
- if name is not None:
- result['name'] = name
-
- if conditions.luks_open():
- name = module.params['name']
- if name is None:
- try:
- name = crypt.generate_luks_name(conditions.device)
- except ValueError as e:
- module.fail_json(msg="luks_device error: %s" % e)
- if not module.check_mode:
- try:
- crypt.run_luks_open(conditions.device,
- module.params['keyfile'],
- module.params['passphrase'],
- name)
- except ValueError as e:
- module.fail_json(msg="luks_device error: %s" % e)
- result['name'] = name
- result['changed'] = True
- if module.check_mode:
- module.exit_json(**result)
-
- # luks close
- if conditions.luks_close():
- if conditions.device is not None:
- try:
- name = crypt.get_container_name_by_device(
- conditions.device)
- except ValueError as e:
- module.fail_json(msg="luks_device error: %s" % e)
- else:
- name = module.params['name']
- if not module.check_mode:
- try:
- crypt.run_luks_close(name)
- except ValueError as e:
- module.fail_json(msg="luks_device error: %s" % e)
- result['name'] = name
- result['changed'] = True
- if module.check_mode:
- module.exit_json(**result)
-
- # luks add key
- if conditions.luks_add_key():
- if not module.check_mode:
- try:
- crypt.run_luks_add_key(conditions.device,
- module.params['keyfile'],
- module.params['passphrase'],
- module.params['new_keyfile'],
- module.params['new_passphrase'])
- except ValueError as e:
- module.fail_json(msg="luks_device error: %s" % e)
- result['changed'] = True
- if module.check_mode:
- module.exit_json(**result)
-
- # luks remove key
- if conditions.luks_remove_key():
- if not module.check_mode:
- try:
- last_key = module.params['force_remove_last_key']
- crypt.run_luks_remove_key(conditions.device,
- module.params['remove_keyfile'],
- module.params['remove_passphrase'],
- force_remove_last_key=last_key)
- except ValueError as e:
- module.fail_json(msg="luks_device error: %s" % e)
- result['changed'] = True
- if module.check_mode:
- module.exit_json(**result)
-
- # luks remove
- if conditions.luks_remove():
- if not module.check_mode:
- try:
- crypt.run_luks_remove(conditions.device)
- except ValueError as e:
- module.fail_json(msg="luks_device error: %s" % e)
- result['changed'] = True
- if module.check_mode:
- module.exit_json(**result)
-
- # Success - return result
- module.exit_json(**result)
-
-
-def main():
- run_module()
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/crypto/openssh_cert.py b/lib/ansible/modules/crypto/openssh_cert.py
deleted file mode 100644
index e22e27afa2..0000000000
--- a/lib/ansible/modules/crypto/openssh_cert.py
+++ /dev/null
@@ -1,590 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-
-# (c) 2018, David Kainz <dkainz@mgit.at> <dave.jokain@gmx.at>
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-
-ANSIBLE_METADATA = {
- 'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'community'
-}
-
-DOCUMENTATION = '''
----
-module: openssh_cert
-author: "David Kainz (@lolcube)"
-version_added: "2.8"
-short_description: Generate OpenSSH host or user certificates.
-description:
- - Generate and regenerate OpenSSH host or user certificates.
-requirements:
- - "ssh-keygen"
-options:
- state:
- description:
- - Whether the host or user certificate should exist or not, taking action if the state is different from what is stated.
- type: str
- default: "present"
- choices: [ 'present', 'absent' ]
- type:
- description:
- - Whether the module should generate a host or a user certificate.
- - Required if I(state) is C(present).
- type: str
- choices: ['host', 'user']
- force:
- description:
- - Should the certificate be regenerated even if it already exists and is valid.
- type: bool
- default: false
- path:
- description:
- - Path of the file containing the certificate.
- type: path
- required: true
- signing_key:
- description:
- - The path to the private openssh key that is used for signing the public key in order to generate the certificate.
- - Required if I(state) is C(present).
- type: path
- public_key:
- description:
- - The path to the public key that will be signed with the signing key in order to generate the certificate.
- - Required if I(state) is C(present).
- type: path
- valid_from:
- description:
- - "The point in time the certificate is valid from. Time can be specified either as relative time or as absolute timestamp.
- Time will always be interpreted as UTC. Valid formats are: C([+-]timespec | YYYY-MM-DD | YYYY-MM-DDTHH:MM:SS | YYYY-MM-DD HH:MM:SS | always)
- where timespec can be an integer + C([w | d | h | m | s]) (e.g. C(+32w1d2h).
- Note that if using relative time this module is NOT idempotent."
- - Required if I(state) is C(present).
- type: str
- valid_to:
- description:
- - "The point in time the certificate is valid to. Time can be specified either as relative time or as absolute timestamp.
- Time will always be interpreted as UTC. Valid formats are: C([+-]timespec | YYYY-MM-DD | YYYY-MM-DDTHH:MM:SS | YYYY-MM-DD HH:MM:SS | forever)
- where timespec can be an integer + C([w | d | h | m | s]) (e.g. C(+32w1d2h).
- Note that if using relative time this module is NOT idempotent."
- - Required if I(state) is C(present).
- type: str
- valid_at:
- description:
- - "Check if the certificate is valid at a certain point in time. If it is not the certificate will be regenerated.
- Time will always be interpreted as UTC. Mainly to be used with relative timespec for I(valid_from) and / or I(valid_to).
- Note that if using relative time this module is NOT idempotent."
- type: str
- principals:
- description:
- - "Certificates may be limited to be valid for a set of principal (user/host) names.
- By default, generated certificates are valid for all users or hosts."
- type: list
- elements: str
- options:
- description:
- - "Specify certificate options when signing a key. The option that are valid for user certificates are:"
- - "C(clear): Clear all enabled permissions. This is useful for clearing the default set of permissions so permissions may be added individually."
- - "C(force-command=command): Forces the execution of command instead of any shell or
- command specified by the user when the certificate is used for authentication."
- - "C(no-agent-forwarding): Disable ssh-agent forwarding (permitted by default)."
- - "C(no-port-forwarding): Disable port forwarding (permitted by default)."
- - "C(no-pty Disable): PTY allocation (permitted by default)."
- - "C(no-user-rc): Disable execution of C(~/.ssh/rc) by sshd (permitted by default)."
- - "C(no-x11-forwarding): Disable X11 forwarding (permitted by default)"
- - "C(permit-agent-forwarding): Allows ssh-agent forwarding."
- - "C(permit-port-forwarding): Allows port forwarding."
- - "C(permit-pty): Allows PTY allocation."
- - "C(permit-user-rc): Allows execution of C(~/.ssh/rc) by sshd."
- - "C(permit-x11-forwarding): Allows X11 forwarding."
- - "C(source-address=address_list): Restrict the source addresses from which the certificate is considered valid.
- The C(address_list) is a comma-separated list of one or more address/netmask pairs in CIDR format."
- - "At present, no options are valid for host keys."
- type: list
- elements: str
- identifier:
- description:
- - Specify the key identity when signing a public key. The identifier that is logged by the server when the certificate is used for authentication.
- type: str
- serial_number:
- description:
- - "Specify the certificate serial number.
- The serial number is logged by the server when the certificate is used for authentication.
- The certificate serial number may be used in a KeyRevocationList.
- The serial number may be omitted for checks, but must be specified again for a new certificate.
- Note: The default value set by ssh-keygen is 0."
- type: int
-
-extends_documentation_fragment: files
-'''
-
-EXAMPLES = '''
-# Generate an OpenSSH user certificate that is valid forever and for all users
-- openssh_cert:
- type: user
- signing_key: /path/to/private_key
- public_key: /path/to/public_key.pub
- path: /path/to/certificate
- valid_from: always
- valid_to: forever
-
-# Generate an OpenSSH host certificate that is valid for 32 weeks from now and will be regenerated
-# if it is valid for less than 2 weeks from the time the module is being run
-- openssh_cert:
- type: host
- signing_key: /path/to/private_key
- public_key: /path/to/public_key.pub
- path: /path/to/certificate
- valid_from: +0s
- valid_to: +32w
- valid_at: +2w
-
-# Generate an OpenSSH host certificate that is valid forever and only for example.com and examplehost
-- openssh_cert:
- type: host
- signing_key: /path/to/private_key
- public_key: /path/to/public_key.pub
- path: /path/to/certificate
- valid_from: always
- valid_to: forever
- principals:
- - example.com
- - examplehost
-
-# Generate an OpenSSH host Certificate that is valid from 21.1.2001 to 21.1.2019
-- openssh_cert:
- type: host
- signing_key: /path/to/private_key
- public_key: /path/to/public_key.pub
- path: /path/to/certificate
- valid_from: "2001-01-21"
- valid_to: "2019-01-21"
-
-# Generate an OpenSSH user Certificate with clear and force-command option:
-- openssh_cert:
- type: user
- signing_key: /path/to/private_key
- public_key: /path/to/public_key.pub
- path: /path/to/certificate
- valid_from: always
- valid_to: forever
- options:
- - "clear"
- - "force-command=/tmp/bla/foo"
-
-'''
-
-RETURN = '''
-type:
- description: type of the certificate (host or user)
- returned: changed or success
- type: str
- sample: host
-filename:
- description: path to the certificate
- returned: changed or success
- type: str
- sample: /tmp/certificate-cert.pub
-info:
- description: Information about the certificate. Output of C(ssh-keygen -L -f).
- returned: change or success
- type: list
- elements: str
-
-'''
-
-import os
-import errno
-import re
-import tempfile
-
-from datetime import datetime
-from datetime import MINYEAR, MAXYEAR
-from shutil import copy2
-from shutil import rmtree
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils.crypto import convert_relative_to_datetime
-from ansible.module_utils._text import to_native
-
-
-class CertificateError(Exception):
- pass
-
-
-class Certificate(object):
-
- def __init__(self, module):
- self.state = module.params['state']
- self.force = module.params['force']
- self.type = module.params['type']
- self.signing_key = module.params['signing_key']
- self.public_key = module.params['public_key']
- self.path = module.params['path']
- self.identifier = module.params['identifier']
- self.serial_number = module.params['serial_number']
- self.valid_from = module.params['valid_from']
- self.valid_to = module.params['valid_to']
- self.valid_at = module.params['valid_at']
- self.principals = module.params['principals']
- self.options = module.params['options']
- self.changed = False
- self.check_mode = module.check_mode
- self.cert_info = {}
-
- if self.state == 'present':
-
- if self.options and self.type == "host":
- module.fail_json(msg="Options can only be used with user certificates.")
-
- if self.valid_at:
- self.valid_at = self.valid_at.lstrip()
-
- self.valid_from = self.valid_from.lstrip()
- self.valid_to = self.valid_to.lstrip()
-
- self.ssh_keygen = module.get_bin_path('ssh-keygen', True)
-
- def generate(self, module):
-
- if not self.is_valid(module, perms_required=False) or self.force:
- args = [
- self.ssh_keygen,
- '-s', self.signing_key
- ]
-
- validity = ""
-
- if not (self.valid_from == "always" and self.valid_to == "forever"):
-
- if not self.valid_from == "always":
- timeobj = self.convert_to_datetime(module, self.valid_from)
- validity += (
- str(timeobj.year).zfill(4) +
- str(timeobj.month).zfill(2) +
- str(timeobj.day).zfill(2) +
- str(timeobj.hour).zfill(2) +
- str(timeobj.minute).zfill(2) +
- str(timeobj.second).zfill(2)
- )
- else:
- validity += "19700101010101"
-
- validity += ":"
-
- if self.valid_to == "forever":
- # on ssh-keygen versions that have the year 2038 bug this will cause the datetime to be 2038-01-19T04:14:07
- timeobj = datetime(MAXYEAR, 12, 31)
- else:
- timeobj = self.convert_to_datetime(module, self.valid_to)
-
- validity += (
- str(timeobj.year).zfill(4) +
- str(timeobj.month).zfill(2) +
- str(timeobj.day).zfill(2) +
- str(timeobj.hour).zfill(2) +
- str(timeobj.minute).zfill(2) +
- str(timeobj.second).zfill(2)
- )
-
- args.extend(["-V", validity])
-
- if self.type == 'host':
- args.extend(['-h'])
-
- if self.identifier:
- args.extend(['-I', self.identifier])
- else:
- args.extend(['-I', ""])
-
- if self.serial_number is not None:
- args.extend(['-z', str(self.serial_number)])
-
- if self.principals:
- args.extend(['-n', ','.join(self.principals)])
-
- if self.options:
- for option in self.options:
- args.extend(['-O'])
- args.extend([option])
-
- args.extend(['-P', ''])
-
- try:
- temp_directory = tempfile.mkdtemp()
- copy2(self.public_key, temp_directory)
- args.extend([temp_directory + "/" + os.path.basename(self.public_key)])
- module.run_command(args, environ_update=dict(TZ="UTC"), check_rc=True)
- copy2(temp_directory + "/" + os.path.splitext(os.path.basename(self.public_key))[0] + "-cert.pub", self.path)
- rmtree(temp_directory, ignore_errors=True)
- proc = module.run_command([self.ssh_keygen, '-L', '-f', self.path])
- self.cert_info = proc[1].split()
- self.changed = True
- except Exception as e:
- try:
- self.remove()
- rmtree(temp_directory, ignore_errors=True)
- except OSError as exc:
- if exc.errno != errno.ENOENT:
- raise CertificateError(exc)
- else:
- pass
- module.fail_json(msg="%s" % to_native(e))
-
- file_args = module.load_file_common_arguments(module.params)
- if module.set_fs_attributes_if_different(file_args, False):
- self.changed = True
-
- def convert_to_datetime(self, module, timestring):
-
- if self.is_relative(timestring):
- result = convert_relative_to_datetime(timestring)
- if result is None:
- module.fail_json(
- msg="'%s' is not a valid time format." % timestring)
- else:
- return result
- else:
- formats = ["%Y-%m-%d",
- "%Y-%m-%d %H:%M:%S",
- "%Y-%m-%dT%H:%M:%S",
- ]
- for fmt in formats:
- try:
- return datetime.strptime(timestring, fmt)
- except ValueError:
- pass
- module.fail_json(msg="'%s' is not a valid time format" % timestring)
-
- def is_relative(self, timestr):
- if timestr.startswith("+") or timestr.startswith("-"):
- return True
- return False
-
- def is_same_datetime(self, datetime_one, datetime_two):
-
- # This function is for backwards compatibility only because .total_seconds() is new in python2.7
- def timedelta_total_seconds(time_delta):
- return (time_delta.microseconds + 0.0 + (time_delta.seconds + time_delta.days * 24 * 3600) * 10 ** 6) / 10 ** 6
- # try to use .total_ seconds() from python2.7
- try:
- return (datetime_one - datetime_two).total_seconds() == 0.0
- except AttributeError:
- return timedelta_total_seconds(datetime_one - datetime_two) == 0.0
-
- def is_valid(self, module, perms_required=True):
-
- def _check_state():
- return os.path.exists(self.path)
-
- if _check_state():
- proc = module.run_command([self.ssh_keygen, '-L', '-f', self.path], environ_update=dict(TZ="UTC"), check_rc=False)
- if proc[0] != 0:
- return False
- self.cert_info = proc[1].split()
- principals = re.findall("(?<=Principals:)(.*)(?=Critical)", proc[1], re.S)[0].split()
- principals = list(map(str.strip, principals))
- if principals == ["(none)"]:
- principals = None
- cert_type = re.findall("( user | host )", proc[1])[0].strip()
- serial_number = re.search(r"Serial: (\d+)", proc[1]).group(1)
- validity = re.findall("(from (\\d{4}-\\d{2}-\\d{2}T\\d{2}(:\\d{2}){2}) to (\\d{4}-\\d{2}-\\d{2}T\\d{2}(:\\d{2}){2}))", proc[1])
- if validity:
- if validity[0][1]:
- cert_valid_from = self.convert_to_datetime(module, validity[0][1])
- if self.is_same_datetime(cert_valid_from, self.convert_to_datetime(module, "1970-01-01 01:01:01")):
- cert_valid_from = datetime(MINYEAR, 1, 1)
- else:
- cert_valid_from = datetime(MINYEAR, 1, 1)
-
- if validity[0][3]:
- cert_valid_to = self.convert_to_datetime(module, validity[0][3])
- if self.is_same_datetime(cert_valid_to, self.convert_to_datetime(module, "2038-01-19 03:14:07")):
- cert_valid_to = datetime(MAXYEAR, 12, 31)
- else:
- cert_valid_to = datetime(MAXYEAR, 12, 31)
- else:
- cert_valid_from = datetime(MINYEAR, 1, 1)
- cert_valid_to = datetime(MAXYEAR, 12, 31)
- else:
- return False
-
- def _check_perms(module):
- file_args = module.load_file_common_arguments(module.params)
- return not module.set_fs_attributes_if_different(file_args, False)
-
- def _check_serial_number():
- if self.serial_number is None:
- return True
- return self.serial_number == int(serial_number)
-
- def _check_type():
- return self.type == cert_type
-
- def _check_principals():
- if not principals or not self.principals:
- return self.principals == principals
- return set(self.principals) == set(principals)
-
- def _check_validity(module):
- if self.valid_from == "always":
- earliest_time = datetime(MINYEAR, 1, 1)
- elif self.is_relative(self.valid_from):
- earliest_time = None
- else:
- earliest_time = self.convert_to_datetime(module, self.valid_from)
-
- if self.valid_to == "forever":
- last_time = datetime(MAXYEAR, 12, 31)
- elif self.is_relative(self.valid_to):
- last_time = None
- else:
- last_time = self.convert_to_datetime(module, self.valid_to)
-
- if earliest_time:
- if not self.is_same_datetime(earliest_time, cert_valid_from):
- return False
- if last_time:
- if not self.is_same_datetime(last_time, cert_valid_to):
- return False
-
- if self.valid_at:
- if cert_valid_from <= self.convert_to_datetime(module, self.valid_at) <= cert_valid_to:
- return True
-
- if earliest_time and last_time:
- return True
-
- return False
-
- if perms_required and not _check_perms(module):
- return False
-
- return _check_type() and _check_principals() and _check_validity(module) and _check_serial_number()
-
- def dump(self):
-
- """Serialize the object into a dictionary."""
-
- def filter_keywords(arr, keywords):
- concated = []
- string = ""
- for word in arr:
- if word in keywords:
- concated.append(string)
- string = word
- else:
- string += " " + word
- concated.append(string)
- # drop the certificate path
- concated.pop(0)
- return concated
-
- def format_cert_info():
- return filter_keywords(self.cert_info, [
- "Type:",
- "Public",
- "Signing",
- "Key",
- "Serial:",
- "Valid:",
- "Principals:",
- "Critical",
- "Extensions:"])
-
- if self.state == 'present':
- result = {
- 'changed': self.changed,
- 'type': self.type,
- 'filename': self.path,
- 'info': format_cert_info(),
- }
- else:
- result = {
- 'changed': self.changed,
- }
-
- return result
-
- def remove(self):
- """Remove the resource from the filesystem."""
-
- try:
- os.remove(self.path)
- self.changed = True
- except OSError as exc:
- if exc.errno != errno.ENOENT:
- raise CertificateError(exc)
- else:
- pass
-
-
-def main():
-
- module = AnsibleModule(
- argument_spec=dict(
- state=dict(type='str', default='present', choices=['absent', 'present']),
- force=dict(type='bool', default=False),
- type=dict(type='str', choices=['host', 'user']),
- signing_key=dict(type='path'),
- public_key=dict(type='path'),
- path=dict(type='path', required=True),
- identifier=dict(type='str'),
- serial_number=dict(type='int'),
- valid_from=dict(type='str'),
- valid_to=dict(type='str'),
- valid_at=dict(type='str'),
- principals=dict(type='list', elements='str'),
- options=dict(type='list', elements='str'),
- ),
- supports_check_mode=True,
- add_file_common_args=True,
- required_if=[('state', 'present', ['type', 'signing_key', 'public_key', 'valid_from', 'valid_to'])],
- )
-
- def isBaseDir(path):
- base_dir = os.path.dirname(path) or '.'
- if not os.path.isdir(base_dir):
- module.fail_json(
- name=base_dir,
- msg='The directory %s does not exist or the file is not a directory' % base_dir
- )
- if module.params['state'] == "present":
- isBaseDir(module.params['signing_key'])
- isBaseDir(module.params['public_key'])
-
- isBaseDir(module.params['path'])
-
- certificate = Certificate(module)
-
- if certificate.state == 'present':
-
- if module.check_mode:
- certificate.changed = module.params['force'] or not certificate.is_valid(module)
- else:
- try:
- certificate.generate(module)
- except Exception as exc:
- module.fail_json(msg=to_native(exc))
-
- else:
-
- if module.check_mode:
- certificate.changed = os.path.exists(module.params['path'])
- if certificate.changed:
- certificate.cert_info = {}
- else:
- try:
- certificate.remove()
- except Exception as exc:
- module.fail_json(msg=to_native(exc))
-
- result = certificate.dump()
- module.exit_json(**result)
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/crypto/openssh_keypair.py b/lib/ansible/modules/crypto/openssh_keypair.py
deleted file mode 100644
index 86667f4f93..0000000000
--- a/lib/ansible/modules/crypto/openssh_keypair.py
+++ /dev/null
@@ -1,493 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-
-# (c) 2018, David Kainz <dkainz@mgit.at> <dave.jokain@gmx.at>
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-
-ANSIBLE_METADATA = {
- 'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'community'
-}
-
-DOCUMENTATION = '''
----
-module: openssh_keypair
-author: "David Kainz (@lolcube)"
-version_added: "2.8"
-short_description: Generate OpenSSH private and public keys.
-description:
- - "This module allows one to (re)generate OpenSSH private and public keys. It uses
- ssh-keygen to generate keys. One can generate C(rsa), C(dsa), C(rsa1), C(ed25519)
- or C(ecdsa) private keys."
-requirements:
- - "ssh-keygen"
-options:
- state:
- description:
- - Whether the private and public keys should exist or not, taking action if the state is different from what is stated.
- type: str
- default: present
- choices: [ present, absent ]
- size:
- description:
- - "Specifies the number of bits in the private key to create. For RSA keys, the minimum size is 1024 bits and the default is 4096 bits.
- Generally, 2048 bits is considered sufficient. DSA keys must be exactly 1024 bits as specified by FIPS 186-2.
- For ECDSA keys, size determines the key length by selecting from one of three elliptic curve sizes: 256, 384 or 521 bits.
- Attempting to use bit lengths other than these three values for ECDSA keys will cause this module to fail.
- Ed25519 keys have a fixed length and the size will be ignored."
- type: int
- type:
- description:
- - "The algorithm used to generate the SSH private key. C(rsa1) is for protocol version 1.
- C(rsa1) is deprecated and may not be supported by every version of ssh-keygen."
- type: str
- default: rsa
- choices: ['rsa', 'dsa', 'rsa1', 'ecdsa', 'ed25519']
- force:
- description:
- - Should the key be regenerated even if it already exists
- type: bool
- default: false
- path:
- description:
- - Name of the files containing the public and private key. The file containing the public key will have the extension C(.pub).
- type: path
- required: true
- comment:
- description:
- - Provides a new comment to the public key. When checking if the key is in the correct state this will be ignored.
- type: str
- version_added: "2.9"
- regenerate:
- description:
- - Allows to configure in which situations the module is allowed to regenerate private keys.
- The module will always generate a new key if the destination file does not exist.
- - By default, the key will be regenerated when it doesn't match the module's options,
- except when the key cannot be read or the passphrase does not match. Please note that
- this B(changed) for Ansible 2.10. For Ansible 2.9, the behavior was as if C(full_idempotence)
- is specified.
- - If set to C(never), the module will fail if the key cannot be read or the passphrase
- isn't matching, and will never regenerate an existing key.
- - If set to C(fail), the module will fail if the key does not correspond to the module's
- options.
- - If set to C(partial_idempotence), the key will be regenerated if it does not conform to
- the module's options. The key is B(not) regenerated if it cannot be read (broken file),
- the key is protected by an unknown passphrase, or when they key is not protected by a
- passphrase, but a passphrase is specified.
- - If set to C(full_idempotence), the key will be regenerated if it does not conform to the
- module's options. This is also the case if the key cannot be read (broken file), the key
- is protected by an unknown passphrase, or when they key is not protected by a passphrase,
- but a passphrase is specified. Make sure you have a B(backup) when using this option!
- - If set to C(always), the module will always regenerate the key. This is equivalent to
- setting I(force) to C(yes).
- - Note that adjusting the comment and the permissions can be changed without regeneration.
- Therefore, even for C(never), the task can result in changed.
- type: str
- choices:
- - never
- - fail
- - partial_idempotence
- - full_idempotence
- - always
- default: partial_idempotence
- version_added: '2.10'
-notes:
- - In case the ssh key is broken or password protected, the module will fail. Set the I(force) option to C(yes) if you want to regenerate the keypair.
-
-extends_documentation_fragment: files
-'''
-
-EXAMPLES = '''
-# Generate an OpenSSH keypair with the default values (4096 bits, rsa)
-- openssh_keypair:
- path: /tmp/id_ssh_rsa
-
-# Generate an OpenSSH rsa keypair with a different size (2048 bits)
-- openssh_keypair:
- path: /tmp/id_ssh_rsa
- size: 2048
-
-# Force regenerate an OpenSSH keypair if it already exists
-- openssh_keypair:
- path: /tmp/id_ssh_rsa
- force: True
-
-# Generate an OpenSSH keypair with a different algorithm (dsa)
-- openssh_keypair:
- path: /tmp/id_ssh_dsa
- type: dsa
-'''
-
-RETURN = '''
-size:
- description: Size (in bits) of the SSH private key
- returned: changed or success
- type: int
- sample: 4096
-type:
- description: Algorithm used to generate the SSH private key
- returned: changed or success
- type: str
- sample: rsa
-filename:
- description: Path to the generated SSH private key file
- returned: changed or success
- type: str
- sample: /tmp/id_ssh_rsa
-fingerprint:
- description: The fingerprint of the key.
- returned: changed or success
- type: str
- sample: SHA256:r4YCZxihVjedH2OlfjVGI6Y5xAYtdCwk8VxKyzVyYfM
-public_key:
- description: The public key of the generated SSH private key
- returned: changed or success
- type: str
- sample: ssh-rsa AAAAB3Nza(...omitted...)veL4E3Xcw== test_key
-comment:
- description: The comment of the generated key
- returned: changed or success
- type: str
- sample: test@comment
-'''
-
-import os
-import stat
-import errno
-
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils._text import to_native
-
-
-class KeypairError(Exception):
- pass
-
-
-class Keypair(object):
-
- def __init__(self, module):
- self.path = module.params['path']
- self.state = module.params['state']
- self.force = module.params['force']
- self.size = module.params['size']
- self.type = module.params['type']
- self.comment = module.params['comment']
- self.changed = False
- self.check_mode = module.check_mode
- self.privatekey = None
- self.fingerprint = {}
- self.public_key = {}
- self.regenerate = module.params['regenerate']
- if self.regenerate == 'always':
- self.force = True
-
- if self.type in ('rsa', 'rsa1'):
- self.size = 4096 if self.size is None else self.size
- if self.size < 1024:
- module.fail_json(msg=('For RSA keys, the minimum size is 1024 bits and the default is 4096 bits. '
- 'Attempting to use bit lengths under 1024 will cause the module to fail.'))
-
- if self.type == 'dsa':
- self.size = 1024 if self.size is None else self.size
- if self.size != 1024:
- module.fail_json(msg=('DSA keys must be exactly 1024 bits as specified by FIPS 186-2.'))
-
- if self.type == 'ecdsa':
- self.size = 256 if self.size is None else self.size
- if self.size not in (256, 384, 521):
- module.fail_json(msg=('For ECDSA keys, size determines the key length by selecting from '
- 'one of three elliptic curve sizes: 256, 384 or 521 bits. '
- 'Attempting to use bit lengths other than these three values for '
- 'ECDSA keys will cause this module to fail. '))
- if self.type == 'ed25519':
- self.size = 256
-
- def generate(self, module):
- # generate a keypair
- if self.force or not self.isPrivateKeyValid(module, perms_required=False):
- args = [
- module.get_bin_path('ssh-keygen', True),
- '-q',
- '-N', '',
- '-b', str(self.size),
- '-t', self.type,
- '-f', self.path,
- ]
-
- if self.comment:
- args.extend(['-C', self.comment])
- else:
- args.extend(['-C', ""])
-
- try:
- if os.path.exists(self.path) and not os.access(self.path, os.W_OK):
- os.chmod(self.path, stat.S_IWUSR + stat.S_IRUSR)
- self.changed = True
- stdin_data = None
- if os.path.exists(self.path):
- stdin_data = 'y'
- module.run_command(args, data=stdin_data)
- proc = module.run_command([module.get_bin_path('ssh-keygen', True), '-lf', self.path])
- self.fingerprint = proc[1].split()
- pubkey = module.run_command([module.get_bin_path('ssh-keygen', True), '-yf', self.path])
- self.public_key = pubkey[1].strip('\n')
- except Exception as e:
- self.remove()
- module.fail_json(msg="%s" % to_native(e))
-
- elif not self.isPublicKeyValid(module, perms_required=False):
- pubkey = module.run_command([module.get_bin_path('ssh-keygen', True), '-yf', self.path])
- pubkey = pubkey[1].strip('\n')
- try:
- self.changed = True
- with open(self.path + ".pub", "w") as pubkey_f:
- pubkey_f.write(pubkey + '\n')
- os.chmod(self.path + ".pub", stat.S_IWUSR + stat.S_IRUSR + stat.S_IRGRP + stat.S_IROTH)
- except IOError:
- module.fail_json(
- msg='The public key is missing or does not match the private key. '
- 'Unable to regenerate the public key.')
- self.public_key = pubkey
-
- if self.comment:
- try:
- if os.path.exists(self.path) and not os.access(self.path, os.W_OK):
- os.chmod(self.path, stat.S_IWUSR + stat.S_IRUSR)
- args = [module.get_bin_path('ssh-keygen', True),
- '-q', '-o', '-c', '-C', self.comment, '-f', self.path]
- module.run_command(args)
- except IOError:
- module.fail_json(
- msg='Unable to update the comment for the public key.')
-
- file_args = module.load_file_common_arguments(module.params)
- if module.set_fs_attributes_if_different(file_args, False):
- self.changed = True
- file_args['path'] = file_args['path'] + '.pub'
- if module.set_fs_attributes_if_different(file_args, False):
- self.changed = True
-
- def _check_pass_protected_or_broken_key(self, module):
- key_state = module.run_command([module.get_bin_path('ssh-keygen', True),
- '-P', '', '-yf', self.path], check_rc=False)
- if key_state[0] == 255 or 'is not a public key file' in key_state[2]:
- return True
- if 'incorrect passphrase' in key_state[2] or 'load failed' in key_state[2]:
- return True
- return False
-
- def isPrivateKeyValid(self, module, perms_required=True):
-
- # check if the key is correct
- def _check_state():
- return os.path.exists(self.path)
-
- if not _check_state():
- return False
-
- if self._check_pass_protected_or_broken_key(module):
- if self.regenerate in ('full_idempotence', 'always'):
- return False
- module.fail_json(msg='Unable to read the key. The key is protected with a passphrase or broken.'
- ' Will not proceed. To force regeneration, call the module with `generate`'
- ' set to `full_idempotence` or `always`, or with `force=yes`.')
-
- proc = module.run_command([module.get_bin_path('ssh-keygen', True), '-lf', self.path], check_rc=False)
- if not proc[0] == 0:
- if os.path.isdir(self.path):
- module.fail_json(msg='%s is a directory. Please specify a path to a file.' % (self.path))
-
- if self.regenerate in ('full_idempotence', 'always'):
- return False
- module.fail_json(msg='Unable to read the key. The key is protected with a passphrase or broken.'
- ' Will not proceed. To force regeneration, call the module with `generate`'
- ' set to `full_idempotence` or `always`, or with `force=yes`.')
-
- fingerprint = proc[1].split()
- keysize = int(fingerprint[0])
- keytype = fingerprint[-1][1:-1].lower()
-
- self.fingerprint = fingerprint
-
- if self.regenerate == 'never':
- return True
-
- def _check_type():
- return self.type == keytype
-
- def _check_size():
- return self.size == keysize
-
- if not (_check_type() and _check_size()):
- if self.regenerate in ('partial_idempotence', 'full_idempotence', 'always'):
- return False
- module.fail_json(msg='Key has wrong type and/or size.'
- ' Will not proceed. To force regeneration, call the module with `generate`'
- ' set to `partial_idempotence`, `full_idempotence` or `always`, or with `force=yes`.')
-
- def _check_perms(module):
- file_args = module.load_file_common_arguments(module.params)
- return not module.set_fs_attributes_if_different(file_args, False)
-
- return not perms_required or _check_perms(module)
-
- def isPublicKeyValid(self, module, perms_required=True):
-
- def _get_pubkey_content():
- if os.path.exists(self.path + ".pub"):
- with open(self.path + ".pub", "r") as pubkey_f:
- present_pubkey = pubkey_f.read().strip(' \n')
- return present_pubkey
- else:
- return False
-
- def _parse_pubkey(pubkey_content):
- if pubkey_content:
- parts = pubkey_content.split(' ', 2)
- if len(parts) < 2:
- return False
- return parts[0], parts[1], '' if len(parts) <= 2 else parts[2]
- return False
-
- def _pubkey_valid(pubkey):
- if pubkey_parts and _parse_pubkey(pubkey):
- return pubkey_parts[:2] == _parse_pubkey(pubkey)[:2]
- return False
-
- def _comment_valid():
- if pubkey_parts:
- return pubkey_parts[2] == self.comment
- return False
-
- def _check_perms(module):
- file_args = module.load_file_common_arguments(module.params)
- file_args['path'] = file_args['path'] + '.pub'
- return not module.set_fs_attributes_if_different(file_args, False)
-
- pubkey_parts = _parse_pubkey(_get_pubkey_content())
-
- pubkey = module.run_command([module.get_bin_path('ssh-keygen', True), '-yf', self.path])
- pubkey = pubkey[1].strip('\n')
- if _pubkey_valid(pubkey):
- self.public_key = pubkey
- else:
- return False
-
- if self.comment:
- if not _comment_valid():
- return False
-
- if perms_required:
- if not _check_perms(module):
- return False
-
- return True
-
- def dump(self):
- # return result as a dict
-
- """Serialize the object into a dictionary."""
- result = {
- 'changed': self.changed,
- 'size': self.size,
- 'type': self.type,
- 'filename': self.path,
- # On removal this has no value
- 'fingerprint': self.fingerprint[1] if self.fingerprint else '',
- 'public_key': self.public_key,
- 'comment': self.comment if self.comment else '',
- }
-
- return result
-
- def remove(self):
- """Remove the resource from the filesystem."""
-
- try:
- os.remove(self.path)
- self.changed = True
- except OSError as exc:
- if exc.errno != errno.ENOENT:
- raise KeypairError(exc)
- else:
- pass
-
- if os.path.exists(self.path + ".pub"):
- try:
- os.remove(self.path + ".pub")
- self.changed = True
- except OSError as exc:
- if exc.errno != errno.ENOENT:
- raise KeypairError(exc)
- else:
- pass
-
-
-def main():
-
- # Define Ansible Module
- module = AnsibleModule(
- argument_spec=dict(
- state=dict(type='str', default='present', choices=['present', 'absent']),
- size=dict(type='int'),
- type=dict(type='str', default='rsa', choices=['rsa', 'dsa', 'rsa1', 'ecdsa', 'ed25519']),
- force=dict(type='bool', default=False),
- path=dict(type='path', required=True),
- comment=dict(type='str'),
- regenerate=dict(
- type='str',
- default='partial_idempotence',
- choices=['never', 'fail', 'partial_idempotence', 'full_idempotence', 'always']
- ),
- ),
- supports_check_mode=True,
- add_file_common_args=True,
- )
-
- # Check if Path exists
- base_dir = os.path.dirname(module.params['path']) or '.'
- if not os.path.isdir(base_dir):
- module.fail_json(
- name=base_dir,
- msg='The directory %s does not exist or the file is not a directory' % base_dir
- )
-
- keypair = Keypair(module)
-
- if keypair.state == 'present':
-
- if module.check_mode:
- result = keypair.dump()
- result['changed'] = keypair.force or not keypair.isPrivateKeyValid(module) or not keypair.isPublicKeyValid(module)
- module.exit_json(**result)
-
- try:
- keypair.generate(module)
- except Exception as exc:
- module.fail_json(msg=to_native(exc))
- else:
-
- if module.check_mode:
- keypair.changed = os.path.exists(module.params['path'])
- if keypair.changed:
- keypair.fingerprint = {}
- result = keypair.dump()
- module.exit_json(**result)
-
- try:
- keypair.remove()
- except Exception as exc:
- module.fail_json(msg=to_native(exc))
-
- result = keypair.dump()
-
- module.exit_json(**result)
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/crypto/openssl_certificate.py b/lib/ansible/modules/crypto/openssl_certificate.py
deleted file mode 100644
index 4bd5e5c468..0000000000
--- a/lib/ansible/modules/crypto/openssl_certificate.py
+++ /dev/null
@@ -1,2756 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-
-# Copyright: (c) 2016-2017, Yanis Guenane <yanis+ansible@guenane.org>
-# Copyright: (c) 2017, Markus Teufelberger <mteufelberger+ansible@mgit.at>
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'community'}
-
-DOCUMENTATION = r'''
----
-module: openssl_certificate
-version_added: "2.4"
-short_description: Generate and/or check OpenSSL certificates
-description:
- - This module allows one to (re)generate OpenSSL certificates.
- - It implements a notion of provider (ie. C(selfsigned), C(ownca), C(acme), C(assertonly), C(entrust))
- for your certificate.
- - The C(assertonly) provider is intended for use cases where one is only interested in
- checking properties of a supplied certificate. Please note that this provider has been
- deprecated in Ansible 2.9 and will be removed in Ansible 2.13. See the examples on how
- to emulate C(assertonly) usage with M(openssl_certificate_info), M(openssl_csr_info),
- M(openssl_privatekey_info) and M(assert). This also allows more flexible checks than
- the ones offered by the C(assertonly) provider.
- - The C(ownca) provider is intended for generating OpenSSL certificate signed with your own
- CA (Certificate Authority) certificate (self-signed certificate).
- - Many properties that can be specified in this module are for validation of an
- existing or newly generated certificate. The proper place to specify them, if you
- want to receive a certificate with these properties is a CSR (Certificate Signing Request).
- - "Please note that the module regenerates existing certificate if it doesn't match the module's
- options, or if it seems to be corrupt. If you are concerned that this could overwrite
- your existing certificate, consider using the I(backup) option."
- - It uses the pyOpenSSL or cryptography python library to interact with OpenSSL.
- - If both the cryptography and PyOpenSSL libraries are available (and meet the minimum version requirements)
- cryptography will be preferred as a backend over PyOpenSSL (unless the backend is forced with C(select_crypto_backend)).
- Please note that the PyOpenSSL backend was deprecated in Ansible 2.9 and will be removed in Ansible 2.13.
-requirements:
- - PyOpenSSL >= 0.15 or cryptography >= 1.6 (if using C(selfsigned) or C(assertonly) provider)
- - acme-tiny >= 4.0.0 (if using the C(acme) provider)
-author:
- - Yanis Guenane (@Spredzy)
- - Markus Teufelberger (@MarkusTeufelberger)
-options:
- state:
- description:
- - Whether the certificate should exist or not, taking action if the state is different from what is stated.
- type: str
- default: present
- choices: [ absent, present ]
-
- path:
- description:
- - Remote absolute path where the generated certificate file should be created or is already located.
- type: path
- required: true
-
- provider:
- description:
- - Name of the provider to use to generate/retrieve the OpenSSL certificate.
- - The C(assertonly) provider will not generate files and fail if the certificate file is missing.
- - The C(assertonly) provider has been deprecated in Ansible 2.9 and will be removed in Ansible 2.13.
- Please see the examples on how to emulate it with M(openssl_certificate_info), M(openssl_csr_info),
- M(openssl_privatekey_info) and M(assert).
- - "The C(entrust) provider was added for Ansible 2.9 and requires credentials for the
- L(Entrust Certificate Services,https://www.entrustdatacard.com/products/categories/ssl-certificates) (ECS) API."
- - Required if I(state) is C(present).
- type: str
- choices: [ acme, assertonly, entrust, ownca, selfsigned ]
-
- force:
- description:
- - Generate the certificate, even if it already exists.
- type: bool
- default: no
-
- csr_path:
- description:
- - Path to the Certificate Signing Request (CSR) used to generate this certificate.
- - This is not required in C(assertonly) mode.
- - This is mutually exclusive with I(csr_content).
- type: path
- csr_content:
- description:
- - Content of the Certificate Signing Request (CSR) used to generate this certificate.
- - This is not required in C(assertonly) mode.
- - This is mutually exclusive with I(csr_path).
- type: str
- version_added: "2.10"
-
- privatekey_path:
- description:
- - Path to the private key to use when signing the certificate.
- - This is mutually exclusive with I(privatekey_content).
- type: path
- privatekey_content:
- description:
- - Path to the private key to use when signing the certificate.
- - This is mutually exclusive with I(privatekey_path).
- type: str
- version_added: "2.10"
-
- privatekey_passphrase:
- description:
- - The passphrase for the I(privatekey_path) resp. I(privatekey_content).
- - This is required if the private key is password protected.
- type: str
-
- selfsigned_version:
- description:
- - Version of the C(selfsigned) certificate.
- - Nowadays it should almost always be C(3).
- - This is only used by the C(selfsigned) provider.
- type: int
- default: 3
- version_added: "2.5"
-
- selfsigned_digest:
- description:
- - Digest algorithm to be used when self-signing the certificate.
- - This is only used by the C(selfsigned) provider.
- type: str
- default: sha256
-
- selfsigned_not_before:
- description:
- - The point in time the certificate is valid from.
- - Time can be specified either as relative time or as absolute timestamp.
- - Time will always be interpreted as UTC.
- - Valid format is C([+-]timespec | ASN.1 TIME) where timespec can be an integer
- + C([w | d | h | m | s]) (e.g. C(+32w1d2h).
- - Note that if using relative time this module is NOT idempotent.
- - If this value is not specified, the certificate will start being valid from now.
- - This is only used by the C(selfsigned) provider.
- type: str
- default: +0s
- aliases: [ selfsigned_notBefore ]
-
- selfsigned_not_after:
- description:
- - The point in time at which the certificate stops being valid.
- - Time can be specified either as relative time or as absolute timestamp.
- - Time will always be interpreted as UTC.
- - Valid format is C([+-]timespec | ASN.1 TIME) where timespec can be an integer
- + C([w | d | h | m | s]) (e.g. C(+32w1d2h).
- - Note that if using relative time this module is NOT idempotent.
- - If this value is not specified, the certificate will stop being valid 10 years from now.
- - This is only used by the C(selfsigned) provider.
- type: str
- default: +3650d
- aliases: [ selfsigned_notAfter ]
-
- selfsigned_create_subject_key_identifier:
- description:
- - Whether to create the Subject Key Identifier (SKI) from the public key.
- - A value of C(create_if_not_provided) (default) only creates a SKI when the CSR does not
- provide one.
- - A value of C(always_create) always creates a SKI. If the CSR provides one, that one is
- ignored.
- - A value of C(never_create) never creates a SKI. If the CSR provides one, that one is used.
- - This is only used by the C(selfsigned) provider.
- - Note that this is only supported if the C(cryptography) backend is used!
- type: str
- choices: [create_if_not_provided, always_create, never_create]
- default: create_if_not_provided
- version_added: "2.9"
-
- ownca_path:
- description:
- - Remote absolute path of the CA (Certificate Authority) certificate.
- - This is only used by the C(ownca) provider.
- - This is mutually exclusive with I(ownca_content).
- type: path
- version_added: "2.7"
- ownca_content:
- description:
- - Content of the CA (Certificate Authority) certificate.
- - This is only used by the C(ownca) provider.
- - This is mutually exclusive with I(ownca_path).
- type: str
- version_added: "2.10"
-
- ownca_privatekey_path:
- description:
- - Path to the CA (Certificate Authority) private key to use when signing the certificate.
- - This is only used by the C(ownca) provider.
- - This is mutually exclusive with I(ownca_privatekey_content).
- type: path
- version_added: "2.7"
- ownca_privatekey_content:
- description:
- - Path to the CA (Certificate Authority) private key to use when signing the certificate.
- - This is only used by the C(ownca) provider.
- - This is mutually exclusive with I(ownca_privatekey_path).
- type: str
- version_added: "2.10"
-
- ownca_privatekey_passphrase:
- description:
- - The passphrase for the I(ownca_privatekey_path) resp. I(ownca_privatekey_content).
- - This is only used by the C(ownca) provider.
- type: str
- version_added: "2.7"
-
- ownca_digest:
- description:
- - The digest algorithm to be used for the C(ownca) certificate.
- - This is only used by the C(ownca) provider.
- type: str
- default: sha256
- version_added: "2.7"
-
- ownca_version:
- description:
- - The version of the C(ownca) certificate.
- - Nowadays it should almost always be C(3).
- - This is only used by the C(ownca) provider.
- type: int
- default: 3
- version_added: "2.7"
-
- ownca_not_before:
- description:
- - The point in time the certificate is valid from.
- - Time can be specified either as relative time or as absolute timestamp.
- - Time will always be interpreted as UTC.
- - Valid format is C([+-]timespec | ASN.1 TIME) where timespec can be an integer
- + C([w | d | h | m | s]) (e.g. C(+32w1d2h).
- - Note that if using relative time this module is NOT idempotent.
- - If this value is not specified, the certificate will start being valid from now.
- - This is only used by the C(ownca) provider.
- type: str
- default: +0s
- version_added: "2.7"
-
- ownca_not_after:
- description:
- - The point in time at which the certificate stops being valid.
- - Time can be specified either as relative time or as absolute timestamp.
- - Time will always be interpreted as UTC.
- - Valid format is C([+-]timespec | ASN.1 TIME) where timespec can be an integer
- + C([w | d | h | m | s]) (e.g. C(+32w1d2h).
- - Note that if using relative time this module is NOT idempotent.
- - If this value is not specified, the certificate will stop being valid 10 years from now.
- - This is only used by the C(ownca) provider.
- type: str
- default: +3650d
- version_added: "2.7"
-
- ownca_create_subject_key_identifier:
- description:
- - Whether to create the Subject Key Identifier (SKI) from the public key.
- - A value of C(create_if_not_provided) (default) only creates a SKI when the CSR does not
- provide one.
- - A value of C(always_create) always creates a SKI. If the CSR provides one, that one is
- ignored.
- - A value of C(never_create) never creates a SKI. If the CSR provides one, that one is used.
- - This is only used by the C(ownca) provider.
- - Note that this is only supported if the C(cryptography) backend is used!
- type: str
- choices: [create_if_not_provided, always_create, never_create]
- default: create_if_not_provided
- version_added: "2.9"
-
- ownca_create_authority_key_identifier:
- description:
- - Create a Authority Key Identifier from the CA's certificate. If the CSR provided
- a authority key identifier, it is ignored.
- - The Authority Key Identifier is generated from the CA certificate's Subject Key Identifier,
- if available. If it is not available, the CA certificate's public key will be used.
- - This is only used by the C(ownca) provider.
- - Note that this is only supported if the C(cryptography) backend is used!
- type: bool
- default: yes
- version_added: "2.9"
-
- acme_accountkey_path:
- description:
- - The path to the accountkey for the C(acme) provider.
- - This is only used by the C(acme) provider.
- type: path
-
- acme_challenge_path:
- description:
- - The path to the ACME challenge directory that is served on U(http://<HOST>:80/.well-known/acme-challenge/)
- - This is only used by the C(acme) provider.
- type: path
-
- acme_chain:
- description:
- - Include the intermediate certificate to the generated certificate
- - This is only used by the C(acme) provider.
- - Note that this is only available for older versions of C(acme-tiny).
- New versions include the chain automatically, and setting I(acme_chain) to C(yes) results in an error.
- type: bool
- default: no
- version_added: "2.5"
-
- acme_directory:
- description:
- - "The ACME directory to use. You can use any directory that supports the ACME protocol, such as Buypass or Let's Encrypt."
- - "Let's Encrypt recommends using their staging server while developing jobs. U(https://letsencrypt.org/docs/staging-environment/)."
- type: str
- default: https://acme-v02.api.letsencrypt.org/directory
- version_added: "2.10"
-
- signature_algorithms:
- description:
- - A list of algorithms that you would accept the certificate to be signed with
- (e.g. ['sha256WithRSAEncryption', 'sha512WithRSAEncryption']).
- - This is only used by the C(assertonly) provider.
- - This option is deprecated since Ansible 2.9 and will be removed with the C(assertonly) provider in Ansible 2.13.
- For alternatives, see the example on replacing C(assertonly).
- type: list
- elements: str
-
- issuer:
- description:
- - The key/value pairs that must be present in the issuer name field of the certificate.
- - If you need to specify more than one value with the same key, use a list as value.
- - This is only used by the C(assertonly) provider.
- - This option is deprecated since Ansible 2.9 and will be removed with the C(assertonly) provider in Ansible 2.13.
- For alternatives, see the example on replacing C(assertonly).
- type: dict
-
- issuer_strict:
- description:
- - If set to C(yes), the I(issuer) field must contain only these values.
- - This is only used by the C(assertonly) provider.
- - This option is deprecated since Ansible 2.9 and will be removed with the C(assertonly) provider in Ansible 2.13.
- For alternatives, see the example on replacing C(assertonly).
- type: bool
- default: no
- version_added: "2.5"
-
- subject:
- description:
- - The key/value pairs that must be present in the subject name field of the certificate.
- - If you need to specify more than one value with the same key, use a list as value.
- - This is only used by the C(assertonly) provider.
- - This option is deprecated since Ansible 2.9 and will be removed with the C(assertonly) provider in Ansible 2.13.
- For alternatives, see the example on replacing C(assertonly).
- type: dict
-
- subject_strict:
- description:
- - If set to C(yes), the I(subject) field must contain only these values.
- - This is only used by the C(assertonly) provider.
- - This option is deprecated since Ansible 2.9 and will be removed with the C(assertonly) provider in Ansible 2.13.
- For alternatives, see the example on replacing C(assertonly).
- type: bool
- default: no
- version_added: "2.5"
-
- has_expired:
- description:
- - Checks if the certificate is expired/not expired at the time the module is executed.
- - This is only used by the C(assertonly) provider.
- - This option is deprecated since Ansible 2.9 and will be removed with the C(assertonly) provider in Ansible 2.13.
- For alternatives, see the example on replacing C(assertonly).
- type: bool
- default: no
-
- version:
- description:
- - The version of the certificate.
- - Nowadays it should almost always be 3.
- - This is only used by the C(assertonly) provider.
- - This option is deprecated since Ansible 2.9 and will be removed with the C(assertonly) provider in Ansible 2.13.
- For alternatives, see the example on replacing C(assertonly).
- type: int
-
- valid_at:
- description:
- - The certificate must be valid at this point in time.
- - The timestamp is formatted as an ASN.1 TIME.
- - This is only used by the C(assertonly) provider.
- - This option is deprecated since Ansible 2.9 and will be removed with the C(assertonly) provider in Ansible 2.13.
- For alternatives, see the example on replacing C(assertonly).
- type: str
-
- invalid_at:
- description:
- - The certificate must be invalid at this point in time.
- - The timestamp is formatted as an ASN.1 TIME.
- - This is only used by the C(assertonly) provider.
- - This option is deprecated since Ansible 2.9 and will be removed with the C(assertonly) provider in Ansible 2.13.
- For alternatives, see the example on replacing C(assertonly).
- type: str
-
- not_before:
- description:
- - The certificate must start to become valid at this point in time.
- - The timestamp is formatted as an ASN.1 TIME.
- - This is only used by the C(assertonly) provider.
- - This option is deprecated since Ansible 2.9 and will be removed with the C(assertonly) provider in Ansible 2.13.
- For alternatives, see the example on replacing C(assertonly).
- type: str
- aliases: [ notBefore ]
-
- not_after:
- description:
- - The certificate must expire at this point in time.
- - The timestamp is formatted as an ASN.1 TIME.
- - This is only used by the C(assertonly) provider.
- - This option is deprecated since Ansible 2.9 and will be removed with the C(assertonly) provider in Ansible 2.13.
- For alternatives, see the example on replacing C(assertonly).
- type: str
- aliases: [ notAfter ]
-
- valid_in:
- description:
- - The certificate must still be valid at this relative time offset from now.
- - Valid format is C([+-]timespec | number_of_seconds) where timespec can be an integer
- + C([w | d | h | m | s]) (e.g. C(+32w1d2h).
- - Note that if using this parameter, this module is NOT idempotent.
- - This is only used by the C(assertonly) provider.
- - This option is deprecated since Ansible 2.9 and will be removed with the C(assertonly) provider in Ansible 2.13.
- For alternatives, see the example on replacing C(assertonly).
- type: str
-
- key_usage:
- description:
- - The I(key_usage) extension field must contain all these values.
- - This is only used by the C(assertonly) provider.
- - This option is deprecated since Ansible 2.9 and will be removed with the C(assertonly) provider in Ansible 2.13.
- For alternatives, see the example on replacing C(assertonly).
- type: list
- elements: str
- aliases: [ keyUsage ]
-
- key_usage_strict:
- description:
- - If set to C(yes), the I(key_usage) extension field must contain only these values.
- - This is only used by the C(assertonly) provider.
- - This option is deprecated since Ansible 2.9 and will be removed with the C(assertonly) provider in Ansible 2.13.
- For alternatives, see the example on replacing C(assertonly).
- type: bool
- default: no
- aliases: [ keyUsage_strict ]
-
- extended_key_usage:
- description:
- - The I(extended_key_usage) extension field must contain all these values.
- - This is only used by the C(assertonly) provider.
- - This option is deprecated since Ansible 2.9 and will be removed with the C(assertonly) provider in Ansible 2.13.
- For alternatives, see the example on replacing C(assertonly).
- type: list
- elements: str
- aliases: [ extendedKeyUsage ]
-
- extended_key_usage_strict:
- description:
- - If set to C(yes), the I(extended_key_usage) extension field must contain only these values.
- - This is only used by the C(assertonly) provider.
- - This option is deprecated since Ansible 2.9 and will be removed with the C(assertonly) provider in Ansible 2.13.
- For alternatives, see the example on replacing C(assertonly).
- type: bool
- default: no
- aliases: [ extendedKeyUsage_strict ]
-
- subject_alt_name:
- description:
- - The I(subject_alt_name) extension field must contain these values.
- - This is only used by the C(assertonly) provider.
- - This option is deprecated since Ansible 2.9 and will be removed with the C(assertonly) provider in Ansible 2.13.
- For alternatives, see the example on replacing C(assertonly).
- type: list
- elements: str
- aliases: [ subjectAltName ]
-
- subject_alt_name_strict:
- description:
- - If set to C(yes), the I(subject_alt_name) extension field must contain only these values.
- - This is only used by the C(assertonly) provider.
- - This option is deprecated since Ansible 2.9 and will be removed with the C(assertonly) provider in Ansible 2.13.
- For alternatives, see the example on replacing C(assertonly).
- type: bool
- default: no
- aliases: [ subjectAltName_strict ]
-
- select_crypto_backend:
- description:
- - Determines which crypto backend to use.
- - The default choice is C(auto), which tries to use C(cryptography) if available, and falls back to C(pyopenssl).
- - If set to C(pyopenssl), will try to use the L(pyOpenSSL,https://pypi.org/project/pyOpenSSL/) library.
- - If set to C(cryptography), will try to use the L(cryptography,https://cryptography.io/) library.
- - Please note that the C(pyopenssl) backend has been deprecated in Ansible 2.9, and will be removed in Ansible 2.13.
- From that point on, only the C(cryptography) backend will be available.
- type: str
- default: auto
- choices: [ auto, cryptography, pyopenssl ]
- version_added: "2.8"
-
- backup:
- description:
- - Create a backup file including a timestamp so you can get the original
- certificate back if you overwrote it with a new one by accident.
- - This is not used by the C(assertonly) provider.
- - This option is deprecated since Ansible 2.9 and will be removed with the C(assertonly) provider in Ansible 2.13.
- For alternatives, see the example on replacing C(assertonly).
- type: bool
- default: no
- version_added: "2.8"
-
- entrust_cert_type:
- description:
- - Specify the type of certificate requested.
- - This is only used by the C(entrust) provider.
- type: str
- default: STANDARD_SSL
- choices: [ 'STANDARD_SSL', 'ADVANTAGE_SSL', 'UC_SSL', 'EV_SSL', 'WILDCARD_SSL', 'PRIVATE_SSL', 'PD_SSL', 'CDS_ENT_LITE', 'CDS_ENT_PRO', 'SMIME_ENT' ]
- version_added: "2.9"
-
- entrust_requester_email:
- description:
- - The email of the requester of the certificate (for tracking purposes).
- - This is only used by the C(entrust) provider.
- - This is required if the provider is C(entrust).
- type: str
- version_added: "2.9"
-
- entrust_requester_name:
- description:
- - The name of the requester of the certificate (for tracking purposes).
- - This is only used by the C(entrust) provider.
- - This is required if the provider is C(entrust).
- type: str
- version_added: "2.9"
-
- entrust_requester_phone:
- description:
- - The phone number of the requester of the certificate (for tracking purposes).
- - This is only used by the C(entrust) provider.
- - This is required if the provider is C(entrust).
- type: str
- version_added: "2.9"
-
- entrust_api_user:
- description:
- - The username for authentication to the Entrust Certificate Services (ECS) API.
- - This is only used by the C(entrust) provider.
- - This is required if the provider is C(entrust).
- type: str
- version_added: "2.9"
-
- entrust_api_key:
- description:
- - The key (password) for authentication to the Entrust Certificate Services (ECS) API.
- - This is only used by the C(entrust) provider.
- - This is required if the provider is C(entrust).
- type: str
- version_added: "2.9"
-
- entrust_api_client_cert_path:
- description:
- - The path to the client certificate used to authenticate to the Entrust Certificate Services (ECS) API.
- - This is only used by the C(entrust) provider.
- - This is required if the provider is C(entrust).
- type: path
- version_added: "2.9"
-
- entrust_api_client_cert_key_path:
- description:
- - The path to the private key of the client certificate used to authenticate to the Entrust Certificate Services (ECS) API.
- - This is only used by the C(entrust) provider.
- - This is required if the provider is C(entrust).
- type: path
- version_added: "2.9"
-
- entrust_not_after:
- description:
- - The point in time at which the certificate stops being valid.
- - Time can be specified either as relative time or as an absolute timestamp.
- - A valid absolute time format is C(ASN.1 TIME) such as C(2019-06-18).
- - A valid relative time format is C([+-]timespec) where timespec can be an integer + C([w | d | h | m | s]), such as C(+365d) or C(+32w1d2h)).
- - Time will always be interpreted as UTC.
- - Note that only the date (day, month, year) is supported for specifying the expiry date of the issued certificate.
- - The full date-time is adjusted to EST (GMT -5:00) before issuance, which may result in a certificate with an expiration date one day
- earlier than expected if a relative time is used.
- - The minimum certificate lifetime is 90 days, and maximum is three years.
- - If this value is not specified, the certificate will stop being valid 365 days the date of issue.
- - This is only used by the C(entrust) provider.
- type: str
- default: +365d
- version_added: "2.9"
-
- entrust_api_specification_path:
- description:
- - The path to the specification file defining the Entrust Certificate Services (ECS) API configuration.
- - You can use this to keep a local copy of the specification to avoid downloading it every time the module is used.
- - This is only used by the C(entrust) provider.
- type: path
- default: https://cloud.entrust.net/EntrustCloud/documentation/cms-api-2.1.0.yaml
- version_added: "2.9"
-
- return_content:
- description:
- - If set to C(yes), will return the (current or generated) certificate's content as I(certificate).
- type: bool
- default: no
- version_added: "2.10"
-
-extends_documentation_fragment: files
-notes:
- - All ASN.1 TIME values should be specified following the YYYYMMDDHHMMSSZ pattern.
- - Date specified should be UTC. Minutes and seconds are mandatory.
- - For security reason, when you use C(ownca) provider, you should NOT run M(openssl_certificate) on
- a target machine, but on a dedicated CA machine. It is recommended not to store the CA private key
- on the target machine. Once signed, the certificate can be moved to the target machine.
-seealso:
-- module: openssl_csr
-- module: openssl_dhparam
-- module: openssl_pkcs12
-- module: openssl_privatekey
-- module: openssl_publickey
-'''
-
-EXAMPLES = r'''
-- name: Generate a Self Signed OpenSSL certificate
- openssl_certificate:
- path: /etc/ssl/crt/ansible.com.crt
- privatekey_path: /etc/ssl/private/ansible.com.pem
- csr_path: /etc/ssl/csr/ansible.com.csr
- provider: selfsigned
-
-- name: Generate an OpenSSL certificate signed with your own CA certificate
- openssl_certificate:
- path: /etc/ssl/crt/ansible.com.crt
- csr_path: /etc/ssl/csr/ansible.com.csr
- ownca_path: /etc/ssl/crt/ansible_CA.crt
- ownca_privatekey_path: /etc/ssl/private/ansible_CA.pem
- provider: ownca
-
-- name: Generate a Let's Encrypt Certificate
- openssl_certificate:
- path: /etc/ssl/crt/ansible.com.crt
- csr_path: /etc/ssl/csr/ansible.com.csr
- provider: acme
- acme_accountkey_path: /etc/ssl/private/ansible.com.pem
- acme_challenge_path: /etc/ssl/challenges/ansible.com/
-
-- name: Force (re-)generate a new Let's Encrypt Certificate
- openssl_certificate:
- path: /etc/ssl/crt/ansible.com.crt
- csr_path: /etc/ssl/csr/ansible.com.csr
- provider: acme
- acme_accountkey_path: /etc/ssl/private/ansible.com.pem
- acme_challenge_path: /etc/ssl/challenges/ansible.com/
- force: yes
-
-- name: Generate an Entrust certificate via the Entrust Certificate Services (ECS) API
- openssl_certificate:
- path: /etc/ssl/crt/ansible.com.crt
- csr_path: /etc/ssl/csr/ansible.com.csr
- provider: entrust
- entrust_requester_name: Jo Doe
- entrust_requester_email: jdoe@ansible.com
- entrust_requester_phone: 555-555-5555
- entrust_cert_type: STANDARD_SSL
- entrust_api_user: apiusername
- entrust_api_key: a^lv*32!cd9LnT
- entrust_api_client_cert_path: /etc/ssl/entrust/ecs-client.crt
- entrust_api_client_cert_key_path: /etc/ssl/entrust/ecs-key.crt
- entrust_api_specification_path: /etc/ssl/entrust/api-docs/cms-api-2.1.0.yaml
-
-# The following example shows one assertonly usage using all existing options for
-# assertonly, and shows how to emulate the behavior with the openssl_certificate_info,
-# openssl_csr_info, openssl_privatekey_info and assert modules:
-
-- openssl_certificate:
- provider: assertonly
- path: /etc/ssl/crt/ansible.com.crt
- csr_path: /etc/ssl/csr/ansible.com.csr
- privatekey_path: /etc/ssl/csr/ansible.com.key
- signature_algorithms:
- - sha256WithRSAEncryption
- - sha512WithRSAEncryption
- subject:
- commonName: ansible.com
- subject_strict: yes
- issuer:
- commonName: ansible.com
- issuer_strict: yes
- has_expired: no
- version: 3
- key_usage:
- - Data Encipherment
- key_usage_strict: yes
- extended_key_usage:
- - DVCS
- extended_key_usage_strict: yes
- subject_alt_name:
- - dns:ansible.com
- subject_alt_name_strict: yes
- not_before: 20190331202428Z
- not_after: 20190413202428Z
- valid_at: "+1d10h"
- invalid_at: 20200331202428Z
- valid_in: 10 # in ten seconds
-
-- openssl_certificate_info:
- path: /etc/ssl/crt/ansible.com.crt
- # for valid_at, invalid_at and valid_in
- valid_at:
- one_day_ten_hours: "+1d10h"
- fixed_timestamp: 20200331202428Z
- ten_seconds: "+10"
- register: result
-
-- openssl_csr_info:
- # Verifies that the CSR signature is valid; module will fail if not
- path: /etc/ssl/csr/ansible.com.csr
- register: result_csr
-
-- openssl_privatekey_info:
- path: /etc/ssl/csr/ansible.com.key
- register: result_privatekey
-
-- assert:
- that:
- # When private key is specified for assertonly, this will be checked:
- - result.public_key == result_privatekey.public_key
- # When CSR is specified for assertonly, this will be checked:
- - result.public_key == result_csr.public_key
- - result.subject_ordered == result_csr.subject_ordered
- - result.extensions_by_oid == result_csr.extensions_by_oid
- # signature_algorithms check
- - "result.signature_algorithm == 'sha256WithRSAEncryption' or result.signature_algorithm == 'sha512WithRSAEncryption'"
- # subject and subject_strict
- - "result.subject.commonName == 'ansible.com'"
- - "result.subject | length == 1" # the number must be the number of entries you check for
- # issuer and issuer_strict
- - "result.issuer.commonName == 'ansible.com'"
- - "result.issuer | length == 1" # the number must be the number of entries you check for
- # has_expired
- - not result.expired
- # version
- - result.version == 3
- # key_usage and key_usage_strict
- - "'Data Encipherment' in result.key_usage"
- - "result.key_usage | length == 1" # the number must be the number of entries you check for
- # extended_key_usage and extended_key_usage_strict
- - "'DVCS' in result.extended_key_usage"
- - "result.extended_key_usage | length == 1" # the number must be the number of entries you check for
- # subject_alt_name and subject_alt_name_strict
- - "'dns:ansible.com' in result.subject_alt_name"
- - "result.subject_alt_name | length == 1" # the number must be the number of entries you check for
- # not_before and not_after
- - "result.not_before == '20190331202428Z'"
- - "result.not_after == '20190413202428Z'"
- # valid_at, invalid_at and valid_in
- - "result.valid_at.one_day_ten_hours" # for valid_at
- - "not result.valid_at.fixed_timestamp" # for invalid_at
- - "result.valid_at.ten_seconds" # for valid_in
-
-# Examples for some checks one could use the assertonly provider for:
-# (Please note that assertonly has been deprecated!)
-
-# How to use the assertonly provider to implement and trigger your own custom certificate generation workflow:
-- name: Check if a certificate is currently still valid, ignoring failures
- openssl_certificate:
- path: /etc/ssl/crt/example.com.crt
- provider: assertonly
- has_expired: no
- ignore_errors: yes
- register: validity_check
-
-- name: Run custom task(s) to get a new, valid certificate in case the initial check failed
- command: superspecialSSL recreate /etc/ssl/crt/example.com.crt
- when: validity_check.failed
-
-- name: Check the new certificate again for validity with the same parameters, this time failing the play if it is still invalid
- openssl_certificate:
- path: /etc/ssl/crt/example.com.crt
- provider: assertonly
- has_expired: no
- when: validity_check.failed
-
-# Some other checks that assertonly could be used for:
-- name: Verify that an existing certificate was issued by the Let's Encrypt CA and is currently still valid
- openssl_certificate:
- path: /etc/ssl/crt/example.com.crt
- provider: assertonly
- issuer:
- O: Let's Encrypt
- has_expired: no
-
-- name: Ensure that a certificate uses a modern signature algorithm (no SHA1, MD5 or DSA)
- openssl_certificate:
- path: /etc/ssl/crt/example.com.crt
- provider: assertonly
- signature_algorithms:
- - sha224WithRSAEncryption
- - sha256WithRSAEncryption
- - sha384WithRSAEncryption
- - sha512WithRSAEncryption
- - sha224WithECDSAEncryption
- - sha256WithECDSAEncryption
- - sha384WithECDSAEncryption
- - sha512WithECDSAEncryption
-
-- name: Ensure that the existing certificate belongs to the specified private key
- openssl_certificate:
- path: /etc/ssl/crt/example.com.crt
- privatekey_path: /etc/ssl/private/example.com.pem
- provider: assertonly
-
-- name: Ensure that the existing certificate is still valid at the winter solstice 2017
- openssl_certificate:
- path: /etc/ssl/crt/example.com.crt
- provider: assertonly
- valid_at: 20171221162800Z
-
-- name: Ensure that the existing certificate is still valid 2 weeks (1209600 seconds) from now
- openssl_certificate:
- path: /etc/ssl/crt/example.com.crt
- provider: assertonly
- valid_in: 1209600
-
-- name: Ensure that the existing certificate is only used for digital signatures and encrypting other keys
- openssl_certificate:
- path: /etc/ssl/crt/example.com.crt
- provider: assertonly
- key_usage:
- - digitalSignature
- - keyEncipherment
- key_usage_strict: true
-
-- name: Ensure that the existing certificate can be used for client authentication
- openssl_certificate:
- path: /etc/ssl/crt/example.com.crt
- provider: assertonly
- extended_key_usage:
- - clientAuth
-
-- name: Ensure that the existing certificate can only be used for client authentication and time stamping
- openssl_certificate:
- path: /etc/ssl/crt/example.com.crt
- provider: assertonly
- extended_key_usage:
- - clientAuth
- - 1.3.6.1.5.5.7.3.8
- extended_key_usage_strict: true
-
-- name: Ensure that the existing certificate has a certain domain in its subjectAltName
- openssl_certificate:
- path: /etc/ssl/crt/example.com.crt
- provider: assertonly
- subject_alt_name:
- - www.example.com
- - test.example.com
-'''
-
-RETURN = r'''
-filename:
- description: Path to the generated certificate.
- returned: changed or success
- type: str
- sample: /etc/ssl/crt/www.ansible.com.crt
-backup_file:
- description: Name of backup file created.
- returned: changed and if I(backup) is C(yes)
- type: str
- sample: /path/to/www.ansible.com.crt.2019-03-09@11:22~
-certificate:
- description: The (current or generated) certificate's content.
- returned: if I(state) is C(present) and I(return_content) is C(yes)
- type: str
- version_added: "2.10"
-'''
-
-
-from random import randint
-import abc
-import datetime
-import time
-import os
-import tempfile
-import traceback
-from distutils.version import LooseVersion
-
-from ansible.module_utils import crypto as crypto_utils
-from ansible.module_utils.basic import AnsibleModule, missing_required_lib
-from ansible.module_utils._text import to_native, to_bytes, to_text
-from ansible.module_utils.compat import ipaddress as compat_ipaddress
-from ansible.module_utils.ecs.api import ECSClient, RestOperationException, SessionConfigurationException
-
-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
-try:
- import cryptography
- from cryptography import x509
- from cryptography.hazmat.backends import default_backend
- from cryptography.hazmat.primitives.serialization import Encoding
- from cryptography.x509 import NameAttribute, Name
- from cryptography.x509.oid import NameOID
- CRYPTOGRAPHY_VERSION = LooseVersion(cryptography.__version__)
-except ImportError:
- CRYPTOGRAPHY_IMP_ERR = traceback.format_exc()
- CRYPTOGRAPHY_FOUND = False
-else:
- CRYPTOGRAPHY_FOUND = True
-
-
-class CertificateError(crypto_utils.OpenSSLObjectError):
- pass
-
-
-class Certificate(crypto_utils.OpenSSLObject):
-
- def __init__(self, module, backend):
- super(Certificate, self).__init__(
- module.params['path'],
- module.params['state'],
- module.params['force'],
- module.check_mode
- )
-
- self.provider = module.params['provider']
- self.privatekey_path = module.params['privatekey_path']
- self.privatekey_content = module.params['privatekey_content']
- if self.privatekey_content is not None:
- self.privatekey_content = self.privatekey_content.encode('utf-8')
- self.privatekey_passphrase = module.params['privatekey_passphrase']
- self.csr_path = module.params['csr_path']
- self.csr_content = module.params['csr_content']
- if self.csr_content is not None:
- self.csr_content = self.csr_content.encode('utf-8')
- self.cert = None
- self.privatekey = None
- self.csr = None
- self.backend = backend
- self.module = module
- self.return_content = module.params['return_content']
-
- # The following are default values which make sure check() works as
- # before if providers do not explicitly change these properties.
- self.create_subject_key_identifier = 'never_create'
- self.create_authority_key_identifier = False
-
- self.backup = module.params['backup']
- self.backup_file = None
-
- def _validate_privatekey(self):
- if self.backend == 'pyopenssl':
- ctx = OpenSSL.SSL.Context(OpenSSL.SSL.TLSv1_2_METHOD)
- ctx.use_privatekey(self.privatekey)
- ctx.use_certificate(self.cert)
- try:
- ctx.check_privatekey()
- return True
- except OpenSSL.SSL.Error:
- return False
- elif self.backend == 'cryptography':
- return crypto_utils.cryptography_compare_public_keys(self.cert.public_key(), self.privatekey.public_key())
-
- def _validate_csr(self):
- if self.backend == 'pyopenssl':
- # Verify that CSR is signed by certificate's private key
- try:
- self.csr.verify(self.cert.get_pubkey())
- except OpenSSL.crypto.Error:
- return False
- # Check subject
- if self.csr.get_subject() != self.cert.get_subject():
- return False
- # Check extensions
- csr_extensions = self.csr.get_extensions()
- cert_extension_count = self.cert.get_extension_count()
- if len(csr_extensions) != cert_extension_count:
- return False
- for extension_number in range(0, cert_extension_count):
- cert_extension = self.cert.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
- if not self.csr.is_signature_valid:
- return False
- if not crypto_utils.cryptography_compare_public_keys(self.csr.public_key(), self.cert.public_key()):
- return False
- # Check subject
- if self.csr.subject != self.cert.subject:
- return False
- # Check extensions
- cert_exts = list(self.cert.extensions)
- csr_exts = list(self.csr.extensions)
- if self.create_subject_key_identifier != 'never_create':
- # Filter out SubjectKeyIdentifier extension before comparison
- cert_exts = list(filter(lambda x: not isinstance(x.value, x509.SubjectKeyIdentifier), cert_exts))
- csr_exts = list(filter(lambda x: not isinstance(x.value, x509.SubjectKeyIdentifier), csr_exts))
- if self.create_authority_key_identifier:
- # Filter out AuthorityKeyIdentifier extension before comparison
- cert_exts = list(filter(lambda x: not isinstance(x.value, x509.AuthorityKeyIdentifier), cert_exts))
- csr_exts = list(filter(lambda x: not isinstance(x.value, x509.AuthorityKeyIdentifier), csr_exts))
- if len(cert_exts) != len(csr_exts):
- return False
- for cert_ext in cert_exts:
- try:
- csr_ext = self.csr.extensions.get_extension_for_oid(cert_ext.oid)
- if cert_ext != csr_ext:
- return False
- except cryptography.x509.ExtensionNotFound as dummy:
- return False
- return True
-
- def remove(self, module):
- if self.backup:
- self.backup_file = module.backup_local(self.path)
- super(Certificate, self).remove(module)
-
- def check(self, module, perms_required=True):
- """Ensure the resource is in its desired state."""
-
- state_and_perms = super(Certificate, self).check(module, perms_required)
-
- if not state_and_perms:
- return False
-
- try:
- self.cert = crypto_utils.load_certificate(self.path, backend=self.backend)
- except Exception as dummy:
- return False
-
- if self.privatekey_path or self.privatekey_content:
- try:
- self.privatekey = crypto_utils.load_privatekey(
- path=self.privatekey_path,
- content=self.privatekey_content,
- passphrase=self.privatekey_passphrase,
- backend=self.backend
- )
- except crypto_utils.OpenSSLBadPassphraseError as exc:
- raise CertificateError(exc)
- if not self._validate_privatekey():
- return False
-
- if self.csr_path or self.csr_content:
- self.csr = crypto_utils.load_certificate_request(
- path=self.csr_path,
- content=self.csr_content,
- backend=self.backend
- )
- if not self._validate_csr():
- return False
-
- # Check SubjectKeyIdentifier
- if self.backend == 'cryptography' and self.create_subject_key_identifier != 'never_create':
- # Get hold of certificate's SKI
- try:
- ext = self.cert.extensions.get_extension_for_class(x509.SubjectKeyIdentifier)
- except cryptography.x509.ExtensionNotFound as dummy:
- return False
- # Get hold of CSR's SKI for 'create_if_not_provided'
- csr_ext = None
- if self.create_subject_key_identifier == 'create_if_not_provided':
- try:
- csr_ext = self.csr.extensions.get_extension_for_class(x509.SubjectKeyIdentifier)
- except cryptography.x509.ExtensionNotFound as dummy:
- pass
- if csr_ext is None:
- # If CSR had no SKI, or we chose to ignore it ('always_create'), compare with created SKI
- if ext.value.digest != x509.SubjectKeyIdentifier.from_public_key(self.cert.public_key()).digest:
- return False
- else:
- # If CSR had SKI and we didn't ignore it ('create_if_not_provided'), compare SKIs
- if ext.value.digest != csr_ext.value.digest:
- return False
-
- return True
-
-
-class CertificateAbsent(Certificate):
- def __init__(self, module):
- super(CertificateAbsent, self).__init__(module, 'cryptography') # backend doesn't matter
-
- def generate(self, module):
- pass
-
- def dump(self, check_mode=False):
- # Use only for absent
-
- result = {
- 'changed': self.changed,
- 'filename': self.path,
- 'privatekey': self.privatekey_path,
- 'csr': self.csr_path
- }
- if self.backup_file:
- result['backup_file'] = self.backup_file
- if self.return_content:
- result['certificate'] = None
-
- return result
-
-
-class SelfSignedCertificateCryptography(Certificate):
- """Generate the self-signed certificate, using the cryptography backend"""
- def __init__(self, module):
- super(SelfSignedCertificateCryptography, self).__init__(module, 'cryptography')
- self.create_subject_key_identifier = module.params['selfsigned_create_subject_key_identifier']
- self.notBefore = crypto_utils.get_relative_time_option(module.params['selfsigned_not_before'], 'selfsigned_not_before', backend=self.backend)
- self.notAfter = crypto_utils.get_relative_time_option(module.params['selfsigned_not_after'], 'selfsigned_not_after', backend=self.backend)
- self.digest = crypto_utils.select_message_digest(module.params['selfsigned_digest'])
- self.version = module.params['selfsigned_version']
- self.serial_number = x509.random_serial_number()
-
- 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.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.csr = crypto_utils.load_certificate_request(
- path=self.csr_path,
- content=self.csr_content,
- backend=self.backend
- )
- self._module = module
-
- try:
- self.privatekey = crypto_utils.load_privatekey(
- path=self.privatekey_path,
- content=self.privatekey_content,
- passphrase=self.privatekey_passphrase,
- backend=self.backend
- )
- except crypto_utils.OpenSSLBadPassphraseError as exc:
- module.fail_json(msg=to_native(exc))
-
- if crypto_utils.cryptography_key_needs_digest_for_signing(self.privatekey):
- if self.digest is None:
- raise CertificateError(
- 'The digest %s is not supported with the cryptography backend' % module.params['selfsigned_digest']
- )
- else:
- self.digest = None
-
- def generate(self, module):
- if self.privatekey_content is None and not os.path.exists(self.privatekey_path):
- raise CertificateError(
- 'The private key %s does not exist' % self.privatekey_path
- )
- if self.csr_content is None and not os.path.exists(self.csr_path):
- raise CertificateError(
- 'The certificate signing request file %s does not exist' % self.csr_path
- )
- if not self.check(module, perms_required=False) or self.force:
- try:
- cert_builder = x509.CertificateBuilder()
- cert_builder = cert_builder.subject_name(self.csr.subject)
- cert_builder = cert_builder.issuer_name(self.csr.subject)
- cert_builder = cert_builder.serial_number(self.serial_number)
- cert_builder = cert_builder.not_valid_before(self.notBefore)
- cert_builder = cert_builder.not_valid_after(self.notAfter)
- cert_builder = cert_builder.public_key(self.privatekey.public_key())
- has_ski = False
- for extension in self.csr.extensions:
- if isinstance(extension.value, x509.SubjectKeyIdentifier):
- if self.create_subject_key_identifier == 'always_create':
- continue
- has_ski = True
- cert_builder = cert_builder.add_extension(extension.value, critical=extension.critical)
- if not has_ski and self.create_subject_key_identifier != 'never_create':
- cert_builder = cert_builder.add_extension(
- x509.SubjectKeyIdentifier.from_public_key(self.privatekey.public_key()),
- critical=False
- )
- except ValueError as e:
- raise CertificateError(str(e))
-
- try:
- certificate = cert_builder.sign(
- private_key=self.privatekey, algorithm=self.digest,
- backend=default_backend()
- )
- except TypeError as e:
- if str(e) == 'Algorithm must be a registered hash algorithm.' and self.digest is None:
- module.fail_json(msg='Signing with Ed25519 and Ed448 keys requires cryptography 2.8 or newer.')
- raise
-
- self.cert = certificate
-
- if self.backup:
- self.backup_file = module.backup_local(self.path)
- crypto_utils.write_file(module, certificate.public_bytes(Encoding.PEM))
- self.changed = True
- else:
- self.cert = crypto_utils.load_certificate(self.path, backend=self.backend)
-
- file_args = module.load_file_common_arguments(module.params)
- if module.set_fs_attributes_if_different(file_args, False):
- self.changed = True
-
- def dump(self, check_mode=False):
-
- result = {
- 'changed': self.changed,
- 'filename': self.path,
- 'privatekey': self.privatekey_path,
- 'csr': self.csr_path
- }
- if self.backup_file:
- result['backup_file'] = self.backup_file
- if self.return_content:
- content = crypto_utils.load_file_if_exists(self.path, ignore_errors=True)
- result['certificate'] = content.decode('utf-8') if content else None
-
- if check_mode:
- result.update({
- 'notBefore': self.notBefore.strftime("%Y%m%d%H%M%SZ"),
- 'notAfter': self.notAfter.strftime("%Y%m%d%H%M%SZ"),
- 'serial_number': self.serial_number,
- })
- else:
- result.update({
- 'notBefore': self.cert.not_valid_before.strftime("%Y%m%d%H%M%SZ"),
- 'notAfter': self.cert.not_valid_after.strftime("%Y%m%d%H%M%SZ"),
- 'serial_number': self.cert.serial_number,
- })
-
- return result
-
-
-class SelfSignedCertificate(Certificate):
- """Generate the self-signed certificate."""
-
- def __init__(self, module):
- super(SelfSignedCertificate, self).__init__(module, '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 = crypto_utils.get_relative_time_option(module.params['selfsigned_not_before'], 'selfsigned_not_before', backend=self.backend)
- self.notAfter = crypto_utils.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 = randint(1000, 99999)
-
- 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.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.csr = crypto_utils.load_certificate_request(
- path=self.csr_path,
- content=self.csr_content,
- )
- try:
- self.privatekey = crypto_utils.load_privatekey(
- path=self.privatekey_path,
- content=self.privatekey_content,
- passphrase=self.privatekey_passphrase,
- )
- except crypto_utils.OpenSSLBadPassphraseError as exc:
- module.fail_json(msg=str(exc))
-
- def generate(self, module):
-
- if self.privatekey_content is None and not os.path.exists(self.privatekey_path):
- raise CertificateError(
- 'The private key %s does not exist' % self.privatekey_path
- )
-
- if self.csr_content is None and not os.path.exists(self.csr_path):
- raise CertificateError(
- 'The certificate signing request file %s does not exist' % self.csr_path
- )
-
- if not self.check(module, perms_required=False) or self.force:
- cert = 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
-
- if self.backup:
- self.backup_file = module.backup_local(self.path)
- crypto_utils.write_file(module, crypto.dump_certificate(crypto.FILETYPE_PEM, self.cert))
- self.changed = True
-
- file_args = module.load_file_common_arguments(module.params)
- if module.set_fs_attributes_if_different(file_args, False):
- self.changed = True
-
- def dump(self, check_mode=False):
-
- result = {
- 'changed': self.changed,
- 'filename': self.path,
- 'privatekey': self.privatekey_path,
- 'csr': self.csr_path
- }
- if self.backup_file:
- result['backup_file'] = self.backup_file
- if self.return_content:
- content = crypto_utils.load_file_if_exists(self.path, ignore_errors=True)
- result['certificate'] = content.decode('utf-8') if content else None
-
- if check_mode:
- result.update({
- 'notBefore': self.notBefore,
- 'notAfter': self.notAfter,
- 'serial_number': self.serial_number,
- })
- else:
- result.update({
- 'notBefore': self.cert.get_notBefore(),
- 'notAfter': self.cert.get_notAfter(),
- 'serial_number': self.cert.get_serial_number(),
- })
-
- return result
-
-
-class OwnCACertificateCryptography(Certificate):
- """Generate the own CA certificate. Using the cryptography backend"""
- def __init__(self, module):
- super(OwnCACertificateCryptography, self).__init__(module, 'cryptography')
- self.create_subject_key_identifier = module.params['ownca_create_subject_key_identifier']
- self.create_authority_key_identifier = module.params['ownca_create_authority_key_identifier']
- self.notBefore = crypto_utils.get_relative_time_option(module.params['ownca_not_before'], 'ownca_not_before', backend=self.backend)
- self.notAfter = crypto_utils.get_relative_time_option(module.params['ownca_not_after'], 'ownca_not_after', backend=self.backend)
- self.digest = crypto_utils.select_message_digest(module.params['ownca_digest'])
- self.version = module.params['ownca_version']
- self.serial_number = x509.random_serial_number()
- self.ca_cert_path = module.params['ownca_path']
- self.ca_cert_content = 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 = module.params['ownca_privatekey_path']
- self.ca_privatekey_content = 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 = 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.csr = crypto_utils.load_certificate_request(
- path=self.csr_path,
- content=self.csr_content,
- backend=self.backend
- )
- self.ca_cert = crypto_utils.load_certificate(
- path=self.ca_cert_path,
- content=self.ca_cert_content,
- backend=self.backend
- )
- try:
- self.ca_private_key = crypto_utils.load_privatekey(
- path=self.ca_privatekey_path,
- content=self.ca_privatekey_content,
- passphrase=self.ca_privatekey_passphrase,
- backend=self.backend
- )
- except crypto_utils.OpenSSLBadPassphraseError as exc:
- module.fail_json(msg=str(exc))
-
- if crypto_utils.cryptography_key_needs_digest_for_signing(self.ca_private_key):
- if self.digest is None:
- raise CertificateError(
- 'The digest %s is not supported with the cryptography backend' % module.params['ownca_digest']
- )
- else:
- self.digest = None
-
- def generate(self, module):
-
- if self.ca_cert_content is None and not os.path.exists(self.ca_cert_path):
- raise CertificateError(
- 'The CA certificate %s does not exist' % 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 %s does not exist' % self.ca_privatekey_path
- )
-
- if self.csr_content is None and not os.path.exists(self.csr_path):
- raise CertificateError(
- 'The certificate signing request file %s does not exist' % self.csr_path
- )
-
- if not self.check(module, perms_required=False) or self.force:
- cert_builder = x509.CertificateBuilder()
- cert_builder = cert_builder.subject_name(self.csr.subject)
- cert_builder = cert_builder.issuer_name(self.ca_cert.subject)
- cert_builder = cert_builder.serial_number(self.serial_number)
- cert_builder = cert_builder.not_valid_before(self.notBefore)
- cert_builder = cert_builder.not_valid_after(self.notAfter)
- cert_builder = cert_builder.public_key(self.csr.public_key())
- has_ski = False
- for extension in self.csr.extensions:
- if isinstance(extension.value, x509.SubjectKeyIdentifier):
- if self.create_subject_key_identifier == 'always_create':
- continue
- has_ski = True
- if self.create_authority_key_identifier and isinstance(extension.value, x509.AuthorityKeyIdentifier):
- continue
- cert_builder = cert_builder.add_extension(extension.value, critical=extension.critical)
- if not has_ski and self.create_subject_key_identifier != 'never_create':
- cert_builder = cert_builder.add_extension(
- x509.SubjectKeyIdentifier.from_public_key(self.csr.public_key()),
- critical=False
- )
- if self.create_authority_key_identifier:
- try:
- ext = self.ca_cert.extensions.get_extension_for_class(x509.SubjectKeyIdentifier)
- cert_builder = cert_builder.add_extension(
- x509.AuthorityKeyIdentifier.from_issuer_subject_key_identifier(ext.value)
- if CRYPTOGRAPHY_VERSION >= LooseVersion('2.7') else
- x509.AuthorityKeyIdentifier.from_issuer_subject_key_identifier(ext),
- critical=False
- )
- except cryptography.x509.ExtensionNotFound:
- cert_builder = cert_builder.add_extension(
- x509.AuthorityKeyIdentifier.from_issuer_public_key(self.ca_cert.public_key()),
- critical=False
- )
-
- try:
- certificate = cert_builder.sign(
- private_key=self.ca_private_key, algorithm=self.digest,
- backend=default_backend()
- )
- except TypeError as e:
- if str(e) == 'Algorithm must be a registered hash algorithm.' and self.digest is None:
- module.fail_json(msg='Signing with Ed25519 and Ed448 keys requires cryptography 2.8 or newer.')
- raise
-
- self.cert = certificate
-
- if self.backup:
- self.backup_file = module.backup_local(self.path)
- crypto_utils.write_file(module, certificate.public_bytes(Encoding.PEM))
- self.changed = True
- else:
- self.cert = crypto_utils.load_certificate(self.path, backend=self.backend)
-
- file_args = module.load_file_common_arguments(module.params)
- if module.set_fs_attributes_if_different(file_args, False):
- self.changed = True
-
- def check(self, module, perms_required=True):
- """Ensure the resource is in its desired state."""
-
- if not super(OwnCACertificateCryptography, self).check(module, perms_required):
- return False
-
- # Check AuthorityKeyIdentifier
- if self.create_authority_key_identifier:
- try:
- ext = self.ca_cert.extensions.get_extension_for_class(x509.SubjectKeyIdentifier)
- expected_ext = (
- x509.AuthorityKeyIdentifier.from_issuer_subject_key_identifier(ext.value)
- if CRYPTOGRAPHY_VERSION >= LooseVersion('2.7') else
- x509.AuthorityKeyIdentifier.from_issuer_subject_key_identifier(ext)
- )
- except cryptography.x509.ExtensionNotFound:
- expected_ext = x509.AuthorityKeyIdentifier.from_issuer_public_key(self.ca_cert.public_key())
- try:
- ext = self.cert.extensions.get_extension_for_class(x509.AuthorityKeyIdentifier)
- if ext.value != expected_ext:
- return False
- except cryptography.x509.ExtensionNotFound as dummy:
- return False
-
- return True
-
- def dump(self, check_mode=False):
-
- result = {
- 'changed': self.changed,
- 'filename': self.path,
- 'privatekey': self.privatekey_path,
- 'csr': self.csr_path,
- 'ca_cert': self.ca_cert_path,
- 'ca_privatekey': self.ca_privatekey_path
- }
- if self.backup_file:
- result['backup_file'] = self.backup_file
- if self.return_content:
- content = crypto_utils.load_file_if_exists(self.path, ignore_errors=True)
- result['certificate'] = content.decode('utf-8') if content else None
-
- if check_mode:
- result.update({
- 'notBefore': self.notBefore.strftime("%Y%m%d%H%M%SZ"),
- 'notAfter': self.notAfter.strftime("%Y%m%d%H%M%SZ"),
- 'serial_number': self.serial_number,
- })
- else:
- result.update({
- 'notBefore': self.cert.not_valid_before.strftime("%Y%m%d%H%M%SZ"),
- 'notAfter': self.cert.not_valid_after.strftime("%Y%m%d%H%M%SZ"),
- 'serial_number': self.cert.serial_number,
- })
-
- return result
-
-
-class OwnCACertificate(Certificate):
- """Generate the own CA certificate."""
-
- def __init__(self, module):
- super(OwnCACertificate, self).__init__(module, 'pyopenssl')
- self.notBefore = crypto_utils.get_relative_time_option(module.params['ownca_not_before'], 'ownca_not_before', backend=self.backend)
- self.notAfter = crypto_utils.get_relative_time_option(module.params['ownca_not_after'], 'ownca_not_after', backend=self.backend)
- self.digest = module.params['ownca_digest']
- self.version = module.params['ownca_version']
- self.serial_number = randint(1000, 99999)
- if module.params['ownca_create_subject_key_identifier'] != 'create_if_not_provided':
- module.fail_json(msg='ownca_create_subject_key_identifier cannot be used with the pyOpenSSL backend!')
- if module.params['ownca_create_authority_key_identifier']:
- module.warn('ownca_create_authority_key_identifier is ignored by the pyOpenSSL backend!')
- self.ca_cert_path = module.params['ownca_path']
- self.ca_cert_content = 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 = module.params['ownca_privatekey_path']
- self.ca_privatekey_content = 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 = 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.csr = crypto_utils.load_certificate_request(
- path=self.csr_path,
- content=self.csr_content,
- )
- self.ca_cert = crypto_utils.load_certificate(
- path=self.ca_cert_path,
- content=self.ca_cert_content,
- )
- try:
- self.ca_privatekey = crypto_utils.load_privatekey(
- path=self.ca_privatekey_path,
- content=self.ca_privatekey_content,
- passphrase=self.ca_privatekey_passphrase
- )
- except crypto_utils.OpenSSLBadPassphraseError as exc:
- module.fail_json(msg=str(exc))
-
- def generate(self, module):
-
- if self.ca_cert_content is None and not os.path.exists(self.ca_cert_path):
- raise CertificateError(
- 'The CA certificate %s does not exist' % 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 %s does not exist' % self.ca_privatekey_path
- )
-
- if self.csr_content is None and not os.path.exists(self.csr_path):
- raise CertificateError(
- 'The certificate signing request file %s does not exist' % self.csr_path
- )
-
- if not self.check(module, perms_required=False) or self.force:
- cert = 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
-
- if self.backup:
- self.backup_file = module.backup_local(self.path)
- crypto_utils.write_file(module, crypto.dump_certificate(crypto.FILETYPE_PEM, self.cert))
- self.changed = True
-
- file_args = module.load_file_common_arguments(module.params)
- if module.set_fs_attributes_if_different(file_args, False):
- self.changed = True
-
- def dump(self, check_mode=False):
-
- result = {
- 'changed': self.changed,
- 'filename': self.path,
- 'privatekey': self.privatekey_path,
- 'csr': self.csr_path,
- 'ca_cert': self.ca_cert_path,
- 'ca_privatekey': self.ca_privatekey_path
- }
- if self.backup_file:
- result['backup_file'] = self.backup_file
- if self.return_content:
- content = crypto_utils.load_file_if_exists(self.path, ignore_errors=True)
- result['certificate'] = content.decode('utf-8') if content else None
-
- if check_mode:
- result.update({
- 'notBefore': self.notBefore,
- 'notAfter': self.notAfter,
- 'serial_number': self.serial_number,
- })
- else:
- result.update({
- 'notBefore': self.cert.get_notBefore(),
- 'notAfter': self.cert.get_notAfter(),
- 'serial_number': self.cert.get_serial_number(),
- })
-
- return result
-
-
-def compare_sets(subset, superset, equality=False):
- if equality:
- return set(subset) == set(superset)
- else:
- return all(x in superset for x in subset)
-
-
-def compare_dicts(subset, superset, equality=False):
- if equality:
- return subset == superset
- else:
- return all(superset.get(x) == v for x, v in subset.items())
-
-
-NO_EXTENSION = 'no extension'
-
-
-class AssertOnlyCertificateBase(Certificate):
-
- def __init__(self, module, backend):
- super(AssertOnlyCertificateBase, self).__init__(module, backend)
-
- self.signature_algorithms = module.params['signature_algorithms']
- if module.params['subject']:
- self.subject = crypto_utils.parse_name_field(module.params['subject'])
- else:
- self.subject = []
- self.subject_strict = module.params['subject_strict']
- if module.params['issuer']:
- self.issuer = crypto_utils.parse_name_field(module.params['issuer'])
- else:
- self.issuer = []
- self.issuer_strict = module.params['issuer_strict']
- self.has_expired = module.params['has_expired']
- self.version = module.params['version']
- self.key_usage = module.params['key_usage']
- self.key_usage_strict = module.params['key_usage_strict']
- self.extended_key_usage = module.params['extended_key_usage']
- self.extended_key_usage_strict = module.params['extended_key_usage_strict']
- self.subject_alt_name = module.params['subject_alt_name']
- self.subject_alt_name_strict = module.params['subject_alt_name_strict']
- self.not_before = module.params['not_before']
- self.not_after = module.params['not_after']
- self.valid_at = module.params['valid_at']
- self.invalid_at = module.params['invalid_at']
- self.valid_in = module.params['valid_in']
- if self.valid_in and not self.valid_in.startswith("+") and not self.valid_in.startswith("-"):
- try:
- int(self.valid_in)
- except ValueError:
- module.fail_json(msg='The supplied value for "valid_in" (%s) is not an integer or a valid timespec' % self.valid_in)
- self.valid_in = "+" + self.valid_in + "s"
-
- # Load objects
- self.cert = crypto_utils.load_certificate(self.path, backend=self.backend)
- if self.privatekey_path is not None or self.privatekey_content is not None:
- try:
- self.privatekey = crypto_utils.load_privatekey(
- path=self.privatekey_path,
- content=self.privatekey_content,
- passphrase=self.privatekey_passphrase,
- backend=self.backend
- )
- except crypto_utils.OpenSSLBadPassphraseError as exc:
- raise CertificateError(exc)
- if self.csr_path is not None or self.csr_content is not None:
- self.csr = crypto_utils.load_certificate_request(
- path=self.csr_path,
- content=self.csr_content,
- backend=self.backend
- )
-
- @abc.abstractmethod
- def _validate_privatekey(self):
- pass
-
- @abc.abstractmethod
- def _validate_csr_signature(self):
- pass
-
- @abc.abstractmethod
- def _validate_csr_subject(self):
- pass
-
- @abc.abstractmethod
- def _validate_csr_extensions(self):
- pass
-
- @abc.abstractmethod
- def _validate_signature_algorithms(self):
- pass
-
- @abc.abstractmethod
- def _validate_subject(self):
- pass
-
- @abc.abstractmethod
- def _validate_issuer(self):
- pass
-
- @abc.abstractmethod
- def _validate_has_expired(self):
- pass
-
- @abc.abstractmethod
- def _validate_version(self):
- pass
-
- @abc.abstractmethod
- def _validate_key_usage(self):
- pass
-
- @abc.abstractmethod
- def _validate_extended_key_usage(self):
- pass
-
- @abc.abstractmethod
- def _validate_subject_alt_name(self):
- pass
-
- @abc.abstractmethod
- def _validate_not_before(self):
- pass
-
- @abc.abstractmethod
- def _validate_not_after(self):
- pass
-
- @abc.abstractmethod
- def _validate_valid_at(self):
- pass
-
- @abc.abstractmethod
- def _validate_invalid_at(self):
- pass
-
- @abc.abstractmethod
- def _validate_valid_in(self):
- pass
-
- def assertonly(self, module):
- messages = []
- if self.privatekey_path is not None or self.privatekey_content is not None:
- if not self._validate_privatekey():
- messages.append(
- 'Certificate %s and private key %s do not match' %
- (self.path, self.privatekey_path or '(provided in module options)')
- )
-
- if self.csr_path is not None or self.csr_content is not None:
- if not self._validate_csr_signature():
- messages.append(
- 'Certificate %s and CSR %s do not match: private key mismatch' %
- (self.path, self.csr_path or '(provided in module options)')
- )
- if not self._validate_csr_subject():
- messages.append(
- 'Certificate %s and CSR %s do not match: subject mismatch' %
- (self.path, self.csr_path or '(provided in module options)')
- )
- if not self._validate_csr_extensions():
- messages.append(
- 'Certificate %s and CSR %s do not match: extensions mismatch' %
- (self.path, self.csr_path or '(provided in module options)')
- )
-
- if self.signature_algorithms is not None:
- wrong_alg = self._validate_signature_algorithms()
- if wrong_alg:
- messages.append(
- 'Invalid signature algorithm (got %s, expected one of %s)' %
- (wrong_alg, self.signature_algorithms)
- )
-
- if self.subject is not None:
- failure = self._validate_subject()
- if failure:
- dummy, cert_subject = failure
- messages.append(
- 'Invalid subject component (got %s, expected all of %s to be present)' %
- (cert_subject, self.subject)
- )
-
- if self.issuer is not None:
- failure = self._validate_issuer()
- if failure:
- dummy, cert_issuer = failure
- messages.append(
- 'Invalid issuer component (got %s, expected all of %s to be present)' % (cert_issuer, self.issuer)
- )
-
- if self.has_expired is not None:
- cert_expired = self._validate_has_expired()
- if cert_expired != self.has_expired:
- messages.append(
- 'Certificate expiration check failed (certificate expiration is %s, expected %s)' %
- (cert_expired, self.has_expired)
- )
-
- if self.version is not None:
- cert_version = self._validate_version()
- if cert_version != self.version:
- messages.append(
- 'Invalid certificate version number (got %s, expected %s)' %
- (cert_version, self.version)
- )
-
- if self.key_usage is not None:
- failure = self._validate_key_usage()
- if failure == NO_EXTENSION:
- messages.append('Found no keyUsage extension')
- elif failure:
- dummy, cert_key_usage = failure
- messages.append(
- 'Invalid keyUsage components (got %s, expected all of %s to be present)' %
- (cert_key_usage, self.key_usage)
- )
-
- if self.extended_key_usage is not None:
- failure = self._validate_extended_key_usage()
- if failure == NO_EXTENSION:
- messages.append('Found no extendedKeyUsage extension')
- elif failure:
- dummy, ext_cert_key_usage = failure
- messages.append(
- 'Invalid extendedKeyUsage component (got %s, expected all of %s to be present)' % (ext_cert_key_usage, self.extended_key_usage)
- )
-
- if self.subject_alt_name is not None:
- failure = self._validate_subject_alt_name()
- if failure == NO_EXTENSION:
- messages.append('Found no subjectAltName extension')
- elif failure:
- dummy, cert_san = failure
- messages.append(
- 'Invalid subjectAltName component (got %s, expected all of %s to be present)' %
- (cert_san, self.subject_alt_name)
- )
-
- if self.not_before is not None:
- cert_not_valid_before = self._validate_not_before()
- if cert_not_valid_before != crypto_utils.get_relative_time_option(self.not_before, 'not_before', backend=self.backend):
- messages.append(
- 'Invalid not_before component (got %s, expected %s to be present)' %
- (cert_not_valid_before, self.not_before)
- )
-
- if self.not_after is not None:
- cert_not_valid_after = self._validate_not_after()
- if cert_not_valid_after != crypto_utils.get_relative_time_option(self.not_after, 'not_after', backend=self.backend):
- messages.append(
- 'Invalid not_after component (got %s, expected %s to be present)' %
- (cert_not_valid_after, self.not_after)
- )
-
- if self.valid_at is not None:
- not_before, valid_at, not_after = self._validate_valid_at()
- if not (not_before <= valid_at <= not_after):
- messages.append(
- 'Certificate is not valid for the specified date (%s) - not_before: %s - not_after: %s' %
- (self.valid_at, not_before, not_after)
- )
-
- if self.invalid_at is not None:
- not_before, invalid_at, not_after = self._validate_invalid_at()
- if not_before <= invalid_at <= not_after:
- messages.append(
- 'Certificate is not invalid for the specified date (%s) - not_before: %s - not_after: %s' %
- (self.invalid_at, not_before, not_after)
- )
-
- if self.valid_in is not None:
- not_before, valid_in, not_after = self._validate_valid_in()
- if not not_before <= valid_in <= not_after:
- messages.append(
- 'Certificate is not valid in %s from now (that would be %s) - not_before: %s - not_after: %s' %
- (self.valid_in, valid_in, not_before, not_after)
- )
- return messages
-
- def generate(self, module):
- """Don't generate anything - only assert"""
- messages = self.assertonly(module)
- if messages:
- module.fail_json(msg=' | '.join(messages))
-
- def check(self, module, perms_required=False):
- """Ensure the resource is in its desired state."""
- messages = self.assertonly(module)
- return len(messages) == 0
-
- def dump(self, check_mode=False):
- result = {
- 'changed': self.changed,
- 'filename': self.path,
- 'privatekey': self.privatekey_path,
- 'csr': self.csr_path,
- }
- if self.return_content:
- content = crypto_utils.load_file_if_exists(self.path, ignore_errors=True)
- result['certificate'] = content.decode('utf-8') if content else None
- return result
-
-
-class AssertOnlyCertificateCryptography(AssertOnlyCertificateBase):
- """Validate the supplied cert, using the cryptography backend"""
- def __init__(self, module):
- super(AssertOnlyCertificateCryptography, self).__init__(module, 'cryptography')
-
- def _validate_privatekey(self):
- return crypto_utils.cryptography_compare_public_keys(self.cert.public_key(), self.privatekey.public_key())
-
- def _validate_csr_signature(self):
- if not self.csr.is_signature_valid:
- return False
- return crypto_utils.cryptography_compare_public_keys(self.csr.public_key(), self.cert.public_key())
-
- def _validate_csr_subject(self):
- return self.csr.subject == self.cert.subject
-
- def _validate_csr_extensions(self):
- cert_exts = self.cert.extensions
- csr_exts = self.csr.extensions
- if len(cert_exts) != len(csr_exts):
- return False
- for cert_ext in cert_exts:
- try:
- csr_ext = csr_exts.get_extension_for_oid(cert_ext.oid)
- if cert_ext != csr_ext:
- return False
- except cryptography.x509.ExtensionNotFound as dummy:
- return False
- return True
-
- def _validate_signature_algorithms(self):
- if self.cert.signature_algorithm_oid._name not in self.signature_algorithms:
- return self.cert.signature_algorithm_oid._name
-
- def _validate_subject(self):
- expected_subject = Name([NameAttribute(oid=crypto_utils.cryptography_name_to_oid(sub[0]), value=to_text(sub[1]))
- for sub in self.subject])
- cert_subject = self.cert.subject
- if not compare_sets(expected_subject, cert_subject, self.subject_strict):
- return expected_subject, cert_subject
-
- def _validate_issuer(self):
- expected_issuer = Name([NameAttribute(oid=crypto_utils.cryptography_name_to_oid(iss[0]), value=to_text(iss[1]))
- for iss in self.issuer])
- cert_issuer = self.cert.issuer
- if not compare_sets(expected_issuer, cert_issuer, self.issuer_strict):
- return self.issuer, cert_issuer
-
- def _validate_has_expired(self):
- cert_not_after = self.cert.not_valid_after
- cert_expired = cert_not_after < datetime.datetime.utcnow()
- return cert_expired
-
- def _validate_version(self):
- if self.cert.version == x509.Version.v1:
- return 1
- if self.cert.version == x509.Version.v3:
- return 3
- return "unknown"
-
- def _validate_key_usage(self):
- try:
- current_key_usage = self.cert.extensions.get_extension_for_class(x509.KeyUsage).value
- test_key_usage = dict(
- digital_signature=current_key_usage.digital_signature,
- content_commitment=current_key_usage.content_commitment,
- key_encipherment=current_key_usage.key_encipherment,
- data_encipherment=current_key_usage.data_encipherment,
- key_agreement=current_key_usage.key_agreement,
- key_cert_sign=current_key_usage.key_cert_sign,
- crl_sign=current_key_usage.crl_sign,
- encipher_only=False,
- decipher_only=False
- )
- if test_key_usage['key_agreement']:
- test_key_usage.update(dict(
- encipher_only=current_key_usage.encipher_only,
- decipher_only=current_key_usage.decipher_only
- ))
-
- key_usages = crypto_utils.cryptography_parse_key_usage_params(self.key_usage)
- if not compare_dicts(key_usages, test_key_usage, self.key_usage_strict):
- return self.key_usage, [k for k, v in test_key_usage.items() if v is True]
-
- except cryptography.x509.ExtensionNotFound:
- # 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):
- try:
- current_ext_keyusage = self.cert.extensions.get_extension_for_class(x509.ExtendedKeyUsage).value
- usages = [crypto_utils.cryptography_name_to_oid(usage) for usage in self.extended_key_usage]
- expected_ext_keyusage = x509.ExtendedKeyUsage(usages)
- if not compare_sets(expected_ext_keyusage, current_ext_keyusage, self.extended_key_usage_strict):
- return [eku.value for eku in expected_ext_keyusage], [eku.value for eku in current_ext_keyusage]
-
- except cryptography.x509.ExtensionNotFound:
- # 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):
- try:
- current_san = self.cert.extensions.get_extension_for_class(x509.SubjectAlternativeName).value
- expected_san = [crypto_utils.cryptography_get_name(san) for san in self.subject_alt_name]
- if not compare_sets(expected_san, current_san, self.subject_alt_name_strict):
- return self.subject_alt_name, current_san
- except cryptography.x509.ExtensionNotFound:
- # 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.cert.not_valid_before
-
- def _validate_not_after(self):
- return self.cert.not_valid_after
-
- def _validate_valid_at(self):
- rt = crypto_utils.get_relative_time_option(self.valid_at, 'valid_at', backend=self.backend)
- return self.cert.not_valid_before, rt, self.cert.not_valid_after
-
- def _validate_invalid_at(self):
- rt = crypto_utils.get_relative_time_option(self.invalid_at, 'invalid_at', backend=self.backend)
- return self.cert.not_valid_before, rt, self.cert.not_valid_after
-
- def _validate_valid_in(self):
- valid_in_date = crypto_utils.get_relative_time_option(self.valid_in, "valid_in", backend=self.backend)
- return self.cert.not_valid_before, valid_in_date, self.cert.not_valid_after
-
-
-class AssertOnlyCertificate(AssertOnlyCertificateBase):
- """validate the supplied certificate."""
-
- def __init__(self, module):
- super(AssertOnlyCertificate, 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.cert)
- try:
- ctx.check_privatekey()
- return True
- except OpenSSL.SSL.Error:
- return False
-
- def _validate_csr_signature(self):
- try:
- self.csr.verify(self.cert.get_pubkey())
- except OpenSSL.crypto.Error:
- return False
-
- def _validate_csr_subject(self):
- if self.csr.get_subject() != self.cert.get_subject():
- return False
-
- def _validate_csr_extensions(self):
- csr_extensions = self.csr.get_extensions()
- cert_extension_count = self.cert.get_extension_count()
- if len(csr_extensions) != cert_extension_count:
- return False
- for extension_number in range(0, cert_extension_count):
- cert_extension = self.cert.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.cert.get_signature_algorithm() not in self.signature_algorithms:
- return self.cert.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.cert.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.cert.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.cert.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.cert.get_version() + 1
-
- def _validate_key_usage(self):
- found = False
- for extension_idx in range(0, self.cert.get_extension_count()):
- extension = self.cert.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.cert.get_extension_count()):
- extension = self.cert.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 _normalize_san(self, 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:'):
- ip = compat_ipaddress.ip_address(san[3:])
- san = 'IP:{0}'.format(ip.compressed)
- return san
-
- def _validate_subject_alt_name(self):
- found = False
- 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':
- found = True
- l_altnames = [self._normalize_san(altname.strip()) for altname in
- to_text(extension, errors='surrogate_or_strict').split(', ')]
- sans = [self._normalize_san(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.cert.get_notBefore()
-
- def _validate_not_after(self):
- return self.cert.get_notAfter()
-
- def _validate_valid_at(self):
- rt = crypto_utils.get_relative_time_option(self.valid_at, "valid_at", backend=self.backend)
- rt = to_bytes(rt, errors='surrogate_or_strict')
- return self.cert.get_notBefore(), rt, self.cert.get_notAfter()
-
- def _validate_invalid_at(self):
- rt = crypto_utils.get_relative_time_option(self.invalid_at, "invalid_at", backend=self.backend)
- rt = to_bytes(rt, errors='surrogate_or_strict')
- return self.cert.get_notBefore(), rt, self.cert.get_notAfter()
-
- def _validate_valid_in(self):
- valid_in_asn1 = crypto_utils.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.cert.get_notBefore(), valid_in_date, self.cert.get_notAfter()
-
-
-class EntrustCertificate(Certificate):
- """Retrieve a certificate using Entrust (ECS)."""
-
- def __init__(self, module, backend):
- super(EntrustCertificate, self).__init__(module, backend)
- self.trackingId = None
- self.notAfter = crypto_utils.get_relative_time_option(module.params['entrust_not_after'], 'entrust_not_after', backend=self.backend)
-
- if self.csr_content is None or not os.path.exists(self.csr_path):
- raise CertificateError(
- 'The certificate signing request file {0} does not exist'.format(self.csr_path)
- )
-
- self.csr = crypto_utils.load_certificate_request(
- path=self.csr_path,
- content=self.csr_content,
- backend=self.backend,
- )
-
- # ECS API defaults to using the validated organization tied to the account.
- # 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.
- self.csr_org = None
- if self.backend == 'pyopenssl':
- 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:
- 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)
- if len(csr_subject_orgs) == 1:
- self.csr_org = csr_subject_orgs[0].value
- elif len(csr_subject_orgs) > 1:
- module.fail_json(msg=("Entrust provider does not currently support multiple validated organizations. Multiple organizations found in "
- "Subject DN: '{0}'. ".format(self.csr.subject)))
- # If no organization in the CSR, explicitly tell ECS that it should be blank in issued cert, not defaulted to
- # organization tied to the account.
- if self.csr_org is None:
- self.csr_org = ''
-
- try:
- self.ecs_client = ECSClient(
- entrust_api_user=module.params.get('entrust_api_user'),
- entrust_api_key=module.params.get('entrust_api_key'),
- entrust_api_cert=module.params.get('entrust_api_client_cert_path'),
- entrust_api_cert_key=module.params.get('entrust_api_client_cert_key_path'),
- entrust_api_specification_path=module.params.get('entrust_api_specification_path')
- )
- except SessionConfigurationException as e:
- module.fail_json(msg='Failed to initialize Entrust Provider: {0}'.format(to_native(e.message)))
-
- def generate(self, module):
-
- if not self.check(module, perms_required=False) or self.force:
- # Read the CSR that was generated for us
- body = {}
- if self.csr_content is not None:
- body['csr'] = self.csr_content
- else:
- with open(self.csr_path, 'r') as csr_file:
- body['csr'] = csr_file.read()
-
- body['certType'] = module.params['entrust_cert_type']
-
- # Handle expiration (30 days if not specified)
- expiry = self.notAfter
- if not expiry:
- gmt_now = datetime.datetime.fromtimestamp(time.mktime(time.gmtime()))
- expiry = gmt_now + datetime.timedelta(days=365)
-
- expiry_iso3339 = expiry.strftime("%Y-%m-%dT%H:%M:%S.00Z")
- body['certExpiryDate'] = expiry_iso3339
- body['org'] = self.csr_org
- body['tracking'] = {
- 'requesterName': module.params['entrust_requester_name'],
- 'requesterEmail': module.params['entrust_requester_email'],
- 'requesterPhone': module.params['entrust_requester_phone'],
- }
-
- try:
- result = self.ecs_client.NewCertRequest(Body=body)
- self.trackingId = result.get('trackingId')
- except RestOperationException as e:
- module.fail_json(msg='Failed to request new certificate from Entrust Certificate Services (ECS): {0}'.format(to_native(e.message)))
-
- if self.backup:
- self.backup_file = module.backup_local(self.path)
- crypto_utils.write_file(module, to_bytes(result.get('endEntityCert')))
- self.cert = crypto_utils.load_certificate(self.path, backend=self.backend)
- self.changed = True
-
- def check(self, module, perms_required=True):
- """Ensure the resource is in its desired state."""
-
- parent_check = super(EntrustCertificate, self).check(module, perms_required)
-
- try:
- cert_details = self._get_cert_details()
- except RestOperationException as e:
- module.fail_json(msg='Failed to get status of existing certificate from Entrust Certificate Services (ECS): {0}.'.format(to_native(e.message)))
-
- # Always issue a new certificate if the certificate is expired, suspended or revoked
- status = cert_details.get('status', False)
- if status == 'EXPIRED' or status == 'SUSPENDED' or status == 'REVOKED':
- return False
-
- # If the requested cert type was specified and it is for a different certificate type than the initial certificate, a new one is needed
- if module.params['entrust_cert_type'] and cert_details.get('certType') and module.params['entrust_cert_type'] != cert_details.get('certType'):
- return False
-
- return parent_check
-
- def _get_cert_details(self):
- cert_details = {}
- if self.cert:
- serial_number = None
- expiry = None
- if self.backend == 'pyopenssl':
- serial_number = "{0:X}".format(self.cert.get_serial_number())
- time_string = to_native(self.cert.get_notAfter())
- expiry = datetime.datetime.strptime(time_string, "%Y%m%d%H%M%SZ")
- elif self.backend == 'cryptography':
- serial_number = "{0:X}".format(self.cert.serial_number)
- expiry = self.cert.not_valid_after
-
- # get some information about the expiry of this certificate
- expiry_iso3339 = expiry.strftime("%Y-%m-%dT%H:%M:%S.00Z")
- cert_details['expiresAfter'] = expiry_iso3339
-
- # If a trackingId is not already defined (from the result of a generate)
- # use the serial number to identify the tracking Id
- if self.trackingId is None and serial_number is not None:
- cert_results = self.ecs_client.GetCertificates(serialNumber=serial_number).get('certificates', {})
-
- # Finding 0 or more than 1 result is a very unlikely use case, it simply means we cannot perform additional checks
- # on the 'state' as returned by Entrust Certificate Services (ECS). The general certificate validity is
- # still checked as it is in the rest of the module.
- if len(cert_results) == 1:
- self.trackingId = cert_results[0].get('trackingId')
-
- if self.trackingId is not None:
- cert_details.update(self.ecs_client.GetCertificate(trackingId=self.trackingId))
-
- return cert_details
-
- def dump(self, check_mode=False):
-
- result = {
- 'changed': self.changed,
- 'filename': self.path,
- 'privatekey': self.privatekey_path,
- 'csr': self.csr_path,
- }
-
- if self.backup_file:
- result['backup_file'] = self.backup_file
- if self.return_content:
- content = crypto_utils.load_file_if_exists(self.path, ignore_errors=True)
- result['certificate'] = content.decode('utf-8') if content else None
-
- result.update(self._get_cert_details())
-
- return result
-
-
-class AcmeCertificate(Certificate):
- """Retrieve a certificate using the ACME protocol."""
-
- # Since there's no real use of the backend,
- # other than the 'self.check' function, we just pass the backend to the constructor
-
- def __init__(self, module, backend):
- super(AcmeCertificate, self).__init__(module, backend)
- self.accountkey_path = module.params['acme_accountkey_path']
- self.challenge_path = module.params['acme_challenge_path']
- self.use_chain = module.params['acme_chain']
- self.acme_directory = module.params['acme_directory']
-
- def generate(self, module):
-
- if self.csr_content is None and not os.path.exists(self.csr_path):
- raise CertificateError(
- 'The certificate signing request file %s does not exist' % self.csr_path
- )
-
- if not os.path.exists(self.accountkey_path):
- raise CertificateError(
- 'The account key %s does not exist' % self.accountkey_path
- )
-
- if not os.path.exists(self.challenge_path):
- raise CertificateError(
- 'The challenge path %s does not exist' % self.challenge_path
- )
-
- if not self.check(module, perms_required=False) or self.force:
- acme_tiny_path = self.module.get_bin_path('acme-tiny', required=True)
- command = [acme_tiny_path]
- if self.use_chain:
- command.append('--chain')
- command.extend(['--account-key', self.accountkey_path])
- if self.csr_content is not None:
- # We need to temporarily write the CSR to disk
- fd, tmpsrc = tempfile.mkstemp()
- module.add_cleanup_file(tmpsrc) # Ansible will delete the file on exit
- f = os.fdopen(fd, 'wb')
- try:
- f.write(self.csr_content)
- except Exception as err:
- try:
- f.close()
- except Exception as dummy:
- pass
- module.fail_json(
- msg="failed to create temporary CSR file: %s" % to_native(err),
- exception=traceback.format_exc()
- )
- f.close()
- command.extend(['--csr', tmpsrc])
- else:
- command.extend(['--csr', self.csr_path])
- command.extend(['--acme-dir', self.challenge_path])
- command.extend(['--directory-url', self.acme_directory])
-
- try:
- crt = module.run_command(command, check_rc=True)[1]
- if self.backup:
- self.backup_file = module.backup_local(self.path)
- crypto_utils.write_file(module, to_bytes(crt))
- self.changed = True
- except OSError as exc:
- raise CertificateError(exc)
-
- file_args = module.load_file_common_arguments(module.params)
- if module.set_fs_attributes_if_different(file_args, False):
- self.changed = True
-
- def dump(self, check_mode=False):
-
- result = {
- 'changed': self.changed,
- 'filename': self.path,
- 'privatekey': self.privatekey_path,
- 'accountkey': self.accountkey_path,
- 'csr': self.csr_path,
- }
- if self.backup_file:
- result['backup_file'] = self.backup_file
- if self.return_content:
- content = crypto_utils.load_file_if_exists(self.path, ignore_errors=True)
- result['certificate'] = content.decode('utf-8') if content else None
-
- return result
-
-
-def main():
- module = AnsibleModule(
- argument_spec=dict(
- state=dict(type='str', default='present', choices=['present', 'absent']),
- path=dict(type='path', required=True),
- provider=dict(type='str', choices=['acme', 'assertonly', 'entrust', 'ownca', 'selfsigned']),
- force=dict(type='bool', default=False,),
- csr_path=dict(type='path'),
- csr_content=dict(type='str'),
- backup=dict(type='bool', default=False),
- select_crypto_backend=dict(type='str', default='auto', choices=['auto', 'cryptography', 'pyopenssl']),
- return_content=dict(type='bool', default=False),
-
- # General properties of a certificate
- privatekey_path=dict(type='path'),
- privatekey_content=dict(type='str'),
- privatekey_passphrase=dict(type='str', no_log=True),
-
- # provider: assertonly
- signature_algorithms=dict(type='list', elements='str', removed_in_version='2.13'),
- subject=dict(type='dict', removed_in_version='2.13'),
- subject_strict=dict(type='bool', default=False, removed_in_version='2.13'),
- issuer=dict(type='dict', removed_in_version='2.13'),
- issuer_strict=dict(type='bool', default=False, removed_in_version='2.13'),
- has_expired=dict(type='bool', default=False, removed_in_version='2.13'),
- version=dict(type='int', removed_in_version='2.13'),
- key_usage=dict(type='list', elements='str', aliases=['keyUsage'], removed_in_version='2.13'),
- key_usage_strict=dict(type='bool', default=False, aliases=['keyUsage_strict'], removed_in_version='2.13'),
- extended_key_usage=dict(type='list', elements='str', aliases=['extendedKeyUsage'], removed_in_version='2.13'),
- extended_key_usage_strict=dict(type='bool', default=False, aliases=['extendedKeyUsage_strict'], removed_in_version='2.13'),
- subject_alt_name=dict(type='list', elements='str', aliases=['subjectAltName'], removed_in_version='2.13'),
- subject_alt_name_strict=dict(type='bool', default=False, aliases=['subjectAltName_strict'], removed_in_version='2.13'),
- not_before=dict(type='str', aliases=['notBefore'], removed_in_version='2.13'),
- not_after=dict(type='str', aliases=['notAfter'], removed_in_version='2.13'),
- valid_at=dict(type='str', removed_in_version='2.13'),
- invalid_at=dict(type='str', removed_in_version='2.13'),
- valid_in=dict(type='str', removed_in_version='2.13'),
-
- # provider: selfsigned
- selfsigned_version=dict(type='int', default=3),
- selfsigned_digest=dict(type='str', default='sha256'),
- selfsigned_not_before=dict(type='str', default='+0s', aliases=['selfsigned_notBefore']),
- selfsigned_not_after=dict(type='str', default='+3650d', aliases=['selfsigned_notAfter']),
- selfsigned_create_subject_key_identifier=dict(
- type='str',
- default='create_if_not_provided',
- choices=['create_if_not_provided', 'always_create', 'never_create']
- ),
-
- # provider: ownca
- ownca_path=dict(type='path'),
- ownca_content=dict(type='str'),
- ownca_privatekey_path=dict(type='path'),
- ownca_privatekey_content=dict(type='str'),
- ownca_privatekey_passphrase=dict(type='str', no_log=True),
- ownca_digest=dict(type='str', default='sha256'),
- ownca_version=dict(type='int', default=3),
- ownca_not_before=dict(type='str', default='+0s'),
- ownca_not_after=dict(type='str', default='+3650d'),
- ownca_create_subject_key_identifier=dict(
- type='str',
- default='create_if_not_provided',
- choices=['create_if_not_provided', 'always_create', 'never_create']
- ),
- ownca_create_authority_key_identifier=dict(type='bool', default=True),
-
- # provider: acme
- acme_accountkey_path=dict(type='path'),
- acme_challenge_path=dict(type='path'),
- acme_chain=dict(type='bool', default=False),
- acme_directory=dict(type='str', default="https://acme-v02.api.letsencrypt.org/directory"),
-
- # provider: entrust
- entrust_cert_type=dict(type='str', default='STANDARD_SSL',
- choices=['STANDARD_SSL', 'ADVANTAGE_SSL', 'UC_SSL', 'EV_SSL', 'WILDCARD_SSL',
- 'PRIVATE_SSL', 'PD_SSL', 'CDS_ENT_LITE', 'CDS_ENT_PRO', 'SMIME_ENT']),
- entrust_requester_email=dict(type='str'),
- entrust_requester_name=dict(type='str'),
- entrust_requester_phone=dict(type='str'),
- entrust_api_user=dict(type='str'),
- entrust_api_key=dict(type='str', no_log=True),
- entrust_api_client_cert_path=dict(type='path'),
- entrust_api_client_cert_key_path=dict(type='path', no_log=True),
- entrust_api_specification_path=dict(type='path', default='https://cloud.entrust.net/EntrustCloud/documentation/cms-api-2.1.0.yaml'),
- entrust_not_after=dict(type='str', default='+365d'),
- ),
- supports_check_mode=True,
- add_file_common_args=True,
- required_if=[
- ['state', 'present', ['provider']],
- ['provider', 'entrust', ['entrust_requester_email', 'entrust_requester_name', 'entrust_requester_phone',
- 'entrust_api_user', 'entrust_api_key', 'entrust_api_client_cert_path',
- 'entrust_api_client_cert_key_path']],
- ],
- mutually_exclusive=[
- ['csr_path', 'csr_content'],
- ['privatekey_path', 'privatekey_content'],
- ['ownca_path', 'ownca_content'],
- ['ownca_privatekey_path', 'ownca_privatekey_content'],
- ],
- )
-
- try:
- if module.params['state'] == 'absent':
- certificate = CertificateAbsent(module)
-
- else:
- if module.params['provider'] != 'assertonly' and module.params['csr_path'] is None and module.params['csr_content'] is None:
- module.fail_json(msg='csr_path or csr_content is required when provider is not assertonly')
-
- base_dir = os.path.dirname(module.params['path']) or '.'
- if not os.path.isdir(base_dir):
- module.fail_json(
- name=base_dir,
- msg='The directory %s does not exist or the file is not a directory' % base_dir
- )
-
- provider = module.params['provider']
- if provider == 'assertonly':
- module.deprecate("The 'assertonly' provider is deprecated; please see the examples of "
- "the 'openssl_certificate' module on how to replace it with other modules",
- version='2.13')
- elif provider == 'selfsigned':
- if module.params['privatekey_path'] is None and module.params['privatekey_content'] is None:
- module.fail_json(msg='One of privatekey_path and privatekey_content must be specified for the selfsigned provider.')
- elif provider == 'acme':
- if module.params['acme_accountkey_path'] is None:
- module.fail_json(msg='The acme_accountkey_path option must be specified for the acme provider.')
- if module.params['acme_challenge_path'] is None:
- module.fail_json(msg='The acme_challenge_path option must be specified for the acme provider.')
- elif provider == 'ownca':
- if module.params['ownca_path'] is None and module.params['ownca_content'] is None:
- module.fail_json(msg='One of ownca_path and ownca_content must be specified for the ownca provider.')
- if module.params['ownca_privatekey_path'] is None and module.params['ownca_privatekey_content'] is None:
- module.fail_json(msg='One of ownca_privatekey_path and ownca_privatekey_content must be specified for the ownca provider.')
-
- backend = module.params['select_crypto_backend']
- if backend == 'auto':
- # Detect what backend we can use
- can_use_cryptography = CRYPTOGRAPHY_FOUND and CRYPTOGRAPHY_VERSION >= LooseVersion(MINIMAL_CRYPTOGRAPHY_VERSION)
- can_use_pyopenssl = PYOPENSSL_FOUND and PYOPENSSL_VERSION >= LooseVersion(MINIMAL_PYOPENSSL_VERSION)
-
- # If cryptography is available we'll use it
- if can_use_cryptography:
- backend = 'cryptography'
- elif can_use_pyopenssl:
- backend = 'pyopenssl'
-
- if module.params['selfsigned_version'] == 2 or module.params['ownca_version'] == 2:
- module.warn('crypto backend forced to pyopenssl. The cryptography library does not support v2 certificates')
- backend = 'pyopenssl'
-
- # Fail if no backend has been found
- if backend == 'auto':
- module.fail_json(msg=("Can't detect any of the required Python libraries "
- "cryptography (>= {0}) or PyOpenSSL (>= {1})").format(
- MINIMAL_CRYPTOGRAPHY_VERSION,
- MINIMAL_PYOPENSSL_VERSION))
-
- if backend == 'pyopenssl':
- if not PYOPENSSL_FOUND:
- module.fail_json(msg=missing_required_lib('pyOpenSSL >= {0}'.format(MINIMAL_PYOPENSSL_VERSION)),
- exception=PYOPENSSL_IMP_ERR)
- if module.params['provider'] in ['selfsigned', 'ownca', 'assertonly']:
- try:
- getattr(crypto.X509Req, 'get_extensions')
- except AttributeError:
- module.fail_json(msg='You need to have PyOpenSSL>=0.15')
-
- module.deprecate('The module is using the PyOpenSSL backend. This backend has been deprecated', version='2.13')
- if provider == 'selfsigned':
- certificate = SelfSignedCertificate(module)
- elif provider == 'acme':
- certificate = AcmeCertificate(module, 'pyopenssl')
- elif provider == 'ownca':
- certificate = OwnCACertificate(module)
- elif provider == 'entrust':
- certificate = EntrustCertificate(module, 'pyopenssl')
- else:
- certificate = AssertOnlyCertificate(module)
- elif backend == 'cryptography':
- if not CRYPTOGRAPHY_FOUND:
- module.fail_json(msg=missing_required_lib('cryptography >= {0}'.format(MINIMAL_CRYPTOGRAPHY_VERSION)),
- exception=CRYPTOGRAPHY_IMP_ERR)
- if module.params['selfsigned_version'] == 2 or module.params['ownca_version'] == 2:
- module.fail_json(msg='The cryptography backend does not support v2 certificates, '
- 'use select_crypto_backend=pyopenssl for v2 certificates')
- if provider == 'selfsigned':
- certificate = SelfSignedCertificateCryptography(module)
- elif provider == 'acme':
- certificate = AcmeCertificate(module, 'cryptography')
- elif provider == 'ownca':
- certificate = OwnCACertificateCryptography(module)
- elif provider == 'entrust':
- certificate = EntrustCertificate(module, 'cryptography')
- else:
- certificate = AssertOnlyCertificateCryptography(module)
-
- if module.params['state'] == 'present':
- if module.check_mode:
- result = certificate.dump(check_mode=True)
- result['changed'] = module.params['force'] or not certificate.check(module)
- module.exit_json(**result)
-
- certificate.generate(module)
- else:
- if module.check_mode:
- result = certificate.dump(check_mode=True)
- result['changed'] = os.path.exists(module.params['path'])
- module.exit_json(**result)
-
- certificate.remove(module)
-
- result = certificate.dump()
- module.exit_json(**result)
- except crypto_utils.OpenSSLObjectError as exc:
- module.fail_json(msg=to_native(exc))
-
-
-if __name__ == "__main__":
- main()
diff --git a/lib/ansible/modules/crypto/openssl_certificate_info.py b/lib/ansible/modules/crypto/openssl_certificate_info.py
deleted file mode 100644
index 2d7459ae9d..0000000000
--- a/lib/ansible/modules/crypto/openssl_certificate_info.py
+++ /dev/null
@@ -1,863 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-
-# Copyright: (c) 2016-2017, Yanis Guenane <yanis+ansible@guenane.org>
-# Copyright: (c) 2017, Markus Teufelberger <mteufelberger+ansible@mgit.at>
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'community'}
-
-DOCUMENTATION = r'''
----
-module: openssl_certificate_info
-version_added: '2.8'
-short_description: Provide information of OpenSSL X.509 certificates
-description:
- - 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
- 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 Ansible 2.13.
-requirements:
- - PyOpenSSL >= 0.15 or cryptography >= 1.6
-author:
- - Felix Fontein (@felixfontein)
- - Yanis Guenane (@Spredzy)
- - Markus Teufelberger (@MarkusTeufelberger)
-options:
- path:
- description:
- - Remote absolute path where the certificate file is loaded from.
- - Either I(path) or I(content) must be specified, but not both.
- type: path
- content:
- description:
- - Content of the X.509 certificate in PEM format.
- - Either I(path) or I(content) must be specified, but not both.
- type: str
- version_added: "2.10"
- valid_at:
- description:
- - A dict of names mapping to time specifications. Every time specified here
- will be checked whether the certificate is valid at this point. See the
- C(valid_at) return value for informations on the result.
- - Time can be specified either as relative time or as absolute timestamp.
- - Time will always be interpreted as UTC.
- - Valid format is C([+-]timespec | ASN.1 TIME) where timespec can be an integer
- + C([w | d | h | m | s]) (e.g. C(+32w1d2h), and ASN.1 TIME (i.e. pattern C(YYYYMMDDHHMMSSZ)).
- Note that all timestamps will be treated as being in UTC.
- type: dict
- select_crypto_backend:
- description:
- - Determines which crypto backend to use.
- - The default choice is C(auto), which tries to use C(cryptography) if available, and falls back to C(pyopenssl).
- - If set to C(pyopenssl), will try to use the L(pyOpenSSL,https://pypi.org/project/pyOpenSSL/) library.
- - If set to C(cryptography), will try to use the L(cryptography,https://cryptography.io/) library.
- - Please note that the C(pyopenssl) backend has been deprecated in Ansible 2.9, and will be removed in Ansible 2.13.
- From that point on, only the C(cryptography) backend will be available.
- type: str
- default: auto
- choices: [ auto, cryptography, pyopenssl ]
-
-notes:
- - All timestamp values are provided in ASN.1 TIME format, i.e. following the C(YYYYMMDDHHMMSSZ) pattern.
- They are all in UTC.
-seealso:
-- module: openssl_certificate
-'''
-
-EXAMPLES = r'''
-- name: Generate a Self Signed OpenSSL certificate
- openssl_certificate:
- path: /etc/ssl/crt/ansible.com.crt
- privatekey_path: /etc/ssl/private/ansible.com.pem
- csr_path: /etc/ssl/csr/ansible.com.csr
- provider: selfsigned
-
-
-# Get information on the certificate
-
-- name: Get information on generated certificate
- openssl_certificate_info:
- path: /etc/ssl/crt/ansible.com.crt
- register: result
-
-- name: Dump information
- debug:
- var: result
-
-
-# Check whether the certificate is valid or not valid at certain times, fail
-# if this is not the case. The first task (openssl_certificate_info) collects
-# the information, and the second task (assert) validates the result and
-# makes the playbook fail in case something is not as expected.
-
-- name: Test whether that certificate is valid tomorrow and/or in three weeks
- openssl_certificate_info:
- path: /etc/ssl/crt/ansible.com.crt
- valid_at:
- point_1: "+1d"
- point_2: "+3w"
- register: result
-
-- name: Validate that certificate is valid tomorrow, but not in three weeks
- assert:
- that:
- - result.valid_at.point_1 # valid in one day
- - not result.valid_at.point_2 # not valid in three weeks
-'''
-
-RETURN = r'''
-expired:
- description: Whether the certificate is expired (i.e. C(notAfter) is in the past)
- returned: success
- type: bool
-basic_constraints:
- description: Entries in the C(basic_constraints) extension, or C(none) if extension is not present.
- returned: success
- type: list
- elements: str
- sample: "[CA:TRUE, pathlen:1]"
-basic_constraints_critical:
- description: Whether the C(basic_constraints) extension is critical.
- returned: success
- type: bool
-extended_key_usage:
- description: Entries in the C(extended_key_usage) extension, or C(none) if extension is not present.
- returned: success
- type: list
- elements: str
- sample: "[Biometric Info, DVCS, Time Stamping]"
-extended_key_usage_critical:
- description: Whether the C(extended_key_usage) extension is critical.
- returned: success
- type: bool
-extensions_by_oid:
- description: Returns a dictionary for every extension OID
- returned: success
- type: dict
- contains:
- critical:
- description: Whether the extension is critical.
- returned: success
- type: bool
- value:
- description: The Base64 encoded value (in DER format) of the extension
- returned: success
- type: str
- sample: "MAMCAQU="
- sample: '{"1.3.6.1.5.5.7.1.24": { "critical": false, "value": "MAMCAQU="}}'
-key_usage:
- description: Entries in the C(key_usage) extension, or C(none) if extension is not present.
- returned: success
- type: str
- sample: "[Key Agreement, Data Encipherment]"
-key_usage_critical:
- description: Whether the C(key_usage) extension is critical.
- returned: success
- type: bool
-subject_alt_name:
- description: Entries in the C(subject_alt_name) extension, or C(none) if extension is not present.
- returned: success
- type: list
- elements: str
- sample: "[DNS:www.ansible.com, IP:1.2.3.4]"
-subject_alt_name_critical:
- description: Whether the C(subject_alt_name) extension is critical.
- returned: success
- type: bool
-ocsp_must_staple:
- description: C(yes) if the OCSP Must Staple extension is present, C(none) otherwise.
- returned: success
- type: bool
-ocsp_must_staple_critical:
- description: Whether the C(ocsp_must_staple) extension is critical.
- returned: success
- type: bool
-issuer:
- description:
- - The certificate's issuer.
- - Note that for repeated values, only the last one will be returned.
- returned: success
- type: dict
- sample: '{"organizationName": "Ansible", "commonName": "ca.example.com"}'
-issuer_ordered:
- description: The certificate's issuer as an ordered list of tuples.
- returned: success
- type: list
- elements: list
- sample: '[["organizationName", "Ansible"], ["commonName": "ca.example.com"]]'
- version_added: "2.9"
-subject:
- description:
- - The certificate's subject as a dictionary.
- - Note that for repeated values, only the last one will be returned.
- returned: success
- type: dict
- sample: '{"commonName": "www.example.com", "emailAddress": "test@example.com"}'
-subject_ordered:
- description: The certificate's subject as an ordered list of tuples.
- returned: success
- type: list
- elements: list
- sample: '[["commonName", "www.example.com"], ["emailAddress": "test@example.com"]]'
- version_added: "2.9"
-not_after:
- description: C(notAfter) date as ASN.1 TIME
- returned: success
- type: str
- sample: 20190413202428Z
-not_before:
- description: C(notBefore) date as ASN.1 TIME
- returned: success
- type: str
- sample: 20190331202428Z
-public_key:
- description: Certificate's public key in PEM format
- returned: success
- type: str
- sample: "-----BEGIN PUBLIC KEY-----\nMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A..."
-public_key_fingerprints:
- description:
- - Fingerprints of certificate's public key.
- - For every hash algorithm available, the fingerprint is computed.
- returned: success
- type: dict
- sample: "{'sha256': 'd4:b3:aa:6d:c8:04:ce:4e:ba:f6:29:4d:92:a3:94:b0:c2:ff:bd:bf:33:63:11:43:34:0f:51:b0:95:09:2f:63',
- 'sha512': 'f7:07:4a:f0:b0:f0:e6:8b:95:5f:f9:e6:61:0a:32:68:f1..."
-signature_algorithm:
- description: The signature algorithm used to sign the certificate.
- returned: success
- type: str
- sample: sha256WithRSAEncryption
-serial_number:
- description: The certificate's serial number.
- returned: success
- type: int
- sample: 1234
-version:
- description: The certificate version.
- returned: success
- type: int
- sample: 3
-valid_at:
- description: For every time stamp provided in the I(valid_at) option, a
- boolean whether the certificate is valid at that point in time
- or not.
- returned: success
- type: dict
-subject_key_identifier:
- description:
- - The certificate's subject key identifier.
- - The identifier is returned in hexadecimal, with C(:) used to separate bytes.
- - Is C(none) if the C(SubjectKeyIdentifier) extension is not present.
- returned: success and if the pyOpenSSL backend is I(not) used
- type: str
- sample: '00:11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff:00:11:22:33'
- version_added: "2.9"
-authority_key_identifier:
- description:
- - The certificate's authority key identifier.
- - The identifier is returned in hexadecimal, with C(:) used to separate bytes.
- - Is C(none) if the C(AuthorityKeyIdentifier) extension is not present.
- returned: success and if the pyOpenSSL backend is I(not) used
- type: str
- sample: '00:11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff:00:11:22:33'
- version_added: "2.9"
-authority_cert_issuer:
- description:
- - The certificate's authority cert issuer as a list of general names.
- - Is C(none) if the C(AuthorityKeyIdentifier) extension is not present.
- returned: success and if the pyOpenSSL backend is I(not) used
- type: list
- elements: str
- sample: "[DNS:www.ansible.com, IP:1.2.3.4]"
- version_added: "2.9"
-authority_cert_serial_number:
- description:
- - The certificate's authority cert serial number.
- - Is C(none) if the C(AuthorityKeyIdentifier) extension is not present.
- returned: success and if the pyOpenSSL backend is I(not) used
- type: int
- sample: '12345'
- version_added: "2.9"
-ocsp_uri:
- description: The OCSP responder URI, if included in the certificate. Will be
- C(none) if no OCSP responder URI is included.
- returned: success
- type: str
- version_added: "2.9"
-'''
-
-
-import abc
-import binascii
-import datetime
-import os
-import re
-import traceback
-from distutils.version import LooseVersion
-
-from ansible.module_utils import crypto as crypto_utils
-from ansible.module_utils.basic import AnsibleModule, missing_required_lib
-from ansible.module_utils.six import string_types
-from ansible.module_utils._text import to_native, to_text, to_bytes
-from ansible.module_utils.compat import ipaddress as compat_ipaddress
-
-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
-try:
- import cryptography
- from cryptography import x509
- from cryptography.hazmat.primitives import serialization
- CRYPTOGRAPHY_VERSION = LooseVersion(cryptography.__version__)
-except ImportError:
- CRYPTOGRAPHY_IMP_ERR = traceback.format_exc()
- CRYPTOGRAPHY_FOUND = False
-else:
- CRYPTOGRAPHY_FOUND = True
-
-
-TIMESTAMP_FORMAT = "%Y%m%d%H%M%SZ"
-
-
-class CertificateInfo(crypto_utils.OpenSSLObject):
- def __init__(self, module, backend):
- super(CertificateInfo, self).__init__(
- module.params['path'] or '',
- 'present',
- False,
- module.check_mode,
- )
- self.backend = backend
- self.module = module
- self.content = module.params['content']
- if self.content is not None:
- self.content = self.content.encode('utf-8')
-
- self.valid_at = module.params['valid_at']
- if self.valid_at:
- for k, v in self.valid_at.items():
- if not isinstance(v, string_types):
- self.module.fail_json(
- msg='The value for valid_at.{0} must be of type string (got {1})'.format(k, type(v))
- )
- self.valid_at[k] = crypto_utils.get_relative_time_option(v, 'valid_at.{0}'.format(k))
-
- def generate(self):
- # Empty method because crypto_utils.OpenSSLObject wants this
- pass
-
- def dump(self):
- # Empty method because crypto_utils.OpenSSLObject wants this
- pass
-
- @abc.abstractmethod
- def _get_signature_algorithm(self):
- pass
-
- @abc.abstractmethod
- def _get_subject_ordered(self):
- pass
-
- @abc.abstractmethod
- def _get_issuer_ordered(self):
- pass
-
- @abc.abstractmethod
- def _get_version(self):
- pass
-
- @abc.abstractmethod
- def _get_key_usage(self):
- pass
-
- @abc.abstractmethod
- def _get_extended_key_usage(self):
- pass
-
- @abc.abstractmethod
- def _get_basic_constraints(self):
- pass
-
- @abc.abstractmethod
- def _get_ocsp_must_staple(self):
- pass
-
- @abc.abstractmethod
- def _get_subject_alt_name(self):
- pass
-
- @abc.abstractmethod
- def _get_not_before(self):
- pass
-
- @abc.abstractmethod
- def _get_not_after(self):
- pass
-
- @abc.abstractmethod
- def _get_public_key(self, binary):
- pass
-
- @abc.abstractmethod
- def _get_subject_key_identifier(self):
- pass
-
- @abc.abstractmethod
- def _get_authority_key_identifier(self):
- pass
-
- @abc.abstractmethod
- def _get_serial_number(self):
- pass
-
- @abc.abstractmethod
- def _get_all_extensions(self):
- pass
-
- @abc.abstractmethod
- def _get_ocsp_uri(self):
- pass
-
- def get_info(self):
- result = dict()
- self.cert = crypto_utils.load_certificate(self.path, content=self.content, backend=self.backend)
-
- result['signature_algorithm'] = self._get_signature_algorithm()
- subject = self._get_subject_ordered()
- issuer = self._get_issuer_ordered()
- result['subject'] = dict()
- for k, v in subject:
- result['subject'][k] = v
- result['subject_ordered'] = subject
- result['issuer'] = dict()
- for k, v in issuer:
- result['issuer'][k] = v
- result['issuer_ordered'] = issuer
- result['version'] = self._get_version()
- result['key_usage'], result['key_usage_critical'] = self._get_key_usage()
- result['extended_key_usage'], result['extended_key_usage_critical'] = self._get_extended_key_usage()
- result['basic_constraints'], result['basic_constraints_critical'] = self._get_basic_constraints()
- result['ocsp_must_staple'], result['ocsp_must_staple_critical'] = self._get_ocsp_must_staple()
- result['subject_alt_name'], result['subject_alt_name_critical'] = self._get_subject_alt_name()
-
- not_before = self._get_not_before()
- not_after = self._get_not_after()
- result['not_before'] = not_before.strftime(TIMESTAMP_FORMAT)
- result['not_after'] = not_after.strftime(TIMESTAMP_FORMAT)
- result['expired'] = not_after < datetime.datetime.utcnow()
-
- result['valid_at'] = dict()
- if self.valid_at:
- for k, v in self.valid_at.items():
- result['valid_at'][k] = not_before <= v <= not_after
-
- result['public_key'] = self._get_public_key(binary=False)
- pk = self._get_public_key(binary=True)
- result['public_key_fingerprints'] = crypto_utils.get_fingerprint_of_bytes(pk) if pk is not None else dict()
-
- if self.backend != 'pyopenssl':
- ski = self._get_subject_key_identifier()
- if ski is not None:
- ski = to_native(binascii.hexlify(ski))
- ski = ':'.join([ski[i:i + 2] for i in range(0, len(ski), 2)])
- result['subject_key_identifier'] = ski
-
- aki, aci, acsn = self._get_authority_key_identifier()
- if aki is not None:
- aki = to_native(binascii.hexlify(aki))
- aki = ':'.join([aki[i:i + 2] for i in range(0, len(aki), 2)])
- result['authority_key_identifier'] = aki
- result['authority_cert_issuer'] = aci
- result['authority_cert_serial_number'] = acsn
-
- result['serial_number'] = self._get_serial_number()
- result['extensions_by_oid'] = self._get_all_extensions()
- result['ocsp_uri'] = self._get_ocsp_uri()
-
- return result
-
-
-class CertificateInfoCryptography(CertificateInfo):
- """Validate the supplied cert, using the cryptography backend"""
- def __init__(self, module):
- super(CertificateInfoCryptography, self).__init__(module, 'cryptography')
-
- def _get_signature_algorithm(self):
- return crypto_utils.cryptography_oid_to_name(self.cert.signature_algorithm_oid)
-
- def _get_subject_ordered(self):
- result = []
- for attribute in self.cert.subject:
- result.append([crypto_utils.cryptography_oid_to_name(attribute.oid), attribute.value])
- return result
-
- def _get_issuer_ordered(self):
- result = []
- for attribute in self.cert.issuer:
- result.append([crypto_utils.cryptography_oid_to_name(attribute.oid), attribute.value])
- return result
-
- def _get_version(self):
- if self.cert.version == x509.Version.v1:
- return 1
- if self.cert.version == x509.Version.v3:
- return 3
- return "unknown"
-
- def _get_key_usage(self):
- try:
- current_key_ext = self.cert.extensions.get_extension_for_class(x509.KeyUsage)
- current_key_usage = current_key_ext.value
- key_usage = dict(
- digital_signature=current_key_usage.digital_signature,
- content_commitment=current_key_usage.content_commitment,
- key_encipherment=current_key_usage.key_encipherment,
- data_encipherment=current_key_usage.data_encipherment,
- key_agreement=current_key_usage.key_agreement,
- key_cert_sign=current_key_usage.key_cert_sign,
- crl_sign=current_key_usage.crl_sign,
- encipher_only=False,
- decipher_only=False,
- )
- if key_usage['key_agreement']:
- key_usage.update(dict(
- encipher_only=current_key_usage.encipher_only,
- decipher_only=current_key_usage.decipher_only
- ))
-
- key_usage_names = dict(
- digital_signature='Digital Signature',
- content_commitment='Non Repudiation',
- key_encipherment='Key Encipherment',
- data_encipherment='Data Encipherment',
- key_agreement='Key Agreement',
- key_cert_sign='Certificate Sign',
- crl_sign='CRL Sign',
- encipher_only='Encipher Only',
- decipher_only='Decipher Only',
- )
- return sorted([
- key_usage_names[name] for name, value in key_usage.items() if value
- ]), current_key_ext.critical
- except cryptography.x509.ExtensionNotFound:
- return None, False
-
- def _get_extended_key_usage(self):
- try:
- ext_keyusage_ext = self.cert.extensions.get_extension_for_class(x509.ExtendedKeyUsage)
- return sorted([
- crypto_utils.cryptography_oid_to_name(eku) for eku in ext_keyusage_ext.value
- ]), ext_keyusage_ext.critical
- except cryptography.x509.ExtensionNotFound:
- return None, False
-
- def _get_basic_constraints(self):
- try:
- ext_keyusage_ext = self.cert.extensions.get_extension_for_class(x509.BasicConstraints)
- result = []
- result.append('CA:{0}'.format('TRUE' if ext_keyusage_ext.value.ca else 'FALSE'))
- if ext_keyusage_ext.value.path_length is not None:
- result.append('pathlen:{0}'.format(ext_keyusage_ext.value.path_length))
- return sorted(result), ext_keyusage_ext.critical
- except cryptography.x509.ExtensionNotFound:
- return None, False
-
- def _get_ocsp_must_staple(self):
- try:
- try:
- # This only works with cryptography >= 2.1
- tlsfeature_ext = self.cert.extensions.get_extension_for_class(x509.TLSFeature)
- value = cryptography.x509.TLSFeatureType.status_request in tlsfeature_ext.value
- except AttributeError as dummy:
- # Fallback for cryptography < 2.1
- oid = x509.oid.ObjectIdentifier("1.3.6.1.5.5.7.1.24")
- tlsfeature_ext = self.cert.extensions.get_extension_for_oid(oid)
- value = tlsfeature_ext.value.value == b"\x30\x03\x02\x01\x05"
- return value, tlsfeature_ext.critical
- except cryptography.x509.ExtensionNotFound:
- return None, False
-
- def _get_subject_alt_name(self):
- try:
- san_ext = self.cert.extensions.get_extension_for_class(x509.SubjectAlternativeName)
- result = [crypto_utils.cryptography_decode_name(san) for san in san_ext.value]
- return result, san_ext.critical
- except cryptography.x509.ExtensionNotFound:
- return None, False
-
- def _get_not_before(self):
- return self.cert.not_valid_before
-
- def _get_not_after(self):
- return self.cert.not_valid_after
-
- def _get_public_key(self, binary):
- return self.cert.public_key().public_bytes(
- serialization.Encoding.DER if binary else serialization.Encoding.PEM,
- serialization.PublicFormat.SubjectPublicKeyInfo
- )
-
- def _get_subject_key_identifier(self):
- try:
- ext = self.cert.extensions.get_extension_for_class(x509.SubjectKeyIdentifier)
- return ext.value.digest
- except cryptography.x509.ExtensionNotFound:
- return None
-
- def _get_authority_key_identifier(self):
- try:
- ext = self.cert.extensions.get_extension_for_class(x509.AuthorityKeyIdentifier)
- issuer = None
- if ext.value.authority_cert_issuer is not None:
- issuer = [crypto_utils.cryptography_decode_name(san) for san in ext.value.authority_cert_issuer]
- return ext.value.key_identifier, issuer, ext.value.authority_cert_serial_number
- except cryptography.x509.ExtensionNotFound:
- return None, None, None
-
- def _get_serial_number(self):
- return self.cert.serial_number
-
- def _get_all_extensions(self):
- return crypto_utils.cryptography_get_extensions_from_cert(self.cert)
-
- def _get_ocsp_uri(self):
- try:
- ext = self.cert.extensions.get_extension_for_class(x509.AuthorityInformationAccess)
- for desc in ext.value:
- if desc.access_method == x509.oid.AuthorityInformationAccessOID.OCSP:
- if isinstance(desc.access_location, x509.UniformResourceIdentifier):
- return desc.access_location.value
- except x509.ExtensionNotFound as dummy:
- pass
- return None
-
-
-class CertificateInfoPyOpenSSL(CertificateInfo):
- """validate the supplied certificate."""
-
- def __init__(self, module):
- super(CertificateInfoPyOpenSSL, self).__init__(module, 'pyopenssl')
-
- 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([crypto_utils.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 = [
- crypto_utils.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 _normalize_san(self, san):
- if san.startswith('IP Address:'):
- san = 'IP:' + san[len('IP Address:'):]
- if san.startswith('IP:'):
- ip = compat_ipaddress.ip_address(san[3:])
- san = 'IP:{0}'.format(ip.compressed)
- return san
-
- 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 = [self._normalize_san(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(self, binary):
- try:
- return crypto.dump_publickey(
- crypto.FILETYPE_ASN1 if binary else crypto.FILETYPE_PEM,
- self.cert.get_pubkey()
- )
- except AttributeError:
- try:
- # pyOpenSSL < 16.0:
- bio = crypto._new_mem_buf()
- if binary:
- rc = crypto._lib.i2d_PUBKEY_bio(bio, self.cert.get_pubkey()._pkey)
- else:
- 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_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 crypto_utils.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 main():
- module = AnsibleModule(
- argument_spec=dict(
- path=dict(type='path'),
- content=dict(type='str'),
- valid_at=dict(type='dict'),
- select_crypto_backend=dict(type='str', default='auto', choices=['auto', 'cryptography', 'pyopenssl']),
- ),
- required_one_of=(
- ['path', 'content'],
- ),
- mutually_exclusive=(
- ['path', 'content'],
- ),
- supports_check_mode=True,
- )
-
- try:
- if module.params['path'] is not None:
- base_dir = os.path.dirname(module.params['path']) or '.'
- if not os.path.isdir(base_dir):
- module.fail_json(
- name=base_dir,
- msg='The directory %s does not exist or the file is not a directory' % base_dir
- )
-
- backend = module.params['select_crypto_backend']
- if backend == 'auto':
- # Detect what backend we can use
- can_use_cryptography = CRYPTOGRAPHY_FOUND and CRYPTOGRAPHY_VERSION >= LooseVersion(MINIMAL_CRYPTOGRAPHY_VERSION)
- can_use_pyopenssl = PYOPENSSL_FOUND and PYOPENSSL_VERSION >= LooseVersion(MINIMAL_PYOPENSSL_VERSION)
-
- # If cryptography is available we'll use it
- if can_use_cryptography:
- backend = 'cryptography'
- elif can_use_pyopenssl:
- backend = 'pyopenssl'
-
- # Fail if no backend has been found
- if backend == 'auto':
- module.fail_json(msg=("Can't detect any of the required Python libraries "
- "cryptography (>= {0}) or PyOpenSSL (>= {1})").format(
- MINIMAL_CRYPTOGRAPHY_VERSION,
- MINIMAL_PYOPENSSL_VERSION))
-
- if backend == 'pyopenssl':
- if not PYOPENSSL_FOUND:
- module.fail_json(msg=missing_required_lib('pyOpenSSL >= {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')
-
- module.deprecate('The module is using the PyOpenSSL backend. This backend has been deprecated', version='2.13')
- certificate = CertificateInfoPyOpenSSL(module)
- elif backend == 'cryptography':
- if not CRYPTOGRAPHY_FOUND:
- module.fail_json(msg=missing_required_lib('cryptography >= {0}'.format(MINIMAL_CRYPTOGRAPHY_VERSION)),
- exception=CRYPTOGRAPHY_IMP_ERR)
- certificate = CertificateInfoCryptography(module)
-
- result = certificate.get_info()
- module.exit_json(**result)
- except crypto_utils.OpenSSLObjectError as exc:
- module.fail_json(msg=to_native(exc))
-
-
-if __name__ == "__main__":
- main()
diff --git a/lib/ansible/modules/crypto/openssl_csr.py b/lib/ansible/modules/crypto/openssl_csr.py
deleted file mode 100644
index ea2cf68c2a..0000000000
--- a/lib/ansible/modules/crypto/openssl_csr.py
+++ /dev/null
@@ -1,1159 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-
-# Copyrigt: (c) 2017, Yanis Guenane <yanis+ansible@guenane.org>
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'community'}
-
-DOCUMENTATION = r'''
----
-module: openssl_csr
-version_added: '2.4'
-short_description: Generate OpenSSL Certificate Signing Request (CSR)
-description:
- - This module allows one to (re)generate OpenSSL certificate signing requests.
- - It uses the pyOpenSSL python library to interact with openssl. This module supports
- the subjectAltName, keyUsage, extendedKeyUsage, basicConstraints and OCSP Must Staple
- extensions.
- - "Please note that the module regenerates existing CSR if it doesn't match the module's
- options, or if it seems to be corrupt. If you are concerned that this could overwrite
- your existing CSR, 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 Ansible 2.13."
-requirements:
- - Either cryptography >= 1.3
- - Or pyOpenSSL >= 0.15
-author:
-- Yanis Guenane (@Spredzy)
-options:
- state:
- description:
- - Whether the certificate signing request should exist or not, taking action if the state is different from what is stated.
- type: str
- default: present
- choices: [ absent, present ]
- digest:
- description:
- - The digest used when signing the certificate signing request with the private key.
- type: str
- default: sha256
- privatekey_path:
- description:
- - The path to the private key to use when signing the certificate signing request.
- - Either I(privatekey_path) or I(privatekey_content) must be specified if I(state) is C(present), but not both.
- type: path
- privatekey_content:
- description:
- - The content of the private key to use when signing the certificate signing request.
- - Either I(privatekey_path) or I(privatekey_content) must be specified if I(state) is C(present), but not both.
- type: str
- version_added: "2.10"
- privatekey_passphrase:
- description:
- - The passphrase for the private key.
- - This is required if the private key is password protected.
- type: str
- version:
- description:
- - The version of the certificate signing request.
- - "The only allowed value according to L(RFC 2986,https://tools.ietf.org/html/rfc2986#section-4.1)
- is 1."
- - This option will no longer accept unsupported values from Ansible 2.14 on.
- type: int
- default: 1
- force:
- description:
- - Should the certificate signing request be forced regenerated by this ansible module.
- type: bool
- default: no
- path:
- description:
- - The name of the file into which the generated OpenSSL certificate signing request will be written.
- type: path
- required: true
- subject:
- description:
- - Key/value pairs that will be present in the subject name field of the certificate signing request.
- - If you need to specify more than one value with the same key, use a list as value.
- type: dict
- version_added: '2.5'
- country_name:
- description:
- - The countryName field of the certificate signing request subject.
- type: str
- aliases: [ C, countryName ]
- state_or_province_name:
- description:
- - The stateOrProvinceName field of the certificate signing request subject.
- type: str
- aliases: [ ST, stateOrProvinceName ]
- locality_name:
- description:
- - The localityName field of the certificate signing request subject.
- type: str
- aliases: [ L, localityName ]
- organization_name:
- description:
- - The organizationName field of the certificate signing request subject.
- type: str
- aliases: [ O, organizationName ]
- organizational_unit_name:
- description:
- - The organizationalUnitName field of the certificate signing request subject.
- type: str
- aliases: [ OU, organizationalUnitName ]
- common_name:
- description:
- - The commonName field of the certificate signing request subject.
- type: str
- aliases: [ CN, commonName ]
- email_address:
- description:
- - The emailAddress field of the certificate signing request subject.
- type: str
- aliases: [ E, emailAddress ]
- subject_alt_name:
- description:
- - SAN extension to attach to the certificate signing request.
- - This can either be a 'comma separated string' or a YAML list.
- - Values must be prefixed by their options. (i.e., C(email), C(URI), C(DNS), C(RID), C(IP), C(dirName),
- C(otherName) and the ones specific to your CA)
- - Note that if no SAN is specified, but a common name, the common
- name will be added as a SAN except if C(useCommonNameForSAN) is
- set to I(false).
- - More at U(https://tools.ietf.org/html/rfc5280#section-4.2.1.6).
- type: list
- elements: str
- aliases: [ subjectAltName ]
- subject_alt_name_critical:
- description:
- - Should the subjectAltName extension be considered as critical.
- type: bool
- aliases: [ subjectAltName_critical ]
- use_common_name_for_san:
- description:
- - If set to C(yes), the module will fill the common name in for
- C(subject_alt_name) with C(DNS:) prefix if no SAN is specified.
- type: bool
- default: yes
- version_added: '2.8'
- aliases: [ useCommonNameForSAN ]
- key_usage:
- description:
- - This defines the purpose (e.g. encipherment, signature, certificate signing)
- of the key contained in the certificate.
- type: list
- elements: str
- aliases: [ keyUsage ]
- key_usage_critical:
- description:
- - Should the keyUsage extension be considered as critical.
- type: bool
- aliases: [ keyUsage_critical ]
- extended_key_usage:
- description:
- - Additional restrictions (e.g. client authentication, server authentication)
- on the allowed purposes for which the public key may be used.
- type: list
- elements: str
- aliases: [ extKeyUsage, extendedKeyUsage ]
- extended_key_usage_critical:
- description:
- - Should the extkeyUsage extension be considered as critical.
- type: bool
- aliases: [ extKeyUsage_critical, extendedKeyUsage_critical ]
- basic_constraints:
- description:
- - Indicates basic constraints, such as if the certificate is a CA.
- type: list
- elements: str
- version_added: '2.5'
- aliases: [ basicConstraints ]
- basic_constraints_critical:
- description:
- - Should the basicConstraints extension be considered as critical.
- type: bool
- version_added: '2.5'
- aliases: [ basicConstraints_critical ]
- ocsp_must_staple:
- description:
- - Indicates that the certificate should contain the OCSP Must Staple
- extension (U(https://tools.ietf.org/html/rfc7633)).
- type: bool
- version_added: '2.5'
- aliases: [ ocspMustStaple ]
- ocsp_must_staple_critical:
- description:
- - Should the OCSP Must Staple extension be considered as critical
- - Note that according to the RFC, this extension should not be marked
- as critical, as old clients not knowing about OCSP Must Staple
- are required to reject such certificates
- (see U(https://tools.ietf.org/html/rfc7633#section-4)).
- type: bool
- version_added: '2.5'
- aliases: [ ocspMustStaple_critical ]
- select_crypto_backend:
- description:
- - Determines which crypto backend to use.
- - The default choice is C(auto), which tries to use C(cryptography) if available, and falls back to C(pyopenssl).
- - If set to C(pyopenssl), will try to use the L(pyOpenSSL,https://pypi.org/project/pyOpenSSL/) library.
- - If set to C(cryptography), will try to use the L(cryptography,https://cryptography.io/) library.
- - Please note that the C(pyopenssl) backend has been deprecated in Ansible 2.9, and will be removed in Ansible 2.13.
- From that point on, only the C(cryptography) backend will be available.
- type: str
- default: auto
- choices: [ auto, cryptography, pyopenssl ]
- version_added: '2.8'
- backup:
- description:
- - Create a backup file including a timestamp so you can get the original
- CSR back if you overwrote it with a new one by accident.
- type: bool
- default: no
- version_added: "2.8"
- create_subject_key_identifier:
- description:
- - Create the Subject Key Identifier from the public key.
- - "Please note that commercial CAs can ignore the value, respectively use a value of
- their own choice instead. Specifying this option is mostly useful for self-signed
- certificates or for own CAs."
- - Note that this is only supported if the C(cryptography) backend is used!
- type: bool
- default: no
- version_added: "2.9"
- subject_key_identifier:
- description:
- - The subject key identifier as a hex string, where two bytes are separated by colons.
- - "Example: C(00:11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff:00:11:22:33)"
- - "Please note that commercial CAs ignore this value, respectively use a value of their
- own choice. Specifying this option is mostly useful for self-signed certificates
- or for own CAs."
- - Note that this option can only be used if I(create_subject_key_identifier) is C(no).
- - Note that this is only supported if the C(cryptography) backend is used!
- type: str
- version_added: "2.9"
- authority_key_identifier:
- description:
- - The authority key identifier as a hex string, where two bytes are separated by colons.
- - "Example: C(00:11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff:00:11:22:33)"
- - If specified, I(authority_cert_issuer) must also be specified.
- - "Please note that commercial CAs ignore this value, respectively use a value of their
- own choice. Specifying this option is mostly useful for self-signed certificates
- or for own CAs."
- - Note that this is only supported if the C(cryptography) backend is used!
- - The C(AuthorityKeyIdentifier) will only be added if at least one of I(authority_key_identifier),
- I(authority_cert_issuer) and I(authority_cert_serial_number) is specified.
- type: str
- version_added: "2.9"
- authority_cert_issuer:
- description:
- - Names that will be present in the authority cert issuer field of the certificate signing request.
- - Values must be prefixed by their options. (i.e., C(email), C(URI), C(DNS), C(RID), C(IP), C(dirName),
- C(otherName) and the ones specific to your CA)
- - "Example: C(DNS:ca.example.org)"
- - If specified, I(authority_key_identifier) must also be specified.
- - "Please note that commercial CAs ignore this value, respectively use a value of their
- own choice. Specifying this option is mostly useful for self-signed certificates
- or for own CAs."
- - Note that this is only supported if the C(cryptography) backend is used!
- - The C(AuthorityKeyIdentifier) will only be added if at least one of I(authority_key_identifier),
- I(authority_cert_issuer) and I(authority_cert_serial_number) is specified.
- type: list
- elements: str
- version_added: "2.9"
- authority_cert_serial_number:
- description:
- - The authority cert serial number.
- - Note that this is only supported if the C(cryptography) backend is used!
- - "Please note that commercial CAs ignore this value, respectively use a value of their
- own choice. Specifying this option is mostly useful for self-signed certificates
- or for own CAs."
- - The C(AuthorityKeyIdentifier) will only be added if at least one of I(authority_key_identifier),
- I(authority_cert_issuer) and I(authority_cert_serial_number) is specified.
- type: int
- version_added: "2.9"
- return_content:
- description:
- - If set to C(yes), will return the (current or generated) CSR's content as I(csr).
- type: bool
- default: no
- version_added: "2.10"
-extends_documentation_fragment:
-- files
-notes:
- - If the certificate signing request already exists it will be checked whether subjectAltName,
- keyUsage, extendedKeyUsage and basicConstraints only contain the requested values, whether
- OCSP Must Staple is as requested, and if the request was signed by the given private key.
-seealso:
-- module: openssl_certificate
-- module: openssl_dhparam
-- module: openssl_pkcs12
-- module: openssl_privatekey
-- module: openssl_publickey
-'''
-
-EXAMPLES = r'''
-- name: Generate an OpenSSL Certificate Signing Request
- openssl_csr:
- path: /etc/ssl/csr/www.ansible.com.csr
- privatekey_path: /etc/ssl/private/ansible.com.pem
- common_name: www.ansible.com
-
-- name: Generate an OpenSSL Certificate Signing Request with an inline key
- openssl_csr:
- path: /etc/ssl/csr/www.ansible.com.csr
- privatekey_content: "{{ private_key_content }}"
- common_name: www.ansible.com
-
-- name: Generate an OpenSSL Certificate Signing Request with a passphrase protected private key
- openssl_csr:
- path: /etc/ssl/csr/www.ansible.com.csr
- privatekey_path: /etc/ssl/private/ansible.com.pem
- privatekey_passphrase: ansible
- common_name: www.ansible.com
-
-- name: Generate an OpenSSL Certificate Signing Request with Subject information
- openssl_csr:
- path: /etc/ssl/csr/www.ansible.com.csr
- privatekey_path: /etc/ssl/private/ansible.com.pem
- country_name: FR
- organization_name: Ansible
- email_address: jdoe@ansible.com
- common_name: www.ansible.com
-
-- name: Generate an OpenSSL Certificate Signing Request with subjectAltName extension
- openssl_csr:
- path: /etc/ssl/csr/www.ansible.com.csr
- privatekey_path: /etc/ssl/private/ansible.com.pem
- subject_alt_name: 'DNS:www.ansible.com,DNS:m.ansible.com'
-
-- name: Generate an OpenSSL CSR with subjectAltName extension with dynamic list
- openssl_csr:
- path: /etc/ssl/csr/www.ansible.com.csr
- privatekey_path: /etc/ssl/private/ansible.com.pem
- subject_alt_name: "{{ item.value | map('regex_replace', '^', 'DNS:') | list }}"
- with_dict:
- dns_server:
- - www.ansible.com
- - m.ansible.com
-
-- name: Force regenerate an OpenSSL Certificate Signing Request
- openssl_csr:
- path: /etc/ssl/csr/www.ansible.com.csr
- privatekey_path: /etc/ssl/private/ansible.com.pem
- force: yes
- common_name: www.ansible.com
-
-- name: Generate an OpenSSL Certificate Signing Request with special key usages
- openssl_csr:
- path: /etc/ssl/csr/www.ansible.com.csr
- privatekey_path: /etc/ssl/private/ansible.com.pem
- common_name: www.ansible.com
- key_usage:
- - digitalSignature
- - keyAgreement
- extended_key_usage:
- - clientAuth
-
-- name: Generate an OpenSSL Certificate Signing Request with OCSP Must Staple
- openssl_csr:
- path: /etc/ssl/csr/www.ansible.com.csr
- privatekey_path: /etc/ssl/private/ansible.com.pem
- common_name: www.ansible.com
- ocsp_must_staple: yes
-'''
-
-RETURN = r'''
-privatekey:
- description:
- - Path to the TLS/SSL private key the CSR was generated for
- - Will be C(none) if the private key has been provided in I(privatekey_content).
- returned: changed or success
- type: str
- sample: /etc/ssl/private/ansible.com.pem
-filename:
- description: Path to the generated Certificate Signing Request
- returned: changed or success
- type: str
- sample: /etc/ssl/csr/www.ansible.com.csr
-subject:
- description: A list of the subject tuples attached to the CSR
- returned: changed or success
- type: list
- elements: list
- sample: "[('CN', 'www.ansible.com'), ('O', 'Ansible')]"
-subjectAltName:
- description: The alternative names this CSR is valid for
- returned: changed or success
- type: list
- elements: str
- sample: [ 'DNS:www.ansible.com', 'DNS:m.ansible.com' ]
-keyUsage:
- description: Purpose for which the public key may be used
- returned: changed or success
- type: list
- elements: str
- sample: [ 'digitalSignature', 'keyAgreement' ]
-extendedKeyUsage:
- description: Additional restriction on the public key purposes
- returned: changed or success
- type: list
- elements: str
- sample: [ 'clientAuth' ]
-basicConstraints:
- description: Indicates if the certificate belongs to a CA
- returned: changed or success
- type: list
- elements: str
- sample: ['CA:TRUE', 'pathLenConstraint:0']
-ocsp_must_staple:
- description: Indicates whether the certificate has the OCSP
- Must Staple feature enabled
- returned: changed or success
- type: bool
- sample: false
-backup_file:
- description: Name of backup file created.
- returned: changed and if I(backup) is C(yes)
- type: str
- sample: /path/to/www.ansible.com.csr.2019-03-09@11:22~
-csr:
- description: The (current or generated) CSR's content.
- returned: if I(state) is C(present) and I(return_content) is C(yes)
- type: str
- version_added: "2.10"
-'''
-
-import abc
-import binascii
-import os
-import traceback
-from distutils.version import LooseVersion
-
-from ansible.module_utils import crypto as crypto_utils
-from ansible.module_utils.basic import AnsibleModule, missing_required_lib
-from ansible.module_utils._text import to_native, to_bytes, to_text
-from ansible.module_utils.compat import ipaddress as compat_ipaddress
-
-MINIMAL_PYOPENSSL_VERSION = '0.15'
-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
-try:
- import cryptography
- import cryptography.x509
- import cryptography.x509.oid
- import cryptography.exceptions
- import cryptography.hazmat.backends
- import cryptography.hazmat.primitives.serialization
- import cryptography.hazmat.primitives.hashes
- CRYPTOGRAPHY_VERSION = LooseVersion(cryptography.__version__)
-except ImportError:
- CRYPTOGRAPHY_IMP_ERR = traceback.format_exc()
- CRYPTOGRAPHY_FOUND = False
-else:
- CRYPTOGRAPHY_FOUND = True
- CRYPTOGRAPHY_MUST_STAPLE_NAME = cryptography.x509.oid.ObjectIdentifier("1.3.6.1.5.5.7.1.24")
- CRYPTOGRAPHY_MUST_STAPLE_VALUE = b"\x30\x03\x02\x01\x05"
-
-
-class CertificateSigningRequestError(crypto_utils.OpenSSLObjectError):
- pass
-
-
-class CertificateSigningRequestBase(crypto_utils.OpenSSLObject):
-
- def __init__(self, module):
- super(CertificateSigningRequestBase, self).__init__(
- module.params['path'],
- module.params['state'],
- module.params['force'],
- module.check_mode
- )
- self.digest = module.params['digest']
- self.privatekey_path = module.params['privatekey_path']
- self.privatekey_content = module.params['privatekey_content']
- if self.privatekey_content is not None:
- self.privatekey_content = self.privatekey_content.encode('utf-8')
- self.privatekey_passphrase = module.params['privatekey_passphrase']
- self.version = module.params['version']
- self.subjectAltName = module.params['subject_alt_name']
- self.subjectAltName_critical = module.params['subject_alt_name_critical']
- self.keyUsage = module.params['key_usage']
- self.keyUsage_critical = module.params['key_usage_critical']
- self.extendedKeyUsage = module.params['extended_key_usage']
- self.extendedKeyUsage_critical = module.params['extended_key_usage_critical']
- self.basicConstraints = module.params['basic_constraints']
- self.basicConstraints_critical = module.params['basic_constraints_critical']
- self.ocspMustStaple = module.params['ocsp_must_staple']
- self.ocspMustStaple_critical = module.params['ocsp_must_staple_critical']
- self.create_subject_key_identifier = module.params['create_subject_key_identifier']
- self.subject_key_identifier = module.params['subject_key_identifier']
- self.authority_key_identifier = module.params['authority_key_identifier']
- self.authority_cert_issuer = module.params['authority_cert_issuer']
- self.authority_cert_serial_number = module.params['authority_cert_serial_number']
- self.request = None
- self.privatekey = None
- self.csr_bytes = None
- self.return_content = module.params['return_content']
-
- if self.create_subject_key_identifier and self.subject_key_identifier is not None:
- module.fail_json(msg='subject_key_identifier cannot be specified if create_subject_key_identifier is true')
-
- self.backup = module.params['backup']
- self.backup_file = None
-
- self.subject = [
- ('C', module.params['country_name']),
- ('ST', module.params['state_or_province_name']),
- ('L', module.params['locality_name']),
- ('O', module.params['organization_name']),
- ('OU', module.params['organizational_unit_name']),
- ('CN', module.params['common_name']),
- ('emailAddress', module.params['email_address']),
- ]
-
- if module.params['subject']:
- self.subject = self.subject + crypto_utils.parse_name_field(module.params['subject'])
- self.subject = [(entry[0], entry[1]) for entry in self.subject if entry[1]]
-
- if not self.subjectAltName and module.params['use_common_name_for_san']:
- for sub in self.subject:
- if sub[0] in ('commonName', 'CN'):
- self.subjectAltName = ['DNS:%s' % sub[1]]
- break
-
- if self.subject_key_identifier is not None:
- try:
- self.subject_key_identifier = binascii.unhexlify(self.subject_key_identifier.replace(':', ''))
- except Exception as e:
- raise CertificateSigningRequestError('Cannot parse subject_key_identifier: {0}'.format(e))
-
- if self.authority_key_identifier is not None:
- try:
- self.authority_key_identifier = binascii.unhexlify(self.authority_key_identifier.replace(':', ''))
- except Exception as e:
- raise CertificateSigningRequestError('Cannot parse authority_key_identifier: {0}'.format(e))
-
- @abc.abstractmethod
- def _generate_csr(self):
- pass
-
- def generate(self, module):
- '''Generate the certificate signing request.'''
- if not self.check(module, perms_required=False) or self.force:
- result = self._generate_csr()
- if self.backup:
- self.backup_file = module.backup_local(self.path)
- if self.return_content:
- self.csr_bytes = result
- crypto_utils.write_file(module, result)
- self.changed = True
-
- file_args = module.load_file_common_arguments(module.params)
- if module.set_fs_attributes_if_different(file_args, False):
- self.changed = True
-
- @abc.abstractmethod
- def _load_private_key(self):
- pass
-
- @abc.abstractmethod
- def _check_csr(self):
- pass
-
- def check(self, module, perms_required=True):
- """Ensure the resource is in its desired state."""
- state_and_perms = super(CertificateSigningRequestBase, self).check(module, perms_required)
-
- self._load_private_key()
-
- if not state_and_perms:
- return False
-
- return self._check_csr()
-
- def remove(self, module):
- if self.backup:
- self.backup_file = module.backup_local(self.path)
- super(CertificateSigningRequestBase, self).remove(module)
-
- def dump(self):
- '''Serialize the object into a dictionary.'''
-
- result = {
- 'privatekey': self.privatekey_path,
- 'filename': self.path,
- 'subject': self.subject,
- 'subjectAltName': self.subjectAltName,
- 'keyUsage': self.keyUsage,
- 'extendedKeyUsage': self.extendedKeyUsage,
- 'basicConstraints': self.basicConstraints,
- 'ocspMustStaple': self.ocspMustStaple,
- 'changed': self.changed
- }
- if self.backup_file:
- result['backup_file'] = self.backup_file
- if self.return_content:
- if self.csr_bytes is None:
- self.csr_bytes = crypto_utils.load_file_if_exists(self.path, ignore_errors=True)
- result['csr'] = self.csr_bytes.decode('utf-8') if self.csr_bytes else None
-
- return result
-
-
-class CertificateSigningRequestPyOpenSSL(CertificateSigningRequestBase):
-
- def __init__(self, module):
- if module.params['create_subject_key_identifier']:
- module.fail_json(msg='You cannot use create_subject_key_identifier with the pyOpenSSL backend!')
- for o in ('subject_key_identifier', 'authority_key_identifier', 'authority_cert_issuer', 'authority_cert_serial_number'):
- if module.params[o] is not None:
- module.fail_json(msg='You cannot use {0} with the pyOpenSSL backend!'.format(o))
- super(CertificateSigningRequestPyOpenSSL, self).__init__(module)
-
- def _generate_csr(self):
- 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.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.request = req
-
- return crypto.dump_certificate_request(crypto.FILETYPE_PEM, self.request)
-
- def _load_private_key(self):
- try:
- self.privatekey = crypto_utils.load_privatekey(
- path=self.privatekey_path,
- content=self.privatekey_content,
- passphrase=self.privatekey_passphrase
- )
- except crypto_utils.OpenSSLBadPassphraseError as exc:
- raise CertificateSigningRequestError(exc)
-
- def _normalize_san(self, 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:'):
- ip = compat_ipaddress.ip_address(san[3:])
- san = 'IP:{0}'.format(ip.compressed)
- return san
-
- 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 = [self._normalize_san(altname.strip()) for altname in
- to_text(altnames_ext, errors='surrogate_or_strict').split(',') if altname.strip()]
- if self.subjectAltName:
- if (set(altnames) != set([self._normalize_san(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_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))
-
- def _check_signature(csr):
- try:
- return csr.verify(self.privatekey)
- except crypto.Error:
- return False
-
- try:
- csr = crypto_utils.load_certificate_request(self.path, backend='pyopenssl')
- except Exception as dummy:
- return False
-
- return _check_subject(csr) and _check_extensions(csr) and _check_signature(csr)
-
-
-class CertificateSigningRequestCryptography(CertificateSigningRequestBase):
-
- def __init__(self, module):
- super(CertificateSigningRequestCryptography, self).__init__(module)
- self.cryptography_backend = cryptography.hazmat.backends.default_backend()
- self.module = module
- if self.version != 1:
- module.warn('The cryptography backend only supports version 1. (The only valid value according to RFC 2986.)')
-
- def _generate_csr(self):
- csr = cryptography.x509.CertificateSigningRequestBuilder()
- try:
- csr = csr.subject_name(cryptography.x509.Name([
- cryptography.x509.NameAttribute(crypto_utils.cryptography_name_to_oid(entry[0]), to_text(entry[1])) for entry in self.subject
- ]))
- except ValueError as e:
- raise CertificateSigningRequestError(e)
-
- if self.subjectAltName:
- csr = csr.add_extension(cryptography.x509.SubjectAlternativeName([
- crypto_utils.cryptography_get_name(name) for name in self.subjectAltName
- ]), critical=self.subjectAltName_critical)
-
- if self.keyUsage:
- params = crypto_utils.cryptography_parse_key_usage_params(self.keyUsage)
- csr = csr.add_extension(cryptography.x509.KeyUsage(**params), critical=self.keyUsage_critical)
-
- if self.extendedKeyUsage:
- usages = [crypto_utils.cryptography_name_to_oid(usage) for usage in self.extendedKeyUsage]
- csr = csr.add_extension(cryptography.x509.ExtendedKeyUsage(usages), critical=self.extendedKeyUsage_critical)
-
- if self.basicConstraints:
- params = {}
- ca, path_length = crypto_utils.cryptography_get_basic_constraints(self.basicConstraints)
- csr = csr.add_extension(cryptography.x509.BasicConstraints(ca, path_length), critical=self.basicConstraints_critical)
-
- if self.ocspMustStaple:
- try:
- # This only works with cryptography >= 2.1
- csr = csr.add_extension(cryptography.x509.TLSFeature([cryptography.x509.TLSFeatureType.status_request]), critical=self.ocspMustStaple_critical)
- except AttributeError as dummy:
- csr = csr.add_extension(
- cryptography.x509.UnrecognizedExtension(CRYPTOGRAPHY_MUST_STAPLE_NAME, CRYPTOGRAPHY_MUST_STAPLE_VALUE),
- critical=self.ocspMustStaple_critical
- )
-
- if self.create_subject_key_identifier:
- csr = csr.add_extension(
- cryptography.x509.SubjectKeyIdentifier.from_public_key(self.privatekey.public_key()),
- critical=False
- )
- elif self.subject_key_identifier is not None:
- csr = csr.add_extension(cryptography.x509.SubjectKeyIdentifier(self.subject_key_identifier), critical=False)
-
- if self.authority_key_identifier is not None or self.authority_cert_issuer is not None or self.authority_cert_serial_number is not None:
- issuers = None
- if self.authority_cert_issuer is not None:
- issuers = [crypto_utils.cryptography_get_name(n) for n in self.authority_cert_issuer]
- csr = csr.add_extension(
- cryptography.x509.AuthorityKeyIdentifier(self.authority_key_identifier, issuers, self.authority_cert_serial_number),
- critical=False
- )
-
- digest = None
- if crypto_utils.cryptography_key_needs_digest_for_signing(self.privatekey):
- if self.digest == 'sha256':
- digest = cryptography.hazmat.primitives.hashes.SHA256()
- elif self.digest == 'sha384':
- digest = cryptography.hazmat.primitives.hashes.SHA384()
- elif self.digest == 'sha512':
- digest = cryptography.hazmat.primitives.hashes.SHA512()
- elif self.digest == 'sha1':
- digest = cryptography.hazmat.primitives.hashes.SHA1()
- elif self.digest == 'md5':
- digest = cryptography.hazmat.primitives.hashes.MD5()
- # FIXME
- else:
- raise CertificateSigningRequestError('Unsupported digest "{0}"'.format(self.digest))
- try:
- self.request = csr.sign(self.privatekey, digest, self.cryptography_backend)
- except TypeError as e:
- if str(e) == 'Algorithm must be a registered hash algorithm.' and digest is None:
- self.module.fail_json(msg='Signing with Ed25519 and Ed448 keys requires cryptography 2.8 or newer.')
- raise
-
- return self.request.public_bytes(cryptography.hazmat.primitives.serialization.Encoding.PEM)
-
- def _load_private_key(self):
- try:
- if self.privatekey_content is not None:
- content = self.privatekey_content
- else:
- with open(self.privatekey_path, 'rb') as f:
- content = f.read()
- self.privatekey = cryptography.hazmat.primitives.serialization.load_pem_private_key(
- content,
- None if self.privatekey_passphrase is None else to_bytes(self.privatekey_passphrase),
- backend=self.cryptography_backend
- )
- except Exception as e:
- raise CertificateSigningRequestError(e)
-
- def _check_csr(self):
- def _check_subject(csr):
- subject = [(crypto_utils.cryptography_name_to_oid(entry[0]), entry[1]) for entry in self.subject]
- current_subject = [(sub.oid, sub.value) for sub in csr.subject]
- return set(subject) == set(current_subject)
-
- def _find_extension(extensions, exttype):
- return next(
- (ext for ext in extensions if isinstance(ext.value, exttype)),
- None
- )
-
- def _check_subjectAltName(extensions):
- current_altnames_ext = _find_extension(extensions, cryptography.x509.SubjectAlternativeName)
- current_altnames = [str(altname) for altname in current_altnames_ext.value] if current_altnames_ext else []
- altnames = [str(crypto_utils.cryptography_get_name(altname)) for altname in self.subjectAltName] if self.subjectAltName else []
- if set(altnames) != set(current_altnames):
- return False
- if altnames:
- if current_altnames_ext.critical != self.subjectAltName_critical:
- return False
- return True
-
- def _check_keyUsage(extensions):
- current_keyusage_ext = _find_extension(extensions, cryptography.x509.KeyUsage)
- if not self.keyUsage:
- return current_keyusage_ext is None
- elif current_keyusage_ext is None:
- return False
- params = crypto_utils.cryptography_parse_key_usage_params(self.keyUsage)
- for param in params:
- if getattr(current_keyusage_ext.value, '_' + param) != params[param]:
- return False
- if current_keyusage_ext.critical != self.keyUsage_critical:
- return False
- return True
-
- def _check_extenededKeyUsage(extensions):
- current_usages_ext = _find_extension(extensions, cryptography.x509.ExtendedKeyUsage)
- current_usages = [str(usage) for usage in current_usages_ext.value] if current_usages_ext else []
- usages = [str(crypto_utils.cryptography_name_to_oid(usage)) for usage in self.extendedKeyUsage] if self.extendedKeyUsage else []
- if set(current_usages) != set(usages):
- return False
- if usages:
- if current_usages_ext.critical != self.extendedKeyUsage_critical:
- return False
- return True
-
- def _check_basicConstraints(extensions):
- bc_ext = _find_extension(extensions, cryptography.x509.BasicConstraints)
- current_ca = bc_ext.value.ca if bc_ext else False
- current_path_length = bc_ext.value.path_length if bc_ext else None
- ca, path_length = crypto_utils.cryptography_get_basic_constraints(self.basicConstraints)
- # Check CA flag
- if ca != current_ca:
- return False
- # Check path length
- if path_length != current_path_length:
- return False
- # Check criticality
- if self.basicConstraints:
- if bc_ext.critical != self.basicConstraints_critical:
- return False
- return True
-
- def _check_ocspMustStaple(extensions):
- try:
- # This only works with cryptography >= 2.1
- tlsfeature_ext = _find_extension(extensions, cryptography.x509.TLSFeature)
- has_tlsfeature = True
- except AttributeError as dummy:
- tlsfeature_ext = next(
- (ext for ext in extensions if ext.value.oid == CRYPTOGRAPHY_MUST_STAPLE_NAME),
- None
- )
- has_tlsfeature = False
- if self.ocspMustStaple:
- if not tlsfeature_ext or tlsfeature_ext.critical != self.ocspMustStaple_critical:
- return False
- if has_tlsfeature:
- return cryptography.x509.TLSFeatureType.status_request in tlsfeature_ext.value
- else:
- return tlsfeature_ext.value.value == CRYPTOGRAPHY_MUST_STAPLE_VALUE
- else:
- return tlsfeature_ext is None
-
- def _check_subject_key_identifier(extensions):
- ext = _find_extension(extensions, cryptography.x509.SubjectKeyIdentifier)
- if self.create_subject_key_identifier or self.subject_key_identifier is not None:
- if not ext or ext.critical:
- return False
- if self.create_subject_key_identifier:
- digest = cryptography.x509.SubjectKeyIdentifier.from_public_key(self.privatekey.public_key()).digest
- return ext.value.digest == digest
- else:
- return ext.value.digest == self.subject_key_identifier
- else:
- return ext is None
-
- def _check_authority_key_identifier(extensions):
- ext = _find_extension(extensions, cryptography.x509.AuthorityKeyIdentifier)
- if self.authority_key_identifier is not None or self.authority_cert_issuer is not None or self.authority_cert_serial_number is not None:
- if not ext or ext.critical:
- return False
- aci = None
- csr_aci = None
- if self.authority_cert_issuer is not None:
- aci = [str(crypto_utils.cryptography_get_name(n)) for n in self.authority_cert_issuer]
- if ext.value.authority_cert_issuer is not None:
- csr_aci = [str(n) for n in ext.value.authority_cert_issuer]
- return (ext.value.key_identifier == self.authority_key_identifier
- and csr_aci == aci
- and ext.value.authority_cert_serial_number == self.authority_cert_serial_number)
- else:
- return ext is None
-
- def _check_extensions(csr):
- extensions = csr.extensions
- return (_check_subjectAltName(extensions) and _check_keyUsage(extensions) and
- _check_extenededKeyUsage(extensions) and _check_basicConstraints(extensions) and
- _check_ocspMustStaple(extensions) and _check_subject_key_identifier(extensions) and
- _check_authority_key_identifier(extensions))
-
- def _check_signature(csr):
- if not csr.is_signature_valid:
- return False
- # To check whether public key of CSR belongs to private key,
- # encode both public keys and compare PEMs.
- key_a = csr.public_key().public_bytes(
- cryptography.hazmat.primitives.serialization.Encoding.PEM,
- cryptography.hazmat.primitives.serialization.PublicFormat.SubjectPublicKeyInfo
- )
- key_b = self.privatekey.public_key().public_bytes(
- cryptography.hazmat.primitives.serialization.Encoding.PEM,
- cryptography.hazmat.primitives.serialization.PublicFormat.SubjectPublicKeyInfo
- )
- return key_a == key_b
-
- try:
- csr = crypto_utils.load_certificate_request(self.path, backend='cryptography')
- except Exception as dummy:
- return False
-
- return _check_subject(csr) and _check_extensions(csr) and _check_signature(csr)
-
-
-def main():
- module = AnsibleModule(
- argument_spec=dict(
- state=dict(type='str', default='present', choices=['absent', 'present']),
- digest=dict(type='str', default='sha256'),
- privatekey_path=dict(type='path'),
- privatekey_content=dict(type='str'),
- privatekey_passphrase=dict(type='str', no_log=True),
- version=dict(type='int', default=1),
- force=dict(type='bool', default=False),
- path=dict(type='path', required=True),
- subject=dict(type='dict'),
- country_name=dict(type='str', aliases=['C', 'countryName']),
- state_or_province_name=dict(type='str', aliases=['ST', 'stateOrProvinceName']),
- locality_name=dict(type='str', aliases=['L', 'localityName']),
- organization_name=dict(type='str', aliases=['O', 'organizationName']),
- organizational_unit_name=dict(type='str', aliases=['OU', 'organizationalUnitName']),
- common_name=dict(type='str', aliases=['CN', 'commonName']),
- email_address=dict(type='str', aliases=['E', 'emailAddress']),
- subject_alt_name=dict(type='list', elements='str', aliases=['subjectAltName']),
- subject_alt_name_critical=dict(type='bool', default=False, aliases=['subjectAltName_critical']),
- use_common_name_for_san=dict(type='bool', default=True, aliases=['useCommonNameForSAN']),
- key_usage=dict(type='list', elements='str', aliases=['keyUsage']),
- key_usage_critical=dict(type='bool', default=False, aliases=['keyUsage_critical']),
- extended_key_usage=dict(type='list', elements='str', aliases=['extKeyUsage', 'extendedKeyUsage']),
- extended_key_usage_critical=dict(type='bool', default=False, aliases=['extKeyUsage_critical', 'extendedKeyUsage_critical']),
- basic_constraints=dict(type='list', elements='str', aliases=['basicConstraints']),
- basic_constraints_critical=dict(type='bool', default=False, aliases=['basicConstraints_critical']),
- ocsp_must_staple=dict(type='bool', default=False, aliases=['ocspMustStaple']),
- ocsp_must_staple_critical=dict(type='bool', default=False, aliases=['ocspMustStaple_critical']),
- backup=dict(type='bool', default=False),
- create_subject_key_identifier=dict(type='bool', default=False),
- subject_key_identifier=dict(type='str'),
- authority_key_identifier=dict(type='str'),
- authority_cert_issuer=dict(type='list', elements='str'),
- authority_cert_serial_number=dict(type='int'),
- select_crypto_backend=dict(type='str', default='auto', choices=['auto', 'cryptography', 'pyopenssl']),
- return_content=dict(type='bool', default=False),
- ),
- required_together=[('authority_cert_issuer', 'authority_cert_serial_number')],
- required_if=[('state', 'present', ['privatekey_path', 'privatekey_content'], True)],
- mutually_exclusive=(
- ['privatekey_path', 'privatekey_content'],
- ),
- add_file_common_args=True,
- supports_check_mode=True,
- )
-
- if module.params['version'] != 1:
- module.deprecate('The version option will only support allowed values from Ansible 2.14 on. '
- 'Currently, only the value 1 is allowed by RFC 2986', version='2.14')
-
- base_dir = os.path.dirname(module.params['path']) or '.'
- if not os.path.isdir(base_dir):
- module.fail_json(name=base_dir, msg='The directory %s does not exist or the file is not a directory' % base_dir)
-
- backend = module.params['select_crypto_backend']
- if backend == 'auto':
- # Detection what is possible
- 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
- if can_use_cryptography:
- backend = 'cryptography'
- elif can_use_pyopenssl:
- backend = 'pyopenssl'
-
- # Success?
- if backend == 'auto':
- module.fail_json(msg=("Can't detect any of the required Python libraries "
- "cryptography (>= {0}) or PyOpenSSL (>= {1})").format(
- MINIMAL_CRYPTOGRAPHY_VERSION,
- MINIMAL_PYOPENSSL_VERSION))
- try:
- if backend == 'pyopenssl':
- 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.13')
- csr = CertificateSigningRequestPyOpenSSL(module)
- elif backend == 'cryptography':
- if not CRYPTOGRAPHY_FOUND:
- module.fail_json(msg=missing_required_lib('cryptography >= {0}'.format(MINIMAL_CRYPTOGRAPHY_VERSION)),
- exception=CRYPTOGRAPHY_IMP_ERR)
- csr = CertificateSigningRequestCryptography(module)
-
- if module.params['state'] == 'present':
- if module.check_mode:
- result = csr.dump()
- result['changed'] = module.params['force'] or not csr.check(module)
- module.exit_json(**result)
-
- csr.generate(module)
-
- else:
- if module.check_mode:
- result = csr.dump()
- result['changed'] = os.path.exists(module.params['path'])
- module.exit_json(**result)
-
- csr.remove(module)
-
- result = csr.dump()
- module.exit_json(**result)
- except crypto_utils.OpenSSLObjectError as exc:
- module.fail_json(msg=to_native(exc))
-
-
-if __name__ == "__main__":
- main()
diff --git a/lib/ansible/modules/crypto/openssl_csr_info.py b/lib/ansible/modules/crypto/openssl_csr_info.py
deleted file mode 100644
index 713ee33808..0000000000
--- a/lib/ansible/modules/crypto/openssl_csr_info.py
+++ /dev/null
@@ -1,667 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-
-# Copyright: (c) 2016-2017, Yanis Guenane <yanis+ansible@guenane.org>
-# Copyright: (c) 2017, Markus Teufelberger <mteufelberger+ansible@mgit.at>
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'community'}
-
-DOCUMENTATION = r'''
----
-module: openssl_csr_info
-version_added: '2.8'
-short_description: Provide information of OpenSSL Certificate Signing Requests (CSR)
-description:
- - 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
- variables are still returned.
- - It uses the pyOpenSSL or cryptography python library to interact with OpenSSL. If both the
- cryptography and PyOpenSSL libraries are available (and meet the minimum version requirements)
- cryptography will be preferred as a backend over PyOpenSSL (unless the backend is forced with
- C(select_crypto_backend)). Please note that the PyOpenSSL backend was deprecated in Ansible 2.9
- and will be removed in Ansible 2.13.
-requirements:
- - PyOpenSSL >= 0.15 or cryptography >= 1.3
-author:
- - Felix Fontein (@felixfontein)
- - Yanis Guenane (@Spredzy)
-options:
- path:
- description:
- - Remote absolute path where the CSR file is loaded from.
- - Either I(path) or I(content) must be specified, but not both.
- type: path
- content:
- description:
- - Content of the CSR file.
- - Either I(path) or I(content) must be specified, but not both.
- type: str
- version_added: "2.10"
- select_crypto_backend:
- description:
- - Determines which crypto backend to use.
- - The default choice is C(auto), which tries to use C(cryptography) if available, and falls back to C(pyopenssl).
- - If set to C(pyopenssl), will try to use the L(pyOpenSSL,https://pypi.org/project/pyOpenSSL/) library.
- - If set to C(cryptography), will try to use the L(cryptography,https://cryptography.io/) library.
- - Please note that the C(pyopenssl) backend has been deprecated in Ansible 2.9, and will be removed in Ansible 2.13.
- From that point on, only the C(cryptography) backend will be available.
- type: str
- default: auto
- choices: [ auto, cryptography, pyopenssl ]
-
-seealso:
-- module: openssl_csr
-'''
-
-EXAMPLES = r'''
-- name: Generate an OpenSSL Certificate Signing Request
- openssl_csr:
- path: /etc/ssl/csr/www.ansible.com.csr
- privatekey_path: /etc/ssl/private/ansible.com.pem
- common_name: www.ansible.com
-
-- name: Get information on the CSR
- openssl_csr_info:
- path: /etc/ssl/csr/www.ansible.com.csr
- register: result
-
-- name: Dump information
- debug:
- var: result
-'''
-
-RETURN = r'''
-signature_valid:
- description:
- - Whether the CSR's signature is valid.
- - In case the check returns C(no), the module will fail.
- returned: success
- type: bool
-basic_constraints:
- description: Entries in the C(basic_constraints) extension, or C(none) if extension is not present.
- returned: success
- type: list
- elements: str
- sample: "[CA:TRUE, pathlen:1]"
-basic_constraints_critical:
- description: Whether the C(basic_constraints) extension is critical.
- returned: success
- type: bool
-extended_key_usage:
- description: Entries in the C(extended_key_usage) extension, or C(none) if extension is not present.
- returned: success
- type: list
- elements: str
- sample: "[Biometric Info, DVCS, Time Stamping]"
-extended_key_usage_critical:
- description: Whether the C(extended_key_usage) extension is critical.
- returned: success
- type: bool
-extensions_by_oid:
- description: Returns a dictionary for every extension OID
- returned: success
- type: dict
- contains:
- critical:
- description: Whether the extension is critical.
- returned: success
- type: bool
- value:
- description: The Base64 encoded value (in DER format) of the extension
- returned: success
- type: str
- sample: "MAMCAQU="
- sample: '{"1.3.6.1.5.5.7.1.24": { "critical": false, "value": "MAMCAQU="}}'
-key_usage:
- description: Entries in the C(key_usage) extension, or C(none) if extension is not present.
- returned: success
- type: str
- sample: "[Key Agreement, Data Encipherment]"
-key_usage_critical:
- description: Whether the C(key_usage) extension is critical.
- returned: success
- type: bool
-subject_alt_name:
- description: Entries in the C(subject_alt_name) extension, or C(none) if extension is not present.
- returned: success
- type: list
- elements: str
- sample: "[DNS:www.ansible.com, IP:1.2.3.4]"
-subject_alt_name_critical:
- description: Whether the C(subject_alt_name) extension is critical.
- returned: success
- type: bool
-ocsp_must_staple:
- description: C(yes) if the OCSP Must Staple extension is present, C(none) otherwise.
- returned: success
- type: bool
-ocsp_must_staple_critical:
- description: Whether the C(ocsp_must_staple) extension is critical.
- returned: success
- type: bool
-subject:
- description:
- - The CSR's subject as a dictionary.
- - Note that for repeated values, only the last one will be returned.
- returned: success
- type: dict
- sample: '{"commonName": "www.example.com", "emailAddress": "test@example.com"}'
-subject_ordered:
- description: The CSR's subject as an ordered list of tuples.
- returned: success
- type: list
- elements: list
- sample: '[["commonName", "www.example.com"], ["emailAddress": "test@example.com"]]'
- version_added: "2.9"
-public_key:
- description: CSR's public key in PEM format
- returned: success
- type: str
- sample: "-----BEGIN PUBLIC KEY-----\nMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A..."
-public_key_fingerprints:
- description:
- - Fingerprints of CSR's public key.
- - For every hash algorithm available, the fingerprint is computed.
- returned: success
- type: dict
- sample: "{'sha256': 'd4:b3:aa:6d:c8:04:ce:4e:ba:f6:29:4d:92:a3:94:b0:c2:ff:bd:bf:33:63:11:43:34:0f:51:b0:95:09:2f:63',
- 'sha512': 'f7:07:4a:f0:b0:f0:e6:8b:95:5f:f9:e6:61:0a:32:68:f1..."
-subject_key_identifier:
- description:
- - The CSR's subject key identifier.
- - The identifier is returned in hexadecimal, with C(:) used to separate bytes.
- - Is C(none) if the C(SubjectKeyIdentifier) extension is not present.
- returned: success and if the pyOpenSSL backend is I(not) used
- type: str
- sample: '00:11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff:00:11:22:33'
- version_added: "2.9"
-authority_key_identifier:
- description:
- - The CSR's authority key identifier.
- - The identifier is returned in hexadecimal, with C(:) used to separate bytes.
- - Is C(none) if the C(AuthorityKeyIdentifier) extension is not present.
- returned: success and if the pyOpenSSL backend is I(not) used
- type: str
- sample: '00:11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff:00:11:22:33'
- version_added: "2.9"
-authority_cert_issuer:
- description:
- - The CSR's authority cert issuer as a list of general names.
- - Is C(none) if the C(AuthorityKeyIdentifier) extension is not present.
- returned: success and if the pyOpenSSL backend is I(not) used
- type: list
- elements: str
- sample: "[DNS:www.ansible.com, IP:1.2.3.4]"
- version_added: "2.9"
-authority_cert_serial_number:
- description:
- - The CSR's authority cert serial number.
- - Is C(none) if the C(AuthorityKeyIdentifier) extension is not present.
- returned: success and if the pyOpenSSL backend is I(not) used
- type: int
- sample: '12345'
- version_added: "2.9"
-'''
-
-
-import abc
-import binascii
-import os
-import traceback
-from distutils.version import LooseVersion
-
-from ansible.module_utils import crypto as crypto_utils
-from ansible.module_utils.basic import AnsibleModule, missing_required_lib
-from ansible.module_utils._text import to_native, to_text, to_bytes
-from ansible.module_utils.compat import ipaddress as compat_ipaddress
-
-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
-try:
- import cryptography
- from cryptography import x509
- from cryptography.hazmat.primitives import serialization
- CRYPTOGRAPHY_VERSION = LooseVersion(cryptography.__version__)
-except ImportError:
- CRYPTOGRAPHY_IMP_ERR = traceback.format_exc()
- CRYPTOGRAPHY_FOUND = False
-else:
- CRYPTOGRAPHY_FOUND = True
-
-
-TIMESTAMP_FORMAT = "%Y%m%d%H%M%SZ"
-
-
-class CertificateSigningRequestInfo(crypto_utils.OpenSSLObject):
- def __init__(self, module, backend):
- super(CertificateSigningRequestInfo, self).__init__(
- module.params['path'] or '',
- 'present',
- False,
- module.check_mode,
- )
- self.backend = backend
- self.module = module
- self.content = module.params['content']
- if self.content is not None:
- self.content = self.content.encode('utf-8')
-
- def generate(self):
- # Empty method because crypto_utils.OpenSSLObject wants this
- pass
-
- def dump(self):
- # Empty method because crypto_utils.OpenSSLObject wants this
- pass
-
- @abc.abstractmethod
- def _get_subject_ordered(self):
- pass
-
- @abc.abstractmethod
- def _get_key_usage(self):
- pass
-
- @abc.abstractmethod
- def _get_extended_key_usage(self):
- pass
-
- @abc.abstractmethod
- def _get_basic_constraints(self):
- pass
-
- @abc.abstractmethod
- def _get_ocsp_must_staple(self):
- pass
-
- @abc.abstractmethod
- def _get_subject_alt_name(self):
- pass
-
- @abc.abstractmethod
- def _get_public_key(self, binary):
- pass
-
- @abc.abstractmethod
- def _get_subject_key_identifier(self):
- pass
-
- @abc.abstractmethod
- def _get_authority_key_identifier(self):
- pass
-
- @abc.abstractmethod
- def _get_all_extensions(self):
- pass
-
- @abc.abstractmethod
- def _is_signature_valid(self):
- pass
-
- def get_info(self):
- result = dict()
- self.csr = crypto_utils.load_certificate_request(self.path, content=self.content, backend=self.backend)
-
- subject = self._get_subject_ordered()
- result['subject'] = dict()
- for k, v in subject:
- result['subject'][k] = v
- result['subject_ordered'] = subject
- result['key_usage'], result['key_usage_critical'] = self._get_key_usage()
- result['extended_key_usage'], result['extended_key_usage_critical'] = self._get_extended_key_usage()
- result['basic_constraints'], result['basic_constraints_critical'] = self._get_basic_constraints()
- result['ocsp_must_staple'], result['ocsp_must_staple_critical'] = self._get_ocsp_must_staple()
- result['subject_alt_name'], result['subject_alt_name_critical'] = self._get_subject_alt_name()
-
- result['public_key'] = self._get_public_key(binary=False)
- pk = self._get_public_key(binary=True)
- result['public_key_fingerprints'] = crypto_utils.get_fingerprint_of_bytes(pk) if pk is not None else dict()
-
- if self.backend != 'pyopenssl':
- ski = self._get_subject_key_identifier()
- if ski is not None:
- ski = to_native(binascii.hexlify(ski))
- ski = ':'.join([ski[i:i + 2] for i in range(0, len(ski), 2)])
- result['subject_key_identifier'] = ski
-
- aki, aci, acsn = self._get_authority_key_identifier()
- if aki is not None:
- aki = to_native(binascii.hexlify(aki))
- aki = ':'.join([aki[i:i + 2] for i in range(0, len(aki), 2)])
- result['authority_key_identifier'] = aki
- result['authority_cert_issuer'] = aci
- result['authority_cert_serial_number'] = acsn
-
- result['extensions_by_oid'] = self._get_all_extensions()
-
- result['signature_valid'] = self._is_signature_valid()
- if not result['signature_valid']:
- self.module.fail_json(
- msg='CSR signature is invalid!',
- **result
- )
- return result
-
-
-class CertificateSigningRequestInfoCryptography(CertificateSigningRequestInfo):
- """Validate the supplied CSR, using the cryptography backend"""
- def __init__(self, module):
- super(CertificateSigningRequestInfoCryptography, self).__init__(module, 'cryptography')
-
- def _get_subject_ordered(self):
- result = []
- for attribute in self.csr.subject:
- result.append([crypto_utils.cryptography_oid_to_name(attribute.oid), attribute.value])
- return result
-
- def _get_key_usage(self):
- try:
- current_key_ext = self.csr.extensions.get_extension_for_class(x509.KeyUsage)
- current_key_usage = current_key_ext.value
- key_usage = dict(
- digital_signature=current_key_usage.digital_signature,
- content_commitment=current_key_usage.content_commitment,
- key_encipherment=current_key_usage.key_encipherment,
- data_encipherment=current_key_usage.data_encipherment,
- key_agreement=current_key_usage.key_agreement,
- key_cert_sign=current_key_usage.key_cert_sign,
- crl_sign=current_key_usage.crl_sign,
- encipher_only=False,
- decipher_only=False,
- )
- if key_usage['key_agreement']:
- key_usage.update(dict(
- encipher_only=current_key_usage.encipher_only,
- decipher_only=current_key_usage.decipher_only
- ))
-
- key_usage_names = dict(
- digital_signature='Digital Signature',
- content_commitment='Non Repudiation',
- key_encipherment='Key Encipherment',
- data_encipherment='Data Encipherment',
- key_agreement='Key Agreement',
- key_cert_sign='Certificate Sign',
- crl_sign='CRL Sign',
- encipher_only='Encipher Only',
- decipher_only='Decipher Only',
- )
- return sorted([
- key_usage_names[name] for name, value in key_usage.items() if value
- ]), current_key_ext.critical
- except cryptography.x509.ExtensionNotFound:
- return None, False
-
- def _get_extended_key_usage(self):
- try:
- ext_keyusage_ext = self.csr.extensions.get_extension_for_class(x509.ExtendedKeyUsage)
- return sorted([
- crypto_utils.cryptography_oid_to_name(eku) for eku in ext_keyusage_ext.value
- ]), ext_keyusage_ext.critical
- except cryptography.x509.ExtensionNotFound:
- return None, False
-
- def _get_basic_constraints(self):
- try:
- ext_keyusage_ext = self.csr.extensions.get_extension_for_class(x509.BasicConstraints)
- result = []
- result.append('CA:{0}'.format('TRUE' if ext_keyusage_ext.value.ca else 'FALSE'))
- if ext_keyusage_ext.value.path_length is not None:
- result.append('pathlen:{0}'.format(ext_keyusage_ext.value.path_length))
- return sorted(result), ext_keyusage_ext.critical
- except cryptography.x509.ExtensionNotFound:
- return None, False
-
- def _get_ocsp_must_staple(self):
- try:
- try:
- # This only works with cryptography >= 2.1
- tlsfeature_ext = self.csr.extensions.get_extension_for_class(x509.TLSFeature)
- value = cryptography.x509.TLSFeatureType.status_request in tlsfeature_ext.value
- except AttributeError as dummy:
- # Fallback for cryptography < 2.1
- oid = x509.oid.ObjectIdentifier("1.3.6.1.5.5.7.1.24")
- tlsfeature_ext = self.csr.extensions.get_extension_for_oid(oid)
- value = tlsfeature_ext.value.value == b"\x30\x03\x02\x01\x05"
- return value, tlsfeature_ext.critical
- except cryptography.x509.ExtensionNotFound:
- return None, False
-
- def _get_subject_alt_name(self):
- try:
- san_ext = self.csr.extensions.get_extension_for_class(x509.SubjectAlternativeName)
- result = [crypto_utils.cryptography_decode_name(san) for san in san_ext.value]
- return result, san_ext.critical
- except cryptography.x509.ExtensionNotFound:
- return None, False
-
- def _get_public_key(self, binary):
- return self.csr.public_key().public_bytes(
- serialization.Encoding.DER if binary else serialization.Encoding.PEM,
- serialization.PublicFormat.SubjectPublicKeyInfo
- )
-
- def _get_subject_key_identifier(self):
- try:
- ext = self.csr.extensions.get_extension_for_class(x509.SubjectKeyIdentifier)
- return ext.value.digest
- except cryptography.x509.ExtensionNotFound:
- return None
-
- def _get_authority_key_identifier(self):
- try:
- ext = self.csr.extensions.get_extension_for_class(x509.AuthorityKeyIdentifier)
- issuer = None
- if ext.value.authority_cert_issuer is not None:
- issuer = [crypto_utils.cryptography_decode_name(san) for san in ext.value.authority_cert_issuer]
- return ext.value.key_identifier, issuer, ext.value.authority_cert_serial_number
- except cryptography.x509.ExtensionNotFound:
- return None, None, None
-
- def _get_all_extensions(self):
- return crypto_utils.cryptography_get_extensions_from_csr(self.csr)
-
- def _is_signature_valid(self):
- return self.csr.is_signature_valid
-
-
-class CertificateSigningRequestInfoPyOpenSSL(CertificateSigningRequestInfo):
- """validate the supplied CSR."""
-
- def __init__(self, module):
- super(CertificateSigningRequestInfoPyOpenSSL, self).__init__(module, 'pyopenssl')
-
- def __get_name(self, name):
- result = []
- for sub in name.get_components():
- result.append([crypto_utils.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 = [
- crypto_utils.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 _normalize_san(self, 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:'):
- ip = compat_ipaddress.ip_address(san[3:])
- san = 'IP:{0}'.format(ip.compressed)
- return san
-
- def _get_subject_alt_name(self):
- for extension in self.csr.get_extensions():
- if extension.get_short_name() == b'subjectAltName':
- result = [self._normalize_san(altname.strip()) for altname in
- to_text(extension, errors='surrogate_or_strict').split(', ')]
- return result, bool(extension.get_critical())
- return None, False
-
- def _get_public_key(self, binary):
- try:
- return crypto.dump_publickey(
- crypto.FILETYPE_ASN1 if binary else crypto.FILETYPE_PEM,
- self.csr.get_pubkey()
- )
- except AttributeError:
- try:
- bio = crypto._new_mem_buf()
- if binary:
- rc = crypto._lib.i2d_PUBKEY_bio(bio, self.csr.get_pubkey()._pkey)
- else:
- 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_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 crypto_utils.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 main():
- module = AnsibleModule(
- argument_spec=dict(
- path=dict(type='path'),
- content=dict(type='str'),
- select_crypto_backend=dict(type='str', default='auto', choices=['auto', 'cryptography', 'pyopenssl']),
- ),
- required_one_of=(
- ['path', 'content'],
- ),
- mutually_exclusive=(
- ['path', 'content'],
- ),
- supports_check_mode=True,
- )
-
- try:
- if module.params['path'] is not None:
- base_dir = os.path.dirname(module.params['path']) or '.'
- if not os.path.isdir(base_dir):
- module.fail_json(
- name=base_dir,
- msg='The directory %s does not exist or the file is not a directory' % base_dir
- )
-
- backend = module.params['select_crypto_backend']
- if backend == 'auto':
- # Detect what backend we can use
- can_use_cryptography = CRYPTOGRAPHY_FOUND and CRYPTOGRAPHY_VERSION >= LooseVersion(MINIMAL_CRYPTOGRAPHY_VERSION)
- can_use_pyopenssl = PYOPENSSL_FOUND and PYOPENSSL_VERSION >= LooseVersion(MINIMAL_PYOPENSSL_VERSION)
-
- # If cryptography is available we'll use it
- if can_use_cryptography:
- backend = 'cryptography'
- elif can_use_pyopenssl:
- backend = 'pyopenssl'
-
- # Fail if no backend has been found
- if backend == 'auto':
- module.fail_json(msg=("Can't detect any of the required Python libraries "
- "cryptography (>= {0}) or PyOpenSSL (>= {1})").format(
- MINIMAL_CRYPTOGRAPHY_VERSION,
- MINIMAL_PYOPENSSL_VERSION))
-
- if backend == 'pyopenssl':
- if not PYOPENSSL_FOUND:
- module.fail_json(msg=missing_required_lib('pyOpenSSL >= {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')
-
- module.deprecate('The module is using the PyOpenSSL backend. This backend has been deprecated', version='2.13')
- certificate = CertificateSigningRequestInfoPyOpenSSL(module)
- elif backend == 'cryptography':
- if not CRYPTOGRAPHY_FOUND:
- module.fail_json(msg=missing_required_lib('cryptography >= {0}'.format(MINIMAL_CRYPTOGRAPHY_VERSION)),
- exception=CRYPTOGRAPHY_IMP_ERR)
- certificate = CertificateSigningRequestInfoCryptography(module)
-
- result = certificate.get_info()
- module.exit_json(**result)
- except crypto_utils.OpenSSLObjectError as exc:
- module.fail_json(msg=to_native(exc))
-
-
-if __name__ == "__main__":
- main()
diff --git a/lib/ansible/modules/crypto/openssl_dhparam.py b/lib/ansible/modules/crypto/openssl_dhparam.py
deleted file mode 100644
index 5e06db9717..0000000000
--- a/lib/ansible/modules/crypto/openssl_dhparam.py
+++ /dev/null
@@ -1,418 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-
-# Copyright: (c) 2017, Thom Wiggers <ansible@thomwiggers.nl>
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'community'}
-
-DOCUMENTATION = r'''
----
-module: openssl_dhparam
-version_added: "2.5"
-short_description: Generate OpenSSL Diffie-Hellman Parameters
-description:
- - This module allows one to (re)generate OpenSSL DH-params.
- - This module uses file common arguments to specify generated file permissions.
- - "Please note that the module regenerates existing DH params if they don't
- match the module's options. If you are concerned that this could overwrite
- your existing DH params, consider using the I(backup) option."
- - The module can use the cryptography Python library, or the C(openssl) executable.
- By default, it tries to detect which one is available. This can be overridden
- with the I(select_crypto_backend) option.
-requirements:
- - Either cryptography >= 2.0
- - Or OpenSSL binary C(openssl)
-author:
- - Thom Wiggers (@thomwiggers)
-options:
- state:
- description:
- - Whether the parameters should exist or not,
- taking action if the state is different from what is stated.
- type: str
- default: present
- choices: [ absent, present ]
- size:
- description:
- - Size (in bits) of the generated DH-params.
- type: int
- default: 4096
- force:
- description:
- - Should the parameters be regenerated even it it already exists.
- type: bool
- default: no
- path:
- description:
- - Name of the file in which the generated parameters will be saved.
- type: path
- required: true
- backup:
- description:
- - Create a backup file including a timestamp so you can get the original
- DH params back if you overwrote them with new ones by accident.
- type: bool
- default: no
- version_added: "2.8"
- select_crypto_backend:
- description:
- - Determines which crypto backend to use.
- - The default choice is C(auto), which tries to use C(cryptography) if available, and falls back to C(openssl).
- - If set to C(openssl), will try to use the OpenSSL C(openssl) executable.
- - If set to C(cryptography), will try to use the L(cryptography,https://cryptography.io/) library.
- type: str
- default: auto
- choices: [ auto, cryptography, openssl ]
- version_added: '2.10'
- return_content:
- description:
- - If set to C(yes), will return the (current or generated) DH params' content as I(dhparams).
- type: bool
- default: no
- version_added: "2.10"
-extends_documentation_fragment:
-- files
-seealso:
-- module: openssl_certificate
-- module: openssl_csr
-- module: openssl_pkcs12
-- module: openssl_privatekey
-- module: openssl_publickey
-'''
-
-EXAMPLES = r'''
-- name: Generate Diffie-Hellman parameters with the default size (4096 bits)
- openssl_dhparam:
- path: /etc/ssl/dhparams.pem
-
-- name: Generate DH Parameters with a different size (2048 bits)
- openssl_dhparam:
- path: /etc/ssl/dhparams.pem
- size: 2048
-
-- name: Force regenerate an DH parameters if they already exist
- openssl_dhparam:
- path: /etc/ssl/dhparams.pem
- force: yes
-'''
-
-RETURN = r'''
-size:
- description: Size (in bits) of the Diffie-Hellman parameters.
- returned: changed or success
- type: int
- sample: 4096
-filename:
- description: Path to the generated Diffie-Hellman parameters.
- returned: changed or success
- type: str
- sample: /etc/ssl/dhparams.pem
-backup_file:
- description: Name of backup file created.
- returned: changed and if I(backup) is C(yes)
- type: str
- sample: /path/to/dhparams.pem.2019-03-09@11:22~
-dhparams:
- description: The (current or generated) DH params' content.
- returned: if I(state) is C(present) and I(return_content) is C(yes)
- type: str
- version_added: "2.10"
-'''
-
-import abc
-import os
-import re
-import tempfile
-import traceback
-from distutils.version import LooseVersion
-
-from ansible.module_utils.basic import AnsibleModule, missing_required_lib
-from ansible.module_utils._text import to_native
-from ansible.module_utils import crypto as crypto_utils
-
-
-MINIMAL_CRYPTOGRAPHY_VERSION = '2.0'
-
-CRYPTOGRAPHY_IMP_ERR = None
-try:
- import cryptography
- import cryptography.exceptions
- import cryptography.hazmat.backends
- import cryptography.hazmat.primitives.asymmetric.dh
- import cryptography.hazmat.primitives.serialization
- CRYPTOGRAPHY_VERSION = LooseVersion(cryptography.__version__)
-except ImportError:
- CRYPTOGRAPHY_IMP_ERR = traceback.format_exc()
- CRYPTOGRAPHY_FOUND = False
-else:
- CRYPTOGRAPHY_FOUND = True
-
-
-class DHParameterError(Exception):
- pass
-
-
-class DHParameterBase(object):
-
- def __init__(self, module):
- self.state = module.params['state']
- self.path = module.params['path']
- self.size = module.params['size']
- self.force = module.params['force']
- self.changed = False
- self.return_content = module.params['return_content']
-
- self.backup = module.params['backup']
- self.backup_file = None
-
- @abc.abstractmethod
- def _do_generate(self, module):
- """Actually generate the DH params."""
- pass
-
- def generate(self, module):
- """Generate DH params."""
- changed = False
-
- # ony generate when necessary
- if self.force or not self._check_params_valid(module):
- self._do_generate(module)
- changed = True
-
- # fix permissions (checking force not necessary as done above)
- if not self._check_fs_attributes(module):
- # Fix done implicitly by
- # AnsibleModule.set_fs_attributes_if_different
- changed = True
-
- self.changed = changed
-
- def remove(self, module):
- if self.backup:
- self.backup_file = module.backup_local(self.path)
- try:
- os.remove(self.path)
- self.changed = True
- except OSError as exc:
- module.fail_json(msg=to_native(exc))
-
- def check(self, module):
- """Ensure the resource is in its desired state."""
- if self.force:
- return False
- return self._check_params_valid(module) and self._check_fs_attributes(module)
-
- @abc.abstractmethod
- def _check_params_valid(self, module):
- """Check if the params are in the correct state"""
- pass
-
- def _check_fs_attributes(self, module):
- """Checks (and changes if not in check mode!) fs attributes"""
- file_args = module.load_file_common_arguments(module.params)
- attrs_changed = module.set_fs_attributes_if_different(file_args, False)
-
- return not attrs_changed
-
- def dump(self):
- """Serialize the object into a dictionary."""
-
- result = {
- 'size': self.size,
- 'filename': self.path,
- 'changed': self.changed,
- }
- if self.backup_file:
- result['backup_file'] = self.backup_file
- if self.return_content:
- content = crypto_utils.load_file_if_exists(self.path, ignore_errors=True)
- result['dhparams'] = content.decode('utf-8') if content else None
-
- return result
-
-
-class DHParameterAbsent(DHParameterBase):
-
- def __init__(self, module):
- super(DHParameterAbsent, self).__init__(module)
-
- def _do_generate(self, module):
- """Actually generate the DH params."""
- pass
-
- def _check_params_valid(self, module):
- """Check if the params are in the correct state"""
- pass
-
-
-class DHParameterOpenSSL(DHParameterBase):
-
- def __init__(self, module):
- super(DHParameterOpenSSL, self).__init__(module)
- self.openssl_bin = module.get_bin_path('openssl', True)
-
- def _do_generate(self, module):
- """Actually generate the DH params."""
- # create a tempfile
- fd, tmpsrc = tempfile.mkstemp()
- os.close(fd)
- module.add_cleanup_file(tmpsrc) # Ansible will delete the file on exit
- # openssl dhparam -out <path> <bits>
- command = [self.openssl_bin, 'dhparam', '-out', tmpsrc, str(self.size)]
- rc, dummy, err = module.run_command(command, check_rc=False)
- if rc != 0:
- raise DHParameterError(to_native(err))
- if self.backup:
- self.backup_file = module.backup_local(self.path)
- try:
- module.atomic_move(tmpsrc, self.path)
- except Exception as e:
- module.fail_json(msg="Failed to write to file %s: %s" % (self.path, str(e)))
-
- def _check_params_valid(self, module):
- """Check if the params are in the correct state"""
- command = [self.openssl_bin, 'dhparam', '-check', '-text', '-noout', '-in', self.path]
- rc, out, err = module.run_command(command, check_rc=False)
- result = to_native(out)
- if rc != 0:
- # If the call failed the file probably doesn't exist or is
- # unreadable
- return False
- # output contains "(xxxx bit)"
- match = re.search(r"Parameters:\s+\((\d+) bit\).*", result)
- if not match:
- return False # No "xxxx bit" in output
-
- bits = int(match.group(1))
-
- # if output contains "WARNING" we've got a problem
- if "WARNING" in result or "WARNING" in to_native(err):
- return False
-
- return bits == self.size
-
-
-class DHParameterCryptography(DHParameterBase):
-
- def __init__(self, module):
- super(DHParameterCryptography, self).__init__(module)
- self.crypto_backend = cryptography.hazmat.backends.default_backend()
-
- def _do_generate(self, module):
- """Actually generate the DH params."""
- # Generate parameters
- params = cryptography.hazmat.primitives.asymmetric.dh.generate_parameters(
- generator=2,
- key_size=self.size,
- backend=self.crypto_backend,
- )
- # Serialize parameters
- result = params.parameter_bytes(
- encoding=cryptography.hazmat.primitives.serialization.Encoding.PEM,
- format=cryptography.hazmat.primitives.serialization.ParameterFormat.PKCS3,
- )
- # Write result
- if self.backup:
- self.backup_file = module.backup_local(self.path)
- crypto_utils.write_file(module, result)
-
- def _check_params_valid(self, module):
- """Check if the params are in the correct state"""
- # Load parameters
- try:
- with open(self.path, 'rb') as f:
- data = f.read()
- params = self.crypto_backend.load_pem_parameters(data)
- except Exception as dummy:
- return False
- # Check parameters
- bits = crypto_utils.count_bits(params.parameter_numbers().p)
- return bits == self.size
-
-
-def main():
- """Main function"""
-
- module = AnsibleModule(
- argument_spec=dict(
- state=dict(type='str', default='present', choices=['absent', 'present']),
- size=dict(type='int', default=4096),
- force=dict(type='bool', default=False),
- path=dict(type='path', required=True),
- backup=dict(type='bool', default=False),
- select_crypto_backend=dict(type='str', default='auto', choices=['auto', 'cryptography', 'openssl']),
- return_content=dict(type='bool', default=False),
- ),
- supports_check_mode=True,
- add_file_common_args=True,
- )
-
- base_dir = os.path.dirname(module.params['path']) or '.'
- if not os.path.isdir(base_dir):
- module.fail_json(
- name=base_dir,
- msg="The directory '%s' does not exist or the file is not a directory" % base_dir
- )
-
- if module.params['state'] == 'present':
- backend = module.params['select_crypto_backend']
- if backend == 'auto':
- # Detection what is possible
- can_use_cryptography = CRYPTOGRAPHY_FOUND and CRYPTOGRAPHY_VERSION >= LooseVersion(MINIMAL_CRYPTOGRAPHY_VERSION)
- can_use_openssl = module.get_bin_path('openssl', False) is not None
-
- # First try cryptography, then OpenSSL
- if can_use_cryptography:
- backend = 'cryptography'
- elif can_use_openssl:
- backend = 'openssl'
-
- # Success?
- if backend == 'auto':
- module.fail_json(msg=("Can't detect either the required Python library cryptography (>= {0}) "
- "or the OpenSSL binary openssl").format(MINIMAL_CRYPTOGRAPHY_VERSION))
-
- if backend == 'openssl':
- dhparam = DHParameterOpenSSL(module)
- elif backend == 'cryptography':
- if not CRYPTOGRAPHY_FOUND:
- module.fail_json(msg=missing_required_lib('cryptography >= {0}'.format(MINIMAL_CRYPTOGRAPHY_VERSION)),
- exception=CRYPTOGRAPHY_IMP_ERR)
- dhparam = DHParameterCryptography(module)
-
- if module.check_mode:
- result = dhparam.dump()
- result['changed'] = module.params['force'] or not dhparam.check(module)
- module.exit_json(**result)
-
- try:
- dhparam.generate(module)
- except DHParameterError as exc:
- module.fail_json(msg=to_native(exc))
- else:
- dhparam = DHParameterAbsent(module)
-
- if module.check_mode:
- result = dhparam.dump()
- result['changed'] = os.path.exists(module.params['path'])
- module.exit_json(**result)
-
- if os.path.exists(module.params['path']):
- try:
- dhparam.remove(module)
- except Exception as exc:
- module.fail_json(msg=to_native(exc))
-
- result = dhparam.dump()
-
- module.exit_json(**result)
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/crypto/openssl_pkcs12.py b/lib/ansible/modules/crypto/openssl_pkcs12.py
deleted file mode 100644
index c80e616ef7..0000000000
--- a/lib/ansible/modules/crypto/openssl_pkcs12.py
+++ /dev/null
@@ -1,470 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-
-# Copyright: (c) 2017, Guillaume Delpierre <gde@llew.me>
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'community'}
-
-DOCUMENTATION = r'''
----
-module: openssl_pkcs12
-author:
-- Guillaume Delpierre (@gdelpierre)
-version_added: "2.7"
-short_description: Generate OpenSSL PKCS#12 archive
-description:
- - This module allows one to (re-)generate PKCS#12.
-requirements:
- - python-pyOpenSSL
-options:
- action:
- description:
- - C(export) or C(parse) a PKCS#12.
- type: str
- default: export
- choices: [ export, parse ]
- other_certificates:
- description:
- - List of other certificates to include. Pre 2.8 this parameter was called C(ca_certificates)
- type: list
- elements: path
- aliases: [ ca_certificates ]
- certificate_path:
- description:
- - The path to read certificates and private keys from.
- - Must be in PEM format.
- type: path
- force:
- description:
- - Should the file be regenerated even if it already exists.
- type: bool
- default: no
- friendly_name:
- description:
- - Specifies the friendly name for the certificate and private key.
- type: str
- aliases: [ name ]
- iter_size:
- description:
- - Number of times to repeat the encryption step.
- type: int
- default: 2048
- maciter_size:
- description:
- - Number of times to repeat the MAC step.
- type: int
- default: 1
- passphrase:
- description:
- - The PKCS#12 password.
- type: str
- path:
- description:
- - Filename to write the PKCS#12 file to.
- type: path
- required: true
- privatekey_passphrase:
- description:
- - Passphrase source to decrypt any input private keys with.
- type: str
- privatekey_path:
- description:
- - File to read private key from.
- type: path
- state:
- description:
- - Whether the file should exist or not.
- All parameters except C(path) are ignored when state is C(absent).
- choices: [ absent, present ]
- default: present
- type: str
- src:
- description:
- - PKCS#12 file path to parse.
- type: path
- backup:
- description:
- - Create a backup file including a timestamp so you can get the original
- output file back if you overwrote it with a new one by accident.
- type: bool
- default: no
- version_added: "2.8"
- return_content:
- description:
- - If set to C(yes), will return the (current or generated) PKCS#12's content as I(pkcs12).
- type: bool
- default: no
- version_added: "2.10"
-extends_documentation_fragment:
- - files
-seealso:
-- module: openssl_certificate
-- module: openssl_csr
-- module: openssl_dhparam
-- module: openssl_privatekey
-- module: openssl_publickey
-'''
-
-EXAMPLES = r'''
-- name: Generate PKCS#12 file
- openssl_pkcs12:
- action: export
- path: /opt/certs/ansible.p12
- friendly_name: raclette
- privatekey_path: /opt/certs/keys/key.pem
- certificate_path: /opt/certs/cert.pem
- other_certificates: /opt/certs/ca.pem
- state: present
-
-- name: Change PKCS#12 file permission
- openssl_pkcs12:
- action: export
- path: /opt/certs/ansible.p12
- friendly_name: raclette
- privatekey_path: /opt/certs/keys/key.pem
- certificate_path: /opt/certs/cert.pem
- other_certificates: /opt/certs/ca.pem
- state: present
- mode: '0600'
-
-- name: Regen PKCS#12 file
- openssl_pkcs12:
- action: export
- src: /opt/certs/ansible.p12
- path: /opt/certs/ansible.p12
- friendly_name: raclette
- privatekey_path: /opt/certs/keys/key.pem
- certificate_path: /opt/certs/cert.pem
- other_certificates: /opt/certs/ca.pem
- state: present
- mode: '0600'
- force: yes
-
-- name: Dump/Parse PKCS#12 file
- openssl_pkcs12:
- action: parse
- src: /opt/certs/ansible.p12
- path: /opt/certs/ansible.pem
- state: present
-
-- name: Remove PKCS#12 file
- openssl_pkcs12:
- path: /opt/certs/ansible.p12
- state: absent
-'''
-
-RETURN = r'''
-filename:
- description: Path to the generate PKCS#12 file.
- returned: changed or success
- type: str
- sample: /opt/certs/ansible.p12
-privatekey:
- description: Path to the TLS/SSL private key the public key was generated from.
- returned: changed or success
- type: str
- sample: /etc/ssl/private/ansible.com.pem
-backup_file:
- description: Name of backup file created.
- returned: changed and if I(backup) is C(yes)
- type: str
- sample: /path/to/ansible.com.pem.2019-03-09@11:22~
-pkcs12:
- description: The (current or generated) PKCS#12's content Base64 encoded.
- returned: if I(state) is C(present) and I(return_content) is C(yes)
- type: str
- version_added: "2.10"
-'''
-
-import base64
-import stat
-import os
-import traceback
-
-PYOPENSSL_IMP_ERR = None
-try:
- from OpenSSL import crypto
-except ImportError:
- PYOPENSSL_IMP_ERR = traceback.format_exc()
- pyopenssl_found = False
-else:
- pyopenssl_found = True
-
-from ansible.module_utils.basic import AnsibleModule, missing_required_lib
-from ansible.module_utils import crypto as crypto_utils
-from ansible.module_utils._text import to_bytes, to_native
-
-
-class PkcsError(crypto_utils.OpenSSLObjectError):
- pass
-
-
-class Pkcs(crypto_utils.OpenSSLObject):
-
- def __init__(self, module):
- super(Pkcs, self).__init__(
- module.params['path'],
- module.params['state'],
- module.params['force'],
- module.check_mode
- )
- self.action = module.params['action']
- self.other_certificates = module.params['other_certificates']
- self.certificate_path = module.params['certificate_path']
- self.friendly_name = module.params['friendly_name']
- self.iter_size = module.params['iter_size']
- self.maciter_size = module.params['maciter_size']
- self.passphrase = module.params['passphrase']
- self.pkcs12 = None
- self.privatekey_passphrase = module.params['privatekey_passphrase']
- self.privatekey_path = module.params['privatekey_path']
- self.pkcs12_bytes = None
- self.return_content = module.params['return_content']
- self.src = module.params['src']
-
- if module.params['mode'] is None:
- module.params['mode'] = '0400'
-
- self.backup = module.params['backup']
- self.backup_file = None
-
- def check(self, module, perms_required=True):
- """Ensure the resource is in its desired state."""
-
- state_and_perms = super(Pkcs, self).check(module, perms_required)
-
- def _check_pkey_passphrase():
- if self.privatekey_passphrase:
- try:
- crypto_utils.load_privatekey(self.privatekey_path,
- self.privatekey_passphrase)
- except crypto.Error:
- return False
- except crypto_utils.OpenSSLBadPassphraseError:
- return False
- return True
-
- if not state_and_perms:
- return state_and_perms
-
- if os.path.exists(self.path) and module.params['action'] == 'export':
- dummy = self.generate(module)
- self.src = self.path
- try:
- pkcs12_privatekey, pkcs12_certificate, pkcs12_other_certificates, pkcs12_friendly_name = self.parse()
- except crypto.Error:
- return False
- if (pkcs12_privatekey is not None) and (self.privatekey_path is not None):
- expected_pkey = crypto.dump_privatekey(crypto.FILETYPE_PEM,
- self.pkcs12.get_privatekey())
- if pkcs12_privatekey != expected_pkey:
- return False
- elif bool(pkcs12_privatekey) != bool(self.privatekey_path):
- return False
-
- if (pkcs12_certificate is not None) and (self.certificate_path is not None):
-
- expected_cert = crypto.dump_certificate(crypto.FILETYPE_PEM,
- self.pkcs12.get_certificate())
- if pkcs12_certificate != expected_cert:
- return False
- elif bool(pkcs12_certificate) != bool(self.certificate_path):
- return False
-
- if (pkcs12_other_certificates is not None) and (self.other_certificates is not None):
- expected_other_certs = [crypto.dump_certificate(crypto.FILETYPE_PEM,
- other_cert) for other_cert in self.pkcs12.get_ca_certificates()]
- if set(pkcs12_other_certificates) != set(expected_other_certs):
- return False
- elif bool(pkcs12_other_certificates) != bool(self.other_certificates):
- return False
-
- if pkcs12_privatekey:
- # This check is required because pyOpenSSL will not return a friendly name
- # if the private key is not set in the file
- if ((self.pkcs12.get_friendlyname() is not None) and (pkcs12_friendly_name is not None)):
- if self.pkcs12.get_friendlyname() != pkcs12_friendly_name:
- return False
- elif bool(self.pkcs12.get_friendlyname()) != bool(pkcs12_friendly_name):
- return False
- else:
- return False
-
- return _check_pkey_passphrase()
-
- def dump(self):
- """Serialize the object into a dictionary."""
-
- result = {
- 'filename': self.path,
- }
- if self.privatekey_path:
- result['privatekey_path'] = self.privatekey_path
- if self.backup_file:
- result['backup_file'] = self.backup_file
- if self.return_content:
- if self.pkcs12_bytes is None:
- self.pkcs12_bytes = crypto_utils.load_file_if_exists(self.path, ignore_errors=True)
- result['pkcs12'] = base64.b64encode(self.pkcs12_bytes) if self.pkcs12_bytes else None
-
- return result
-
- def generate(self, module):
- """Generate PKCS#12 file archive."""
- self.pkcs12 = crypto.PKCS12()
-
- if self.other_certificates:
- other_certs = [crypto_utils.load_certificate(other_cert) for other_cert
- in self.other_certificates]
- self.pkcs12.set_ca_certificates(other_certs)
-
- if self.certificate_path:
- self.pkcs12.set_certificate(crypto_utils.load_certificate(
- self.certificate_path))
-
- if self.friendly_name:
- self.pkcs12.set_friendlyname(to_bytes(self.friendly_name))
-
- if self.privatekey_path:
- try:
- self.pkcs12.set_privatekey(crypto_utils.load_privatekey(
- self.privatekey_path,
- self.privatekey_passphrase)
- )
- except crypto_utils.OpenSSLBadPassphraseError as exc:
- raise PkcsError(exc)
-
- return self.pkcs12.export(self.passphrase, self.iter_size, self.maciter_size)
-
- def remove(self, module):
- if self.backup:
- self.backup_file = module.backup_local(self.path)
- super(Pkcs, self).remove(module)
-
- def parse(self):
- """Read PKCS#12 file."""
-
- try:
- with open(self.src, 'rb') as pkcs12_fh:
- pkcs12_content = pkcs12_fh.read()
- p12 = crypto.load_pkcs12(pkcs12_content,
- self.passphrase)
- pkey = crypto.dump_privatekey(crypto.FILETYPE_PEM,
- p12.get_privatekey())
- crt = crypto.dump_certificate(crypto.FILETYPE_PEM,
- p12.get_certificate())
- other_certs = []
- if p12.get_ca_certificates() is not None:
- other_certs = [crypto.dump_certificate(crypto.FILETYPE_PEM,
- other_cert) for other_cert in p12.get_ca_certificates()]
-
- friendly_name = p12.get_friendlyname()
-
- return (pkey, crt, other_certs, friendly_name)
-
- except IOError as exc:
- raise PkcsError(exc)
-
- def write(self, module, content, mode=None):
- """Write the PKCS#12 file."""
- if self.backup:
- self.backup_file = module.backup_local(self.path)
- crypto_utils.write_file(module, content, mode)
- if self.return_content:
- self.pkcs12_bytes = content
-
-
-def main():
- argument_spec = dict(
- action=dict(type='str', default='export', choices=['export', 'parse']),
- other_certificates=dict(type='list', elements='path', aliases=['ca_certificates']),
- certificate_path=dict(type='path'),
- force=dict(type='bool', default=False),
- friendly_name=dict(type='str', aliases=['name']),
- iter_size=dict(type='int', default=2048),
- maciter_size=dict(type='int', default=1),
- passphrase=dict(type='str', no_log=True),
- path=dict(type='path', required=True),
- privatekey_passphrase=dict(type='str', no_log=True),
- privatekey_path=dict(type='path'),
- state=dict(type='str', default='present', choices=['absent', 'present']),
- src=dict(type='path'),
- backup=dict(type='bool', default=False),
- return_content=dict(type='bool', default=False),
- )
-
- required_if = [
- ['action', 'parse', ['src']],
- ]
-
- module = AnsibleModule(
- add_file_common_args=True,
- argument_spec=argument_spec,
- required_if=required_if,
- supports_check_mode=True,
- )
-
- if not pyopenssl_found:
- module.fail_json(msg=missing_required_lib('pyOpenSSL'), exception=PYOPENSSL_IMP_ERR)
-
- base_dir = os.path.dirname(module.params['path']) or '.'
- if not os.path.isdir(base_dir):
- module.fail_json(
- name=base_dir,
- msg="The directory '%s' does not exist or the path is not a directory" % base_dir
- )
-
- try:
- pkcs12 = Pkcs(module)
- changed = False
-
- if module.params['state'] == 'present':
- if module.check_mode:
- result = pkcs12.dump()
- result['changed'] = module.params['force'] or not pkcs12.check(module)
- module.exit_json(**result)
-
- if not pkcs12.check(module, perms_required=False) or module.params['force']:
- if module.params['action'] == 'export':
- if not module.params['friendly_name']:
- module.fail_json(msg='Friendly_name is required')
- pkcs12_content = pkcs12.generate(module)
- pkcs12.write(module, pkcs12_content, 0o600)
- changed = True
- else:
- pkey, cert, other_certs, friendly_name = pkcs12.parse()
- dump_content = '%s%s%s' % (to_native(pkey), to_native(cert), to_native(b''.join(other_certs)))
- pkcs12.write(module, to_bytes(dump_content))
-
- file_args = module.load_file_common_arguments(module.params)
- if module.set_fs_attributes_if_different(file_args, changed):
- changed = True
- else:
- if module.check_mode:
- result = pkcs12.dump()
- result['changed'] = os.path.exists(module.params['path'])
- module.exit_json(**result)
-
- if os.path.exists(module.params['path']):
- pkcs12.remove(module)
- changed = True
-
- result = pkcs12.dump()
- result['changed'] = changed
- if os.path.exists(module.params['path']):
- file_mode = "%04o" % stat.S_IMODE(os.stat(module.params['path']).st_mode)
- result['mode'] = file_mode
-
- module.exit_json(**result)
- except crypto_utils.OpenSSLObjectError as exc:
- module.fail_json(msg=to_native(exc))
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/crypto/openssl_privatekey.py b/lib/ansible/modules/crypto/openssl_privatekey.py
deleted file mode 100644
index 2fdfdab10c..0000000000
--- a/lib/ansible/modules/crypto/openssl_privatekey.py
+++ /dev/null
@@ -1,943 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-
-# Copyright: (c) 2016, Yanis Guenane <yanis+ansible@guenane.org>
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'community'}
-
-DOCUMENTATION = r'''
----
-module: openssl_privatekey
-version_added: "2.3"
-short_description: Generate OpenSSL private keys
-description:
- - This module allows one to (re)generate OpenSSL private keys.
- - One can generate L(RSA,https://en.wikipedia.org/wiki/RSA_%28cryptosystem%29),
- L(DSA,https://en.wikipedia.org/wiki/Digital_Signature_Algorithm),
- L(ECC,https://en.wikipedia.org/wiki/Elliptic-curve_cryptography) or
- L(EdDSA,https://en.wikipedia.org/wiki/EdDSA) private keys.
- - Keys are generated in PEM format.
- - "Please note that the module regenerates private keys if they don't match
- the module's options. In particular, if you provide another passphrase
- (or specify none), change the keysize, etc., the private key will be
- regenerated. If you are concerned that this could **overwrite your private key**,
- 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 Ansible 2.13."
-requirements:
- - Either cryptography >= 1.2.3 (older versions might work as well)
- - Or pyOpenSSL
-author:
- - Yanis Guenane (@Spredzy)
- - Felix Fontein (@felixfontein)
-options:
- state:
- description:
- - Whether the private key should exist or not, taking action if the state is different from what is stated.
- type: str
- default: present
- choices: [ absent, present ]
- size:
- description:
- - Size (in bits) of the TLS/SSL key to generate.
- type: int
- default: 4096
- type:
- description:
- - The algorithm used to generate the TLS/SSL private key.
- - Note that C(ECC), C(X25519), C(X448), C(Ed25519) and C(Ed448) require the C(cryptography) backend.
- C(X25519) needs cryptography 2.5 or newer, while C(X448), C(Ed25519) and C(Ed448) require
- cryptography 2.6 or newer. For C(ECC), the minimal cryptography version required depends on the
- I(curve) option.
- type: str
- default: RSA
- choices: [ DSA, ECC, Ed25519, Ed448, RSA, X25519, X448 ]
- curve:
- description:
- - Note that not all curves are supported by all versions of C(cryptography).
- - For maximal interoperability, C(secp384r1) or C(secp256r1) should be used.
- - We use the curve names as defined in the
- L(IANA registry for TLS,https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-8).
- type: str
- choices:
- - secp384r1
- - secp521r1
- - secp224r1
- - secp192r1
- - secp256r1
- - secp256k1
- - brainpoolP256r1
- - brainpoolP384r1
- - brainpoolP512r1
- - sect571k1
- - sect409k1
- - sect283k1
- - sect233k1
- - sect163k1
- - sect571r1
- - sect409r1
- - sect283r1
- - sect233r1
- - sect163r2
- version_added: "2.8"
- force:
- description:
- - Should the key be regenerated even if it already exists.
- type: bool
- default: no
- path:
- description:
- - Name of the file in which the generated TLS/SSL private key will be written. It will have 0600 mode.
- type: path
- required: true
- passphrase:
- description:
- - The passphrase for the private key.
- type: str
- version_added: "2.4"
- cipher:
- description:
- - The cipher to encrypt the private key. (Valid values can be found by
- 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
- version_added: "2.4"
- select_crypto_backend:
- description:
- - Determines which crypto backend to use.
- - The default choice is C(auto), which tries to use C(cryptography) if available, and falls back to C(pyopenssl).
- - If set to C(pyopenssl), will try to use the L(pyOpenSSL,https://pypi.org/project/pyOpenSSL/) library.
- - If set to C(cryptography), will try to use the L(cryptography,https://cryptography.io/) library.
- - Please note that the C(pyopenssl) backend has been deprecated in Ansible 2.9, and will be removed in Ansible 2.13.
- From that point on, only the C(cryptography) backend will be available.
- type: str
- default: auto
- choices: [ auto, cryptography, pyopenssl ]
- version_added: "2.8"
- format:
- description:
- - Determines which format the private key is written in. By default, PKCS1 (traditional OpenSSL format)
- is used for all keys which support it. Please note that not every key can be exported in any format.
- - The value C(auto) selects a fromat based on the key format. The value C(auto_ignore) does the same,
- but for existing private key files, it will not force a regenerate when its format is not the automatically
- selected one for generation.
- - Note that if the format for an existing private key mismatches, the key is *regenerated* by default.
- 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
- default: auto_ignore
- choices: [ pkcs1, pkcs8, raw, auto, auto_ignore ]
- version_added: "2.10"
- format_mismatch:
- description:
- - Determines behavior of the module if the format of a private key does not match the expected format, but all
- other parameters are as expected.
- - If set to C(regenerate) (default), generates a new private key.
- - If set to C(convert), the key will be converted to the new format instead.
- - Only supported by the C(cryptography) backend.
- type: str
- default: regenerate
- choices: [ regenerate, convert ]
- version_added: "2.10"
- backup:
- description:
- - Create a backup file including a timestamp so you can get
- the original private key back if you overwrote it with a new one by accident.
- type: bool
- default: no
- version_added: "2.8"
- return_content:
- description:
- - If set to C(yes), will return the (current or generated) private key's content as I(privatekey).
- - Note that especially if the private key is not encrypted, you have to make sure that the returned
- value is treated appropriately and not accidentally written to logs etc.! Use with care!
- type: bool
- default: no
- version_added: "2.10"
- regenerate:
- description:
- - Allows to configure in which situations the module is allowed to regenerate private keys.
- The module will always generate a new key if the destination file does not exist.
- - By default, the key will be regenerated when it doesn't match the module's options,
- except when the key cannot be read or the passphrase does not match. Please note that
- this B(changed) for Ansible 2.10. For Ansible 2.9, the behavior was as if C(full_idempotence)
- is specified.
- - If set to C(never), the module will fail if the key cannot be read or the passphrase
- isn't matching, and will never regenerate an existing key.
- - If set to C(fail), the module will fail if the key does not correspond to the module's
- options.
- - If set to C(partial_idempotence), the key will be regenerated if it does not conform to
- the module's options. The key is B(not) regenerated if it cannot be read (broken file),
- the key is protected by an unknown passphrase, or when they key is not protected by a
- passphrase, but a passphrase is specified.
- - If set to C(full_idempotence), the key will be regenerated if it does not conform to the
- module's options. This is also the case if the key cannot be read (broken file), the key
- is protected by an unknown passphrase, or when they key is not protected by a passphrase,
- but a passphrase is specified. Make sure you have a B(backup) when using this option!
- - If set to C(always), the module will always regenerate the key. This is equivalent to
- setting I(force) to C(yes).
- - Note that if I(format_mismatch) is set to C(convert) and everything matches except the
- format, the key will always be converted, except if I(regenerate) is set to C(always).
- type: str
- choices:
- - never
- - fail
- - partial_idempotence
- - full_idempotence
- - always
- default: full_idempotence
- version_added: '2.10'
-extends_documentation_fragment:
-- files
-seealso:
-- module: openssl_certificate
-- module: openssl_csr
-- module: openssl_dhparam
-- module: openssl_pkcs12
-- module: openssl_publickey
-'''
-
-EXAMPLES = r'''
-- name: Generate an OpenSSL private key with the default values (4096 bits, RSA)
- openssl_privatekey:
- path: /etc/ssl/private/ansible.com.pem
-
-- name: Generate an OpenSSL private key with the default values (4096 bits, RSA) and a passphrase
- openssl_privatekey:
- path: /etc/ssl/private/ansible.com.pem
- passphrase: ansible
- cipher: aes256
-
-- name: Generate an OpenSSL private key with a different size (2048 bits)
- openssl_privatekey:
- path: /etc/ssl/private/ansible.com.pem
- size: 2048
-
-- name: Force regenerate an OpenSSL private key if it already exists
- openssl_privatekey:
- path: /etc/ssl/private/ansible.com.pem
- force: yes
-
-- name: Generate an OpenSSL private key with a different algorithm (DSA)
- openssl_privatekey:
- path: /etc/ssl/private/ansible.com.pem
- type: DSA
-'''
-
-RETURN = r'''
-size:
- description: Size (in bits) of the TLS/SSL private key.
- returned: changed or success
- type: int
- sample: 4096
-type:
- description: Algorithm used to generate the TLS/SSL private key.
- returned: changed or success
- type: str
- sample: RSA
-curve:
- description: Elliptic curve used to generate the TLS/SSL private key.
- returned: changed or success, and I(type) is C(ECC)
- type: str
- sample: secp256r1
-filename:
- description: Path to the generated TLS/SSL private key file.
- returned: changed or success
- type: str
- sample: /etc/ssl/private/ansible.com.pem
-fingerprint:
- description:
- - 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
- type: dict
- sample:
- md5: "84:75:71:72:8d:04:b5:6c:4d:37:6d:66:83:f5:4c:29"
- sha1: "51:cc:7c:68:5d:eb:41:43:88:7e:1a:ae:c7:f8:24:72:ee:71:f6:10"
- sha224: "b1:19:a6:6c:14:ac:33:1d:ed:18:50:d3:06:5c:b2:32:91:f1:f1:52:8c:cb:d5:75:e9:f5:9b:46"
- sha256: "41:ab:c7:cb:d5:5f:30:60:46:99:ac:d4:00:70:cf:a1:76:4f:24:5d:10:24:57:5d:51:6e:09:97:df:2f:de:c7"
- sha384: "85:39:50:4e:de:d9:19:33:40:70:ae:10:ab:59:24:19:51:c3:a2:e4:0b:1c:b1:6e:dd:b3:0c:d9:9e:6a:46:af:da:18:f8:ef:ae:2e:c0:9a:75:2c:9b:b3:0f:3a:5f:3d"
- sha512: "fd:ed:5e:39:48:5f:9f:fe:7f:25:06:3f:79:08:cd:ee:a5:e7:b3:3d:13:82:87:1f:84:e1:f5:c7:28:77:53:94:86:56:38:69:f0:d9:35:22:01:1e:a6:60:...:0f:9b"
-backup_file:
- description: Name of backup file created.
- returned: changed and if I(backup) is C(yes)
- type: str
- sample: /path/to/privatekey.pem.2019-03-09@11:22~
-privatekey:
- description:
- - The (current or generated) private key's content.
- - Will be Base64-encoded if the key is in raw format.
- returned: if I(state) is C(present) and I(return_content) is C(yes)
- type: str
- version_added: "2.10"
-'''
-
-import abc
-import base64
-import os
-import traceback
-from distutils.version import LooseVersion
-
-MINIMAL_PYOPENSSL_VERSION = '0.6'
-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
-try:
- import cryptography
- import cryptography.exceptions
- import cryptography.hazmat.backends
- import cryptography.hazmat.primitives.serialization
- import cryptography.hazmat.primitives.asymmetric.rsa
- import cryptography.hazmat.primitives.asymmetric.dsa
- import cryptography.hazmat.primitives.asymmetric.ec
- import cryptography.hazmat.primitives.asymmetric.utils
- CRYPTOGRAPHY_VERSION = LooseVersion(cryptography.__version__)
-except ImportError:
- CRYPTOGRAPHY_IMP_ERR = traceback.format_exc()
- CRYPTOGRAPHY_FOUND = False
-else:
- CRYPTOGRAPHY_FOUND = True
-
-from ansible.module_utils.crypto import (
- CRYPTOGRAPHY_HAS_X25519,
- CRYPTOGRAPHY_HAS_X25519_FULL,
- CRYPTOGRAPHY_HAS_X448,
- CRYPTOGRAPHY_HAS_ED25519,
- CRYPTOGRAPHY_HAS_ED448,
-)
-
-from ansible.module_utils import crypto as crypto_utils
-from ansible.module_utils._text import to_native, to_bytes
-from ansible.module_utils.basic import AnsibleModule, missing_required_lib
-
-
-class PrivateKeyError(crypto_utils.OpenSSLObjectError):
- pass
-
-
-class PrivateKeyBase(crypto_utils.OpenSSLObject):
-
- def __init__(self, module):
- super(PrivateKeyBase, self).__init__(
- module.params['path'],
- module.params['state'],
- module.params['force'],
- module.check_mode
- )
- self.size = module.params['size']
- self.passphrase = module.params['passphrase']
- self.cipher = module.params['cipher']
- self.privatekey = None
- self.fingerprint = {}
- self.format = module.params['format']
- self.format_mismatch = module.params['format_mismatch']
- self.privatekey_bytes = None
- self.return_content = module.params['return_content']
- self.regenerate = module.params['regenerate']
- if self.regenerate == 'always':
- self.force = True
-
- self.backup = module.params['backup']
- self.backup_file = None
-
- if module.params['mode'] is None:
- module.params['mode'] = '0600'
-
- @abc.abstractmethod
- def _generate_private_key(self):
- """(Re-)Generate private key."""
- pass
-
- @abc.abstractmethod
- def _ensure_private_key_loaded(self):
- """Make sure that the private key has been loaded."""
- pass
-
- @abc.abstractmethod
- def _get_private_key_data(self):
- """Return bytes for self.privatekey"""
- pass
-
- @abc.abstractmethod
- def _get_fingerprint(self):
- pass
-
- def generate(self, module):
- """Generate a keypair."""
-
- if not self.check(module, perms_required=False, ignore_conversion=True) or self.force:
- # Regenerate
- if self.backup:
- self.backup_file = module.backup_local(self.path)
- self._generate_private_key()
- privatekey_data = self._get_private_key_data()
- if self.return_content:
- self.privatekey_bytes = privatekey_data
- crypto_utils.write_file(module, privatekey_data, 0o600)
- self.changed = True
- elif not self.check(module, perms_required=False, ignore_conversion=False):
- # Convert
- if self.backup:
- self.backup_file = module.backup_local(self.path)
- self._ensure_private_key_loaded()
- privatekey_data = self._get_private_key_data()
- if self.return_content:
- self.privatekey_bytes = privatekey_data
- crypto_utils.write_file(module, privatekey_data, 0o600)
- self.changed = True
-
- self.fingerprint = self._get_fingerprint()
- file_args = module.load_file_common_arguments(module.params)
- if module.set_fs_attributes_if_different(file_args, False):
- self.changed = True
-
- def remove(self, module):
- if self.backup:
- self.backup_file = module.backup_local(self.path)
- super(PrivateKeyBase, self).remove(module)
-
- @abc.abstractmethod
- def _check_passphrase(self):
- pass
-
- @abc.abstractmethod
- def _check_size_and_type(self):
- pass
-
- @abc.abstractmethod
- def _check_format(self):
- pass
-
- def check(self, module, perms_required=True, ignore_conversion=True):
- """Ensure the resource is in its desired state."""
-
- state_and_perms = super(PrivateKeyBase, self).check(module, perms_required=False)
-
- if not state_and_perms:
- # key does not exist
- return False
-
- if not self._check_passphrase():
- if self.regenerate in ('full_idempotence', 'always'):
- return False
- module.fail_json(msg='Unable to read the key. The key is protected with a another passphrase / no passphrase or broken.'
- ' Will not proceed. To force regeneration, call the module with `generate`'
- ' set to `full_idempotence` or `always`, or with `force=yes`.')
-
- if self.regenerate != 'never':
- if not self._check_size_and_type():
- if self.regenerate in ('partial_idempotence', 'full_idempotence', 'always'):
- return False
- module.fail_json(msg='Key has wrong type and/or size.'
- ' Will not proceed. To force regeneration, call the module with `generate`'
- ' set to `partial_idempotence`, `full_idempotence` or `always`, or with `force=yes`.')
-
- if not self._check_format():
- # During conversion step, convert if format does not match and format_mismatch == 'convert'
- if not ignore_conversion and self.format_mismatch == 'convert':
- return False
- # During generation step, regenerate if format does not match and format_mismatch == 'regenerate'
- if ignore_conversion and self.format_mismatch == 'regenerate' and self.regenerate != 'never':
- if not ignore_conversion or self.regenerate in ('partial_idempotence', 'full_idempotence', 'always'):
- return False
- module.fail_json(msg='Key has wrong format.'
- ' Will not proceed. To force regeneration, call the module with `generate`'
- ' set to `partial_idempotence`, `full_idempotence` or `always`, or with `force=yes`.'
- ' To convert the key, set `format_mismatch` to `convert`.')
-
- # check whether permissions are correct (in case that needs to be checked)
- return not perms_required or super(PrivateKeyBase, self).check(module, perms_required=perms_required)
-
- def dump(self):
- """Serialize the object into a dictionary."""
-
- result = {
- 'size': self.size,
- 'filename': self.path,
- 'changed': self.changed,
- 'fingerprint': self.fingerprint,
- }
- if self.backup_file:
- result['backup_file'] = self.backup_file
- if self.return_content:
- if self.privatekey_bytes is None:
- self.privatekey_bytes = crypto_utils.load_file_if_exists(self.path, ignore_errors=True)
- if self.privatekey_bytes:
- if crypto_utils.identify_private_key_format(self.privatekey_bytes) == 'raw':
- result['privatekey'] = base64.b64encode(self.privatekey_bytes)
- else:
- result['privatekey'] = self.privatekey_bytes.decode('utf-8')
- else:
- result['privatekey'] = None
-
- return result
-
-
-# Implementation with using pyOpenSSL
-class PrivateKeyPyOpenSSL(PrivateKeyBase):
-
- def __init__(self, module):
- super(PrivateKeyPyOpenSSL, self).__init__(module)
-
- if module.params['type'] == 'RSA':
- self.type = crypto.TYPE_RSA
- elif module.params['type'] == 'DSA':
- self.type = crypto.TYPE_DSA
- else:
- module.fail_json(msg="PyOpenSSL backend only supports RSA and DSA keys.")
-
- if self.format != 'auto_ignore':
- module.fail_json(msg="PyOpenSSL backend only supports auto_ignore format.")
-
- def _generate_private_key(self):
- """(Re-)Generate private key."""
- self.privatekey = crypto.PKey()
- try:
- self.privatekey.generate_key(self.type, self.size)
- except (TypeError, ValueError) as exc:
- raise PrivateKeyError(exc)
-
- def _ensure_private_key_loaded(self):
- """Make sure that the private key has been loaded."""
- if self.privatekey is None:
- try:
- self.privatekey = privatekey = crypto_utils.load_privatekey(self.path, self.passphrase)
- except crypto_utils.OpenSSLBadPassphraseError as exc:
- raise PrivateKeyError(exc)
-
- def _get_private_key_data(self):
- """Return bytes for self.privatekey"""
- if self.cipher and self.passphrase:
- return crypto.dump_privatekey(crypto.FILETYPE_PEM, self.privatekey,
- self.cipher, to_bytes(self.passphrase))
- else:
- return crypto.dump_privatekey(crypto.FILETYPE_PEM, self.privatekey)
-
- def _get_fingerprint(self):
- return crypto_utils.get_fingerprint(self.path, self.passphrase)
-
- def _check_passphrase(self):
- try:
- crypto_utils.load_privatekey(self.path, self.passphrase)
- return True
- except Exception as dummy:
- return False
-
- def _check_size_and_type(self):
- def _check_size(privatekey):
- return self.size == privatekey.bits()
-
- def _check_type(privatekey):
- return self.type == privatekey.type()
-
- self._ensure_private_key_loaded()
- return _check_size(self.privatekey) and _check_type(self.privatekey)
-
- def _check_format(self):
- # Not supported by this backend
- return True
-
- def dump(self):
- """Serialize the object into a dictionary."""
-
- result = super(PrivateKeyPyOpenSSL, self).dump()
-
- if self.type == crypto.TYPE_RSA:
- result['type'] = 'RSA'
- else:
- result['type'] = 'DSA'
-
- return result
-
-
-# Implementation with using cryptography
-class PrivateKeyCryptography(PrivateKeyBase):
-
- def _get_ec_class(self, ectype):
- ecclass = cryptography.hazmat.primitives.asymmetric.ec.__dict__.get(ectype)
- if ecclass is None:
- self.module.fail_json(msg='Your cryptography version does not support {0}'.format(ectype))
- return ecclass
-
- def _add_curve(self, name, ectype, deprecated=False):
- def create(size):
- ecclass = self._get_ec_class(ectype)
- return ecclass()
-
- def verify(privatekey):
- ecclass = self._get_ec_class(ectype)
- return isinstance(privatekey.private_numbers().public_numbers.curve, ecclass)
-
- self.curves[name] = {
- 'create': create,
- 'verify': verify,
- 'deprecated': deprecated,
- }
-
- def __init__(self, module):
- super(PrivateKeyCryptography, self).__init__(module)
-
- self.curves = dict()
- self._add_curve('secp384r1', 'SECP384R1')
- self._add_curve('secp521r1', 'SECP521R1')
- self._add_curve('secp224r1', 'SECP224R1')
- self._add_curve('secp192r1', 'SECP192R1')
- self._add_curve('secp256r1', 'SECP256R1')
- self._add_curve('secp256k1', 'SECP256K1')
- self._add_curve('brainpoolP256r1', 'BrainpoolP256R1', deprecated=True)
- self._add_curve('brainpoolP384r1', 'BrainpoolP384R1', deprecated=True)
- self._add_curve('brainpoolP512r1', 'BrainpoolP512R1', deprecated=True)
- self._add_curve('sect571k1', 'SECT571K1', deprecated=True)
- self._add_curve('sect409k1', 'SECT409K1', deprecated=True)
- self._add_curve('sect283k1', 'SECT283K1', deprecated=True)
- self._add_curve('sect233k1', 'SECT233K1', deprecated=True)
- self._add_curve('sect163k1', 'SECT163K1', deprecated=True)
- self._add_curve('sect571r1', 'SECT571R1', deprecated=True)
- self._add_curve('sect409r1', 'SECT409R1', deprecated=True)
- self._add_curve('sect283r1', 'SECT283R1', deprecated=True)
- self._add_curve('sect233r1', 'SECT233R1', deprecated=True)
- self._add_curve('sect163r2', 'SECT163R2', deprecated=True)
-
- self.module = module
- self.cryptography_backend = cryptography.hazmat.backends.default_backend()
-
- self.type = module.params['type']
- self.curve = module.params['curve']
- if not CRYPTOGRAPHY_HAS_X25519 and self.type == 'X25519':
- self.module.fail_json(msg='Your cryptography version does not support X25519')
- if not CRYPTOGRAPHY_HAS_X25519_FULL and self.type == 'X25519':
- self.module.fail_json(msg='Your cryptography version does not support X25519 serialization')
- if not CRYPTOGRAPHY_HAS_X448 and self.type == 'X448':
- self.module.fail_json(msg='Your cryptography version does not support X448')
- if not CRYPTOGRAPHY_HAS_ED25519 and self.type == 'Ed25519':
- self.module.fail_json(msg='Your cryptography version does not support Ed25519')
- if not CRYPTOGRAPHY_HAS_ED448 and self.type == 'Ed448':
- self.module.fail_json(msg='Your cryptography version does not support Ed448')
-
- def _get_wanted_format(self):
- if self.format not in ('auto', 'auto_ignore'):
- return self.format
- if self.type in ('X25519', 'X448', 'Ed25519', 'Ed448'):
- return 'pkcs8'
- else:
- return 'pkcs1'
-
- def _generate_private_key(self):
- """(Re-)Generate private key."""
- try:
- if self.type == 'RSA':
- self.privatekey = cryptography.hazmat.primitives.asymmetric.rsa.generate_private_key(
- public_exponent=65537, # OpenSSL always uses this
- key_size=self.size,
- backend=self.cryptography_backend
- )
- if self.type == 'DSA':
- self.privatekey = cryptography.hazmat.primitives.asymmetric.dsa.generate_private_key(
- key_size=self.size,
- backend=self.cryptography_backend
- )
- if CRYPTOGRAPHY_HAS_X25519_FULL and self.type == 'X25519':
- self.privatekey = cryptography.hazmat.primitives.asymmetric.x25519.X25519PrivateKey.generate()
- if CRYPTOGRAPHY_HAS_X448 and self.type == 'X448':
- self.privatekey = cryptography.hazmat.primitives.asymmetric.x448.X448PrivateKey.generate()
- if CRYPTOGRAPHY_HAS_ED25519 and self.type == 'Ed25519':
- self.privatekey = cryptography.hazmat.primitives.asymmetric.ed25519.Ed25519PrivateKey.generate()
- if CRYPTOGRAPHY_HAS_ED448 and self.type == 'Ed448':
- self.privatekey = cryptography.hazmat.primitives.asymmetric.ed448.Ed448PrivateKey.generate()
- if self.type == 'ECC' and self.curve in self.curves:
- if self.curves[self.curve]['deprecated']:
- self.module.warn('Elliptic curves of type {0} should not be used for new keys!'.format(self.curve))
- self.privatekey = cryptography.hazmat.primitives.asymmetric.ec.generate_private_key(
- curve=self.curves[self.curve]['create'](self.size),
- backend=self.cryptography_backend
- )
- except cryptography.exceptions.UnsupportedAlgorithm as dummy:
- self.module.fail_json(msg='Cryptography backend does not support the algorithm required for {0}'.format(self.type))
-
- def _ensure_private_key_loaded(self):
- """Make sure that the private key has been loaded."""
- if self.privatekey is None:
- self.privatekey = self._load_privatekey()
-
- def _get_private_key_data(self):
- """Return bytes for self.privatekey"""
- # Select export format and encoding
- try:
- export_format = self._get_wanted_format()
- export_encoding = cryptography.hazmat.primitives.serialization.Encoding.PEM
- if export_format == 'pkcs1':
- # "TraditionalOpenSSL" format is PKCS1
- export_format = cryptography.hazmat.primitives.serialization.PrivateFormat.TraditionalOpenSSL
- elif export_format == 'pkcs8':
- export_format = cryptography.hazmat.primitives.serialization.PrivateFormat.PKCS8
- elif export_format == 'raw':
- export_format = cryptography.hazmat.primitives.serialization.PrivateFormat.Raw
- export_encoding = cryptography.hazmat.primitives.serialization.Encoding.Raw
- except AttributeError:
- self.module.fail_json(msg='Cryptography backend does not support the selected output format "{0}"'.format(self.format))
-
- # Select key encryption
- encryption_algorithm = cryptography.hazmat.primitives.serialization.NoEncryption()
- if self.cipher and self.passphrase:
- if self.cipher == 'auto':
- encryption_algorithm = cryptography.hazmat.primitives.serialization.BestAvailableEncryption(to_bytes(self.passphrase))
- else:
- self.module.fail_json(msg='Cryptography backend can only use "auto" for cipher option.')
-
- # Serialize key
- try:
- return self.privatekey.private_bytes(
- encoding=export_encoding,
- format=export_format,
- encryption_algorithm=encryption_algorithm
- )
- except ValueError as dummy:
- self.module.fail_json(
- msg='Cryptography backend cannot serialize the private key in the required format "{0}"'.format(self.format)
- )
- except Exception as dummy:
- self.module.fail_json(
- msg='Error while serializing the private key in the required format "{0}"'.format(self.format),
- exception=traceback.format_exc()
- )
-
- def _load_privatekey(self):
- try:
- # Read bytes
- with open(self.path, 'rb') as f:
- data = f.read()
- # Interpret bytes depending on format.
- format = crypto_utils.identify_private_key_format(data)
- if format == 'raw':
- if len(data) == 56 and CRYPTOGRAPHY_HAS_X448:
- return cryptography.hazmat.primitives.asymmetric.x448.X448PrivateKey.from_private_bytes(data)
- if len(data) == 57 and CRYPTOGRAPHY_HAS_ED448:
- return cryptography.hazmat.primitives.asymmetric.ed448.Ed448PrivateKey.from_private_bytes(data)
- if len(data) == 32:
- if CRYPTOGRAPHY_HAS_X25519 and (self.type == 'X25519' or not CRYPTOGRAPHY_HAS_ED25519):
- return cryptography.hazmat.primitives.asymmetric.x25519.X25519PrivateKey.from_private_bytes(data)
- if CRYPTOGRAPHY_HAS_ED25519 and (self.type == 'Ed25519' or not CRYPTOGRAPHY_HAS_X25519):
- return cryptography.hazmat.primitives.asymmetric.ed25519.Ed25519PrivateKey.from_private_bytes(data)
- if CRYPTOGRAPHY_HAS_X25519 and CRYPTOGRAPHY_HAS_ED25519:
- try:
- return cryptography.hazmat.primitives.asymmetric.x25519.X25519PrivateKey.from_private_bytes(data)
- except Exception:
- return cryptography.hazmat.primitives.asymmetric.ed25519.Ed25519PrivateKey.from_private_bytes(data)
- raise PrivateKeyError('Cannot load raw key')
- else:
- return cryptography.hazmat.primitives.serialization.load_pem_private_key(
- data,
- None if self.passphrase is None else to_bytes(self.passphrase),
- backend=self.cryptography_backend
- )
- except Exception as e:
- raise PrivateKeyError(e)
-
- def _get_fingerprint(self):
- # Get bytes of public key
- private_key = self._load_privatekey()
- public_key = private_key.public_key()
- public_key_bytes = public_key.public_bytes(
- cryptography.hazmat.primitives.serialization.Encoding.DER,
- cryptography.hazmat.primitives.serialization.PublicFormat.SubjectPublicKeyInfo
- )
- # Get fingerprints of public_key_bytes
- return crypto_utils.get_fingerprint_of_bytes(public_key_bytes)
-
- def _check_passphrase(self):
- try:
- with open(self.path, 'rb') as f:
- data = f.read()
- format = crypto_utils.identify_private_key_format(data)
- if format == 'raw':
- # Raw keys cannot be encrypted. To avoid incompatibilities, we try to
- # actually load the key (and return False when this fails).
- self._load_privatekey()
- # Loading the key succeeded. Only return True when no passphrase was
- # provided.
- return self.passphrase is None
- else:
- return cryptography.hazmat.primitives.serialization.load_pem_private_key(
- data,
- None if self.passphrase is None else to_bytes(self.passphrase),
- backend=self.cryptography_backend
- )
- except Exception as dummy:
- return False
-
- def _check_size_and_type(self):
- self._ensure_private_key_loaded()
-
- if isinstance(self.privatekey, cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateKey):
- return self.type == 'RSA' and self.size == self.privatekey.key_size
- if isinstance(self.privatekey, cryptography.hazmat.primitives.asymmetric.dsa.DSAPrivateKey):
- return self.type == 'DSA' and self.size == self.privatekey.key_size
- if CRYPTOGRAPHY_HAS_X25519 and isinstance(self.privatekey, cryptography.hazmat.primitives.asymmetric.x25519.X25519PrivateKey):
- return self.type == 'X25519'
- if CRYPTOGRAPHY_HAS_X448 and isinstance(self.privatekey, cryptography.hazmat.primitives.asymmetric.x448.X448PrivateKey):
- return self.type == 'X448'
- if CRYPTOGRAPHY_HAS_ED25519 and isinstance(self.privatekey, cryptography.hazmat.primitives.asymmetric.ed25519.Ed25519PrivateKey):
- return self.type == 'Ed25519'
- if CRYPTOGRAPHY_HAS_ED448 and isinstance(self.privatekey, cryptography.hazmat.primitives.asymmetric.ed448.Ed448PrivateKey):
- return self.type == 'Ed448'
- if isinstance(self.privatekey, cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePrivateKey):
- if self.type != 'ECC':
- return False
- if self.curve not in self.curves:
- return False
- return self.curves[self.curve]['verify'](self.privatekey)
-
- return False
-
- def _check_format(self):
- if self.format == 'auto_ignore':
- return True
- try:
- with open(self.path, 'rb') as f:
- content = f.read()
- format = crypto_utils.identify_private_key_format(content)
- return format == self._get_wanted_format()
- except Exception as dummy:
- return False
-
- def dump(self):
- """Serialize the object into a dictionary."""
- result = super(PrivateKeyCryptography, self).dump()
- result['type'] = self.type
- if self.type == 'ECC':
- result['curve'] = self.curve
- return result
-
-
-def main():
-
- module = AnsibleModule(
- argument_spec=dict(
- state=dict(type='str', default='present', choices=['present', 'absent']),
- size=dict(type='int', default=4096),
- type=dict(type='str', default='RSA', choices=[
- 'DSA', 'ECC', 'Ed25519', 'Ed448', 'RSA', 'X25519', 'X448'
- ]),
- curve=dict(type='str', choices=[
- 'secp384r1', 'secp521r1', 'secp224r1', 'secp192r1', 'secp256r1',
- 'secp256k1', 'brainpoolP256r1', 'brainpoolP384r1', 'brainpoolP512r1',
- 'sect571k1', 'sect409k1', 'sect283k1', 'sect233k1', 'sect163k1',
- 'sect571r1', 'sect409r1', 'sect283r1', 'sect233r1', 'sect163r2',
- ]),
- force=dict(type='bool', default=False),
- path=dict(type='path', required=True),
- passphrase=dict(type='str', no_log=True),
- cipher=dict(type='str'),
- backup=dict(type='bool', default=False),
- format=dict(type='str', default='auto_ignore', choices=['pkcs1', 'pkcs8', 'raw', 'auto', 'auto_ignore']),
- format_mismatch=dict(type='str', default='regenerate', choices=['regenerate', 'convert']),
- select_crypto_backend=dict(type='str', choices=['auto', 'pyopenssl', 'cryptography'], default='auto'),
- return_content=dict(type='bool', default=False),
- regenerate=dict(
- type='str',
- default='full_idempotence',
- choices=['never', 'fail', 'partial_idempotence', 'full_idempotence', 'always']
- ),
- ),
- supports_check_mode=True,
- add_file_common_args=True,
- required_together=[
- ['cipher', 'passphrase']
- ],
- required_if=[
- ['type', 'ECC', ['curve']],
- ],
- )
-
- base_dir = os.path.dirname(module.params['path']) or '.'
- if not os.path.isdir(base_dir):
- module.fail_json(
- name=base_dir,
- msg='The directory %s does not exist or the file is not a directory' % base_dir
- )
-
- backend = module.params['select_crypto_backend']
- if backend == 'auto':
- # Detection what is possible
- 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
- if module.params['cipher'] and module.params['passphrase'] and module.params['cipher'] != 'auto':
- # First try pyOpenSSL, then 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?
- if backend == 'auto':
- module.fail_json(msg=("Can't detect any of the required Python libraries "
- "cryptography (>= {0}) or PyOpenSSL (>= {1})").format(
- MINIMAL_CRYPTOGRAPHY_VERSION,
- MINIMAL_PYOPENSSL_VERSION))
- try:
- 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.13')
- private_key = PrivateKeyPyOpenSSL(module)
- elif backend == 'cryptography':
- if not CRYPTOGRAPHY_FOUND:
- module.fail_json(msg=missing_required_lib('cryptography >= {0}'.format(MINIMAL_CRYPTOGRAPHY_VERSION)),
- exception=CRYPTOGRAPHY_IMP_ERR)
- private_key = PrivateKeyCryptography(module)
-
- if private_key.state == 'present':
- if module.check_mode:
- result = private_key.dump()
- result['changed'] = private_key.force \
- or not private_key.check(module, ignore_conversion=True) \
- or not private_key.check(module, ignore_conversion=False)
- module.exit_json(**result)
-
- private_key.generate(module)
- else:
- if module.check_mode:
- result = private_key.dump()
- result['changed'] = os.path.exists(module.params['path'])
- module.exit_json(**result)
-
- private_key.remove(module)
-
- result = private_key.dump()
- module.exit_json(**result)
- except crypto_utils.OpenSSLObjectError as exc:
- module.fail_json(msg=to_native(exc))
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/crypto/openssl_privatekey_info.py b/lib/ansible/modules/crypto/openssl_privatekey_info.py
deleted file mode 100644
index c3f2b16dad..0000000000
--- a/lib/ansible/modules/crypto/openssl_privatekey_info.py
+++ /dev/null
@@ -1,651 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-
-# Copyright: (c) 2016-2017, Yanis Guenane <yanis+ansible@guenane.org>
-# Copyright: (c) 2017, Markus Teufelberger <mteufelberger+ansible@mgit.at>
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'community'}
-
-DOCUMENTATION = r'''
----
-module: openssl_privatekey_info
-version_added: '2.8'
-short_description: Provide information for OpenSSL private keys
-description:
- - This module allows one to query information on OpenSSL private keys.
- - In case the key consistency checks fail, the module will fail as this indicates a faked
- 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
- C(key_is_consistent).
- - It uses the pyOpenSSL or cryptography python library to interact with OpenSSL. If both the
- cryptography and PyOpenSSL libraries are available (and meet the minimum version requirements)
- cryptography will be preferred as a backend over PyOpenSSL (unless the backend is forced with
- C(select_crypto_backend)). Please note that the PyOpenSSL backend was deprecated in Ansible 2.9
- and will be removed in Ansible 2.13.
-requirements:
- - PyOpenSSL >= 0.15 or cryptography >= 1.2.3
-author:
- - Felix Fontein (@felixfontein)
- - Yanis Guenane (@Spredzy)
-options:
- path:
- description:
- - Remote absolute path where the private key file is loaded from.
- type: path
- content:
- description:
- - Content of the private key file.
- - Either I(path) or I(content) must be specified, but not both.
- type: str
- version_added: "2.10"
- passphrase:
- description:
- - The passphrase for the private key.
- type: str
- return_private_key_data:
- description:
- - Whether to return private key data.
- - Only set this to C(yes) when you want private information about this key to
- leave the remote machine.
- - "WARNING: you have to make sure that private key data isn't accidentally logged!"
- type: bool
- default: no
-
- select_crypto_backend:
- description:
- - Determines which crypto backend to use.
- - The default choice is C(auto), which tries to use C(cryptography) if available, and falls back to C(pyopenssl).
- - If set to C(pyopenssl), will try to use the L(pyOpenSSL,https://pypi.org/project/pyOpenSSL/) library.
- - If set to C(cryptography), will try to use the L(cryptography,https://cryptography.io/) library.
- - Please note that the C(pyopenssl) backend has been deprecated in Ansible 2.9, and will be removed in Ansible 2.13.
- From that point on, only the C(cryptography) backend will be available.
- type: str
- default: auto
- choices: [ auto, cryptography, pyopenssl ]
-
-seealso:
-- module: openssl_privatekey
-'''
-
-EXAMPLES = r'''
-- name: Generate an OpenSSL private key with the default values (4096 bits, RSA)
- openssl_privatekey:
- path: /etc/ssl/private/ansible.com.pem
-
-- name: Get information on generated key
- openssl_privatekey_info:
- path: /etc/ssl/private/ansible.com.pem
- register: result
-
-- name: Dump information
- debug:
- var: result
-'''
-
-RETURN = r'''
-can_load_key:
- description: Whether the module was able to load the private key from disk
- returned: always
- type: bool
-can_parse_key:
- description: Whether the module was able to parse the private key
- returned: always
- type: bool
-key_is_consistent:
- description:
- - Whether the key is consistent. Can also return C(none) next to C(yes) and
- C(no), to indicate that consistency couldn't be checked.
- - In case the check returns C(no), the module will fail.
- returned: always
- type: bool
-public_key:
- description: Private key's public key in PEM format
- returned: success
- type: str
- sample: "-----BEGIN PUBLIC KEY-----\nMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A..."
-public_key_fingerprints:
- description:
- - Fingerprints of private key's public key.
- - For every hash algorithm available, the fingerprint is computed.
- returned: success
- type: dict
- sample: "{'sha256': 'd4:b3:aa:6d:c8:04:ce:4e:ba:f6:29:4d:92:a3:94:b0:c2:ff:bd:bf:33:63:11:43:34:0f:51:b0:95:09:2f:63',
- 'sha512': 'f7:07:4a:f0:b0:f0:e6:8b:95:5f:f9:e6:61:0a:32:68:f1..."
-type:
- description:
- - The key's type.
- - One of C(RSA), C(DSA), C(ECC), C(Ed25519), C(X25519), C(Ed448), or C(X448).
- - Will start with C(unknown) if the key type cannot be determined.
- returned: success
- type: str
- sample: RSA
-public_data:
- description:
- - Public key data. Depends on key type.
- returned: success
- type: dict
-private_data:
- description:
- - Private key data. Depends on key type.
- returned: success and when I(return_private_key_data) is set to C(yes)
- type: dict
-'''
-
-
-import abc
-import os
-import traceback
-from distutils.version import LooseVersion
-
-from ansible.module_utils import crypto as crypto_utils
-from ansible.module_utils.basic import AnsibleModule, missing_required_lib
-from ansible.module_utils._text import to_native, to_bytes
-
-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
-try:
- import cryptography
- from cryptography.hazmat.primitives import serialization
- CRYPTOGRAPHY_VERSION = LooseVersion(cryptography.__version__)
- try:
- import cryptography.hazmat.primitives.asymmetric.x25519
- CRYPTOGRAPHY_HAS_X25519 = True
- except ImportError:
- CRYPTOGRAPHY_HAS_X25519 = False
- try:
- import cryptography.hazmat.primitives.asymmetric.x448
- CRYPTOGRAPHY_HAS_X448 = True
- except ImportError:
- CRYPTOGRAPHY_HAS_X448 = False
- try:
- import cryptography.hazmat.primitives.asymmetric.ed25519
- CRYPTOGRAPHY_HAS_ED25519 = True
- except ImportError:
- CRYPTOGRAPHY_HAS_ED25519 = False
- try:
- import cryptography.hazmat.primitives.asymmetric.ed448
- CRYPTOGRAPHY_HAS_ED448 = True
- except ImportError:
- CRYPTOGRAPHY_HAS_ED448 = False
-except ImportError:
- CRYPTOGRAPHY_IMP_ERR = traceback.format_exc()
- CRYPTOGRAPHY_FOUND = False
-else:
- CRYPTOGRAPHY_FOUND = True
-
-SIGNATURE_TEST_DATA = b'1234'
-
-
-def _get_cryptography_key_info(key):
- key_public_data = dict()
- key_private_data = dict()
- if isinstance(key, cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateKey):
- key_type = 'RSA'
- key_public_data['size'] = key.key_size
- key_public_data['modulus'] = key.public_key().public_numbers().n
- key_public_data['exponent'] = key.public_key().public_numbers().e
- key_private_data['p'] = key.private_numbers().p
- key_private_data['q'] = key.private_numbers().q
- key_private_data['exponent'] = key.private_numbers().d
- elif isinstance(key, cryptography.hazmat.primitives.asymmetric.dsa.DSAPrivateKey):
- key_type = 'DSA'
- key_public_data['size'] = key.key_size
- key_public_data['p'] = key.parameters().parameter_numbers().p
- key_public_data['q'] = key.parameters().parameter_numbers().q
- key_public_data['g'] = key.parameters().parameter_numbers().g
- key_public_data['y'] = key.public_key().public_numbers().y
- key_private_data['x'] = key.private_numbers().x
- elif CRYPTOGRAPHY_HAS_X25519 and isinstance(key, cryptography.hazmat.primitives.asymmetric.x25519.X25519PrivateKey):
- key_type = 'X25519'
- elif CRYPTOGRAPHY_HAS_X448 and isinstance(key, cryptography.hazmat.primitives.asymmetric.x448.X448PrivateKey):
- key_type = 'X448'
- elif CRYPTOGRAPHY_HAS_ED25519 and isinstance(key, cryptography.hazmat.primitives.asymmetric.ed25519.Ed25519PrivateKey):
- key_type = 'Ed25519'
- elif CRYPTOGRAPHY_HAS_ED448 and isinstance(key, cryptography.hazmat.primitives.asymmetric.ed448.Ed448PrivateKey):
- key_type = 'Ed448'
- elif isinstance(key, cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePrivateKey):
- key_type = 'ECC'
- key_public_data['curve'] = key.public_key().curve.name
- key_public_data['x'] = key.public_key().public_numbers().x
- key_public_data['y'] = key.public_key().public_numbers().y
- key_public_data['exponent_size'] = key.public_key().curve.key_size
- key_private_data['multiplier'] = key.private_numbers().private_value
- else:
- key_type = 'unknown ({0})'.format(type(key))
- return key_type, key_public_data, key_private_data
-
-
-def _check_dsa_consistency(key_public_data, key_private_data):
- # Get parameters
- p = key_public_data.get('p')
- q = key_public_data.get('q')
- g = key_public_data.get('g')
- y = key_public_data.get('y')
- x = key_private_data.get('x')
- for v in (p, q, g, y, x):
- if v is None:
- return None
- # Make sure that g is not 0, 1 or -1 in Z/pZ
- if g < 2 or g >= p - 1:
- return False
- # Make sure that x is in range
- if x < 1 or x >= q:
- return False
- # Check whether q divides p-1
- if (p - 1) % q != 0:
- return False
- # Check that g**q mod p == 1
- if crypto_utils.binary_exp_mod(g, q, p) != 1:
- return False
- # Check whether g**x mod p == y
- if crypto_utils.binary_exp_mod(g, x, p) != y:
- return False
- # Check (quickly) whether p or q are not primes
- if crypto_utils.quick_is_not_prime(q) or crypto_utils.quick_is_not_prime(p):
- return False
- return True
-
-
-def _is_cryptography_key_consistent(key, key_public_data, key_private_data):
- if isinstance(key, cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateKey):
- return bool(key._backend._lib.RSA_check_key(key._rsa_cdata))
- if isinstance(key, cryptography.hazmat.primitives.asymmetric.dsa.DSAPrivateKey):
- result = _check_dsa_consistency(key_public_data, key_private_data)
- if result is not None:
- return result
- try:
- signature = key.sign(SIGNATURE_TEST_DATA, cryptography.hazmat.primitives.hashes.SHA256())
- except AttributeError:
- # sign() was added in cryptography 1.5, but we support older versions
- return None
- try:
- key.public_key().verify(
- signature,
- SIGNATURE_TEST_DATA,
- cryptography.hazmat.primitives.hashes.SHA256()
- )
- return True
- except cryptography.exceptions.InvalidSignature:
- return False
- if isinstance(key, cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePrivateKey):
- try:
- signature = key.sign(
- SIGNATURE_TEST_DATA,
- cryptography.hazmat.primitives.asymmetric.ec.ECDSA(cryptography.hazmat.primitives.hashes.SHA256())
- )
- except AttributeError:
- # sign() was added in cryptography 1.5, but we support older versions
- return None
- try:
- key.public_key().verify(
- signature,
- SIGNATURE_TEST_DATA,
- cryptography.hazmat.primitives.asymmetric.ec.ECDSA(cryptography.hazmat.primitives.hashes.SHA256())
- )
- return True
- except cryptography.exceptions.InvalidSignature:
- return False
- has_simple_sign_function = False
- if CRYPTOGRAPHY_HAS_ED25519 and isinstance(key, cryptography.hazmat.primitives.asymmetric.ed25519.Ed25519PrivateKey):
- has_simple_sign_function = True
- if CRYPTOGRAPHY_HAS_ED448 and isinstance(key, cryptography.hazmat.primitives.asymmetric.ed448.Ed448PrivateKey):
- has_simple_sign_function = True
- if has_simple_sign_function:
- signature = key.sign(SIGNATURE_TEST_DATA)
- try:
- key.public_key().verify(signature, SIGNATURE_TEST_DATA)
- return True
- except cryptography.exceptions.InvalidSignature:
- return False
- # For X25519 and X448, there's no test yet.
- return None
-
-
-class PrivateKeyInfo(crypto_utils.OpenSSLObject):
- def __init__(self, module, backend):
- super(PrivateKeyInfo, self).__init__(
- module.params['path'] or '',
- 'present',
- False,
- module.check_mode,
- )
- self.backend = backend
- self.module = module
- self.content = module.params['content']
-
- self.passphrase = module.params['passphrase']
- self.return_private_key_data = module.params['return_private_key_data']
-
- def generate(self):
- # Empty method because crypto_utils.OpenSSLObject wants this
- pass
-
- def dump(self):
- # Empty method because crypto_utils.OpenSSLObject wants this
- pass
-
- @abc.abstractmethod
- def _get_public_key(self, binary):
- pass
-
- @abc.abstractmethod
- def _get_key_info(self):
- pass
-
- @abc.abstractmethod
- def _is_key_consistent(self, key_public_data, key_private_data):
- pass
-
- def get_info(self):
- result = dict(
- can_load_key=False,
- can_parse_key=False,
- key_is_consistent=None,
- )
- if self.content is not None:
- priv_key_detail = self.content.encode('utf-8')
- result['can_load_key'] = True
- else:
- try:
- with open(self.path, 'rb') as b_priv_key_fh:
- priv_key_detail = b_priv_key_fh.read()
- result['can_load_key'] = True
- except (IOError, OSError) as exc:
- self.module.fail_json(msg=to_native(exc), **result)
- try:
- self.key = crypto_utils.load_privatekey(
- path=None,
- content=priv_key_detail,
- passphrase=to_bytes(self.passphrase) if self.passphrase is not None else self.passphrase,
- backend=self.backend
- )
- result['can_parse_key'] = True
- except crypto_utils.OpenSSLObjectError as exc:
- self.module.fail_json(msg=to_native(exc), **result)
-
- result['public_key'] = self._get_public_key(binary=False)
- pk = self._get_public_key(binary=True)
- result['public_key_fingerprints'] = crypto_utils.get_fingerprint_of_bytes(pk) if pk is not None else dict()
-
- key_type, key_public_data, key_private_data = self._get_key_info()
- result['type'] = key_type
- result['public_data'] = key_public_data
- if self.return_private_key_data:
- result['private_data'] = key_private_data
-
- result['key_is_consistent'] = self._is_key_consistent(key_public_data, key_private_data)
- if result['key_is_consistent'] is False:
- # Only fail when it is False, to avoid to fail on None (which means "we don't know")
- result['key_is_consistent'] = False
- self.module.fail_json(
- msg="Private key is not consistent! (See "
- "https://blog.hboeck.de/archives/888-How-I-tricked-Symantec-with-a-Fake-Private-Key.html)",
- **result
- )
- return result
-
-
-class PrivateKeyInfoCryptography(PrivateKeyInfo):
- """Validate the supplied private key, using the cryptography backend"""
- def __init__(self, module):
- super(PrivateKeyInfoCryptography, self).__init__(module, 'cryptography')
-
- def _get_public_key(self, binary):
- return self.key.public_key().public_bytes(
- serialization.Encoding.DER if binary else serialization.Encoding.PEM,
- serialization.PublicFormat.SubjectPublicKeyInfo
- )
-
- def _get_key_info(self):
- return _get_cryptography_key_info(self.key)
-
- def _is_key_consistent(self, key_public_data, key_private_data):
- return _is_cryptography_key_consistent(self.key, key_public_data, key_private_data)
-
-
-class PrivateKeyInfoPyOpenSSL(PrivateKeyInfo):
- """validate the supplied private key."""
-
- def __init__(self, module):
- super(PrivateKeyInfoPyOpenSSL, self).__init__(module, 'pyopenssl')
-
- 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 bigint_to_int(self, 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_key_info(self):
- key_public_data = dict()
- key_private_data = dict()
- openssl_key_type = self.key.type()
- try_fallback = True
- if crypto.TYPE_RSA == openssl_key_type:
- key_type = 'RSA'
- key_public_data['size'] = self.key.bits()
-
- 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_public_data['modulus'] = self.bigint_to_int(n[0])
- key_public_data['exponent'] = self.bigint_to_int(e[0])
- key_private_data['exponent'] = self.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'] = self.bigint_to_int(p[0])
- key_private_data['q'] = self.bigint_to_int(q[0])
- else:
- # Get modulus and exponents
- key_public_data['modulus'] = self.bigint_to_int(key.n)
- key_public_data['exponent'] = self.bigint_to_int(key.e)
- key_private_data['exponent'] = self.bigint_to_int(key.d)
- # Get factors
- key_private_data['p'] = self.bigint_to_int(key.p)
- key_private_data['q'] = self.bigint_to_int(key.q)
- try_fallback = False
- except AttributeError:
- # Use fallback if available
- pass
- elif crypto.TYPE_DSA == openssl_key_type:
- key_type = 'DSA'
- key_public_data['size'] = self.key.bits()
-
- 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 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'] = self.bigint_to_int(p[0])
- key_public_data['q'] = self.bigint_to_int(q[0])
- key_public_data['g'] = self.bigint_to_int(g[0])
- # Get public and 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_public_data['y'] = self.bigint_to_int(y[0])
- key_private_data['x'] = self.bigint_to_int(x[0])
- else:
- # Get public parameters (primes and group element)
- key_public_data['p'] = self.bigint_to_int(key.p)
- key_public_data['q'] = self.bigint_to_int(key.q)
- key_public_data['g'] = self.bigint_to_int(key.g)
- # Get public and private key exponents
- key_public_data['y'] = self.bigint_to_int(key.pub_key)
- key_private_data['x'] = self.bigint_to_int(key.priv_key)
- try_fallback = False
- except AttributeError:
- # Use fallback if available
- pass
- 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_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 main():
- module = AnsibleModule(
- argument_spec=dict(
- path=dict(type='path'),
- content=dict(type='str'),
- passphrase=dict(type='str', no_log=True),
- return_private_key_data=dict(type='bool', default=False),
- select_crypto_backend=dict(type='str', default='auto', choices=['auto', 'cryptography', 'pyopenssl']),
- ),
- required_one_of=(
- ['path', 'content'],
- ),
- mutually_exclusive=(
- ['path', 'content'],
- ),
- supports_check_mode=True,
- )
-
- try:
- if module.params['path'] is not None:
- base_dir = os.path.dirname(module.params['path']) or '.'
- if not os.path.isdir(base_dir):
- module.fail_json(
- name=base_dir,
- msg='The directory %s does not exist or the file is not a directory' % base_dir
- )
-
- backend = module.params['select_crypto_backend']
- if backend == 'auto':
- # Detect what backend we can use
- can_use_cryptography = CRYPTOGRAPHY_FOUND and CRYPTOGRAPHY_VERSION >= LooseVersion(MINIMAL_CRYPTOGRAPHY_VERSION)
- can_use_pyopenssl = PYOPENSSL_FOUND and PYOPENSSL_VERSION >= LooseVersion(MINIMAL_PYOPENSSL_VERSION)
-
- # If cryptography is available we'll use it
- if can_use_cryptography:
- backend = 'cryptography'
- elif can_use_pyopenssl:
- backend = 'pyopenssl'
-
- # Fail if no backend has been found
- if backend == 'auto':
- module.fail_json(msg=("Can't detect any of the required Python libraries "
- "cryptography (>= {0}) or PyOpenSSL (>= {1})").format(
- MINIMAL_CRYPTOGRAPHY_VERSION,
- MINIMAL_PYOPENSSL_VERSION))
-
- if backend == 'pyopenssl':
- if not PYOPENSSL_FOUND:
- module.fail_json(msg=missing_required_lib('pyOpenSSL >= {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.13')
- privatekey = PrivateKeyInfoPyOpenSSL(module)
- elif backend == 'cryptography':
- if not CRYPTOGRAPHY_FOUND:
- module.fail_json(msg=missing_required_lib('cryptography >= {0}'.format(MINIMAL_CRYPTOGRAPHY_VERSION)),
- exception=CRYPTOGRAPHY_IMP_ERR)
- privatekey = PrivateKeyInfoCryptography(module)
-
- result = privatekey.get_info()
- module.exit_json(**result)
- except crypto_utils.OpenSSLObjectError as exc:
- module.fail_json(msg=to_native(exc))
-
-
-if __name__ == "__main__":
- main()
diff --git a/lib/ansible/modules/crypto/openssl_publickey.py b/lib/ansible/modules/crypto/openssl_publickey.py
deleted file mode 100644
index 6526b6fe93..0000000000
--- a/lib/ansible/modules/crypto/openssl_publickey.py
+++ /dev/null
@@ -1,478 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-
-# Copyright: (c) 2016, Yanis Guenane <yanis+ansible@guenane.org>
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'community'}
-
-DOCUMENTATION = r'''
----
-module: openssl_publickey
-version_added: "2.3"
-short_description: Generate an OpenSSL public key from its private key.
-description:
- - This module allows one to (re)generate OpenSSL public keys from their private keys.
- - Keys are generated in PEM or OpenSSH format.
- - 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. 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 Ansible 2.13."
-requirements:
- - Either 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)
-author:
- - Yanis Guenane (@Spredzy)
- - Felix Fontein (@felixfontein)
-options:
- state:
- description:
- - Whether the public key should exist or not, taking action if the state is different from what is stated.
- type: str
- default: present
- choices: [ absent, present ]
- force:
- description:
- - Should the key be regenerated even it it already exists.
- type: bool
- default: no
- format:
- description:
- - The format of the public key.
- type: str
- default: PEM
- choices: [ OpenSSH, PEM ]
- version_added: "2.4"
- path:
- description:
- - Name of the file in which the generated TLS/SSL public key will be written.
- type: path
- required: true
- privatekey_path:
- description:
- - Path to the TLS/SSL private key from which to generate the public key.
- - Either I(privatekey_path) or I(privatekey_content) must be specified, but not both.
- If I(state) is C(present), one of them is required.
- type: path
- privatekey_content:
- description:
- - The content of the TLS/SSL private key from which to generate the public key.
- - Either I(privatekey_path) or I(privatekey_content) must be specified, but not both.
- If I(state) is C(present), one of them is required.
- type: str
- version_added: "2.10"
- privatekey_passphrase:
- description:
- - The passphrase for the private key.
- type: str
- version_added: "2.4"
- backup:
- description:
- - Create a backup file including a timestamp so you can get the original
- public key back if you overwrote it with a different one by accident.
- type: bool
- default: no
- version_added: "2.8"
- select_crypto_backend:
- description:
- - Determines which crypto backend to use.
- - The default choice is C(auto), which tries to use C(cryptography) if available, and falls back to C(pyopenssl).
- - If set to C(pyopenssl), will try to use the L(pyOpenSSL,https://pypi.org/project/pyOpenSSL/) library.
- - If set to C(cryptography), will try to use the L(cryptography,https://cryptography.io/) library.
- type: str
- default: auto
- choices: [ auto, cryptography, pyopenssl ]
- version_added: "2.9"
- return_content:
- description:
- - If set to C(yes), will return the (current or generated) public key's content as I(publickey).
- type: bool
- default: no
- version_added: "2.10"
-extends_documentation_fragment:
-- files
-seealso:
-- module: openssl_certificate
-- module: openssl_csr
-- module: openssl_dhparam
-- module: openssl_pkcs12
-- module: openssl_privatekey
-'''
-
-EXAMPLES = r'''
-- name: Generate an OpenSSL public key in PEM format
- openssl_publickey:
- path: /etc/ssl/public/ansible.com.pem
- privatekey_path: /etc/ssl/private/ansible.com.pem
-
-- name: Generate an OpenSSL public key in PEM format from an inline key
- openssl_publickey:
- path: /etc/ssl/public/ansible.com.pem
- privatekey_content: "{{ private_key_content }}"
-
-- name: Generate an OpenSSL public key in OpenSSH v2 format
- openssl_publickey:
- path: /etc/ssl/public/ansible.com.pem
- privatekey_path: /etc/ssl/private/ansible.com.pem
- format: OpenSSH
-
-- name: Generate an OpenSSL public key with a passphrase protected private key
- openssl_publickey:
- path: /etc/ssl/public/ansible.com.pem
- privatekey_path: /etc/ssl/private/ansible.com.pem
- privatekey_passphrase: ansible
-
-- name: Force regenerate an OpenSSL public key if it already exists
- openssl_publickey:
- path: /etc/ssl/public/ansible.com.pem
- privatekey_path: /etc/ssl/private/ansible.com.pem
- force: yes
-
-- name: Remove an OpenSSL public key
- openssl_publickey:
- path: /etc/ssl/public/ansible.com.pem
- state: absent
-'''
-
-RETURN = r'''
-privatekey:
- description:
- - Path to the TLS/SSL private key the public key was generated from.
- - Will be C(none) if the private key has been provided in I(privatekey_content).
- returned: changed or success
- type: str
- sample: /etc/ssl/private/ansible.com.pem
-format:
- description: The format of the public key (PEM, OpenSSH, ...).
- returned: changed or success
- type: str
- sample: PEM
-filename:
- description: Path to the generated TLS/SSL public key file.
- returned: changed or success
- type: str
- sample: /etc/ssl/public/ansible.com.pem
-fingerprint:
- description:
- - 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
- type: dict
- sample:
- md5: "84:75:71:72:8d:04:b5:6c:4d:37:6d:66:83:f5:4c:29"
- sha1: "51:cc:7c:68:5d:eb:41:43:88:7e:1a:ae:c7:f8:24:72:ee:71:f6:10"
- sha224: "b1:19:a6:6c:14:ac:33:1d:ed:18:50:d3:06:5c:b2:32:91:f1:f1:52:8c:cb:d5:75:e9:f5:9b:46"
- sha256: "41:ab:c7:cb:d5:5f:30:60:46:99:ac:d4:00:70:cf:a1:76:4f:24:5d:10:24:57:5d:51:6e:09:97:df:2f:de:c7"
- sha384: "85:39:50:4e:de:d9:19:33:40:70:ae:10:ab:59:24:19:51:c3:a2:e4:0b:1c:b1:6e:dd:b3:0c:d9:9e:6a:46:af:da:18:f8:ef:ae:2e:c0:9a:75:2c:9b:b3:0f:3a:5f:3d"
- sha512: "fd:ed:5e:39:48:5f:9f:fe:7f:25:06:3f:79:08:cd:ee:a5:e7:b3:3d:13:82:87:1f:84:e1:f5:c7:28:77:53:94:86:56:38:69:f0:d9:35:22:01:1e:a6:60:...:0f:9b"
-backup_file:
- description: Name of backup file created.
- returned: changed and if I(backup) is C(yes)
- type: str
- sample: /path/to/publickey.pem.2019-03-09@11:22~
-publickey:
- description: The (current or generated) public key's content.
- returned: if I(state) is C(present) and I(return_content) is C(yes)
- type: str
- version_added: "2.10"
-'''
-
-import os
-import traceback
-from distutils.version import LooseVersion
-
-MINIMAL_PYOPENSSL_VERSION = '16.0.0'
-MINIMAL_CRYPTOGRAPHY_VERSION = '1.2.3'
-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
-try:
- import cryptography
- from cryptography.hazmat.backends import default_backend
- from cryptography.hazmat.primitives import serialization as crypto_serialization
- CRYPTOGRAPHY_VERSION = LooseVersion(cryptography.__version__)
-except ImportError:
- CRYPTOGRAPHY_IMP_ERR = traceback.format_exc()
- CRYPTOGRAPHY_FOUND = False
-else:
- CRYPTOGRAPHY_FOUND = True
-
-from ansible.module_utils import crypto as crypto_utils
-from ansible.module_utils._text import to_native
-from ansible.module_utils.basic import AnsibleModule, missing_required_lib
-
-
-class PublicKeyError(crypto_utils.OpenSSLObjectError):
- pass
-
-
-class PublicKey(crypto_utils.OpenSSLObject):
-
- def __init__(self, module, backend):
- super(PublicKey, self).__init__(
- module.params['path'],
- module.params['state'],
- module.params['force'],
- module.check_mode
- )
- self.format = module.params['format']
- self.privatekey_path = module.params['privatekey_path']
- self.privatekey_content = module.params['privatekey_content']
- if self.privatekey_content is not None:
- self.privatekey_content = self.privatekey_content.encode('utf-8')
- self.privatekey_passphrase = module.params['privatekey_passphrase']
- self.privatekey = None
- self.publickey_bytes = None
- self.return_content = module.params['return_content']
- self.fingerprint = {}
- self.backend = backend
-
- self.backup = module.params['backup']
- self.backup_file = None
-
- def _create_publickey(self, module):
- self.privatekey = crypto_utils.load_privatekey(
- path=self.privatekey_path,
- content=self.privatekey_content,
- passphrase=self.privatekey_passphrase,
- backend=self.backend
- )
- if self.backend == 'cryptography':
- if self.format == 'OpenSSH':
- return self.privatekey.public_key().public_bytes(
- crypto_serialization.Encoding.OpenSSH,
- crypto_serialization.PublicFormat.OpenSSH
- )
- else:
- return self.privatekey.public_key().public_bytes(
- crypto_serialization.Encoding.PEM,
- 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):
- """Generate the public key."""
-
- if self.privatekey_content is None and not os.path.exists(self.privatekey_path):
- raise PublicKeyError(
- 'The private key %s does not exist' % self.privatekey_path
- )
-
- if not self.check(module, perms_required=False) or self.force:
- try:
- publickey_content = self._create_publickey(module)
- if self.return_content:
- self.publickey_bytes = publickey_content
-
- if self.backup:
- self.backup_file = module.backup_local(self.path)
- crypto_utils.write_file(module, publickey_content)
-
- self.changed = True
- except crypto_utils.OpenSSLBadPassphraseError as exc:
- raise PublicKeyError(exc)
- except (IOError, OSError) as exc:
- raise PublicKeyError(exc)
-
- self.fingerprint = crypto_utils.get_fingerprint(
- path=self.privatekey_path,
- content=self.privatekey_content,
- passphrase=self.privatekey_passphrase,
- backend=self.backend,
- )
- file_args = module.load_file_common_arguments(module.params)
- if module.set_fs_attributes_if_different(file_args, False):
- self.changed = True
-
- def check(self, module, perms_required=True):
- """Ensure the resource is in its desired state."""
-
- state_and_perms = super(PublicKey, self).check(module, perms_required)
-
- def _check_privatekey():
- if self.privatekey_content is None and not os.path.exists(self.privatekey_path):
- return False
-
- try:
- with open(self.path, 'rb') as public_key_fh:
- publickey_content = public_key_fh.read()
- if self.return_content:
- self.publickey_bytes = publickey_content
- if self.backend == 'cryptography':
- if self.format == 'OpenSSH':
- # Read and dump public key. Makes sure that the comment is stripped off.
- current_publickey = crypto_serialization.load_ssh_public_key(publickey_content, backend=default_backend())
- publickey_content = current_publickey.public_bytes(
- crypto_serialization.Encoding.OpenSSH,
- crypto_serialization.PublicFormat.OpenSSH
- )
- else:
- current_publickey = crypto_serialization.load_pem_public_key(publickey_content, backend=default_backend())
- publickey_content = current_publickey.public_bytes(
- crypto_serialization.Encoding.PEM,
- 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:
- return False
-
- try:
- desired_publickey = self._create_publickey(module)
- except crypto_utils.OpenSSLBadPassphraseError as exc:
- raise PublicKeyError(exc)
-
- return publickey_content == desired_publickey
-
- if not state_and_perms:
- return state_and_perms
-
- return _check_privatekey()
-
- def remove(self, module):
- if self.backup:
- self.backup_file = module.backup_local(self.path)
- super(PublicKey, self).remove(module)
-
- def dump(self):
- """Serialize the object into a dictionary."""
-
- result = {
- 'privatekey': self.privatekey_path,
- 'filename': self.path,
- 'format': self.format,
- 'changed': self.changed,
- 'fingerprint': self.fingerprint,
- }
- if self.backup_file:
- result['backup_file'] = self.backup_file
- if self.return_content:
- if self.publickey_bytes is None:
- self.publickey_bytes = crypto_utils.load_file_if_exists(self.path, ignore_errors=True)
- result['publickey'] = self.publickey_bytes.decode('utf-8') if self.publickey_bytes else None
-
- return result
-
-
-def main():
-
- module = AnsibleModule(
- argument_spec=dict(
- state=dict(type='str', default='present', choices=['present', 'absent']),
- force=dict(type='bool', default=False),
- path=dict(type='path', required=True),
- privatekey_path=dict(type='path'),
- privatekey_content=dict(type='str'),
- format=dict(type='str', default='PEM', choices=['OpenSSH', 'PEM']),
- privatekey_passphrase=dict(type='str', no_log=True),
- backup=dict(type='bool', default=False),
- select_crypto_backend=dict(type='str', choices=['auto', 'pyopenssl', 'cryptography'], default='auto'),
- return_content=dict(type='bool', default=False),
- ),
- supports_check_mode=True,
- add_file_common_args=True,
- required_if=[('state', 'present', ['privatekey_path', 'privatekey_content'], True)],
- mutually_exclusive=(
- ['privatekey_path', 'privatekey_content'],
- ),
- )
-
- minimal_cryptography_version = MINIMAL_CRYPTOGRAPHY_VERSION
- if module.params['format'] == 'OpenSSH':
- minimal_cryptography_version = MINIMAL_CRYPTOGRAPHY_VERSION_OPENSSH
-
- backend = module.params['select_crypto_backend']
- if backend == 'auto':
- # Detection what is possible
- 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
- if can_use_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?
- if backend == 'auto':
- module.fail_json(msg=("Can't detect any of the required Python libraries "
- "cryptography (>= {0}) or PyOpenSSL (>= {1})").format(
- minimal_cryptography_version,
- MINIMAL_PYOPENSSL_VERSION))
-
- if module.params['format'] == 'OpenSSH' and backend != 'cryptography':
- module.fail_json(msg="Format OpenSSH requires the cryptography backend.")
-
- 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.13')
- elif backend == 'cryptography':
- if not CRYPTOGRAPHY_FOUND:
- module.fail_json(msg=missing_required_lib('cryptography >= {0}'.format(minimal_cryptography_version)),
- exception=CRYPTOGRAPHY_IMP_ERR)
-
- base_dir = os.path.dirname(module.params['path']) or '.'
- if not os.path.isdir(base_dir):
- module.fail_json(
- name=base_dir,
- msg="The directory '%s' does not exist or the file is not a directory" % base_dir
- )
-
- try:
- public_key = PublicKey(module, backend)
-
- if public_key.state == 'present':
- if module.check_mode:
- result = public_key.dump()
- result['changed'] = module.params['force'] or not public_key.check(module)
- module.exit_json(**result)
-
- public_key.generate(module)
- else:
- if module.check_mode:
- result = public_key.dump()
- result['changed'] = os.path.exists(module.params['path'])
- module.exit_json(**result)
-
- public_key.remove(module)
-
- result = public_key.dump()
- module.exit_json(**result)
- except crypto_utils.OpenSSLObjectError as exc:
- module.fail_json(msg=to_native(exc))
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/crypto/x509_crl.py b/lib/ansible/modules/crypto/x509_crl.py
deleted file mode 100644
index ef601edadc..0000000000
--- a/lib/ansible/modules/crypto/x509_crl.py
+++ /dev/null
@@ -1,783 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-
-# Copyright: (c) 2019, Felix Fontein <felix@fontein.de>
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'community'}
-
-DOCUMENTATION = r'''
----
-module: x509_crl
-version_added: "2.10"
-short_description: Generate Certificate Revocation Lists (CRLs)
-description:
- - This module allows one to (re)generate or update Certificate Revocation Lists (CRLs).
- - Certificates on the revocation list can be either specified via serial number and (optionally) their issuer,
- or as a path to a certificate file in PEM format.
-requirements:
- - cryptography >= 1.2
-author:
- - Felix Fontein (@felixfontein)
-options:
- state:
- description:
- - Whether the CRL file should exist or not, taking action if the state is different from what is stated.
- type: str
- default: present
- choices: [ absent, present ]
-
- mode:
- description:
- - Defines how to process entries of existing CRLs.
- - If set to C(generate), makes sure that the CRL has the exact set of revoked certificates
- as specified in I(revoked_certificates).
- - If set to C(update), makes sure that the CRL contains the revoked certificates from
- I(revoked_certificates), but can also contain other revoked certificates. If the CRL file
- already exists, all entries from the existing CRL will also be included in the new CRL.
- When using C(update), you might be interested in setting I(ignore_timestamps) to C(yes).
- type: str
- default: generate
- choices: [ generate, update ]
-
- force:
- description:
- - Should the CRL be forced to be regenerated.
- type: bool
- default: no
-
- backup:
- description:
- - Create a backup file including a timestamp so you can get the original
- CRL back if you overwrote it with a new one by accident.
- type: bool
- default: no
-
- path:
- description:
- - Remote absolute path where the generated CRL file should be created or is already located.
- type: path
- required: yes
-
- privatekey_path:
- description:
- - Path to the CA's private key to use when signing the CRL.
- - Either I(privatekey_path) or I(privatekey_content) must be specified if I(state) is C(present), but not both.
- type: path
-
- privatekey_content:
- description:
- - The content of the CA's private key to use when signing the CRL.
- - Either I(privatekey_path) or I(privatekey_content) must be specified if I(state) is C(present), but not both.
- type: str
-
- privatekey_passphrase:
- description:
- - The passphrase for the I(privatekey_path).
- - This is required if the private key is password protected.
- type: str
-
- issuer:
- description:
- - Key/value pairs that will be present in the issuer name field of the CRL.
- - If you need to specify more than one value with the same key, use a list as value.
- - Required if I(state) is C(present).
- type: dict
-
- last_update:
- description:
- - The point in time from which this CRL can be trusted.
- - Time can be specified either as relative time or as absolute timestamp.
- - Time will always be interpreted as UTC.
- - Valid format is C([+-]timespec | ASN.1 TIME) where timespec can be an integer
- + C([w | d | h | m | s]) (e.g. C(+32w1d2h).
- - Note that if using relative time this module is NOT idempotent, except when
- I(ignore_timestamps) is set to C(yes).
- type: str
- default: "+0s"
-
- next_update:
- description:
- - "The absolute latest point in time by which this I(issuer) is expected to have issued
- another CRL. Many clients will treat a CRL as expired once I(next_update) occurs."
- - Time can be specified either as relative time or as absolute timestamp.
- - Time will always be interpreted as UTC.
- - Valid format is C([+-]timespec | ASN.1 TIME) where timespec can be an integer
- + C([w | d | h | m | s]) (e.g. C(+32w1d2h).
- - Note that if using relative time this module is NOT idempotent, except when
- I(ignore_timestamps) is set to C(yes).
- - Required if I(state) is C(present).
- type: str
-
- digest:
- description:
- - Digest algorithm to be used when signing the CRL.
- type: str
- default: sha256
-
- revoked_certificates:
- description:
- - List of certificates to be revoked.
- - Required if I(state) is C(present).
- type: list
- elements: dict
- suboptions:
- path:
- description:
- - Path to a certificate in PEM format.
- - The serial number and issuer will be extracted from the certificate.
- - Mutually exclusive with I(content) and I(serial_number). One of these three options
- must be specified.
- type: path
- content:
- description:
- - Content of a certificate in PEM format.
- - The serial number and issuer will be extracted from the certificate.
- - Mutually exclusive with I(path) and I(serial_number). One of these three options
- must be specified.
- type: str
- serial_number:
- description:
- - Serial number of the certificate.
- - Mutually exclusive with I(path) and I(content). One of these three options must
- be specified.
- type: int
- revocation_date:
- description:
- - The point in time the certificate was revoked.
- - Time can be specified either as relative time or as absolute timestamp.
- - Time will always be interpreted as UTC.
- - Valid format is C([+-]timespec | ASN.1 TIME) where timespec can be an integer
- + C([w | d | h | m | s]) (e.g. C(+32w1d2h).
- - Note that if using relative time this module is NOT idempotent, except when
- I(ignore_timestamps) is set to C(yes).
- type: str
- default: "+0s"
- issuer:
- description:
- - The certificate's issuer.
- - "Example: C(DNS:ca.example.org)"
- type: list
- elements: str
- issuer_critical:
- description:
- - Whether the certificate issuer extension should be critical.
- type: bool
- default: no
- reason:
- description:
- - The value for the revocation reason extension.
- type: str
- choices:
- - unspecified
- - key_compromise
- - ca_compromise
- - affiliation_changed
- - superseded
- - cessation_of_operation
- - certificate_hold
- - privilege_withdrawn
- - aa_compromise
- - remove_from_crl
- reason_critical:
- description:
- - Whether the revocation reason extension should be critical.
- type: bool
- default: no
- invalidity_date:
- description:
- - The point in time it was known/suspected that the private key was compromised
- or that the certificate otherwise became invalid.
- - Time can be specified either as relative time or as absolute timestamp.
- - Time will always be interpreted as UTC.
- - Valid format is C([+-]timespec | ASN.1 TIME) where timespec can be an integer
- + C([w | d | h | m | s]) (e.g. C(+32w1d2h).
- - Note that if using relative time this module is NOT idempotent. This will NOT
- change when I(ignore_timestamps) is set to C(yes).
- type: str
- invalidity_date_critical:
- description:
- - Whether the invalidity date extension should be critical.
- type: bool
- default: no
-
- ignore_timestamps:
- description:
- - Whether the timestamps I(last_update), I(next_update) and I(revocation_date) (in
- I(revoked_certificates)) should be ignored for idempotency checks. The timestamp
- I(invalidity_date) in I(revoked_certificates) will never be ignored.
- - Use this in combination with relative timestamps for these values to get idempotency.
- type: bool
- default: no
-
- return_content:
- description:
- - If set to C(yes), will return the (current or generated) CRL's content as I(crl).
- type: bool
- default: no
-
-extends_documentation_fragment:
- - files
-
-notes:
- - All ASN.1 TIME values should be specified following the YYYYMMDDHHMMSSZ pattern.
- - Date specified should be UTC. Minutes and seconds are mandatory.
-'''
-
-EXAMPLES = r'''
-- name: Generate a CRL
- x509_crl:
- path: /etc/ssl/my-ca.crl
- privatekey_path: /etc/ssl/private/my-ca.pem
- issuer:
- CN: My CA
- last_update: "+0s"
- next_update: "+7d"
- revoked_certificates:
- - serial_number: 1234
- revocation_date: 20190331202428Z
- issuer:
- CN: My CA
- - serial_number: 2345
- revocation_date: 20191013152910Z
- reason: affiliation_changed
- invalidity_date: 20191001000000Z
- - path: /etc/ssl/crt/revoked-cert.pem
- revocation_date: 20191010010203Z
-'''
-
-RETURN = r'''
-filename:
- description: Path to the generated CRL
- returned: changed or success
- type: str
- sample: /path/to/my-ca.crl
-backup_file:
- description: Name of backup file created.
- returned: changed and if I(backup) is C(yes)
- type: str
- sample: /path/to/my-ca.crl.2019-03-09@11:22~
-privatekey:
- description: Path to the private CA key
- returned: changed or success
- type: str
- sample: /path/to/my-ca.pem
-issuer:
- description:
- - The CRL's issuer.
- - Note that for repeated values, only the last one will be returned.
- returned: success
- type: dict
- sample: '{"organizationName": "Ansible", "commonName": "ca.example.com"}'
-issuer_ordered:
- description: The CRL's issuer as an ordered list of tuples.
- returned: success
- type: list
- elements: list
- sample: '[["organizationName", "Ansible"], ["commonName": "ca.example.com"]]'
-last_update:
- description: The point in time from which this CRL can be trusted as ASN.1 TIME.
- returned: success
- type: str
- sample: 20190413202428Z
-next_update:
- description: The point in time from which a new CRL will be issued and the client has to check for it as ASN.1 TIME.
- returned: success
- type: str
- sample: 20190413202428Z
-digest:
- description: The signature algorithm used to sign the CRL.
- returned: success
- type: str
- sample: sha256WithRSAEncryption
-revoked_certificates:
- description: List of certificates to be revoked.
- returned: success
- type: list
- elements: dict
- contains:
- serial_number:
- description: Serial number of the certificate.
- type: int
- sample: 1234
- revocation_date:
- description: The point in time the certificate was revoked as ASN.1 TIME.
- type: str
- sample: 20190413202428Z
- issuer:
- description: The certificate's issuer.
- type: list
- elements: str
- sample: '["DNS:ca.example.org"]'
- issuer_critical:
- description: Whether the certificate issuer extension is critical.
- type: bool
- sample: no
- reason:
- description:
- - The value for the revocation reason extension.
- - One of C(unspecified), C(key_compromise), C(ca_compromise), C(affiliation_changed), C(superseded),
- C(cessation_of_operation), C(certificate_hold), C(privilege_withdrawn), C(aa_compromise), and
- C(remove_from_crl).
- type: str
- sample: key_compromise
- reason_critical:
- description: Whether the revocation reason extension is critical.
- type: bool
- sample: no
- invalidity_date:
- description: |
- The point in time it was known/suspected that the private key was compromised
- or that the certificate otherwise became invalid as ASN.1 TIME.
- type: str
- sample: 20190413202428Z
- invalidity_date_critical:
- description: Whether the invalidity date extension is critical.
- type: bool
- sample: no
-crl:
- description: The (current or generated) CRL's content.
- returned: if I(state) is C(present) and I(return_content) is C(yes)
- type: str
-'''
-
-
-import os
-import traceback
-from distutils.version import LooseVersion
-
-from ansible.module_utils import crypto as crypto_utils
-from ansible.module_utils._text import to_native, to_text
-from ansible.module_utils.basic import AnsibleModule, missing_required_lib
-
-MINIMAL_CRYPTOGRAPHY_VERSION = '1.2'
-
-CRYPTOGRAPHY_IMP_ERR = None
-try:
- import cryptography
- from cryptography import x509
- from cryptography.hazmat.backends import default_backend
- from cryptography.hazmat.primitives.serialization import Encoding
- from cryptography.x509 import (
- CertificateRevocationListBuilder,
- RevokedCertificateBuilder,
- NameAttribute,
- Name,
- )
- CRYPTOGRAPHY_VERSION = LooseVersion(cryptography.__version__)
-except ImportError:
- CRYPTOGRAPHY_IMP_ERR = traceback.format_exc()
- CRYPTOGRAPHY_FOUND = False
-else:
- CRYPTOGRAPHY_FOUND = True
-
-
-TIMESTAMP_FORMAT = "%Y%m%d%H%M%SZ"
-
-
-class CRLError(crypto_utils.OpenSSLObjectError):
- pass
-
-
-class CRL(crypto_utils.OpenSSLObject):
-
- def __init__(self, module):
- super(CRL, self).__init__(
- module.params['path'],
- module.params['state'],
- module.params['force'],
- module.check_mode
- )
-
- self.update = module.params['mode'] == 'update'
- self.ignore_timestamps = module.params['ignore_timestamps']
- self.return_content = module.params['return_content']
- self.crl_content = None
-
- self.privatekey_path = module.params['privatekey_path']
- self.privatekey_content = module.params['privatekey_content']
- if self.privatekey_content is not None:
- self.privatekey_content = self.privatekey_content.encode('utf-8')
- self.privatekey_passphrase = module.params['privatekey_passphrase']
-
- self.issuer = crypto_utils.parse_name_field(module.params['issuer'])
- self.issuer = [(entry[0], entry[1]) for entry in self.issuer if entry[1]]
-
- self.last_update = crypto_utils.get_relative_time_option(module.params['last_update'], 'last_update')
- self.next_update = crypto_utils.get_relative_time_option(module.params['next_update'], 'next_update')
-
- self.digest = crypto_utils.select_message_digest(module.params['digest'])
- if self.digest is None:
- raise CRLError('The digest "{0}" is not supported'.format(module.params['digest']))
-
- self.revoked_certificates = []
- for i, rc in enumerate(module.params['revoked_certificates']):
- result = {
- 'serial_number': None,
- 'revocation_date': None,
- 'issuer': None,
- 'issuer_critical': False,
- 'reason': None,
- 'reason_critical': False,
- 'invalidity_date': None,
- 'invalidity_date_critical': False,
- }
- path_prefix = 'revoked_certificates[{0}].'.format(i)
- if rc['path'] is not None or rc['content'] is not None:
- # Load certificate from file or content
- try:
- if rc['content'] is not None:
- rc['content'] = rc['content'].encode('utf-8')
- cert = crypto_utils.load_certificate(rc['path'], content=rc['content'], backend='cryptography')
- try:
- result['serial_number'] = cert.serial_number
- except AttributeError:
- # The property was called "serial" before cryptography 1.4
- result['serial_number'] = cert.serial
- except crypto_utils.OpenSSLObjectError as e:
- if rc['content'] is not None:
- module.fail_json(
- msg='Cannot parse certificate from {0}content: {1}'.format(path_prefix, to_native(e))
- )
- else:
- module.fail_json(
- msg='Cannot read certificate "{1}" from {0}path: {2}'.format(path_prefix, rc['path'], to_native(e))
- )
- else:
- # Specify serial_number (and potentially issuer) directly
- result['serial_number'] = rc['serial_number']
- # All other options
- if rc['issuer']:
- result['issuer'] = [crypto_utils.cryptography_get_name(issuer) for issuer in rc['issuer']]
- result['issuer_critical'] = rc['issuer_critical']
- result['revocation_date'] = crypto_utils.get_relative_time_option(
- rc['revocation_date'],
- path_prefix + 'revocation_date'
- )
- if rc['reason']:
- result['reason'] = crypto_utils.REVOCATION_REASON_MAP[rc['reason']]
- result['reason_critical'] = rc['reason_critical']
- if rc['invalidity_date']:
- result['invalidity_date'] = crypto_utils.get_relative_time_option(
- rc['invalidity_date'],
- path_prefix + 'invalidity_date'
- )
- result['invalidity_date_critical'] = rc['invalidity_date_critical']
- self.revoked_certificates.append(result)
-
- self.module = module
-
- self.backup = module.params['backup']
- self.backup_file = None
-
- try:
- self.privatekey = crypto_utils.load_privatekey(
- path=self.privatekey_path,
- content=self.privatekey_content,
- passphrase=self.privatekey_passphrase,
- backend='cryptography'
- )
- except crypto_utils.OpenSSLBadPassphraseError as exc:
- raise CRLError(exc)
-
- self.crl = None
- try:
- with open(self.path, 'rb') as f:
- data = f.read()
- self.crl = x509.load_pem_x509_crl(data, default_backend())
- if self.return_content:
- self.crl_content = data
- except Exception as dummy:
- self.crl_content = None
-
- def remove(self):
- if self.backup:
- self.backup_file = self.module.backup_local(self.path)
- super(CRL, self).remove(self.module)
-
- def _compress_entry(self, entry):
- if self.ignore_timestamps:
- # Throw out revocation_date
- return (
- entry['serial_number'],
- tuple(entry['issuer']) if entry['issuer'] is not None else None,
- entry['issuer_critical'],
- entry['reason'],
- entry['reason_critical'],
- entry['invalidity_date'],
- entry['invalidity_date_critical'],
- )
- else:
- return (
- entry['serial_number'],
- entry['revocation_date'],
- tuple(entry['issuer']) if entry['issuer'] is not None else None,
- entry['issuer_critical'],
- entry['reason'],
- entry['reason_critical'],
- entry['invalidity_date'],
- entry['invalidity_date_critical'],
- )
-
- def check(self, perms_required=True):
- """Ensure the resource is in its desired state."""
-
- state_and_perms = super(CRL, self).check(self.module, perms_required)
-
- if not state_and_perms:
- return False
-
- if self.crl is None:
- return False
-
- if self.last_update != self.crl.last_update and not self.ignore_timestamps:
- return False
- if self.next_update != self.crl.next_update and not self.ignore_timestamps:
- return False
- if self.digest.name != self.crl.signature_hash_algorithm.name:
- return False
-
- want_issuer = [(crypto_utils.cryptography_name_to_oid(entry[0]), entry[1]) for entry in self.issuer]
- if want_issuer != [(sub.oid, sub.value) for sub in self.crl.issuer]:
- return False
-
- old_entries = [self._compress_entry(crypto_utils.cryptography_decode_revoked_certificate(cert)) for cert in self.crl]
- new_entries = [self._compress_entry(cert) for cert in self.revoked_certificates]
- if self.update:
- # We don't simply use a set so that duplicate entries are treated correctly
- for entry in new_entries:
- try:
- old_entries.remove(entry)
- except ValueError:
- return False
- else:
- if old_entries != new_entries:
- return False
-
- return True
-
- def _generate_crl(self):
- backend = default_backend()
- crl = CertificateRevocationListBuilder()
-
- try:
- crl = crl.issuer_name(Name([
- NameAttribute(crypto_utils.cryptography_name_to_oid(entry[0]), to_text(entry[1]))
- for entry in self.issuer
- ]))
- except ValueError as e:
- raise CRLError(e)
-
- crl = crl.last_update(self.last_update)
- crl = crl.next_update(self.next_update)
-
- if self.update and self.crl:
- new_entries = set([self._compress_entry(entry) for entry in self.revoked_certificates])
- for entry in self.crl:
- decoded_entry = self._compress_entry(crypto_utils.cryptography_decode_revoked_certificate(entry))
- if decoded_entry not in new_entries:
- crl = crl.add_revoked_certificate(entry)
- for entry in self.revoked_certificates:
- revoked_cert = RevokedCertificateBuilder()
- revoked_cert = revoked_cert.serial_number(entry['serial_number'])
- revoked_cert = revoked_cert.revocation_date(entry['revocation_date'])
- if entry['issuer'] is not None:
- revoked_cert = revoked_cert.add_extension(
- x509.CertificateIssuer([
- crypto_utils.cryptography_get_name(name) for name in self.entry['issuer']
- ]),
- entry['issuer_critical']
- )
- if entry['reason'] is not None:
- revoked_cert = revoked_cert.add_extension(
- x509.CRLReason(entry['reason']),
- entry['reason_critical']
- )
- if entry['invalidity_date'] is not None:
- revoked_cert = revoked_cert.add_extension(
- x509.InvalidityDate(entry['invalidity_date']),
- entry['invalidity_date_critical']
- )
- crl = crl.add_revoked_certificate(revoked_cert.build(backend))
-
- self.crl = crl.sign(self.privatekey, self.digest, backend=backend)
- return self.crl.public_bytes(Encoding.PEM)
-
- def generate(self):
- if not self.check(perms_required=False) or self.force:
- result = self._generate_crl()
- if self.return_content:
- self.crl_content = result
- if self.backup:
- self.backup_file = self.module.backup_local(self.path)
- crypto_utils.write_file(self.module, result)
- self.changed = True
-
- file_args = self.module.load_file_common_arguments(self.module.params)
- if self.module.set_fs_attributes_if_different(file_args, False):
- self.changed = True
-
- def _dump_revoked(self, entry):
- return {
- 'serial_number': entry['serial_number'],
- 'revocation_date': entry['revocation_date'].strftime(TIMESTAMP_FORMAT),
- 'issuer':
- [crypto_utils.cryptography_decode_name(issuer) for issuer in entry['issuer']]
- if entry['issuer'] is not None else None,
- 'issuer_critical': entry['issuer_critical'],
- 'reason': crypto_utils.REVOCATION_REASON_MAP_INVERSE.get(entry['reason']) if entry['reason'] is not None else None,
- 'reason_critical': entry['reason_critical'],
- 'invalidity_date':
- entry['invalidity_date'].strftime(TIMESTAMP_FORMAT)
- if entry['invalidity_date'] is not None else None,
- 'invalidity_date_critical': entry['invalidity_date_critical'],
- }
-
- def dump(self, check_mode=False):
- result = {
- 'changed': self.changed,
- 'filename': self.path,
- 'privatekey': self.privatekey_path,
- 'last_update': None,
- 'next_update': None,
- 'digest': None,
- 'issuer_ordered': None,
- 'issuer': None,
- 'revoked_certificates': [],
- }
- if self.backup_file:
- result['backup_file'] = self.backup_file
-
- if check_mode:
- result['last_update'] = self.last_update.strftime(TIMESTAMP_FORMAT)
- result['next_update'] = self.next_update.strftime(TIMESTAMP_FORMAT)
- # result['digest'] = crypto_utils.cryptography_oid_to_name(self.crl.signature_algorithm_oid)
- result['digest'] = self.module.params['digest']
- result['issuer_ordered'] = self.issuer
- result['issuer'] = {}
- for k, v in self.issuer:
- result['issuer'][k] = v
- result['revoked_certificates'] = []
- for entry in self.revoked_certificates:
- result['revoked_certificates'].append(self._dump_revoked(entry))
- elif self.crl:
- result['last_update'] = self.crl.last_update.strftime(TIMESTAMP_FORMAT)
- result['next_update'] = self.crl.next_update.strftime(TIMESTAMP_FORMAT)
- try:
- result['digest'] = crypto_utils.cryptography_oid_to_name(self.crl.signature_algorithm_oid)
- except AttributeError:
- # Older cryptography versions don't have signature_algorithm_oid yet
- dotted = crypto_utils._obj2txt(
- self.crl._backend._lib,
- self.crl._backend._ffi,
- self.crl._x509_crl.sig_alg.algorithm
- )
- oid = x509.oid.ObjectIdentifier(dotted)
- result['digest'] = crypto_utils.cryptography_oid_to_name(oid)
- issuer = []
- for attribute in self.crl.issuer:
- issuer.append([crypto_utils.cryptography_oid_to_name(attribute.oid), attribute.value])
- result['issuer_ordered'] = issuer
- result['issuer'] = {}
- for k, v in issuer:
- result['issuer'][k] = v
- result['revoked_certificates'] = []
- for cert in self.crl:
- entry = crypto_utils.cryptography_decode_revoked_certificate(cert)
- result['revoked_certificates'].append(self._dump_revoked(entry))
-
- if self.return_content:
- result['crl'] = self.crl_content
-
- return result
-
-
-def main():
- module = AnsibleModule(
- argument_spec=dict(
- state=dict(type='str', default='present', choices=['present', 'absent']),
- mode=dict(type='str', default='generate', choices=['generate', 'update']),
- force=dict(type='bool', default=False),
- backup=dict(type='bool', default=False),
- path=dict(type='path', required=True),
- privatekey_path=dict(type='path'),
- privatekey_content=dict(type='str'),
- privatekey_passphrase=dict(type='str', no_log=True),
- issuer=dict(type='dict'),
- last_update=dict(type='str', default='+0s'),
- next_update=dict(type='str'),
- digest=dict(type='str', default='sha256'),
- ignore_timestamps=dict(type='bool', default=False),
- return_content=dict(type='bool', default=False),
- revoked_certificates=dict(
- type='list',
- elements='dict',
- options=dict(
- path=dict(type='path'),
- content=dict(type='str'),
- serial_number=dict(type='int'),
- revocation_date=dict(type='str', default='+0s'),
- issuer=dict(type='list', elements='str'),
- issuer_critical=dict(type='bool', default=False),
- reason=dict(
- type='str',
- choices=[
- 'unspecified', 'key_compromise', 'ca_compromise', 'affiliation_changed',
- 'superseded', 'cessation_of_operation', 'certificate_hold',
- 'privilege_withdrawn', 'aa_compromise', 'remove_from_crl'
- ]
- ),
- reason_critical=dict(type='bool', default=False),
- invalidity_date=dict(type='str'),
- invalidity_date_critical=dict(type='bool', default=False),
- ),
- required_one_of=[['path', 'content', 'serial_number']],
- mutually_exclusive=[['path', 'content', 'serial_number']],
- ),
- ),
- required_if=[
- ('state', 'present', ['privatekey_path', 'privatekey_content'], True),
- ('state', 'present', ['issuer', 'next_update', 'revoked_certificates'], False),
- ],
- mutually_exclusive=(
- ['privatekey_path', 'privatekey_content'],
- ),
- supports_check_mode=True,
- add_file_common_args=True,
- )
-
- if not CRYPTOGRAPHY_FOUND:
- module.fail_json(msg=missing_required_lib('cryptography >= {0}'.format(MINIMAL_CRYPTOGRAPHY_VERSION)),
- exception=CRYPTOGRAPHY_IMP_ERR)
-
- try:
- crl = CRL(module)
-
- if module.params['state'] == 'present':
- if module.check_mode:
- result = crl.dump(check_mode=True)
- result['changed'] = module.params['force'] or not crl.check()
- module.exit_json(**result)
-
- crl.generate()
- else:
- if module.check_mode:
- result = crl.dump(check_mode=True)
- result['changed'] = os.path.exists(module.params['path'])
- module.exit_json(**result)
-
- crl.remove()
-
- result = crl.dump()
- module.exit_json(**result)
- except crypto_utils.OpenSSLObjectError as exc:
- module.fail_json(msg=to_native(exc))
-
-
-if __name__ == "__main__":
- main()
diff --git a/lib/ansible/modules/crypto/x509_crl_info.py b/lib/ansible/modules/crypto/x509_crl_info.py
deleted file mode 100644
index b61db26ff1..0000000000
--- a/lib/ansible/modules/crypto/x509_crl_info.py
+++ /dev/null
@@ -1,281 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-
-# Copyright: (c) 2020, Felix Fontein <felix@fontein.de>
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'community'}
-
-DOCUMENTATION = r'''
----
-module: x509_crl_info
-version_added: "2.10"
-short_description: Retrieve information on Certificate Revocation Lists (CRLs)
-description:
- - This module allows one to retrieve information on Certificate Revocation Lists (CRLs).
-requirements:
- - cryptography >= 1.2
-author:
- - Felix Fontein (@felixfontein)
-options:
- path:
- description:
- - Remote absolute path where the generated CRL file should be created or is already located.
- - Either I(path) or I(content) must be specified, but not both.
- type: path
- content:
- description:
- - Content of the X.509 certificate in PEM format.
- - Either I(path) or I(content) must be specified, but not both.
- type: str
-
-notes:
- - All timestamp values are provided in ASN.1 TIME format, i.e. following the C(YYYYMMDDHHMMSSZ) pattern.
- They are all in UTC.
-seealso:
- - module: x509_crl
-'''
-
-EXAMPLES = r'''
-- name: Get information on CRL
- x509_crl_info:
- path: /etc/ssl/my-ca.crl
- register: result
-
-- debug:
- msg: "{{ result }}"
-'''
-
-RETURN = r'''
-issuer:
- description:
- - The CRL's issuer.
- - Note that for repeated values, only the last one will be returned.
- returned: success
- type: dict
- sample: '{"organizationName": "Ansible", "commonName": "ca.example.com"}'
-issuer_ordered:
- description: The CRL's issuer as an ordered list of tuples.
- returned: success
- type: list
- elements: list
- sample: '[["organizationName", "Ansible"], ["commonName": "ca.example.com"]]'
-last_update:
- description: The point in time from which this CRL can be trusted as ASN.1 TIME.
- returned: success
- type: str
- sample: 20190413202428Z
-next_update:
- description: The point in time from which a new CRL will be issued and the client has to check for it as ASN.1 TIME.
- returned: success
- type: str
- sample: 20190413202428Z
-digest:
- description: The signature algorithm used to sign the CRL.
- returned: success
- type: str
- sample: sha256WithRSAEncryption
-revoked_certificates:
- description: List of certificates to be revoked.
- returned: success
- type: list
- elements: dict
- contains:
- serial_number:
- description: Serial number of the certificate.
- type: int
- sample: 1234
- revocation_date:
- description: The point in time the certificate was revoked as ASN.1 TIME.
- type: str
- sample: 20190413202428Z
- issuer:
- description: The certificate's issuer.
- type: list
- elements: str
- sample: '["DNS:ca.example.org"]'
- issuer_critical:
- description: Whether the certificate issuer extension is critical.
- type: bool
- sample: no
- reason:
- description:
- - The value for the revocation reason extension.
- - One of C(unspecified), C(key_compromise), C(ca_compromise), C(affiliation_changed), C(superseded),
- C(cessation_of_operation), C(certificate_hold), C(privilege_withdrawn), C(aa_compromise), and
- C(remove_from_crl).
- type: str
- sample: key_compromise
- reason_critical:
- description: Whether the revocation reason extension is critical.
- type: bool
- sample: no
- invalidity_date:
- description: |
- The point in time it was known/suspected that the private key was compromised
- or that the certificate otherwise became invalid as ASN.1 TIME.
- type: str
- sample: 20190413202428Z
- invalidity_date_critical:
- description: Whether the invalidity date extension is critical.
- type: bool
- sample: no
-'''
-
-
-import traceback
-from distutils.version import LooseVersion
-
-from ansible.module_utils import crypto as crypto_utils
-from ansible.module_utils._text import to_native
-from ansible.module_utils.basic import AnsibleModule, missing_required_lib
-
-MINIMAL_CRYPTOGRAPHY_VERSION = '1.2'
-
-CRYPTOGRAPHY_IMP_ERR = None
-try:
- import cryptography
- from cryptography import x509
- from cryptography.hazmat.backends import default_backend
- CRYPTOGRAPHY_VERSION = LooseVersion(cryptography.__version__)
-except ImportError:
- CRYPTOGRAPHY_IMP_ERR = traceback.format_exc()
- CRYPTOGRAPHY_FOUND = False
-else:
- CRYPTOGRAPHY_FOUND = True
-
-
-TIMESTAMP_FORMAT = "%Y%m%d%H%M%SZ"
-
-
-class CRLError(crypto_utils.OpenSSLObjectError):
- pass
-
-
-class CRLInfo(crypto_utils.OpenSSLObject):
- """The main module implementation."""
-
- def __init__(self, module):
- super(CRLInfo, self).__init__(
- module.params['path'] or '',
- 'present',
- False,
- module.check_mode
- )
-
- self.content = module.params['content']
-
- self.module = module
-
- self.crl = None
- if self.content is None:
- try:
- with open(self.path, 'rb') as f:
- data = f.read()
- except Exception as e:
- self.module.fail_json(msg='Error while reading CRL file from disk: {0}'.format(e))
- else:
- data = self.content.encode('utf-8')
-
- try:
- self.crl = x509.load_pem_x509_crl(data, default_backend())
- except Exception as e:
- self.module.fail_json(msg='Error while decoding CRL: {0}'.format(e))
-
- def _dump_revoked(self, entry):
- return {
- 'serial_number': entry['serial_number'],
- 'revocation_date': entry['revocation_date'].strftime(TIMESTAMP_FORMAT),
- 'issuer':
- [crypto_utils.cryptography_decode_name(issuer) for issuer in entry['issuer']]
- if entry['issuer'] is not None else None,
- 'issuer_critical': entry['issuer_critical'],
- 'reason': crypto_utils.REVOCATION_REASON_MAP_INVERSE.get(entry['reason']) if entry['reason'] is not None else None,
- 'reason_critical': entry['reason_critical'],
- 'invalidity_date':
- entry['invalidity_date'].strftime(TIMESTAMP_FORMAT)
- if entry['invalidity_date'] is not None else None,
- 'invalidity_date_critical': entry['invalidity_date_critical'],
- }
-
- def get_info(self):
- result = {
- 'changed': False,
- 'last_update': None,
- 'next_update': None,
- 'digest': None,
- 'issuer_ordered': None,
- 'issuer': None,
- 'revoked_certificates': [],
- }
-
- result['last_update'] = self.crl.last_update.strftime(TIMESTAMP_FORMAT)
- result['next_update'] = self.crl.next_update.strftime(TIMESTAMP_FORMAT)
- try:
- result['digest'] = crypto_utils.cryptography_oid_to_name(self.crl.signature_algorithm_oid)
- except AttributeError:
- # Older cryptography versions don't have signature_algorithm_oid yet
- dotted = crypto_utils._obj2txt(
- self.crl._backend._lib,
- self.crl._backend._ffi,
- self.crl._x509_crl.sig_alg.algorithm
- )
- oid = x509.oid.ObjectIdentifier(dotted)
- result['digest'] = crypto_utils.cryptography_oid_to_name(oid)
- issuer = []
- for attribute in self.crl.issuer:
- issuer.append([crypto_utils.cryptography_oid_to_name(attribute.oid), attribute.value])
- result['issuer_ordered'] = issuer
- result['issuer'] = {}
- for k, v in issuer:
- result['issuer'][k] = v
- result['revoked_certificates'] = []
- for cert in self.crl:
- entry = crypto_utils.cryptography_decode_revoked_certificate(cert)
- result['revoked_certificates'].append(self._dump_revoked(entry))
-
- return result
-
- def generate(self):
- # Empty method because crypto_utils.OpenSSLObject wants this
- pass
-
- def dump(self):
- # Empty method because crypto_utils.OpenSSLObject wants this
- pass
-
-
-def main():
- module = AnsibleModule(
- argument_spec=dict(
- path=dict(type='path'),
- content=dict(type='str'),
- ),
- required_one_of=(
- ['path', 'content'],
- ),
- mutually_exclusive=(
- ['path', 'content'],
- ),
- supports_check_mode=True,
- )
-
- if not CRYPTOGRAPHY_FOUND:
- module.fail_json(msg=missing_required_lib('cryptography >= {0}'.format(MINIMAL_CRYPTOGRAPHY_VERSION)),
- exception=CRYPTOGRAPHY_IMP_ERR)
-
- try:
- crl = CRLInfo(module)
- result = crl.get_info()
- module.exit_json(**result)
- except crypto_utils.OpenSSLObjectError as e:
- module.fail_json(msg=to_native(e))
-
-
-if __name__ == "__main__":
- main()
diff --git a/lib/ansible/plugins/doc_fragments/acme.py b/lib/ansible/plugins/doc_fragments/acme.py
deleted file mode 100644
index 8802a7499b..0000000000
--- a/lib/ansible/plugins/doc_fragments/acme.py
+++ /dev/null
@@ -1,118 +0,0 @@
-# -*- coding: utf-8 -*-
-
-# Copyright: (c) 2016 Michael Gruener <michael.gruener@chaosmoon.net>
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-
-class ModuleDocFragment(object):
-
- # Standard files documentation fragment
- DOCUMENTATION = r'''
-notes:
- - "If a new enough version of the C(cryptography) library
- is available (see Requirements for details), it will be used
- instead of the C(openssl) binary. This can be explicitly disabled
- or enabled with the C(select_crypto_backend) option. Note that using
- the C(openssl) binary will be slower and less secure, as private key
- contents always have to be stored on disk (see
- C(account_key_content))."
- - "Although the defaults are chosen so that the module can be used with
- the L(Let's Encrypt,https://letsencrypt.org/) CA, the module can in
- principle be used with any CA providing an ACME endpoint, such as
- L(Buypass Go SSL,https://www.buypass.com/ssl/products/acme)."
-requirements:
- - python >= 2.6
- - either openssl or L(cryptography,https://cryptography.io/) >= 1.5
-options:
- account_key_src:
- description:
- - "Path to a file containing the ACME account RSA or Elliptic Curve
- key."
- - "RSA keys can be created with C(openssl genrsa ...). Elliptic curve keys can
- be created with C(openssl ecparam -genkey ...). Any other tool creating
- private keys in PEM format can be used as well."
- - "Mutually exclusive with C(account_key_content)."
- - "Required if C(account_key_content) is not used."
- type: path
- aliases: [ account_key ]
- account_key_content:
- description:
- - "Content of the ACME account RSA or Elliptic Curve key."
- - "Mutually exclusive with C(account_key_src)."
- - "Required if C(account_key_src) is not used."
- - "*Warning:* the content will be written into a temporary file, which will
- be deleted by Ansible when the module completes. Since this is an
- important private key — it can be used to change the account key,
- or to revoke your certificates without knowing their private keys
- —, this might not be acceptable."
- - "In case C(cryptography) is used, the content is not written into a
- temporary file. It can still happen that it is written to disk by
- Ansible in the process of moving the module with its argument to
- the node where it is executed."
- type: str
- version_added: "2.5"
- account_uri:
- description:
- - "If specified, assumes that the account URI is as given. If the
- account key does not match this account, or an account with this
- URI does not exist, the module fails."
- type: str
- version_added: "2.7"
- acme_version:
- description:
- - "The ACME version of the endpoint."
- - "Must be 1 for the classic Let's Encrypt and Buypass ACME endpoints,
- or 2 for standardized ACME v2 endpoints."
- - "The default value is 1. Note that in Ansible 2.14, this option *will
- be required* and will no longer have a default."
- - "Please also note that we will deprecate ACME v1 support eventually."
- type: int
- choices: [ 1, 2 ]
- version_added: "2.5"
- acme_directory:
- description:
- - "The ACME directory to use. This is the entry point URL to access
- CA server API."
- - "For safety reasons the default is set to the Let's Encrypt staging
- server (for the ACME v1 protocol). This will create technically correct,
- but untrusted certificates."
- - "The default value is U(https://acme-staging.api.letsencrypt.org/directory).
- Note that in Ansible 2.14, this option *will be required* and will no longer
- have a default."
- - "For Let's Encrypt, all staging endpoints can be found here:
- U(https://letsencrypt.org/docs/staging-environment/). For Buypass, all
- endpoints can be found here:
- U(https://community.buypass.com/t/63d4ay/buypass-go-ssl-endpoints)"
- - "For Let's Encrypt, the production directory URL for ACME v1 is
- U(https://acme-v01.api.letsencrypt.org/directory), and the production
- directory URL for ACME v2 is U(https://acme-v02.api.letsencrypt.org/directory)."
- - "For Buypass, the production directory URL for ACME v2 and v1 is
- U(https://api.buypass.com/acme/directory)."
- - "*Warning:* So far, the module has only been tested against Let's Encrypt
- (staging and production), Buypass (staging and production), and
- L(Pebble testing server,https://github.com/letsencrypt/Pebble)."
- type: str
- validate_certs:
- description:
- - Whether calls to the ACME directory will validate TLS certificates.
- - "*Warning:* Should *only ever* be set to C(no) for testing purposes,
- for example when testing against a local Pebble server."
- type: bool
- default: yes
- version_added: "2.5"
- select_crypto_backend:
- description:
- - Determines which crypto backend to use.
- - The default choice is C(auto), which tries to use C(cryptography) if available, and falls back to
- C(openssl).
- - If set to C(openssl), will try to use the C(openssl) binary.
- - If set to C(cryptography), will try to use the
- L(cryptography,https://cryptography.io/) library.
- type: str
- default: auto
- choices: [ auto, cryptography, openssl ]
- version_added: "2.7"
-'''
diff --git a/lib/ansible/plugins/doc_fragments/ecs_credential.py b/lib/ansible/plugins/doc_fragments/ecs_credential.py
deleted file mode 100644
index 5f20fc7fd5..0000000000
--- a/lib/ansible/plugins/doc_fragments/ecs_credential.py
+++ /dev/null
@@ -1,43 +0,0 @@
-# -*- coding: utf-8 -*-
-
-# Copyright (c), Entrust Datacard Corporation, 2019
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
-
-
-class ModuleDocFragment(object):
-
- # Plugin options for Entrust Certificate Services (ECS) credentials
- DOCUMENTATION = r'''
-options:
- entrust_api_user:
- description:
- - The username for authentication to the Entrust Certificate Services (ECS) API.
- type: str
- required: true
- entrust_api_key:
- description:
- - The key (password) for authentication to the Entrust Certificate Services (ECS) API.
- type: str
- required: true
- entrust_api_client_cert_path:
- description:
- - The path to the client certificate used to authenticate to the Entrust Certificate Services (ECS) API.
- type: path
- required: true
- entrust_api_client_cert_key_path:
- description:
- - The path to the key for the client certificate used to authenticate to the Entrust Certificate Services (ECS) API.
- type: path
- required: true
- entrust_api_specification_path:
- description:
- - The path to the specification file defining the Entrust Certificate Services (ECS) API configuration.
- - You can use this to keep a local copy of the specification to avoid downloading it every time the module is used.
- type: path
- default: https://cloud.entrust.net/EntrustCloud/documentation/cms-api-2.1.0.yaml
-requirements:
- - "PyYAML >= 3.11"
-'''
diff --git a/test/integration/targets/acme_account/aliases b/test/integration/targets/acme_account/aliases
deleted file mode 100644
index d793633030..0000000000
--- a/test/integration/targets/acme_account/aliases
+++ /dev/null
@@ -1,2 +0,0 @@
-shippable/cloud/group1
-cloud/acme
diff --git a/test/integration/targets/acme_account/meta/main.yml b/test/integration/targets/acme_account/meta/main.yml
deleted file mode 100644
index 81d1e7e77a..0000000000
--- a/test/integration/targets/acme_account/meta/main.yml
+++ /dev/null
@@ -1,2 +0,0 @@
-dependencies:
- - setup_acme
diff --git a/test/integration/targets/acme_account/tasks/impl.yml b/test/integration/targets/acme_account/tasks/impl.yml
deleted file mode 100644
index 3cd10c479e..0000000000
--- a/test/integration/targets/acme_account/tasks/impl.yml
+++ /dev/null
@@ -1,244 +0,0 @@
-- name: Generate account key
- command: openssl ecparam -name prime256v1 -genkey -out {{ output_dir }}/accountkey.pem
-
-- name: Parse account key (to ease debugging some test failures)
- command: openssl ec -in {{ output_dir }}/accountkey.pem -noout -text
-
-- name: Do not try to create account
- acme_account:
- select_crypto_backend: "{{ select_crypto_backend }}"
- account_key_src: "{{ output_dir }}/accountkey.pem"
- acme_version: 2
- acme_directory: https://{{ acme_host }}:14000/dir
- validate_certs: no
- state: present
- allow_creation: no
- ignore_errors: yes
- register: account_not_created
-
-- name: Create it now (check mode, diff)
- acme_account:
- select_crypto_backend: "{{ select_crypto_backend }}"
- account_key_src: "{{ output_dir }}/accountkey.pem"
- acme_version: 2
- acme_directory: https://{{ acme_host }}:14000/dir
- validate_certs: no
- state: present
- allow_creation: yes
- terms_agreed: yes
- contact:
- - mailto:example@example.org
- check_mode: yes
- diff: yes
- register: account_created_check
-
-- name: Create it now
- acme_account:
- select_crypto_backend: "{{ select_crypto_backend }}"
- account_key_src: "{{ output_dir }}/accountkey.pem"
- acme_version: 2
- acme_directory: https://{{ acme_host }}:14000/dir
- validate_certs: no
- state: present
- allow_creation: yes
- terms_agreed: yes
- contact:
- - mailto:example@example.org
- register: account_created
-
-- name: Create it now (idempotent)
- acme_account:
- select_crypto_backend: "{{ select_crypto_backend }}"
- account_key_src: "{{ output_dir }}/accountkey.pem"
- acme_version: 2
- acme_directory: https://{{ acme_host }}:14000/dir
- validate_certs: no
- state: present
- allow_creation: yes
- terms_agreed: yes
- contact:
- - mailto:example@example.org
- register: account_created_idempotent
-
-- name: Change email address (check mode, diff)
- acme_account:
- select_crypto_backend: "{{ select_crypto_backend }}"
- account_key_content: "{{ lookup('file', output_dir ~ '/accountkey.pem') }}"
- acme_version: 2
- acme_directory: https://{{ acme_host }}:14000/dir
- validate_certs: no
- state: present
- # allow_creation: no
- contact:
- - mailto:example@example.com
- check_mode: yes
- diff: yes
- register: account_modified_check
-
-- name: Change email address
- acme_account:
- select_crypto_backend: "{{ select_crypto_backend }}"
- account_key_content: "{{ lookup('file', output_dir ~ '/accountkey.pem') }}"
- acme_version: 2
- acme_directory: https://{{ acme_host }}:14000/dir
- validate_certs: no
- state: present
- # allow_creation: no
- contact:
- - mailto:example@example.com
- register: account_modified
-
-- name: Change email address (idempotent)
- acme_account:
- select_crypto_backend: "{{ select_crypto_backend }}"
- account_key_src: "{{ output_dir }}/accountkey.pem"
- account_uri: "{{ account_created.account_uri }}"
- acme_version: 2
- acme_directory: https://{{ acme_host }}:14000/dir
- validate_certs: no
- state: present
- # allow_creation: no
- contact:
- - mailto:example@example.com
- register: account_modified_idempotent
-
-- name: Cannot access account with wrong URI
- acme_account:
- select_crypto_backend: "{{ select_crypto_backend }}"
- account_key_src: "{{ output_dir }}/accountkey.pem"
- account_uri: "{{ account_created.account_uri ~ '12345thisdoesnotexist' }}"
- acme_version: 2
- acme_directory: https://{{ acme_host }}:14000/dir
- validate_certs: no
- state: present
- contact: []
- ignore_errors: yes
- register: account_modified_wrong_uri
-
-- name: Clear contact email addresses (check mode, diff)
- acme_account:
- select_crypto_backend: "{{ select_crypto_backend }}"
- account_key_src: "{{ output_dir }}/accountkey.pem"
- acme_version: 2
- acme_directory: https://{{ acme_host }}:14000/dir
- validate_certs: no
- state: present
- # allow_creation: no
- contact: []
- check_mode: yes
- diff: yes
- register: account_modified_2_check
-
-- name: Clear contact email addresses
- acme_account:
- select_crypto_backend: "{{ select_crypto_backend }}"
- account_key_src: "{{ output_dir }}/accountkey.pem"
- acme_version: 2
- acme_directory: https://{{ acme_host }}:14000/dir
- validate_certs: no
- state: present
- # allow_creation: no
- contact: []
- register: account_modified_2
-
-- name: Clear contact email addresses (idempotent)
- acme_account:
- select_crypto_backend: "{{ select_crypto_backend }}"
- account_key_src: "{{ output_dir }}/accountkey.pem"
- acme_version: 2
- acme_directory: https://{{ acme_host }}:14000/dir
- validate_certs: no
- state: present
- # allow_creation: no
- contact: []
- register: account_modified_2_idempotent
-
-- name: Generate new account key
- command: openssl ecparam -name secp384r1 -genkey -out {{ output_dir }}/accountkey2.pem
-
-- name: Parse account key (to ease debugging some test failures)
- command: openssl ec -in {{ output_dir }}/accountkey2.pem -noout -text
-
-- name: Change account key (check mode, diff)
- acme_account:
- select_crypto_backend: "{{ select_crypto_backend }}"
- account_key_src: "{{ output_dir }}/accountkey.pem"
- acme_version: 2
- acme_directory: https://{{ acme_host }}:14000/dir
- validate_certs: no
- new_account_key_src: "{{ output_dir }}/accountkey2.pem"
- state: changed_key
- contact:
- - mailto:example@example.com
- check_mode: yes
- diff: yes
- register: account_change_key_check
-
-- name: Change account key
- acme_account:
- select_crypto_backend: "{{ select_crypto_backend }}"
- account_key_src: "{{ output_dir }}/accountkey.pem"
- acme_version: 2
- acme_directory: https://{{ acme_host }}:14000/dir
- validate_certs: no
- new_account_key_src: "{{ output_dir }}/accountkey2.pem"
- state: changed_key
- contact:
- - mailto:example@example.com
- register: account_change_key
-
-- name: Deactivate account (check mode, diff)
- acme_account:
- select_crypto_backend: "{{ select_crypto_backend }}"
- account_key_src: "{{ output_dir }}/accountkey2.pem"
- acme_version: 2
- acme_directory: https://{{ acme_host }}:14000/dir
- validate_certs: no
- state: absent
- check_mode: yes
- diff: yes
- register: account_deactivate_check
-
-- name: Deactivate account
- acme_account:
- select_crypto_backend: "{{ select_crypto_backend }}"
- account_key_src: "{{ output_dir }}/accountkey2.pem"
- acme_version: 2
- acme_directory: https://{{ acme_host }}:14000/dir
- validate_certs: no
- state: absent
- register: account_deactivate
-
-- name: Deactivate account (idempotent)
- acme_account:
- select_crypto_backend: "{{ select_crypto_backend }}"
- account_key_src: "{{ output_dir }}/accountkey2.pem"
- acme_version: 2
- acme_directory: https://{{ acme_host }}:14000/dir
- validate_certs: no
- state: absent
- register: account_deactivate_idempotent
-
-- name: Do not try to create account II
- acme_account:
- select_crypto_backend: "{{ select_crypto_backend }}"
- account_key_src: "{{ output_dir }}/accountkey2.pem"
- acme_version: 2
- acme_directory: https://{{ acme_host }}:14000/dir
- validate_certs: no
- state: present
- allow_creation: no
- ignore_errors: yes
- register: account_not_created_2
-
-- name: Do not try to create account III
- acme_account:
- select_crypto_backend: "{{ select_crypto_backend }}"
- account_key_src: "{{ output_dir }}/accountkey.pem"
- acme_version: 2
- acme_directory: https://{{ acme_host }}:14000/dir
- validate_certs: no
- state: present
- allow_creation: no
- ignore_errors: yes
- register: account_not_created_3
diff --git a/test/integration/targets/acme_account/tasks/main.yml b/test/integration/targets/acme_account/tasks/main.yml
deleted file mode 100644
index e46c6dc4d0..0000000000
--- a/test/integration/targets/acme_account/tasks/main.yml
+++ /dev/null
@@ -1,31 +0,0 @@
----
-- block:
- - name: Running tests with OpenSSL backend
- include_tasks: impl.yml
- vars:
- select_crypto_backend: openssl
-
- - import_tasks: ../tests/validate.yml
-
- # Old 0.9.8 versions have insufficient CLI support for signing with EC keys
- when: openssl_version.stdout is version('1.0.0', '>=')
-
-- name: Remove output directory
- file:
- path: "{{ output_dir }}"
- state: absent
-
-- name: Re-create output directory
- file:
- path: "{{ output_dir }}"
- state: directory
-
-- block:
- - name: Running tests with cryptography backend
- include_tasks: impl.yml
- vars:
- select_crypto_backend: cryptography
-
- - import_tasks: ../tests/validate.yml
-
- when: cryptography_version.stdout is version('1.5', '>=')
diff --git a/test/integration/targets/acme_account/tests/validate.yml b/test/integration/targets/acme_account/tests/validate.yml
deleted file mode 100644
index 3bc1d7aa73..0000000000
--- a/test/integration/targets/acme_account/tests/validate.yml
+++ /dev/null
@@ -1,129 +0,0 @@
----
-- name: Validate that account wasn't created in the first step
- assert:
- that:
- - account_not_created is failed
- - account_not_created.msg == 'Account does not exist or is deactivated.'
-
-- name: Validate that account was created in the second step (check mode)
- assert:
- that:
- - account_created_check is changed
- - account_created_check.account_uri is none
- - "'diff' in account_created_check"
- - "account_created_check.diff.before == {}"
- - "'after' in account_created_check.diff"
- - account_created_check.diff.after.contact | length == 1
- - account_created_check.diff.after.contact[0] == 'mailto:example@example.org'
-
-- name: Validate that account was created in the second step
- assert:
- that:
- - account_created is changed
- - account_created.account_uri is not none
-
-- name: Validate that account was created in the second step (idempotency)
- assert:
- that:
- - account_created_idempotent is not changed
- - account_created_idempotent.account_uri is not none
-
-- name: Validate that email address was changed (check mode)
- assert:
- that:
- - account_modified_check is changed
- - account_modified_check.account_uri is not none
- - "'diff' in account_modified_check"
- - account_modified_check.diff.before.contact | length == 1
- - account_modified_check.diff.before.contact[0] == 'mailto:example@example.org'
- - account_modified_check.diff.after.contact | length == 1
- - account_modified_check.diff.after.contact[0] == 'mailto:example@example.com'
-
-- name: Validate that email address was changed
- assert:
- that:
- - account_modified is changed
- - account_modified.account_uri is not none
-
-- name: Validate that email address was not changed a second time (idempotency)
- assert:
- that:
- - account_modified_idempotent is not changed
- - account_modified_idempotent.account_uri is not none
-
-- name: Make sure that with the wrong account URI, the account cannot be changed
- assert:
- that:
- - account_modified_wrong_uri is failed
-
-- name: Validate that email address was cleared (check mode)
- assert:
- that:
- - account_modified_2_check is changed
- - account_modified_2_check.account_uri is not none
- - "'diff' in account_modified_2_check"
- - account_modified_2_check.diff.before.contact | length == 1
- - account_modified_2_check.diff.before.contact[0] == 'mailto:example@example.com'
- - account_modified_2_check.diff.after.contact | length == 0
-
-- name: Validate that email address was cleared
- assert:
- that:
- - account_modified_2 is changed
- - account_modified_2.account_uri is not none
-
-- name: Validate that email address was not cleared a second time (idempotency)
- assert:
- that:
- - account_modified_2_idempotent is not changed
- - account_modified_2_idempotent.account_uri is not none
-
-- name: Validate that the account key was changed (check mode)
- assert:
- that:
- - account_change_key_check is changed
- - account_change_key_check.account_uri is not none
- - "'diff' in account_change_key_check"
- - account_change_key_check.diff.before.public_account_key != account_change_key_check.diff.after.public_account_key
-
-- name: Validate that the account key was changed
- assert:
- that:
- - account_change_key is changed
- - account_change_key.account_uri is not none
-
-- name: Validate that the account was deactivated (check mode)
- assert:
- that:
- - account_deactivate_check is changed
- - account_deactivate_check.account_uri is not none
- - "'diff' in account_deactivate_check"
- - "account_deactivate_check.diff.before != {}"
- - "account_deactivate_check.diff.after == {}"
-
-- name: Validate that the account was deactivated
- assert:
- that:
- - account_deactivate is changed
- - account_deactivate.account_uri is not none
-
-- name: Validate that the account was really deactivated (idempotency)
- assert:
- that:
- - account_deactivate_idempotent is not changed
- # The next condition should be true for all conforming ACME servers.
- # In case it is not true, it could be both an error in acme_account
- # and in the ACME server.
- - account_deactivate_idempotent.account_uri is none
-
-- name: Validate that the account is gone (new account key)
- assert:
- that:
- - account_not_created_2 is failed
- - account_not_created_2.msg == 'Account does not exist or is deactivated.'
-
-- name: Validate that the account is gone (old account key)
- assert:
- that:
- - account_not_created_3 is failed
- - account_not_created_3.msg == 'Account does not exist or is deactivated.'
diff --git a/test/integration/targets/acme_account_info/aliases b/test/integration/targets/acme_account_info/aliases
deleted file mode 100644
index d793633030..0000000000
--- a/test/integration/targets/acme_account_info/aliases
+++ /dev/null
@@ -1,2 +0,0 @@
-shippable/cloud/group1
-cloud/acme
diff --git a/test/integration/targets/acme_account_info/meta/main.yml b/test/integration/targets/acme_account_info/meta/main.yml
deleted file mode 100644
index 81d1e7e77a..0000000000
--- a/test/integration/targets/acme_account_info/meta/main.yml
+++ /dev/null
@@ -1,2 +0,0 @@
-dependencies:
- - setup_acme
diff --git a/test/integration/targets/acme_account_info/tasks/impl.yml b/test/integration/targets/acme_account_info/tasks/impl.yml
deleted file mode 100644
index 3bce6bbaf0..0000000000
--- a/test/integration/targets/acme_account_info/tasks/impl.yml
+++ /dev/null
@@ -1,82 +0,0 @@
----
-- name: Generate account key
- command: openssl ecparam -name prime256v1 -genkey -out {{ output_dir }}/accountkey.pem
-
-- name: Generate second account key
- command: openssl ecparam -name prime256v1 -genkey -out {{ output_dir }}/accountkey2.pem
-
-- name: Parse account key (to ease debugging some test failures)
- command: openssl ec -in {{ output_dir }}/accountkey.pem -noout -text
-
-- name: Check that account does not exist
- acme_account_info:
- select_crypto_backend: "{{ select_crypto_backend }}"
- account_key_src: "{{ output_dir }}/accountkey.pem"
- acme_version: 2
- acme_directory: https://{{ acme_host }}:14000/dir
- validate_certs: no
- register: account_not_created
-
-- name: Create it now
- acme_account:
- select_crypto_backend: "{{ select_crypto_backend }}"
- account_key_src: "{{ output_dir }}/accountkey.pem"
- acme_version: 2
- acme_directory: https://{{ acme_host }}:14000/dir
- validate_certs: no
- state: present
- allow_creation: yes
- terms_agreed: yes
- contact:
- - mailto:example@example.org
-
-- name: Check that account exists
- acme_account_info:
- select_crypto_backend: "{{ select_crypto_backend }}"
- account_key_src: "{{ output_dir }}/accountkey.pem"
- acme_version: 2
- acme_directory: https://{{ acme_host }}:14000/dir
- validate_certs: no
- register: account_created
-
-- name: Clear email address
- acme_account:
- select_crypto_backend: "{{ select_crypto_backend }}"
- account_key_content: "{{ lookup('file', output_dir ~ '/accountkey.pem') }}"
- acme_version: 2
- acme_directory: https://{{ acme_host }}:14000/dir
- validate_certs: no
- state: present
- allow_creation: no
- contact: []
-
-- name: Check that account was modified
- acme_account_info:
- select_crypto_backend: "{{ select_crypto_backend }}"
- account_key_src: "{{ output_dir }}/accountkey.pem"
- acme_version: 2
- acme_directory: https://{{ acme_host }}:14000/dir
- validate_certs: no
- account_uri: "{{ account_created.account_uri }}"
- register: account_modified
-
-- name: Check with wrong account URI
- acme_account_info:
- select_crypto_backend: "{{ select_crypto_backend }}"
- account_key_src: "{{ output_dir }}/accountkey.pem"
- acme_version: 2
- acme_directory: https://{{ acme_host }}:14000/dir
- validate_certs: no
- account_uri: "{{ account_created.account_uri }}test1234doesnotexists"
- register: account_not_exist
-
-- name: Check with wrong account key
- acme_account_info:
- select_crypto_backend: "{{ select_crypto_backend }}"
- account_key_src: "{{ output_dir }}/accountkey2.pem"
- acme_version: 2
- acme_directory: https://{{ acme_host }}:14000/dir
- validate_certs: no
- account_uri: "{{ account_created.account_uri }}"
- ignore_errors: yes
- register: account_wrong_key
diff --git a/test/integration/targets/acme_account_info/tasks/main.yml b/test/integration/targets/acme_account_info/tasks/main.yml
deleted file mode 100644
index e46c6dc4d0..0000000000
--- a/test/integration/targets/acme_account_info/tasks/main.yml
+++ /dev/null
@@ -1,31 +0,0 @@
----
-- block:
- - name: Running tests with OpenSSL backend
- include_tasks: impl.yml
- vars:
- select_crypto_backend: openssl
-
- - import_tasks: ../tests/validate.yml
-
- # Old 0.9.8 versions have insufficient CLI support for signing with EC keys
- when: openssl_version.stdout is version('1.0.0', '>=')
-
-- name: Remove output directory
- file:
- path: "{{ output_dir }}"
- state: absent
-
-- name: Re-create output directory
- file:
- path: "{{ output_dir }}"
- state: directory
-
-- block:
- - name: Running tests with cryptography backend
- include_tasks: impl.yml
- vars:
- select_crypto_backend: cryptography
-
- - import_tasks: ../tests/validate.yml
-
- when: cryptography_version.stdout is version('1.5', '>=')
diff --git a/test/integration/targets/acme_account_info/tests/validate.yml b/test/integration/targets/acme_account_info/tests/validate.yml
deleted file mode 100644
index 5863a8c361..0000000000
--- a/test/integration/targets/acme_account_info/tests/validate.yml
+++ /dev/null
@@ -1,40 +0,0 @@
----
-- name: Validate that account wasn't there
- assert:
- that:
- - not account_not_created.exists
- - account_not_created.account_uri is none
- - "'account' not in account_not_created"
-
-- name: Validate that account was created
- assert:
- that:
- - account_created.exists
- - account_created.account_uri is not none
- - "'account' in account_created"
- - "'contact' in account_created.account"
- - "'public_account_key' in account_created.account"
- - account_created.account.contact | length == 1
- - "account_created.account.contact[0] == 'mailto:example@example.org'"
-
-- name: Validate that account email was removed
- assert:
- that:
- - account_modified.exists
- - account_modified.account_uri is not none
- - "'account' in account_modified"
- - "'contact' in account_modified.account"
- - "'public_account_key' in account_modified.account"
- - account_modified.account.contact | length == 0
-
-- name: Validate that account does not exist with wrong account URI
- assert:
- that:
- - not account_not_exist.exists
- - account_not_exist.account_uri is none
- - "'account' not in account_not_exist"
-
-- name: Validate that account cannot be accessed with wrong key
- assert:
- that:
- - account_wrong_key is failed
diff --git a/test/integration/targets/acme_certificate/aliases b/test/integration/targets/acme_certificate/aliases
deleted file mode 100644
index d793633030..0000000000
--- a/test/integration/targets/acme_certificate/aliases
+++ /dev/null
@@ -1,2 +0,0 @@
-shippable/cloud/group1
-cloud/acme
diff --git a/test/integration/targets/acme_certificate/meta/main.yml b/test/integration/targets/acme_certificate/meta/main.yml
deleted file mode 100644
index 81d1e7e77a..0000000000
--- a/test/integration/targets/acme_certificate/meta/main.yml
+++ /dev/null
@@ -1,2 +0,0 @@
-dependencies:
- - setup_acme
diff --git a/test/integration/targets/acme_certificate/tasks/impl.yml b/test/integration/targets/acme_certificate/tasks/impl.yml
deleted file mode 100644
index 04f02710fc..0000000000
--- a/test/integration/targets/acme_certificate/tasks/impl.yml
+++ /dev/null
@@ -1,451 +0,0 @@
----
-## SET UP ACCOUNT KEYS ########################################################################
-- name: Create ECC256 account key
- command: openssl ecparam -name prime256v1 -genkey -out {{ output_dir }}/account-ec256.pem
-- name: Create ECC384 account key
- command: openssl ecparam -name secp384r1 -genkey -out {{ output_dir }}/account-ec384.pem
-- name: Create RSA-2048 account key
- command: openssl genrsa -out {{ output_dir }}/account-rsa2048.pem 2048
-## SET UP ACCOUNTS ############################################################################
-- name: Make sure ECC256 account hasn't been created yet
- acme_account:
- select_crypto_backend: "{{ select_crypto_backend }}"
- acme_version: 2
- acme_directory: https://{{ acme_host }}:14000/dir
- validate_certs: no
- account_key_src: "{{ output_dir }}/account-ec256.pem"
- state: absent
-- name: Create ECC384 account
- acme_account:
- select_crypto_backend: "{{ select_crypto_backend }}"
- acme_version: 2
- acme_directory: https://{{ acme_host }}:14000/dir
- validate_certs: no
- account_key_content: "{{ lookup('file', output_dir ~ '/account-ec384.pem') }}"
- state: present
- allow_creation: yes
- terms_agreed: yes
- contact:
- - mailto:example@example.org
- - mailto:example@example.com
-- name: Create RSA-2048 account
- acme_account:
- select_crypto_backend: "{{ select_crypto_backend }}"
- acme_version: 2
- acme_directory: https://{{ acme_host }}:14000/dir
- validate_certs: no
- account_key_src: "{{ output_dir }}/account-rsa2048.pem"
- state: present
- allow_creation: yes
- terms_agreed: yes
- contact: []
-## OBTAIN CERTIFICATES ########################################################################
-- name: Obtain cert 1
- include_tasks: obtain-cert.yml
- vars:
- certgen_title: Certificate 1
- certificate_name: cert-1
- key_type: rsa
- rsa_bits: 2048
- subject_alt_name: "DNS:example.com"
- subject_alt_name_critical: no
- account_key: account-ec256
- challenge: http-01
- modify_account: yes
- deactivate_authzs: no
- force: no
- remaining_days: 10
- terms_agreed: yes
- account_email: "example@example.org"
- retrieve_all_alternates: yes
- acme_expected_root_number: 1
- select_chain:
- - test_certificates: last
- issuer: "{{ acme_roots[1].subject }}"
-- name: Store obtain results for cert 1
- set_fact:
- cert_1_obtain_results: "{{ certificate_obtain_result }}"
- cert_1_alternate: "{{ 1 if select_crypto_backend == 'cryptography' else 0 }}"
-- name: Obtain cert 2
- include_tasks: obtain-cert.yml
- vars:
- certgen_title: Certificate 2
- certificate_name: cert-2
- key_type: ec256
- subject_alt_name: "DNS:*.example.com,DNS:example.com"
- subject_alt_name_critical: yes
- account_key: account-ec384
- challenge: dns-01
- modify_account: no
- deactivate_authzs: yes
- force: no
- remaining_days: 10
- terms_agreed: no
- account_email: ""
- acme_expected_root_number: 0
- retrieve_all_alternates: yes
- select_chain:
- # All intermediates have the same subject, so always the first
- # chain will be found, and we need a second condition to make sure
- # that the first condition actually works. (The second condition
- # has been tested above.)
- - test_certificates: all
- subject: "{{ acme_intermediates[0].subject }}"
- - test_certificates: all
- issuer: "{{ acme_roots[2].subject }}"
-- name: Store obtain results for cert 2
- set_fact:
- cert_2_obtain_results: "{{ certificate_obtain_result }}"
- cert_2_alternate: "{{ 0 if select_crypto_backend == 'cryptography' else 0 }}"
-- name: Obtain cert 3
- include_tasks: obtain-cert.yml
- vars:
- certgen_title: Certificate 3
- certificate_name: cert-3
- key_type: ec384
- subject_alt_name: "DNS:*.example.com,DNS:example.org,DNS:t1.example.com"
- subject_alt_name_critical: no
- account_key_content: "{{ lookup('file', output_dir ~ '/account-rsa2048.pem') }}"
- challenge: dns-01
- modify_account: no
- deactivate_authzs: no
- force: no
- remaining_days: 10
- terms_agreed: no
- account_email: ""
- acme_expected_root_number: 0
- retrieve_all_alternates: yes
- select_chain:
- - test_certificates: last
- subject: "{{ acme_roots[1].subject }}"
-- name: Store obtain results for cert 3
- set_fact:
- cert_3_obtain_results: "{{ certificate_obtain_result }}"
- cert_3_alternate: "{{ 0 if select_crypto_backend == 'cryptography' else 0 }}"
-- name: Obtain cert 4
- include_tasks: obtain-cert.yml
- vars:
- certgen_title: Certificate 4
- certificate_name: cert-4
- key_type: rsa
- rsa_bits: 2048
- subject_alt_name: "DNS:example.com,DNS:t1.example.com,DNS:test.t2.example.com,DNS:example.org,DNS:test.example.org"
- subject_alt_name_critical: no
- account_key: account-rsa2048
- challenge: http-01
- modify_account: no
- deactivate_authzs: yes
- force: yes
- remaining_days: 10
- terms_agreed: no
- account_email: ""
- acme_expected_root_number: 2
- select_chain:
- - test_certificates: last
- issuer: "{{ acme_roots[2].subject }}"
- - test_certificates: last
- issuer: "{{ acme_roots[1].subject }}"
-- name: Store obtain results for cert 4
- set_fact:
- cert_4_obtain_results: "{{ certificate_obtain_result }}"
- cert_4_alternate: "{{ 2 if select_crypto_backend == 'cryptography' else 0 }}"
-- name: Obtain cert 5
- include_tasks: obtain-cert.yml
- vars:
- certgen_title: Certificate 5, Iteration 1/4
- certificate_name: cert-5
- key_type: ec521
- subject_alt_name: "DNS:t2.example.com"
- subject_alt_name_critical: no
- account_key: account-ec384
- challenge: http-01
- modify_account: no
- deactivate_authzs: yes
- force: yes
- remaining_days: 10
- terms_agreed: no
- account_email: ""
-- name: Store obtain results for cert 5a
- set_fact:
- cert_5a_obtain_results: "{{ certificate_obtain_result }}"
- cert_5_alternate: "{{ 0 if select_crypto_backend == 'cryptography' else 0 }}"
-- name: Obtain cert 5 (should not, since already there and valid for more than 10 days)
- include_tasks: obtain-cert.yml
- vars:
- certgen_title: Certificate 5, Iteration 2/4
- certificate_name: cert-5
- key_type: ec521
- subject_alt_name: "DNS:t2.example.com"
- subject_alt_name_critical: no
- account_key: account-ec384
- challenge: http-01
- modify_account: no
- deactivate_authzs: yes
- force: no
- remaining_days: 10
- terms_agreed: no
- account_email: ""
-- name: Store obtain results for cert 5b
- set_fact:
- cert_5_recreate_1: "{{ challenge_data is changed }}"
-- name: Obtain cert 5 (should again by less days)
- include_tasks: obtain-cert.yml
- vars:
- certgen_title: Certificate 5, Iteration 3/4
- certificate_name: cert-5
- key_type: ec521
- subject_alt_name: "DNS:t2.example.com"
- subject_alt_name_critical: no
- account_key: account-ec384
- challenge: http-01
- modify_account: no
- deactivate_authzs: yes
- force: yes
- remaining_days: 1000
- terms_agreed: no
- account_email: ""
-- name: Store obtain results for cert 5c
- set_fact:
- cert_5_recreate_2: "{{ challenge_data is changed }}"
- cert_5c_obtain_results: "{{ certificate_obtain_result }}"
-- name: Obtain cert 5 (should again by force)
- include_tasks: obtain-cert.yml
- vars:
- certgen_title: Certificate 5, Iteration 4/4
- certificate_name: cert-5
- key_type: ec521
- subject_alt_name: "DNS:t2.example.com"
- subject_alt_name_critical: no
- account_key_content: "{{ lookup('file', output_dir ~ '/account-ec384.pem') }}"
- challenge: http-01
- modify_account: no
- deactivate_authzs: yes
- force: yes
- remaining_days: 10
- terms_agreed: no
- account_email: ""
-- name: Store obtain results for cert 5d
- set_fact:
- cert_5_recreate_3: "{{ challenge_data is changed }}"
- cert_5d_obtain_results: "{{ certificate_obtain_result }}"
-- name: Obtain cert 6
- include_tasks: obtain-cert.yml
- vars:
- certgen_title: Certificate 6
- certificate_name: cert-6
- key_type: rsa
- rsa_bits: 2048
- subject_alt_name: "DNS:example.org"
- subject_alt_name_critical: no
- account_key: account-ec256
- challenge: tls-alpn-01
- modify_account: yes
- deactivate_authzs: no
- force: no
- remaining_days: 10
- terms_agreed: yes
- account_email: "example@example.org"
- acme_expected_root_number: 0
- select_chain:
- # All intermediates have the same subject key identifier, so always
- # the first chain will be found, and we need a second condition to
- # make sure that the first condition actually works. (The second
- # condition has been tested above.)
- - test_certificates: last
- subject_key_identifier: "{{ acme_intermediates[0].subject_key_identifier }}"
- - test_certificates: last
- issuer: "{{ acme_roots[1].subject }}"
-- name: Store obtain results for cert 6
- set_fact:
- cert_6_obtain_results: "{{ certificate_obtain_result }}"
- cert_6_alternate: "{{ 0 if select_crypto_backend == 'cryptography' else 0 }}"
-- name: Obtain cert 7
- include_tasks: obtain-cert.yml
- vars:
- certgen_title: Certificate 7
- certificate_name: cert-7
- key_type: rsa
- rsa_bits: 2048
- subject_alt_name:
- - "IP:127.0.0.1"
- # - "IP:::1"
- subject_alt_name_critical: no
- account_key: account-ec256
- challenge: http-01
- modify_account: yes
- deactivate_authzs: no
- force: no
- remaining_days: 10
- terms_agreed: yes
- account_email: "example@example.org"
- acme_expected_root_number: 2
- select_chain:
- - test_certificates: last
- authority_key_identifier: "{{ acme_roots[2].subject_key_identifier }}"
-- name: Store obtain results for cert 7
- set_fact:
- cert_7_obtain_results: "{{ certificate_obtain_result }}"
- cert_7_alternate: "{{ 2 if select_crypto_backend == 'cryptography' else 0 }}"
-- name: Obtain cert 8
- include_tasks: obtain-cert.yml
- vars:
- certgen_title: Certificate 8
- certificate_name: cert-8
- key_type: rsa
- rsa_bits: 2048
- subject_alt_name:
- - "IP:127.0.0.1"
- # IPv4 only since our test validation server doesn't work
- # with IPv6 (thanks to Python's socketserver).
- subject_alt_name_critical: no
- account_key: account-ec256
- challenge: tls-alpn-01
- challenge_alpn_tls: acme_challenge_cert_helper
- modify_account: yes
- deactivate_authzs: no
- force: no
- remaining_days: 10
- terms_agreed: yes
- account_email: "example@example.org"
-- name: Store obtain results for cert 8
- set_fact:
- cert_8_obtain_results: "{{ certificate_obtain_result }}"
- cert_8_alternate: "{{ 0 if select_crypto_backend == 'cryptography' else 0 }}"
-## DISSECT CERTIFICATES #######################################################################
-# Make sure certificates are valid. Root certificate for Pebble equals the chain certificate.
-- name: Verifying cert 1
- command: openssl verify -CAfile "{{ output_dir }}/cert-1-root.pem" -untrusted "{{ output_dir }}/cert-1-chain.pem" "{{ output_dir }}/cert-1.pem"
- ignore_errors: yes
- register: cert_1_valid
-- name: Verifying cert 2
- command: openssl verify -CAfile "{{ output_dir }}/cert-2-root.pem" -untrusted "{{ output_dir }}/cert-2-chain.pem" "{{ output_dir }}/cert-2.pem"
- ignore_errors: yes
- register: cert_2_valid
-- name: Verifying cert 3
- command: openssl verify -CAfile "{{ output_dir }}/cert-3-root.pem" -untrusted "{{ output_dir }}/cert-3-chain.pem" "{{ output_dir }}/cert-3.pem"
- ignore_errors: yes
- register: cert_3_valid
-- name: Verifying cert 4
- command: openssl verify -CAfile "{{ output_dir }}/cert-4-root.pem" -untrusted "{{ output_dir }}/cert-4-chain.pem" "{{ output_dir }}/cert-4.pem"
- ignore_errors: yes
- register: cert_4_valid
-- name: Verifying cert 5
- command: openssl verify -CAfile "{{ output_dir }}/cert-5-root.pem" -untrusted "{{ output_dir }}/cert-5-chain.pem" "{{ output_dir }}/cert-5.pem"
- ignore_errors: yes
- register: cert_5_valid
-- name: Verifying cert 6
- command: openssl verify -CAfile "{{ output_dir }}/cert-6-root.pem" -untrusted "{{ output_dir }}/cert-6-chain.pem" "{{ output_dir }}/cert-6.pem"
- ignore_errors: yes
- register: cert_6_valid
-- name: Verifying cert 7
- command: openssl verify -CAfile "{{ output_dir }}/cert-7-root.pem" -untrusted "{{ output_dir }}/cert-7-chain.pem" "{{ output_dir }}/cert-7.pem"
- ignore_errors: yes
- register: cert_7_valid
-- name: Verifying cert 8
- command: openssl verify -CAfile "{{ output_dir }}/cert-8-root.pem" -untrusted "{{ output_dir }}/cert-8-chain.pem" "{{ output_dir }}/cert-8.pem"
- ignore_errors: yes
- register: cert_8_valid
-# Dump certificate info
-- name: Dumping cert 1
- command: openssl x509 -in "{{ output_dir }}/cert-1.pem" -noout -text
- register: cert_1_text
-- name: Dumping cert 2
- command: openssl x509 -in "{{ output_dir }}/cert-2.pem" -noout -text
- register: cert_2_text
-- name: Dumping cert 3
- command: openssl x509 -in "{{ output_dir }}/cert-3.pem" -noout -text
- register: cert_3_text
-- name: Dumping cert 4
- command: openssl x509 -in "{{ output_dir }}/cert-4.pem" -noout -text
- register: cert_4_text
-- name: Dumping cert 5
- command: openssl x509 -in "{{ output_dir }}/cert-5.pem" -noout -text
- register: cert_5_text
-- name: Dumping cert 6
- command: openssl x509 -in "{{ output_dir }}/cert-6.pem" -noout -text
- register: cert_6_text
-- name: Dumping cert 7
- command: openssl x509 -in "{{ output_dir }}/cert-7.pem" -noout -text
- register: cert_7_text
-- name: Dumping cert 8
- command: openssl x509 -in "{{ output_dir }}/cert-8.pem" -noout -text
- register: cert_8_text
-# Dump certificate info
-- name: Dumping cert 1
- openssl_certificate_info:
- path: "{{ output_dir }}/cert-1.pem"
- register: cert_1_info
-- name: Dumping cert 2
- openssl_certificate_info:
- path: "{{ output_dir }}/cert-2.pem"
- register: cert_2_info
-- name: Dumping cert 3
- openssl_certificate_info:
- path: "{{ output_dir }}/cert-3.pem"
- register: cert_3_info
-- name: Dumping cert 4
- openssl_certificate_info:
- path: "{{ output_dir }}/cert-4.pem"
- register: cert_4_info
-- name: Dumping cert 5
- openssl_certificate_info:
- path: "{{ output_dir }}/cert-5.pem"
- register: cert_5_info
-- name: Dumping cert 6
- openssl_certificate_info:
- path: "{{ output_dir }}/cert-6.pem"
- register: cert_6_info
-- name: Dumping cert 7
- openssl_certificate_info:
- path: "{{ output_dir }}/cert-7.pem"
- register: cert_7_info
-- name: Dumping cert 8
- openssl_certificate_info:
- path: "{{ output_dir }}/cert-8.pem"
- register: cert_8_info
-## GET ACCOUNT ORDERS #########################################################################
-- name: Don't retrieve orders
- acme_account_info:
- select_crypto_backend: "{{ select_crypto_backend }}"
- account_key_src: "{{ output_dir }}/account-ec256.pem"
- acme_version: 2
- acme_directory: https://{{ acme_host }}:14000/dir
- validate_certs: no
- retrieve_orders: ignore
- register: account_orders_not
-- name: Retrieve orders as URL list (1/2)
- acme_account_info:
- select_crypto_backend: "{{ select_crypto_backend }}"
- account_key_src: "{{ output_dir }}/account-ec256.pem"
- acme_version: 2
- acme_directory: https://{{ acme_host }}:14000/dir
- validate_certs: no
- retrieve_orders: url_list
- register: account_orders_urls
-- name: Retrieve orders as URL list (2/2)
- acme_account_info:
- select_crypto_backend: "{{ select_crypto_backend }}"
- account_key_src: "{{ output_dir }}/account-ec384.pem"
- acme_version: 2
- acme_directory: https://{{ acme_host }}:14000/dir
- validate_certs: no
- retrieve_orders: url_list
- register: account_orders_urls2
-- name: Retrieve orders as object list (1/2)
- acme_account_info:
- select_crypto_backend: "{{ select_crypto_backend }}"
- account_key_src: "{{ output_dir }}/account-ec256.pem"
- acme_version: 2
- acme_directory: https://{{ acme_host }}:14000/dir
- validate_certs: no
- retrieve_orders: object_list
- register: account_orders_full
-- name: Retrieve orders as object list (2/2)
- acme_account_info:
- select_crypto_backend: "{{ select_crypto_backend }}"
- account_key_src: "{{ output_dir }}/account-ec384.pem"
- acme_version: 2
- acme_directory: https://{{ acme_host }}:14000/dir
- validate_certs: no
- retrieve_orders: object_list
- register: account_orders_full2
diff --git a/test/integration/targets/acme_certificate/tasks/main.yml b/test/integration/targets/acme_certificate/tasks/main.yml
deleted file mode 100644
index da66f5f3e4..0000000000
--- a/test/integration/targets/acme_certificate/tasks/main.yml
+++ /dev/null
@@ -1,102 +0,0 @@
----
-- block:
- - name: Obtain root and intermediate certificates
- get_url:
- url: "http://{{ acme_host }}:5000/{{ item.0 }}-certificate-for-ca/{{ item.1 }}"
- dest: "{{ output_dir }}/acme-{{ item.0 }}-{{ item.1 }}.pem"
- loop: "{{ query('nested', types, root_numbers) }}"
-
- - name: Analyze root certificates
- openssl_certificate_info:
- path: "{{ output_dir }}/acme-root-{{ item }}.pem"
- loop: "{{ root_numbers }}"
- register: acme_roots
-
- - name: Analyze intermediate certificates
- openssl_certificate_info:
- path: "{{ output_dir }}/acme-intermediate-{{ item }}.pem"
- loop: "{{ root_numbers }}"
- register: acme_intermediates
-
- - set_fact:
- x__: "{{ item | dict2items | selectattr('key', 'in', interesting_keys) | list | items2dict }}"
- y__: "{{ lookup('file', output_dir ~ '/acme-root-' ~ item.item ~ '.pem', rstrip=False) }}"
- loop: "{{ acme_roots.results }}"
- register: acme_roots_tmp
-
- - set_fact:
- x__: "{{ item | dict2items | selectattr('key', 'in', interesting_keys) | list | items2dict }}"
- y__: "{{ lookup('file', output_dir ~ '/acme-intermediate-' ~ item.item ~ '.pem', rstrip=False) }}"
- loop: "{{ acme_intermediates.results }}"
- register: acme_intermediates_tmp
-
- - set_fact:
- acme_roots: "{{ acme_roots_tmp.results | map(attribute='ansible_facts.x__') | list }}"
- acme_root_certs: "{{ acme_roots_tmp.results | map(attribute='ansible_facts.y__') | list }}"
- acme_intermediates: "{{ acme_intermediates_tmp.results | map(attribute='ansible_facts.x__') | list }}"
- acme_intermediate_certs: "{{ acme_intermediates_tmp.results | map(attribute='ansible_facts.y__') | list }}"
-
- vars:
- types:
- - root
- - intermediate
- root_numbers:
- # The number 3 comes from here: https://github.com/ansible/acme-test-container/blob/master/run.sh#L12
- - 0
- - 1
- - 2
- - 3
- interesting_keys:
- - authority_key_identifier
- - subject_key_identifier
- - issuer
- - subject
- #- serial_number
- #- public_key_fingerprints
-
-- name: ACME root certificate info
- debug:
- var: acme_roots
-
-#- name: ACME root certificates as PEM
-# debug:
-# var: acme_root_certs
-
-- name: ACME intermediate certificate info
- debug:
- var: acme_intermediates
-
-#- name: ACME intermediate certificates as PEM
-# debug:
-# var: acme_intermediate_certs
-
-- block:
- - name: Running tests with OpenSSL backend
- include_tasks: impl.yml
- vars:
- select_crypto_backend: openssl
-
- - import_tasks: ../tests/validate.yml
-
- # Old 0.9.8 versions have insufficient CLI support for signing with EC keys
- when: openssl_version.stdout is version('1.0.0', '>=')
-
-- name: Remove output directory
- file:
- path: "{{ output_dir }}"
- state: absent
-
-- name: Re-create output directory
- file:
- path: "{{ output_dir }}"
- state: directory
-
-- block:
- - name: Running tests with cryptography backend
- include_tasks: impl.yml
- vars:
- select_crypto_backend: cryptography
-
- - import_tasks: ../tests/validate.yml
-
- when: cryptography_version.stdout is version('1.5', '>=')
diff --git a/test/integration/targets/acme_certificate/tasks/obtain-cert.yml b/test/integration/targets/acme_certificate/tasks/obtain-cert.yml
deleted file mode 120000
index 532df9452e..0000000000
--- a/test/integration/targets/acme_certificate/tasks/obtain-cert.yml
+++ /dev/null
@@ -1 +0,0 @@
-../../setup_acme/tasks/obtain-cert.yml \ No newline at end of file
diff --git a/test/integration/targets/acme_certificate/tests/validate.yml b/test/integration/targets/acme_certificate/tests/validate.yml
deleted file mode 100644
index cd7d28b0d8..0000000000
--- a/test/integration/targets/acme_certificate/tests/validate.yml
+++ /dev/null
@@ -1,144 +0,0 @@
----
-- name: Check that certificate 1 is valid
- assert:
- that:
- - cert_1_valid is not failed
-- name: Check that certificate 1 contains correct SANs
- assert:
- that:
- - "'DNS:example.com' in cert_1_text.stdout"
-- name: Check that certificate 1 retrieval got all chains
- assert:
- that:
- - "'all_chains' in cert_1_obtain_results"
- - "cert_1_obtain_results.all_chains | length > 1"
- - "'cert' in cert_1_obtain_results.all_chains[cert_1_alternate | int]"
- - "'chain' in cert_1_obtain_results.all_chains[cert_1_alternate | int]"
- - "'full_chain' in cert_1_obtain_results.all_chains[cert_1_alternate | int]"
- - "lookup('file', output_dir ~ '/cert-1.pem', rstrip=False) == cert_1_obtain_results.all_chains[cert_1_alternate | int].cert"
- - "lookup('file', output_dir ~ '/cert-1-chain.pem', rstrip=False) == cert_1_obtain_results.all_chains[cert_1_alternate | int].chain"
- - "lookup('file', output_dir ~ '/cert-1-fullchain.pem', rstrip=False) == cert_1_obtain_results.all_chains[cert_1_alternate | int].full_chain"
-
-- name: Check that certificate 2 is valid
- assert:
- that:
- - cert_2_valid is not failed
-- name: Check that certificate 2 contains correct SANs
- assert:
- that:
- - "'DNS:*.example.com' in cert_2_text.stdout"
- - "'DNS:example.com' in cert_2_text.stdout"
-- name: Check that certificate 1 retrieval got all chains
- assert:
- that:
- - "'all_chains' in cert_2_obtain_results"
- - "cert_2_obtain_results.all_chains | length > 1"
- - "'cert' in cert_2_obtain_results.all_chains[cert_2_alternate | int]"
- - "'chain' in cert_2_obtain_results.all_chains[cert_2_alternate | int]"
- - "'full_chain' in cert_2_obtain_results.all_chains[cert_2_alternate | int]"
- - "lookup('file', output_dir ~ '/cert-2.pem', rstrip=False) == cert_2_obtain_results.all_chains[cert_2_alternate | int].cert"
- - "lookup('file', output_dir ~ '/cert-2-chain.pem', rstrip=False) == cert_2_obtain_results.all_chains[cert_2_alternate | int].chain"
- - "lookup('file', output_dir ~ '/cert-2-fullchain.pem', rstrip=False) == cert_2_obtain_results.all_chains[cert_2_alternate | int].full_chain"
-
-- name: Check that certificate 3 is valid
- assert:
- that:
- - cert_3_valid is not failed
-- name: Check that certificate 3 contains correct SANs
- assert:
- that:
- - "'DNS:*.example.com' in cert_3_text.stdout"
- - "'DNS:example.org' in cert_3_text.stdout"
- - "'DNS:t1.example.com' in cert_3_text.stdout"
-- name: Check that certificate 1 retrieval got all chains
- assert:
- that:
- - "'all_chains' in cert_3_obtain_results"
- - "cert_3_obtain_results.all_chains | length > 1"
- - "'cert' in cert_3_obtain_results.all_chains[cert_3_alternate | int]"
- - "'chain' in cert_3_obtain_results.all_chains[cert_3_alternate | int]"
- - "'full_chain' in cert_3_obtain_results.all_chains[cert_3_alternate | int]"
- - "lookup('file', output_dir ~ '/cert-3.pem', rstrip=False) == cert_3_obtain_results.all_chains[cert_3_alternate | int].cert"
- - "lookup('file', output_dir ~ '/cert-3-chain.pem', rstrip=False) == cert_3_obtain_results.all_chains[cert_3_alternate | int].chain"
- - "lookup('file', output_dir ~ '/cert-3-fullchain.pem', rstrip=False) == cert_3_obtain_results.all_chains[cert_3_alternate | int].full_chain"
-
-- name: Check that certificate 4 is valid
- assert:
- that:
- - cert_4_valid is not failed
-- name: Check that certificate 4 contains correct SANs
- assert:
- that:
- - "'DNS:example.com' in cert_4_text.stdout"
- - "'DNS:t1.example.com' in cert_4_text.stdout"
- - "'DNS:test.t2.example.com' in cert_4_text.stdout"
- - "'DNS:example.org' in cert_4_text.stdout"
- - "'DNS:test.example.org' in cert_4_text.stdout"
-- name: Check that certificate 4 retrieval did not get all chains
- assert:
- that:
- - "'all_chains' not in cert_4_obtain_results"
-
-- name: Check that certificate 5 is valid
- assert:
- that:
- - cert_5_valid is not failed
-- name: Check that certificate 5 contains correct SANs
- assert:
- that:
- - "'DNS:t2.example.com' in cert_5_text.stdout"
-- name: Check that certificate 5 was not recreated on the first try
- assert:
- that:
- - cert_5_recreate_1 == False
-- name: Check that certificate 5 was recreated on the second try
- assert:
- that:
- - cert_5_recreate_2 == True
-- name: Check that certificate 5 was recreated on the third try
- assert:
- that:
- - cert_5_recreate_3 == True
-
-- name: Check that certificate 6 is valid
- assert:
- that:
- - cert_6_valid is not failed
-- name: Check that certificate 6 contains correct SANs
- assert:
- that:
- - "'DNS:example.org' in cert_6_text.stdout"
-
-- name: Validate that orders were not retrieved
- assert:
- that:
- - "'account' in account_orders_not"
- - "'orders' not in account_orders_not"
-
-- name: Validate that orders were retrieved as list of URLs (1/2)
- assert:
- that:
- - "'account' in account_orders_urls"
- - "'orders' in account_orders_urls"
- - "account_orders_urls.orders[0] is string"
-
-- name: Validate that orders were retrieved as list of URLs (2/2)
- assert:
- that:
- - "'account' in account_orders_urls2"
- - "'orders' in account_orders_urls2"
- - "account_orders_urls2.orders[0] is string"
-
-- name: Validate that orders were retrieved as list of objects (1/2)
- assert:
- that:
- - "'account' in account_orders_full"
- - "'orders' in account_orders_full"
- - "account_orders_full.orders[0].status is string"
-
-- name: Validate that orders were retrieved as list of objects (2/2)
- assert:
- that:
- - "'account' in account_orders_full2"
- - "'orders' in account_orders_full2"
- - "account_orders_full2.orders[0].status is string"
diff --git a/test/integration/targets/acme_certificate_revoke/aliases b/test/integration/targets/acme_certificate_revoke/aliases
deleted file mode 100644
index d793633030..0000000000
--- a/test/integration/targets/acme_certificate_revoke/aliases
+++ /dev/null
@@ -1,2 +0,0 @@
-shippable/cloud/group1
-cloud/acme
diff --git a/test/integration/targets/acme_certificate_revoke/meta/main.yml b/test/integration/targets/acme_certificate_revoke/meta/main.yml
deleted file mode 100644
index 81d1e7e77a..0000000000
--- a/test/integration/targets/acme_certificate_revoke/meta/main.yml
+++ /dev/null
@@ -1,2 +0,0 @@
-dependencies:
- - setup_acme
diff --git a/test/integration/targets/acme_certificate_revoke/tasks/impl.yml b/test/integration/targets/acme_certificate_revoke/tasks/impl.yml
deleted file mode 100644
index 69545b6971..0000000000
--- a/test/integration/targets/acme_certificate_revoke/tasks/impl.yml
+++ /dev/null
@@ -1,89 +0,0 @@
----
-## SET UP ACCOUNT KEYS ########################################################################
-- name: Create ECC256 account key
- command: openssl ecparam -name prime256v1 -genkey -out {{ output_dir }}/account-ec256.pem
-- name: Create ECC384 account key
- command: openssl ecparam -name secp384r1 -genkey -out {{ output_dir }}/account-ec384.pem
-- name: Create RSA-2048 account key
- command: openssl genrsa -out {{ output_dir }}/account-rsa2048.pem 2048
-## CREATE ACCOUNTS AND OBTAIN CERTIFICATES ####################################################
-- name: Obtain cert 1
- include_tasks: obtain-cert.yml
- vars:
- certgen_title: Certificate 1 for revocation
- certificate_name: cert-1
- key_type: rsa
- rsa_bits: 2048
- subject_alt_name: "DNS:example.com"
- subject_alt_name_critical: no
- account_key_content: "{{ lookup('file', output_dir ~ '/account-ec256.pem') }}"
- challenge: http-01
- modify_account: yes
- deactivate_authzs: no
- force: no
- remaining_days: 10
- terms_agreed: yes
- account_email: "example@example.org"
-- name: Obtain cert 2
- include_tasks: obtain-cert.yml
- vars:
- certgen_title: Certificate 2 for revocation
- certificate_name: cert-2
- key_type: ec256
- subject_alt_name: "DNS:*.example.com"
- subject_alt_name_critical: yes
- account_key: account-ec384
- challenge: dns-01
- modify_account: yes
- deactivate_authzs: yes
- force: no
- remaining_days: 10
- terms_agreed: yes
- account_email: "example@example.org"
-- name: Obtain cert 3
- include_tasks: obtain-cert.yml
- vars:
- certgen_title: Certificate 3 for revocation
- certificate_name: cert-3
- key_type: ec384
- subject_alt_name: "DNS:t1.example.com"
- subject_alt_name_critical: no
- account_key: account-rsa2048
- challenge: dns-01
- modify_account: yes
- deactivate_authzs: no
- force: no
- remaining_days: 10
- terms_agreed: yes
- account_email: "example@example.org"
-## REVOKE CERTIFICATES ########################################################################
-- name: Revoke certificate 1 via account key
- acme_certificate_revoke:
- select_crypto_backend: "{{ select_crypto_backend }}"
- account_key_src: "{{ output_dir }}/account-ec256.pem"
- certificate: "{{ output_dir }}/cert-1.pem"
- acme_version: 2
- acme_directory: https://{{ acme_host }}:14000/dir
- validate_certs: no
- ignore_errors: yes
- register: cert_1_revoke
-- name: Revoke certificate 2 via certificate private key
- acme_certificate_revoke:
- select_crypto_backend: "{{ select_crypto_backend }}"
- private_key_src: "{{ output_dir }}/cert-2.key"
- certificate: "{{ output_dir }}/cert-2.pem"
- acme_version: 2
- acme_directory: https://{{ acme_host }}:14000/dir
- validate_certs: no
- ignore_errors: yes
- register: cert_2_revoke
-- name: Revoke certificate 3 via account key (fullchain)
- acme_certificate_revoke:
- select_crypto_backend: "{{ select_crypto_backend }}"
- account_key_content: "{{ lookup('file', output_dir ~ '/account-rsa2048.pem') }}"
- certificate: "{{ output_dir }}/cert-3-fullchain.pem"
- acme_version: 2
- acme_directory: https://{{ acme_host }}:14000/dir
- validate_certs: no
- ignore_errors: yes
- register: cert_3_revoke
diff --git a/test/integration/targets/acme_certificate_revoke/tasks/main.yml b/test/integration/targets/acme_certificate_revoke/tasks/main.yml
deleted file mode 100644
index e46c6dc4d0..0000000000
--- a/test/integration/targets/acme_certificate_revoke/tasks/main.yml
+++ /dev/null
@@ -1,31 +0,0 @@
----
-- block:
- - name: Running tests with OpenSSL backend
- include_tasks: impl.yml
- vars:
- select_crypto_backend: openssl
-
- - import_tasks: ../tests/validate.yml
-
- # Old 0.9.8 versions have insufficient CLI support for signing with EC keys
- when: openssl_version.stdout is version('1.0.0', '>=')
-
-- name: Remove output directory
- file:
- path: "{{ output_dir }}"
- state: absent
-
-- name: Re-create output directory
- file:
- path: "{{ output_dir }}"
- state: directory
-
-- block:
- - name: Running tests with cryptography backend
- include_tasks: impl.yml
- vars:
- select_crypto_backend: cryptography
-
- - import_tasks: ../tests/validate.yml
-
- when: cryptography_version.stdout is version('1.5', '>=')
diff --git a/test/integration/targets/acme_certificate_revoke/tasks/obtain-cert.yml b/test/integration/targets/acme_certificate_revoke/tasks/obtain-cert.yml
deleted file mode 120000
index 532df9452e..0000000000
--- a/test/integration/targets/acme_certificate_revoke/tasks/obtain-cert.yml
+++ /dev/null
@@ -1 +0,0 @@
-../../setup_acme/tasks/obtain-cert.yml \ No newline at end of file
diff --git a/test/integration/targets/acme_certificate_revoke/tests/validate.yml b/test/integration/targets/acme_certificate_revoke/tests/validate.yml
deleted file mode 100644
index 7f8f9c5438..0000000000
--- a/test/integration/targets/acme_certificate_revoke/tests/validate.yml
+++ /dev/null
@@ -1,16 +0,0 @@
----
-- name: Check that certificate 1 was revoked
- assert:
- that:
- - cert_1_revoke is changed
- - cert_1_revoke is not failed
-- name: Check that certificate 2 was revoked
- assert:
- that:
- - cert_2_revoke is changed
- - cert_2_revoke is not failed
-- name: Check that certificate 3 was revoked
- assert:
- that:
- - cert_3_revoke is changed
- - cert_3_revoke is not failed
diff --git a/test/integration/targets/acme_challenge_cert_helper/aliases b/test/integration/targets/acme_challenge_cert_helper/aliases
deleted file mode 100644
index d793633030..0000000000
--- a/test/integration/targets/acme_challenge_cert_helper/aliases
+++ /dev/null
@@ -1,2 +0,0 @@
-shippable/cloud/group1
-cloud/acme
diff --git a/test/integration/targets/acme_challenge_cert_helper/meta/main.yml b/test/integration/targets/acme_challenge_cert_helper/meta/main.yml
deleted file mode 100644
index 81d1e7e77a..0000000000
--- a/test/integration/targets/acme_challenge_cert_helper/meta/main.yml
+++ /dev/null
@@ -1,2 +0,0 @@
-dependencies:
- - setup_acme
diff --git a/test/integration/targets/acme_challenge_cert_helper/tasks/main.yml b/test/integration/targets/acme_challenge_cert_helper/tasks/main.yml
deleted file mode 100644
index 857485634c..0000000000
--- a/test/integration/targets/acme_challenge_cert_helper/tasks/main.yml
+++ /dev/null
@@ -1,25 +0,0 @@
----
-- block:
- - name: Create ECC256 account key
- command: openssl ecparam -name prime256v1 -genkey -out {{ output_dir }}/account-ec256.pem
- - name: Obtain cert 1
- include_tasks: obtain-cert.yml
- vars:
- select_crypto_backend: auto
- certgen_title: Certificate 1
- certificate_name: cert-1
- key_type: rsa
- rsa_bits: 2048
- subject_alt_name: "DNS:example.com"
- subject_alt_name_critical: no
- account_key: account-ec256
- challenge: tls-alpn-01
- challenge_alpn_tls: acme_challenge_cert_helper
- modify_account: yes
- deactivate_authzs: no
- force: no
- remaining_days: 10
- terms_agreed: yes
- account_email: "example@example.org"
-
- when: openssl_version.stdout is version('1.0.0', '>=') or cryptography_version.stdout is version('1.5', '>=')
diff --git a/test/integration/targets/acme_challenge_cert_helper/tasks/obtain-cert.yml b/test/integration/targets/acme_challenge_cert_helper/tasks/obtain-cert.yml
deleted file mode 120000
index 532df9452e..0000000000
--- a/test/integration/targets/acme_challenge_cert_helper/tasks/obtain-cert.yml
+++ /dev/null
@@ -1 +0,0 @@
-../../setup_acme/tasks/obtain-cert.yml \ No newline at end of file
diff --git a/test/integration/targets/acme_inspect/aliases b/test/integration/targets/acme_inspect/aliases
deleted file mode 100644
index d793633030..0000000000
--- a/test/integration/targets/acme_inspect/aliases
+++ /dev/null
@@ -1,2 +0,0 @@
-shippable/cloud/group1
-cloud/acme
diff --git a/test/integration/targets/acme_inspect/meta/main.yml b/test/integration/targets/acme_inspect/meta/main.yml
deleted file mode 100644
index 81d1e7e77a..0000000000
--- a/test/integration/targets/acme_inspect/meta/main.yml
+++ /dev/null
@@ -1,2 +0,0 @@
-dependencies:
- - setup_acme
diff --git a/test/integration/targets/acme_inspect/tasks/impl.yml b/test/integration/targets/acme_inspect/tasks/impl.yml
deleted file mode 100644
index 6628548d1d..0000000000
--- a/test/integration/targets/acme_inspect/tasks/impl.yml
+++ /dev/null
@@ -1,151 +0,0 @@
----
-- name: Generate account key
- command: openssl ecparam -name prime256v1 -genkey -out {{ output_dir }}/accountkey.pem
-
-- name: Parse account key (to ease debugging some test failures)
- command: openssl ec -in {{ output_dir }}/accountkey.pem -noout -text
-
-- name: Get directory
- acme_inspect:
- acme_directory: https://{{ acme_host }}:14000/dir
- acme_version: 2
- validate_certs: no
- method: directory-only
- register: directory
-- debug: var=directory
-
-- name: Create an account
- acme_inspect:
- acme_directory: https://{{ acme_host }}:14000/dir
- acme_version: 2
- validate_certs: no
- account_key_src: "{{ output_dir }}/accountkey.pem"
- url: "{{ directory.directory.newAccount}}"
- method: post
- content: '{"termsOfServiceAgreed":true}'
- register: account_creation
- # account_creation.headers.location contains the account URI
- # if creation was successful
-- debug: var=account_creation
-
-- name: Get account information
- acme_inspect:
- acme_directory: https://{{ acme_host }}:14000/dir
- acme_version: 2
- validate_certs: no
- account_key_src: "{{ output_dir }}/accountkey.pem"
- account_uri: "{{ account_creation.headers.location }}"
- url: "{{ account_creation.headers.location }}"
- method: get
- register: account_get
-- debug: var=account_get
-
-- name: Update account contacts
- acme_inspect:
- acme_directory: https://{{ acme_host }}:14000/dir
- acme_version: 2
- validate_certs: no
- account_key_src: "{{ output_dir }}/accountkey.pem"
- account_uri: "{{ account_creation.headers.location }}"
- url: "{{ account_creation.headers.location }}"
- method: post
- content: '{{ account_info | to_json }}'
- vars:
- account_info:
- # For valid values, see
- # https://www.rfc-editor.org/rfc/rfc8555.html#section-7.3
- contact:
- - mailto:me@example.com
- register: account_update
-- debug: var=account_update
-
-- name: Create certificate order
- acme_inspect:
- acme_directory: https://{{ acme_host }}:14000/dir
- acme_version: 2
- validate_certs: no
- account_key_src: "{{ output_dir }}/accountkey.pem"
- account_uri: "{{ account_creation.headers.location }}"
- url: "{{ directory.directory.newOrder }}"
- method: post
- content: '{{ create_order | to_json }}'
- vars:
- create_order:
- # For valid values, see
- # https://www.rfc-editor.org/rfc/rfc8555.html#section-7.4 and
- # https://www.rfc-editor.org/rfc/rfc8738.html
- identifiers:
- - type: dns
- value: example.com
- - type: dns
- value: example.org
- register: new_order
-- debug: var=new_order
-
-- name: Get order information
- acme_inspect:
- acme_directory: https://{{ acme_host }}:14000/dir
- acme_version: 2
- validate_certs: no
- account_key_src: "{{ output_dir }}/accountkey.pem"
- account_uri: "{{ account_creation.headers.location }}"
- url: "{{ new_order.headers.location }}"
- method: get
- register: order
-- debug: var=order
-
-- name: Get authzs for order
- acme_inspect:
- acme_directory: https://{{ acme_host }}:14000/dir
- acme_version: 2
- validate_certs: no
- account_key_src: "{{ output_dir }}/accountkey.pem"
- account_uri: "{{ account_creation.headers.location }}"
- url: "{{ item }}"
- method: get
- loop: "{{ order.output_json.authorizations }}"
- register: authz
-- debug: var=authz
-
-- name: Get HTTP-01 challenge for authz
- acme_inspect:
- acme_directory: https://{{ acme_host }}:14000/dir
- acme_version: 2
- validate_certs: no
- account_key_src: "{{ output_dir }}/accountkey.pem"
- account_uri: "{{ account_creation.headers.location }}"
- url: "{{ (item.challenges | selectattr('type', 'equalto', 'http-01') | list)[0].url }}"
- method: get
- register: http01challenge
- loop: "{{ authz.results | map(attribute='output_json') | list }}"
-- debug: var=http01challenge
-
-- name: Activate HTTP-01 challenge manually
- acme_inspect:
- acme_directory: https://{{ acme_host }}:14000/dir
- acme_version: 2
- validate_certs: no
- account_key_src: "{{ output_dir }}/accountkey.pem"
- account_uri: "{{ account_creation.headers.location }}"
- url: "{{ item.url }}"
- method: post
- content: '{}'
- register: activation
- loop: "{{ http01challenge.results | map(attribute='output_json') | list }}"
-- debug: var=activation
-
-- name: Get HTTP-01 challenge results
- acme_inspect:
- acme_directory: https://{{ acme_host }}:14000/dir
- acme_version: 2
- validate_certs: no
- account_key_src: "{{ output_dir }}/accountkey.pem"
- account_uri: "{{ account_creation.headers.location }}"
- url: "{{ item.url }}"
- method: get
- register: validation_result
- loop: "{{ http01challenge.results | map(attribute='output_json') | list }}"
- until: "validation_result.output_json.status != 'pending'"
- retries: 20
- delay: 1
-- debug: var=validation_result
diff --git a/test/integration/targets/acme_inspect/tasks/main.yml b/test/integration/targets/acme_inspect/tasks/main.yml
deleted file mode 100644
index e46c6dc4d0..0000000000
--- a/test/integration/targets/acme_inspect/tasks/main.yml
+++ /dev/null
@@ -1,31 +0,0 @@
----
-- block:
- - name: Running tests with OpenSSL backend
- include_tasks: impl.yml
- vars:
- select_crypto_backend: openssl
-
- - import_tasks: ../tests/validate.yml
-
- # Old 0.9.8 versions have insufficient CLI support for signing with EC keys
- when: openssl_version.stdout is version('1.0.0', '>=')
-
-- name: Remove output directory
- file:
- path: "{{ output_dir }}"
- state: absent
-
-- name: Re-create output directory
- file:
- path: "{{ output_dir }}"
- state: directory
-
-- block:
- - name: Running tests with cryptography backend
- include_tasks: impl.yml
- vars:
- select_crypto_backend: cryptography
-
- - import_tasks: ../tests/validate.yml
-
- when: cryptography_version.stdout is version('1.5', '>=')
diff --git a/test/integration/targets/acme_inspect/tests/validate.yml b/test/integration/targets/acme_inspect/tests/validate.yml
deleted file mode 100644
index a45b512f30..0000000000
--- a/test/integration/targets/acme_inspect/tests/validate.yml
+++ /dev/null
@@ -1,131 +0,0 @@
----
-- name: Check directory output
- assert:
- that:
- - directory is not changed
- - "'directory' in directory"
- - "'newAccount' in directory.directory"
- - "'newOrder' in directory.directory"
- - "'newNonce' in directory.directory"
- - "'headers' not in directory"
- - "'output_text' not in directory"
- - "'output_json' not in directory"
-
-- name: Check account creation output
- assert:
- that:
- - account_creation is changed
- - "'directory' in account_creation"
- - "'headers' in account_creation"
- - "'output_text' in account_creation"
- - "'output_json' in account_creation"
- - account_creation.headers.status == 201
- - "'location' in account_creation.headers"
- - account_creation.output_json.status == 'valid'
- - not (account_creation.output_json.contact | default([]))
- - account_creation.output_text | from_json == account_creation.output_json
-
-- name: Check account get output
- assert:
- that:
- - account_get is not changed
- - "'directory' in account_get"
- - "'headers' in account_get"
- - "'output_text' in account_get"
- - "'output_json' in account_get"
- - account_get.headers.status == 200
- - account_get.output_json == account_creation.output_json
-
-- name: Check account update output
- assert:
- that:
- - account_update is changed
- - "'directory' in account_update"
- - "'headers' in account_update"
- - "'output_text' in account_update"
- - "'output_json' in account_update"
- - account_update.output_json.status == 'valid'
- - account_update.output_json.contact | length == 1
- - account_update.output_json.contact[0] == 'mailto:me@example.com'
-
-- name: Check certificate request output
- assert:
- that:
- - new_order is changed
- - "'directory' in new_order"
- - "'headers' in new_order"
- - "'output_text' in new_order"
- - "'output_json' in new_order"
- - new_order.output_json.authorizations | length == 2
- - new_order.output_json.identifiers | length == 2
- - new_order.output_json.status == 'pending'
- - "'finalize' in new_order.output_json"
-
-- name: Check get order output
- assert:
- that:
- - order is not changed
- - "'directory' in order"
- - "'headers' in order"
- - "'output_text' in order"
- - "'output_json' in order"
- # The order of identifiers and authorizations is randomized!
- # - new_order.output_json == order.output_json
-
-- name: Check get authz output
- assert:
- that:
- - item is not changed
- - "'directory' in item"
- - "'headers' in item"
- - "'output_text' in item"
- - "'output_json' in item"
- - item.output_json.challenges | length >= 3
- - item.output_json.identifier.type == 'dns'
- - item.output_json.status == 'pending'
- loop: "{{ authz.results }}"
-
-- name: Check get challenge output
- assert:
- that:
- - item is not changed
- - "'directory' in item"
- - "'headers' in item"
- - "'output_text' in item"
- - "'output_json' in item"
- - item.output_json.status == 'pending'
- - item.output_json.type == 'http-01'
- - item.output_json.url == item.invocation.module_args.url
- - "'token' in item.output_json"
- loop: "{{ http01challenge.results }}"
-
-- name: Check challenge activation output
- assert:
- that:
- - item is changed
- - "'directory' in item"
- - "'headers' in item"
- - "'output_text' in item"
- - "'output_json' in item"
- - item.output_json.status == 'pending'
- - item.output_json.type == 'http-01'
- - item.output_json.url == item.invocation.module_args.url
- - "'token' in item.output_json"
- loop: "{{ activation.results }}"
-
-- name: Check validation result
- assert:
- that:
- - item is not changed
- - "'directory' in item"
- - "'headers' in item"
- - "'output_text' in item"
- - "'output_json' in item"
- - item.output_json.status == 'invalid'
- - item.output_json.type == 'http-01'
- - item.output_json.url == item.invocation.module_args.url
- - "'token' in item.output_json"
- - "'validated' in item.output_json"
- - "'error' in item.output_json"
- - item.output_json.error.type == 'urn:ietf:params:acme:error:unauthorized'
- loop: "{{ validation_result.results }}"
diff --git a/test/integration/targets/certificate_complete_chain/aliases b/test/integration/targets/certificate_complete_chain/aliases
deleted file mode 100644
index f8e28c7e46..0000000000
--- a/test/integration/targets/certificate_complete_chain/aliases
+++ /dev/null
@@ -1,2 +0,0 @@
-shippable/posix/group1
-skip/aix
diff --git a/test/integration/targets/certificate_complete_chain/files/cert1-chain.pem b/test/integration/targets/certificate_complete_chain/files/cert1-chain.pem
deleted file mode 100644
index 3e0f6c0851..0000000000
--- a/test/integration/targets/certificate_complete_chain/files/cert1-chain.pem
+++ /dev/null
@@ -1,22 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIDnzCCAyWgAwIBAgIQWyXOaQfEJlVm0zkMmalUrTAKBggqhkjOPQQDAzCBhTEL
-MAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE
-BxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMT
-IkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTQwOTI1MDAw
-MDAwWhcNMjkwOTI0MjM1OTU5WjCBkjELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdy
-ZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09N
-T0RPIENBIExpbWl0ZWQxODA2BgNVBAMTL0NPTU9ETyBFQ0MgRG9tYWluIFZhbGlk
-YXRpb24gU2VjdXJlIFNlcnZlciBDQSAyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcD
-QgAEAjgZgTrJaYRwWQKOqIofMN+83gP8eR06JSxrQSEYgur5PkrkM8wSzypD/A7y
-ZADA4SVQgiTNtkk4DyVHkUikraOCAWYwggFiMB8GA1UdIwQYMBaAFHVxpxlIGbyd
-nepBR9+UxEh3mdN5MB0GA1UdDgQWBBRACWFn8LyDcU/eEggsb9TUK3Y9ljAOBgNV
-HQ8BAf8EBAMCAYYwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHSUEFjAUBggrBgEF
-BQcDAQYIKwYBBQUHAwIwGwYDVR0gBBQwEjAGBgRVHSAAMAgGBmeBDAECATBMBgNV
-HR8ERTBDMEGgP6A9hjtodHRwOi8vY3JsLmNvbW9kb2NhLmNvbS9DT01PRE9FQ0ND
-ZXJ0aWZpY2F0aW9uQXV0aG9yaXR5LmNybDByBggrBgEFBQcBAQRmMGQwOwYIKwYB
-BQUHMAKGL2h0dHA6Ly9jcnQuY29tb2RvY2EuY29tL0NPTU9ET0VDQ0FkZFRydXN0
-Q0EuY3J0MCUGCCsGAQUFBzABhhlodHRwOi8vb2NzcC5jb21vZG9jYTQuY29tMAoG
-CCqGSM49BAMDA2gAMGUCMQCsaEclgBNPE1bAojcJl1pQxOfttGHLKIoKETKm4nHf
-EQGJbwd6IGZrGNC5LkP3Um8CMBKFfI4TZpIEuppFCZRKMGHRSdxv6+ctyYnPHmp8
-7IXOMCVZuoFwNLg0f+cB0eLLUg==
------END CERTIFICATE-----
diff --git a/test/integration/targets/certificate_complete_chain/files/cert1-fullchain.pem b/test/integration/targets/certificate_complete_chain/files/cert1-fullchain.pem
deleted file mode 100644
index e12a7ca815..0000000000
--- a/test/integration/targets/certificate_complete_chain/files/cert1-fullchain.pem
+++ /dev/null
@@ -1,51 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIFBTCCBKugAwIBAgIQL+c9oQXpvdcOD3BKAncbgDAKBggqhkjOPQQDAjCBkjEL
-MAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE
-BxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxODA2BgNVBAMT
-L0NPTU9ETyBFQ0MgRG9tYWluIFZhbGlkYXRpb24gU2VjdXJlIFNlcnZlciBDQSAy
-MB4XDTE4MDcxMTAwMDAwMFoXDTE5MDExNzIzNTk1OVowbDEhMB8GA1UECxMYRG9t
-YWluIENvbnRyb2wgVmFsaWRhdGVkMSEwHwYDVQQLExhQb3NpdGl2ZVNTTCBNdWx0
-aS1Eb21haW4xJDAiBgNVBAMTG3NzbDgwMzAyNS5jbG91ZGZsYXJlc3NsLmNvbTBZ
-MBMGByqGSM49AgEGCCqGSM49AwEHA0IABMap9sMZnCzTXID1chTOmtOk8p6+SHbG
-3fmyJJljI7sN9RddlLKar9VBS48WguVv1R6trvERIYj8TzKCVBzu9mmjggMGMIID
-AjAfBgNVHSMEGDAWgBRACWFn8LyDcU/eEggsb9TUK3Y9ljAdBgNVHQ4EFgQUd/6a
-t8j7v5DsL7xWacf8VyzOLJcwDgYDVR0PAQH/BAQDAgeAMAwGA1UdEwEB/wQCMAAw
-HQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCME8GA1UdIARIMEYwOgYLKwYB
-BAGyMQECAgcwKzApBggrBgEFBQcCARYdaHR0cHM6Ly9zZWN1cmUuY29tb2RvLmNv
-bS9DUFMwCAYGZ4EMAQIBMFYGA1UdHwRPME0wS6BJoEeGRWh0dHA6Ly9jcmwuY29t
-b2RvY2E0LmNvbS9DT01PRE9FQ0NEb21haW5WYWxpZGF0aW9uU2VjdXJlU2VydmVy
-Q0EyLmNybDCBiAYIKwYBBQUHAQEEfDB6MFEGCCsGAQUFBzAChkVodHRwOi8vY3J0
-LmNvbW9kb2NhNC5jb20vQ09NT0RPRUNDRG9tYWluVmFsaWRhdGlvblNlY3VyZVNl
-cnZlckNBMi5jcnQwJQYIKwYBBQUHMAGGGWh0dHA6Ly9vY3NwLmNvbW9kb2NhNC5j
-b20wSAYDVR0RBEEwP4Ibc3NsODAzMDI1LmNsb3VkZmxhcmVzc2wuY29tghAqLmhz
-Y29zY2RuNDAubmV0gg5oc2Nvc2NkbjQwLm5ldDCCAQMGCisGAQQB1nkCBAIEgfQE
-gfEA7wB2AO5Lvbd1zmC64UJpH6vhnmajD35fsHLYgwDEe4l6qP3LAAABZIbVA88A
-AAQDAEcwRQIhANtN489Izy3iss/eF8rUw/gir8rqyA2t3lpxnco+J2NlAiBBku5M
-iGD8whW5/31byPj0/ype1MmG0QYrq3qWvYiQ3QB1AHR+2oMxrTMQkSGcziVPQnDC
-v/1eQiAIxjc1eeYQe8xWAAABZIbVBB4AAAQDAEYwRAIgSjcL7B4cbgm2XED69G7/
-iFPe2zkWhxnkgGISSwuXw1gCICzwPmfbjEfwDNXEuBs7JXkPRaT1pi7hZ9aR5wJJ
-TKH9MAoGCCqGSM49BAMCA0gAMEUCIQDqxmFLcme3Ldd+jiMQf7fT5pSezZfMOL0S
-cNmfGvNtPQIgec3sO/ylnnaztCy5KDjYsnh+rm01bxs+nz2DnOPF+xo=
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIIDnzCCAyWgAwIBAgIQWyXOaQfEJlVm0zkMmalUrTAKBggqhkjOPQQDAzCBhTEL
-MAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE
-BxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMT
-IkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTQwOTI1MDAw
-MDAwWhcNMjkwOTI0MjM1OTU5WjCBkjELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdy
-ZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09N
-T0RPIENBIExpbWl0ZWQxODA2BgNVBAMTL0NPTU9ETyBFQ0MgRG9tYWluIFZhbGlk
-YXRpb24gU2VjdXJlIFNlcnZlciBDQSAyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcD
-QgAEAjgZgTrJaYRwWQKOqIofMN+83gP8eR06JSxrQSEYgur5PkrkM8wSzypD/A7y
-ZADA4SVQgiTNtkk4DyVHkUikraOCAWYwggFiMB8GA1UdIwQYMBaAFHVxpxlIGbyd
-nepBR9+UxEh3mdN5MB0GA1UdDgQWBBRACWFn8LyDcU/eEggsb9TUK3Y9ljAOBgNV
-HQ8BAf8EBAMCAYYwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHSUEFjAUBggrBgEF
-BQcDAQYIKwYBBQUHAwIwGwYDVR0gBBQwEjAGBgRVHSAAMAgGBmeBDAECATBMBgNV
-HR8ERTBDMEGgP6A9hjtodHRwOi8vY3JsLmNvbW9kb2NhLmNvbS9DT01PRE9FQ0ND
-ZXJ0aWZpY2F0aW9uQXV0aG9yaXR5LmNybDByBggrBgEFBQcBAQRmMGQwOwYIKwYB
-BQUHMAKGL2h0dHA6Ly9jcnQuY29tb2RvY2EuY29tL0NPTU9ET0VDQ0FkZFRydXN0
-Q0EuY3J0MCUGCCsGAQUFBzABhhlodHRwOi8vb2NzcC5jb21vZG9jYTQuY29tMAoG
-CCqGSM49BAMDA2gAMGUCMQCsaEclgBNPE1bAojcJl1pQxOfttGHLKIoKETKm4nHf
-EQGJbwd6IGZrGNC5LkP3Um8CMBKFfI4TZpIEuppFCZRKMGHRSdxv6+ctyYnPHmp8
-7IXOMCVZuoFwNLg0f+cB0eLLUg==
------END CERTIFICATE-----
diff --git a/test/integration/targets/certificate_complete_chain/files/cert1-root.pem b/test/integration/targets/certificate_complete_chain/files/cert1-root.pem
deleted file mode 100644
index 546c95e30d..0000000000
--- a/test/integration/targets/certificate_complete_chain/files/cert1-root.pem
+++ /dev/null
@@ -1,16 +0,0 @@
------BEGIN CERTIFICATE-----
-MIICiTCCAg+gAwIBAgIQH0evqmIAcFBUTAGem2OZKjAKBggqhkjOPQQDAzCBhTEL
-MAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE
-BxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMT
-IkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDgwMzA2MDAw
-MDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdy
-ZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09N
-T0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlv
-biBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQDR3svdcmCFYX7deSR
-FtSrYpn1PlILBs5BAH+X4QokPB0BBO490o0JlwzgdeT6+3eKKvUDYEs2ixYjFq0J
-cfRK9ChQtP6IHG4/bC8vCVlbpVsLM5niwz2J+Wos77LTBumjQjBAMB0GA1UdDgQW
-BBR1cacZSBm8nZ3qQUfflMRId5nTeTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/
-BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjEA7wNbeqy3eApyt4jf/7VGFAkK+qDm
-fQjGGoe9GKhzvSbKYAydzpmfz1wPMOG+FDHqAjAU9JM8SaczepBGR7NjfRObTrdv
-GDeAU/7dIOA1mjbRxwG55tzd8/8dLDoWV9mSOdY=
------END CERTIFICATE-----
diff --git a/test/integration/targets/certificate_complete_chain/files/cert1.pem b/test/integration/targets/certificate_complete_chain/files/cert1.pem
deleted file mode 100644
index d00d252d8d..0000000000
--- a/test/integration/targets/certificate_complete_chain/files/cert1.pem
+++ /dev/null
@@ -1,29 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIFBTCCBKugAwIBAgIQL+c9oQXpvdcOD3BKAncbgDAKBggqhkjOPQQDAjCBkjEL
-MAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE
-BxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxODA2BgNVBAMT
-L0NPTU9ETyBFQ0MgRG9tYWluIFZhbGlkYXRpb24gU2VjdXJlIFNlcnZlciBDQSAy
-MB4XDTE4MDcxMTAwMDAwMFoXDTE5MDExNzIzNTk1OVowbDEhMB8GA1UECxMYRG9t
-YWluIENvbnRyb2wgVmFsaWRhdGVkMSEwHwYDVQQLExhQb3NpdGl2ZVNTTCBNdWx0
-aS1Eb21haW4xJDAiBgNVBAMTG3NzbDgwMzAyNS5jbG91ZGZsYXJlc3NsLmNvbTBZ
-MBMGByqGSM49AgEGCCqGSM49AwEHA0IABMap9sMZnCzTXID1chTOmtOk8p6+SHbG
-3fmyJJljI7sN9RddlLKar9VBS48WguVv1R6trvERIYj8TzKCVBzu9mmjggMGMIID
-AjAfBgNVHSMEGDAWgBRACWFn8LyDcU/eEggsb9TUK3Y9ljAdBgNVHQ4EFgQUd/6a
-t8j7v5DsL7xWacf8VyzOLJcwDgYDVR0PAQH/BAQDAgeAMAwGA1UdEwEB/wQCMAAw
-HQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCME8GA1UdIARIMEYwOgYLKwYB
-BAGyMQECAgcwKzApBggrBgEFBQcCARYdaHR0cHM6Ly9zZWN1cmUuY29tb2RvLmNv
-bS9DUFMwCAYGZ4EMAQIBMFYGA1UdHwRPME0wS6BJoEeGRWh0dHA6Ly9jcmwuY29t
-b2RvY2E0LmNvbS9DT01PRE9FQ0NEb21haW5WYWxpZGF0aW9uU2VjdXJlU2VydmVy
-Q0EyLmNybDCBiAYIKwYBBQUHAQEEfDB6MFEGCCsGAQUFBzAChkVodHRwOi8vY3J0
-LmNvbW9kb2NhNC5jb20vQ09NT0RPRUNDRG9tYWluVmFsaWRhdGlvblNlY3VyZVNl
-cnZlckNBMi5jcnQwJQYIKwYBBQUHMAGGGWh0dHA6Ly9vY3NwLmNvbW9kb2NhNC5j
-b20wSAYDVR0RBEEwP4Ibc3NsODAzMDI1LmNsb3VkZmxhcmVzc2wuY29tghAqLmhz
-Y29zY2RuNDAubmV0gg5oc2Nvc2NkbjQwLm5ldDCCAQMGCisGAQQB1nkCBAIEgfQE
-gfEA7wB2AO5Lvbd1zmC64UJpH6vhnmajD35fsHLYgwDEe4l6qP3LAAABZIbVA88A
-AAQDAEcwRQIhANtN489Izy3iss/eF8rUw/gir8rqyA2t3lpxnco+J2NlAiBBku5M
-iGD8whW5/31byPj0/ype1MmG0QYrq3qWvYiQ3QB1AHR+2oMxrTMQkSGcziVPQnDC
-v/1eQiAIxjc1eeYQe8xWAAABZIbVBB4AAAQDAEYwRAIgSjcL7B4cbgm2XED69G7/
-iFPe2zkWhxnkgGISSwuXw1gCICzwPmfbjEfwDNXEuBs7JXkPRaT1pi7hZ9aR5wJJ
-TKH9MAoGCCqGSM49BAMCA0gAMEUCIQDqxmFLcme3Ldd+jiMQf7fT5pSezZfMOL0S
-cNmfGvNtPQIgec3sO/ylnnaztCy5KDjYsnh+rm01bxs+nz2DnOPF+xo=
------END CERTIFICATE-----
diff --git a/test/integration/targets/certificate_complete_chain/files/cert2-altchain.pem b/test/integration/targets/certificate_complete_chain/files/cert2-altchain.pem
deleted file mode 100644
index 4e82cb56d8..0000000000
--- a/test/integration/targets/certificate_complete_chain/files/cert2-altchain.pem
+++ /dev/null
@@ -1,32 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIFjTCCA3WgAwIBAgIRANOxciY0IzLc9AUoUSrsnGowDQYJKoZIhvcNAQELBQAw
-TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
-cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTYxMDA2MTU0MzU1
-WhcNMjExMDA2MTU0MzU1WjBKMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3Mg
-RW5jcnlwdDEjMCEGA1UEAxMaTGV0J3MgRW5jcnlwdCBBdXRob3JpdHkgWDMwggEi
-MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCc0wzwWuUuR7dyXTeDs2hjMOrX
-NSYZJeG9vjXxcJIvt7hLQQWrqZ41CFjssSrEaIcLo+N15Obzp2JxunmBYB/XkZqf
-89B4Z3HIaQ6Vkc/+5pnpYDxIzH7KTXcSJJ1HG1rrueweNwAcnKx7pwXqzkrrvUHl
-Npi5y/1tPJZo3yMqQpAMhnRnyH+lmrhSYRQTP2XpgofL2/oOVvaGifOFP5eGr7Dc
-Gu9rDZUWfcQroGWymQQ2dYBrrErzG5BJeC+ilk8qICUpBMZ0wNAxzY8xOJUWuqgz
-uEPxsR/DMH+ieTETPS02+OP88jNquTkxxa/EjQ0dZBYzqvqEKbbUC8DYfcOTAgMB
-AAGjggFnMIIBYzAOBgNVHQ8BAf8EBAMCAYYwEgYDVR0TAQH/BAgwBgEB/wIBADBU
-BgNVHSAETTBLMAgGBmeBDAECATA/BgsrBgEEAYLfEwEBATAwMC4GCCsGAQUFBwIB
-FiJodHRwOi8vY3BzLnJvb3QteDEubGV0c2VuY3J5cHQub3JnMB0GA1UdDgQWBBSo
-SmpjBH3duubRObemRWXv86jsoTAzBgNVHR8ELDAqMCigJqAkhiJodHRwOi8vY3Js
-LnJvb3QteDEubGV0c2VuY3J5cHQub3JnMHIGCCsGAQUFBwEBBGYwZDAwBggrBgEF
-BQcwAYYkaHR0cDovL29jc3Aucm9vdC14MS5sZXRzZW5jcnlwdC5vcmcvMDAGCCsG
-AQUFBzAChiRodHRwOi8vY2VydC5yb290LXgxLmxldHNlbmNyeXB0Lm9yZy8wHwYD
-VR0jBBgwFoAUebRZ5nu25eQBc4AIiMgaWPbpm24wDQYJKoZIhvcNAQELBQADggIB
-ABnPdSA0LTqmRf/Q1eaM2jLonG4bQdEnqOJQ8nCqxOeTRrToEKtwT++36gTSlBGx
-A/5dut82jJQ2jxN8RI8L9QFXrWi4xXnA2EqA10yjHiR6H9cj6MFiOnb5In1eWsRM
-UM2v3e9tNsCAgBukPHAg1lQh07rvFKm/Bz9BCjaxorALINUfZ9DD64j2igLIxle2
-DPxW8dI/F2loHMjXZjqG8RkqZUdoxtID5+90FgsGIfkMpqgRS05f4zPbCEHqCXl1
-eO5HyELTgcVlLXXQDgAWnRzut1hFJeczY1tjQQno6f6s+nMydLN26WuU4s3UYvOu
-OsUxRlJu7TSRHqDC3lSE5XggVkzdaPkuKGQbGpny+01/47hfXXNB7HntWNZ6N2Vw
-p7G6OfY+YQrZwIaQmhrIqJZuigsrbe3W+gdn5ykE9+Ky0VgVUsfxo52mwFYs1JKY
-2PGDuWx8M6DlS6qQkvHaRUo0FMd8TsSlbF0/v965qGFKhSDeQoMpYnwcmQilRh/0
-ayLThlHLN81gSkJjVrPI0Y8xCVPB4twb1PFUd2fPM3sA1tJ83sZ5v8vgFv2yofKR
-PB0t6JzUA81mSqM3kxl5e+IZwhYAyO0OTg3/fs8HqGTNKd9BqoUwSRBzp06JMg5b
-rUCGwbCUDI0mxadJ3Bz4WxR6fyNpBK2yAinWEsikxqEt
------END CERTIFICATE-----
diff --git a/test/integration/targets/certificate_complete_chain/files/cert2-altroot.pem b/test/integration/targets/certificate_complete_chain/files/cert2-altroot.pem
deleted file mode 100644
index b85c8037f6..0000000000
--- a/test/integration/targets/certificate_complete_chain/files/cert2-altroot.pem
+++ /dev/null
@@ -1,31 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw
-TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
-cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4
-WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu
-ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY
-MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc
-h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+
-0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U
-A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW
-T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH
-B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC
-B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv
-KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn
-OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn
-jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw
-qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI
-rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV
-HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq
-hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL
-ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ
-3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK
-NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5
-ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur
-TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC
-jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc
-oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq
-4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA
-mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d
-emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=
------END CERTIFICATE-----
diff --git a/test/integration/targets/certificate_complete_chain/files/cert2-chain.pem b/test/integration/targets/certificate_complete_chain/files/cert2-chain.pem
deleted file mode 100644
index 0002462ce8..0000000000
--- a/test/integration/targets/certificate_complete_chain/files/cert2-chain.pem
+++ /dev/null
@@ -1,27 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/
-MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT
-DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0Nlow
-SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT
-GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOC
-AQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF
-q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8
-SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0
-Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWA
-a6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj
-/PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0T
-AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG
-CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv
-bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k
-c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw
-VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC
-ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz
-MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu
-Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsF
-AAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo
-uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/
-wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu
-X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlG
-PfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6
-KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg==
------END CERTIFICATE-----
diff --git a/test/integration/targets/certificate_complete_chain/files/cert2-fullchain.pem b/test/integration/targets/certificate_complete_chain/files/cert2-fullchain.pem
deleted file mode 100644
index cf75a331c4..0000000000
--- a/test/integration/targets/certificate_complete_chain/files/cert2-fullchain.pem
+++ /dev/null
@@ -1,72 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIH5jCCBs6gAwIBAgISA2gSCm/BtvCR2e2bIap5YbXaMA0GCSqGSIb3DQEBCwUA
-MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQD
-ExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMzAeFw0xODA3MjcxNzMxMjdaFw0x
-ODEwMjUxNzMxMjdaMB4xHDAaBgNVBAMTE3d3dy5sZXRzZW5jcnlwdC5vcmcwggEi
-MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDpL8ZjVL0MUkUAIbYO9+ZCni+c
-ghGd9WhM2Ztaay6Wyh6lNoCdltdqTwUhE4O+d7UFModjM3G/KMyfuujr06c5iGKL
-3saPmIzLaRPIEOUlB2rKgasKhe8mDRyRLzQSXXgnsaKcTBBuhIHvtP51ZMr05nJJ
-sX/5FGjj96w+KJel6E/Ux1a1ZDOFkAYNSIrJJhA5jjIvUPr+Ri6Oc6UlhF9oueKI
-uWBILxQpC778tBWdHoZeBCNTHA1VvtwC53OeuHvdZm1jB/e30Mgf5DtVizYpFXVD
-mztkrd6z/3B6ZwPyfCE4KgzSf70/byOz971OJxNKTUVWedKHHDlrMxfsPclbAgMB
-AAGjggTwMIIE7DAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEG
-CCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFG1w4j/KDrYSFu7m9DPE
-xRR0E5gzMB8GA1UdIwQYMBaAFKhKamMEfd265tE5t6ZFZe/zqOyhMG8GCCsGAQUF
-BwEBBGMwYTAuBggrBgEFBQcwAYYiaHR0cDovL29jc3AuaW50LXgzLmxldHNlbmNy
-eXB0Lm9yZzAvBggrBgEFBQcwAoYjaHR0cDovL2NlcnQuaW50LXgzLmxldHNlbmNy
-eXB0Lm9yZy8wggHxBgNVHREEggHoMIIB5IIbY2VydC5pbnQteDEubGV0c2VuY3J5
-cHQub3JnghtjZXJ0LmludC14Mi5sZXRzZW5jcnlwdC5vcmeCG2NlcnQuaW50LXgz
-LmxldHNlbmNyeXB0Lm9yZ4IbY2VydC5pbnQteDQubGV0c2VuY3J5cHQub3Jnghxj
-ZXJ0LnJvb3QteDEubGV0c2VuY3J5cHQub3Jngh9jZXJ0LnN0YWdpbmcteDEubGV0
-c2VuY3J5cHQub3Jngh9jZXJ0LnN0Zy1pbnQteDEubGV0c2VuY3J5cHQub3JngiBj
-ZXJ0LnN0Zy1yb290LXgxLmxldHNlbmNyeXB0Lm9yZ4ISY3AubGV0c2VuY3J5cHQu
-b3JnghpjcC5yb290LXgxLmxldHNlbmNyeXB0Lm9yZ4ITY3BzLmxldHNlbmNyeXB0
-Lm9yZ4IbY3BzLnJvb3QteDEubGV0c2VuY3J5cHQub3Jnghtjcmwucm9vdC14MS5s
-ZXRzZW5jcnlwdC5vcmeCD2xldHNlbmNyeXB0Lm9yZ4IWb3JpZ2luLmxldHNlbmNy
-eXB0Lm9yZ4IXb3JpZ2luMi5sZXRzZW5jcnlwdC5vcmeCFnN0YXR1cy5sZXRzZW5j
-cnlwdC5vcmeCE3d3dy5sZXRzZW5jcnlwdC5vcmcwgf4GA1UdIASB9jCB8zAIBgZn
-gQwBAgEwgeYGCysGAQQBgt8TAQEBMIHWMCYGCCsGAQUFBwIBFhpodHRwOi8vY3Bz
-LmxldHNlbmNyeXB0Lm9yZzCBqwYIKwYBBQUHAgIwgZ4MgZtUaGlzIENlcnRpZmlj
-YXRlIG1heSBvbmx5IGJlIHJlbGllZCB1cG9uIGJ5IFJlbHlpbmcgUGFydGllcyBh
-bmQgb25seSBpbiBhY2NvcmRhbmNlIHdpdGggdGhlIENlcnRpZmljYXRlIFBvbGlj
-eSBmb3VuZCBhdCBodHRwczovL2xldHNlbmNyeXB0Lm9yZy9yZXBvc2l0b3J5LzCC
-AQQGCisGAQQB1nkCBAIEgfUEgfIA8AB2AMEWSuCnctLUOS3ICsEHcNTwxJvemRpI
-QMH6B1Fk9jNgAAABZN0ChToAAAQDAEcwRQIgblal8oXnfoopr1+dWVhvBx+sqHT0
-eLYxJHBTaRp3j1QCIQDhFQqMk6DDXUgcU12K36zLVFwJTdAJI4RBisnX+g+W0AB2
-ACk8UZZUyDlluqpQ/FgH1Ldvv1h6KXLcpMMM9OVFR/R4AAABZN0Chz4AAAQDAEcw
-RQIhAImOjvkritUNKJZB7dcUtjoyIbfNwdCspvRiEzXuvVQoAiAZryoyg3TcMun5
-Gb2dEn1cttMnPW9u670/JdRjvjU/wTANBgkqhkiG9w0BAQsFAAOCAQEAGepCmckP
-Tn9Sz268FEwkdD+6wWaPfeYlh+9nacFh90nQ35EYQMOK8a+X7ixHGbRz19On3Wt4
-1fcbPa9SefocTjAintMwwreCxpRTmwGACYojd7vRWEmA6q7+/HO2BfZahWzclOjw
-mSDBycDEm8R0ZK52vYjzVno8x0mrsmSO0403S/6syYB/guH6P17kIBw+Tgx6/i/c
-I1C6MoFkuaAKUUcZmgGGBgE+L/7cWtWjbkVXyA3ZQQy9G7rcBT+N/RrDfBh4iZDq
-jAN5UIIYL8upBhjiMYVuoJrH2nklzEwr5SWKcccJX5eWkGLUwlcY9LGAA8+17l2I
-l1Ou20Dm9TxnNw==
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/
-MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT
-DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0Nlow
-SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT
-GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOC
-AQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF
-q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8
-SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0
-Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWA
-a6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj
-/PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0T
-AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG
-CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv
-bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k
-c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw
-VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC
-ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz
-MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu
-Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsF
-AAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo
-uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/
-wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu
-X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlG
-PfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6
-KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg==
------END CERTIFICATE-----
diff --git a/test/integration/targets/certificate_complete_chain/files/cert2-root.pem b/test/integration/targets/certificate_complete_chain/files/cert2-root.pem
deleted file mode 100644
index b2e43c9381..0000000000
--- a/test/integration/targets/certificate_complete_chain/files/cert2-root.pem
+++ /dev/null
@@ -1,20 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIDSjCCAjKgAwIBAgIQRK+wgNajJ7qJMDmGLvhAazANBgkqhkiG9w0BAQUFADA/
-MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT
-DkRTVCBSb290IENBIFgzMB4XDTAwMDkzMDIxMTIxOVoXDTIxMDkzMDE0MDExNVow
-PzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMRcwFQYDVQQD
-Ew5EU1QgUm9vdCBDQSBYMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
-AN+v6ZdQCINXtMxiZfaQguzH0yxrMMpb7NnDfcdAwRgUi+DoM3ZJKuM/IUmTrE4O
-rz5Iy2Xu/NMhD2XSKtkyj4zl93ewEnu1lcCJo6m67XMuegwGMoOifooUMM0RoOEq
-OLl5CjH9UL2AZd+3UWODyOKIYepLYYHsUmu5ouJLGiifSKOeDNoJjj4XLh7dIN9b
-xiqKqy69cK3FCxolkHRyxXtqqzTWMIn/5WgTe1QLyNau7Fqckh49ZLOMxt+/yUFw
-7BZy1SbsOFU5Q9D8/RhcQPGX69Wam40dutolucbY38EVAjqr2m7xPi71XAicPNaD
-aeQQmxkqtilX4+U9m5/wAl0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNV
-HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMSnsaR7LHH62+FLkHX/xBVghYkQMA0GCSqG
-SIb3DQEBBQUAA4IBAQCjGiybFwBcqR7uKGY3Or+Dxz9LwwmglSBd49lZRNI+DT69
-ikugdB/OEIKcdBodfpga3csTS7MgROSR6cz8faXbauX+5v3gTt23ADq1cEmv8uXr
-AvHRAosZy5Q6XkjEGB5YGV8eAlrwDPGxrancWYaLbumR9YbK+rlmM6pZW87ipxZz
-R8srzJmwN0jP41ZL9c8PDHIyh8bwRLtTcm1D9SZImlJnt1ir/md2cXjbDaJWFBM5
-JDGFoqgCWjBH4d1QB7wCCZAA62RjYJsWvIjJEubSfZGL+T0yjWW06XyxV3bqxbYo
-Ob8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ
------END CERTIFICATE-----
diff --git a/test/integration/targets/certificate_complete_chain/files/cert2.pem b/test/integration/targets/certificate_complete_chain/files/cert2.pem
deleted file mode 100644
index 834eedc440..0000000000
--- a/test/integration/targets/certificate_complete_chain/files/cert2.pem
+++ /dev/null
@@ -1,45 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIH5jCCBs6gAwIBAgISA2gSCm/BtvCR2e2bIap5YbXaMA0GCSqGSIb3DQEBCwUA
-MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQD
-ExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMzAeFw0xODA3MjcxNzMxMjdaFw0x
-ODEwMjUxNzMxMjdaMB4xHDAaBgNVBAMTE3d3dy5sZXRzZW5jcnlwdC5vcmcwggEi
-MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDpL8ZjVL0MUkUAIbYO9+ZCni+c
-ghGd9WhM2Ztaay6Wyh6lNoCdltdqTwUhE4O+d7UFModjM3G/KMyfuujr06c5iGKL
-3saPmIzLaRPIEOUlB2rKgasKhe8mDRyRLzQSXXgnsaKcTBBuhIHvtP51ZMr05nJJ
-sX/5FGjj96w+KJel6E/Ux1a1ZDOFkAYNSIrJJhA5jjIvUPr+Ri6Oc6UlhF9oueKI
-uWBILxQpC778tBWdHoZeBCNTHA1VvtwC53OeuHvdZm1jB/e30Mgf5DtVizYpFXVD
-mztkrd6z/3B6ZwPyfCE4KgzSf70/byOz971OJxNKTUVWedKHHDlrMxfsPclbAgMB
-AAGjggTwMIIE7DAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEG
-CCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFG1w4j/KDrYSFu7m9DPE
-xRR0E5gzMB8GA1UdIwQYMBaAFKhKamMEfd265tE5t6ZFZe/zqOyhMG8GCCsGAQUF
-BwEBBGMwYTAuBggrBgEFBQcwAYYiaHR0cDovL29jc3AuaW50LXgzLmxldHNlbmNy
-eXB0Lm9yZzAvBggrBgEFBQcwAoYjaHR0cDovL2NlcnQuaW50LXgzLmxldHNlbmNy
-eXB0Lm9yZy8wggHxBgNVHREEggHoMIIB5IIbY2VydC5pbnQteDEubGV0c2VuY3J5
-cHQub3JnghtjZXJ0LmludC14Mi5sZXRzZW5jcnlwdC5vcmeCG2NlcnQuaW50LXgz
-LmxldHNlbmNyeXB0Lm9yZ4IbY2VydC5pbnQteDQubGV0c2VuY3J5cHQub3Jnghxj
-ZXJ0LnJvb3QteDEubGV0c2VuY3J5cHQub3Jngh9jZXJ0LnN0YWdpbmcteDEubGV0
-c2VuY3J5cHQub3Jngh9jZXJ0LnN0Zy1pbnQteDEubGV0c2VuY3J5cHQub3JngiBj
-ZXJ0LnN0Zy1yb290LXgxLmxldHNlbmNyeXB0Lm9yZ4ISY3AubGV0c2VuY3J5cHQu
-b3JnghpjcC5yb290LXgxLmxldHNlbmNyeXB0Lm9yZ4ITY3BzLmxldHNlbmNyeXB0
-Lm9yZ4IbY3BzLnJvb3QteDEubGV0c2VuY3J5cHQub3Jnghtjcmwucm9vdC14MS5s
-ZXRzZW5jcnlwdC5vcmeCD2xldHNlbmNyeXB0Lm9yZ4IWb3JpZ2luLmxldHNlbmNy
-eXB0Lm9yZ4IXb3JpZ2luMi5sZXRzZW5jcnlwdC5vcmeCFnN0YXR1cy5sZXRzZW5j
-cnlwdC5vcmeCE3d3dy5sZXRzZW5jcnlwdC5vcmcwgf4GA1UdIASB9jCB8zAIBgZn
-gQwBAgEwgeYGCysGAQQBgt8TAQEBMIHWMCYGCCsGAQUFBwIBFhpodHRwOi8vY3Bz
-LmxldHNlbmNyeXB0Lm9yZzCBqwYIKwYBBQUHAgIwgZ4MgZtUaGlzIENlcnRpZmlj
-YXRlIG1heSBvbmx5IGJlIHJlbGllZCB1cG9uIGJ5IFJlbHlpbmcgUGFydGllcyBh
-bmQgb25seSBpbiBhY2NvcmRhbmNlIHdpdGggdGhlIENlcnRpZmljYXRlIFBvbGlj
-eSBmb3VuZCBhdCBodHRwczovL2xldHNlbmNyeXB0Lm9yZy9yZXBvc2l0b3J5LzCC
-AQQGCisGAQQB1nkCBAIEgfUEgfIA8AB2AMEWSuCnctLUOS3ICsEHcNTwxJvemRpI
-QMH6B1Fk9jNgAAABZN0ChToAAAQDAEcwRQIgblal8oXnfoopr1+dWVhvBx+sqHT0
-eLYxJHBTaRp3j1QCIQDhFQqMk6DDXUgcU12K36zLVFwJTdAJI4RBisnX+g+W0AB2
-ACk8UZZUyDlluqpQ/FgH1Ldvv1h6KXLcpMMM9OVFR/R4AAABZN0Chz4AAAQDAEcw
-RQIhAImOjvkritUNKJZB7dcUtjoyIbfNwdCspvRiEzXuvVQoAiAZryoyg3TcMun5
-Gb2dEn1cttMnPW9u670/JdRjvjU/wTANBgkqhkiG9w0BAQsFAAOCAQEAGepCmckP
-Tn9Sz268FEwkdD+6wWaPfeYlh+9nacFh90nQ35EYQMOK8a+X7ixHGbRz19On3Wt4
-1fcbPa9SefocTjAintMwwreCxpRTmwGACYojd7vRWEmA6q7+/HO2BfZahWzclOjw
-mSDBycDEm8R0ZK52vYjzVno8x0mrsmSO0403S/6syYB/guH6P17kIBw+Tgx6/i/c
-I1C6MoFkuaAKUUcZmgGGBgE+L/7cWtWjbkVXyA3ZQQy9G7rcBT+N/RrDfBh4iZDq
-jAN5UIIYL8upBhjiMYVuoJrH2nklzEwr5SWKcccJX5eWkGLUwlcY9LGAA8+17l2I
-l1Ou20Dm9TxnNw==
------END CERTIFICATE-----
diff --git a/test/integration/targets/certificate_complete_chain/files/roots.pem b/test/integration/targets/certificate_complete_chain/files/roots.pem
deleted file mode 100644
index ee6c058d37..0000000000
--- a/test/integration/targets/certificate_complete_chain/files/roots.pem
+++ /dev/null
@@ -1,3733 +0,0 @@
-# ACCVRAIZ1
------BEGIN CERTIFICATE-----
-MIIH0zCCBbugAwIBAgIIXsO3pkN/pOAwDQYJKoZIhvcNAQEFBQAwQjESMBAGA1UE
-AwwJQUNDVlJBSVoxMRAwDgYDVQQLDAdQS0lBQ0NWMQ0wCwYDVQQKDARBQ0NWMQsw
-CQYDVQQGEwJFUzAeFw0xMTA1MDUwOTM3MzdaFw0zMDEyMzEwOTM3MzdaMEIxEjAQ
-BgNVBAMMCUFDQ1ZSQUlaMTEQMA4GA1UECwwHUEtJQUNDVjENMAsGA1UECgwEQUND
-VjELMAkGA1UEBhMCRVMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCb
-qau/YUqXry+XZpp0X9DZlv3P4uRm7x8fRzPCRKPfmt4ftVTdFXxpNRFvu8gMjmoY
-HtiP2Ra8EEg2XPBjs5BaXCQ316PWywlxufEBcoSwfdtNgM3802/J+Nq2DoLSRYWo
-G2ioPej0RGy9ocLLA76MPhMAhN9KSMDjIgro6TenGEyxCQ0jVn8ETdkXhBilyNpA
-lHPrzg5XPAOBOp0KoVdDaaxXbXmQeOW1tDvYvEyNKKGno6e6Ak4l0Squ7a4DIrhr
-IA8wKFSVf+DuzgpmndFALW4ir50awQUZ0m/A8p/4e7MCQvtQqR0tkw8jq8bBD5L/
-0KIV9VMJcRz/RROE5iZe+OCIHAr8Fraocwa48GOEAqDGWuzndN9wrqODJerWx5eH
-k6fGioozl2A3ED6XPm4pFdahD9GILBKfb6qkxkLrQaLjlUPTAYVtjrs78yM2x/47
-4KElB0iryYl0/wiPgL/AlmXz7uxLaL2diMMxs0Dx6M/2OLuc5NF/1OVYm3z61PMO
-m3WR5LpSLhl+0fXNWhn8ugb2+1KoS5kE3fj5tItQo05iifCHJPqDQsGH+tUtKSpa
-cXpkatcnYGMN285J9Y0fkIkyF/hzQ7jSWpOGYdbhdQrqeWZ2iE9x6wQl1gpaepPl
-uUsXQA+xtrn13k/c4LOsOxFwYIRKQ26ZIMApcQrAZQIDAQABo4ICyzCCAscwfQYI
-KwYBBQUHAQEEcTBvMEwGCCsGAQUFBzAChkBodHRwOi8vd3d3LmFjY3YuZXMvZmls
-ZWFkbWluL0FyY2hpdm9zL2NlcnRpZmljYWRvcy9yYWl6YWNjdjEuY3J0MB8GCCsG
-AQUFBzABhhNodHRwOi8vb2NzcC5hY2N2LmVzMB0GA1UdDgQWBBTSh7Tj3zcnk1X2
-VuqB5TbMjB4/vTAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFNKHtOPfNyeT
-VfZW6oHlNsyMHj+9MIIBcwYDVR0gBIIBajCCAWYwggFiBgRVHSAAMIIBWDCCASIG
-CCsGAQUFBwICMIIBFB6CARAAQQB1AHQAbwByAGkAZABhAGQAIABkAGUAIABDAGUA
-cgB0AGkAZgBpAGMAYQBjAGkA8wBuACAAUgBhAO0AegAgAGQAZQAgAGwAYQAgAEEA
-QwBDAFYAIAAoAEEAZwBlAG4AYwBpAGEAIABkAGUAIABUAGUAYwBuAG8AbABvAGcA
-7QBhACAAeQAgAEMAZQByAHQAaQBmAGkAYwBhAGMAaQDzAG4AIABFAGwAZQBjAHQA
-cgDzAG4AaQBjAGEALAAgAEMASQBGACAAUQA0ADYAMAAxADEANQA2AEUAKQAuACAA
-QwBQAFMAIABlAG4AIABoAHQAdABwADoALwAvAHcAdwB3AC4AYQBjAGMAdgAuAGUA
-czAwBggrBgEFBQcCARYkaHR0cDovL3d3dy5hY2N2LmVzL2xlZ2lzbGFjaW9uX2Mu
-aHRtMFUGA1UdHwROMEwwSqBIoEaGRGh0dHA6Ly93d3cuYWNjdi5lcy9maWxlYWRt
-aW4vQXJjaGl2b3MvY2VydGlmaWNhZG9zL3JhaXphY2N2MV9kZXIuY3JsMA4GA1Ud
-DwEB/wQEAwIBBjAXBgNVHREEEDAOgQxhY2N2QGFjY3YuZXMwDQYJKoZIhvcNAQEF
-BQADggIBAJcxAp/n/UNnSEQU5CmH7UwoZtCPNdpNYbdKl02125DgBS4OxnnQ8pdp
-D70ER9m+27Up2pvZrqmZ1dM8MJP1jaGo/AaNRPTKFpV8M9xii6g3+CfYCS0b78gU
-JyCpZET/LtZ1qmxNYEAZSUNUY9rizLpm5U9EelvZaoErQNV/+QEnWCzI7UiRfD+m
-AM/EKXMRNt6GGT6d7hmKG9Ww7Y49nCrADdg9ZuM8Db3VlFzi4qc1GwQA9j9ajepD
-vV+JHanBsMyZ4k0ACtrJJ1vnE5Bc5PUzolVt3OAJTS+xJlsndQAJxGJ3KQhfnlms
-tn6tn1QwIgPBHnFk/vk4CpYY3QIUrCPLBhwepH2NDd4nQeit2hW3sCPdK6jT2iWH
-7ehVRE2I9DZ+hJp4rPcOVkkO1jMl1oRQQmwgEh0q1b688nCBpHBgvgW1m54ERL5h
-I6zppSSMEYCUWqKiuUnSwdzRp+0xESyeGabu4VXhwOrPDYTkF7eifKXeVSUG7szA
-h1xA2syVP1XgNce4hL60Xc16gwFy7ofmXx2utYXGJt/mwZrpHgJHnyqobalbz+xF
-d3+YJ5oyXSrjhO7FmGYvliAd3djDJ9ew+f7Zfc3Qn48LFFhRny+Lwzgt3uiP1o2H
-pPVWQxaZLPSkVrQ0uGE3ycJYgBugl6H8WY3pEfbRD0tVNEYqi4Y7
------END CERTIFICATE-----
-
-# AC RAIZ FNMT-RCM
------BEGIN CERTIFICATE-----
-MIIFgzCCA2ugAwIBAgIPXZONMGc2yAYdGsdUhGkHMA0GCSqGSIb3DQEBCwUAMDsx
-CzAJBgNVBAYTAkVTMREwDwYDVQQKDAhGTk1ULVJDTTEZMBcGA1UECwwQQUMgUkFJ
-WiBGTk1ULVJDTTAeFw0wODEwMjkxNTU5NTZaFw0zMDAxMDEwMDAwMDBaMDsxCzAJ
-BgNVBAYTAkVTMREwDwYDVQQKDAhGTk1ULVJDTTEZMBcGA1UECwwQQUMgUkFJWiBG
-Tk1ULVJDTTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALpxgHpMhm5/
-yBNtwMZ9HACXjywMI7sQmkCpGreHiPibVmr75nuOi5KOpyVdWRHbNi63URcfqQgf
-BBckWKo3Shjf5TnUV/3XwSyRAZHiItQDwFj8d0fsjz50Q7qsNI1NOHZnjrDIbzAz
-WHFctPVrbtQBULgTfmxKo0nRIBnuvMApGGWn3v7v3QqQIecaZ5JCEJhfTzC8PhxF
-tBDXaEAUwED653cXeuYLj2VbPNmaUtu1vZ5Gzz3rkQUCwJaydkxNEJY7kvqcfw+Z
-374jNUUeAlz+taibmSXaXvMiwzn15Cou08YfxGyqxRxqAQVKL9LFwag0Jl1mpdIC
-IfkYtwb1TplvqKtMUejPUBjFd8g5CSxJkjKZqLsXF3mwWsXmo8RZZUc1g16p6DUL
-mbvkzSDGm0oGObVo/CK67lWMK07q87Hj/LaZmtVC+nFNCM+HHmpxffnTtOmlcYF7
-wk5HlqX2doWjKI/pgG6BU6VtX7hI+cL5NqYuSf+4lsKMB7ObiFj86xsc3i1w4peS
-MKGJ47xVqCfWS+2QrYv6YyVZLag13cqXM7zlzced0ezvXg5KkAYmY6252TUtB7p2
-ZSysV4999AeU14ECll2jB0nVetBX+RvnU0Z1qrB5QstocQjpYL05ac70r8NWQMet
-UqIJ5G+GR4of6ygnXYMgrwTJbFaai0b1AgMBAAGjgYMwgYAwDwYDVR0TAQH/BAUw
-AwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFPd9xf3E6Jobd2Sn9R2gzL+H
-YJptMD4GA1UdIAQ3MDUwMwYEVR0gADArMCkGCCsGAQUFBwIBFh1odHRwOi8vd3d3
-LmNlcnQuZm5tdC5lcy9kcGNzLzANBgkqhkiG9w0BAQsFAAOCAgEAB5BK3/MjTvDD
-nFFlm5wioooMhfNzKWtN/gHiqQxjAb8EZ6WdmF/9ARP67Jpi6Yb+tmLSbkyU+8B1
-RXxlDPiyN8+sD8+Nb/kZ94/sHvJwnvDKuO+3/3Y3dlv2bojzr2IyIpMNOmqOFGYM
-LVN0V2Ue1bLdI4E7pWYjJ2cJj+F3qkPNZVEI7VFY/uY5+ctHhKQV8Xa7pO6kO8Rf
-77IzlhEYt8llvhjho6Tc+hj507wTmzl6NLrTQfv6MooqtyuGC2mDOL7Nii4LcK2N
-JpLuHvUBKwrZ1pebbuCoGRw6IYsMHkCtA+fdZn71uSANA+iW+YJF1DngoABd15jm
-fZ5nc8OaKveri6E6FO80vFIOiZiaBECEHX5FaZNXzuvO+FB8TxxuBEOb+dY7Ixjp
-6o7RTUaN8Tvkasq6+yO3m/qZASlaWFot4/nUbQ4mrcFuNLwy+AwF+mWj2zs3gyLp
-1txyM/1d8iC9djwj2ij3+RvrWWTV3F9yfiD8zYm1kGdNYno/Tq0dwzn+evQoFt9B
-9kiABdcPUXmsEKvU7ANm5mqwujGSQkBqvjrTcuFqN1W8rB2Vt2lh8kORdOag0wok
-RqEIr9baRRmW1FMdW4R58MD3R++Lj8UGrp1MYp3/RgT408m2ECVAdf4WqslKYIYv
-uu8wd+RU4riEmViAqhOLUTpPSPaLtrM=
------END CERTIFICATE-----
-
-# Actalis Authentication Root CA
------BEGIN CERTIFICATE-----
-MIIFuzCCA6OgAwIBAgIIVwoRl0LE48wwDQYJKoZIhvcNAQELBQAwazELMAkGA1UE
-BhMCSVQxDjAMBgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlzIFMucC5BLi8w
-MzM1ODUyMDk2NzEnMCUGA1UEAwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290
-IENBMB4XDTExMDkyMjExMjIwMloXDTMwMDkyMjExMjIwMlowazELMAkGA1UEBhMC
-SVQxDjAMBgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlzIFMucC5BLi8wMzM1
-ODUyMDk2NzEnMCUGA1UEAwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290IENB
-MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAp8bEpSmkLO/lGMWwUKNv
-UTufClrJwkg4CsIcoBh/kbWHuUA/3R1oHwiD1S0eiKD4j1aPbZkCkpAW1V8IbInX
-4ay8IMKx4INRimlNAJZaby/ARH6jDuSRzVju3PvHHkVH3Se5CAGfpiEd9UEtL0z9
-KK3giq0itFZljoZUj5NDKd45RnijMCO6zfB9E1fAXdKDa0hMxKufgFpbOr3JpyI/
-gCczWw63igxdBzcIy2zSekciRDXFzMwujt0q7bd9Zg1fYVEiVRvjRuPjPdA1Yprb
-rxTIW6HMiRvhMCb8oJsfgadHHwTrozmSBp+Z07/T6k9QnBn+locePGX2oxgkg4YQ
-51Q+qDp2JE+BIcXjDwL4k5RHILv+1A7TaLndxHqEguNTVHnd25zS8gebLra8Pu2F
-be8lEfKXGkJh90qX6IuxEAf6ZYGyojnP9zz/GPvG8VqLWeICrHuS0E4UT1lF9gxe
-KF+w6D9Fz8+vm2/7hNN3WpVvrJSEnu68wEqPSpP4RCHiMUVhUE4Q2OM1fEwZtN4F
-v6MGn8i1zeQf1xcGDXqVdFUNaBr8EBtiZJ1t4JWgw5QHVw0U5r0F+7if5t+L4sbn
-fpb2U8WANFAoWPASUHEXMLrmeGO89LKtmyuy/uE5jF66CyCU3nuDuP/jVo23Eek7
-jPKxwV2dpAtMK9myGPW1n0sCAwEAAaNjMGEwHQYDVR0OBBYEFFLYiDrIn3hm7Ynz
-ezhwlMkCAjbQMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUUtiIOsifeGbt
-ifN7OHCUyQICNtAwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBCwUAA4ICAQAL
-e3KHwGCmSUyIWOYdiPcUZEim2FgKDk8TNd81HdTtBjHIgT5q1d07GjLukD0R0i70
-jsNjLiNmsGe+b7bAEzlgqqI0JZN1Ut6nna0Oh4lScWoWPBkdg/iaKWW+9D+a2fDz
-WochcYBNy+A4mz+7+uAwTc+G02UQGRjRlwKxK3JCaKygvU5a2hi/a5iB0P2avl4V
-SM0RFbnAKVy06Ij3Pjaut2L9HmLecHgQHEhb2rykOLpn7VU+Xlff1ANATIGk0k9j
-pwlCCRT8AKnCgHNPLsBA2RF7SOp6AsDT6ygBJlh0wcBzIm2Tlf05fbsq4/aC4yyX
-X04fkZT6/iyj2HYauE2yOE+b+h1IYHkm4vP9qdCa6HCPSXrW5b0KDtst842/6+Ok
-fcvHlXHo2qN8xcL4dJIEG4aspCJTQLas/kx2z/uUMsA1n3Y/buWQbqCmJqK4LL7R
-K4X9p2jIugErsWx0Hbhzlefut8cl8ABMALJ+tguLHPPAUJ4lueAI3jZm/zel0btU
-ZCzJJ7VLkn5l/9Mt4blOvH+kQSGQQXemOR/qnuOf0GZvBeyqdn6/axag67XH/JJU
-LysRJyU3eExRarDzzFhdFPFqSBX/wge2sY0PjlxQRrM9vwGYT7JZVEc+NHt4bVaT
-LnPqZih4zR0Uv6CPLy64Lo7yFIrM6bV8+2ydDKXhlg==
------END CERTIFICATE-----
-
-# AddTrust External Root
------BEGIN CERTIFICATE-----
-MIIENjCCAx6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBvMQswCQYDVQQGEwJTRTEU
-MBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFkZFRydXN0IEV4dGVybmFs
-IFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBFeHRlcm5hbCBDQSBSb290
-MB4XDTAwMDUzMDEwNDgzOFoXDTIwMDUzMDEwNDgzOFowbzELMAkGA1UEBhMCU0Ux
-FDASBgNVBAoTC0FkZFRydXN0IEFCMSYwJAYDVQQLEx1BZGRUcnVzdCBFeHRlcm5h
-bCBUVFAgTmV0d29yazEiMCAGA1UEAxMZQWRkVHJ1c3QgRXh0ZXJuYWwgQ0EgUm9v
-dDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALf3GjPm8gAELTngTlvt
-H7xsD821+iO2zt6bETOXpClMfZOfvUq8k+0DGuOPz+VtUFrWlymUWoCwSXrbLpX9
-uMq/NzgtHj6RQa1wVsfwTz/oMp50ysiQVOnGXw94nZpAPA6sYapeFI+eh6FqUNzX
-mk6vBbOmcZSccbNQYArHE504B4YCqOmoaSYYkKtMsE8jqzpPhNjfzp/haW+710LX
-a0Tkx63ubUFfclpxCDezeWWkWaCUN/cALw3CknLa0Dhy2xSoRcRdKn23tNbE7qzN
-E0S3ySvdQwAl+mG5aWpYIxG3pzOPVnVZ9c0p10a3CitlttNCbxWyuHv77+ldU9U0
-WicCAwEAAaOB3DCB2TAdBgNVHQ4EFgQUrb2YejS0Jvf6xCZU7wO94CTLVBowCwYD
-VR0PBAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wgZkGA1UdIwSBkTCBjoAUrb2YejS0
-Jvf6xCZU7wO94CTLVBqhc6RxMG8xCzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRU
-cnVzdCBBQjEmMCQGA1UECxMdQWRkVHJ1c3QgRXh0ZXJuYWwgVFRQIE5ldHdvcmsx
-IjAgBgNVBAMTGUFkZFRydXN0IEV4dGVybmFsIENBIFJvb3SCAQEwDQYJKoZIhvcN
-AQEFBQADggEBALCb4IUlwtYj4g+WBpKdQZic2YR5gdkeWxQHIzZlj7DYd7usQWxH
-YINRsPkyPef89iYTx4AWpb9a/IfPeHmJIZriTAcKhjW88t5RxNKWt9x+Tu5w/Rw5
-6wwCURQtjr0W4MHfRnXnJK3s9EK0hZNwEGe6nQY1ShjTK3rMUUKhemPR5ruhxSvC
-Nr4TDea9Y355e6cJDUCrat2PisP29owaQgVR1EX1n6diIWgVIEM8med8vSTYqZEX
-c4g/VhsxOBi0cQ+azcgOno4uG+GMmIPLHzHxREzGBHNJdmAPx/i9F4BrLunMTA5a
-mnkPIAou1Z5jJh5VkpTYghdae9C8x49OhgQ=
------END CERTIFICATE-----
-
-# AffirmTrust Commercial
------BEGIN CERTIFICATE-----
-MIIDTDCCAjSgAwIBAgIId3cGJyapsXwwDQYJKoZIhvcNAQELBQAwRDELMAkGA1UE
-BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVz
-dCBDb21tZXJjaWFsMB4XDTEwMDEyOTE0MDYwNloXDTMwMTIzMTE0MDYwNlowRDEL
-MAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZp
-cm1UcnVzdCBDb21tZXJjaWFsMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
-AQEA9htPZwcroRX1BiLLHwGy43NFBkRJLLtJJRTWzsO3qyxPxkEylFf6EqdbDuKP
-Hx6GGaeqtS25Xw2Kwq+FNXkyLbscYjfysVtKPcrNcV/pQr6U6Mje+SJIZMblq8Yr
-ba0F8PrVC8+a5fBQpIs7R6UjW3p6+DM/uO+Zl+MgwdYoic+U+7lF7eNAFxHUdPAL
-MeIrJmqbTFeurCA+ukV6BfO9m2kVrn1OIGPENXY6BwLJN/3HR+7o8XYdcxXyl6S1
-yHp52UKqK39c/s4mT6NmgTWvRLpUHhwwMmWd5jyTXlBOeuM61G7MGvv50jeuJCqr
-VwMiKA1JdX+3KNp1v47j3A55MQIDAQABo0IwQDAdBgNVHQ4EFgQUnZPGU4teyq8/
-nx4P5ZmVvCT2lI8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwDQYJ
-KoZIhvcNAQELBQADggEBAFis9AQOzcAN/wr91LoWXym9e2iZWEnStB03TX8nfUYG
-XUPGhi4+c7ImfU+TqbbEKpqrIZcUsd6M06uJFdhrJNTxFq7YpFzUf1GO7RgBsZNj
-vbz4YYCanrHOQnDiqX0GJX0nof5v7LMeJNrjS1UaADs1tDvZ110w/YETifLCBivt
-Z8SOyUOyXGsViQK8YvxO8rUzqrJv0wqiUOP2O+guRMLbZjipM1ZI8W0bM40NjD9g
-N53Tym1+NH4Nn3J2ixufcv1SNUFFApYvHLKac0khsUlHRUe072o0EclNmsxZt9YC
-nlpOZbWUrhvfKbAW8b8Angc6F2S1BLUjIZkKlTuXfO8=
------END CERTIFICATE-----
-
-# AffirmTrust Networking
------BEGIN CERTIFICATE-----
-MIIDTDCCAjSgAwIBAgIIfE8EORzUmS0wDQYJKoZIhvcNAQEFBQAwRDELMAkGA1UE
-BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVz
-dCBOZXR3b3JraW5nMB4XDTEwMDEyOTE0MDgyNFoXDTMwMTIzMTE0MDgyNFowRDEL
-MAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZp
-cm1UcnVzdCBOZXR3b3JraW5nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
-AQEAtITMMxcua5Rsa2FSoOujz3mUTOWUgJnLVWREZY9nZOIG41w3SfYvm4SEHi3y
-YJ0wTsyEheIszx6e/jarM3c1RNg1lho9Nuh6DtjVR6FqaYvZ/Ls6rnla1fTWcbua
-kCNrmreIdIcMHl+5ni36q1Mr3Lt2PpNMCAiMHqIjHNRqrSK6mQEubWXLviRmVSRL
-QESxG9fhwoXA3hA/Pe24/PHxI1Pcv2WXb9n5QHGNfb2V1M6+oF4nI979ptAmDgAp
-6zxG8D1gvz9Q0twmQVGeFDdCBKNwV6gbh+0t+nvujArjqWaJGctB+d1ENmHP4ndG
-yH329JKBNv3bNPFyfvMMFr20FQIDAQABo0IwQDAdBgNVHQ4EFgQUBx/S55zawm6i
-QLSwelAQUHTEyL0wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwDQYJ
-KoZIhvcNAQEFBQADggEBAIlXshZ6qML91tmbmzTCnLQyFE2npN/svqe++EPbkTfO
-tDIuUFUaNU52Q3Eg75N3ThVwLofDwR1t3Mu1J9QsVtFSUzpE0nPIxBsFZVpikpzu
-QY0x2+c06lkh1QF612S4ZDnNye2v7UsDSKegmQGA3GWjNq5lWUhPgkvIZfFXHeVZ
-Lgo/bNjR9eUJtGxUAArgFU2HdW23WJZa3W3SAKD0m0i+wzekujbgfIeFlxoVot4u
-olu9rxj5kFDNcFn4J2dHy8egBzp90SxdbBk6ZrV9/ZFvgrG+CJPbFEfxojfHRZ48
-x3evZKiT3/Zpg4Jg8klCNO1aAFSFHBY2kgxc+qatv9s=
------END CERTIFICATE-----
-
-# AffirmTrust Premium
------BEGIN CERTIFICATE-----
-MIIFRjCCAy6gAwIBAgIIbYwURrGmCu4wDQYJKoZIhvcNAQEMBQAwQTELMAkGA1UE
-BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MRwwGgYDVQQDDBNBZmZpcm1UcnVz
-dCBQcmVtaXVtMB4XDTEwMDEyOTE0MTAzNloXDTQwMTIzMTE0MTAzNlowQTELMAkG
-A1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MRwwGgYDVQQDDBNBZmZpcm1U
-cnVzdCBQcmVtaXVtMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxBLf
-qV/+Qd3d9Z+K4/as4Tx4mrzY8H96oDMq3I0gW64tb+eT2TZwamjPjlGjhVtnBKAQ
-JG9dKILBl1fYSCkTtuG+kU3fhQxTGJoeJKJPj/CihQvL9Cl/0qRY7iZNyaqoe5rZ
-+jjeRFcV5fiMyNlI4g0WJx0eyIOFJbe6qlVBzAMiSy2RjYvmia9mx+n/K+k8rNrS
-s8PhaJyJ+HoAVt70VZVs+7pk3WKL3wt3MutizCaam7uqYoNMtAZ6MMgpv+0GTZe5
-HMQxK9VfvFMSF5yZVylmd2EhMQcuJUmdGPLu8ytxjLW6OQdJd/zvLpKQBY0tL3d7
-70O/Nbua2Plzpyzy0FfuKE4mX4+QaAkvuPjcBukumj5Rp9EixAqnOEhss/n/fauG
-V+O61oV4d7pD6kh/9ti+I20ev9E2bFhc8e6kGVQa9QPSdubhjL08s9NIS+LI+H+S
-qHZGnEJlPqQewQcDWkYtuJfzt9WyVSHvutxMAJf7FJUnM7/oQ0dG0giZFmA7mn7S
-5u046uwBHjxIVkkJx0w3AJ6IDsBz4W9m6XJHMD4Q5QsDyZpCAGzFlH5hxIrff4Ia
-C1nEWTJ3s7xgaVY5/bQGeyzWZDbZvUjthB9+pSKPKrhC9IK31FOQeE4tGv2Bb0TX
-OwF0lkLgAOIua+rF7nKsu7/+6qqo+Nz2snmKtmcCAwEAAaNCMEAwHQYDVR0OBBYE
-FJ3AZ6YMItkm9UWrpmVSESfYRaxjMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/
-BAQDAgEGMA0GCSqGSIb3DQEBDAUAA4ICAQCzV00QYk465KzquByvMiPIs0laUZx2
-KI15qldGF9X1Uva3ROgIRL8YhNILgM3FEv0AVQVhh0HctSSePMTYyPtwni94loMg
-Nt58D2kTiKV1NpgIpsbfrM7jWNa3Pt668+s0QNiigfV4Py/VpfzZotReBA4Xrf5B
-8OWycvpEgjNC6C1Y91aMYj+6QrCcDFx+LmUmXFNPALJ4fqENmS2NuB2OosSw/WDQ
-MKSOyARiqcTtNd56l+0OOF6SL5Nwpamcb6d9Ex1+xghIsV5n61EIJenmJWtSKZGc
-0jlzCFfemQa0W50QBuHCAKi4HEoCChTQwUHK+4w1IX2COPKpVJEZNZOUbWo6xbLQ
-u4mGk+ibyQ86p3q4ofB4Rvr8Ny/lioTz3/4E2aFooC8k4gmVBtWVyuEklut89pMF
-u+1z6S3RdTnX5yTb2E5fQ4+e0BQ5v1VwSJlXMbSc7kqYA5YwH2AG7hsj/oFgIxpH
-YoWlzBk0gG+zrBrjn/B7SK3VAdlntqlyk+otZrWyuOQ9PLLvTIzq6we/qzWaVYa8
-GKa1qF60g2xraUDTn9zxw2lrueFtCfTxqlB2Cnp9ehehVZZCmTEJ3WARjQUwfuaO
-RtGdFNrHF+QFlozEJLUbzxQHskD4o55BhrwE0GuWyCqANP2/7waj3VjFhT0+j/6e
-KeC2uAloGRwYQw==
------END CERTIFICATE-----
-
-# AffirmTrust Premium ECC
------BEGIN CERTIFICATE-----
-MIIB/jCCAYWgAwIBAgIIdJclisc/elQwCgYIKoZIzj0EAwMwRTELMAkGA1UEBhMC
-VVMxFDASBgNVBAoMC0FmZmlybVRydXN0MSAwHgYDVQQDDBdBZmZpcm1UcnVzdCBQ
-cmVtaXVtIEVDQzAeFw0xMDAxMjkxNDIwMjRaFw00MDEyMzExNDIwMjRaMEUxCzAJ
-BgNVBAYTAlVTMRQwEgYDVQQKDAtBZmZpcm1UcnVzdDEgMB4GA1UEAwwXQWZmaXJt
-VHJ1c3QgUHJlbWl1bSBFQ0MwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQNMF4bFZ0D
-0KF5Nbc6PJJ6yhUczWLznCZcBz3lVPqj1swS6vQUX+iOGasvLkjmrBhDeKzQN8O9
-ss0s5kfiGuZjuD0uL3jET9v0D6RoTFVya5UdThhClXjMNzyR4ptlKymjQjBAMB0G
-A1UdDgQWBBSaryl6wBE1NSZRMADDav5A1a7WPDAPBgNVHRMBAf8EBTADAQH/MA4G
-A1UdDwEB/wQEAwIBBjAKBggqhkjOPQQDAwNnADBkAjAXCfOHiFBar8jAQr9HX/Vs
-aobgxCd05DhT1wV/GzTjxi+zygk8N53X57hG8f2h4nECMEJZh0PUUd+60wkyWs6I
-flc9nF9Ca/UHLbXwgpP5WW+uZPpY5Yse42O+tYHNbwKMeQ==
------END CERTIFICATE-----
-
-# Amazon Root CA 1
------BEGIN CERTIFICATE-----
-MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsF
-ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6
-b24gUm9vdCBDQSAxMB4XDTE1MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTEL
-MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv
-b3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALJ4gHHKeNXj
-ca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgHFzZM
-9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qw
-IFAGbHrQgLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6
-VOujw5H5SNz/0egwLX0tdHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L
-93FcXmn/6pUCyziKrlA4b9v7LWIbxcceVOF34GfID5yHI9Y/QCB/IIDEgEw+OyQm
-jgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC
-AYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3DQEBCwUA
-A4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDI
-U5PMCCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/msv0tadQ1wUs
-N+gDS63pYaACbvXy8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vv
-o/ufQJVtMVT8QtPHRh8jrdkPSHCa2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU
-5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2xJNDd2ZhwLnoQdeXeGADbkpy
-rqXRfboQnoZsG4q5WTP468SQvvG5
------END CERTIFICATE-----
-
-# Amazon Root CA 2
------BEGIN CERTIFICATE-----
-MIIFQTCCAymgAwIBAgITBmyf0pY1hp8KD+WGePhbJruKNzANBgkqhkiG9w0BAQwF
-ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6
-b24gUm9vdCBDQSAyMB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTEL
-MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv
-b3QgQ0EgMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK2Wny2cSkxK
-gXlRmeyKy2tgURO8TW0G/LAIjd0ZEGrHJgw12MBvIITplLGbhQPDW9tK6Mj4kHbZ
-W0/jTOgGNk3Mmqw9DJArktQGGWCsN0R5hYGCrVo34A3MnaZMUnbqQ523BNFQ9lXg
-1dKmSYXpN+nKfq5clU1Imj+uIFptiJXZNLhSGkOQsL9sBbm2eLfq0OQ6PBJTYv9K
-8nu+NQWpEjTj82R0Yiw9AElaKP4yRLuH3WUnAnE72kr3H9rN9yFVkE8P7K6C4Z9r
-2UXTu/Bfh+08LDmG2j/e7HJV63mjrdvdfLC6HM783k81ds8P+HgfajZRRidhW+me
-z/CiVX18JYpvL7TFz4QuK/0NURBs+18bvBt+xa47mAExkv8LV/SasrlX6avvDXbR
-8O70zoan4G7ptGmh32n2M8ZpLpcTnqWHsFcQgTfJU7O7f/aS0ZzQGPSSbtqDT6Zj
-mUyl+17vIWR6IF9sZIUVyzfpYgwLKhbcAS4y2j5L9Z469hdAlO+ekQiG+r5jqFoz
-7Mt0Q5X5bGlSNscpb/xVA1wf+5+9R+vnSUeVC06JIglJ4PVhHvG/LopyboBZ/1c6
-+XUyo05f7O0oYtlNc/LMgRdg7c3r3NunysV+Ar3yVAhU/bQtCSwXVEqY0VThUWcI
-0u1ufm8/0i2BWSlmy5A5lREedCf+3euvAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMB
-Af8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSwDPBMMPQFWAJI/TPlUq9LhONm
-UjANBgkqhkiG9w0BAQwFAAOCAgEAqqiAjw54o+Ci1M3m9Zh6O+oAA7CXDpO8Wqj2
-LIxyh6mx/H9z/WNxeKWHWc8w4Q0QshNabYL1auaAn6AFC2jkR2vHat+2/XcycuUY
-+gn0oJMsXdKMdYV2ZZAMA3m3MSNjrXiDCYZohMr/+c8mmpJ5581LxedhpxfL86kS
-k5Nrp+gvU5LEYFiwzAJRGFuFjWJZY7attN6a+yb3ACfAXVU3dJnJUH/jWS5E4ywl
-7uxMMne0nxrpS10gxdr9HIcWxkPo1LsmmkVwXqkLN1PiRnsn/eBG8om3zEK2yygm
-btmlyTrIQRNg91CMFa6ybRoVGld45pIq2WWQgj9sAq+uEjonljYE1x2igGOpm/Hl
-urR8FLBOybEfdF849lHqm/osohHUqS0nGkWxr7JOcQ3AWEbWaQbLU8uz/mtBzUF+
-fUwPfHJ5elnNXkoOrJupmHN5fLT0zLm4BwyydFy4x2+IoZCn9Kr5v2c69BoVYh63
-n749sSmvZ6ES8lgQGVMDMBu4Gon2nL2XA46jCfMdiyHxtN/kHNGfZQIG6lzWE7OE
-76KlXIx3KadowGuuQNKotOrN8I1LOJwZmhsoVLiJkO/KdYE+HvJkJMcYr07/R54H
-9jVlpNMKVv/1F2Rs76giJUmTtt8AF9pYfl3uxRuw0dFfIRDH+fO6AgonB8Xx1sfT
-4PsJYGw=
------END CERTIFICATE-----
-
-# Amazon Root CA 3
------BEGIN CERTIFICATE-----
-MIIBtjCCAVugAwIBAgITBmyf1XSXNmY/Owua2eiedgPySjAKBggqhkjOPQQDAjA5
-MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24g
-Um9vdCBDQSAzMB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkG
-A1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJvb3Qg
-Q0EgMzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABCmXp8ZBf8ANm+gBG1bG8lKl
-ui2yEujSLtf6ycXYqm0fc4E7O5hrOXwzpcVOho6AF2hiRVd9RFgdszflZwjrZt6j
-QjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSr
-ttvXBp43rDCGB5Fwx5zEGbF4wDAKBggqhkjOPQQDAgNJADBGAiEA4IWSoxe3jfkr
-BqWTrBqYaGFy+uGh0PsceGCmQ5nFuMQCIQCcAu/xlJyzlvnrxir4tiz+OpAUFteM
-YyRIHN8wfdVoOw==
------END CERTIFICATE-----
-
-# Amazon Root CA 4
------BEGIN CERTIFICATE-----
-MIIB8jCCAXigAwIBAgITBmyf18G7EEwpQ+Vxe3ssyBrBDjAKBggqhkjOPQQDAzA5
-MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24g
-Um9vdCBDQSA0MB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkG
-A1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJvb3Qg
-Q0EgNDB2MBAGByqGSM49AgEGBSuBBAAiA2IABNKrijdPo1MN/sGKe0uoe0ZLY7Bi
-9i0b2whxIdIA6GO9mif78DluXeo9pcmBqqNbIJhFXRbb/egQbeOc4OO9X4Ri83Bk
-M6DLJC9wuoihKqB1+IGuYgbEgds5bimwHvouXKNCMEAwDwYDVR0TAQH/BAUwAwEB
-/zAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0OBBYEFNPsxzplbszh2naaVvuc84ZtV+WB
-MAoGCCqGSM49BAMDA2gAMGUCMDqLIfG9fhGt0O9Yli/W651+kI0rz2ZVwyzjKKlw
-CkcO8DdZEv8tmZQoTipPNU0zWgIxAOp1AE47xDqUEpHJWEadIRNyp4iciuRMStuW
-1KyLa2tJElMzrdfkviT8tQp21KW8EA==
------END CERTIFICATE-----
-
-# Atos TrustedRoot 2011
------BEGIN CERTIFICATE-----
-MIIDdzCCAl+gAwIBAgIIXDPLYixfszIwDQYJKoZIhvcNAQELBQAwPDEeMBwGA1UE
-AwwVQXRvcyBUcnVzdGVkUm9vdCAyMDExMQ0wCwYDVQQKDARBdG9zMQswCQYDVQQG
-EwJERTAeFw0xMTA3MDcxNDU4MzBaFw0zMDEyMzEyMzU5NTlaMDwxHjAcBgNVBAMM
-FUF0b3MgVHJ1c3RlZFJvb3QgMjAxMTENMAsGA1UECgwEQXRvczELMAkGA1UEBhMC
-REUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCVhTuXbyo7LjvPpvMp
-Nb7PGKw+qtn4TaA+Gke5vJrf8v7MPkfoepbCJI419KkM/IL9bcFyYie96mvr54rM
-VD6QUM+A1JX76LWC1BTFtqlVJVfbsVD2sGBkWXppzwO3bw2+yj5vdHLqqjAqc2K+
-SZFhyBH+DgMq92og3AIVDV4VavzjgsG1xZ1kCWyjWZgHJ8cblithdHFsQ/H3NYkQ
-4J7sVaE3IqKHBAUsR320HLliKWYoyrfhk/WklAOZuXCFteZI6o1Q/NnezG8HDt0L
-cp2AMBYHlT8oDv3FdU9T1nSatCQujgKRz3bFmx5VdJx4IbHwLfELn8LVlhgf8FQi
-eowHAgMBAAGjfTB7MB0GA1UdDgQWBBSnpQaxLKYJYO7Rl+lwrrw7GWzbITAPBgNV
-HRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFKelBrEspglg7tGX6XCuvDsZbNshMBgG
-A1UdIAQRMA8wDQYLKwYBBAGwLQMEAQEwDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3
-DQEBCwUAA4IBAQAmdzTblEiGKkGdLD4GkGDEjKwLVLgfuXvTBznk+j57sj1O7Z8j
-vZfza1zv7v1Apt+hk6EKhqzvINB5Ab149xnYJDE0BAGmuhWawyfc2E8PzBhj/5kP
-DpFrdRbhIfzYJsdHt6bPWHJxfrrhTZVHO8mvbaG0weyJ9rQPOLXiZNwlz6bb65pc
-maHFCN795trV1lpFDMS3wrUU77QR/w4VtfX128a961qn8FYiqTxlVMYVqL2Gns2D
-lmh6cYGJ4Qvh6hEbaAjMaZ7snkGeRDImeuKHCnE96+RapNLbxc3G3mB/ufNPRJLv
-KrcYPqcZ2Qt9sTdBQrC6YB3y/gkRsPCHe6ed
------END CERTIFICATE-----
-
-# Autoridad de Certificacion Firmaprofesional CIF A62634068
------BEGIN CERTIFICATE-----
-MIIGFDCCA/ygAwIBAgIIU+w77vuySF8wDQYJKoZIhvcNAQEFBQAwUTELMAkGA1UE
-BhMCRVMxQjBABgNVBAMMOUF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIEZpcm1h
-cHJvZmVzaW9uYWwgQ0lGIEE2MjYzNDA2ODAeFw0wOTA1MjAwODM4MTVaFw0zMDEy
-MzEwODM4MTVaMFExCzAJBgNVBAYTAkVTMUIwQAYDVQQDDDlBdXRvcmlkYWQgZGUg
-Q2VydGlmaWNhY2lvbiBGaXJtYXByb2Zlc2lvbmFsIENJRiBBNjI2MzQwNjgwggIi
-MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKlmuO6vj78aI14H9M2uDDUtd9
-thDIAl6zQyrET2qyyhxdKJp4ERppWVevtSBC5IsP5t9bpgOSL/UR5GLXMnE42QQM
-cas9UX4PB99jBVzpv5RvwSmCwLTaUbDBPLutN0pcyvFLNg4kq7/DhHf9qFD0sefG
-L9ItWY16Ck6WaVICqjaY7Pz6FIMMNx/Jkjd/14Et5cS54D40/mf0PmbR0/RAz15i
-NA9wBj4gGFrO93IbJWyTdBSTo3OxDqqHECNZXyAFGUftaI6SEspd/NYrspI8IM/h
-X68gvqB2f3bl7BqGYTM+53u0P6APjqK5am+5hyZvQWyIplD9amML9ZMWGxmPsu2b
-m8mQ9QEM3xk9Dz44I8kvjwzRAv4bVdZO0I08r0+k8/6vKtMFnXkIoctXMbScyJCy
-Z/QYFpM6/EfY0XiWMR+6KwxfXZmtY4laJCB22N/9q06mIqqdXuYnin1oKaPnirja
-EbsXLZmdEyRG98Xi2J+Of8ePdG1asuhy9azuJBCtLxTa/y2aRnFHvkLfuwHb9H/T
-KI8xWVvTyQKmtFLKbpf7Q8UIJm+K9Lv9nyiqDdVF8xM6HdjAeI9BZzwelGSuewvF
-6NkBiDkal4ZkQdU7hwxu+g/GvUgUvzlN1J5Bto+WHWOWk9mVBngxaJ43BjuAiUVh
-OSPHG0SjFeUc+JIwuwIDAQABo4HvMIHsMBIGA1UdEwEB/wQIMAYBAf8CAQEwDgYD
-VR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRlzeurNR4APn7VdMActHNHDhpkLzCBpgYD
-VR0gBIGeMIGbMIGYBgRVHSAAMIGPMC8GCCsGAQUFBwIBFiNodHRwOi8vd3d3LmZp
-cm1hcHJvZmVzaW9uYWwuY29tL2NwczBcBggrBgEFBQcCAjBQHk4AUABhAHMAZQBv
-ACAAZABlACAAbABhACAAQgBvAG4AYQBuAG8AdgBhACAANAA3ACAAQgBhAHIAYwBl
-AGwAbwBuAGEAIAAwADgAMAAxADcwDQYJKoZIhvcNAQEFBQADggIBABd9oPm03cXF
-661LJLWhAqvdpYhKsg9VSytXjDvlMd3+xDLx51tkljYyGOylMnfX40S2wBEqgLk9
-am58m9Ot/MPWo+ZkKXzR4Tgegiv/J2Wv+xYVxC5xhOW1//qkR71kMrv2JYSiJ0L1
-ILDCExARzRAVukKQKtJE4ZYm6zFIEv0q2skGz3QeqUvVhyj5eTSSPi5E6PaPT481
-PyWzOdxjKpBrIF/EUhJOlywqrJ2X3kjyo2bbwtKDlaZmp54lD+kLM5FlClrD2VQS
-3a/DTg4fJl4N3LON7NWBcN7STyQF82xO9UxJZo3R/9ILJUFI/lGExkKvgATP0H5k
-SeTy36LssUzAKh3ntLFlosS88Zj0qnAHY7S42jtM+kAiMFsRpvAFDsYCA0irhpuF
-3dvd6qJ2gHN99ZwExEWN57kci57q13XRcrHedUTnQn3iV2t93Jm8PYMo6oCTjcVM
-ZcFwgbg4/EMxsvYDNEeyrPsiBsse3RdHHF9mudMaotoRsaS8I8nkvof/uZS2+F0g
-StRf571oe2XyFR7SOqkt6dhrJKyXWERHrVkY8SFlcN7ONGCoQPHzPKTDKCOM/icz
-Q0CgFzzr6juwcqajuUpLXhZI9LK8yIySxZ2frHI2vDSANGupi5LAuBft7HZT9SQB
-jLMi6Et8Vcad+qMUu2WFbm5PEn4KPJ2V
------END CERTIFICATE-----
-
-# Baltimore CyberTrust Root
------BEGIN CERTIFICATE-----
-MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJ
-RTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYD
-VQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoX
-DTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMCSUUxEjAQBgNVBAoTCUJhbHRpbW9y
-ZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZQmFsdGltb3JlIEN5YmVy
-VHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKMEuyKr
-mD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjr
-IZ3AQSsBUnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeK
-mpYcqWe4PwzV9/lSEy/CG9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSu
-XmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9XbIGevOF6uvUA65ehD5f/xXtabz5OTZy
-dc93Uk3zyZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjprl3RjM71oGDHweI12v/ye
-jl0qhqdNkNwnGjkCAwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoIVDaGezq1
-BE3wMBIGA1UdEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3
-DQEBBQUAA4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT92
-9hkTI7gQCvlYpNRhcL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3Wgx
-jkzSswF07r51XgdIGn9w/xZchMB5hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/oCr0
-Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsaY71k5h+3zvDyny67G7fyUIhz
-ksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9HRCwBXbsdtTLS
-R9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp
------END CERTIFICATE-----
-
-# Buypass Class 2 Root CA
------BEGIN CERTIFICATE-----
-MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEd
-MBsGA1UECgwUQnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3Mg
-Q2xhc3MgMiBSb290IENBMB4XDTEwMTAyNjA4MzgwM1oXDTQwMTAyNjA4MzgwM1ow
-TjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBhc3MgQVMtOTgzMTYzMzI3MSAw
-HgYDVQQDDBdCdXlwYXNzIENsYXNzIDIgUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEB
-BQADggIPADCCAgoCggIBANfHXvfBB9R3+0Mh9PT1aeTuMgHbo4Yf5FkNuud1g1Lr
-6hxhFUi7HQfKjK6w3Jad6sNgkoaCKHOcVgb/S2TwDCo3SbXlzwx87vFKu3MwZfPV
-L4O2fuPn9Z6rYPnT8Z2SdIrkHJasW4DptfQxh6NR/Md+oW+OU3fUl8FVM5I+GC91
-1K2GScuVr1QGbNgGE41b/+EmGVnAJLqBcXmQRFBoJJRfuLMR8SlBYaNByyM21cHx
-MlAQTn/0hpPshNOOvEu/XAFOBz3cFIqUCqTqc/sLUegTBxj6DvEr0VQVfTzh97QZ
-QmdiXnfgolXsttlpF9U6r0TtSsWe5HonfOV116rLJeffawrbD02TTqigzXsu8lkB
-arcNuAeBfos4GzjmCleZPe4h6KP1DBbdi+w0jpwqHAAVF41og9JwnxgIzRFo1clr
-Us3ERo/ctfPYV3Me6ZQ5BL/T3jjetFPsaRyifsSP5BtwrfKi+fv3FmRmaZ9JUaLi
-FRhnBkp/1Wy1TbMz4GHrXb7pmA8y1x1LPC5aAVKRCfLf6o3YBkBjqhHk/sM3nhRS
-P/TizPJhk9H9Z2vXUq6/aKtAQ6BXNVN48FP4YUIHZMbXb5tMOA1jrGKvNouicwoN
-9SG9dKpN6nIDSdvHXx1iY8f93ZHsM+71bbRuMGjeyNYmsHVee7QHIJihdjK4TWxP
-AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFMmAd+BikoL1Rpzz
-uvdMw964o605MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAU18h
-9bqwOlI5LJKwbADJ784g7wbylp7ppHR/ehb8t/W2+xUbP6umwHJdELFx7rxP462s
-A20ucS6vxOOto70MEae0/0qyexAQH6dXQbLArvQsWdZHEIjzIVEpMMpghq9Gqx3t
-OluwlN5E40EIosHsHdb9T7bWR9AUC8rmyrV7d35BH16Dx7aMOZawP5aBQW9gkOLo
-+fsicdl9sz1Gv7SEr5AcD48Saq/v7h56rgJKihcrdv6sVIkkLE8/trKnToyokZf7
-KcZ7XC25y2a2t6hbElGFtQl+Ynhw/qlqYLYdDnkM/crqJIByw5c/8nerQyIKx+u2
-DISCLIBrQYoIwOula9+ZEsuK1V6ADJHgJgg2SMX6OBE1/yWDLfJ6v9r9jv6ly0Us
-H8SIU653DtmadsWOLB2jutXsMq7Aqqz30XpN69QH4kj3Io6wpJ9qzo6ysmD0oyLQ
-I+uUWnpp3Q+/QFesa1lQ2aOZ4W7+jQF5JyMV3pKdewlNWudLSDBaGOYKbeaP4NK7
-5t98biGCwWg5TbSYWGZizEqQXsP6JwSxeRV0mcy+rSDeJmAc61ZRpqPq5KM/p/9h
-3PFaTWwyI0PurKju7koSCTxdccK+efrCh2gdC/1cacwG0Jp9VJkqyTkaGa9LKkPz
-Y11aWOIv4x3kqdbQCtCev9eBCfHJxyYNrJgWVqA=
------END CERTIFICATE-----
-
-# Buypass Class 3 Root CA
------BEGIN CERTIFICATE-----
-MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEd
-MBsGA1UECgwUQnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3Mg
-Q2xhc3MgMyBSb290IENBMB4XDTEwMTAyNjA4Mjg1OFoXDTQwMTAyNjA4Mjg1OFow
-TjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBhc3MgQVMtOTgzMTYzMzI3MSAw
-HgYDVQQDDBdCdXlwYXNzIENsYXNzIDMgUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEB
-BQADggIPADCCAgoCggIBAKXaCpUWUOOV8l6ddjEGMnqb8RB2uACatVI2zSRHsJ8Y
-ZLya9vrVediQYkwiL944PdbgqOkcLNt4EemOaFEVcsfzM4fkoF0LXOBXByow9c3E
-N3coTRiR5r/VUv1xLXA+58bEiuPwKAv0dpihi4dVsjoT/Lc+JzeOIuOoTyrvYLs9
-tznDDgFHmV0ST9tD+leh7fmdvhFHJlsTmKtdFoqwNxxXnUX/iJY2v7vKB3tvh2PX
-0DJq1l1sDPGzbjniazEuOQAnFN44wOwZZoYS6J1yFhNkUsepNxz9gjDthBgd9K5c
-/3ATAOux9TN6S9ZV+AWNS2mw9bMoNlwUxFFzTWsL8TQH2xc519woe2v1n/MuwU8X
-KhDzzMro6/1rqy6any2CbgTUUgGTLT2G/H783+9CHaZr77kgxve9oKeV/afmiSTY
-zIw0bOIjL9kSGiG5VZFvC5F5GQytQIgLcOJ60g7YaEi7ghM5EFjp2CoHxhLbWNvS
-O1UQRwUVZ2J+GGOmRj8JDlQyXr8NYnon74Do29lLBlo3WiXQCBJ31G8JUJc9yB3D
-34xFMFbG02SrZvPAXpacw8Tvw3xrizp5f7NJzz3iiZ+gMEuFuZyUJHmPfWupRWgP
-K9Dx2hzLabjKSWJtyNBjYt1gD1iqj6G8BaVmos8bdrKEZLFMOVLAMLrwjEsCsLa3
-AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFEe4zf/lb+74suwv
-Tg75JbCOPGvDMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAACAj
-QTUEkMJAYmDv4jVM1z+s4jSQuKFvdvoWFqRINyzpkMLyPPgKn9iB5btb2iUspKdV
-cSQy9sgL8rxq+JOssgfCX5/bzMiKqr5qb+FJEMwx14C7u8jYog5kV+qi9cKpMRXS
-IGrs/CIBKM+GuIAeqcwRpTzyFrNHnfzSgCHEy9BHcEGhyoMZCCxt8l13nIoUE9Q2
-HJLw5QY33KbmkJs4j1xrG0aGQ0JfPgEHU1RdZX33inOhmlRaHylDFCfChQ+1iHsa
-O5S3HWCntZznKWlXWpuTekMwGwPXYshApqr8ZORK15FTAaggiG6cX0S5y2CBNOxv
-033aSF/rtJC8LakcC6wc1aJoIIAE1vyxjy+7SjENSoYc6+I2KSb12tjE8nVhz36u
-dmNKekBlk4f4HoCMhuWG1o8O/FMsYOgWYRqiPkN7zTlgVGr18okmAWiDSKIz6MkE
-kbIRNBE+6tBDGR8Dk5AM/1E9V/RBbuHLoL7ryWPNbczk+DaqaJ3tvV2XcEQNtg41
-3OEMXbugUZTLfhbrES+jkkXITHHZvMmZUldGL1DPvTVp9D0VzgalLA8+9oG6lLvD
-u79leNKGef9JOxqDDPDeeOzI8k1MGt6CKfjBWtrt7uYnXuhF0J0cUahoq0Tj0Itq
-4/g7u9xN12TyUb7mqqta6THuBrxzvxNiCp/HuZc=
------END CERTIFICATE-----
-
-# CA Disig Root R2
------BEGIN CERTIFICATE-----
-MIIFaTCCA1GgAwIBAgIJAJK4iNuwisFjMA0GCSqGSIb3DQEBCwUAMFIxCzAJBgNV
-BAYTAlNLMRMwEQYDVQQHEwpCcmF0aXNsYXZhMRMwEQYDVQQKEwpEaXNpZyBhLnMu
-MRkwFwYDVQQDExBDQSBEaXNpZyBSb290IFIyMB4XDTEyMDcxOTA5MTUzMFoXDTQy
-MDcxOTA5MTUzMFowUjELMAkGA1UEBhMCU0sxEzARBgNVBAcTCkJyYXRpc2xhdmEx
-EzARBgNVBAoTCkRpc2lnIGEucy4xGTAXBgNVBAMTEENBIERpc2lnIFJvb3QgUjIw
-ggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCio8QACdaFXS1tFPbCw3Oe
-NcJxVX6B+6tGUODBfEl45qt5WDza/3wcn9iXAng+a0EE6UG9vgMsRfYvZNSrXaNH
-PWSb6WiaxswbP7q+sos0Ai6YVRn8jG+qX9pMzk0DIaPY0jSTVpbLTAwAFjxfGs3I
-x2ymrdMxp7zo5eFm1tL7A7RBZckQrg4FY8aAamkw/dLukO8NJ9+flXP04SXabBbe
-QTg06ov80egEFGEtQX6sx3dOy1FU+16SGBsEWmjGycT6txOgmLcRK7fWV8x8nhfR
-yyX+hk4kLlYMeE2eARKmK6cBZW58Yh2EhN/qwGu1pSqVg8NTEQxzHQuyRpDRQjrO
-QG6Vrf/GlK1ul4SOfW+eioANSW1z4nuSHsPzwfPrLgVv2RvPN3YEyLRa5Beny912
-H9AZdugsBbPWnDTYltxhh5EF5EQIM8HauQhl1K6yNg3ruji6DOWbnuuNZt2Zz9aJ
-QfYEkoopKW1rOhzndX0CcQ7zwOe9yxndnWCywmZgtrEE7snmhrmaZkCo5xHtgUUD
-i/ZnWejBBhG93c+AAk9lQHhcR1DIm+YfgXvkRKhbhZri3lrVx/k6RGZL5DJUfORs
-nLMOPReisjQS1n6yqEm70XooQL6iFh/f5DcfEXP7kAplQ6INfPgGAVUzfbANuPT1
-rqVCV3w2EYx7XsQDnYx5nQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1Ud
-DwEB/wQEAwIBBjAdBgNVHQ4EFgQUtZn4r7CU9eMg1gqtzk5WpC5uQu0wDQYJKoZI
-hvcNAQELBQADggIBACYGXnDnZTPIgm7ZnBc6G3pmsgH2eDtpXi/q/075KMOYKmFM
-tCQSin1tERT3nLXK5ryeJ45MGcipvXrA1zYObYVybqjGom32+nNjf7xueQgcnYqf
-GopTpti72TVVsRHFqQOzVju5hJMiXn7B9hJSi+osZ7z+Nkz1uM/Rs0mSO9MpDpkb
-lvdhuDvEK7Z4bLQjb/D907JedR+Zlais9trhxTF7+9FGs9K8Z7RiVLoJ92Owk6Ka
-+elSLotgEqv89WBW7xBci8QaQtyDW2QOy7W81k/BfDxujRNt+3vrMNDcTa/F1bal
-TFtxyegxvug4BkihGuLq0t4SOVga/4AOgnXmt8kHbA7v/zjxmHHEt38OFdAlab0i
-nSvtBfZGR6ztwPDUO+Ls7pZbkBNOHlY667DvlruWIxG68kOGdGSVyCh13x01utI3
-gzhTODY7z2zp+WsO0PsE6E9312UBeIYMej4hYvF/Y3EMyZ9E26gnonW+boE+18Dr
-G5gPcFw0sorMwIUY6256s/daoQe/qUKS82Ail+QUoQebTnbAjn39pCXHR+3/H3Os
-zMOl6W8KjptlwlCFtaOgUxLMVYdh84GuEEZhvUQhuMI9dM9+JDX6HAcOmz0iyu8x
-L4ysEr3vQCj8KWefshNPZiTEUxnpHikV7+ZtsH8tZ/3zbBt1RqPlShfppNcL
------END CERTIFICATE-----
-
-# Certigna
------BEGIN CERTIFICATE-----
-MIIDqDCCApCgAwIBAgIJAP7c4wEPyUj/MA0GCSqGSIb3DQEBBQUAMDQxCzAJBgNV
-BAYTAkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hMB4X
-DTA3MDYyOTE1MTMwNVoXDTI3MDYyOTE1MTMwNVowNDELMAkGA1UEBhMCRlIxEjAQ
-BgNVBAoMCURoaW15b3RpczERMA8GA1UEAwwIQ2VydGlnbmEwggEiMA0GCSqGSIb3
-DQEBAQUAA4IBDwAwggEKAoIBAQDIaPHJ1tazNHUmgh7stL7qXOEm7RFHYeGifBZ4
-QCHkYJ5ayGPhxLGWkv8YbWkj4Sti993iNi+RB7lIzw7sebYs5zRLcAglozyHGxny
-gQcPOJAZ0xH+hrTy0V4eHpbNgGzOOzGTtvKg0KmVEn2lmsxryIRWijOp5yIVUxbw
-zBfsV1/pogqYCd7jX5xv3EjjhQsVWqa6n6xI4wmy9/Qy3l40vhx4XUJbzg4ij02Q
-130yGLMLLGq/jj8UEYkgDncUtT2UCIf3JR7VsmAA7G8qKCVuKj4YYxclPz5EIBb2
-JsglrgVKtOdjLPOMFlN+XPsRGgjBRmKfIrjxwo1p3Po6WAbfAgMBAAGjgbwwgbkw
-DwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUGu3+QTmQtCRZvgHyUtVF9lo53BEw
-ZAYDVR0jBF0wW4AUGu3+QTmQtCRZvgHyUtVF9lo53BGhOKQ2MDQxCzAJBgNVBAYT
-AkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hggkA/tzj
-AQ/JSP8wDgYDVR0PAQH/BAQDAgEGMBEGCWCGSAGG+EIBAQQEAwIABzANBgkqhkiG
-9w0BAQUFAAOCAQEAhQMeknH2Qq/ho2Ge6/PAD/Kl1NqV5ta+aDY9fm4fTIrv0Q8h
-bV6lUmPOEvjvKtpv6zf+EwLHyzs+ImvaYS5/1HI93TDhHkxAGYwP15zRgzB7mFnc
-fca5DClMoTOi62c6ZYTTluLtdkVwj7Ur3vkj1kluPBS1xp81HlDQwY9qcEQCYsuu
-HWhBp6pX6FOqB9IG9tUUBguRA3UsbHK1YZWaDYu5Def131TN3ubY1gkIl2PlwS6w
-t0QmwCbAr1UwnjvVNioZBPRcHv/PLLf/0P2HQBHVESO7SMAhqaQoLf0V+LBOK/Qw
-WyH8EZE0vkHve52Xdf+XlcCWWC/qu0bXu+TZLg==
------END CERTIFICATE-----
-
-# Certinomis - Root CA
------BEGIN CERTIFICATE-----
-MIIFkjCCA3qgAwIBAgIBATANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJGUjET
-MBEGA1UEChMKQ2VydGlub21pczEXMBUGA1UECxMOMDAwMiA0MzM5OTg5MDMxHTAb
-BgNVBAMTFENlcnRpbm9taXMgLSBSb290IENBMB4XDTEzMTAyMTA5MTcxOFoXDTMz
-MTAyMTA5MTcxOFowWjELMAkGA1UEBhMCRlIxEzARBgNVBAoTCkNlcnRpbm9taXMx
-FzAVBgNVBAsTDjAwMDIgNDMzOTk4OTAzMR0wGwYDVQQDExRDZXJ0aW5vbWlzIC0g
-Um9vdCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANTMCQosP5L2
-fxSeC5yaah1AMGT9qt8OHgZbn1CF6s2Nq0Nn3rD6foCWnoR4kkjW4znuzuRZWJfl
-LieY6pOod5tK8O90gC3rMB+12ceAnGInkYjwSond3IjmFPnVAy//ldu9n+ws+hQV
-WZUKxkd8aRi5pwP5ynapz8dvtF4F/u7BUrJ1Mofs7SlmO/NKFoL21prbcpjp3vDF
-TKWrteoB4owuZH9kb/2jJZOLyKIOSY008B/sWEUuNKqEUL3nskoTuLAPrjhdsKkb
-5nPJWqHZZkCqqU2mNAKthH6yI8H7KsZn9DS2sJVqM09xRLWtwHkziOC/7aOgFLSc
-CbAK42C++PhmiM1b8XcF4LVzbsF9Ri6OSyemzTUK/eVNfaoqoynHWmgE6OXWk6Ri
-wsXm9E/G+Z8ajYJJGYrKWUM66A0ywfRMEwNvbqY/kXPLynNvEiCL7sCCeN5LLsJJ
-wx3tFvYk9CcbXFcx3FXuqB5vbKziRcxXV4p1VxngtViZSTYxPDMBbRZKzbgqg4SG
-m/lg0h9tkQPTYKbVPZrdd5A9NaSfD171UkRpucC63M9933zZxKyGIjK8e2uR73r4
-F2iw4lNVYC2vPsKD2NkJK/DAZNuHi5HMkesE/Xa0lZrmFAYb1TQdvtj/dBxThZng
-WVJKYe2InmtJiUZ+IFrZ50rlau7SZRFDAgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIB
-BjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTvkUz1pcMw6C8I6tNxIqSSaHh0
-2TAfBgNVHSMEGDAWgBTvkUz1pcMw6C8I6tNxIqSSaHh02TANBgkqhkiG9w0BAQsF
-AAOCAgEAfj1U2iJdGlg+O1QnurrMyOMaauo++RLrVl89UM7g6kgmJs95Vn6RHJk/
-0KGRHCwPT5iVWVO90CLYiF2cN/z7ZMF4jIuaYAnq1fohX9B0ZedQxb8uuQsLrbWw
-F6YSjNRieOpWauwK0kDDPAUwPk2Ut59KA9N9J0u2/kTO+hkzGm2kQtHdzMjI1xZS
-g081lLMSVX3l4kLr5JyTCcBMWwerx20RoFAXlCOotQqSD7J6wWAsOMwaplv/8gzj
-qh8c3LigkyfeY+N/IZ865Z764BNqdeuWXGKRlI5nU7aJ+BIJy29SWwNyhlCVCNSN
-h4YVH5Uk2KRvms6knZtt0rJ2BobGVgjF6wnaNsIbW0G+YSrjcOa4pvi2WsS9Iff/
-ql+hbHY5ZtbqTFXhADObE5hjyW/QASAJN1LnDE8+zbz1X5YnpyACleAu6AdBBR8V
-btaw5BngDwKTACdyxYvRVB9dSsNAl35VpnzBMwQUAR1JIGkLGZOdblgi90AMRgwj
-Y/M50n92Uaf0yKHxDHYiI0ZSKS3io0EHVmmY0gUJvGnHWmHNj4FgFU2A3ZDifcRQ
-8ow7bkrHxuaAKzyBvBGAFhAn1/DNP3nMcyrDflOR1m749fPH0FFNjkulW+YZFzvW
-gQncItzujrnEj1PhZ7szuIgVRs/taTX/dQ1G885x4cVrhkIGuUE=
------END CERTIFICATE-----
-
-# Certplus Class 2 Primary CA
------BEGIN CERTIFICATE-----
-MIIDkjCCAnqgAwIBAgIRAIW9S/PY2uNp9pTXX8OlRCMwDQYJKoZIhvcNAQEFBQAw
-PTELMAkGA1UEBhMCRlIxETAPBgNVBAoTCENlcnRwbHVzMRswGQYDVQQDExJDbGFz
-cyAyIFByaW1hcnkgQ0EwHhcNOTkwNzA3MTcwNTAwWhcNMTkwNzA2MjM1OTU5WjA9
-MQswCQYDVQQGEwJGUjERMA8GA1UEChMIQ2VydHBsdXMxGzAZBgNVBAMTEkNsYXNz
-IDIgUHJpbWFyeSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANxQ
-ltAS+DXSCHh6tlJw/W/uz7kRy1134ezpfgSN1sxvc0NXYKwzCkTsA18cgCSR5aiR
-VhKC9+Ar9NuuYS6JEI1rbLqzAr3VNsVINyPi8Fo3UjMXEuLRYE2+L0ER4/YXJQyL
-kcAbmXuZVg2v7tK8R1fjeUl7NIknJITesezpWE7+Tt9avkGtrAjFGA7v0lPubNCd
-EgETjdyAYveVqUSISnFOYFWe2yMZeVYHDD9jC1yw4r5+FfyUM1hBOHTE4Y+L3yas
-H7WLO7dDWWuwJKZtkIvEcupdM5i3y95ee++U8Rs+yskhwcWYAqqi9lt3m/V+llU0
-HGdpwPFC40es/CgcZlUCAwEAAaOBjDCBiTAPBgNVHRMECDAGAQH/AgEKMAsGA1Ud
-DwQEAwIBBjAdBgNVHQ4EFgQU43Mt38sOKAze3bOkynm4jrvoMIkwEQYJYIZIAYb4
-QgEBBAQDAgEGMDcGA1UdHwQwMC4wLKAqoCiGJmh0dHA6Ly93d3cuY2VydHBsdXMu
-Y29tL0NSTC9jbGFzczIuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQCnVM+IRBnL39R/
-AN9WM2K191EBkOvDP9GIROkkXe/nFL0gt5o8AP5tn9uQ3Nf0YtaLcF3n5QRIqWh8
-yfFC82x/xXp8HVGIutIKPidd3i1RTtMTZGnkLuPT55sJmabglZvOGtd/vjzOUrMR
-FcEPF80Du5wlFbqidon8BvEY0JNLDnyCt6X09l/+7UCmnYR0ObncHoUW2ikbhiMA
-ybuJfm6AiB4vFLQDJKgybwOaRywwvlbGp0ICcBvqQNi6BQNwB6SW//1IMwrh3KWB
-kJtN3X3n57LNXMhqlfil9o3EXXgIvnsG1knPGTZQIy4I5p4FTUcY1Rbpsda2ENW7
-l7+ijrRU
------END CERTIFICATE-----
-
-# Certplus Root CA G1
------BEGIN CERTIFICATE-----
-MIIFazCCA1OgAwIBAgISESBVg+QtPlRWhS2DN7cs3EYRMA0GCSqGSIb3DQEBDQUA
-MD4xCzAJBgNVBAYTAkZSMREwDwYDVQQKDAhDZXJ0cGx1czEcMBoGA1UEAwwTQ2Vy
-dHBsdXMgUm9vdCBDQSBHMTAeFw0xNDA1MjYwMDAwMDBaFw0zODAxMTUwMDAwMDBa
-MD4xCzAJBgNVBAYTAkZSMREwDwYDVQQKDAhDZXJ0cGx1czEcMBoGA1UEAwwTQ2Vy
-dHBsdXMgUm9vdCBDQSBHMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB
-ANpQh7bauKk+nWT6VjOaVj0W5QOVsjQcmm1iBdTYj+eJZJ+622SLZOZ5KmHNr49a
-iZFluVj8tANfkT8tEBXgfs+8/H9DZ6itXjYj2JizTfNDnjl8KvzsiNWI7nC9hRYt
-6kuJPKNxQv4c/dMcLRC4hlTqQ7jbxofaqK6AJc96Jh2qkbBIb6613p7Y1/oA/caP
-0FG7Yn2ksYyy/yARujVjBYZHYEMzkPZHogNPlk2dT8Hq6pyi/jQu3rfKG3akt62f
-6ajUeD94/vI4CTYd0hYCyOwqaK/1jpTvLRN6HkJKHRUxrgwEV/xhc/MxVoYxgKDE
-EW4wduOU8F8ExKyHcomYxZ3MVwia9Az8fXoFOvpHgDm2z4QTd28n6v+WZxcIbekN
-1iNQMLAVdBM+5S//Ds3EC0pd8NgAM0lm66EYfFkuPSi5YXHLtaW6uOrc4nBvCGrc
-h2c0798wct3zyT8j/zXhviEpIDCB5BmlIOklynMxdCm+4kLV87ImZsdo/Rmz5yCT
-mehd4F6H50boJZwKKSTUzViGUkAksnsPmBIgJPaQbEfIDbsYIC7Z/fyL8inqh3SV
-4EJQeIQEQWGw9CEjjy3LKCHyamz0GqbFFLQ3ZU+V/YDI+HLlJWvEYLF7bY5KinPO
-WftwenMGE9nTdDckQQoRb5fc5+R+ob0V8rqHDz1oihYHAgMBAAGjYzBhMA4GA1Ud
-DwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSowcCbkahDFXxd
-Bie0KlHYlwuBsTAfBgNVHSMEGDAWgBSowcCbkahDFXxdBie0KlHYlwuBsTANBgkq
-hkiG9w0BAQ0FAAOCAgEAnFZvAX7RvUz1isbwJh/k4DgYzDLDKTudQSk0YcbX8ACh
-66Ryj5QXvBMsdbRX7gp8CXrc1cqh0DQT+Hern+X+2B50ioUHj3/MeXrKls3N/U/7
-/SMNkPX0XtPGYX2eEeAC7gkE2Qfdpoq3DIMku4NQkv5gdRE+2J2winq14J2by5BS
-S7CTKtQ+FjPlnsZlFT5kOwQ/2wyPX1wdaR+v8+khjPPvl/aatxm2hHSco1S1cE5j
-2FddUyGbQJJD+tZ3VTNPZNX70Cxqjm0lpu+F6ALEUz65noe8zDUa3qHpimOHZR4R
-Kttjd5cUvpoUmRGywO6wT/gUITJDT5+rosuoD6o7BlXGEilXCNQ314cnrUlZp5Gr
-RHpejXDbl85IULFzk/bwg2D5zfHhMf1bfHEhYxQUqq/F3pN+aLHsIqKqkHWetUNy
-6mSjhEv9DKgma3GX7lZjZuhCVPnHHd/Qj1vfyDBviP4NxDMcU6ij/UgQ8uQKTuEV
-V/xuZDDCVRHc6qnNSlSsKWNEz0pAoNZoWRsz+e86i9sgktxChL8Bq4fA1SCC28a5
-g4VCXA9DO2pJNdWY9BW/+mGBDAkgGNLQFwzLSABQ6XaCjGTXOqAHVcweMcDvOrRl
-++O/QmueD6i9a5jc2NvLi6Td11n0bt3+qsOR0C5CB8AMTVPNJLFMWx5R9N/pkvo=
------END CERTIFICATE-----
-
-# Certplus Root CA G2
------BEGIN CERTIFICATE-----
-MIICHDCCAaKgAwIBAgISESDZkc6uo+jF5//pAq/Pc7xVMAoGCCqGSM49BAMDMD4x
-CzAJBgNVBAYTAkZSMREwDwYDVQQKDAhDZXJ0cGx1czEcMBoGA1UEAwwTQ2VydHBs
-dXMgUm9vdCBDQSBHMjAeFw0xNDA1MjYwMDAwMDBaFw0zODAxMTUwMDAwMDBaMD4x
-CzAJBgNVBAYTAkZSMREwDwYDVQQKDAhDZXJ0cGx1czEcMBoGA1UEAwwTQ2VydHBs
-dXMgUm9vdCBDQSBHMjB2MBAGByqGSM49AgEGBSuBBAAiA2IABM0PW1aC3/BFGtat
-93nwHcmsltaeTpwftEIRyoa/bfuFo8XlGVzX7qY/aWfYeOKmycTbLXku54uNAm8x
-Ik0G42ByRZ0OQneezs/lf4WbGOT8zC5y0xaTTsqZY1yhBSpsBqNjMGEwDgYDVR0P
-AQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNqDYwJ5jtpMxjwj
-FNiPwyCrKGBZMB8GA1UdIwQYMBaAFNqDYwJ5jtpMxjwjFNiPwyCrKGBZMAoGCCqG
-SM49BAMDA2gAMGUCMHD+sAvZ94OX7PNVHdTcswYO/jOYnYs5kGuUIe22113WTNch
-p+e/IQ8rzfcq3IUHnQIxAIYUFuXcsGXCwI4Un78kFmjlvPl5adytRSv3tjFzzAal
-U5ORGpOucGpnutee5WEaXw==
------END CERTIFICATE-----
-
-# certSIGN ROOT CA
------BEGIN CERTIFICATE-----
-MIIDODCCAiCgAwIBAgIGIAYFFnACMA0GCSqGSIb3DQEBBQUAMDsxCzAJBgNVBAYT
-AlJPMREwDwYDVQQKEwhjZXJ0U0lHTjEZMBcGA1UECxMQY2VydFNJR04gUk9PVCBD
-QTAeFw0wNjA3MDQxNzIwMDRaFw0zMTA3MDQxNzIwMDRaMDsxCzAJBgNVBAYTAlJP
-MREwDwYDVQQKEwhjZXJ0U0lHTjEZMBcGA1UECxMQY2VydFNJR04gUk9PVCBDQTCC
-ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALczuX7IJUqOtdu0KBuqV5Do
-0SLTZLrTk+jUrIZhQGpgV2hUhE28alQCBf/fm5oqrl0Hj0rDKH/v+yv6efHHrfAQ
-UySQi2bJqIirr1qjAOm+ukbuW3N7LBeCgV5iLKECZbO9xSsAfsT8AzNXDe3i+s5d
-RdY4zTW2ssHQnIFKquSyAVwdj1+ZxLGt24gh65AIgoDzMKND5pCCrlUoSe1b16kQ
-OA7+j0xbm0bqQfWwCHTD0IgztnzXdN/chNFDDnU5oSVAKOp4yw4sLjmdjItuFhwv
-JoIQ4uNllAoEwF73XVv4EOLQunpL+943AAAaWyjj0pxzPjKHmKHJUS/X3qwzs08C
-AwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAcYwHQYDVR0O
-BBYEFOCMm9slSbPxfIbWskKHC9BroNnkMA0GCSqGSIb3DQEBBQUAA4IBAQA+0hyJ
-LjX8+HXd5n9liPRyTMks1zJO890ZeUe9jjtbkw9QSSQTaxQGcu8J06Gh40CEyecY
-MnQ8SG4Pn0vU9x7Tk4ZkVJdjclDVVc/6IJMCopvDI5NOFlV2oHB5bc0hH88vLbwZ
-44gx+FkagQnIl6Z0x2DEW8xXjrJ1/RsCCdtZb3KTafcxQdaIOL+Hsr0Wefmq5L6I
-Jd1hJyMctTEHBDa0GpC9oHRxUIltvBTjD4au8as+x6AJzKNI0eDbZOeStc+vckNw
-i/nDhDwTqn6Sm1dTk/pwwpEOMfmbZ13pljheX7NzTogVZ96edhBiIL5VaZVDADlN
-9u6wWk5JRFRYX0KD
------END CERTIFICATE-----
-
-# Certum Trusted Network CA 2
------BEGIN CERTIFICATE-----
-MIIF0jCCA7qgAwIBAgIQIdbQSk8lD8kyN/yqXhKN6TANBgkqhkiG9w0BAQ0FADCB
-gDELMAkGA1UEBhMCUEwxIjAgBgNVBAoTGVVuaXpldG8gVGVjaG5vbG9naWVzIFMu
-QS4xJzAlBgNVBAsTHkNlcnR1bSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEkMCIG
-A1UEAxMbQ2VydHVtIFRydXN0ZWQgTmV0d29yayBDQSAyMCIYDzIwMTExMDA2MDgz
-OTU2WhgPMjA0NjEwMDYwODM5NTZaMIGAMQswCQYDVQQGEwJQTDEiMCAGA1UEChMZ
-VW5pemV0byBUZWNobm9sb2dpZXMgUy5BLjEnMCUGA1UECxMeQ2VydHVtIENlcnRp
-ZmljYXRpb24gQXV0aG9yaXR5MSQwIgYDVQQDExtDZXJ0dW0gVHJ1c3RlZCBOZXR3
-b3JrIENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC9+Xj45tWA
-DGSdhhuWZGc/IjoedQF97/tcZ4zJzFxrqZHmuULlIEub2pt7uZld2ZuAS9eEQCsn
-0+i6MLs+CRqnSZXvK0AkwpfHp+6bJe+oCgCXhVqqndwpyeI1B+twTUrWwbNWuKFB
-OJvR+zF/j+Bf4bE/D44WSWDXBo0Y+aomEKsq09DRZ40bRr5HMNUuctHFY9rnY3lE
-fktjJImGLjQ/KUxSiyqnwOKRKIm5wFv5HdnnJ63/mgKXwcZQkpsCLL2puTRZCr+E
-Sv/f/rOf69me4Jgj7KZrdxYq28ytOxykh9xGc14ZYmhFV+SQgkK7QtbwYeDBoz1m
-o130GO6IyY0XRSmZMnUCMe4pJshrAua1YkV/NxVaI2iJ1D7eTiew8EAMvE0Xy02i
-sx7QBlrd9pPPV3WZ9fqGGmd4s7+W/jTcvedSVuWz5XV710GRBdxdaeOVDUO5/IOW
-OZV7bIBaTxNyxtd9KXpEulKkKtVBRgkg/iKgtlswjbyJDNXXcPiHUv3a76xRLgez
-Tv7QCdpw75j6VuZt27VXS9zlLCUVyJ4ueE742pyehizKV/Ma5ciSixqClnrDvFAS
-adgOWkaLOusm+iPJtrCBvkIApPjW/jAux9JG9uWOdf3yzLnQh1vMBhBgu4M1t15n
-3kfsmUjxpKEV/q2MYo45VU85FrmxY53/twIDAQABo0IwQDAPBgNVHRMBAf8EBTAD
-AQH/MB0GA1UdDgQWBBS2oVQ5AsOgP46KvPrU+Bym0ToO/TAOBgNVHQ8BAf8EBAMC
-AQYwDQYJKoZIhvcNAQENBQADggIBAHGlDs7k6b8/ONWJWsQCYftMxRQXLYtPU2sQ
-F/xlhMcQSZDe28cmk4gmb3DWAl45oPePq5a1pRNcgRRtDoGCERuKTsZPpd1iHkTf
-CVn0W3cLN+mLIMb4Ck4uWBzrM9DPhmDJ2vuAL55MYIR4PSFk1vtBHxgP58l1cb29
-XN40hz5BsA72udY/CROWFC/emh1auVbONTqwX3BNXuMp8SMoclm2q8KMZiYcdywm
-djWLKKdpoPk79SPdhRB0yZADVpHnr7pH1BKXESLjokmUbOe3lEu6LaTaM4tMpkT/
-WjzGHWTYtTHkpjx6qFcL2+1hGsvxznN3Y6SHb0xRONbkX8eftoEq5IVIeVheO/jb
-AoJnwTnbw3RLPTYe+SmTiGhbqEQZIfCn6IENLOiTNrQ3ssqwGyZ6miUfmpqAnksq
-P/ujmv5zMnHCnsZy4YpoJ/HkD7TETKVhk/iXEAcqMCWpuchxuO9ozC1+9eB+D4Ko
-b7a6bINDd82Kkhehnlt4Fj1F4jNy3eFmypnTycUm/Q1oBEauttmbjL4ZvrHG8hnj
-XALKLNhvSgfZyTXaQHXyxKcZb55CEJh15pWLYLztxRLXis7VmFxWlgPF7ncGNf/P
-5O4/E2Hu29othfDNrp2yGAlFw5Khchf8R7agCyzxxN5DaAhqXzvwdmP7zAYspsbi
-DrW5viSP
------END CERTIFICATE-----
-
-# Certum Trusted Network CA
------BEGIN CERTIFICATE-----
-MIIDuzCCAqOgAwIBAgIDBETAMA0GCSqGSIb3DQEBBQUAMH4xCzAJBgNVBAYTAlBM
-MSIwIAYDVQQKExlVbml6ZXRvIFRlY2hub2xvZ2llcyBTLkEuMScwJQYDVQQLEx5D
-ZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxIjAgBgNVBAMTGUNlcnR1bSBU
-cnVzdGVkIE5ldHdvcmsgQ0EwHhcNMDgxMDIyMTIwNzM3WhcNMjkxMjMxMTIwNzM3
-WjB+MQswCQYDVQQGEwJQTDEiMCAGA1UEChMZVW5pemV0byBUZWNobm9sb2dpZXMg
-Uy5BLjEnMCUGA1UECxMeQ2VydHVtIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MSIw
-IAYDVQQDExlDZXJ0dW0gVHJ1c3RlZCBOZXR3b3JrIENBMIIBIjANBgkqhkiG9w0B
-AQEFAAOCAQ8AMIIBCgKCAQEA4/t9o3K6wvDJFIf1awFO4W5AB7ptJ11/91sts1rH
-UV+rpDKmYYe2bg+G0jACl/jXaVehGDldamR5xgFZrDwxSjh80gTSSyjoIF87B6LM
-TXPb865Px1bVWqeWifrzq2jUI4ZZJ88JJ7ysbnKDHDBy3+Ci6dLhdHUZvSqeexVU
-BBvXQzmtVSjF4hq79MDkrjhJM8x2hZ85RdKknvISjFH4fOQtf/WsX+sWn7Et0brM
-kUJ3TCXJkDhv2/DM+44el1k+1WBO5gUo7Ul5E0u6SNsv+XLTOcr+H9g0cvW0QM8x
-AcPs3hEtF10fuFDRXhmnad4HMyjKUJX5p1TLVIZQRan5SQIDAQABo0IwQDAPBgNV
-HRMBAf8EBTADAQH/MB0GA1UdDgQWBBQIds3LB/8k9sXN7buQvOKEN0Z19zAOBgNV
-HQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQEFBQADggEBAKaorSLOAT2mo/9i0Eidi15y
-sHhE49wcrwn9I0j6vSrEuVUEtRCjjSfeC4Jj0O7eDDd5QVsisrCaQVymcODU0HfL
-I9MA4GxWL+FpDQ3Zqr8hgVDZBqWo/5U30Kr+4rP1mS1FhIrlQgnXdAIv94nYmem8
-J9RHjboNRhx3zxSkHLmkMcScKHQDNP8zGSal6Q10tz6XxnboJ5ajZt3hrvJBW8qY
-VoNzcOSGGtIxQbovvi0TWnZvTuhOgQ4/WwMioBK+ZlgRSssDxLQqKi2WF+A5VLxI
-03YnnZotBqbJ7DnSq9ufmgsnAjUpsUCV5/nonFWIGUbWtzT1fs45mtk48VH3Tyw=
------END CERTIFICATE-----
-
-# CFCA EV ROOT
------BEGIN CERTIFICATE-----
-MIIFjTCCA3WgAwIBAgIEGErM1jANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJD
-TjEwMC4GA1UECgwnQ2hpbmEgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9y
-aXR5MRUwEwYDVQQDDAxDRkNBIEVWIFJPT1QwHhcNMTIwODA4MDMwNzAxWhcNMjkx
-MjMxMDMwNzAxWjBWMQswCQYDVQQGEwJDTjEwMC4GA1UECgwnQ2hpbmEgRmluYW5j
-aWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRUwEwYDVQQDDAxDRkNBIEVWIFJP
-T1QwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDXXWvNED8fBVnVBU03
-sQ7smCuOFR36k0sXgiFxEFLXUWRwFsJVaU2OFW2fvwwbwuCjZ9YMrM8irq93VCpL
-TIpTUnrD7i7es3ElweldPe6hL6P3KjzJIx1qqx2hp/Hz7KDVRM8Vz3IvHWOX6Jn5
-/ZOkVIBMUtRSqy5J35DNuF++P96hyk0g1CXohClTt7GIH//62pCfCqktQT+x8Rgp
-7hZZLDRJGqgG16iI0gNyejLi6mhNbiyWZXvKWfry4t3uMCz7zEasxGPrb382KzRz
-EpR/38wmnvFyXVBlWY9ps4deMm/DGIq1lY+wejfeWkU7xzbh72fROdOXW3NiGUgt
-hxwG+3SYIElz8AXSG7Ggo7cbcNOIabla1jj0Ytwli3i/+Oh+uFzJlU9fpy25IGvP
-a931DfSCt/SyZi4QKPaXWnuWFo8BGS1sbn85WAZkgwGDg8NNkt0yxoekN+kWzqot
-aK8KgWU6cMGbrU1tVMoqLUuFG7OA5nBFDWteNfB/O7ic5ARwiRIlk9oKmSJgamNg
-TnYGmE69g60dWIolhdLHZR4tjsbftsbhf4oEIRUpdPA+nJCdDC7xij5aqgwJHsfV
-PKPtl8MeNPo4+QgO48BdK4PRVmrJtqhUUy54Mmc9gn900PvhtgVguXDbjgv5E1hv
-cWAQUhC5wUEJ73IfZzF4/5YFjQIDAQABo2MwYTAfBgNVHSMEGDAWgBTj/i39KNAL
-tbq2osS/BqoFjJP7LzAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAd
-BgNVHQ4EFgQU4/4t/SjQC7W6tqLEvwaqBYyT+y8wDQYJKoZIhvcNAQELBQADggIB
-ACXGumvrh8vegjmWPfBEp2uEcwPenStPuiB/vHiyz5ewG5zz13ku9Ui20vsXiObT
-ej/tUxPQ4i9qecsAIyjmHjdXNYmEwnZPNDatZ8POQQaIxffu2Bq41gt/UP+TqhdL
-jOztUmCypAbqTuv0axn96/Ua4CUqmtzHQTb3yHQFhDmVOdYLO6Qn+gjYXB74BGBS
-ESgoA//vU2YApUo0FmZ8/Qmkrp5nGm9BC2sGE5uPhnEFtC+NiWYzKXZUmhH4J/qy
-P5Hgzg0b8zAarb8iXRvTvyUFTeGSGn+ZnzxEk8rUQElsgIfXBDrDMlI1Dlb4pd19
-xIsNER9Tyx6yF7Zod1rg1MvIB671Oi6ON7fQAUtDKXeMOZePglr4UeWJoBjnaH9d
-Ci77o0cOPaYjesYBx4/IXr9tgFa+iiS6M+qf4TIRnvHST4D2G0CvOJ4RUHlzEhLN
-5mydLIhyPDCBBpEi6lmt2hkuIsKNuYyH4Ga8cyNfIWRjgEj1oDwYPZTISEEdQLpe
-/v5WOaHIz16eGWRGENoXkbcFgKyLmZJ956LYBws2J+dIeWCKw9cTXPhyQN9Ky8+Z
-AAoACxGV2lZFA4gKn2fQ1XmxqI1AbQ3CekD6819kR5LLU7m7Wc5P/dAVUwHY3+vZ
-5nbv0CO7O6l5s9UCKc2Jo5YPSjXnTkLAdc0Hz+Ys63su
------END CERTIFICATE-----
-
-# Chambers of Commerce Root - 2008
------BEGIN CERTIFICATE-----
-MIIHTzCCBTegAwIBAgIJAKPaQn6ksa7aMA0GCSqGSIb3DQEBBQUAMIGuMQswCQYD
-VQQGEwJFVTFDMEEGA1UEBxM6TWFkcmlkIChzZWUgY3VycmVudCBhZGRyZXNzIGF0
-IHd3dy5jYW1lcmZpcm1hLmNvbS9hZGRyZXNzKTESMBAGA1UEBRMJQTgyNzQzMjg3
-MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMuQS4xKTAnBgNVBAMTIENoYW1iZXJz
-IG9mIENvbW1lcmNlIFJvb3QgLSAyMDA4MB4XDTA4MDgwMTEyMjk1MFoXDTM4MDcz
-MTEyMjk1MFowga4xCzAJBgNVBAYTAkVVMUMwQQYDVQQHEzpNYWRyaWQgKHNlZSBj
-dXJyZW50IGFkZHJlc3MgYXQgd3d3LmNhbWVyZmlybWEuY29tL2FkZHJlc3MpMRIw
-EAYDVQQFEwlBODI3NDMyODcxGzAZBgNVBAoTEkFDIENhbWVyZmlybWEgUy5BLjEp
-MCcGA1UEAxMgQ2hhbWJlcnMgb2YgQ29tbWVyY2UgUm9vdCAtIDIwMDgwggIiMA0G
-CSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCvAMtwNyuAWko6bHiUfaN/Gh/2NdW9
-28sNRHI+JrKQUrpjOyhYb6WzbZSm891kDFX29ufyIiKAXuFixrYp4YFs8r/lfTJq
-VKAyGVn+H4vXPWCGhSRv4xGzdz4gljUha7MI2XAuZPeEklPWDrCQiorjh40G072Q
-DuKZoRuGDtqaCrsLYVAGUvGef3bsyw/QHg3PmTA9HMRFEFis1tPo1+XqxQEHd9ZR
-5gN/ikilTWh1uem8nk4ZcfUyS5xtYBkL+8ydddy/Js2Pk3g5eXNeJQ7KXOt3EgfL
-ZEFHcpOrUMPrCXZkNNI5t3YRCQ12RcSprj1qr7V9ZS+UWBDsXHyvfuK2GNnQm05a
-Sd+pZgvMPMZ4fKecHePOjlO+Bd5gD2vlGts/4+EhySnB8esHnFIbAURRPHsl18Tl
-UlRdJQfKFiC4reRB7noI/plvg6aRArBsNlVq5331lubKgdaX8ZSD6e2wsWsSaR6s
-+12pxZjptFtYer49okQ6Y1nUCyXeG0+95QGezdIp1Z8XGQpvvwyQ0wlf2eOKNcx5
-Wk0ZN5K3xMGtr/R5JJqyAQuxr1yW84Ay+1w9mPGgP0revq+ULtlVmhduYJ1jbLhj
-ya6BXBg14JC7vjxPNyK5fuvPnnchpj04gftI2jE9K+OJ9dC1vX7gUMQSibMjmhAx
-hduub+84Mxh2EQIDAQABo4IBbDCCAWgwEgYDVR0TAQH/BAgwBgEB/wIBDDAdBgNV
-HQ4EFgQU+SSsD7K1+HnA+mCIG8TZTQKeFxkwgeMGA1UdIwSB2zCB2IAU+SSsD7K1
-+HnA+mCIG8TZTQKeFxmhgbSkgbEwga4xCzAJBgNVBAYTAkVVMUMwQQYDVQQHEzpN
-YWRyaWQgKHNlZSBjdXJyZW50IGFkZHJlc3MgYXQgd3d3LmNhbWVyZmlybWEuY29t
-L2FkZHJlc3MpMRIwEAYDVQQFEwlBODI3NDMyODcxGzAZBgNVBAoTEkFDIENhbWVy
-ZmlybWEgUy5BLjEpMCcGA1UEAxMgQ2hhbWJlcnMgb2YgQ29tbWVyY2UgUm9vdCAt
-IDIwMDiCCQCj2kJ+pLGu2jAOBgNVHQ8BAf8EBAMCAQYwPQYDVR0gBDYwNDAyBgRV
-HSAAMCowKAYIKwYBBQUHAgEWHGh0dHA6Ly9wb2xpY3kuY2FtZXJmaXJtYS5jb20w
-DQYJKoZIhvcNAQEFBQADggIBAJASryI1wqM58C7e6bXpeHxIvj99RZJe6dqxGfwW
-PJ+0W2aeaufDuV2I6A+tzyMP3iU6XsxPpcG1Lawk0lgH3qLPaYRgM+gQDROpI9CF
-5Y57pp49chNyM/WqfcZjHwj0/gF/JM8rLFQJ3uIrbZLGOU8W6jx+ekbURWpGqOt1
-glanq6B8aBMz9p0w8G8nOSQjKpD9kCk18pPfNKXG9/jvjA9iSnyu0/VU+I22mlaH
-FoI6M6taIgj3grrqLuBHmrS1RaMFO9ncLkVAO+rcf+g769HsJtg1pDDFOqxXnrN2
-pSB7+R5KBWIBpih1YJeSDW4+TTdDDZIVnBgizVGZoCkaPF+KMjNbMMeJL0eYD6MD
-xvbxrN8y8NmBGuScvfaAFPDRLLmF9dijscilIeUcE5fuDr3fKanvNFNb0+RqE4QG
-tjICxFKuItLcsiFCGtpA8CnJ7AoMXOLQusxI0zcKzBIKinmwPQN/aUv0NCB9szTq
-jktk9T79syNnFQ0EuPAtwQlRPLJsFfClI9eDdOTlLsn+mCdCxqvGnrDQWzilm1De
-fhiYtUU79nm06PcaewaD+9CL2rvHvRirCG88gGtAPxkZumWK5r7VXNM21+9AUiRg
-OGcEMeyP84LG3rlV8zsxkVrctQgVrXYlCg17LofiDKYGvCYQbTed7N14jHyAxfDZ
-d0jQ
------END CERTIFICATE-----
-
-# Comodo AAA Services root
------BEGIN CERTIFICATE-----
-MIIEMjCCAxqgAwIBAgIBATANBgkqhkiG9w0BAQUFADB7MQswCQYDVQQGEwJHQjEb
-MBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRow
-GAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDEhMB8GA1UEAwwYQUFBIENlcnRpZmlj
-YXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAwMDAwMFoXDTI4MTIzMTIzNTk1OVowezEL
-MAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE
-BwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxITAfBgNVBAMM
-GEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEBBQADggEP
-ADCCAQoCggEBAL5AnfRu4ep2hxxNRUSOvkbIgwadwSr+GB+O5AL686tdUIoWMQua
-BtDFcCLNSS1UY8y2bmhGC1Pqy0wkwLxyTurxFa70VJoSCsN6sjNg4tqJVfMiWPPe
-3M/vg4aijJRPn2jymJBGhCfHdr/jzDUsi14HZGWCwEiwqJH5YZ92IFCokcdmtet4
-YgNW8IoaE+oxox6gmf049vYnMlhvB/VruPsUK6+3qszWY19zjNoFmag4qMsXeDZR
-rOme9Hg6jc8P2ULimAyrL58OAd7vn5lJ8S3frHRNG5i1R8XlKdH5kBjHYpy+g8cm
-ez6KJcfA3Z3mNWgQIJ2P2N7Sw4ScDV7oL8kCAwEAAaOBwDCBvTAdBgNVHQ4EFgQU
-oBEKIz6W8Qfs4q8p74Klf9AwpLQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQF
-MAMBAf8wewYDVR0fBHQwcjA4oDagNIYyaHR0cDovL2NybC5jb21vZG9jYS5jb20v
-QUFBQ2VydGlmaWNhdGVTZXJ2aWNlcy5jcmwwNqA0oDKGMGh0dHA6Ly9jcmwuY29t
-b2RvLm5ldC9BQUFDZXJ0aWZpY2F0ZVNlcnZpY2VzLmNybDANBgkqhkiG9w0BAQUF
-AAOCAQEACFb8AvCb6P+k+tZ7xkSAzk/ExfYAWMymtrwUSWgEdujm7l3sAg9g1o1Q
-GE8mTgHj5rCl7r+8dFRBv/38ErjHT1r0iWAFf2C3BUrz9vHCv8S5dIa2LX1rzNLz
-Rt0vxuBqw8M0Ayx9lt1awg6nCpnBBYurDC/zXDrPbDdVCYfeU0BsWO/8tqtlbgT2
-G9w84FoVxp7Z8VlIMCFlA2zs6SFz7JsDoeA3raAVGI/6ugLOpyypEBMs1OUIJqsi
-l2D4kF501KKaU73yqWjgom7C12yxow+ev+to51byrvLjKzg6CYG1a4XXvi3tPxq3
-smPi9WIsgtRqAEFQ8TmDn5XpNpaYbg==
------END CERTIFICATE-----
-
-# COMODO Certification Authority
------BEGIN CERTIFICATE-----
-MIIEHTCCAwWgAwIBAgIQToEtioJl4AsC7j41AkblPTANBgkqhkiG9w0BAQUFADCB
-gTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G
-A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxJzAlBgNV
-BAMTHkNPTU9ETyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjEyMDEwMDAw
-MDBaFw0yOTEyMzEyMzU5NTlaMIGBMQswCQYDVQQGEwJHQjEbMBkGA1UECBMSR3Jl
-YXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHEwdTYWxmb3JkMRowGAYDVQQKExFDT01P
-RE8gQ0EgTGltaXRlZDEnMCUGA1UEAxMeQ09NT0RPIENlcnRpZmljYXRpb24gQXV0
-aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0ECLi3LjkRv3
-UcEbVASY06m/weaKXTuH+7uIzg3jLz8GlvCiKVCZrts7oVewdFFxze1CkU1B/qnI
-2GqGd0S7WWaXUF601CxwRM/aN5VCaTwwxHGzUvAhTaHYujl8HJ6jJJ3ygxaYqhZ8
-Q5sVW7euNJH+1GImGEaaP+vB+fGQV+useg2L23IwambV4EajcNxo2f8ESIl33rXp
-+2dtQem8Ob0y2WIC8bGoPW43nOIv4tOiJovGuFVDiOEjPqXSJDlqR6sA1KGzqSX+
-DT+nHbrTUcELpNqsOO9VUCQFZUaTNE8tja3G1CEZ0o7KBWFxB3NH5YoZEr0ETc5O
-nKVIrLsm9wIDAQABo4GOMIGLMB0GA1UdDgQWBBQLWOWLxkwVN6RAqTCpIb5HNlpW
-/zAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zBJBgNVHR8EQjBAMD6g
-PKA6hjhodHRwOi8vY3JsLmNvbW9kb2NhLmNvbS9DT01PRE9DZXJ0aWZpY2F0aW9u
-QXV0aG9yaXR5LmNybDANBgkqhkiG9w0BAQUFAAOCAQEAPpiem/Yb6dc5t3iuHXIY
-SdOH5EOC6z/JqvWote9VfCFSZfnVDeFs9D6Mk3ORLgLETgdxb8CPOGEIqB6BCsAv
-IC9Bi5HcSEW88cbeunZrM8gALTFGTO3nnc+IlP8zwFboJIYmuNg4ON8qa90SzMc/
-RxdMosIGlgnW2/4/PEZB31jiVg88O8EckzXZOFKs7sjsLjBOlDW0JB9LeGna8gI4
-zJVSk/BwJVmcIGfE7vmLV2H0knZ9P4SNVbfo5azV8fUZVqZa+5Acr5Pr5RzUZ5dd
-BA6+C4OmF4O5MBKgxTMVBbkN+8cFduPYSo38NBejxiEovjBFMR7HeL5YYTisO+IB
-ZQ==
------END CERTIFICATE-----
-
-# COMODO ECC Certification Authority
------BEGIN CERTIFICATE-----
-MIICiTCCAg+gAwIBAgIQH0evqmIAcFBUTAGem2OZKjAKBggqhkjOPQQDAzCBhTEL
-MAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE
-BxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMT
-IkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDgwMzA2MDAw
-MDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdy
-ZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09N
-T0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlv
-biBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQDR3svdcmCFYX7deSR
-FtSrYpn1PlILBs5BAH+X4QokPB0BBO490o0JlwzgdeT6+3eKKvUDYEs2ixYjFq0J
-cfRK9ChQtP6IHG4/bC8vCVlbpVsLM5niwz2J+Wos77LTBumjQjBAMB0GA1UdDgQW
-BBR1cacZSBm8nZ3qQUfflMRId5nTeTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/
-BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjEA7wNbeqy3eApyt4jf/7VGFAkK+qDm
-fQjGGoe9GKhzvSbKYAydzpmfz1wPMOG+FDHqAjAU9JM8SaczepBGR7NjfRObTrdv
-GDeAU/7dIOA1mjbRxwG55tzd8/8dLDoWV9mSOdY=
------END CERTIFICATE-----
-
-# COMODO RSA Certification Authority
------BEGIN CERTIFICATE-----
-MIIF2DCCA8CgAwIBAgIQTKr5yttjb+Af907YWwOGnTANBgkqhkiG9w0BAQwFADCB
-hTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G
-A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNV
-BAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAwMTE5
-MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0IxGzAZBgNVBAgT
-EkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMR
-Q09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNh
-dGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCR
-6FSS0gpWsawNJN3Fz0RndJkrN6N9I3AAcbxT38T6KhKPS38QVr2fcHK3YX/JSw8X
-pz3jsARh7v8Rl8f0hj4K+j5c+ZPmNHrZFGvnnLOFoIJ6dq9xkNfs/Q36nGz637CC
-9BR++b7Epi9Pf5l/tfxnQ3K9DADWietrLNPtj5gcFKt+5eNu/Nio5JIk2kNrYrhV
-/erBvGy2i/MOjZrkm2xpmfh4SDBF1a3hDTxFYPwyllEnvGfDyi62a+pGx8cgoLEf
-Zd5ICLqkTqnyg0Y3hOvozIFIQ2dOciqbXL1MGyiKXCJ7tKuY2e7gUYPDCUZObT6Z
-+pUX2nwzV0E8jVHtC7ZcryxjGt9XyD+86V3Em69FmeKjWiS0uqlWPc9vqv9JWL7w
-qP/0uK3pN/u6uPQLOvnoQ0IeidiEyxPx2bvhiWC4jChWrBQdnArncevPDt09qZah
-SL0896+1DSJMwBGB7FY79tOi4lu3sgQiUpWAk2nojkxl8ZEDLXB0AuqLZxUpaVIC
-u9ffUGpVRr+goyhhf3DQw6KqLCGqR84onAZFdr+CGCe01a60y1Dma/RMhnEw6abf
-Fobg2P9A3fvQQoh/ozM6LlweQRGBY84YcWsr7KaKtzFcOmpH4MN5WdYgGq/yapiq
-crxXStJLnbsQ/LBMQeXtHT1eKJ2czL+zUdqnR+WEUwIDAQABo0IwQDAdBgNVHQ4E
-FgQUu69+Aj36pvE8hI6t7jiY7NkyMtQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB
-/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAArx1UaEt65Ru2yyTUEUAJNMnMvl
-wFTPoCWOAvn9sKIN9SCYPBMtrFaisNZ+EZLpLrqeLppysb0ZRGxhNaKatBYSaVqM
-4dc+pBroLwP0rmEdEBsqpIt6xf4FpuHA1sj+nq6PK7o9mfjYcwlYRm6mnPTXJ9OV
-2jeDchzTc+CiR5kDOF3VSXkAKRzH7JsgHAckaVd4sjn8OoSgtZx8jb8uk2Intzna
-FxiuvTwJaP+EmzzV1gsD41eeFPfR60/IvYcjt7ZJQ3mFXLrrkguhxuhoqEwWsRqZ
-CuhTLJK7oQkYdQxlqHvLI7cawiiFwxv/0Cti76R7CZGYZ4wUAc1oBmpjIXUDgIiK
-boHGhfKppC3n9KUkEEeDys30jXlYsQab5xoq2Z0B15R97QNKyvDb6KkBPvVWmcke
-jkk9u+UJueBPSZI9FoJAzMxZxuY67RIuaTxslbH9qh17f4a+Hg4yRvv7E491f0yL
-S0Zj/gA0QHDBw7mh3aZw4gSzQbzpgJHqZJx64SIDqZxubw5lT2yHh17zbqD5daWb
-QOhTsiedSrnAdyGN/4fy3ryM7xfft0kL0fJuMAsaDk527RH89elWsn2/x20Kk4yl
-0MC2Hb46TpSi125sC8KKfPog88Tk5c0NqMuRkrF8hey1FGlmDoLnzc7ILaZRfyHB
-NVOFBkpdn627G190
------END CERTIFICATE-----
-
-# Cybertrust Global Root
------BEGIN CERTIFICATE-----
-MIIDoTCCAomgAwIBAgILBAAAAAABD4WqLUgwDQYJKoZIhvcNAQEFBQAwOzEYMBYG
-A1UEChMPQ3liZXJ0cnVzdCwgSW5jMR8wHQYDVQQDExZDeWJlcnRydXN0IEdsb2Jh
-bCBSb290MB4XDTA2MTIxNTA4MDAwMFoXDTIxMTIxNTA4MDAwMFowOzEYMBYGA1UE
-ChMPQ3liZXJ0cnVzdCwgSW5jMR8wHQYDVQQDExZDeWJlcnRydXN0IEdsb2JhbCBS
-b290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA+Mi8vRRQZhP/8NN5
-7CPytxrHjoXxEnOmGaoQ25yiZXRadz5RfVb23CO21O1fWLE3TdVJDm71aofW0ozS
-J8bi/zafmGWgE07GKmSb1ZASzxQG9Dvj1Ci+6A74q05IlG2OlTEQXO2iLb3VOm2y
-HLtgwEZLAfVJrn5GitB0jaEMAs7u/OePuGtm839EAL9mJRQr3RAwHQeWP032a7iP
-t3sMpTjr3kfb1V05/Iin89cqdPHoWqI7n1C6poxFNcJQZZXcY4Lv3b93TZxiyWNz
-FtApD0mpSPCzqrdsxacwOUBdrsTiXSZT8M4cIwhhqJQZugRiQOwfOHB3EgZxpzAY
-XSUnpQIDAQABo4GlMIGiMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/
-MB0GA1UdDgQWBBS2CHsNesysIEyGVjJez6tuhS1wVzA/BgNVHR8EODA2MDSgMqAw
-hi5odHRwOi8vd3d3Mi5wdWJsaWMtdHJ1c3QuY29tL2NybC9jdC9jdHJvb3QuY3Js
-MB8GA1UdIwQYMBaAFLYIew16zKwgTIZWMl7Pq26FLXBXMA0GCSqGSIb3DQEBBQUA
-A4IBAQBW7wojoFROlZfJ+InaRcHUowAl9B8Tq7ejhVhpwjCt2BWKLePJzYFa+HMj
-Wqd8BfP9IjsO0QbE2zZMcwSO5bAi5MXzLqXZI+O4Tkogp24CJJ8iYGd7ix1yCcUx
-XOl5n4BHPa2hCwcUPUf/A2kaDAtE52Mlp3+yybh2hO0j9n0Hq0V+09+zv+mKts2o
-omcrUtW3ZfA5TGOgkXmTUg9U3YO7n9GPp1Nzw8v/MOx8BLjYRB+TX3EJIrduPuoc
-A06dGiBh+4E37F78CkWr1+cXVdCg6mCbpvbjjFspwgZgFJ0tl0ypkxWdYcQBX0jW
-WL1WMRJOEcgh4LMRkWXbtKaIOM5V
------END CERTIFICATE-----
-
-# Deutsche Telekom Root CA 2
------BEGIN CERTIFICATE-----
-MIIDnzCCAoegAwIBAgIBJjANBgkqhkiG9w0BAQUFADBxMQswCQYDVQQGEwJERTEc
-MBoGA1UEChMTRGV1dHNjaGUgVGVsZWtvbSBBRzEfMB0GA1UECxMWVC1UZWxlU2Vj
-IFRydXN0IENlbnRlcjEjMCEGA1UEAxMaRGV1dHNjaGUgVGVsZWtvbSBSb290IENB
-IDIwHhcNOTkwNzA5MTIxMTAwWhcNMTkwNzA5MjM1OTAwWjBxMQswCQYDVQQGEwJE
-RTEcMBoGA1UEChMTRGV1dHNjaGUgVGVsZWtvbSBBRzEfMB0GA1UECxMWVC1UZWxl
-U2VjIFRydXN0IENlbnRlcjEjMCEGA1UEAxMaRGV1dHNjaGUgVGVsZWtvbSBSb290
-IENBIDIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCrC6M14IspFLEU
-ha88EOQ5bzVdSq7d6mGNlUn0b2SjGmBmpKlAIoTZ1KXleJMOaAGtuU1cOs7TuKhC
-QN/Po7qCWWqSG6wcmtoIKyUn+WkjR/Hg6yx6m/UTAtB+NHzCnjwAWav12gz1Mjwr
-rFDa1sPeg5TKqAyZMg4ISFZbavva4VhYAUlfckE8FQYBjl2tqriTtM2e66foai1S
-NNs671x1Udrb8zH57nGYMsRUFUQM+ZtV7a3fGAigo4aKSe5TBY8ZTNXeWHmb0moc
-QqvF1afPaA+W5OFhmHZhyJF81j4A4pFQh+GdCuatl9Idxjp9y7zaAzTVjlsB9WoH
-txa2bkp/AgMBAAGjQjBAMB0GA1UdDgQWBBQxw3kbuvVT1xfgiXotF2wKsyudMzAP
-BgNVHRMECDAGAQH/AgEFMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOC
-AQEAlGRZrTlk5ynrE/5aw4sTV8gEJPB0d8Bg42f76Ymmg7+Wgnxu1MM9756Abrsp
-tJh6sTtU6zkXR34ajgv8HzFZMQSyzhfzLMdiNlXiItiJVbSYSKpk+tYcNthEeFpa
-IzpXl/V6ME+un2pMSyuOoAPjPuCp1NJ70rOo4nI8rZ7/gFnkm0W09juwzTkZmDLl
-6iFhkOQxIY40sfcvNUqFENrnijchvllj4PKFiDFT1FQUhXB59C4Gdyd1Lx+4ivn+
-xbrYNuSD7Odlt79jWvNGr4GUN9RBjNYj1h7P9WgbRGOiWrqnNVmh5XAFmw4jV5mU
-Cm26OWMohpLzGITY+9HPBVZkVw==
------END CERTIFICATE-----
-
-# DigiCert Assured ID Root CA
------BEGIN CERTIFICATE-----
-MIIDtzCCAp+gAwIBAgIQDOfg5RfYRv6P5WD8G/AwOTANBgkqhkiG9w0BAQUFADBl
-MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
-d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJv
-b3QgQ0EwHhcNMDYxMTEwMDAwMDAwWhcNMzExMTEwMDAwMDAwWjBlMQswCQYDVQQG
-EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNl
-cnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0EwggEi
-MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtDhXO5EOAXLGH87dg+XESpa7c
-JpSIqvTO9SA5KFhgDPiA2qkVlTJhPLWxKISKityfCgyDF3qPkKyK53lTXDGEKvYP
-mDI2dsze3Tyoou9q+yHyUmHfnyDXH+Kx2f4YZNISW1/5WBg1vEfNoTb5a3/UsDg+
-wRvDjDPZ2C8Y/igPs6eD1sNuRMBhNZYW/lmci3Zt1/GiSw0r/wty2p5g0I6QNcZ4
-VYcgoc/lbQrISXwxmDNsIumH0DJaoroTghHtORedmTpyoeb6pNnVFzF1roV9Iq4/
-AUaG9ih5yLHa5FcXxH4cDrC0kqZWs72yl+2qp/C3xag/lRbQ/6GW6whfGHdPAgMB
-AAGjYzBhMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW
-BBRF66Kv9JLLgjEtUYunpyGd823IDzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYun
-pyGd823IDzANBgkqhkiG9w0BAQUFAAOCAQEAog683+Lt8ONyc3pklL/3cmbYMuRC
-dWKuh+vy1dneVrOfzM4UKLkNl2BcEkxY5NM9g0lFWJc1aRqoR+pWxnmrEthngYTf
-fwk8lOa4JiwgvT2zKIn3X/8i4peEH+ll74fg38FnSbNd67IJKusm7Xi+fT8r87cm
-NW1fiQG2SVufAQWbqz0lwcy2f8Lxb4bG+mRo64EtlOtCt/qMHt1i8b5QZ7dsvfPx
-H2sMNgcWfzd8qVttevESRmCD1ycEvkvOl77DZypoEd+A5wwzZr8TDRRu838fYxAe
-+o0bJW1sj6W3YQGx0qMmoRBxna3iw/nDmVG3KwcIzi7mULKn+gpFL6Lw8g==
------END CERTIFICATE-----
-
-# DigiCert Assured ID Root G2
------BEGIN CERTIFICATE-----
-MIIDljCCAn6gAwIBAgIQC5McOtY5Z+pnI7/Dr5r0SzANBgkqhkiG9w0BAQsFADBl
-MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
-d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJv
-b3QgRzIwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBlMQswCQYDVQQG
-EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNl
-cnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzIwggEi
-MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDZ5ygvUj82ckmIkzTz+GoeMVSA
-n61UQbVH35ao1K+ALbkKz3X9iaV9JPrjIgwrvJUXCzO/GU1BBpAAvQxNEP4Htecc
-biJVMWWXvdMX0h5i89vqbFCMP4QMls+3ywPgym2hFEwbid3tALBSfK+RbLE4E9Hp
-EgjAALAcKxHad3A2m67OeYfcgnDmCXRwVWmvo2ifv922ebPynXApVfSr/5Vh88lA
-bx3RvpO704gqu52/clpWcTs/1PPRCv4o76Pu2ZmvA9OPYLfykqGxvYmJHzDNw6Yu
-YjOuFgJ3RFrngQo8p0Quebg/BLxcoIfhG69Rjs3sLPr4/m3wOnyqi+RnlTGNAgMB
-AAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQW
-BBTOw0q5mVXyuNtgv6l+vVa1lzan1jANBgkqhkiG9w0BAQsFAAOCAQEAyqVVjOPI
-QW5pJ6d1Ee88hjZv0p3GeDgdaZaikmkuOGybfQTUiaWxMTeKySHMq2zNixya1r9I
-0jJmwYrA8y8678Dj1JGG0VDjA9tzd29KOVPt3ibHtX2vK0LRdWLjSisCx1BL4Gni
-lmwORGYQRI+tBev4eaymG+g3NJ1TyWGqolKvSnAWhsI6yLETcDbYz+70CjTVW0z9
-B5yiutkBclzzTcHdDrEcDcRjvq30FPuJ7KJBDkzMyFdA0G4Dqs0MjomZmWzwPDCv
-ON9vvKO+KSAnq3T/EyJ43pdSVR6DtVQgA+6uwE9W3jfMw3+qBCe703e4YtsXfJwo
-IhNzbM8m9Yop5w==
------END CERTIFICATE-----
-
-# DigiCert Assured ID Root G3
------BEGIN CERTIFICATE-----
-MIICRjCCAc2gAwIBAgIQC6Fa+h3foLVJRK/NJKBs7DAKBggqhkjOPQQDAzBlMQsw
-CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu
-ZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3Qg
-RzMwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBlMQswCQYDVQQGEwJV
-UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQu
-Y29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzMwdjAQBgcq
-hkjOPQIBBgUrgQQAIgNiAAQZ57ysRGXtzbg/WPuNsVepRC0FFfLvC/8QdJ+1YlJf
-Zn4f5dwbRXkLzMZTCp2NXQLZqVneAlr2lSoOjThKiknGvMYDOAdfVdp+CW7if17Q
-RSAPWXYQ1qAk8C3eNvJsKTmjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/
-BAQDAgGGMB0GA1UdDgQWBBTL0L2p4ZgFUaFNN6KDec6NHSrkhDAKBggqhkjOPQQD
-AwNnADBkAjAlpIFFAmsSS3V0T8gj43DydXLefInwz5FyYZ5eEJJZVrmDxxDnOOlY
-JjZ91eQ0hjkCMHw2U/Aw5WJjOpnitqM7mzT6HtoQknFekROn3aRukswy1vUhZscv
-6pZjamVFkpUBtA==
------END CERTIFICATE-----
-
-# DigiCert Global Root CA
------BEGIN CERTIFICATE-----
-MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh
-MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
-d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD
-QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT
-MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j
-b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG
-9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB
-CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97
-nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt
-43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P
-T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4
-gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO
-BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR
-TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw
-DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr
-hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg
-06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF
-PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls
-YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk
-CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4=
------END CERTIFICATE-----
-
-# DigiCert Global Root G2
------BEGIN CERTIFICATE-----
-MIIDjjCCAnagAwIBAgIQAzrx5qcRqaC7KGSxHQn65TANBgkqhkiG9w0BAQsFADBh
-MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
-d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBH
-MjAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVT
-MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j
-b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEcyMIIBIjANBgkqhkiG
-9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuzfNNNx7a8myaJCtSnX/RrohCgiN9RlUyfuI
-2/Ou8jqJkTx65qsGGmvPrC3oXgkkRLpimn7Wo6h+4FR1IAWsULecYxpsMNzaHxmx
-1x7e/dfgy5SDN67sH0NO3Xss0r0upS/kqbitOtSZpLYl6ZtrAGCSYP9PIUkY92eQ
-q2EGnI/yuum06ZIya7XzV+hdG82MHauVBJVJ8zUtluNJbd134/tJS7SsVQepj5Wz
-tCO7TG1F8PapspUwtP1MVYwnSlcUfIKdzXOS0xZKBgyMUNGPHgm+F6HmIcr9g+UQ
-vIOlCsRnKPZzFBQ9RnbDhxSJITRNrw9FDKZJobq7nMWxM4MphQIDAQABo0IwQDAP
-BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUTiJUIBiV
-5uNu5g/6+rkS7QYXjzkwDQYJKoZIhvcNAQELBQADggEBAGBnKJRvDkhj6zHd6mcY
-1Yl9PMWLSn/pvtsrF9+wX3N3KjITOYFnQoQj8kVnNeyIv/iPsGEMNKSuIEyExtv4
-NeF22d+mQrvHRAiGfzZ0JFrabA0UWTW98kndth/Jsw1HKj2ZL7tcu7XUIOGZX1NG
-Fdtom/DzMNU+MeKNhJ7jitralj41E6Vf8PlwUHBHQRFXGU7Aj64GxJUTFy8bJZ91
-8rGOmaFvE7FBcf6IKshPECBV1/MUReXgRPTqh5Uykw7+U0b6LJ3/iyK5S9kJRaTe
-pLiaWN0bfVKfjllDiIGknibVb63dDcY3fe0Dkhvld1927jyNxF1WW6LZZm6zNTfl
-MrY=
------END CERTIFICATE-----
-
-# DigiCert Global Root G3
------BEGIN CERTIFICATE-----
-MIICPzCCAcWgAwIBAgIQBVVWvPJepDU1w6QP1atFcjAKBggqhkjOPQQDAzBhMQsw
-CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu
-ZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBHMzAe
-Fw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVTMRUw
-EwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20x
-IDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEczMHYwEAYHKoZIzj0CAQYF
-K4EEACIDYgAE3afZu4q4C/sLfyHS8L6+c/MzXRq8NOrexpu80JX28MzQC7phW1FG
-fp4tn+6OYwwX7Adw9c+ELkCDnOg/QW07rdOkFFk2eJ0DQ+4QE2xy3q6Ip6FrtUPO
-Z9wj/wMco+I+o0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAd
-BgNVHQ4EFgQUs9tIpPmhxdiuNkHMEWNpYim8S8YwCgYIKoZIzj0EAwMDaAAwZQIx
-AK288mw/EkrRLTnDCgmXc/SINoyIJ7vmiI1Qhadj+Z4y3maTD/HMsQmP3Wyr+mt/
-oAIwOWZbwmSNuJ5Q3KjVSaLtx9zRSX8XAbjIho9OjIgrqJqpisXRAL34VOKa5Vt8
-sycX
------END CERTIFICATE-----
-
-# DigiCert High Assurance EV Root CA
------BEGIN CERTIFICATE-----
-MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBs
-MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
-d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j
-ZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTMxMTExMDAwMDAwMFowbDEL
-MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3
-LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug
-RVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm
-+9S75S0tMqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTW
-PNt0OKRKzE0lgvdKpVMSOO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEM
-xChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFB
-Ik5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQNAQTXKFx01p8VdteZOE3
-hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUeh10aUAsg
-EsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQF
-MAMBAf8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaA
-FLE+w2kD+L9HAdSYJhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3Nec
-nzyIZgYIVyHbIUf4KmeqvxgydkAQV8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6z
-eM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFpmyPInngiK3BD41VHMWEZ71jF
-hS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkKmNEVX58Svnw2
-Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe
-vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep
-+OkuE6N36B9K
------END CERTIFICATE-----
-
-# DigiCert Trusted Root G4
------BEGIN CERTIFICATE-----
-MIIFkDCCA3igAwIBAgIQBZsbV56OITLiOQe9p3d1XDANBgkqhkiG9w0BAQwFADBi
-MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
-d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3Qg
-RzQwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBiMQswCQYDVQQGEwJV
-UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQu
-Y29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwggIiMA0GCSqG
-SIb3DQEBAQUAA4ICDwAwggIKAoICAQC/5pBzaN675F1KPDAiMGkz7MKnJS7JIT3y
-ithZwuEppz1Yq3aaza57G4QNxDAf8xukOBbrVsaXbR2rsnnyyhHS5F/WBTxSD1If
-xp4VpX6+n6lXFllVcq9ok3DCsrp1mWpzMpTREEQQLt+C8weE5nQ7bXHiLQwb7iDV
-ySAdYyktzuxeTsiT+CFhmzTrBcZe7FsavOvJz82sNEBfsXpm7nfISKhmV1efVFiO
-DCu3T6cw2Vbuyntd463JT17lNecxy9qTXtyOj4DatpGYQJB5w3jHtrHEtWoYOAMQ
-jdjUN6QuBX2I9YI+EJFwq1WCQTLX2wRzKm6RAXwhTNS8rhsDdV14Ztk6MUSaM0C/
-CNdaSaTC5qmgZ92kJ7yhTzm1EVgX9yRcRo9k98FpiHaYdj1ZXUJ2h4mXaXpI8OCi
-EhtmmnTK3kse5w5jrubU75KSOp493ADkRSWJtppEGSt+wJS00mFt6zPZxd9LBADM
-fRyVw4/3IbKyEbe7f/LVjHAsQWCqsWMYRJUadmJ+9oCw++hkpjPRiQfhvbfmQ6QY
-uKZ3AeEPlAwhHbJUKSWJbOUOUlFHdL4mrLZBdd56rF+NP8m800ERElvlEFDrMcXK
-chYiCd98THU/Y+whX8QgUWtvsauGi0/C1kVfnSD8oR7FwI+isX4KJpn15GkvmB0t
-9dmpsh3lGwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB
-hjAdBgNVHQ4EFgQU7NfjgtJxXWRM3y5nP+e6mK4cD08wDQYJKoZIhvcNAQEMBQAD
-ggIBALth2X2pbL4XxJEbw6GiAI3jZGgPVs93rnD5/ZpKmbnJeFwMDF/k5hQpVgs2
-SV1EY+CtnJYYZhsjDT156W1r1lT40jzBQ0CuHVD1UvyQO7uYmWlrx8GnqGikJ9yd
-+SeuMIW59mdNOj6PWTkiU0TryF0Dyu1Qen1iIQqAyHNm0aAFYF/opbSnr6j3bTWc
-fFqK1qI4mfN4i/RN0iAL3gTujJtHgXINwBQy7zBZLq7gcfJW5GqXb5JQbZaNaHqa
-sjYUegbyJLkJEVDXCLG4iXqEI2FCKeWjzaIgQdfRnGTZ6iahixTXTBmyUEFxPT9N
-cCOGDErcgdLMMpSEDQgJlxxPwO5rIHQw0uA5NBCFIRUBCOhVMt5xSdkoF1BN5r5N
-0XWs0Mr7QbhDparTwwVETyw2m+L64kW4I1NsBm9nVX9GtUw/bihaeSbSpKhil9Ie
-4u1Ki7wb/UdKDd9nZn6yW0HQO+T0O/QEY+nvwlQAUaCKKsnOeMzV6ocEGLPOr0mI
-r/OSmbaz5mEP0oUA51Aa5BuVnRmhuZyxm7EAHu/QD09CbMkKvO5D+jpxpchNJqU1
-/YldvIViHTLSoCtU7ZpXwdv6EM8Zt4tKG48BtieVU+i2iW1bvGjUI+iLUaJW+fCm
-gKDWHrO8Dw9TdSmq6hN35N6MgSGtBxBHEa2HPQfRdbzP82Z+
------END CERTIFICATE-----
-
-# DST Root CA X3
------BEGIN CERTIFICATE-----
-MIIDSjCCAjKgAwIBAgIQRK+wgNajJ7qJMDmGLvhAazANBgkqhkiG9w0BAQUFADA/
-MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT
-DkRTVCBSb290IENBIFgzMB4XDTAwMDkzMDIxMTIxOVoXDTIxMDkzMDE0MDExNVow
-PzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMRcwFQYDVQQD
-Ew5EU1QgUm9vdCBDQSBYMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
-AN+v6ZdQCINXtMxiZfaQguzH0yxrMMpb7NnDfcdAwRgUi+DoM3ZJKuM/IUmTrE4O
-rz5Iy2Xu/NMhD2XSKtkyj4zl93ewEnu1lcCJo6m67XMuegwGMoOifooUMM0RoOEq
-OLl5CjH9UL2AZd+3UWODyOKIYepLYYHsUmu5ouJLGiifSKOeDNoJjj4XLh7dIN9b
-xiqKqy69cK3FCxolkHRyxXtqqzTWMIn/5WgTe1QLyNau7Fqckh49ZLOMxt+/yUFw
-7BZy1SbsOFU5Q9D8/RhcQPGX69Wam40dutolucbY38EVAjqr2m7xPi71XAicPNaD
-aeQQmxkqtilX4+U9m5/wAl0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNV
-HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMSnsaR7LHH62+FLkHX/xBVghYkQMA0GCSqG
-SIb3DQEBBQUAA4IBAQCjGiybFwBcqR7uKGY3Or+Dxz9LwwmglSBd49lZRNI+DT69
-ikugdB/OEIKcdBodfpga3csTS7MgROSR6cz8faXbauX+5v3gTt23ADq1cEmv8uXr
-AvHRAosZy5Q6XkjEGB5YGV8eAlrwDPGxrancWYaLbumR9YbK+rlmM6pZW87ipxZz
-R8srzJmwN0jP41ZL9c8PDHIyh8bwRLtTcm1D9SZImlJnt1ir/md2cXjbDaJWFBM5
-JDGFoqgCWjBH4d1QB7wCCZAA62RjYJsWvIjJEubSfZGL+T0yjWW06XyxV3bqxbYo
-Ob8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ
------END CERTIFICATE-----
-
-# D-TRUST Root Class 3 CA 2 2009
------BEGIN CERTIFICATE-----
-MIIEMzCCAxugAwIBAgIDCYPzMA0GCSqGSIb3DQEBCwUAME0xCzAJBgNVBAYTAkRF
-MRUwEwYDVQQKDAxELVRydXN0IEdtYkgxJzAlBgNVBAMMHkQtVFJVU1QgUm9vdCBD
-bGFzcyAzIENBIDIgMjAwOTAeFw0wOTExMDUwODM1NThaFw0yOTExMDUwODM1NTha
-ME0xCzAJBgNVBAYTAkRFMRUwEwYDVQQKDAxELVRydXN0IEdtYkgxJzAlBgNVBAMM
-HkQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgMjAwOTCCASIwDQYJKoZIhvcNAQEB
-BQADggEPADCCAQoCggEBANOySs96R+91myP6Oi/WUEWJNTrGa9v+2wBoqOADER03
-UAifTUpolDWzU9GUY6cgVq/eUXjsKj3zSEhQPgrfRlWLJ23DEE0NkVJD2IfgXU42
-tSHKXzlABF9bfsyjxiupQB7ZNoTWSPOSHjRGICTBpFGOShrvUD9pXRl/RcPHAY9R
-ySPocq60vFYJfxLLHLGvKZAKyVXMD9O0Gu1HNVpK7ZxzBCHQqr0ME7UAyiZsxGsM
-lFqVlNpQmvH/pStmMaTJOKDfHR+4CS7zp+hnUquVH+BGPtikw8paxTGA6Eian5Rp
-/hnd2HN8gcqW3o7tszIFZYQ05ub9VxC1X3a/L7AQDcUCAwEAAaOCARowggEWMA8G
-A1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFP3aFMSfMN4hvR5COfyrYyNJ4PGEMA4G
-A1UdDwEB/wQEAwIBBjCB0wYDVR0fBIHLMIHIMIGAoH6gfIZ6bGRhcDovL2RpcmVj
-dG9yeS5kLXRydXN0Lm5ldC9DTj1ELVRSVVNUJTIwUm9vdCUyMENsYXNzJTIwMyUy
-MENBJTIwMiUyMDIwMDksTz1ELVRydXN0JTIwR21iSCxDPURFP2NlcnRpZmljYXRl
-cmV2b2NhdGlvbmxpc3QwQ6BBoD+GPWh0dHA6Ly93d3cuZC10cnVzdC5uZXQvY3Js
-L2QtdHJ1c3Rfcm9vdF9jbGFzc18zX2NhXzJfMjAwOS5jcmwwDQYJKoZIhvcNAQEL
-BQADggEBAH+X2zDI36ScfSF6gHDOFBJpiBSVYEQBrLLpME+bUMJm2H6NMLVwMeni
-acfzcNsgFYbQDfC+rAF1hM5+n02/t2A7nPPKHeJeaNijnZflQGDSNiH+0LS4F9p0
-o3/U37CYAqxva2ssJSRyoWXuJVrl5jLn8t+rSfrzkGkj2wTZ51xY/GXUl77M/C4K
-zCUqNQT4YJEVdT1B/yMfGchs64JTBKbkTCJNjYy6zltz7GRUUG3RnFX7acM2w4y8
-PIWmawomDeCTmGCufsYkl4phX5GOZpIJhzbNi5stPvZR1FDUWSi9g/LMKHtThm3Y
-Johw1+qRzT65ysCQblrGXnRl11z+o+I=
------END CERTIFICATE-----
-
-# D-TRUST Root Class 3 CA 2 EV 2009
------BEGIN CERTIFICATE-----
-MIIEQzCCAyugAwIBAgIDCYP0MA0GCSqGSIb3DQEBCwUAMFAxCzAJBgNVBAYTAkRF
-MRUwEwYDVQQKDAxELVRydXN0IEdtYkgxKjAoBgNVBAMMIUQtVFJVU1QgUm9vdCBD
-bGFzcyAzIENBIDIgRVYgMjAwOTAeFw0wOTExMDUwODUwNDZaFw0yOTExMDUwODUw
-NDZaMFAxCzAJBgNVBAYTAkRFMRUwEwYDVQQKDAxELVRydXN0IEdtYkgxKjAoBgNV
-BAMMIUQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgRVYgMjAwOTCCASIwDQYJKoZI
-hvcNAQEBBQADggEPADCCAQoCggEBAJnxhDRwui+3MKCOvXwEz75ivJn9gpfSegpn
-ljgJ9hBOlSJzmY3aFS3nBfwZcyK3jpgAvDw9rKFs+9Z5JUut8Mxk2og+KbgPCdM0
-3TP1YtHhzRnp7hhPTFiu4h7WDFsVWtg6uMQYZB7jM7K1iXdODL/ZlGsTl28So/6Z
-qQTMFexgaDbtCHu39b+T7WYxg4zGcTSHThfqr4uRjRxWQa4iN1438h3Z0S0NL2lR
-p75mpoo6Kr3HGrHhFPC+Oh25z1uxav60sUYgovseO3Dvk5h9jHOW8sXvhXCtKSb8
-HgQ+HKDYD8tSg2J87otTlZCpV6LqYQXY+U3EJ/pure3511H3a6UCAwEAAaOCASQw
-ggEgMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNOUikxiEyoZLsyvcop9Ntea
-HNxnMA4GA1UdDwEB/wQEAwIBBjCB3QYDVR0fBIHVMIHSMIGHoIGEoIGBhn9sZGFw
-Oi8vZGlyZWN0b3J5LmQtdHJ1c3QubmV0L0NOPUQtVFJVU1QlMjBSb290JTIwQ2xh
-c3MlMjAzJTIwQ0ElMjAyJTIwRVYlMjAyMDA5LE89RC1UcnVzdCUyMEdtYkgsQz1E
-RT9jZXJ0aWZpY2F0ZXJldm9jYXRpb25saXN0MEagRKBChkBodHRwOi8vd3d3LmQt
-dHJ1c3QubmV0L2NybC9kLXRydXN0X3Jvb3RfY2xhc3NfM19jYV8yX2V2XzIwMDku
-Y3JsMA0GCSqGSIb3DQEBCwUAA4IBAQA07XtaPKSUiO8aEXUHL7P+PPoeUSbrh/Yp
-3uDx1MYkCenBz1UbtDDZzhr+BlGmFaQt77JLvyAoJUnRpjZ3NOhk31KxEcdzes05
-nsKtjHEh8lprr988TlWvsoRlFIm5d8sqMb7Po23Pb0iUMkZv53GMoKaEGTcH8gNF
-CSuGdXzfX2lXANtu2KZyIktQ1HWYVt+3GP9DQ1CuekR78HlR10M9p9OB0/DJT7na
-xpeG0ILD5EJt/rDiZE4OJudANCa1CInXCGNjOCd1HjPqbqjdn5lPdE2BiYBL3ZqX
-KVwvvoFBuYz/6n1gBp7N1z3TLqMVvKjmJuVvw9y4AyHqnxbxLFS1
------END CERTIFICATE-----
-
-# EC-ACC
------BEGIN CERTIFICATE-----
-MIIFVjCCBD6gAwIBAgIQ7is969Qh3hSoYqwE893EATANBgkqhkiG9w0BAQUFADCB
-8zELMAkGA1UEBhMCRVMxOzA5BgNVBAoTMkFnZW5jaWEgQ2F0YWxhbmEgZGUgQ2Vy
-dGlmaWNhY2lvIChOSUYgUS0wODAxMTc2LUkpMSgwJgYDVQQLEx9TZXJ2ZWlzIFB1
-YmxpY3MgZGUgQ2VydGlmaWNhY2lvMTUwMwYDVQQLEyxWZWdldSBodHRwczovL3d3
-dy5jYXRjZXJ0Lm5ldC92ZXJhcnJlbCAoYykwMzE1MDMGA1UECxMsSmVyYXJxdWlh
-IEVudGl0YXRzIGRlIENlcnRpZmljYWNpbyBDYXRhbGFuZXMxDzANBgNVBAMTBkVD
-LUFDQzAeFw0wMzAxMDcyMzAwMDBaFw0zMTAxMDcyMjU5NTlaMIHzMQswCQYDVQQG
-EwJFUzE7MDkGA1UEChMyQWdlbmNpYSBDYXRhbGFuYSBkZSBDZXJ0aWZpY2FjaW8g
-KE5JRiBRLTA4MDExNzYtSSkxKDAmBgNVBAsTH1NlcnZlaXMgUHVibGljcyBkZSBD
-ZXJ0aWZpY2FjaW8xNTAzBgNVBAsTLFZlZ2V1IGh0dHBzOi8vd3d3LmNhdGNlcnQu
-bmV0L3ZlcmFycmVsIChjKTAzMTUwMwYDVQQLEyxKZXJhcnF1aWEgRW50aXRhdHMg
-ZGUgQ2VydGlmaWNhY2lvIENhdGFsYW5lczEPMA0GA1UEAxMGRUMtQUNDMIIBIjAN
-BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsyLHT+KXQpWIR4NA9h0X84NzJB5R
-85iKw5K4/0CQBXCHYMkAqbWUZRkiFRfCQ2xmRJoNBD45b6VLeqpjt4pEndljkYRm
-4CgPukLjbo73FCeTae6RDqNfDrHrZqJyTxIThmV6PttPB/SnCWDaOkKZx7J/sxaV
-HMf5NLWUhdWZXqBIoH7nF2W4onW4HvPlQn2v7fOKSGRdghST2MDk/7NQcvJ29rNd
-QlB50JQ+awwAvthrDk4q7D7SzIKiGGUzE3eeml0aE9jD2z3Il3rucO2n5nzbcc8t
-lGLfbdb1OL4/pYUKGbio2Al1QnDE6u/LDsg0qBIimAy4E5S2S+zw0JDnJwIDAQAB
-o4HjMIHgMB0GA1UdEQQWMBSBEmVjX2FjY0BjYXRjZXJ0Lm5ldDAPBgNVHRMBAf8E
-BTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUoMOLRKo3pUW/l4Ba0fF4
-opvpXY0wfwYDVR0gBHgwdjB0BgsrBgEEAfV4AQMBCjBlMCwGCCsGAQUFBwIBFiBo
-dHRwczovL3d3dy5jYXRjZXJ0Lm5ldC92ZXJhcnJlbDA1BggrBgEFBQcCAjApGidW
-ZWdldSBodHRwczovL3d3dy5jYXRjZXJ0Lm5ldC92ZXJhcnJlbCAwDQYJKoZIhvcN
-AQEFBQADggEBAKBIW4IB9k1IuDlVNZyAelOZ1Vr/sXE7zDkJlF7W2u++AVtd0x7Y
-/X1PzaBB4DSTv8vihpw3kpBWHNzrKQXlxJ7HNd+KDM3FIUPpqojlNcAZQmNaAl6k
-SBg6hW/cnbw/nZzBh7h6YQjpdwt/cKt63dmXLGQehb+8dJahw3oS7AwaboMMPOhy
-Rp/7SNVel+axofjk70YllJyJ22k4vuxcDlbHZVHlUIiIv0LVKz3l+bqeLrPK9HOS
-Agu+TGbrIP65y7WZf+a2E/rKS03Z7lNGBjvGTq2TWoF+bCpLagVFjPIhpDGQh2xl
-nJ2lYJU6Un/10asIbvPuW/mIPX64b24D5EI=
------END CERTIFICATE-----
-
-# EE Certification Centre Root CA
------BEGIN CERTIFICATE-----
-MIIEAzCCAuugAwIBAgIQVID5oHPtPwBMyonY43HmSjANBgkqhkiG9w0BAQUFADB1
-MQswCQYDVQQGEwJFRTEiMCAGA1UECgwZQVMgU2VydGlmaXRzZWVyaW1pc2tlc2t1
-czEoMCYGA1UEAwwfRUUgQ2VydGlmaWNhdGlvbiBDZW50cmUgUm9vdCBDQTEYMBYG
-CSqGSIb3DQEJARYJcGtpQHNrLmVlMCIYDzIwMTAxMDMwMTAxMDMwWhgPMjAzMDEy
-MTcyMzU5NTlaMHUxCzAJBgNVBAYTAkVFMSIwIAYDVQQKDBlBUyBTZXJ0aWZpdHNl
-ZXJpbWlza2Vza3VzMSgwJgYDVQQDDB9FRSBDZXJ0aWZpY2F0aW9uIENlbnRyZSBS
-b290IENBMRgwFgYJKoZIhvcNAQkBFglwa2lAc2suZWUwggEiMA0GCSqGSIb3DQEB
-AQUAA4IBDwAwggEKAoIBAQDIIMDs4MVLqwd4lfNE7vsLDP90jmG7sWLqI9iroWUy
-euuOF0+W2Ap7kaJjbMeMTC55v6kF/GlclY1i+blw7cNRfdCT5mzrMEvhvH2/UpvO
-bntl8jixwKIy72KyaOBhU8E2lf/slLo2rpwcpzIP5Xy0xm90/XsY6KxX7QYgSzIw
-WFv9zajmofxwvI6Sc9uXp3whrj3B9UiHbCe9nyV0gVWw93X2PaRka9ZP585ArQ/d
-MtO8ihJTmMmJ+xAdTX7Nfh9WDSFwhfYggx/2uh8Ej+p3iDXE/+pOoYtNP2MbRMNE
-1CV2yreN1x5KZmTNXMWcg+HCCIia7E6j8T4cLNlsHaFLAgMBAAGjgYowgYcwDwYD
-VR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFBLyWj7qVhy/
-zQas8fElyalL1BSZMEUGA1UdJQQ+MDwGCCsGAQUFBwMCBggrBgEFBQcDAQYIKwYB
-BQUHAwMGCCsGAQUFBwMEBggrBgEFBQcDCAYIKwYBBQUHAwkwDQYJKoZIhvcNAQEF
-BQADggEBAHv25MANqhlHt01Xo/6tu7Fq1Q+e2+RjxY6hUFaTlrg4wCQiZrxTFGGV
-v9DHKpY5P30osxBAIWrEr7BSdxjhlthWXePdNl4dp1BUoMUq5KqMlIpPnTX/dqQG
-E5Gion0ARD9V04I8GtVbvFZMIi5GQ4okQC3zErg7cBqklrkar4dBGmoYDQZPxz5u
-uSlNDUmJEYcyW+ZLBMjkXOZ0c5RdFpgTlf7727FE5TpwrDdr5rMzcijJs1eg9gIW
-iAYLtqZLICjU3j2LrTcFU3T+bsy8QxdxXvnFzBqpYe73dgzzcvRyrc9yAjYHR8/v
-GVCJYMzpJJUPwssd8m92kMfMdcGWxZ0=
------END CERTIFICATE-----
-
-# Entrust.net Premium 2048 Secure Server CA
------BEGIN CERTIFICATE-----
-MIIEKjCCAxKgAwIBAgIEOGPe+DANBgkqhkiG9w0BAQUFADCBtDEUMBIGA1UEChML
-RW50cnVzdC5uZXQxQDA+BgNVBAsUN3d3dy5lbnRydXN0Lm5ldC9DUFNfMjA0OCBp
-bmNvcnAuIGJ5IHJlZi4gKGxpbWl0cyBsaWFiLikxJTAjBgNVBAsTHChjKSAxOTk5
-IEVudHJ1c3QubmV0IExpbWl0ZWQxMzAxBgNVBAMTKkVudHJ1c3QubmV0IENlcnRp
-ZmljYXRpb24gQXV0aG9yaXR5ICgyMDQ4KTAeFw05OTEyMjQxNzUwNTFaFw0yOTA3
-MjQxNDE1MTJaMIG0MRQwEgYDVQQKEwtFbnRydXN0Lm5ldDFAMD4GA1UECxQ3d3d3
-LmVudHJ1c3QubmV0L0NQU18yMDQ4IGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxp
-YWIuKTElMCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDEzMDEG
-A1UEAxMqRW50cnVzdC5uZXQgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgKDIwNDgp
-MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArU1LqRKGsuqjIAcVFmQq
-K0vRvwtKTY7tgHalZ7d4QMBzQshowNtTK91euHaYNZOLGp18EzoOH1u3Hs/lJBQe
-sYGpjX24zGtLA/ECDNyrpUAkAH90lKGdCCmziAv1h3edVc3kw37XamSrhRSGlVuX
-MlBvPci6Zgzj/L24ScF2iUkZ/cCovYmjZy/Gn7xxGWC4LeksyZB2ZnuU4q941mVT
-XTzWnLLPKQP5L6RQstRIzgUyVYr9smRMDuSYB3Xbf9+5CFVghTAp+XtIpGmG4zU/
-HoZdenoVve8AjhUiVBcAkCaTvA5JaJG/+EfTnZVCwQ5N328mz8MYIWJmQ3DW1cAH
-4QIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNV
-HQ4EFgQUVeSB0RGAvtiJuQijMfmhJAkWuXAwDQYJKoZIhvcNAQEFBQADggEBADub
-j1abMOdTmXx6eadNl9cZlZD7Bh/KM3xGY4+WZiT6QBshJ8rmcnPyT/4xmf3IDExo
-U8aAghOY+rat2l098c5u9hURlIIM7j+VrxGrD9cv3h8Dj1csHsm7mhpElesYT6Yf
-zX1XEC+bBAlahLVu2B064dae0Wx5XnkcFMXj0EyTO2U87d89vqbllRrDtRnDvV5b
-u/8j72gZyxKTJ1wDLW8w0B62GqzeWvfRqqgnpv55gcR5mTNXuhKwqeBCbJPKVt7+
-bYQLCIt+jerXmCHG8+c8eS9enNFMFY3h7CI3zJpDC5fcgJCNs2ebb0gIFVbPv/Er
-fF6adulZkMV8gzURZVE=
------END CERTIFICATE-----
-
-# Entrust Root Certification Authority
------BEGIN CERTIFICATE-----
-MIIEkTCCA3mgAwIBAgIERWtQVDANBgkqhkiG9w0BAQUFADCBsDELMAkGA1UEBhMC
-VVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xOTA3BgNVBAsTMHd3dy5lbnRydXN0
-Lm5ldC9DUFMgaXMgaW5jb3Jwb3JhdGVkIGJ5IHJlZmVyZW5jZTEfMB0GA1UECxMW
-KGMpIDIwMDYgRW50cnVzdCwgSW5jLjEtMCsGA1UEAxMkRW50cnVzdCBSb290IENl
-cnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA2MTEyNzIwMjM0MloXDTI2MTEyNzIw
-NTM0MlowgbAxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMTkw
-NwYDVQQLEzB3d3cuZW50cnVzdC5uZXQvQ1BTIGlzIGluY29ycG9yYXRlZCBieSBy
-ZWZlcmVuY2UxHzAdBgNVBAsTFihjKSAyMDA2IEVudHJ1c3QsIEluYy4xLTArBgNV
-BAMTJEVudHJ1c3QgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASIwDQYJ
-KoZIhvcNAQEBBQADggEPADCCAQoCggEBALaVtkNC+sZtKm9I35RMOVcF7sN5EUFo
-Nu3s/poBj6E4KPz3EEZmLk0eGrEaTsbRwJWIsMn/MYszA9u3g3s+IIRe7bJWKKf4
-4LlAcTfFy0cOlypowCKVYhXbR9n10Cv/gkvJrT7eTNuQgFA/CYqEAOwwCj0Yzfv9
-KlmaI5UXLEWeH25DeW0MXJj+SKfFI0dcXv1u5x609mhF0YaDW6KKjbHjKYD+JXGI
-rb68j6xSlkuqUY3kEzEZ6E5Nn9uss2rVvDlUccp6en+Q3X0dgNmBu1kmwhH+5pPi
-94DkZfs0Nw4pgHBNrziGLp5/V6+eF67rHMsoIV+2HNjnogQi+dPa2MsCAwEAAaOB
-sDCBrTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zArBgNVHRAEJDAi
-gA8yMDA2MTEyNzIwMjM0MlqBDzIwMjYxMTI3MjA1MzQyWjAfBgNVHSMEGDAWgBRo
-kORnpKZTgMeGZqTx90tD+4S9bTAdBgNVHQ4EFgQUaJDkZ6SmU4DHhmak8fdLQ/uE
-vW0wHQYJKoZIhvZ9B0EABBAwDhsIVjcuMTo0LjADAgSQMA0GCSqGSIb3DQEBBQUA
-A4IBAQCT1DCw1wMgKtD5Y+iRDAUgqV8ZyntyTtSx29CW+1RaGSwMCPeyvIWonX9t
-O1KzKtvn1ISMY/YPyyYBkVBs9F8U4pN0wBOeMDpQ47RgxRzwIkSNcUesyBrJ6Zua
-AGAT/3B+XxFNSRuzFVJ7yVTav52Vr2ua2J7p8eRDjeIRRDq/r72DQnNSi6q7pynP
-9WQcCk3RvKqsnyrQ/39/2n3qse0wJcGE2jTSW3iDVuycNsMm4hH2Z0kdkquM++v/
-eu6FSqdQgPCnXEqULl8FmTxSQeDNtGPPAUO6nIPcj2A781q0tHuu2guQOHXvgR1m
-0vdXcDazv/wor3ElhVsT/h5/WrQ8
------END CERTIFICATE-----
-
-# Entrust Root Certification Authority - EC1
------BEGIN CERTIFICATE-----
-MIIC+TCCAoCgAwIBAgINAKaLeSkAAAAAUNCR+TAKBggqhkjOPQQDAzCBvzELMAkG
-A1UEBhMCVVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3
-d3cuZW50cnVzdC5uZXQvbGVnYWwtdGVybXMxOTA3BgNVBAsTMChjKSAyMDEyIEVu
-dHJ1c3QsIEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ugb25seTEzMDEGA1UEAxMq
-RW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRUMxMB4XDTEy
-MTIxODE1MjUzNloXDTM3MTIxODE1NTUzNlowgb8xCzAJBgNVBAYTAlVTMRYwFAYD
-VQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQLEx9TZWUgd3d3LmVudHJ1c3QubmV0
-L2xlZ2FsLXRlcm1zMTkwNwYDVQQLEzAoYykgMjAxMiBFbnRydXN0LCBJbmMuIC0g
-Zm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxMzAxBgNVBAMTKkVudHJ1c3QgUm9vdCBD
-ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEVDMTB2MBAGByqGSM49AgEGBSuBBAAi
-A2IABIQTydC6bUF74mzQ61VfZgIaJPRbiWlH47jCffHyAsWfoPZb1YsGGYZPUxBt
-ByQnoaD41UcZYUx9ypMn6nQM72+WCf5j7HBdNq1nd67JnXxVRDqiY1Ef9eNi1KlH
-Bz7MIKNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0O
-BBYEFLdj5xrdjekIplWDpOBqUEFlEUJJMAoGCCqGSM49BAMDA2cAMGQCMGF52OVC
-R98crlOZF7ZvHH3hvxGU0QOIdeSNiaSKd0bebWHvAvX7td/M/k7//qnmpwIwW5nX
-hTcGtXsI/esni0qU+eH6p44mCOh8kmhtc9hvJqwhAriZtyZBWyVgrtBIGu4G
------END CERTIFICATE-----
-
-# Entrust Root Certification Authority - G2
------BEGIN CERTIFICATE-----
-MIIEPjCCAyagAwIBAgIESlOMKDANBgkqhkiG9w0BAQsFADCBvjELMAkGA1UEBhMC
-VVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50
-cnVzdC5uZXQvbGVnYWwtdGVybXMxOTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3Qs
-IEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ugb25seTEyMDAGA1UEAxMpRW50cnVz
-dCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzIwHhcNMDkwNzA3MTcy
-NTU0WhcNMzAxMjA3MTc1NTU0WjCBvjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUVu
-dHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50cnVzdC5uZXQvbGVnYWwt
-dGVybXMxOTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0
-aG9yaXplZCB1c2Ugb25seTEyMDAGA1UEAxMpRW50cnVzdCBSb290IENlcnRpZmlj
-YXRpb24gQXV0aG9yaXR5IC0gRzIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
-AoIBAQC6hLZy254Ma+KZ6TABp3bqMriVQRrJ2mFOWHLP/vaCeb9zYQYKpSfYs1/T
-RU4cctZOMvJyig/3gxnQaoCAAEUesMfnmr8SVycco2gvCoe9amsOXmXzHHfV1IWN
-cCG0szLni6LVhjkCsbjSR87kyUnEO6fe+1R9V77w6G7CebI6C1XiUJgWMhNcL3hW
-wcKUs/Ja5CeanyTXxuzQmyWC48zCxEXFjJd6BmsqEZ+pCm5IO2/b1BEZQvePB7/1
-U1+cPvQXLOZprE4yTGJ36rfo5bs0vBmLrpxR57d+tVOxMyLlbc9wPBr64ptntoP0
-jaWvYkxN4FisZDQSA/i2jZRjJKRxAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAP
-BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqciZ60B7vfec7aVHUbI2fkBJmqzAN
-BgkqhkiG9w0BAQsFAAOCAQEAeZ8dlsa2eT8ijYfThwMEYGprmi5ZiXMRrEPR9RP/
-jTkrwPK9T3CMqS/qF8QLVJ7UG5aYMzyorWKiAHarWWluBh1+xLlEjZivEtRh2woZ
-Rkfz6/djwUAFQKXSt/S1mja/qYh2iARVBCuch38aNzx+LaUa2NSJXsq9rD1s2G2v
-1fN2D807iDginWyTmsQ9v4IbZT+mD12q/OWyFcq1rca8PdCE6OoGcrBNOTJ4vz4R
-nAuknZoh8/CbCzB428Hch0P+vGOaysXCHMnHjf87ElgI5rY97HosTvuDls4MPGmH
-VHOkc8KT/1EQrBVUAdj8BbGJoX90g5pJ19xOe4pIb4tF9g==
------END CERTIFICATE-----
-
-# ePKI Root Certification Authority
------BEGIN CERTIFICATE-----
-MIIFsDCCA5igAwIBAgIQFci9ZUdcr7iXAF7kBtK8nTANBgkqhkiG9w0BAQUFADBe
-MQswCQYDVQQGEwJUVzEjMCEGA1UECgwaQ2h1bmdod2EgVGVsZWNvbSBDby4sIEx0
-ZC4xKjAoBgNVBAsMIWVQS0kgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAe
-Fw0wNDEyMjAwMjMxMjdaFw0zNDEyMjAwMjMxMjdaMF4xCzAJBgNVBAYTAlRXMSMw
-IQYDVQQKDBpDaHVuZ2h3YSBUZWxlY29tIENvLiwgTHRkLjEqMCgGA1UECwwhZVBL
-SSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIICIjANBgkqhkiG9w0BAQEF
-AAOCAg8AMIICCgKCAgEA4SUP7o3biDN1Z82tH306Tm2d0y8U82N0ywEhajfqhFAH
-SyZbCUNsIZ5qyNUD9WBpj8zwIuQf5/dqIjG3LBXy4P4AakP/h2XGtRrBp0xtInAh
-ijHyl3SJCRImHJ7K2RKilTza6We/CKBk49ZCt0Xvl/T29de1ShUCWH2YWEtgvM3X
-DZoTM1PRYfl61dd4s5oz9wCGzh1NlDivqOx4UXCKXBCDUSH3ET00hl7lSM2XgYI1
-TBnsZfZrxQWh7kcT1rMhJ5QQCtkkO7q+RBNGMD+XPNjX12ruOzjjK9SXDrkb5wdJ
-fzcq+Xd4z1TtW0ado4AOkUPB1ltfFLqfpo0kR0BZv3I4sjZsN/+Z0V0OWQqraffA
-sgRFelQArr5T9rXn4fg8ozHSqf4hUmTFpmfwdQcGlBSBVcYn5AGPF8Fqcde+S/uU
-WH1+ETOxQvdibBjWzwloPn9s9h6PYq2lY9sJpx8iQkEeb5mKPtf5P0B6ebClAZLS
-nT0IFaUQAS2zMnaolQ2zepr7BxB4EW/hj8e6DyUadCrlHJhBmd8hh+iVBmoKs2pH
-dmX2Os+PYhcZewoozRrSgx4hxyy/vv9haLdnG7t4TY3OZ+XkwY63I2binZB1NJip
-NiuKmpS5nezMirH4JYlcWrYvjB9teSSnUmjDhDXiZo1jDiVN1Rmy5nk3pyKdVDEC
-AwEAAaNqMGgwHQYDVR0OBBYEFB4M97Zn8uGSJglFwFU5Lnc/QkqiMAwGA1UdEwQF
-MAMBAf8wOQYEZyoHAAQxMC8wLQIBADAJBgUrDgMCGgUAMAcGBWcqAwAABBRFsMLH
-ClZ87lt4DJX5GFPBphzYEDANBgkqhkiG9w0BAQUFAAOCAgEACbODU1kBPpVJufGB
-uvl2ICO1J2B01GqZNF5sAFPZn/KmsSQHRGoqxqWOeBLoR9lYGxMqXnmbnwoqZ6Yl
-PwZpVnPDimZI+ymBV3QGypzqKOg4ZyYr8dW1P2WT+DZdjo2NQCCHGervJ8A9tDkP
-JXtoUHRVnAxZfVo9QZQlUgjgRywVMRnVvwdVxrsStZf0X4OFunHB2WyBEXYKCrC/
-gpf36j36+uwtqSiUO1bd0lEursC9CBWMd1I0ltabrNMdjmEPNXubrjlpC2JgQCA2
-j6/7Nu4tCEoduL+bXPjqpRugc6bY+G7gMwRfaKonh+3ZwZCc7b3jajWvY9+rGNm6
-5ulK6lCKD2GTHuItGeIwlDWSXQ62B68ZgI9HkFFLLk3dheLSClIKF5r8GrBQAuUB
-o2M3IUxExJtRmREOc5wGj1QupyheRDmHVi03vYVElOEMSyycw5KFNGHLD7ibSkNS
-/jQ6fbjpKdx2qcgw+BRxgMYeNkh0IkFch4LoGHGLQYlE535YW6i4jRPpp2zDR+2z
-Gp1iro2C6pSe3VkQw63d4k3jMdXH7OjysP6SHhYKGvzZ8/gntsm+HbRsZJB/9OTE
-W9c3rkIO3aQab3yIVMUWbuF6aC74Or8NpDyJO3inTmODBCEIZ43ygknQW/2xzQ+D
-hNQ+IIX3Sj0rnP0qCglN6oH4EZw=
------END CERTIFICATE-----
-
-# E-Tugra Certification Authority
------BEGIN CERTIFICATE-----
-MIIGSzCCBDOgAwIBAgIIamg+nFGby1MwDQYJKoZIhvcNAQELBQAwgbIxCzAJBgNV
-BAYTAlRSMQ8wDQYDVQQHDAZBbmthcmExQDA+BgNVBAoMN0UtVHXEn3JhIEVCRyBC
-aWxpxZ9pbSBUZWtub2xvamlsZXJpIHZlIEhpem1ldGxlcmkgQS7Fni4xJjAkBgNV
-BAsMHUUtVHVncmEgU2VydGlmaWthc3lvbiBNZXJrZXppMSgwJgYDVQQDDB9FLVR1
-Z3JhIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTEzMDMwNTEyMDk0OFoXDTIz
-MDMwMzEyMDk0OFowgbIxCzAJBgNVBAYTAlRSMQ8wDQYDVQQHDAZBbmthcmExQDA+
-BgNVBAoMN0UtVHXEn3JhIEVCRyBCaWxpxZ9pbSBUZWtub2xvamlsZXJpIHZlIEhp
-em1ldGxlcmkgQS7Fni4xJjAkBgNVBAsMHUUtVHVncmEgU2VydGlmaWthc3lvbiBN
-ZXJrZXppMSgwJgYDVQQDDB9FLVR1Z3JhIENlcnRpZmljYXRpb24gQXV0aG9yaXR5
-MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA4vU/kwVRHoViVF56C/UY
-B4Oufq9899SKa6VjQzm5S/fDxmSJPZQuVIBSOTkHS0vdhQd2h8y/L5VMzH2nPbxH
-D5hw+IyFHnSOkm0bQNGZDbt1bsipa5rAhDGvykPL6ys06I+XawGb1Q5KCKpbknSF
-Q9OArqGIW66z6l7LFpp3RMih9lRozt6Plyu6W0ACDGQXwLWTzeHxE2bODHnv0ZEo
-q1+gElIwcxmOj+GMB6LDu0rw6h8VqO4lzKRG+Bsi77MOQ7osJLjFLFzUHPhdZL3D
-k14opz8n8Y4e0ypQBaNV2cvnOVPAmJ6MVGKLJrD3fY185MaeZkJVgkfnsliNZvcH
-fC425lAcP9tDJMW/hkd5s3kc91r0E+xs+D/iWR+V7kI+ua2oMoVJl0b+SzGPWsut
-dEcf6ZG33ygEIqDUD13ieU/qbIWGvaimzuT6w+Gzrt48Ue7LE3wBf4QOXVGUnhMM
-ti6lTPk5cDZvlsouDERVxcr6XQKj39ZkjFqzAQqptQpHF//vkUAqjqFGOjGY5RH8
-zLtJVor8udBhmm9lbObDyz51Sf6Pp+KJxWfXnUYTTjF2OySznhFlhqt/7x3U+Lzn
-rFpct1pHXFXOVbQicVtbC/DP3KBhZOqp12gKY6fgDT+gr9Oq0n7vUaDmUStVkhUX
-U8u3Zg5mTPj5dUyQ5xJwx0UCAwEAAaNjMGEwHQYDVR0OBBYEFC7j27JJ0JxUeVz6
-Jyr+zE7S6E5UMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAULuPbsknQnFR5
-XPonKv7MTtLoTlQwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBCwUAA4ICAQAF
-Nzr0TbdF4kV1JI+2d1LoHNgQk2Xz8lkGpD4eKexd0dCrfOAKkEh47U6YA5n+KGCR
-HTAduGN8qOY1tfrTYXbm1gdLymmasoR6d5NFFxWfJNCYExL/u6Au/U5Mh/jOXKqY
-GwXgAEZKgoClM4so3O0409/lPun++1ndYYRP0lSWE2ETPo+Aab6TR7U1Q9Jauz1c
-77NCR807VRMGsAnb/WP2OogKmW9+4c4bU2pEZiNRCHu8W1Ki/QY3OEBhj0qWuJA3
-+GbHeJAAFS6LrVE1Uweoa2iu+U48BybNCAVwzDk/dr2l02cmAYamU9JgO3xDf1WK
-vJUawSg5TB9D0pH0clmKuVb8P7Sd2nCcdlqMQ1DujjByTd//SffGqWfZbawCEeI6
-FiWnWAjLb1NBnEg4R2gz0dfHj9R0IdTDBZB6/86WiLEVKV0jq9BgoRJP3vQXzTLl
-yb/IQ639Lo7xr+L0mPoSHyDYwKcMhcWQ9DstliaxLL5Mq+ux0orJ23gTDx4JnW2P
-AJ8C2sH6H3p6CcRK5ogql5+Ji/03X186zjhZhkuvcQu02PJwT58yE+Owp1fl2tpD
-y4Q08ijE6m30Ku/Ba3ba+367hTzSU8JNvnHhRdH9I2cNE3X7z2VnIp2usAnRCf8d
-NL/+I5c30jn6PQ0GC7TbO6Orb1wdtn7os4I07QZcJA==
------END CERTIFICATE-----
-
-# GDCA TrustAUTH R5 ROOT
------BEGIN CERTIFICATE-----
-MIIFiDCCA3CgAwIBAgIIfQmX/vBH6nowDQYJKoZIhvcNAQELBQAwYjELMAkGA1UE
-BhMCQ04xMjAwBgNVBAoMKUdVQU5HIERPTkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZ
-IENPLixMVEQuMR8wHQYDVQQDDBZHRENBIFRydXN0QVVUSCBSNSBST09UMB4XDTE0
-MTEyNjA1MTMxNVoXDTQwMTIzMTE1NTk1OVowYjELMAkGA1UEBhMCQ04xMjAwBgNV
-BAoMKUdVQU5HIERPTkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZIENPLixMVEQuMR8w
-HQYDVQQDDBZHRENBIFRydXN0QVVUSCBSNSBST09UMIICIjANBgkqhkiG9w0BAQEF
-AAOCAg8AMIICCgKCAgEA2aMW8Mh0dHeb7zMNOwZ+Vfy1YI92hhJCfVZmPoiC7XJj
-Dp6L3TQsAlFRwxn9WVSEyfFrs0yw6ehGXTjGoqcuEVe6ghWinI9tsJlKCvLriXBj
-TnnEt1u9ol2x8kECK62pOqPseQrsXzrj/e+APK00mxqriCZ7VqKChh/rNYmDf1+u
-KU49tm7srsHwJ5uu4/Ts765/94Y9cnrrpftZTqfrlYwiOXnhLQiPzLyRuEH3FMEj
-qcOtmkVEs7LXLM3GKeJQEK5cy4KOFxg2fZfmiJqwTTQJ9Cy5WmYqsBebnh52nUpm
-MUHfP/vFBu8btn4aRjb3ZGM74zkYI+dndRTVdVeSN72+ahsmUPI2JgaQxXABZG12
-ZuGR224HwGGALrIuL4xwp9E7PLOR5G62xDtw8mySlwnNR30YwPO7ng/Wi64HtloP
-zgsMR6flPri9fcebNaBhlzpBdRfMK5Z3KpIhHtmVdiBnaM8Nvd/WHwlqmuLMc3Gk
-L30SgLdTMEZeS1SZD2fJpcjyIMGC7J0R38IC+xo70e0gmu9lZJIQDSri3nDxGGeC
-jGHeuLzRL5z7D9Ar7Rt2ueQ5Vfj4oR24qoAATILnsn8JuLwwoC8N9VKejveSswoA
-HQBUlwbgsQfZxw9cZX08bVlX5O2ljelAU58VS6Bx9hoh49pwBiFYFIeFd3mqgnkC
-AwEAAaNCMEAwHQYDVR0OBBYEFOLJQJ9NzuiaoXzPDj9lxSmIahlRMA8GA1UdEwEB
-/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4ICAQDRSVfg
-p8xoWLoBDysZzY2wYUWsEe1jUGn4H3++Fo/9nesLqjJHdtJnJO29fDMylyrHBYZm
-DRd9FBUb1Ov9H5r2XpdptxolpAqzkT9fNqyL7FeoPueBihhXOYV0GkLH6VsTX4/5
-COmSdI31R9KrO9b7eGZONn356ZLpBN79SWP8bfsUcZNnL0dKt7n/HipzcEYwv1ry
-L3ml4Y0M2fmyYzeMN2WFcGpcWwlyua1jPLHd+PwyvzeG5LuOmCd+uh8W4XAR8gPf
-JWIyJyYYMoSf/wA6E7qaTfRPuBRwIrHKK5DOKcFw9C+df/KQHtZa37dG/OaG+svg
-IHZ6uqbL9XzeYqWxi+7egmaKTjowHz+Ay60nugxe19CxVsp3cbK1daFQqUBDF8Io
-2c9Si1vIY9RCPqAzekYu9wogRlR+ak8x8YF+QnQ4ZXMn7sZ8uI7XpTrXmKGcjBBV
-09tL7ECQ8s1uV9JiDnxXk7Gnbc2dg7sq5+W2O3FYrf3RRbxake5TFW/TRQl1brqQ
-XR4EzzffHqhmsYzmIGrv/EhOdJhCrylvLmrH+33RZjEizIYAfmaDDEL0vTSSwxrq
-T8p+ck0LcIymSLumoRT2+1hEmRSuqguTaaApJUqlyyvdimYHFngVV3Eb7PVHhPOe
-MTd61X8kreS8/f3MboPoDKi3QWwH3b08hpcv0g==
------END CERTIFICATE-----
-
-# GeoTrust Global CA
------BEGIN CERTIFICATE-----
-MIIDVDCCAjygAwIBAgIDAjRWMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT
-MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i
-YWwgQ0EwHhcNMDIwNTIxMDQwMDAwWhcNMjIwNTIxMDQwMDAwWjBCMQswCQYDVQQG
-EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEbMBkGA1UEAxMSR2VvVHJ1c3Qg
-R2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2swYYzD9
-9BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEHCIjaWC9mOSm9BXiLnTjoBbdq
-fnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlCGDUUna2YRpIuT8rxh0PBFpVXLVDv
-iS2Aelet8u5fa9IAjbkU+BQVNdnARqN7csiRv8lVK83Qlz6cJmTM386DGXHKTubU
-1XupGc1V3sjs0l44U+VcT4wt/lAjNvxm5suOpDkZALeVAjmRCw7+OC7RHQWa9k0+
-bw8HHa8sHo9gOeL6NlMTOdReJivbPagUvTLrGAMoUgRx5aszPeE4uwc2hGKceeoW
-MPRfwCvocWvk+QIDAQABo1MwUTAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTA
-ephojYn7qwVkDBF9qn1luMrMTjAfBgNVHSMEGDAWgBTAephojYn7qwVkDBF9qn1l
-uMrMTjANBgkqhkiG9w0BAQUFAAOCAQEANeMpauUvXVSOKVCUn5kaFOSPeCpilKIn
-Z57QzxpeR+nBsqTP3UEaBU6bS+5Kb1VSsyShNwrrZHYqLizz/Tt1kL/6cdjHPTfS
-tQWVYrmm3ok9Nns4d0iXrKYgjy6myQzCsplFAMfOEVEiIuCl6rYVSAlk6l5PdPcF
-PseKUgzbFbS9bZvlxrFUaKnjaZC2mqUPuLk/IH2uSrW4nOQdtqvmlKXBx4Ot2/Un
-hw4EbNX/3aBd7YdStysVAq45pmp06drE57xNNB6pXE0zX5IJL4hmXXeXxx12E6nV
-5fEWCRE11azbJHFwLJhWC9kXtNHjUStedejV0NxPNO3CBWaAocvmMw==
------END CERTIFICATE-----
-
-# GeoTrust Primary Certification Authority
------BEGIN CERTIFICATE-----
-MIIDfDCCAmSgAwIBAgIQGKy1av1pthU6Y2yv2vrEoTANBgkqhkiG9w0BAQUFADBY
-MQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjExMC8GA1UEAxMo
-R2VvVHJ1c3QgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjEx
-MjcwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMFgxCzAJBgNVBAYTAlVTMRYwFAYDVQQK
-Ew1HZW9UcnVzdCBJbmMuMTEwLwYDVQQDEyhHZW9UcnVzdCBQcmltYXJ5IENlcnRp
-ZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
-AQEAvrgVe//UfH1nrYNke8hCUy3f9oQIIGHWAVlqnEQRr+92/ZV+zmEwu3qDXwK9
-AWbK7hWNb6EwnL2hhZ6UOvNWiAAxz9juapYC2e0DjPt1befquFUWBRaa9OBesYjA
-ZIVcFU2Ix7e64HXprQU9nceJSOC7KMgD4TCTZF5SwFlwIjVXiIrxlQqD17wxcwE0
-7e9GceBrAqg1cmuXm2bgyxx5X9gaBGgeRwLmnWDiNpcB3841kt++Z8dtd1k7j53W
-kBWUvEI0EME5+bEnPn7WinXFsq+W06Lem+SYvn3h6YGttm/81w7a4DSwDRp35+MI
-mO9Y+pyEtzavwt+s0vQQBnBxNQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4G
-A1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQULNVQQZcVi/CPNmFbSvtr2ZnJM5IwDQYJ
-KoZIhvcNAQEFBQADggEBAFpwfyzdtzRP9YZRqSa+S7iq8XEN3GHHoOo0Hnp3DwQ1
-6CePbJC/kRYkRj5KTs4rFtULUh38H2eiAkUxT87z+gOneZ1TatnaYzr4gNfTmeGl
-4b7UVXGYNTq+k+qurUKykG/g/CFNNWMziUnWm07Kx+dOCQD32sfvmWKZd7aVIl6K
-oKv0uHiYyjgZmclynnjNS6yvGaBzEi38wkG6gZHaFloxt/m0cYASSJlyc1pZU8Fj
-UjPtp8nSOQJw+uCxQmYpqptR7TBUIhRf2asdweSU8Pj1K/fqynhG1riR/aYNKxoU
-AT6A8EKglQdebc3MS6RFjasS6LPeWuWgfOgPIh1a6Vk=
------END CERTIFICATE-----
-
-# GeoTrust Primary Certification Authority - G2
------BEGIN CERTIFICATE-----
-MIICrjCCAjWgAwIBAgIQPLL0SAoA4v7rJDteYD7DazAKBggqhkjOPQQDAzCBmDEL
-MAkGA1UEBhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xOTA3BgNVBAsTMChj
-KSAyMDA3IEdlb1RydXN0IEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTE2
-MDQGA1UEAxMtR2VvVHJ1c3QgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0
-eSAtIEcyMB4XDTA3MTEwNTAwMDAwMFoXDTM4MDExODIzNTk1OVowgZgxCzAJBgNV
-BAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTkwNwYDVQQLEzAoYykgMjAw
-NyBHZW9UcnVzdCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxNjA0BgNV
-BAMTLUdlb1RydXN0IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBH
-MjB2MBAGByqGSM49AgEGBSuBBAAiA2IABBWx6P0DFUPlrOuHNxFi79KDNlJ9RVcL
-So17VDs6bl8VAsBQps8lL33KSLjHUGMcKiEIfJo22Av+0SbFWDEwKCXzXV2juLal
-tJLtbCyf691DiaI8S0iRHVDsJt/WYC69IaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAO
-BgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFBVfNVdRVfslsq0DafwBo/q+EVXVMAoG
-CCqGSM49BAMDA2cAMGQCMGSWWaboCd6LuvpaiIjwH5HTRqjySkwCY/tsXzjbLkGT
-qQ7mndwxHLKgpxgceeHHNgIwOlavmnRs9vuD4DPTCF+hnMJbn0bWtsuRBmOiBucz
-rD6ogRLQy7rQkgu2npaqBA+K
------END CERTIFICATE-----
-
-# GeoTrust Primary Certification Authority - G3
------BEGIN CERTIFICATE-----
-MIID/jCCAuagAwIBAgIQFaxulBmyeUtB9iepwxgPHzANBgkqhkiG9w0BAQsFADCB
-mDELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xOTA3BgNVBAsT
-MChjKSAyMDA4IEdlb1RydXN0IEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25s
-eTE2MDQGA1UEAxMtR2VvVHJ1c3QgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhv
-cml0eSAtIEczMB4XDTA4MDQwMjAwMDAwMFoXDTM3MTIwMTIzNTk1OVowgZgxCzAJ
-BgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTkwNwYDVQQLEzAoYykg
-MjAwOCBHZW9UcnVzdCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxNjA0
-BgNVBAMTLUdlb1RydXN0IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkg
-LSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANziXmJYHTNXOTIz
-+uvLh4yn1ErdBojqZI4xmKU4kB6Yzy5jK/BGvESyiaHAKAxJcCGVn2TAppMSAmUm
-hsalifD614SgcK9PGpc/BkTVyetyEH3kMSj7HGHmKAdEc5IiaacDiGydY8hS2pgn
-5whMcD60yRLBxWeDXTPzAxHsatBT4tG6NmCUgLthY2xbF37fQJQeqw3CIShwiP/W
-JmxsYAQlTlV+fe+/lEjetx3dcI0FX4ilm/LC7urRQEFtYjgdVgbFA0dRIBn8exAL
-DmKudlW/X3e+PkkBUz2YJQN2JFodtNuJ6nnltrM7P7pMKEF/BqxqjsHQ9gUdfeZC
-huOl1UcCAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYw
-HQYDVR0OBBYEFMR5yo6hTgMdHNxr2zFblD4/MH8tMA0GCSqGSIb3DQEBCwUAA4IB
-AQAtxRPPVoB7eni9n64smefv2t+UXglpp+duaIy9cr5HqQ6XErhK8WTTOd8lNNTB
-zU6B8A8ExCSzNJbGpqow32hhc9f5joWJ7w5elShKKiePEI4ufIbEAp7aDHdlDkQN
-kv39sxY2+hENHYwOB4lqKVb3cvTdFZx3NWZXqxNT2I7BQMXXExZacse3aQHEerGD
-AWh9jUGhlBjBJVz88P6DAod8DQ3PLghcSkANPuyBYeYk28rgDi0Hsj5W3I31QYUH
-SJsMC8tJP33st/3LjWeJGqvtux6jAAgIFyqCXDFdRootD4abdNlF+9RAsXqqaC2G
-spki4cErx5z481+oghLrGREt
------END CERTIFICATE-----
-
-# GeoTrust Universal CA
------BEGIN CERTIFICATE-----
-MIIFaDCCA1CgAwIBAgIBATANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQGEwJVUzEW
-MBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEeMBwGA1UEAxMVR2VvVHJ1c3QgVW5pdmVy
-c2FsIENBMB4XDTA0MDMwNDA1MDAwMFoXDTI5MDMwNDA1MDAwMFowRTELMAkGA1UE
-BhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xHjAcBgNVBAMTFUdlb1RydXN0
-IFVuaXZlcnNhbCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAKYV
-VaCjxuAfjJ0hUNfBvitbtaSeodlyWL0AG0y/YckUHUWCq8YdgNY96xCcOq9tJPi8
-cQGeBvV8Xx7BDlXKg5pZMK4ZyzBIle0iN430SppyZj6tlcDgFgDgEB8rMQ7XlFTT
-QjOgNB0eRXbdT8oYN+yFFXoZCPzVx5zw8qkuEKmS5j1YPakWaDwvdSEYfyh3peFh
-F7em6fgemdtzbvQKoiFs7tqqhZJmr/Z6a4LauiIINQ/PQvE1+mrufislzDoR5G2v
-c7J2Ha3QsnhnGqQ5HFELZ1aD/ThdDc7d8Lsrlh/eezJS/R27tQahsiFepdaVaH/w
-mZ7cRQg+59IJDTWU3YBOU5fXtQlEIGQWFwMCTFMNaN7VqnJNk22CDtucvc+081xd
-VHppCZbW2xHBjXWotM85yM48vCR85mLK4b19p71XZQvk/iXttmkQ3CgaRr0BHdCX
-teGYO8A3ZNY9lO4L4fUorgtWv3GLIylBjobFS1J72HGrH4oVpjuDWtdYAVHGTEHZ
-f9hBZ3KiKN9gg6meyHv8U3NyWfWTehd2Ds735VzZC1U0oqpbtWpU5xPKV+yXbfRe
-Bi9Fi1jUIxaS5BZuKGNZMN9QAZxjiRqf2xeUgnA3wySemkfWWspOqGmJch+RbNt+
-nhutxx9z3SxPGWX9f5NAEC7S8O08ni4oPmkmM8V7AgMBAAGjYzBhMA8GA1UdEwEB
-/wQFMAMBAf8wHQYDVR0OBBYEFNq7LqqwDLiIJlF0XG0D08DYj3rWMB8GA1UdIwQY
-MBaAFNq7LqqwDLiIJlF0XG0D08DYj3rWMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG
-9w0BAQUFAAOCAgEAMXjmx7XfuJRAyXHEqDXsRh3ChfMoWIawC/yOsjmPRFWrZIRc
-aanQmjg8+uUfNeVE44B5lGiku8SfPeE0zTBGi1QrlaXv9z+ZhP015s8xxtxqv6fX
-IwjhmF7DWgh2qaavdy+3YL1ERmrvl/9zlcGO6JP7/TG37FcREUWbMPEaiDnBTzyn
-ANXH/KttgCJwpQzgXQQpAvvLoJHRfNbDflDVnVi+QTjruXU8FdmbyUqDWcDaU/0z
-uzYYm4UPFd3uLax2k7nZAY1IEKj79TiG8dsKxr2EoyNB3tZ3b4XUhRxQ4K5RirqN
-Pnbiucon8l+f725ZDQbYKxek0nxru18UGkiPGkzns0ccjkxFKyDuSN/n3QmOGKja
-QI2SJhFTYXNd673nxE0pN2HrrDktZy4W1vUAg4WhzH92xH3kt0tm7wNFYGm2DFKW
-koRepqO1pD4r2czYG0eq8kTaT/kD6PAUyz/zg97QwVTjt+gKN02LIFkDMBmhLMi9
-ER/frslKxfMnZmaGrGiR/9nmUxwPi1xpZQomyB40w11Re9epnAahNt3ViZS82eQt
-DF4JbAiXfKM9fJP/P6EUp8+1Xevb2xzEdt+Iub1FBZUbrvxGakyvSOPOrg/Sfuvm
-bJxPgWp6ZKy7PtXny3YuxadIwVyQD8vIP/rmMuGNG2+k5o7Y+SlIis5z/iw=
------END CERTIFICATE-----
-
-# GeoTrust Universal CA 2
------BEGIN CERTIFICATE-----
-MIIFbDCCA1SgAwIBAgIBATANBgkqhkiG9w0BAQUFADBHMQswCQYDVQQGEwJVUzEW
-MBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEgMB4GA1UEAxMXR2VvVHJ1c3QgVW5pdmVy
-c2FsIENBIDIwHhcNMDQwMzA0MDUwMDAwWhcNMjkwMzA0MDUwMDAwWjBHMQswCQYD
-VQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEgMB4GA1UEAxMXR2VvVHJ1
-c3QgVW5pdmVyc2FsIENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
-AQCzVFLByT7y2dyxUxpZKeexw0Uo5dfR7cXFS6GqdHtXr0om/Nj1XqduGdt0DE81
-WzILAePb63p3NeqqWuDW6KFXlPCQo3RWlEQwAx5cTiuFJnSCegx2oG9NzkEtoBUG
-FF+3Qs17j1hhNNwqCPkuwwGmIkQcTAeC5lvO0Ep8BNMZcyfwqph/Lq9O64ceJHdq
-XbboW0W63MOhBW9Wjo8QJqVJwy7XQYci4E+GymC16qFjwAGXEHm9ADwSbSsVsaxL
-se4YuU6W3Nx2/zu+z18DwPw76L5GG//aQMJS9/7jOvdqdzXQ2o3rXhhqMcceujwb
-KNZrVMaqW9eiLBsZzKIC9ptZvTdrhrVtgrrY6slWvKk2WP0+GfPtDCapkzj4T8Fd
-IgbQl+rhrcZV4IErKIM6+vR7IVEAvlI4zs1meaj0gVbi0IMJR1FbUGrP20gaXT73
-y/Zl92zxlfgCOzJWgjl6W70viRu/obTo/3+NjN8D8WBOWBFM66M/ECuDmgFz2ZRt
-hAAnZqzwcEAJQpKtT5MNYQlRJNiS1QuUYbKHsu3/mjX/hVTK7URDrBs8FmtISgoc
-QIgfksILAAX/8sgCSqSqqcyZlpwvWOB94b67B9xfBHJcMTTD7F8t4D1kkCLm0ey4
-Lt1ZrtmhN79UNdxzMk+MBB4zsslG8dhcyFVQyWi9qLo2CQIDAQABo2MwYTAPBgNV
-HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR281Xh+qQ2+/CfXGJx7Tz0RzgQKzAfBgNV
-HSMEGDAWgBR281Xh+qQ2+/CfXGJx7Tz0RzgQKzAOBgNVHQ8BAf8EBAMCAYYwDQYJ
-KoZIhvcNAQEFBQADggIBAGbBxiPz2eAubl/oz66wsCVNK/g7WJtAJDday6sWSf+z
-dXkzoS9tcBc0kf5nfo/sm+VegqlVHy/c1FEHEv6sFj4sNcZj/NwQ6w2jqtB8zNHQ
-L1EuxBRa3ugZ4T7GzKQp5y6EqgYweHZUcyiYWTjgAA1i00J9IZ+uPTqM1fp3DRgr
-Fg5fNuH8KrUwJM/gYwx7WBr+mbpCErGR9Hxo4sjoryzqyX6uuyo9DRXcNJW2GHSo
-ag/HtPQTxORb7QrSpJdMKu0vbBKJPfEncKpqA1Ihn0CoZ1Dy81of398j9tx4TuaY
-T1U6U+Pv8vSfx3zYWK8pIpe44L2RLrB27FcRz+8pRPPphXpgY+RdM4kX2TGq2tbz
-GDVyz4crL2MjhF2EjD9XoIj8mZEoJmmZ1I+XRL6O1UixpCgp8RW04eWe3fiPpm8m
-1wk8OhwRDqZsN/etRIcsKMfYdIKz0G9KV7s1KSegi+ghp4dkNl3M2Basx7InQJJV
-OCiNUW7dFGdTbHFcJoRNdVq2fmBWqU2t+5sel/MN2dKXVHfaPRK34B7vCAas+YWH
-6aLcr34YEoP9VhdBLtUpgn2Z9DH2canPLAEnpQW5qrJITirvn5NSUZU8UnOOVkwX
-QMAJKOSLakhT2+zNVVXxxvjpoixMptEmX36vWkzaH6byHCx+rgIW0lbQL1dTR+iS
------END CERTIFICATE-----
-
-# Global Chambersign Root - 2008
------BEGIN CERTIFICATE-----
-MIIHSTCCBTGgAwIBAgIJAMnN0+nVfSPOMA0GCSqGSIb3DQEBBQUAMIGsMQswCQYD
-VQQGEwJFVTFDMEEGA1UEBxM6TWFkcmlkIChzZWUgY3VycmVudCBhZGRyZXNzIGF0
-IHd3dy5jYW1lcmZpcm1hLmNvbS9hZGRyZXNzKTESMBAGA1UEBRMJQTgyNzQzMjg3
-MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMuQS4xJzAlBgNVBAMTHkdsb2JhbCBD
-aGFtYmVyc2lnbiBSb290IC0gMjAwODAeFw0wODA4MDExMjMxNDBaFw0zODA3MzEx
-MjMxNDBaMIGsMQswCQYDVQQGEwJFVTFDMEEGA1UEBxM6TWFkcmlkIChzZWUgY3Vy
-cmVudCBhZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNvbS9hZGRyZXNzKTESMBAG
-A1UEBRMJQTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMuQS4xJzAl
-BgNVBAMTHkdsb2JhbCBDaGFtYmVyc2lnbiBSb290IC0gMjAwODCCAiIwDQYJKoZI
-hvcNAQEBBQADggIPADCCAgoCggIBAMDfVtPkOpt2RbQT2//BthmLN0EYlVJH6xed
-KYiONWwGMi5HYvNJBL99RDaxccy9Wglz1dmFRP+RVyXfXjaOcNFccUMd2drvXNL7
-G706tcuto8xEpw2uIRU/uXpbknXYpBI4iRmKt4DS4jJvVpyR1ogQC7N0ZJJ0YPP2
-zxhPYLIj0Mc7zmFLmY/CDNBAspjcDahOo7kKrmCgrUVSY7pmvWjg+b4aqIG7HkF4
-ddPB/gBVsIdU6CeQNR1MM62X/JcumIS/LMmjv9GYERTtY/jKmIhYF5ntRQOXfjyG
-HoiMvvKRhI9lNNgATH23MRdaKXoKGCQwoze1eqkBfSbW+Q6OWfH9GzO1KTsXO0G2
-Id3UwD2ln58fQ1DJu7xsepeY7s2MH/ucUa6LcL0nn3HAa6x9kGbo1106DbDVwo3V
-yJ2dwW3Q0L9R5OP4wzg2rtandeavhENdk5IMagfeOx2YItaswTXbo6Al/3K1dh3e
-beksZixShNBFks4c5eUzHdwHU1SjqoI7mjcv3N2gZOnm3b2u/GSFHTynyQbehP9r
-6GsaPMWis0L7iwk+XwhSx2LE1AVxv8Rk5Pihg+g+EpuoHtQ2TS9x9o0o9oOpE9Jh
-wZG7SMA0j0GMS0zbaRL/UJScIINZc+18ofLx/d33SdNDWKBWY8o9PeU1VlnpDsog
-zCtLkykPAgMBAAGjggFqMIIBZjASBgNVHRMBAf8ECDAGAQH/AgEMMB0GA1UdDgQW
-BBS5CcqcHtvTbDprru1U8VuTBjUuXjCB4QYDVR0jBIHZMIHWgBS5CcqcHtvTbDpr
-ru1U8VuTBjUuXqGBsqSBrzCBrDELMAkGA1UEBhMCRVUxQzBBBgNVBAcTOk1hZHJp
-ZCAoc2VlIGN1cnJlbnQgYWRkcmVzcyBhdCB3d3cuY2FtZXJmaXJtYS5jb20vYWRk
-cmVzcykxEjAQBgNVBAUTCUE4Mjc0MzI4NzEbMBkGA1UEChMSQUMgQ2FtZXJmaXJt
-YSBTLkEuMScwJQYDVQQDEx5HbG9iYWwgQ2hhbWJlcnNpZ24gUm9vdCAtIDIwMDiC
-CQDJzdPp1X0jzjAOBgNVHQ8BAf8EBAMCAQYwPQYDVR0gBDYwNDAyBgRVHSAAMCow
-KAYIKwYBBQUHAgEWHGh0dHA6Ly9wb2xpY3kuY2FtZXJmaXJtYS5jb20wDQYJKoZI
-hvcNAQEFBQADggIBAICIf3DekijZBZRG/5BXqfEv3xoNa/p8DhxJJHkn2EaqbylZ
-UohwEurdPfWbU1Rv4WCiqAm57OtZfMY18dwY6fFn5a+6ReAJ3spED8IXDneRRXoz
-X1+WLGiLwUePmJs9wOzL9dWCkoQ10b42OFZyMVtHLaoXpGNR6woBrX/sdZ7LoR/x
-fxKxueRkf2fWIyr0uDldmOghp+G9PUIadJpwr2hsUF1Jz//7Dl3mLEfXgTpZALVz
-a2Mg9jFFCDkO9HB+QHBaP9BrQql0PSgvAm11cpUJjUhjxsYjV5KTXjXBjfkK9yyd
-Yhz2rXzdpjEetrHHfoUm+qRqtdpjMNHvkzeyZi99Bffnt0uYlDXA2TopwZ2yUDMd
-SqlapskD7+3056huirRXhOukP9DuqqqHW2Pok+JrqNS4cnhrG+055F3Lm6qH1U9O
-AP7Zap88MQ8oAgF9mOinsKJknnn4SPIVqczmyETrP3iZ8ntxPjzxmKfFGBI/5rso
-M0LpRQp8bfKGeS/Fghl9CYl8slR2iK7ewfPM4W7bMdaTrpmg7yVqc5iJWzouE4ge
-v8CSlDQb4ye3ix5vQv/n6TebUB0tovkC7stYWDpxvGjjqsGvHCgfotwjZT+B6q6Z
-09gwzxMNTxXJhLynSC34MCN32EZLeW32jO06f2ARePTpm67VVMB0gNELQp/B
------END CERTIFICATE-----
-
-# GlobalSign ECC Root CA - R4
------BEGIN CERTIFICATE-----
-MIIB4TCCAYegAwIBAgIRKjikHJYKBN5CsiilC+g0mAIwCgYIKoZIzj0EAwIwUDEk
-MCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI0MRMwEQYDVQQKEwpH
-bG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWduMB4XDTEyMTExMzAwMDAwMFoX
-DTM4MDExOTAzMTQwN1owUDEkMCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBD
-QSAtIFI0MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWdu
-MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEuMZ5049sJQ6fLjkZHAOkrprlOQcJ
-FspjsbmG+IpXwVfOQvpzofdlQv8ewQCybnMO/8ch5RikqtlxP6jUuc6MHaNCMEAw
-DgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFFSwe61F
-uOJAf/sKbvu+M8k8o4TVMAoGCCqGSM49BAMCA0gAMEUCIQDckqGgE6bPA7DmxCGX
-kPoUVy0D7O48027KqGx2vKLeuwIgJ6iFJzWbVsaj8kfSt24bAgAXqmemFZHe+pTs
-ewv4n4Q=
------END CERTIFICATE-----
-
-# GlobalSign ECC Root CA - R5
------BEGIN CERTIFICATE-----
-MIICHjCCAaSgAwIBAgIRYFlJ4CYuu1X5CneKcflK2GwwCgYIKoZIzj0EAwMwUDEk
-MCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI1MRMwEQYDVQQKEwpH
-bG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWduMB4XDTEyMTExMzAwMDAwMFoX
-DTM4MDExOTAzMTQwN1owUDEkMCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBD
-QSAtIFI1MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWdu
-MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAER0UOlvt9Xb/pOdEh+J8LttV7HpI6SFkc
-8GIxLcB6KP4ap1yztsyX50XUWPrRd21DosCHZTQKH3rd6zwzocWdTaRvQZU4f8ke
-hOvRnkmSh5SHDDqFSmafnVmTTZdhBoZKo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYD
-VR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUPeYpSJvqB8ohREom3m7e0oPQn1kwCgYI
-KoZIzj0EAwMDaAAwZQIxAOVpEslu28YxuglB4Zf4+/2a4n0Sye18ZNPLBSWLVtmg
-515dTguDnFt2KaAJJiFqYgIwcdK1j1zqO+F4CYWodZI7yFz9SO8NdCKoCOJuxUnO
-xwy8p2Fp8fc74SrL+SvzZpA3
------END CERTIFICATE-----
-
-# GlobalSign Root CA
------BEGIN CERTIFICATE-----
-MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkG
-A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv
-b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAw
-MDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i
-YWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJHbG9iYWxT
-aWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDaDuaZ
-jc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavp
-xy0Sy6scTHAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp
-1Wrjsok6Vjk4bwY8iGlbKk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdG
-snUOhugZitVtbNV4FpWi6cgKOOvyJBNPc1STE4U6G7weNLWLBYy5d4ux2x8gkasJ
-U26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrXgzT/LCrBbBlDSgeF59N8
-9iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8E
-BTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0B
-AQUFAAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOz
-yj1hTdNGCbM+w6DjY1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE
-38NflNUVyRRBnMRddWQVDf9VMOyGj/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymP
-AbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhHhm4qxFYxldBniYUr+WymXUad
-DKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveCX4XSQRjbgbME
-HMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A==
------END CERTIFICATE-----
-
-# GlobalSign Root CA - R2
------BEGIN CERTIFICATE-----
-MIIDujCCAqKgAwIBAgILBAAAAAABD4Ym5g0wDQYJKoZIhvcNAQEFBQAwTDEgMB4G
-A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjIxEzARBgNVBAoTCkdsb2JhbFNp
-Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDYxMjE1MDgwMDAwWhcNMjExMjE1
-MDgwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMjETMBEG
-A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI
-hvcNAQEBBQADggEPADCCAQoCggEBAKbPJA6+Lm8omUVCxKs+IVSbC9N/hHD6ErPL
-v4dfxn+G07IwXNb9rfF73OX4YJYJkhD10FPe+3t+c4isUoh7SqbKSaZeqKeMWhG8
-eoLrvozps6yWJQeXSpkqBy+0Hne/ig+1AnwblrjFuTosvNYSuetZfeLQBoZfXklq
-tTleiDTsvHgMCJiEbKjNS7SgfQx5TfC4LcshytVsW33hoCmEofnTlEnLJGKRILzd
-C9XZzPnqJworc5HGnRusyMvo4KD0L5CLTfuwNhv2GXqF4G3yYROIXJ/gkwpRl4pa
-zq+r1feqCapgvdzZX99yqWATXgAByUr6P6TqBwMhAo6CygPCm48CAwEAAaOBnDCB
-mTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUm+IH
-V2ccHsBqBt5ZtJot39wZhi4wNgYDVR0fBC8wLTAroCmgJ4YlaHR0cDovL2NybC5n
-bG9iYWxzaWduLm5ldC9yb290LXIyLmNybDAfBgNVHSMEGDAWgBSb4gdXZxwewGoG
-3lm0mi3f3BmGLjANBgkqhkiG9w0BAQUFAAOCAQEAmYFThxxol4aR7OBKuEQLq4Gs
-J0/WwbgcQ3izDJr86iw8bmEbTUsp9Z8FHSbBuOmDAGJFtqkIk7mpM0sYmsL4h4hO
-291xNBrBVNpGP+DTKqttVCL1OmLNIG+6KYnX3ZHu01yiPqFbQfXf5WRDLenVOavS
-ot+3i9DAgBkcRcAtjOj4LaR0VknFBbVPFd5uRHg5h6h+u/N5GJG79G+dwfCMNYxd
-AfvDbbnvRG15RjF+Cv6pgsH/76tuIMRQyV+dTZsXjAzlAcmgQWpzU/qlULRuJQ/7
-TBj0/VLZjmmx6BEP3ojY+x1J96relc8geMJgEtslQIxq/H5COEBkEveegeGTLg==
------END CERTIFICATE-----
-
-# GlobalSign Root CA - R3
------BEGIN CERTIFICATE-----
-MIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4G
-A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNp
-Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDkwMzE4MTAwMDAwWhcNMjkwMzE4
-MTAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMzETMBEG
-A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI
-hvcNAQEBBQADggEPADCCAQoCggEBAMwldpB5BngiFvXAg7aEyiie/QV2EcWtiHL8
-RgJDx7KKnQRfJMsuS+FggkbhUqsMgUdwbN1k0ev1LKMPgj0MK66X17YUhhB5uzsT
-gHeMCOFJ0mpiLx9e+pZo34knlTifBtc+ycsmWQ1z3rDI6SYOgxXG71uL0gRgykmm
-KPZpO/bLyCiR5Z2KYVc3rHQU3HTgOu5yLy6c+9C7v/U9AOEGM+iCK65TpjoWc4zd
-QQ4gOsC0p6Hpsk+QLjJg6VfLuQSSaGjlOCZgdbKfd/+RFO+uIEn8rUAVSNECMWEZ
-XriX7613t2Saer9fwRPvm2L7DWzgVGkWqQPabumDk3F2xmmFghcCAwEAAaNCMEAw
-DgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFI/wS3+o
-LkUkrk1Q+mOai97i3Ru8MA0GCSqGSIb3DQEBCwUAA4IBAQBLQNvAUKr+yAzv95ZU
-RUm7lgAJQayzE4aGKAczymvmdLm6AC2upArT9fHxD4q/c2dKg8dEe3jgr25sbwMp
-jjM5RcOO5LlXbKr8EpbsU8Yt5CRsuZRj+9xTaGdWPoO4zzUhw8lo/s7awlOqzJCK
-6fBdRoyV3XpYKBovHd7NADdBj+1EbddTKJd+82cEHhXXipa0095MJ6RMG3NzdvQX
-mcIfeg7jLQitChws/zyrVQ4PkX4268NXSb7hLi18YIvDQVETI53O9zJrlAGomecs
-Mx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7rkpeDMdmztcpH
-WD9f
------END CERTIFICATE-----
-
-# Go Daddy Class 2 CA
------BEGIN CERTIFICATE-----
-MIIEADCCAuigAwIBAgIBADANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEh
-MB8GA1UEChMYVGhlIEdvIERhZGR5IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBE
-YWRkeSBDbGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA0MDYyOTE3
-MDYyMFoXDTM0MDYyOTE3MDYyMFowYzELMAkGA1UEBhMCVVMxITAfBgNVBAoTGFRo
-ZSBHbyBEYWRkeSBHcm91cCwgSW5jLjExMC8GA1UECxMoR28gRGFkZHkgQ2xhc3Mg
-MiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASAwDQYJKoZIhvcNAQEBBQADggEN
-ADCCAQgCggEBAN6d1+pXGEmhW+vXX0iG6r7d/+TvZxz0ZWizV3GgXne77ZtJ6XCA
-PVYYYwhv2vLM0D9/AlQiVBDYsoHUwHU9S3/Hd8M+eKsaA7Ugay9qK7HFiH7Eux6w
-wdhFJ2+qN1j3hybX2C32qRe3H3I2TqYXP2WYktsqbl2i/ojgC95/5Y0V4evLOtXi
-EqITLdiOr18SPaAIBQi2XKVlOARFmR6jYGB0xUGlcmIbYsUfb18aQr4CUWWoriMY
-avx4A6lNf4DD+qta/KFApMoZFv6yyO9ecw3ud72a9nmYvLEHZ6IVDd2gWMZEewo+
-YihfukEHU1jPEX44dMX4/7VpkI+EdOqXG68CAQOjgcAwgb0wHQYDVR0OBBYEFNLE
-sNKR1EwRcbNhyz2h/t2oatTjMIGNBgNVHSMEgYUwgYKAFNLEsNKR1EwRcbNhyz2h
-/t2oatTjoWekZTBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYVGhlIEdvIERhZGR5
-IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRpZmlj
-YXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQAD
-ggEBADJL87LKPpH8EsahB4yOd6AzBhRckB4Y9wimPQoZ+YeAEW5p5JYXMP80kWNy
-OO7MHAGjHZQopDH2esRU1/blMVgDoszOYtuURXO1v0XJJLXVggKtI3lpjbi2Tc7P
-TMozI+gciKqdi0FuFskg5YmezTvacPd+mSYgFFQlq25zheabIZ0KbIIOqPjCDPoQ
-HmyW74cNxA9hi63ugyuV+I6ShHI56yDqg+2DzZduCLzrTia2cyvk0/ZM/iZx4mER
-dEr/VxqHD3VILs9RaRegAhJhldXRQLIQTO7ErBBDpqWeCtWVYpoNz4iCxTIM5Cuf
-ReYNnyicsbkqWletNw+vHX/bvZ8=
------END CERTIFICATE-----
-
-# Go Daddy Root Certificate Authority - G2
------BEGIN CERTIFICATE-----
-MIIDxTCCAq2gAwIBAgIBADANBgkqhkiG9w0BAQsFADCBgzELMAkGA1UEBhMCVVMx
-EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxGjAYBgNVBAoT
-EUdvRGFkZHkuY29tLCBJbmMuMTEwLwYDVQQDEyhHbyBEYWRkeSBSb290IENlcnRp
-ZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIz
-NTk1OVowgYMxCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6b25hMRMwEQYDVQQH
-EwpTY290dHNkYWxlMRowGAYDVQQKExFHb0RhZGR5LmNvbSwgSW5jLjExMC8GA1UE
-AxMoR28gRGFkZHkgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIw
-DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL9xYgjx+lk09xvJGKP3gElY6SKD
-E6bFIEMBO4Tx5oVJnyfq9oQbTqC023CYxzIBsQU+B07u9PpPL1kwIuerGVZr4oAH
-/PMWdYA5UXvl+TW2dE6pjYIT5LY/qQOD+qK+ihVqf94Lw7YZFAXK6sOoBJQ7Rnwy
-DfMAZiLIjWltNowRGLfTshxgtDj6AozO091GB94KPutdfMh8+7ArU6SSYmlRJQVh
-GkSBjCypQ5Yj36w6gZoOKcUcqeldHraenjAKOc7xiID7S13MMuyFYkMlNAJWJwGR
-tDtwKj9useiciAF9n9T521NtYJ2/LOdYq7hfRvzOxBsDPAnrSTFcaUaz4EcCAwEA
-AaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYE
-FDqahQcQZyi27/a9BUFuIMGU2g/eMA0GCSqGSIb3DQEBCwUAA4IBAQCZ21151fmX
-WWcDYfF+OwYxdS2hII5PZYe096acvNjpL9DbWu7PdIxztDhC2gV7+AJ1uP2lsdeu
-9tfeE8tTEH6KRtGX+rcuKxGrkLAngPnon1rpN5+r5N9ss4UXnT3ZJE95kTXWXwTr
-gIOrmgIttRD02JDHBHNA7XIloKmf7J6raBKZV8aPEjoJpL1E/QYVN8Gb5DKj7Tjo
-2GTzLH4U/ALqn83/B2gX2yKQOC16jdFU8WnjXzPKej17CuPKf1855eJ1usV2GDPO
-LPAvTK33sefOT6jEm0pUBsV/fdUID+Ic/n4XuKxe9tQWskMJDE32p2u0mYRlynqI
-4uJEvlz36hz1
------END CERTIFICATE-----
-
-# Hellenic Academic and Research Institutions ECC RootCA 2015
------BEGIN CERTIFICATE-----
-MIICwzCCAkqgAwIBAgIBADAKBggqhkjOPQQDAjCBqjELMAkGA1UEBhMCR1IxDzAN
-BgNVBAcTBkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJl
-c2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkxRDBCBgNVBAMTO0hl
-bGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgRUNDIFJv
-b3RDQSAyMDE1MB4XDTE1MDcwNzEwMzcxMloXDTQwMDYzMDEwMzcxMlowgaoxCzAJ
-BgNVBAYTAkdSMQ8wDQYDVQQHEwZBdGhlbnMxRDBCBgNVBAoTO0hlbGxlbmljIEFj
-YWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgQ2VydC4gQXV0aG9yaXR5
-MUQwQgYDVQQDEztIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0
-dXRpb25zIEVDQyBSb290Q0EgMjAxNTB2MBAGByqGSM49AgEGBSuBBAAiA2IABJKg
-QehLgoRc4vgxEZmGZE4JJS+dQS8KrjVPdJWyUWRrjWvmP3CV8AVER6ZyOFB2lQJa
-jq4onvktTpnvLEhvTCUp6NFxW98dwXU3tNf6e3pCnGoKVlp8aQuqgAkkbH7BRqNC
-MEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFLQi
-C4KZJAEOnLvkDv2/+5cgk5kqMAoGCCqGSM49BAMCA2cAMGQCMGfOFmI4oqxiRaep
-lSTAGiecMjvAwNW6qef4BENThe5SId6d9SWDPp5YSy/XZxMOIQIwBeF1Ad5o7Sof
-TUwJCA3sS61kFyjndc5FZXIhF8siQQ6ME5g4mlRtm8rifOoCWCKR
------END CERTIFICATE-----
-
-# Hellenic Academic and Research Institutions RootCA 2011
------BEGIN CERTIFICATE-----
-MIIEMTCCAxmgAwIBAgIBADANBgkqhkiG9w0BAQUFADCBlTELMAkGA1UEBhMCR1Ix
-RDBCBgNVBAoTO0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1
-dGlvbnMgQ2VydC4gQXV0aG9yaXR5MUAwPgYDVQQDEzdIZWxsZW5pYyBBY2FkZW1p
-YyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25zIFJvb3RDQSAyMDExMB4XDTExMTIw
-NjEzNDk1MloXDTMxMTIwMTEzNDk1MlowgZUxCzAJBgNVBAYTAkdSMUQwQgYDVQQK
-EztIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25zIENl
-cnQuIEF1dGhvcml0eTFAMD4GA1UEAxM3SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJl
-c2VhcmNoIEluc3RpdHV0aW9ucyBSb290Q0EgMjAxMTCCASIwDQYJKoZIhvcNAQEB
-BQADggEPADCCAQoCggEBAKlTAOMupvaO+mDYLZU++CwqVE7NuYRhlFhPjz2L5EPz
-dYmNUeTDN9KKiE15HrcS3UN4SoqS5tdI1Q+kOilENbgH9mgdVc04UfCMJDGFr4PJ
-fel3r+0ae50X+bOdOFAPplp5kYCvN66m0zH7tSYJnTxa71HFK9+WXesyHgLacEns
-bgzImjeN9/E2YEsmLIKe0HjzDQ9jpFEw4fkrJxIH2Oq9GGKYsFk3fb7u8yBRQlqD
-75O6aRXxYp2fmTmCobd0LovUxQt7L/DICto9eQqakxylKHJzkUOap9FNhYS5qXSP
-FEDH3N6sQWRstBmbAmNtJGSPRLIl6s5ddAxjMlyNh+UCAwEAAaOBiTCBhjAPBgNV
-HRMBAf8EBTADAQH/MAsGA1UdDwQEAwIBBjAdBgNVHQ4EFgQUppFC/RNhSiOeCKQp
-5dgTBCPuQSUwRwYDVR0eBEAwPqA8MAWCAy5ncjAFggMuZXUwBoIELmVkdTAGggQu
-b3JnMAWBAy5ncjAFgQMuZXUwBoEELmVkdTAGgQQub3JnMA0GCSqGSIb3DQEBBQUA
-A4IBAQAf73lB4XtuP7KMhjdCSk4cNx6NZrokgclPEg8hwAOXhiVtXdMiKahsog2p
-6z0GW5k6x8zDmjR/qw7IThzh+uTczQ2+vyT+bOdrwg3IBp5OjWEopmr95fZi6hg8
-TqBTnbI6nOulnJEWtk2C4AwFSKls9cz4y51JtPACpf1wA+2KIaWuE4ZJwzNzvoc7
-dIsXRSZMFpGD/md9zU1jZ/rzAxKWeAaNsWftjj++n08C9bMJL/NMh98qy5V8Acys
-Nnq/onN694/BtZqhFLKPM58N7yLcZnuEvUUXBj08yrl3NI/K6s8/MT7jiOOASSXI
-l7WdmplNsDz4SgCbZN2fOUvRJ9e4
------END CERTIFICATE-----
-
-# Hellenic Academic and Research Institutions RootCA 2015
------BEGIN CERTIFICATE-----
-MIIGCzCCA/OgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBpjELMAkGA1UEBhMCR1Ix
-DzANBgNVBAcTBkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5k
-IFJlc2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkxQDA+BgNVBAMT
-N0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgUm9v
-dENBIDIwMTUwHhcNMTUwNzA3MTAxMTIxWhcNNDAwNjMwMTAxMTIxWjCBpjELMAkG
-A1UEBhMCR1IxDzANBgNVBAcTBkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNh
-ZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkx
-QDA+BgNVBAMTN0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1
-dGlvbnMgUm9vdENBIDIwMTUwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
-AQDC+Kk/G4n8PDwEXT2QNrCROnk8ZlrvbTkBSRq0t89/TSNTt5AA4xMqKKYx8ZEA
-4yjsriFBzh/a/X0SWwGDD7mwX5nh8hKDgE0GPt+sr+ehiGsxr/CL0BgzuNtFajT0
-AoAkKAoCFZVedioNmToUW/bLy1O8E00BiDeUJRtCvCLYjqOWXjrZMts+6PAQZe10
-4S+nfK8nNLspfZu2zwnI5dMK/IhlZXQK3HMcXM1AsRzUtoSMTFDPaI6oWa7CJ06C
-ojXdFPQf/7J31Ycvqm59JCfnxssm5uX+Zwdj2EUN3TpZZTlYepKZcj2chF6IIbjV
-9Cz82XBST3i4vTwri5WY9bPRaM8gFH5MXF/ni+X1NYEZN9cRCLdmvtNKzoNXADrD
-gfgXy5I2XdGj2HUb4Ysn6npIQf1FGQatJ5lOwXBH3bWfgVMS5bGMSF0xQxfjjMZ6
-Y5ZLKTBOhE5iGV48zpeQpX8B653g+IuJ3SWYPZK2fu/Z8VFRfS0myGlZYeCsargq
-NhEEelC9MoS+L9xy1dcdFkfkR2YgP/SWxa+OAXqlD3pk9Q0Yh9muiNX6hME6wGko
-LfINaFGq46V3xqSQDqE3izEjR8EJCOtu93ib14L8hCCZSRm2Ekax+0VVFqmjZayc
-Bw/qa9wfLgZy7IaIEuQt218FL+TwA9MmM+eAws1CoRc0CwIDAQABo0IwQDAPBgNV
-HRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUcRVnyMjJvXVd
-ctA4GGqd83EkVAswDQYJKoZIhvcNAQELBQADggIBAHW7bVRLqhBYRjTyYtcWNl0I
-XtVsyIe9tC5G8jH4fOpCtZMWVdyhDBKg2mF+D1hYc2Ryx+hFjtyp8iY/xnmMsVMI
-M4GwVhO+5lFc2JsKT0ucVlMC6U/2DWDqTUJV6HwbISHTGzrMd/K4kPFox/la/vot
-9L/J9UUbzjgQKjeKeaO04wlshYaT/4mWJ3iBj2fjRnRUjtkNaeJK9E10A/+yd+2V
-Z5fkscWrv2oj6NSU4kQoYsRL4vDY4ilrGnB+JGGTe08DMiUNRSQrlrRGar9KC/ea
-j8GsGsVn82800vpzY4zvFrCopEYq+OsS7HK07/grfoxSwIuEVPkvPuNVqNxmsdnh
-X9izjFk0WaSrT2y7HxjbdavYy5LNlDhhDgcGH0tGEPEVvo2FXDtKK4F5D7Rpn0lQ
-l033DlZdwJVqwjbDG2jJ9SrcR5q+ss7FJej6A7na+RZukYT1HCjI/CbM1xyQVqdf
-bzoEvM14iQuODy+jqk+iGxI9FghAD/FGTNeqewjBCvVtJ94Cj8rDtSvK6evIIVM4
-pcw72Hc3MKJP2W/R8kCtQXoXxdZKNYm3QdV8hn9VTYNKpXMgwDqvkPGaJI7ZjnHK
-e7iG2rKPmT4dEw0SEe7Uq/DpFXYC5ODfqiAeW2GFZECpkJcNrVPSWh2HagCXZWK0
-vm9qp/UsQu0yrbYhnr68
------END CERTIFICATE-----
-
-# Hongkong Post Root CA 1
------BEGIN CERTIFICATE-----
-MIIDMDCCAhigAwIBAgICA+gwDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UEBhMCSEsx
-FjAUBgNVBAoTDUhvbmdrb25nIFBvc3QxIDAeBgNVBAMTF0hvbmdrb25nIFBvc3Qg
-Um9vdCBDQSAxMB4XDTAzMDUxNTA1MTMxNFoXDTIzMDUxNTA0NTIyOVowRzELMAkG
-A1UEBhMCSEsxFjAUBgNVBAoTDUhvbmdrb25nIFBvc3QxIDAeBgNVBAMTF0hvbmdr
-b25nIFBvc3QgUm9vdCBDQSAxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
-AQEArP84tulmAknjorThkPlAj3n54r15/gK97iSSHSL22oVyaf7XPwnU3ZG1ApzQ
-jVrhVcNQhrkpJsLj2aDxaQMoIIBFIi1WpztUlVYiWR8o3x8gPW2iNr4joLFutbEn
-PzlTCeqrauh0ssJlXI6/fMN4hM2eFvz1Lk8gKgifd/PFHsSaUmYeSF7jEAaPIpjh
-ZY4bXSNmO7ilMlHIhqqhqZ5/dpTCpmy3QfDVyAY45tQM4vM7TG1QjMSDJ8EThFk9
-nnV0ttgCXjqQesBCNnLsak3c78QA3xMYV18meMjWCnl3v/evt3a5pQuEF10Q6m/h
-q5URX208o1xNg1vysxmKgIsLhwIDAQABoyYwJDASBgNVHRMBAf8ECDAGAQH/AgED
-MA4GA1UdDwEB/wQEAwIBxjANBgkqhkiG9w0BAQUFAAOCAQEADkbVPK7ih9legYsC
-mEEIjEy82tvuJxuC52pF7BaLT4Wg87JwvVqWuspube5Gi27nKi6Wsxkz67SfqLI3
-7piol7Yutmcn1KZJ/RyTZXaeQi/cImyaT/JaFTmxcdcrUehtHJjA2Sr0oYJ71clB
-oiMBdDhViw+5LmeiIAQ32pwL0xch4I+XeTRvhEgCIDMb5jREn5Fw9IBehEPCKdJs
-EhTkYY2sEJCehFC78JZvRZ+K88psT/oROhUVRsPNH4NbLUES7VBnQRM9IauUiqpO
-fMGx+6fWtScvl6tu4B3i0RwsH0Ti/L6RoZz71ilTc4afU9hDDl3WY4JxHYB0yvbi
-AmvZWg==
------END CERTIFICATE-----
-
-# IdenTrust Commercial Root CA 1
------BEGIN CERTIFICATE-----
-MIIFYDCCA0igAwIBAgIQCgFCgAAAAUUjyES1AAAAAjANBgkqhkiG9w0BAQsFADBK
-MQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MScwJQYDVQQDEx5JZGVu
-VHJ1c3QgQ29tbWVyY2lhbCBSb290IENBIDEwHhcNMTQwMTE2MTgxMjIzWhcNMzQw
-MTE2MTgxMjIzWjBKMQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MScw
-JQYDVQQDEx5JZGVuVHJ1c3QgQ29tbWVyY2lhbCBSb290IENBIDEwggIiMA0GCSqG
-SIb3DQEBAQUAA4ICDwAwggIKAoICAQCnUBneP5k91DNG8W9RYYKyqU+PZ4ldhNlT
-3Qwo2dfw/66VQ3KZ+bVdfIrBQuExUHTRgQ18zZshq0PirK1ehm7zCYofWjK9ouuU
-+ehcCuz/mNKvcbO0U59Oh++SvL3sTzIwiEsXXlfEU8L2ApeN2WIrvyQfYo3fw7gp
-S0l4PJNgiCL8mdo2yMKi1CxUAGc1bnO/AljwpN3lsKImesrgNqUZFvX9t++uP0D1
-bVoE/c40yiTcdCMbXTMTEl3EASX2MN0CXZ/g1Ue9tOsbobtJSdifWwLziuQkkORi
-T0/Br4sOdBeo0XKIanoBScy0RnnGF7HamB4HWfp1IYVl3ZBWzvurpWCdxJ35UrCL
-vYf5jysjCiN2O/cz4ckA82n5S6LgTrx+kzmEB/dEcH7+B1rlsazRGMzyNeVJSQjK
-Vsk9+w8YfYs7wRPCTY/JTw436R+hDmrfYi7LNQZReSzIJTj0+kuniVyc0uMNOYZK
-dHzVWYfCP04MXFL0PfdSgvHqo6z9STQaKPNBiDoT7uje/5kdX7rL6B7yuVBgwDHT
-c+XvvqDtMwt0viAgxGds8AgDelWAf0ZOlqf0Hj7h9tgJ4TNkK2PXMl6f+cB7D3hv
-l7yTmvmcEpB4eoCHFddydJxVdHixuuFucAS6T6C6aMN7/zHwcz09lCqxC0EOoP5N
-iGVreTO01wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB
-/zAdBgNVHQ4EFgQU7UQZwNPwBovupHu+QucmVMiONnYwDQYJKoZIhvcNAQELBQAD
-ggIBAA2ukDL2pkt8RHYZYR4nKM1eVO8lvOMIkPkp165oCOGUAFjvLi5+U1KMtlwH
-6oi6mYtQlNeCgN9hCQCTrQ0U5s7B8jeUeLBfnLOic7iPBZM4zY0+sLj7wM+x8uwt
-LRvM7Kqas6pgghstO8OEPVeKlh6cdbjTMM1gCIOQ045U8U1mwF10A0Cj7oV+wh93
-nAbowacYXVKV7cndJZ5t+qntozo00Fl72u1Q8zW/7esUTTHHYPTa8Yec4kjixsU3
-+wYQ+nVZZjFHKdp2mhzpgq7vmrlR94gjmmmVYjzlVYA211QC//G5Xc7UI2/YRYRK
-W2XviQzdFKcgyxilJbQN+QHwotL0AMh0jqEqSI5l2xPE4iUXfeu+h1sXIFRRk0pT
-AwvsXcoz7WL9RccvW9xYoIA55vrX/hMUpu09lEpCdNTDd1lzzY9GvlU47/rokTLq
-l1gEIt44w8y8bckzOmoKaT+gyOpyj4xjhiO9bTyWnpXgSUyqorkqG5w2gXjtw+hG
-4iZZRHUe2XWJUc0QhJ1hYMtd+ZciTY6Y5uN/9lu7rs3KSoFrXgvzUeF0K+l+J6fZ
-mUlO+KWA2yUPHGNiiskzZ2s8EIPGrd6ozRaOjfAHN3Gf8qv8QfXBi+wAN10J5U6A
-7/qxXDgGpRtK4dw4LTzcqx+QGtVKnO7RcGzM7vRX+Bi6hG6H
------END CERTIFICATE-----
-
-# IdenTrust Public Sector Root CA 1
------BEGIN CERTIFICATE-----
-MIIFZjCCA06gAwIBAgIQCgFCgAAAAUUjz0Z8AAAAAjANBgkqhkiG9w0BAQsFADBN
-MQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MSowKAYDVQQDEyFJZGVu
-VHJ1c3QgUHVibGljIFNlY3RvciBSb290IENBIDEwHhcNMTQwMTE2MTc1MzMyWhcN
-MzQwMTE2MTc1MzMyWjBNMQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0
-MSowKAYDVQQDEyFJZGVuVHJ1c3QgUHVibGljIFNlY3RvciBSb290IENBIDEwggIi
-MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC2IpT8pEiv6EdrCvsnduTyP4o7
-ekosMSqMjbCpwzFrqHd2hCa2rIFCDQjrVVi7evi8ZX3yoG2LqEfpYnYeEe4IFNGy
-RBb06tD6Hi9e28tzQa68ALBKK0CyrOE7S8ItneShm+waOh7wCLPQ5CQ1B5+ctMlS
-bdsHyo+1W/CD80/HLaXIrcuVIKQxKFdYWuSNG5qrng0M8gozOSI5Cpcu81N3uURF
-/YTLNiCBWS2ab21ISGHKTN9T0a9SvESfqy9rg3LvdYDaBjMbXcjaY8ZNzaxmMc3R
-3j6HEDbhuaR672BQssvKplbgN6+rNBM5Jeg5ZuSYeqoSmJxZZoY+rfGwyj4GD3vw
-EUs3oERte8uojHH01bWRNszwFcYr3lEXsZdMUD2xlVl8BX0tIdUAvwFnol57plzy
-9yLxkA2T26pEUWbMfXYD62qoKjgZl3YNa4ph+bz27nb9cCvdKTz4Ch5bQhyLVi9V
-GxyhLrXHFub4qjySjmm2AcG1hp2JDws4lFTo6tyePSW8Uybt1as5qsVATFSrsrTZ
-2fjXctscvG29ZV/viDUqZi/u9rNl8DONfJhBaUYPQxxp+pu10GFqzcpL2UyQRqsV
-WaFHVCkugyhfHMKiq3IXAAaOReyL4jM9f9oZRORicsPfIsbyVtTdX5Vy7W1f90gD
-W/3FKqD2cyOEEBsB5wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/
-BAUwAwEB/zAdBgNVHQ4EFgQU43HgntinQtnbcZFrlJPrw6PRFKMwDQYJKoZIhvcN
-AQELBQADggIBAEf63QqwEZE4rU1d9+UOl1QZgkiHVIyqZJnYWv6IAcVYpZmxI1Qj
-t2odIFflAWJBF9MJ23XLblSQdf4an4EKwt3X9wnQW3IV5B4Jaj0z8yGa5hV+rVHV
-DRDtfULAj+7AmgjVQdZcDiFpboBhDhXAuM/FSRJSzL46zNQuOAXeNf0fb7iAaJg9
-TaDKQGXSc3z1i9kKlT/YPyNtGtEqJBnZhbMX73huqVjRI9PHE+1yJX9dsXNw0H8G
-lwmEKYBhHfpe/3OsoOOJuBxxFcbeMX8S3OFtm6/n6J91eEyrRjuazr8FGF1NFTwW
-mhlQBJqymm9li1JfPFgEKCXAZmExfrngdbkaqIHWchezxQMxNRF4eKLg6TCMf4Df
-WN88uieW4oA0beOY02QnrEh+KHdcxiVhJfiFDGX6xDIvpZgF5PgLZxYWxoK4Mhn5
-+bl53B/N66+rDt0b20XkeucC4pVd/GnwU2lhlXV5C15V5jgclKlZM57IcXR5f1GJ
-tshquDDIajjDbp7hNxbqBWJMWxJH7ae0s1hWx0nzfxJoCTFx8G34Tkf71oXuxVhA
-GaQdp/lLQzfcaFpPz+vCZHTetBXZ9FRUGi8c15dxVJCO2SCdUyt/q4/i6jC8UDfv
-8Ue1fXwsBOxonbRJRBD0ckscZOf85muQ3Wl9af0AVqW3rLatt8o+Ae+c
------END CERTIFICATE-----
-
-# ISRG Root X1
------BEGIN CERTIFICATE-----
-MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw
-TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
-cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4
-WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu
-ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY
-MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc
-h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+
-0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U
-A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW
-T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH
-B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC
-B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv
-KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn
-OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn
-jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw
-qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI
-rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV
-HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq
-hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL
-ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ
-3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK
-NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5
-ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur
-TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC
-jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc
-oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq
-4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA
-mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d
-emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=
------END CERTIFICATE-----
-
-# Izenpe.com
------BEGIN CERTIFICATE-----
-MIIF8TCCA9mgAwIBAgIQALC3WhZIX7/hy/WL1xnmfTANBgkqhkiG9w0BAQsFADA4
-MQswCQYDVQQGEwJFUzEUMBIGA1UECgwLSVpFTlBFIFMuQS4xEzARBgNVBAMMCkl6
-ZW5wZS5jb20wHhcNMDcxMjEzMTMwODI4WhcNMzcxMjEzMDgyNzI1WjA4MQswCQYD
-VQQGEwJFUzEUMBIGA1UECgwLSVpFTlBFIFMuQS4xEzARBgNVBAMMCkl6ZW5wZS5j
-b20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDJ03rKDx6sp4boFmVq
-scIbRTJxldn+EFvMr+eleQGPicPK8lVx93e+d5TzcqQsRNiekpsUOqHnJJAKClaO
-xdgmlOHZSOEtPtoKct2jmRXagaKH9HtuJneJWK3W6wyyQXpzbm3benhB6QiIEn6H
-LmYRY2xU+zydcsC8Lv/Ct90NduM61/e0aL6i9eOBbsFGb12N4E3GVFWJGjMxCrFX
-uaOKmMPsOzTFlUFpfnXCPCDFYbpRR6AgkJOhkEvzTnyFRVSa0QUmQbC1TR0zvsQD
-yCV8wXDbO/QJLVQnSKwv4cSsPsjLkkxTOTcj7NMB+eAJRE1NZMDhDVqHIrytG6P+
-JrUV86f8hBnp7KGItERphIPzidF0BqnMC9bC3ieFUCbKF7jJeodWLBoBHmy+E60Q
-rLUk9TiRodZL2vG70t5HtfG8gfZZa88ZU+mNFctKy6lvROUbQc/hhqfK0GqfvEyN
-BjNaooXlkDWgYlwWTvDjovoDGrQscbNYLN57C9saD+veIR8GdwYDsMnvmfzAuU8L
-hij+0rnq49qlw0dpEuDb8PYZi+17cNcC1u2HGCgsBCRMd+RIihrGO5rUD8r6ddIB
-QFqNeb+Lz0vPqhbBleStTIo+F5HUsWLlguWABKQDfo2/2n+iD5dPDNMN+9fR5XJ+
-HMh3/1uaD7euBUbl8agW7EekFwIDAQABo4H2MIHzMIGwBgNVHREEgagwgaWBD2lu
-Zm9AaXplbnBlLmNvbaSBkTCBjjFHMEUGA1UECgw+SVpFTlBFIFMuQS4gLSBDSUYg
-QTAxMzM3MjYwLVJNZXJjLlZpdG9yaWEtR2FzdGVpeiBUMTA1NSBGNjIgUzgxQzBB
-BgNVBAkMOkF2ZGEgZGVsIE1lZGl0ZXJyYW5lbyBFdG9yYmlkZWEgMTQgLSAwMTAx
-MCBWaXRvcmlhLUdhc3RlaXowDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC
-AQYwHQYDVR0OBBYEFB0cZQ6o8iV7tJHP5LGx5r1VdGwFMA0GCSqGSIb3DQEBCwUA
-A4ICAQB4pgwWSp9MiDrAyw6lFn2fuUhfGI8NYjb2zRlrrKvV9pF9rnHzP7MOeIWb
-laQnIUdCSnxIOvVFfLMMjlF4rJUT3sb9fbgakEyrkgPH7UIBzg/YsfqikuFgba56
-awmqxinuaElnMIAkejEWOVt+8Rwu3WwJrfIxwYJOubv5vr8qhT/AQKM6WfxZSzwo
-JNu0FXWuDYi6LnPAvViH5ULy617uHjAimcs30cQhbIHsvm0m5hzkQiCeR7Csg1lw
-LDXWrzY0tM07+DKo7+N4ifuNRSzanLh+QBxh5z6ikixL8s36mLYp//Pye6kfLqCT
-VyvehQP5aTfLnnhqBbTFMXiJ7HqnheG5ezzevh55hM6fcA5ZwjUukCox2eRFekGk
-LhObNA5me0mrZJfQRsN5nXJQY6aYWwa9SG3YOYNw6DXwBdGqvOPbyALqfP2C2sJb
-UjWumDqtujWTI6cfSN01RpiyEGjkpTHCClguGYEQyVB1/OpaFs4R1+7vUIgtYf8/
-QnMFlEPVjjxOAToZpR9GTnfQXeWBIiGH/pR9hNiTrdZoQ0iy2+tzJOeRf1SktoA+
-naM8THLCV8Sg1Mw4J87VBp6iSNnpn86CcDaTmjvfliHjWbcM2pE38P1ZWrOZyGls
-QyYBNWNgVYkDOnXYukrZVP/u3oDYLdE41V4tC5h9Pmzb/CaIxw==
------END CERTIFICATE-----
-
-# LuxTrust Global Root 2
------BEGIN CERTIFICATE-----
-MIIFwzCCA6ugAwIBAgIUCn6m30tEntpqJIWe5rgV0xZ/u7EwDQYJKoZIhvcNAQEL
-BQAwRjELMAkGA1UEBhMCTFUxFjAUBgNVBAoMDUx1eFRydXN0IFMuQS4xHzAdBgNV
-BAMMFkx1eFRydXN0IEdsb2JhbCBSb290IDIwHhcNMTUwMzA1MTMyMTU3WhcNMzUw
-MzA1MTMyMTU3WjBGMQswCQYDVQQGEwJMVTEWMBQGA1UECgwNTHV4VHJ1c3QgUy5B
-LjEfMB0GA1UEAwwWTHV4VHJ1c3QgR2xvYmFsIFJvb3QgMjCCAiIwDQYJKoZIhvcN
-AQEBBQADggIPADCCAgoCggIBANeFl78RmOnwYoNMPIf5U2o3C/IPPIfOb9wmKb3F
-ibrJgz337spbxm1Jc7TJRqMbNBM/wYlFV/TZsfs2ZUv7COJIcRHIbjuend+JZTem
-hfY7RBi2xjcwYkSSl2l9QjAk5A0MiWtj3sXh306pFGxT4GHO9hcvHTy95iJMHZP1
-EMShduxq3sVs35a0VkBCwGKSMKEtFZSg0iAGCW5qbeXrt77U8PEVfIvmTroTzEsn
-Xpk8F12PgX8zPU/TPxvsXD/wPEx1bvKm1Z3aLQdjAsZy6ZS8TEmVT4hSyNvoaYL4
-zDRbIvCGp4m9SAptZoFtyMhk+wHh9OHe2Z7d21vUKpkmFRseTJIpgp7VkoGSQXAZ
-96Tlk0u8d2cx3Rz9MXANF5kM+Qw5GSoXtTBxVdUPrljhPS80m8+f9niFwpN6cj5m
-j5wWEWCPnolvZ77gR1o7DJpni89Gxq44o/KnvObWhWszJHAiS8sIm7vI+AIpHb4g
-DEa/a4ebsypmQjVGbKq6rfmYe+lQVRQxv7HaLe2ArWgk+2mr2HETMOZns4dA/Yl+
-8kPREd8vZS9kzl8UubG/Mb2HeFpZZYiq/FkySIbWTLkpS5XTdvN3JW1CHDiDTf2j
-X5t/Lax5Gw5CMZdjpPuKadUiDTSQMC6otOBttpSsvItO13D8xTiOZCXhTTmQzsmH
-hFhxAgMBAAGjgagwgaUwDwYDVR0TAQH/BAUwAwEB/zBCBgNVHSAEOzA5MDcGByuB
-KwEBAQowLDAqBggrBgEFBQcCARYeaHR0cHM6Ly9yZXBvc2l0b3J5Lmx1eHRydXN0
-Lmx1MA4GA1UdDwEB/wQEAwIBBjAfBgNVHSMEGDAWgBT/GCh2+UgFLKGu8SsbK7JT
-+Et8szAdBgNVHQ4EFgQU/xgodvlIBSyhrvErGyuyU/hLfLMwDQYJKoZIhvcNAQEL
-BQADggIBAGoZFO1uecEsh9QNcH7X9njJCwROxLHOk3D+sFTAMs2ZMGQXvw/l4jP9
-BzZAcg4atmpZ1gDlaCDdLnINH2pkMSCEfUmmWjfrRcmF9dTHF5kH5ptV5AzoqbTO
-jFu1EVzPig4N1qx3gf4ynCSecs5U89BvolbW7MM3LGVYvlcAGvI1+ut7MV3CwRI9
-loGIlonBWVx65n9wNOeD4rHh4bhY79SV5GCc8JaXcozrhAIuZY+kt9J/Z93I055c
-qqmkoCUUBpvsT34tC38ddfEz2O3OuHVtPlu5mB0xDVbYQw8wkbIEa91WvpWAVWe+
-2M2D2RjuLg+GLZKecBPs3lHJQ3gCpU3I+V/EkVhGFndadKpAvAefMLmx9xIX3eP/
-JEAdemrRTxgKqpAd60Ae36EeRJIQmvKN4dFLRp7oRUKX6kWZ8+xm1QL68qZKJKre
-zrnK+T+Tb/mjuuqlPpmt/f97mfVl7vBZKGfXkJWkE4SphMHozs51k2MavDzq1WQf
-LSoSOcbDWjLtR5EWDrw4wVDej8oqkDQc7kGUnF4ZLvhFSZl0kbAEb+MEWrGrKqv+
-x9CWttrhSmQGbmBNvUJO/3jaJMobtNeWOWyu8Q6qp31IiyBMz2TWuJdGsE7RKlY6
-oJO9r4Ak4Ap+58rVyuiFVdw2KuGUaJPHZnJED4AhMmwlxyOAgwrr
------END CERTIFICATE-----
-
-# Microsec e-Szigno Root CA 2009
------BEGIN CERTIFICATE-----
-MIIECjCCAvKgAwIBAgIJAMJ+QwRORz8ZMA0GCSqGSIb3DQEBCwUAMIGCMQswCQYD
-VQQGEwJIVTERMA8GA1UEBwwIQnVkYXBlc3QxFjAUBgNVBAoMDU1pY3Jvc2VjIEx0
-ZC4xJzAlBgNVBAMMHk1pY3Jvc2VjIGUtU3ppZ25vIFJvb3QgQ0EgMjAwOTEfMB0G
-CSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5odTAeFw0wOTA2MTYxMTMwMThaFw0y
-OTEyMzAxMTMwMThaMIGCMQswCQYDVQQGEwJIVTERMA8GA1UEBwwIQnVkYXBlc3Qx
-FjAUBgNVBAoMDU1pY3Jvc2VjIEx0ZC4xJzAlBgNVBAMMHk1pY3Jvc2VjIGUtU3pp
-Z25vIFJvb3QgQ0EgMjAwOTEfMB0GCSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5o
-dTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOn4j/NjrdqG2KfgQvvP
-kd6mJviZpWNwrZuuyjNAfW2WbqEORO7hE52UQlKavXWFdCyoDh2Tthi3jCyoz/tc
-cbna7P7ofo/kLx2yqHWH2Leh5TvPmUpG0IMZfcChEhyVbUr02MelTTMuhTlAdX4U
-fIASmFDHQWe4oIBhVKZsTh/gnQ4H6cm6M+f+wFUoLAKApxn1ntxVUwOXewdI/5n7
-N4okxFnMUBBjjqqpGrCEGob5X7uxUG6k0QrM1XF+H6cbfPVTbiJfyyvm1HxdrtbC
-xkzlBQHZ7Vf8wSN5/PrIJIOV87VqUQHQd9bpEqH5GoP7ghu5sJf0dgYzQ0mg/wu1
-+rUCAwEAAaOBgDB+MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0G
-A1UdDgQWBBTLD8bfQkPMPcu1SCOhGnqmKrs0aDAfBgNVHSMEGDAWgBTLD8bfQkPM
-Pcu1SCOhGnqmKrs0aDAbBgNVHREEFDASgRBpbmZvQGUtc3ppZ25vLmh1MA0GCSqG
-SIb3DQEBCwUAA4IBAQDJ0Q5eLtXMs3w+y/w9/w0olZMEyL/azXm4Q5DwpL7v8u8h
-mLzU1F0G9u5C7DBsoKqpyvGvivo/C3NqPuouQH4frlRheesuCDfXI/OMn74dseGk
-ddug4lQUsbocKaQY9hK6ohQU4zE1yED/t+AFdlfBHFny+L/k7SViXITwfn4fs775
-tyERzAMBVnCnEJIeGzSBHq2cGsMEPO0CYdYeBvNfOofyK/FFh+U9rNHHV4S9a67c
-2Pm2G2JwCz02yULyMtd6YebS2z3PyKnJm9zbWETXbzivf3jTo60adbocwTZ8jx5t
-HMN1Rq41Bab2XD0h7lbwyYIiLXpUq3DDfSJlgnCW
------END CERTIFICATE-----
-
-# NetLock Arany (Class Gold) Főtanúsítvány
------BEGIN CERTIFICATE-----
-MIIEFTCCAv2gAwIBAgIGSUEs5AAQMA0GCSqGSIb3DQEBCwUAMIGnMQswCQYDVQQG
-EwJIVTERMA8GA1UEBwwIQnVkYXBlc3QxFTATBgNVBAoMDE5ldExvY2sgS2Z0LjE3
-MDUGA1UECwwuVGFuw7pzw610dsOhbnlraWFkw7NrIChDZXJ0aWZpY2F0aW9uIFNl
-cnZpY2VzKTE1MDMGA1UEAwwsTmV0TG9jayBBcmFueSAoQ2xhc3MgR29sZCkgRsWR
-dGFuw7pzw610dsOhbnkwHhcNMDgxMjExMTUwODIxWhcNMjgxMjA2MTUwODIxWjCB
-pzELMAkGA1UEBhMCSFUxETAPBgNVBAcMCEJ1ZGFwZXN0MRUwEwYDVQQKDAxOZXRM
-b2NrIEtmdC4xNzA1BgNVBAsMLlRhbsO6c8OtdHbDoW55a2lhZMOzayAoQ2VydGlm
-aWNhdGlvbiBTZXJ2aWNlcykxNTAzBgNVBAMMLE5ldExvY2sgQXJhbnkgKENsYXNz
-IEdvbGQpIEbFkXRhbsO6c8OtdHbDoW55MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
-MIIBCgKCAQEAxCRec75LbRTDofTjl5Bu0jBFHjzuZ9lk4BqKf8owyoPjIMHj9DrT
-lF8afFttvzBPhCf2nx9JvMaZCpDyD/V/Q4Q3Y1GLeqVw/HpYzY6b7cNGbIRwXdrz
-AZAj/E4wqX7hJ2Pn7WQ8oLjJM2P+FpD/sLj916jAwJRDC7bVWaaeVtAkH3B5r9s5
-VA1lddkVQZQBr17s9o3x/61k/iCa11zr/qYfCGSji3ZVrR47KGAuhyXoqq8fxmRG
-ILdwfzzeSNuWU7c5d+Qa4scWhHaXWy+7GRWF+GmF9ZmnqfI0p6m2pgP8b4Y9VHx2
-BJtr+UBdADTHLpl1neWIA6pN+APSQnbAGwIDAKiLo0UwQzASBgNVHRMBAf8ECDAG
-AQH/AgEEMA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUzPpnk/C2uNClwB7zU/2M
-U9+D15YwDQYJKoZIhvcNAQELBQADggEBAKt/7hwWqZw8UQCgwBEIBaeZ5m8BiFRh
-bvG5GK1Krf6BQCOUL/t1fC8oS2IkgYIL9WHxHG64YTjrgfpioTtaYtOUZcTh5m2C
-+C8lcLIhJsFyUR+MLMOEkMNaj7rP9KdlpeuY0fsFskZ1FSNqb4VjMIDw1Z4fKRzC
-bLBQWV2QWzuoDTDPv31/zvGdg73JRm4gpvlhUbohL3u+pRVjodSVh/GeufOJ8z2F
-uLjbvrW5KfnaNwUASZQDhETnv0Mxz3WLJdH0pmT1kvarBes96aULNmLazAZfNou2
-XjG4Kvte9nHfRCaexOYNkbQudZWAUWpLMKawYqGT8ZvYzsRjdT9ZR7E=
------END CERTIFICATE-----
-
-# Network Solutions Certificate Authority
------BEGIN CERTIFICATE-----
-MIID5jCCAs6gAwIBAgIQV8szb8JcFuZHFhfjkDFo4DANBgkqhkiG9w0BAQUFADBi
-MQswCQYDVQQGEwJVUzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMu
-MTAwLgYDVQQDEydOZXR3b3JrIFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3Jp
-dHkwHhcNMDYxMjAxMDAwMDAwWhcNMjkxMjMxMjM1OTU5WjBiMQswCQYDVQQGEwJV
-UzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMuMTAwLgYDVQQDEydO
-ZXR3b3JrIFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggEiMA0GCSqG
-SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDkvH6SMG3G2I4rC7xGzuAnlt7e+foS0zwz
-c7MEL7xxjOWftiJgPl9dzgn/ggwbmlFQGiaJ3dVhXRncEg8tCqJDXRfQNJIg6nPP
-OCwGJgl6cvf6UDL4wpPTaaIjzkGxzOTVHzbRijr4jGPiFFlp7Q3Tf2vouAPlT2rl
-mGNpSAW+Lv8ztumXWWn4Zxmuk2GWRBXTcrA/vGp97Eh/jcOrqnErU2lBUzS1sLnF
-BgrEsEX1QV1uiUV7PTsmjHTC5dLRfbIR1PtYMiKagMnc/Qzpf14Dl847ABSHJ3A4
-qY5usyd2mFHgBeMhqxrVhSI8KbWaFsWAqPS7azCPL0YCorEMIuDTAgMBAAGjgZcw
-gZQwHQYDVR0OBBYEFCEwyfsA106Y2oeqKtCnLrFAMadMMA4GA1UdDwEB/wQEAwIB
-BjAPBgNVHRMBAf8EBTADAQH/MFIGA1UdHwRLMEkwR6BFoEOGQWh0dHA6Ly9jcmwu
-bmV0c29sc3NsLmNvbS9OZXR3b3JrU29sdXRpb25zQ2VydGlmaWNhdGVBdXRob3Jp
-dHkuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQC7rkvnt1frf6ott3NHhWrB5KUd5Oc8
-6fRZZXe1eltajSU24HqXLjjAV2CDmAaDn7l2em5Q4LqILPxFzBiwmZVRDuwduIj/
-h1AcgsLj4DKAv6ALR8jDMe+ZZzKATxcheQxpXN5eNK4CtSbqUN9/GGUsyfJj4akH
-/nxxH2szJGoeBfcFaMBqEssuXmHLrijTfsK0ZpEmXzwuJF/LWA/rKOyvEZbz3Htv
-wKeI8lN3s2Berq4o2jUsbzRF0ybh3uxbTydrFny9RAQYgrOJeRcQcT16ohZO9QHN
-pGxlaKFJdlxDydi8NmdspZS11My5vWo1ViHe2MPr+8ukYEywVaCge1ey
------END CERTIFICATE-----
-
-# OISTE WISeKey Global Root GA CA
------BEGIN CERTIFICATE-----
-MIID8TCCAtmgAwIBAgIQQT1yx/RrH4FDffHSKFTfmjANBgkqhkiG9w0BAQUFADCB
-ijELMAkGA1UEBhMCQ0gxEDAOBgNVBAoTB1dJU2VLZXkxGzAZBgNVBAsTEkNvcHly
-aWdodCAoYykgMjAwNTEiMCAGA1UECxMZT0lTVEUgRm91bmRhdGlvbiBFbmRvcnNl
-ZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBHbG9iYWwgUm9vdCBHQSBDQTAeFw0w
-NTEyMTExNjAzNDRaFw0zNzEyMTExNjA5NTFaMIGKMQswCQYDVQQGEwJDSDEQMA4G
-A1UEChMHV0lTZUtleTEbMBkGA1UECxMSQ29weXJpZ2h0IChjKSAyMDA1MSIwIAYD
-VQQLExlPSVNURSBGb3VuZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBX
-SVNlS2V5IEdsb2JhbCBSb290IEdBIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
-MIIBCgKCAQEAy0+zAJs9Nt350UlqaxBJH+zYK7LG+DKBKUOVTJoZIyEVRd7jyBxR
-VVuuk+g3/ytr6dTqvirdqFEr12bDYVxgAsj1znJ7O7jyTmUIms2kahnBAbtzptf2
-w93NvKSLtZlhuAGio9RN1AU9ka34tAhxZK9w8RxrfvbDd50kc3vkDIzh2TbhmYsF
-mQvtRTEJysIA2/dyoJaqlYfQjse2YXMNdmaM3Bu0Y6Kff5MTMPGhJ9vZ/yxViJGg
-4E8HsChWjBgbl0SOid3gF27nKu+POQoxhILYQBRJLnpB5Kf+42TMwVlxSywhp1t9
-4B3RLoGbw9ho972WG6xwsRYUC9tguSYBBQIDAQABo1EwTzALBgNVHQ8EBAMCAYYw
-DwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUswN+rja8sHnR3JQmthG+IbJphpQw
-EAYJKwYBBAGCNxUBBAMCAQAwDQYJKoZIhvcNAQEFBQADggEBAEuh/wuHbrP5wUOx
-SPMowB0uyQlB+pQAHKSkq0lPjz0e701vvbyk9vImMMkQyh2I+3QZH4VFvbBsUfk2
-ftv1TDI6QU9bR8/oCy22xBmddMVHxjtqD6wU2zz0c5ypBd8A3HR4+vg1YFkCExh8
-vPtNsCBtQ7tgMHpnM1zFmdH4LTlSc/uMqpclXHLZCB6rTjzjgTGfA6b7wP4piFXa
-hNVQA7bihKOmNqoROgHhGEvWRGizPflTdISzRpFGlgC3gCy24eMQ4tui5yiPAZZi
-Fj4A4xylNoEYokxSdsARo27mHbrjWr42U8U+dY+GaSlYU7Wcu2+fXMUY7N0v4ZjJ
-/L7fCg0=
------END CERTIFICATE-----
-
-# OISTE WISeKey Global Root GB CA
------BEGIN CERTIFICATE-----
-MIIDtTCCAp2gAwIBAgIQdrEgUnTwhYdGs/gjGvbCwDANBgkqhkiG9w0BAQsFADBt
-MQswCQYDVQQGEwJDSDEQMA4GA1UEChMHV0lTZUtleTEiMCAGA1UECxMZT0lTVEUg
-Rm91bmRhdGlvbiBFbmRvcnNlZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBHbG9i
-YWwgUm9vdCBHQiBDQTAeFw0xNDEyMDExNTAwMzJaFw0zOTEyMDExNTEwMzFaMG0x
-CzAJBgNVBAYTAkNIMRAwDgYDVQQKEwdXSVNlS2V5MSIwIAYDVQQLExlPSVNURSBG
-b3VuZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBXSVNlS2V5IEdsb2Jh
-bCBSb290IEdCIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2Be3
-HEokKtaXscriHvt9OO+Y9bI5mE4nuBFde9IllIiCFSZqGzG7qFshISvYD06fWvGx
-WuR51jIjK+FTzJlFXHtPrby/h0oLS5daqPZI7H17Dc0hBt+eFf1Biki3IPShehtX
-1F1Q/7pn2COZH8g/497/b1t3sWtuuMlk9+HKQUYOKXHQuSP8yYFfTvdv37+ErXNk
-u7dCjmn21HYdfp2nuFeKUWdy19SouJVUQHMD9ur06/4oQnc/nSMbsrY9gBQHTC5P
-99UKFg29ZkM3fiNDecNAhvVMKdqOmq0NpQSHiB6F4+lT1ZvIiwNjeOvgGUpuuy9r
-M2RYk61pv48b74JIxwIDAQABo1EwTzALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUw
-AwEB/zAdBgNVHQ4EFgQUNQ/INmNe4qPs+TtmFc5RUuORmj0wEAYJKwYBBAGCNxUB
-BAMCAQAwDQYJKoZIhvcNAQELBQADggEBAEBM+4eymYGQfp3FsLAmzYh7KzKNbrgh
-cViXfa43FK8+5/ea4n32cZiZBKpDdHij40lhPnOMTZTg+XHEthYOU3gf1qKHLwI5
-gSk8rxWYITD+KJAAjNHhy/peyP34EEY7onhCkRd0VQreUGdNZtGn//3ZwLWoo4rO
-ZvUPQ82nK1d7Y0Zqqi5S2PTt4W2tKZB4SLrhI6qjiey1q5bAtEuiHZeeevJuQHHf
-aPFlTc58Bd9TZaml8LGXBHAVRgOY1NK/VLSgWH1Sb9pWJmLU2NuJMW8c8CLC02Ic
-Nc1MaRVUGpCY3useX8p3x8uOPUNpnJpY0CQ73xtAln41rYHHTnG6iBM=
------END CERTIFICATE-----
-
-# OpenTrust Root CA G1
------BEGIN CERTIFICATE-----
-MIIFbzCCA1egAwIBAgISESCzkFU5fX82bWTCp59rY45nMA0GCSqGSIb3DQEBCwUA
-MEAxCzAJBgNVBAYTAkZSMRIwEAYDVQQKDAlPcGVuVHJ1c3QxHTAbBgNVBAMMFE9w
-ZW5UcnVzdCBSb290IENBIEcxMB4XDTE0MDUyNjA4NDU1MFoXDTM4MDExNTAwMDAw
-MFowQDELMAkGA1UEBhMCRlIxEjAQBgNVBAoMCU9wZW5UcnVzdDEdMBsGA1UEAwwU
-T3BlblRydXN0IFJvb3QgQ0EgRzEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK
-AoICAQD4eUbalsUwXopxAy1wpLuwxQjczeY1wICkES3d5oeuXT2R0odsN7faYp6b
-wiTXj/HbpqbfRm9RpnHLPhsxZ2L3EVs0J9V5ToybWL0iEA1cJwzdMOWo010hOHQX
-/uMftk87ay3bfWAfjH1MBcLrARYVmBSO0ZB3Ij/swjm4eTrwSSTilZHcYTSSjFR0
-77F9jAHiOH3BX2pfJLKOYheteSCtqx234LSWSE9mQxAGFiQD4eCcjsZGT44ameGP
-uY4zbGneWK2gDqdkVBFpRGZPTBKnjix9xNRbxQA0MMHZmf4yzgeEtE7NCv82TWLx
-p2NX5Ntqp66/K7nJ5rInieV+mhxNaMbBGN4zK1FGSxyO9z0M+Yo0FMT7MzUj8czx
-Kselu7Cizv5Ta01BG2Yospb6p64KTrk5M0ScdMGTHPjgniQlQ/GbI4Kq3ywgsNw2
-TgOzfALU5nsaqocTvz6hdLubDuHAk5/XpGbKuxs74zD0M1mKB3IDVedzagMxbm+W
-G+Oin6+Sx+31QrclTDsTBM8clq8cIqPQqwWyTBIjUtz9GVsnnB47ev1CI9sjgBPw
-vFEVVJSmdz7QdFG9URQIOTfLHzSpMJ1ShC5VkLG631UAC9hWLbFJSXKAqWLXwPYY
-EQRVzXR7z2FwefR7LFxckvzluFqrTJOVoSfupb7PcSNCupt2LQIDAQABo2MwYTAO
-BgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUl0YhVyE1
-2jZVx/PxN3DlCPaTKbYwHwYDVR0jBBgwFoAUl0YhVyE12jZVx/PxN3DlCPaTKbYw
-DQYJKoZIhvcNAQELBQADggIBAB3dAmB84DWn5ph76kTOZ0BP8pNuZtQ5iSas000E
-PLuHIT839HEl2ku6q5aCgZG27dmxpGWX4m9kWaSW7mDKHyP7Rbr/jyTwyqkxf3kf
-gLMtMrpkZ2CvuVnN35pJ06iCsfmYlIrM4LvgBBuZYLFGZdwIorJGnkSI6pN+VxbS
-FXJfLkur1J1juONI5f6ELlgKn0Md/rcYkoZDSw6cMoYsYPXpSOqV7XAp8dUv/TW0
-V8/bhUiZucJvbI/NeJWsZCj9VrDDb8O+WVLhX4SPgPL0DTatdrOjteFkdjpY3H1P
-XlZs5VVZV6Xf8YpmMIzUUmI4d7S+KNfKNsSbBfD4Fdvb8e80nR14SohWZ25g/4/I
-i+GOvUKpMwpZQhISKvqxnUOOBZuZ2mKtVzazHbYNeS2WuOvyDEsMpZTGMKcmGS3t
-TAZQMPH9WD25SxdfGbRqhFS0OE85og2WaMMolP3tLR9Ka0OWLpABEPs4poEL0L91
-09S5zvE/bw4cHjdx5RiHdRk/ULlepEU0rbDK5uUTdg8xFKmOLZTW1YVNcxVPS/Ky
-Pu1svf0OnWZzsD2097+o4BGkxK51CUpjAEggpsadCwmKtODmzj7HPiY46SvepghJ
-AwSQiumPv+i2tCqjI40cHLI5kqiPAlxAOXXUc0ECd97N4EOH1uS6SsNsEn/+KuYj
-1oxx
------END CERTIFICATE-----
-
-# OpenTrust Root CA G2
------BEGIN CERTIFICATE-----
-MIIFbzCCA1egAwIBAgISESChaRu/vbm9UpaPI+hIvyYRMA0GCSqGSIb3DQEBDQUA
-MEAxCzAJBgNVBAYTAkZSMRIwEAYDVQQKDAlPcGVuVHJ1c3QxHTAbBgNVBAMMFE9w
-ZW5UcnVzdCBSb290IENBIEcyMB4XDTE0MDUyNjAwMDAwMFoXDTM4MDExNTAwMDAw
-MFowQDELMAkGA1UEBhMCRlIxEjAQBgNVBAoMCU9wZW5UcnVzdDEdMBsGA1UEAwwU
-T3BlblRydXN0IFJvb3QgQ0EgRzIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK
-AoICAQDMtlelM5QQgTJT32F+D3Y5z1zCU3UdSXqWON2ic2rxb95eolq5cSG+Ntmh
-/LzubKh8NBpxGuga2F8ORAbtp+Dz0mEL4DKiltE48MLaARf85KxP6O6JHnSrT78e
-CbY2albz4e6WiWYkBuTNQjpK3eCasMSCRbP+yatcfD7J6xcvDH1urqWPyKwlCm/6
-1UWY0jUJ9gNDlP7ZvyCVeYCYitmJNbtRG6Q3ffyZO6v/v6wNj0OxmXsWEH4db0fE
-FY8ElggGQgT4hNYdvJGmQr5J1WqIP7wtUdGejeBSzFfdNTVY27SPJIjki9/ca1TS
-gSuyzpJLHB9G+h3Ykst2Z7UJmQnlrBcUVXDGPKBWCgOz3GIZ38i1MH/1PCZ1Eb3X
-G7OHngevZXHloM8apwkQHZOJZlvoPGIytbU6bumFAYueQ4xncyhZW+vj3CzMpSZy
-YhK05pyDRPZRpOLAeiRXyg6lPzq1O4vldu5w5pLeFlwoW5cZJ5L+epJUzpM5ChaH
-vGOz9bGTXOBut9Dq+WIyiET7vycotjCVXRIouZW+j1MY5aIYFuJWpLIsEPUdN6b4
-t/bQWVyJ98LVtZR00dX+G7bw5tYee9I8y6jj9RjzIR9u701oBnstXW5DiabA+aC/
-gh7PU3+06yzbXfZqfUAkBXKJOAGTy3HCOV0GEfZvePg3DTmEJwIDAQABo2MwYTAO
-BgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUajn6QiL3
-5okATV59M4PLuG53hq8wHwYDVR0jBBgwFoAUajn6QiL35okATV59M4PLuG53hq8w
-DQYJKoZIhvcNAQENBQADggIBAJjLq0A85TMCl38th6aP1F5Kr7ge57tx+4BkJamz
-Gj5oXScmp7oq4fBXgwpkTx4idBvpkF/wrM//T2h6OKQQbA2xx6R3gBi2oihEdqc0
-nXGEL8pZ0keImUEiyTCYYW49qKgFbdEfwFFEVn8nNQLdXpgKQuswv42hm1GqO+qT
-RmTFAHneIWv2V6CG1wZy7HBGS4tz3aAhdT7cHcCP009zHIXZ/n9iyJVvttN7jLpT
-wm+bREx50B1ws9efAvSyB7DH5fitIw6mVskpEndI2S9G/Tvw/HRwkqWOOAgfZDC2
-t0v7NqwQjqBSM2OdAzVWxWm9xiNaJ5T2pBL4LTM8oValX9YZ6e18CL13zSdkzJTa
-TkZQh+D5wVOAHrut+0dSixv9ovneDiK3PTNZbNTe9ZUGMg1RGUFcPk8G97krgCf2
-o6p6fAbhQ8MTOWIaNr3gKC6UAuQpLmBVrkA9sHSSXvAgZJY/X0VdiLWK2gKgW0VU
-3jg9CcCoSmVGFvyqv1ROTVu+OEO3KMqLM6oaJbolXCkvW0pujOotnCr2BXbgd5eA
-iN1nE28daCSLT7d0geX0YJ96Vdc+N9oWaz53rK4YcJUIeSkDiv7BO7M/Gg+kO14f
-WKGVyasvc0rQLW6aWQ9VGHgtPFGml4vmu7JwqkwR3v98KzfUetF3NI/n+UL3PIEM
-S1IK
------END CERTIFICATE-----
-
-# OpenTrust Root CA G3
------BEGIN CERTIFICATE-----
-MIICITCCAaagAwIBAgISESDm+Ez8JLC+BUCs2oMbNGA/MAoGCCqGSM49BAMDMEAx
-CzAJBgNVBAYTAkZSMRIwEAYDVQQKDAlPcGVuVHJ1c3QxHTAbBgNVBAMMFE9wZW5U
-cnVzdCBSb290IENBIEczMB4XDTE0MDUyNjAwMDAwMFoXDTM4MDExNTAwMDAwMFow
-QDELMAkGA1UEBhMCRlIxEjAQBgNVBAoMCU9wZW5UcnVzdDEdMBsGA1UEAwwUT3Bl
-blRydXN0IFJvb3QgQ0EgRzMwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAARK7liuTcpm
-3gY6oxH84Bjwbhy6LTAMidnW7ptzg6kjFYwvWYpa3RTqnVkrQ7cG7DK2uu5Bta1d
-oYXM6h0UZqNnfkbilPPntlahFVmhTzeXuSIevRHr9LIfXsMUmuXZl5mjYzBhMA4G
-A1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRHd8MUi2I5
-DMlv4VBN0BBY3JWIbTAfBgNVHSMEGDAWgBRHd8MUi2I5DMlv4VBN0BBY3JWIbTAK
-BggqhkjOPQQDAwNpADBmAjEAj6jcnboMBBf6Fek9LykBl7+BFjNAk2z8+e2AcG+q
-j9uEwov1NcoG3GRvaBbhj5G5AjEA2Euly8LQCGzpGPta3U1fJAuwACEl74+nBCZx
-4nxp5V2a+EEfOzmTk51V6s2N8fvB
------END CERTIFICATE-----
-
-# QuoVadis Root CA 1 G3
------BEGIN CERTIFICATE-----
-MIIFYDCCA0igAwIBAgIUeFhfLq0sGUvjNwc1NBMotZbUZZMwDQYJKoZIhvcNAQEL
-BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc
-BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMSBHMzAeFw0xMjAxMTIxNzI3NDRaFw00
-MjAxMTIxNzI3NDRaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM
-aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDEgRzMwggIiMA0GCSqG
-SIb3DQEBAQUAA4ICDwAwggIKAoICAQCgvlAQjunybEC0BJyFuTHK3C3kEakEPBtV
-wedYMB0ktMPvhd6MLOHBPd+C5k+tR4ds7FtJwUrVu4/sh6x/gpqG7D0DmVIB0jWe
-rNrwU8lmPNSsAgHaJNM7qAJGr6Qc4/hzWHa39g6QDbXwz8z6+cZM5cOGMAqNF341
-68Xfuw6cwI2H44g4hWf6Pser4BOcBRiYz5P1sZK0/CPTz9XEJ0ngnjybCKOLXSoh
-4Pw5qlPafX7PGglTvF0FBM+hSo+LdoINofjSxxR3W5A2B4GbPgb6Ul5jxaYA/qXp
-UhtStZI5cgMJYr2wYBZupt0lwgNm3fME0UDiTouG9G/lg6AnhF4EwfWQvTA9xO+o
-abw4m6SkltFi2mnAAZauy8RRNOoMqv8hjlmPSlzkYZqn0ukqeI1RPToV7qJZjqlc
-3sX5kCLliEVx3ZGZbHqfPT2YfF72vhZooF6uCyP8Wg+qInYtyaEQHeTTRCOQiJ/G
-KubX9ZqzWB4vMIkIG1SitZgj7Ah3HJVdYdHLiZxfokqRmu8hqkkWCKi9YSgxyXSt
-hfbZxbGL0eUQMk1fiyA6PEkfM4VZDdvLCXVDaXP7a3F98N/ETH3Goy7IlXnLc6KO
-Tk0k+17kBL5yG6YnLUlamXrXXAkgt3+UuU/xDRxeiEIbEbfnkduebPRq34wGmAOt
-zCjvpUfzUwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB
-BjAdBgNVHQ4EFgQUo5fW816iEOGrRZ88F2Q87gFwnMwwDQYJKoZIhvcNAQELBQAD
-ggIBABj6W3X8PnrHX3fHyt/PX8MSxEBd1DKquGrX1RUVRpgjpeaQWxiZTOOtQqOC
-MTaIzen7xASWSIsBx40Bz1szBpZGZnQdT+3Btrm0DWHMY37XLneMlhwqI2hrhVd2
-cDMT/uFPpiN3GPoajOi9ZcnPP/TJF9zrx7zABC4tRi9pZsMbj/7sPtPKlL92CiUN
-qXsCHKnQO18LwIE6PWThv6ctTr1NxNgpxiIY0MWscgKCP6o6ojoilzHdCGPDdRS5
-YCgtW2jgFqlmgiNR9etT2DGbe+m3nUvriBbP+V04ikkwj+3x6xn0dxoxGE1nVGwv
-b2X52z3sIexe9PSLymBlVNFxZPT5pqOBMzYzcfCkeF9OrYMh3jRJjehZrJ3ydlo2
-8hP0r+AJx2EqbPfgna67hkooby7utHnNkDPDs3b69fBsnQGQ+p6Q9pxyz0fawx/k
-NSBT8lTR32GDpgLiJTjehTItXnOQUl1CxM49S+H5GYQd1aJQzEH7QRTDvdbJWqNj
-ZgKAvQU6O0ec7AAmTPWIUb+oI38YB7AL7YsmoWTTYUrrXJ/es69nA7Mf3W1daWhp
-q1467HxpvMc7hU6eFbm0FU/DlXpY18ls6Wy58yljXrQs8C097Vpl4KlbQMJImYFt
-nh8GKjwStIsPm6Ik8KaN1nrgS7ZklmOVhMJKzRwuJIczYOXD
------END CERTIFICATE-----
-
-# QuoVadis Root CA 2
------BEGIN CERTIFICATE-----
-MIIFtzCCA5+gAwIBAgICBQkwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0x
-GTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJv
-b3QgQ0EgMjAeFw0wNjExMjQxODI3MDBaFw0zMTExMjQxODIzMzNaMEUxCzAJBgNV
-BAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRswGQYDVQQDExJRdW9W
-YWRpcyBSb290IENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCa
-GMpLlA0ALa8DKYrwD4HIrkwZhR0In6spRIXzL4GtMh6QRr+jhiYaHv5+HBg6XJxg
-Fyo6dIMzMH1hVBHL7avg5tKifvVrbxi3Cgst/ek+7wrGsxDp3MJGF/hd/aTa/55J
-WpzmM+Yklvc/ulsrHHo1wtZn/qtmUIttKGAr79dgw8eTvI02kfN/+NsRE8Scd3bB
-rrcCaoF6qUWD4gXmuVbBlDePSHFjIuwXZQeVikvfj8ZaCuWw419eaxGrDPmF60Tp
-+ARz8un+XJiM9XOva7R+zdRcAitMOeGylZUtQofX1bOQQ7dsE/He3fbE+Ik/0XX1
-ksOR1YqI0JDs3G3eicJlcZaLDQP9nL9bFqyS2+r+eXyt66/3FsvbzSUr5R/7mp/i
-Ucw6UwxI5g69ybR2BlLmEROFcmMDBOAENisgGQLodKcftslWZvB1JdxnwQ5hYIiz
-PtGo/KPaHbDRsSNU30R2be1B2MGyIrZTHN81Hdyhdyox5C315eXbyOD/5YDXC2Og
-/zOhD7osFRXql7PSorW+8oyWHhqPHWykYTe5hnMz15eWniN9gqRMgeKh0bpnX5UH
-oycR7hYQe7xFSkyyBNKr79X9DFHOUGoIMfmR2gyPZFwDwzqLID9ujWc9Otb+fVuI
-yV77zGHcizN300QyNQliBJIWENieJ0f7OyHj+OsdWwIDAQABo4GwMIGtMA8GA1Ud
-EwEB/wQFMAMBAf8wCwYDVR0PBAQDAgEGMB0GA1UdDgQWBBQahGK8SEwzJQTU7tD2
-A8QZRtGUazBuBgNVHSMEZzBlgBQahGK8SEwzJQTU7tD2A8QZRtGUa6FJpEcwRTEL
-MAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMT
-ElF1b1ZhZGlzIFJvb3QgQ0EgMoICBQkwDQYJKoZIhvcNAQEFBQADggIBAD4KFk2f
-BluornFdLwUvZ+YTRYPENvbzwCYMDbVHZF34tHLJRqUDGCdViXh9duqWNIAXINzn
-g/iN/Ae42l9NLmeyhP3ZRPx3UIHmfLTJDQtyU/h2BwdBR5YM++CCJpNVjP4iH2Bl
-fF/nJrP3MpCYUNQ3cVX2kiF495V5+vgtJodmVjB3pjd4M1IQWK4/YY7yarHvGH5K
-WWPKjaJW1acvvFYfzznB4vsKqBUsfU16Y8Zsl0Q80m/DShcK+JDSV6IZUaUtl0Ha
-B0+pUNqQjZRG4T7wlP0QADj1O+hA4bRuVhogzG9Yje0uRY/W6ZM/57Es3zrWIozc
-hLsib9D45MY56QSIPMO661V6bYCZJPVsAfv4l7CUW+v90m/xd2gNNWQjrLhVoQPR
-TUIZ3Ph1WVaj+ahJefivDrkRoHy3au000LYmYjgahwz46P0u05B/B5EqHdZ+XIWD
-mbA4CD/pXvk1B+TJYm5Xf6dQlfe6yJvmjqIBxdZmv3lh8zwc4bmCXF2gw+nYSL0Z
-ohEUGW6yhhtoPkg3Goi3XZZenMfvJ2II4pEZXNLxId26F0KCl3GBUzGpn/Z9Yr9y
-4aOTHcyKJloJONDO1w2AFrR4pTqHTI2KpdVGl/IsELm8VCLAAVBpQ570su9t+Oza
-8eOx79+Rj1QqCyXBJhnEUhAFZdWCEOrCMc0u
------END CERTIFICATE-----
-
-# QuoVadis Root CA
------BEGIN CERTIFICATE-----
-MIIF0DCCBLigAwIBAgIEOrZQizANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJC
-TTEZMBcGA1UEChMQUXVvVmFkaXMgTGltaXRlZDElMCMGA1UECxMcUm9vdCBDZXJ0
-aWZpY2F0aW9uIEF1dGhvcml0eTEuMCwGA1UEAxMlUXVvVmFkaXMgUm9vdCBDZXJ0
-aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wMTAzMTkxODMzMzNaFw0yMTAzMTcxODMz
-MzNaMH8xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMSUw
-IwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MS4wLAYDVQQDEyVR
-dW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG
-9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv2G1lVO6V/z68mcLOhrfEYBklbTRvM16z/Yp
-li4kVEAkOPcahdxYTMukJ0KX0J+DisPkBgNbAKVRHnAEdOLB1Dqr1607BxgFjv2D
-rOpm2RgbaIr1VxqYuvXtdj182d6UajtLF8HVj71lODqV0D1VNk7feVcxKh7YWWVJ
-WCCYfqtffp/p1k3sg3Spx2zY7ilKhSoGFPlU5tPaZQeLYzcS19Dsw3sgQUSj7cug
-F+FxZc4dZjH3dgEZyH0DWLaVSR2mEiboxgx24ONmy+pdpibu5cxfvWenAScOospU
-xbF6lR1xHkopigPcakXBpBlebzbNw6Kwt/5cOOJSvPhEQ+aQuwIDAQABo4ICUjCC
-Ak4wPQYIKwYBBQUHAQEEMTAvMC0GCCsGAQUFBzABhiFodHRwczovL29jc3AucXVv
-dmFkaXNvZmZzaG9yZS5jb20wDwYDVR0TAQH/BAUwAwEB/zCCARoGA1UdIASCAREw
-ggENMIIBCQYJKwYBBAG+WAABMIH7MIHUBggrBgEFBQcCAjCBxxqBxFJlbGlhbmNl
-IG9uIHRoZSBRdW9WYWRpcyBSb290IENlcnRpZmljYXRlIGJ5IGFueSBwYXJ0eSBh
-c3N1bWVzIGFjY2VwdGFuY2Ugb2YgdGhlIHRoZW4gYXBwbGljYWJsZSBzdGFuZGFy
-ZCB0ZXJtcyBhbmQgY29uZGl0aW9ucyBvZiB1c2UsIGNlcnRpZmljYXRpb24gcHJh
-Y3RpY2VzLCBhbmQgdGhlIFF1b1ZhZGlzIENlcnRpZmljYXRlIFBvbGljeS4wIgYI
-KwYBBQUHAgEWFmh0dHA6Ly93d3cucXVvdmFkaXMuYm0wHQYDVR0OBBYEFItLbe3T
-KbkGGew5Oanwl4Rqy+/fMIGuBgNVHSMEgaYwgaOAFItLbe3TKbkGGew5Oanwl4Rq
-y+/foYGEpIGBMH8xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1p
-dGVkMSUwIwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MS4wLAYD
-VQQDEyVRdW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggQ6tlCL
-MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEAitQUtf70mpKnGdSk
-fnIYj9lofFIk3WdvOXrEql494liwTXCYhGHoG+NpGA7O+0dQoE7/8CQfvbLO9Sf8
-7C9TqnN7Az10buYWnuulLsS/VidQK2K6vkscPFVcQR0kvoIgR13VRH56FmjffU1R
-cHhXHTMe/QKZnAzNCgVPx7uOpHX6Sm2xgI4JVrmcGmD+XcHXetwReNDWXcG31a0y
-mQM6isxUJTkxgXsTIlG6Rmyhu576BGxJJnSP0nPrzDCi5upZIof4l/UO/erMkqQW
-xFIY6iHOsfHmhIHluqmGKPJDWl0Snawe2ajlCmqnf6CHKc/yiU3U7MXi5nrQNiOK
-SnQ2+Q==
------END CERTIFICATE-----
-
-# QuoVadis Root CA 2 G3
------BEGIN CERTIFICATE-----
-MIIFYDCCA0igAwIBAgIURFc0JFuBiZs18s64KztbpybwdSgwDQYJKoZIhvcNAQEL
-BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc
-BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMiBHMzAeFw0xMjAxMTIxODU5MzJaFw00
-MjAxMTIxODU5MzJaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM
-aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDIgRzMwggIiMA0GCSqG
-SIb3DQEBAQUAA4ICDwAwggIKAoICAQChriWyARjcV4g/Ruv5r+LrI3HimtFhZiFf
-qq8nUeVuGxbULX1QsFN3vXg6YOJkApt8hpvWGo6t/x8Vf9WVHhLL5hSEBMHfNrMW
-n4rjyduYNM7YMxcoRvynyfDStNVNCXJJ+fKH46nafaF9a7I6JaltUkSs+L5u+9ym
-c5GQYaYDFCDy54ejiK2toIz/pgslUiXnFgHVy7g1gQyjO/Dh4fxaXc6AcW34Sas+
-O7q414AB+6XrW7PFXmAqMaCvN+ggOp+oMiwMzAkd056OXbxMmO7FGmh77FOm6RQ1
-o9/NgJ8MSPsc9PG/Srj61YxxSscfrf5BmrODXfKEVu+lV0POKa2Mq1W/xPtbAd0j
-IaFYAI7D0GoT7RPjEiuA3GfmlbLNHiJuKvhB1PLKFAeNilUSxmn1uIZoL1NesNKq
-IcGY5jDjZ1XHm26sGahVpkUG0CM62+tlXSoREfA7T8pt9DTEceT/AFr2XK4jYIVz
-8eQQsSWu1ZK7E8EM4DnatDlXtas1qnIhO4M15zHfeiFuuDIIfR0ykRVKYnLP43eh
-vNURG3YBZwjgQQvD6xVu+KQZ2aKrr+InUlYrAoosFCT5v0ICvybIxo/gbjh9Uy3l
-7ZizlWNof/k19N+IxWA1ksB8aRxhlRbQ694Lrz4EEEVlWFA4r0jyWbYW8jwNkALG
-cC4BrTwV1wIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB
-BjAdBgNVHQ4EFgQU7edvdlq/YOxJW8ald7tyFnGbxD0wDQYJKoZIhvcNAQELBQAD
-ggIBAJHfgD9DCX5xwvfrs4iP4VGyvD11+ShdyLyZm3tdquXK4Qr36LLTn91nMX66
-AarHakE7kNQIXLJgapDwyM4DYvmL7ftuKtwGTTwpD4kWilhMSA/ohGHqPHKmd+RC
-roijQ1h5fq7KpVMNqT1wvSAZYaRsOPxDMuHBR//47PERIjKWnML2W2mWeyAMQ0Ga
-W/ZZGYjeVYg3UQt4XAoeo0L9x52ID8DyeAIkVJOviYeIyUqAHerQbj5hLja7NQ4n
-lv1mNDthcnPxFlxHBlRJAHpYErAK74X9sbgzdWqTHBLmYF5vHX/JHyPLhGGfHoJE
-+V+tYlUkmlKY7VHnoX6XOuYvHxHaU4AshZ6rNRDbIl9qxV6XU/IyAgkwo1jwDQHV
-csaxfGl7w/U2Rcxhbl5MlMVerugOXou/983g7aEOGzPuVBj+D77vfoRrQ+NwmNtd
-dbINWQeFFSM51vHfqSYP1kjHs6Yi9TM3WpVHn3u6GBVv/9YUZINJ0gpnIdsPNWNg
-KCLjsZWDzYWm3S8P52dSbrsvhXz1SnPnxT7AvSESBT/8twNJAlvIJebiVDj1eYeM
-HVOyToV7BjjHLPj4sHKNJeV3UvQDHEimUF+IIDBu8oJDqz2XhOdT+yHBTw8imoa4
-WSr2Rz0ZiC3oheGe7IUIarFsNMkd7EgrO3jtZsSOeWmD3n+M
------END CERTIFICATE-----
-
-# QuoVadis Root CA 3
------BEGIN CERTIFICATE-----
-MIIGnTCCBIWgAwIBAgICBcYwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0x
-GTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJv
-b3QgQ0EgMzAeFw0wNjExMjQxOTExMjNaFw0zMTExMjQxOTA2NDRaMEUxCzAJBgNV
-BAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRswGQYDVQQDExJRdW9W
-YWRpcyBSb290IENBIDMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDM
-V0IWVJzmmNPTTe7+7cefQzlKZbPoFog02w1ZkXTPkrgEQK0CSzGrvI2RaNggDhoB
-4hp7Thdd4oq3P5kazethq8Jlph+3t723j/z9cI8LoGe+AaJZz3HmDyl2/7FWeUUr
-H556VOijKTVopAFPD6QuN+8bv+OPEKhyq1hX51SGyMnzW9os2l2ObjyjPtr7guXd
-8lyyBTNvijbO0BNO/79KDDRMpsMhvVAEVeuxu537RR5kFd5VAYwCdrXLoT9Cabwv
-vWhDFlaJKjdhkf2mrk7AyxRllDdLkgbvBNDInIjbC3uBr7E9KsRlOni27tyAsdLT
-mZw67mtaa7ONt9XOnMK+pUsvFrGeaDsGb659n/je7Mwpp5ijJUMv7/FfJuGITfhe
-btfZFG4ZM2mnO4SJk8RTVROhUXhA+LjJou57ulJCg54U7QVSWllWp5f8nT8KKdjc
-T5EOE7zelaTfi5m+rJsziO+1ga8bxiJTyPbH7pcUsMV8eFLI8M5ud2CEpukqdiDt
-WAEXMJPpGovgc2PZapKUSU60rUqFxKMiMPwJ7Wgic6aIDFUhWMXhOp8q3crhkODZ
-c6tsgLjoC2SToJyMGf+z0gzskSaHirOi4XCPLArlzW1oUevaPwV/izLmE1xr/l9A
-4iLItLRkT9a6fUg+qGkM17uGcclzuD87nSVL2v9A6wIDAQABo4IBlTCCAZEwDwYD
-VR0TAQH/BAUwAwEB/zCB4QYDVR0gBIHZMIHWMIHTBgkrBgEEAb5YAAMwgcUwgZMG
-CCsGAQUFBwICMIGGGoGDQW55IHVzZSBvZiB0aGlzIENlcnRpZmljYXRlIGNvbnN0
-aXR1dGVzIGFjY2VwdGFuY2Ugb2YgdGhlIFF1b1ZhZGlzIFJvb3QgQ0EgMyBDZXJ0
-aWZpY2F0ZSBQb2xpY3kgLyBDZXJ0aWZpY2F0aW9uIFByYWN0aWNlIFN0YXRlbWVu
-dC4wLQYIKwYBBQUHAgEWIWh0dHA6Ly93d3cucXVvdmFkaXNnbG9iYWwuY29tL2Nw
-czALBgNVHQ8EBAMCAQYwHQYDVR0OBBYEFPLAE+CCQz777i9nMpY1XNu4ywLQMG4G
-A1UdIwRnMGWAFPLAE+CCQz777i9nMpY1XNu4ywLQoUmkRzBFMQswCQYDVQQGEwJC
-TTEZMBcGA1UEChMQUXVvVmFkaXMgTGltaXRlZDEbMBkGA1UEAxMSUXVvVmFkaXMg
-Um9vdCBDQSAzggIFxjANBgkqhkiG9w0BAQUFAAOCAgEAT62gLEz6wPJv92ZVqyM0
-7ucp2sNbtrCD2dDQ4iH782CnO11gUyeim/YIIirnv6By5ZwkajGxkHon24QRiSem
-d1o417+shvzuXYO8BsbRd2sPbSQvS3pspweWyuOEn62Iix2rFo1bZhfZFvSLgNLd
-+LJ2w/w4E6oM3kJpK27zPOuAJ9v1pkQNn1pVWQvVDVJIxa6f8i+AxeoyUDUSly7B
-4f/xI4hROJ/yZlZ25w9Rl6VSDE1JUZU2Pb+iSwwQHYaZTKrzchGT5Or2m9qoXadN
-t54CrnMAyNojA+j56hl0YgCUyyIgvpSnWbWCar6ZeXqp8kokUvd0/bpO5qgdAm6x
-DYBEwa7TIzdfu4V8K5Iu6H6li92Z4b8nby1dqnuH/grdS/yO9SbkbnBCbjPsMZ57
-k8HkyWkaPcBrTiJt7qtYTcbQQcEr6k8Sh17rRdhs9ZgC06DYVYoGmRmioHfRMJ6s
-zHXug/WwYjnPbFfiTNKRCw51KBuav/0aQ/HKd/s7j2G4aSgWQgRecCocIdiP4b0j
-Wy10QJLZYxkNc91pvGJHvOB0K7Lrfb5BG7XARsWhIstfTsEokt4YutUqKLsRixeT
-mJlglFwjz1onl14LBQaTNx47aTbrqZ5hHY8y2o4M1nQ+ewkk2gF3R8Q7zTSMmfXK
-4SVhM7JZG+Ju1zdXtg2pEto=
------END CERTIFICATE-----
-
-# QuoVadis Root CA 3 G3
------BEGIN CERTIFICATE-----
-MIIFYDCCA0igAwIBAgIULvWbAiin23r/1aOp7r0DoM8Sah0wDQYJKoZIhvcNAQEL
-BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc
-BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMyBHMzAeFw0xMjAxMTIyMDI2MzJaFw00
-MjAxMTIyMDI2MzJaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM
-aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDMgRzMwggIiMA0GCSqG
-SIb3DQEBAQUAA4ICDwAwggIKAoICAQCzyw4QZ47qFJenMioKVjZ/aEzHs286IxSR
-/xl/pcqs7rN2nXrpixurazHb+gtTTK/FpRp5PIpM/6zfJd5O2YIyC0TeytuMrKNu
-FoM7pmRLMon7FhY4futD4tN0SsJiCnMK3UmzV9KwCoWdcTzeo8vAMvMBOSBDGzXR
-U7Ox7sWTaYI+FrUoRqHe6okJ7UO4BUaKhvVZR74bbwEhELn9qdIoyhA5CcoTNs+c
-ra1AdHkrAj80//ogaX3T7mH1urPnMNA3I4ZyYUUpSFlob3emLoG+B01vr87ERROR
-FHAGjx+f+IdpsQ7vw4kZ6+ocYfx6bIrc1gMLnia6Et3UVDmrJqMz6nWB2i3ND0/k
-A9HvFZcba5DFApCTZgIhsUfei5pKgLlVj7WiL8DWM2fafsSntARE60f75li59wzw
-eyuxwHApw0BiLTtIadwjPEjrewl5qW3aqDCYz4ByA4imW0aucnl8CAMhZa634Ryl
-sSqiMd5mBPfAdOhx3v89WcyWJhKLhZVXGqtrdQtEPREoPHtht+KPZ0/l7DxMYIBp
-VzgeAVuNVejH38DMdyM0SXV89pgR6y3e7UEuFAUCf+D+IOs15xGsIs5XPd7JMG0Q
-A4XN8f+MFrXBsj6IbGB/kE+V9/YtrQE5BwT6dYB9v0lQ7e/JxHwc64B+27bQ3RP+
-ydOc17KXqQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB
-BjAdBgNVHQ4EFgQUxhfQvKjqAkPyGwaZXSuQILnXnOQwDQYJKoZIhvcNAQELBQAD
-ggIBADRh2Va1EodVTd2jNTFGu6QHcrxfYWLopfsLN7E8trP6KZ1/AvWkyaiTt3px
-KGmPc+FSkNrVvjrlt3ZqVoAh313m6Tqe5T72omnHKgqwGEfcIHB9UqM+WXzBusnI
-FUBhynLWcKzSt/Ac5IYp8M7vaGPQtSCKFWGafoaYtMnCdvvMujAWzKNhxnQT5Wvv
-oxXqA/4Ti2Tk08HS6IT7SdEQTXlm66r99I0xHnAUrdzeZxNMgRVhvLfZkXdxGYFg
-u/BYpbWcC/ePIlUnwEsBbTuZDdQdm2NnL9DuDcpmvJRPpq3t/O5jrFc/ZSXPsoaP
-0Aj/uHYUbt7lJ+yreLVTubY/6CD50qi+YUbKh4yE8/nxoGibIh6BJpsQBJFxwAYf
-3KDTuVan45gtf4Od34wrnDKOMpTwATwiKp9Dwi7DmDkHOHv8XgBCH/MyJnmDhPbl
-8MFREsALHgQjDFSlTC9JxUrRtm5gDWv8a4uFJGS3iQ6rJUdbPM9+Sb3H6QrG2vd+
-DhcI00iX0HGS8A85PjRqHH3Y8iKuu2n0M7SmSFXRDw4m6Oy2Cy2nhTXN/VnIn9HN
-PlopNLk9hM6xZdRZkZFWdSHBd575euFgndOtBBj0fOtek49TSiIp+EgrPk2GrFt/
-ywaZWWDYWGWVjUTR939+J399roD1B0y2PpxxVJkES/1Y+Zj0
------END CERTIFICATE-----
-
-# Secure Global CA
------BEGIN CERTIFICATE-----
-MIIDvDCCAqSgAwIBAgIQB1YipOjUiolN9BPI8PjqpTANBgkqhkiG9w0BAQUFADBK
-MQswCQYDVQQGEwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24x
-GTAXBgNVBAMTEFNlY3VyZSBHbG9iYWwgQ0EwHhcNMDYxMTA3MTk0MjI4WhcNMjkx
-MjMxMTk1MjA2WjBKMQswCQYDVQQGEwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3Qg
-Q29ycG9yYXRpb24xGTAXBgNVBAMTEFNlY3VyZSBHbG9iYWwgQ0EwggEiMA0GCSqG
-SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvNS7YrGxVaQZx5RNoJLNP2MwhR/jxYDiJ
-iQPpvepeRlMJ3Fz1Wuj3RSoC6zFh1ykzTM7HfAo3fg+6MpjhHZevj8fcyTiW89sa
-/FHtaMbQbqR8JNGuQsiWUGMu4P51/pinX0kuleM5M2SOHqRfkNJnPLLZ/kG5VacJ
-jnIFHovdRIWCQtBJwB1g8NEXLJXr9qXBkqPFwqcIYA1gBBCWeZ4WNOaptvolRTnI
-HmX5k/Wq8VLcmZg9pYYaDDUz+kulBAYVHDGA76oYa8J719rO+TMg1fW9ajMtgQT7
-sFzUnKPiXB3jqUJ1XnvUd+85VLrJChgbEplJL4hL/VBi0XPnj3pDAgMBAAGjgZ0w
-gZowEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0PBAQDAgGGMA8GA1UdEwEB/wQF
-MAMBAf8wHQYDVR0OBBYEFK9EBMJBfkiD2045AuzshHrmzsmkMDQGA1UdHwQtMCsw
-KaAnoCWGI2h0dHA6Ly9jcmwuc2VjdXJldHJ1c3QuY29tL1NHQ0EuY3JsMBAGCSsG
-AQQBgjcVAQQDAgEAMA0GCSqGSIb3DQEBBQUAA4IBAQBjGghAfaReUw132HquHw0L
-URYD7xh8yOOvaliTFGCRsoTciE6+OYo68+aCiV0BN7OrJKQVDpI1WkpEXk5X+nXO
-H0jOZvQ8QCaSmGwb7iRGDBezUqXbpZGRzzfTb+cnCDpOGR86p1hcF895P4vkp9Mm
-I50mD1hp/Ed+stCNi5O/KU9DaXR2Z0vPB4zmAve14bRDtUstFJ/53CYNv6ZHdAbY
-iNE6KTCEztI5gGIbqMdXSbxqVVFnFUq+NQfk1XWYN3kwFNspnWzFacxHVaIw98xc
-f8LDmBxrThaA63p4ZUWiABqvDA1VZDRIuJK58bRQKfJPIx/abKwfROHdI3hRW8cW
------END CERTIFICATE-----
-
-# SecureSign RootCA11
------BEGIN CERTIFICATE-----
-MIIDbTCCAlWgAwIBAgIBATANBgkqhkiG9w0BAQUFADBYMQswCQYDVQQGEwJKUDEr
-MCkGA1UEChMiSmFwYW4gQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcywgSW5jLjEcMBoG
-A1UEAxMTU2VjdXJlU2lnbiBSb290Q0ExMTAeFw0wOTA0MDgwNDU2NDdaFw0yOTA0
-MDgwNDU2NDdaMFgxCzAJBgNVBAYTAkpQMSswKQYDVQQKEyJKYXBhbiBDZXJ0aWZp
-Y2F0aW9uIFNlcnZpY2VzLCBJbmMuMRwwGgYDVQQDExNTZWN1cmVTaWduIFJvb3RD
-QTExMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA/XeqpRyQBTvLTJsz
-i1oURaTnkBbR31fSIRCkF/3frNYfp+TbfPfs37gD2pRY/V1yfIw/XwFndBWW4wI8
-h9uuywGOwvNmxoVF9ALGOrVisq/6nL+k5tSAMJjzDbaTj6nU2DbysPyKyiyhFTOV
-MdrAG/LuYpmGYz+/3ZMqg6h2uRMft85OQoWPIucuGvKVCbIFtUROd6EgvanyTgp9
-UK31BQ1FT0Zx/Sg+U/sE2C3XZR1KG/rPO7AxmjVuyIsG0wCR8pQIZUyxNAYAeoni
-8McDWc/V1uinMrPmmECGxc0nEovMe863ETxiYAcjPitAbpSACW22s293bzUIUPsC
-h8U+iQIDAQABo0IwQDAdBgNVHQ4EFgQUW/hNT7KlhtQ60vFjmqC+CfZXt94wDgYD
-VR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEB
-AKChOBZmLqdWHyGcBvod7bkixTgm2E5P7KN/ed5GIaGHd48HCJqypMWvDzKYC3xm
-KbabfSVSSUOrTC4rbnpwrxYO4wJs+0LmGJ1F2FXI6Dvd5+H0LgscNFxsWEr7jIhQ
-X5Ucv+2rIrVls4W6ng+4reV6G4pQOh29Dbx7VFALuUKvVaAYga1lme++5Jy/xIWr
-QbJUb9wlze144o4MjQlJ3WN7WmmWAiGovVJZ6X01y8hSyn+B/tlr0/cR7SXf+Of5
-pPpyl4RTDaXQMhhRdlkUbA/r7F+AjHVDg8OFmP9Mni0N5HeDk061lgeLKBObjBmN
-QSdJQO7e5iNEOdyhIta6A/I=
------END CERTIFICATE-----
-
-# SecureTrust CA
------BEGIN CERTIFICATE-----
-MIIDuDCCAqCgAwIBAgIQDPCOXAgWpa1Cf/DrJxhZ0DANBgkqhkiG9w0BAQUFADBI
-MQswCQYDVQQGEwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24x
-FzAVBgNVBAMTDlNlY3VyZVRydXN0IENBMB4XDTA2MTEwNzE5MzExOFoXDTI5MTIz
-MTE5NDA1NVowSDELMAkGA1UEBhMCVVMxIDAeBgNVBAoTF1NlY3VyZVRydXN0IENv
-cnBvcmF0aW9uMRcwFQYDVQQDEw5TZWN1cmVUcnVzdCBDQTCCASIwDQYJKoZIhvcN
-AQEBBQADggEPADCCAQoCggEBAKukgeWVzfX2FI7CT8rU4niVWJxB4Q2ZQCQXOZEz
-Zum+4YOvYlyJ0fwkW2Gz4BERQRwdbvC4u/jep4G6pkjGnx29vo6pQT64lO0pGtSO
-0gMdA+9tDWccV9cGrcrI9f4Or2YlSASWC12juhbDCE/RRvgUXPLIXgGZbf2IzIao
-wW8xQmxSPmjL8xk037uHGFaAJsTQ3MBv396gwpEWoGQRS0S8Hvbn+mPeZqx2pHGj
-7DaUaHp3pLHnDi+BeuK1cobvomuL8A/b01k/unK8RCSc43Oz969XL0Imnal0ugBS
-8kvNU3xHCzaFDmapCJcWNFfBZveA4+1wVMeT4C4oFVmHursCAwEAAaOBnTCBmjAT
-BgkrBgEEAYI3FAIEBh4EAEMAQTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB
-/zAdBgNVHQ4EFgQUQjK2FvoE/f5dS3rD/fdMQB1aQ68wNAYDVR0fBC0wKzApoCeg
-JYYjaHR0cDovL2NybC5zZWN1cmV0cnVzdC5jb20vU1RDQS5jcmwwEAYJKwYBBAGC
-NxUBBAMCAQAwDQYJKoZIhvcNAQEFBQADggEBADDtT0rhWDpSclu1pqNlGKa7UTt3
-6Z3q059c4EVlew3KW+JwULKUBRSuSceNQQcSc5R+DCMh/bwQf2AQWnL1mA6s7Ll/
-3XpvXdMc9P+IBWlCqQVxyLesJugutIxq/3HcuLHfmbx8IVQr5Fiiu1cprp6poxkm
-D5kuCLDv/WnPmRoJjeOnnyvJNjR7JLN4TJUXpAYmHrZkUjZfYGfZnMUFdAvnZyPS
-CPyI6a6Lf+Ew9Dd+/cYy2i2eRDAwbO4H3tI0/NL/QPZL9GZGBlSm8jIKYyYwa5vR
-3ItHuuG51WLQoqD0ZwV4KWMabwTW+MZMo5qxN7SN5ShLHZ4swrhovO0C7jE=
------END CERTIFICATE-----
-
-# Security Communication Root CA
------BEGIN CERTIFICATE-----
-MIIDWjCCAkKgAwIBAgIBADANBgkqhkiG9w0BAQUFADBQMQswCQYDVQQGEwJKUDEY
-MBYGA1UEChMPU0VDT00gVHJ1c3QubmV0MScwJQYDVQQLEx5TZWN1cml0eSBDb21t
-dW5pY2F0aW9uIFJvb3RDQTEwHhcNMDMwOTMwMDQyMDQ5WhcNMjMwOTMwMDQyMDQ5
-WjBQMQswCQYDVQQGEwJKUDEYMBYGA1UEChMPU0VDT00gVHJ1c3QubmV0MScwJQYD
-VQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTEwggEiMA0GCSqGSIb3
-DQEBAQUAA4IBDwAwggEKAoIBAQCzs/5/022x7xZ8V6UMbXaKL0u/ZPtM7orw8yl8
-9f/uKuDp6bpbZCKamm8sOiZpUQWZJtzVHGpxxpp9Hp3dfGzGjGdnSj74cbAZJ6kJ
-DKaVv0uMDPpVmDvY6CKhS3E4eayXkmmziX7qIWgGmBSWh9JhNrxtJ1aeV+7AwFb9
-Ms+k2Y7CI9eNqPPYJayX5HA49LY6tJ07lyZDo6G8SVlyTCMwhwFY9k6+HGhWZq/N
-QV3Is00qVUarH9oe4kA92819uZKAnDfdDJZkndwi92SL32HeFZRSFaB9UslLqCHJ
-xrHty8OVYNEP8Ktw+N/LTX7s1vqr2b1/VPKl6Xn62dZ2JChzAgMBAAGjPzA9MB0G
-A1UdDgQWBBSgc0mZaNyFW2XjmygvV5+9M7wHSDALBgNVHQ8EBAMCAQYwDwYDVR0T
-AQH/BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAaECpqLvkT115swW1F7NgE+vG
-kl3g0dNq/vu+m22/xwVtWSDEHPC32oRYAmP6SBbvT6UL90qY8j+eG61Ha2POCEfr
-Uj94nK9NrvjVT8+amCoQQTlSxN3Zmw7vkwGusi7KaEIkQmywszo+zenaSMQVy+n5
-Bw+SUEmK3TGXX8npN6o7WWWXlDLJs58+OmJYxUmtYg5xpTKqL8aJdkNAExNnPaJU
-JRDL8Try2frbSVa7pv6nQTXD4IhhyYjH3zYQIphZ6rBK+1YWc26sTfcioU+tHXot
-RSflMMFe8toTyyVCUZVHA4xsIcx0Qu1T/zOLjw9XARYvz6buyXAiFL39vmwLAw==
------END CERTIFICATE-----
-
-# Security Communication RootCA2
------BEGIN CERTIFICATE-----
-MIIDdzCCAl+gAwIBAgIBADANBgkqhkiG9w0BAQsFADBdMQswCQYDVQQGEwJKUDEl
-MCMGA1UEChMcU0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEnMCUGA1UECxMe
-U2VjdXJpdHkgQ29tbXVuaWNhdGlvbiBSb290Q0EyMB4XDTA5MDUyOTA1MDAzOVoX
-DTI5MDUyOTA1MDAzOVowXTELMAkGA1UEBhMCSlAxJTAjBgNVBAoTHFNFQ09NIFRy
-dXN0IFN5c3RlbXMgQ08uLExURC4xJzAlBgNVBAsTHlNlY3VyaXR5IENvbW11bmlj
-YXRpb24gUm9vdENBMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANAV
-OVKxUrO6xVmCxF1SrjpDZYBLx/KWvNs2l9amZIyoXvDjChz335c9S672XewhtUGr
-zbl+dp+++T42NKA7wfYxEUV0kz1XgMX5iZnK5atq1LXaQZAQwdbWQonCv/Q4EpVM
-VAX3NuRFg3sUZdbcDE3R3n4MqzvEFb46VqZab3ZpUql6ucjrappdUtAtCms1FgkQ
-hNBqyjoGADdH5H5XTz+L62e4iKrFvlNVspHEfbmwhRkGeC7bYRr6hfVKkaHnFtWO
-ojnflLhwHyg/i/xAXmODPIMqGplrz95Zajv8bxbXH/1KEOtOghY6rCcMU/Gt1SSw
-awNQwS08Ft1ENCcadfsCAwEAAaNCMEAwHQYDVR0OBBYEFAqFqXdlBZh8QIH4D5cs
-OPEK7DzPMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3
-DQEBCwUAA4IBAQBMOqNErLlFsceTfsgLCkLfZOoc7llsCLqJX2rKSpWeeo8HxdpF
-coJxDjrSzG+ntKEju/Ykn8sX/oymzsLS28yN/HH8AynBbF0zX2S2ZTuJbxh2ePXc
-okgfGT+Ok+vx+hfuzU7jBBJV1uXk3fs+BXziHV7Gp7yXT2g69ekuCkO2r1dcYmh8
-t/2jioSgrGK+KwmHNPBqAbubKVY8/gA3zyNs8U6qtnRGEmyR7jTV7JqR50S+kDFy
-1UkC9gLl9B/rfNmWVan/7Ir5mUf/NVoCqgTLiluHcSmRvaS0eg29mvVXIwAHIRc/
-SjnRBUkLp7Y3gaVdjKozXoEofKd9J+sAro03
------END CERTIFICATE-----
-
-# Sonera Class 2 Root CA
------BEGIN CERTIFICATE-----
-MIIDIDCCAgigAwIBAgIBHTANBgkqhkiG9w0BAQUFADA5MQswCQYDVQQGEwJGSTEP
-MA0GA1UEChMGU29uZXJhMRkwFwYDVQQDExBTb25lcmEgQ2xhc3MyIENBMB4XDTAx
-MDQwNjA3Mjk0MFoXDTIxMDQwNjA3Mjk0MFowOTELMAkGA1UEBhMCRkkxDzANBgNV
-BAoTBlNvbmVyYTEZMBcGA1UEAxMQU29uZXJhIENsYXNzMiBDQTCCASIwDQYJKoZI
-hvcNAQEBBQADggEPADCCAQoCggEBAJAXSjWdyvANlsdE+hY3/Ei9vX+ALTU74W+o
-Z6m/AxxNjG8yR9VBaKQTBME1DJqEQ/xcHf+Js+gXGM2RX/uJ4+q/Tl18GybTdXnt
-5oTjV+WtKcT0OijnpXuENmmz/V52vaMtmdOQTiMofRhj8VQ7Jp12W5dCsv+u8E7s
-3TmVToMGf+dJQMjFAbJUWmYdPfz56TwKnoG4cPABi+QjVHzIrviQHgCWctRUz2Ej
-vOr7nQKV0ba5cTppCD8PtOFCx4j1P5iop7oc4HFx71hXgVB6XGt0Rg6DA5jDjqhu
-8nYybieDwnPz3BjotJPqdURrBGAgcVeHnfO+oJAjPYok4doh28MCAwEAAaMzMDEw
-DwYDVR0TAQH/BAUwAwEB/zARBgNVHQ4ECgQISqCqWITTXjwwCwYDVR0PBAQDAgEG
-MA0GCSqGSIb3DQEBBQUAA4IBAQBazof5FnIVV0sd2ZvnoiYw7JNn39Yt0jSv9zil
-zqsWuasvfDXLrNAPtEwr/IDva4yRXzZ299uzGxnq9LIR/WFxRL8oszodv7ND6J+/
-3DEIcbCdjdY0RzKQxmUk96BKfARzjzlvF4xytb1LyHr4e4PDKE6cCepnP7JnBBvD
-FNr450kkkdAdavphOe9r5yF1BgfYErQhIHBCcYHaPJo2vqZbDWpsmh+Re/n570K6
-Tk6ezAyNlNzZRZxe7EJQY670XcSxEtzKO6gunRRaBXW37Ndj4ro1tgQIkejanZz2
-ZrUYrAqmVCY0M9IbwdR/GjqOC6oybtv8TyWf2TLHllpwrN9M
------END CERTIFICATE-----
-
-# SSL.com EV Root Certification Authority ECC
------BEGIN CERTIFICATE-----
-MIIClDCCAhqgAwIBAgIILCmcWxbtBZUwCgYIKoZIzj0EAwIwfzELMAkGA1UEBhMC
-VVMxDjAMBgNVBAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQKDA9T
-U0wgQ29ycG9yYXRpb24xNDAyBgNVBAMMK1NTTC5jb20gRVYgUm9vdCBDZXJ0aWZp
-Y2F0aW9uIEF1dGhvcml0eSBFQ0MwHhcNMTYwMjEyMTgxNTIzWhcNNDEwMjEyMTgx
-NTIzWjB/MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxEDAOBgNVBAcMB0hv
-dXN0b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjE0MDIGA1UEAwwrU1NMLmNv
-bSBFViBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IEVDQzB2MBAGByqGSM49
-AgEGBSuBBAAiA2IABKoSR5CYG/vvw0AHgyBO8TCCogbR8pKGYfL2IWjKAMTH6kMA
-VIbc/R/fALhBYlzccBYy3h+Z1MzFB8gIH2EWB1E9fVwHU+M1OIzfzZ/ZLg1Kthku
-WnBaBu2+8KGwytAJKaNjMGEwHQYDVR0OBBYEFFvKXuXe0oGqzagtZFG22XKbl+ZP
-MA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUW8pe5d7SgarNqC1kUbbZcpuX
-5k8wDgYDVR0PAQH/BAQDAgGGMAoGCCqGSM49BAMCA2gAMGUCMQCK5kCJN+vp1RPZ
-ytRrJPOwPYdGWBrssd9v+1a6cGvHOMzosYxPD/fxZ3YOg9AeUY8CMD32IygmTMZg
-h5Mmm7I1HrrW9zzRHM76JTymGoEVW/MSD2zuZYrJh6j5B+BimoxcSg==
------END CERTIFICATE-----
-
-# SSL.com EV Root Certification Authority RSA R2
------BEGIN CERTIFICATE-----
-MIIF6zCCA9OgAwIBAgIIVrYpzTS8ePYwDQYJKoZIhvcNAQELBQAwgYIxCzAJBgNV
-BAYTAlVTMQ4wDAYDVQQIDAVUZXhhczEQMA4GA1UEBwwHSG91c3RvbjEYMBYGA1UE
-CgwPU1NMIENvcnBvcmF0aW9uMTcwNQYDVQQDDC5TU0wuY29tIEVWIFJvb3QgQ2Vy
-dGlmaWNhdGlvbiBBdXRob3JpdHkgUlNBIFIyMB4XDTE3MDUzMTE4MTQzN1oXDTQy
-MDUzMDE4MTQzN1owgYIxCzAJBgNVBAYTAlVTMQ4wDAYDVQQIDAVUZXhhczEQMA4G
-A1UEBwwHSG91c3RvbjEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9uMTcwNQYDVQQD
-DC5TU0wuY29tIEVWIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgUlNBIFIy
-MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAjzZlQOHWTcDXtOlG2mvq
-M0fNTPl9fb69LT3w23jhhqXZuglXaO1XPqDQCEGD5yhBJB/jchXQARr7XnAjssuf
-OePPxU7Gkm0mxnu7s9onnQqG6YE3Bf7wcXHswxzpY6IXFJ3vG2fThVUCAtZJycxa
-4bH3bzKfydQ7iEGonL3Lq9ttewkfokxykNorCPzPPFTOZw+oz12WGQvE43LrrdF9
-HSfvkusQv1vrO6/PgN3B0pYEW3p+pKk8OHakYo6gOV7qd89dAFmPZiw+B6KjBSYR
-aZfqhbcPlgtLyEDhULouisv3D5oi53+aNxPN8k0TayHRwMwi8qFG9kRpnMphNQcA
-b9ZhCBHqurj26bNg5U257J8UZslXWNvNh2n4ioYSA0e/ZhN2rHd9NCSFg83XqpyQ
-Gp8hLH94t2S42Oim9HizVcuE0jLEeK6jj2HdzghTreyI/BXkmg3mnxp3zkyPuBQV
-PWKchjgGAGYS5Fl2WlPAApiiECtoRHuOec4zSnaqW4EWG7WK2NAAe15itAnWhmMO
-pgWVSbooi4iTsjQc2KRVbrcc0N6ZVTsj9CLg+SlmJuwgUHfbSguPvuUCYHBBXtSu
-UDkiFCbLsjtzdFVHB3mBOagwE0TlBIqulhMlQg+5U8Sb/M3kHN48+qvWBkofZ6aY
-MBzdLNvcGJVXZsb/XItW9XcCAwEAAaNjMGEwDwYDVR0TAQH/BAUwAwEB/zAfBgNV
-HSMEGDAWgBT5YLvU49U09rj1BoAlp3PbRmmonjAdBgNVHQ4EFgQU+WC71OPVNPa4
-9QaAJadz20ZpqJ4wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4ICAQBW
-s47LCp1Jjr+kxJG7ZhcFUZh1++VQLHqe8RT6q9OKPv+RKY9ji9i0qVQBDb6Thi/5
-Sm3HXvVX+cpVHBK+Rw82xd9qt9t1wkclf7nxY/hoLVUE0fKNsKTPvDxeH3jnpaAg
-cLAExbf3cqfeIg29MyVGjGSSJuM+LmOW2puMPfgYCdcDzH2GguDKBAdRUNf/ktUM
-79qGn5nX67evaOI5JpS6aLe/g9Pqemc9YmeuJeVy6OLk7K4S9ksrPJ/psEDzOFSz
-/bdoyNrGj1E8svuR3Bznm53htw1yj+KkxKl4+esUrMZDBcJlOSgYAsOCsp0FvmXt
-ll9ldDz7CTUue5wT/RsPXcdtgTpWD8w74a8CLyKsRspGPKAcTNZEtF4uXBVmCeEm
-Kf7GUmG6sXP/wwyc5WxqlD8UykAWlYTzWamsX0xhk23RO8yilQwipmdnRC652dKK
-QbNmC1r7fSOl8hqw/96bg5Qu0T/fkreRrwU7ZcegbLHNYhLDkBvjJc40vG93drEQ
-w/cFGsDWr3RiSBd3kmmQYRzelYB0VI8YHMPzA9C/pEN1hlMYegouCRw2n5H9gooi
-S9EOUCXdywMMF8mDAAhONU2Ki+3wApRmLER/y5UnlhetCTCstnEXbosX9hwJ1C07
-mKVx01QT2WDz9UtmT/rx7iASjbSsV7FFY6GsdqnC+w==
------END CERTIFICATE-----
-
-# SSL.com Root Certification Authority ECC
------BEGIN CERTIFICATE-----
-MIICjTCCAhSgAwIBAgIIdebfy8FoW6gwCgYIKoZIzj0EAwIwfDELMAkGA1UEBhMC
-VVMxDjAMBgNVBAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQKDA9T
-U0wgQ29ycG9yYXRpb24xMTAvBgNVBAMMKFNTTC5jb20gUm9vdCBDZXJ0aWZpY2F0
-aW9uIEF1dGhvcml0eSBFQ0MwHhcNMTYwMjEyMTgxNDAzWhcNNDEwMjEyMTgxNDAz
-WjB8MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxEDAOBgNVBAcMB0hvdXN0
-b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjExMC8GA1UEAwwoU1NMLmNvbSBS
-b290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IEVDQzB2MBAGByqGSM49AgEGBSuB
-BAAiA2IABEVuqVDEpiM2nl8ojRfLliJkP9x6jh3MCLOicSS6jkm5BBtHllirLZXI
-7Z4INcgn64mMU1jrYor+8FsPazFSY0E7ic3s7LaNGdM0B9y7xgZ/wkWV7Mt/qCPg
-CemB+vNH06NjMGEwHQYDVR0OBBYEFILRhXMw5zUE044CkvvlpNHEIejNMA8GA1Ud
-EwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUgtGFczDnNQTTjgKS++Wk0cQh6M0wDgYD
-VR0PAQH/BAQDAgGGMAoGCCqGSM49BAMCA2cAMGQCMG/n61kRpGDPYbCWe+0F+S8T
-kdzt5fxQaxFGRrMcIQBiu77D5+jNB5n5DQtdcj7EqgIwH7y6C+IwJPt8bYBVCpk+
-gA0z5Wajs6O7pdWLjwkspl1+4vAHCGht0nxpbl/f5Wpl
------END CERTIFICATE-----
-
-# SSL.com Root Certification Authority RSA
------BEGIN CERTIFICATE-----
-MIIF3TCCA8WgAwIBAgIIeyyb0xaAMpkwDQYJKoZIhvcNAQELBQAwfDELMAkGA1UE
-BhMCVVMxDjAMBgNVBAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQK
-DA9TU0wgQ29ycG9yYXRpb24xMTAvBgNVBAMMKFNTTC5jb20gUm9vdCBDZXJ0aWZp
-Y2F0aW9uIEF1dGhvcml0eSBSU0EwHhcNMTYwMjEyMTczOTM5WhcNNDEwMjEyMTcz
-OTM5WjB8MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxEDAOBgNVBAcMB0hv
-dXN0b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjExMC8GA1UEAwwoU1NMLmNv
-bSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IFJTQTCCAiIwDQYJKoZIhvcN
-AQEBBQADggIPADCCAgoCggIBAPkP3aMrfcvQKv7sZ4Wm5y4bunfh4/WvpOz6Sl2R
-xFdHaxh3a3by/ZPkPQ/CFp4LZsNWlJ4Xg4XOVu/yFv0AYvUiCVToZRdOQbngT0aX
-qhvIuG5iXmmxX9sqAn78bMrzQdjt0Oj8P2FI7bADFB0QDksZ4LtO7IZl/zbzXmcC
-C52GVWH9ejjt/uIZALdvoVBidXQ8oPrIJZK0bnoix/geoeOy3ZExqysdBP+lSgQ3
-6YWkMyv94tZVNHwZpEpox7Ko07fKoZOI68GXvIz5HdkihCR0xwQ9aqkpk8zruFvh
-/l8lqjRYyMEjVJ0bmBHDOJx+PYZspQ9AhnwC9FwCTyjLrnGfDzrIM/4RJTXq/LrF
-YD3ZfBjVsqnTdXgDciLKOsMf7yzlLqn6niy2UUb9rwPW6mBo6oUWNmuF6R7As93E
-JNyAKoFBbZQ+yODJgUEAnl6/f8UImKIYLEJAs/lvOCdLToD0PYFH4Ih86hzOtXVc
-US4cK38acijnALXRdMbX5J+tB5O2UzU1/Dfkw/ZdFr4hc96SCvigY2q8lpJqPvi8
-ZVWb3vUNiSYE/CUapiVpy8JtynziWV+XrOvvLsi81xtZPCvM8hnIk2snYxnP/Okm
-+Mpxm3+T/jRnhE6Z6/yzeAkzcLpmpnbtG3PrGqUNxCITIJRWCk4sbE6x/c+cCbqi
-M+2HAgMBAAGjYzBhMB0GA1UdDgQWBBTdBAkHovV6fVJTEpKV7jiAJQ2mWTAPBgNV
-HRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFN0ECQei9Xp9UlMSkpXuOIAlDaZZMA4G
-A1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsFAAOCAgEAIBgRlCn7Jp0cHh5wYfGV
-cpNxJK1ok1iOMq8bs3AD/CUrdIWQPXhq9LmLpZc7tRiRux6n+UBbkflVma8eEdBc
-Hadm47GUBwwyOabqG7B52B2ccETjit3E+ZUfijhDPwGFpUenPUayvOUiaPd7nNgs
-PgohyC0zrL/FgZkxdMF1ccW+sfAjRfSda/wZY52jvATGGAslu1OJD7OAUN5F7kR/
-q5R4ZJjT9ijdh9hwZXT7DrkT66cPYakylszeu+1jTBi7qUD3oFRuIIhxdRjqerQ0
-cuAjJ3dctpDqhiVAq+8zD8ufgr6iIPv2tS0a5sKFsXQP+8hlAqRSAUfdSSLBv9jr
-a6x+3uxjMxW3IwiPxg+NQVrdjsW5j+VFP3jbutIbQLH+cU0/4IGiul607BXgk90I
-H37hVZkLId6Tngr75qNJvTYw/ud3sqB1l7UtgYgXZSD32pAAn8lSzDLKNXz1PQ/Y
-K9f1JmzJBjSWFupwWRoyeXkLtoh/D1JIPb9s2KJELtFOt3JY04kTlf5Eq/jXixtu
-nLwsoFvVagCvXzfh1foQC5ichucmj87w7G6KVwuA406ywKBjYZC6VWg3dGq2ktuf
-oYYitmUnDuy2n0Jg5GfCtdpBC8TTi2EbvPofkSvXRAdeuims2cXp71NIWuuA8ShY
-Ic2wBlX7Jz9TkHCpBB5XJ7k=
------END CERTIFICATE-----
-
-# Staat der Nederlanden EV Root CA
------BEGIN CERTIFICATE-----
-MIIFcDCCA1igAwIBAgIEAJiWjTANBgkqhkiG9w0BAQsFADBYMQswCQYDVQQGEwJO
-TDEeMBwGA1UECgwVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSkwJwYDVQQDDCBTdGFh
-dCBkZXIgTmVkZXJsYW5kZW4gRVYgUm9vdCBDQTAeFw0xMDEyMDgxMTE5MjlaFw0y
-MjEyMDgxMTEwMjhaMFgxCzAJBgNVBAYTAk5MMR4wHAYDVQQKDBVTdGFhdCBkZXIg
-TmVkZXJsYW5kZW4xKTAnBgNVBAMMIFN0YWF0IGRlciBOZWRlcmxhbmRlbiBFViBS
-b290IENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA48d+ifkkSzrS
-M4M1LGns3Amk41GoJSt5uAg94JG6hIXGhaTK5skuU6TJJB79VWZxXSzFYGgEt9nC
-UiY4iKTWO0Cmws0/zZiTs1QUWJZV1VD+hq2kY39ch/aO5ieSZxeSAgMs3NZmdO3d
-Z//BYY1jTw+bbRcwJu+r0h8QoPnFfxZpgQNH7R5ojXKhTbImxrpsX23Wr9GxE46p
-rfNeaXUmGD5BKyF/7otdBwadQ8QpCiv8Kj6GyzyDOvnJDdrFmeK8eEEzduG/L13l
-pJhQDBXd4Pqcfzho0LKmeqfRMb1+ilgnQ7O6M5HTp5gVXJrm0w912fxBmJc+qiXb
-j5IusHsMX/FjqTf5m3VpTCgmJdrV8hJwRVXj33NeN/UhbJCONVrJ0yPr08C+eKxC
-KFhmpUZtcALXEPlLVPxdhkqHz3/KRawRWrUgUY0viEeXOcDPusBCAUCZSCELa6fS
-/ZbV0b5GnUngC6agIk440ME8MLxwjyx1zNDFjFE7PZQIZCZhfbnDZY8UnCHQqv0X
-cgOPvZuM5l5Tnrmd74K74bzickFbIZTTRTeU0d8JOV3nI6qaHcptqAqGhYqCvkIH
-1vI4gnPah1vlPNOePqc7nvQDs/nxfRN0Av+7oeX6AHkcpmZBiFxgV6YuCcS6/ZrP
-px9Aw7vMWgpVSzs4dlG4Y4uElBbmVvMCAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB
-/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFP6rAJCYniT8qcwaivsnuL8wbqg7
-MA0GCSqGSIb3DQEBCwUAA4ICAQDPdyxuVr5Os7aEAJSrR8kN0nbHhp8dB9O2tLsI
-eK9p0gtJ3jPFrK3CiAJ9Brc1AsFgyb/E6JTe1NOpEyVa/m6irn0F3H3zbPB+po3u
-2dfOWBfoqSmuc0iH55vKbimhZF8ZE/euBhD/UcabTVUlT5OZEAFTdfETzsemQUHS
-v4ilf0X8rLiltTMMgsT7B/Zq5SWEXwbKwYY5EdtYzXc7LMJMD16a4/CrPmEbUCTC
-wPTxGfARKbalGAKb12NMcIxHowNDXLldRqANb/9Zjr7dn3LDWyvfjFvO5QxGbJKy
-CqNMVEIYFRIYvdr8unRu/8G2oGTYqV9Vrp9canaW2HNnh/tNf1zuacpzEPuKqf2e
-vTY4SUmH9A4U8OmHuD+nT3pajnnUk+S7aFKErGzp85hwVXIy+TSrK0m1zSBi5Dp6
-Z2Orltxtrpfs/J92VoguZs9btsmksNcFuuEnL5O7Jiqik7Ab846+HUCjuTaPPoIa
-Gl6I6lD4WeKDRikL40Rc4ZW2aZCaFG+XroHPaO+Zmr615+F/+PoTRxZMzG0IQOeL
-eG9QgkRQP2YGiqtDhFZKDyAthg710tvSeopLzaXoTvFeJiUBWSOgftL2fiFX1ye8
-FVdMpEbB4IMeDExNH08GGeL5qPQ6gqGyeUN51q1veieQA6TqJIc/2b3Z6fJfUEkc
-7uzXLg==
------END CERTIFICATE-----
-
-# Staat der Nederlanden Root CA - G2
------BEGIN CERTIFICATE-----
-MIIFyjCCA7KgAwIBAgIEAJiWjDANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJO
-TDEeMBwGA1UECgwVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSswKQYDVQQDDCJTdGFh
-dCBkZXIgTmVkZXJsYW5kZW4gUm9vdCBDQSAtIEcyMB4XDTA4MDMyNjExMTgxN1oX
-DTIwMDMyNTExMDMxMFowWjELMAkGA1UEBhMCTkwxHjAcBgNVBAoMFVN0YWF0IGRl
-ciBOZWRlcmxhbmRlbjErMCkGA1UEAwwiU3RhYXQgZGVyIE5lZGVybGFuZGVuIFJv
-b3QgQ0EgLSBHMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMVZ5291
-qj5LnLW4rJ4L5PnZyqtdj7U5EILXr1HgO+EASGrP2uEGQxGZqhQlEq0i6ABtQ8Sp
-uOUfiUtnvWFI7/3S4GCI5bkYYCjDdyutsDeqN95kWSpGV+RLufg3fNU254DBtvPU
-Z5uW6M7XxgpT0GtJlvOjCwV3SPcl5XCsMBQgJeN/dVrlSPhOewMHBPqCYYdu8DvE
-pMfQ9XQ+pV0aCPKbJdL2rAQmPlU6Yiile7Iwr/g3wtG61jj99O9JMDeZJiFIhQGp
-5Rbn3JBV3w/oOM2ZNyFPXfUib2rFEhZgF1XyZWampzCROME4HYYEhLoaJXhena/M
-UGDWE4dS7WMfbWV9whUYdMrhfmQpjHLYFhN9C0lK8SgbIHRrxT3dsKpICT0ugpTN
-GmXZK4iambwYfp/ufWZ8Pr2UuIHOzZgweMFvZ9C+X+Bo7d7iscksWXiSqt8rYGPy
-5V6548r6f1CGPqI0GAwJaCgRHOThuVw+R7oyPxjMW4T182t0xHJ04eOLoEq9jWYv
-6q012iDTiIJh8BIitrzQ1aTsr1SIJSQ8p22xcik/Plemf1WvbibG/ufMQFxRRIEK
-eN5KzlW/HdXZt1bv8Hb/C3m1r737qWmRRpdogBQ2HbN/uymYNqUg+oJgYjOk7Na6
-B6duxc8UpufWkjTYgfX8HV2qXB72o007uPc5AgMBAAGjgZcwgZQwDwYDVR0TAQH/
-BAUwAwEB/zBSBgNVHSAESzBJMEcGBFUdIAAwPzA9BggrBgEFBQcCARYxaHR0cDov
-L3d3dy5wa2lvdmVyaGVpZC5ubC9wb2xpY2llcy9yb290LXBvbGljeS1HMjAOBgNV
-HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJFoMocVHYnitfGsNig0jQt8YojrMA0GCSqG
-SIb3DQEBCwUAA4ICAQCoQUpnKpKBglBu4dfYszk78wIVCVBR7y29JHuIhjv5tLyS
-CZa59sCrI2AGeYwRTlHSeYAz+51IvuxBQ4EffkdAHOV6CMqqi3WtFMTC6GY8ggen
-5ieCWxjmD27ZUD6KQhgpxrRW/FYQoAUXvQwjf/ST7ZwaUb7dRUG/kSS0H4zpX897
-IZmflZ85OkYcbPnNe5yQzSipx6lVu6xiNGI1E0sUOlWDuYaNkqbG9AclVMwWVxJK
-gnjIFNkXgiYtXSAfea7+1HAWFpWD2DU5/1JddRwWxRNVz0fMdWVSSt7wsKfkCpYL
-+63C4iWEst3kvX5ZbJvw8NjnyvLplzh+ib7M+zkXYT9y2zqR2GUBGR2tUKRXCnxL
-vJxxcypFURmFzI79R6d0lR2o0a9OF7FpJsKqeFdbxU2n5Z4FF5TKsl+gSRiNNOkm
-bEgeqmiSBeGCc1qb3AdbCG19ndeNIdn8FCCqwkXfP+cAslHkwvgFuXkajDTznlvk
-N1trSt8sV4pAWja63XVECDdCcAz+3F4hoKOKwJCcaNpQ5kUQR3i2TtJlycM33+FC
-Y7BXN0Ute4qcvwXqZVUz9zkQxSgqIXobisQk+T8VyJoVIPVVYpbtbZNQvOSqeK3Z
-ywplh6ZmwcSBo3c6WB4L7oOLnR7SUqTMHW+wmG2UMbX4cQrcufx9MmDm66+KAQ==
------END CERTIFICATE-----
-
-# Staat der Nederlanden Root CA - G3
------BEGIN CERTIFICATE-----
-MIIFdDCCA1ygAwIBAgIEAJiiOTANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJO
-TDEeMBwGA1UECgwVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSswKQYDVQQDDCJTdGFh
-dCBkZXIgTmVkZXJsYW5kZW4gUm9vdCBDQSAtIEczMB4XDTEzMTExNDExMjg0MloX
-DTI4MTExMzIzMDAwMFowWjELMAkGA1UEBhMCTkwxHjAcBgNVBAoMFVN0YWF0IGRl
-ciBOZWRlcmxhbmRlbjErMCkGA1UEAwwiU3RhYXQgZGVyIE5lZGVybGFuZGVuIFJv
-b3QgQ0EgLSBHMzCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAL4yolQP
-cPssXFnrbMSkUeiFKrPMSjTysF/zDsccPVMeiAho2G89rcKezIJnByeHaHE6n3WW
-IkYFsO2tx1ueKt6c/DrGlaf1F2cY5y9JCAxcz+bMNO14+1Cx3Gsy8KL+tjzk7FqX
-xz8ecAgwoNzFs21v0IJyEavSgWhZghe3eJJg+szeP4TrjTgzkApyI/o1zCZxMdFy
-KJLZWyNtZrVtB0LrpjPOktvA9mxjeM3KTj215VKb8b475lRgsGYeCasH/lSJEULR
-9yS6YHgamPfJEf0WwTUaVHXvQ9Plrk7O53vDxk5hUUurmkVLoR9BvUhTFXFkC4az
-5S6+zqQbwSmEorXLCCN2QyIkHxcE1G6cxvx/K2Ya7Irl1s9N9WMJtxU51nus6+N8
-6U78dULI7ViVDAZCopz35HCz33JvWjdAidiFpNfxC95DGdRKWCyMijmev4SH8RY7
-Ngzp07TKbBlBUgmhHbBqv4LvcFEhMtwFdozL92TkA1CvjJFnq8Xy7ljY3r735zHP
-bMk7ccHViLVlvMDoFxcHErVc0qsgk7TmgoNwNsXNo42ti+yjwUOH5kPiNL6VizXt
-BznaqB16nzaeErAMZRKQFWDZJkBE41ZgpRDUajz9QdwOWke275dhdU/Z/seyHdTt
-XUmzqWrLZoQT1Vyg3N9udwbRcXXIV2+vD3dbAgMBAAGjQjBAMA8GA1UdEwEB/wQF
-MAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRUrfrHkleuyjWcLhL75Lpd
-INyUVzANBgkqhkiG9w0BAQsFAAOCAgEAMJmdBTLIXg47mAE6iqTnB/d6+Oea31BD
-U5cqPco8R5gu4RV78ZLzYdqQJRZlwJ9UXQ4DO1t3ApyEtg2YXzTdO2PCwyiBwpwp
-LiniyMMB8jPqKqrMCQj3ZWfGzd/TtiunvczRDnBfuCPRy5FOCvTIeuXZYzbB1N/8
-Ipf3YF3qKS9Ysr1YvY2WTxB1v0h7PVGHoTx0IsL8B3+A3MSs/mrBcDCw6Y5p4ixp
-gZQJut3+TcCDjJRYwEYgr5wfAvg1VUkvRtTA8KCWAg8zxXHzniN9lLf9OtMJgwYh
-/WA9rjLA0u6NpvDntIJ8CsxwyXmA+P5M9zWEGYox+wrZ13+b8KKaa8MFSu1BYBQw
-0aoRQm7TIwIEC8Zl3d1Sd9qBa7Ko+gE4uZbqKmxnl4mUnrzhVNXkanjvSr0rmj1A
-fsbAddJu+2gw7OyLnflJNZoaLNmzlTnVHpL3prllL+U9bTpITAjc5CgSKL59NVzq
-4BZ+Extq1z7XnvwtdbLBFNUjA9tbbws+eC8N3jONFrdI54OagQ97wUNNVQQXOEpR
-1VmiiXTTn74eS9fGbbeIJG9gkaSChVtWQbzQRKtqE77RLFi3EjNYsjdj3BP1lB0/
-QFH1T/U67cjF68IeHRaVesd+QnGTbksVtzDfqu1XhUisHWrdOWnk4Xl4vs4Fv6EM
-94B7IWcnMFk=
------END CERTIFICATE-----
-
-# Starfield Class 2 CA
------BEGIN CERTIFICATE-----
-MIIEDzCCAvegAwIBAgIBADANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJVUzEl
-MCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMp
-U3RhcmZpZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQw
-NjI5MTczOTE2WhcNMzQwNjI5MTczOTE2WjBoMQswCQYDVQQGEwJVUzElMCMGA1UE
-ChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMpU3RhcmZp
-ZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEgMA0GCSqGSIb3
-DQEBAQUAA4IBDQAwggEIAoIBAQC3Msj+6XGmBIWtDBFk385N78gDGIc/oav7PKaf
-8MOh2tTYbitTkPskpD6E8J7oX+zlJ0T1KKY/e97gKvDIr1MvnsoFAZMej2YcOadN
-+lq2cwQlZut3f+dZxkqZJRRU6ybH838Z1TBwj6+wRir/resp7defqgSHo9T5iaU0
-X9tDkYI22WY8sbi5gv2cOj4QyDvvBmVmepsZGD3/cVE8MC5fvj13c7JdBmzDI1aa
-K4UmkhynArPkPw2vCHmCuDY96pzTNbO8acr1zJ3o/WSNF4Azbl5KXZnJHoe0nRrA
-1W4TNSNe35tfPe/W93bC6j67eA0cQmdrBNj41tpvi/JEoAGrAgEDo4HFMIHCMB0G
-A1UdDgQWBBS/X7fRzt0fhvRbVazc1xDCDqmI5zCBkgYDVR0jBIGKMIGHgBS/X7fR
-zt0fhvRbVazc1xDCDqmI56FspGowaDELMAkGA1UEBhMCVVMxJTAjBgNVBAoTHFN0
-YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAsTKVN0YXJmaWVsZCBD
-bGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8w
-DQYJKoZIhvcNAQEFBQADggEBAAWdP4id0ckaVaGsafPzWdqbAYcaT1epoXkJKtv3
-L7IezMdeatiDh6GX70k1PncGQVhiv45YuApnP+yz3SFmH8lU+nLMPUxA2IGvd56D
-eruix/U0F47ZEUD0/CwqTRV/p2JdLiXTAAsgGh1o+Re49L2L7ShZ3U0WixeDyLJl
-xy16paq8U4Zt3VekyvggQQto8PT7dL5WXXp59fkdheMtlb71cZBDzI0fmgAKhynp
-VSJYACPq4xJDKVtHCN2MQWplBqjlIapBtJUhlbl90TSrE9atvNziPTnNvT51cKEY
-WQPJIrSPnNVeKtelttQKbfi3QBFGmh95DmK/D5fs4C8fF5Q=
------END CERTIFICATE-----
-
-# Starfield Root Certificate Authority - G2
------BEGIN CERTIFICATE-----
-MIID3TCCAsWgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMx
-EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT
-HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAMTKVN0YXJmaWVs
-ZCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAw
-MFoXDTM3MTIzMTIzNTk1OVowgY8xCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6
-b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFyZmllbGQgVGVj
-aG5vbG9naWVzLCBJbmMuMTIwMAYDVQQDEylTdGFyZmllbGQgUm9vdCBDZXJ0aWZp
-Y2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
-ggEBAL3twQP89o/8ArFvW59I2Z154qK3A2FWGMNHttfKPTUuiUP3oWmb3ooa/RMg
-nLRJdzIpVv257IzdIvpy3Cdhl+72WoTsbhm5iSzchFvVdPtrX8WJpRBSiUZV9Lh1
-HOZ/5FSuS/hVclcCGfgXcVnrHigHdMWdSL5stPSksPNkN3mSwOxGXn/hbVNMYq/N
-Hwtjuzqd+/x5AJhhdM8mgkBj87JyahkNmcrUDnXMN/uLicFZ8WJ/X7NfZTD4p7dN
-dloedl40wOiWVpmKs/B/pM293DIxfJHP4F8R+GuqSVzRmZTRouNjWwl2tVZi4Ut0
-HZbUJtQIBFnQmA4O5t78w+wfkPECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAO
-BgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFHwMMh+n2TB/xH1oo2Kooc6rB1snMA0G
-CSqGSIb3DQEBCwUAA4IBAQARWfolTwNvlJk7mh+ChTnUdgWUXuEok21iXQnCoKjU
-sHU48TRqneSfioYmUeYs0cYtbpUgSpIB7LiKZ3sx4mcujJUDJi5DnUox9g61DLu3
-4jd/IroAow57UvtruzvE03lRTs2Q9GcHGcg8RnoNAX3FWOdt5oUwF5okxBDgBPfg
-8n/Uqgr/Qh037ZTlZFkSIHc40zI+OIF1lnP6aI+xy84fxez6nH7PfrHxBy22/L/K
-pL/QlwVKvOoYKAKQvVR4CSFx09F9HdkWsKlhPdAKACL8x3vLCWRFCztAgfd9fDL1
-mMpYjn0q7pBZc2T5NnReJaH1ZgUufzkVqSr7UIuOhWn0
------END CERTIFICATE-----
-
-# Starfield Services Root Certificate Authority - G2
------BEGIN CERTIFICATE-----
-MIID7zCCAtegAwIBAgIBADANBgkqhkiG9w0BAQsFADCBmDELMAkGA1UEBhMCVVMx
-EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT
-HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xOzA5BgNVBAMTMlN0YXJmaWVs
-ZCBTZXJ2aWNlcyBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5
-MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgZgxCzAJBgNVBAYTAlVTMRAwDgYD
-VQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFy
-ZmllbGQgVGVjaG5vbG9naWVzLCBJbmMuMTswOQYDVQQDEzJTdGFyZmllbGQgU2Vy
-dmljZXMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZI
-hvcNAQEBBQADggEPADCCAQoCggEBANUMOsQq+U7i9b4Zl1+OiFOxHz/Lz58gE20p
-OsgPfTz3a3Y4Y9k2YKibXlwAgLIvWX/2h/klQ4bnaRtSmpDhcePYLQ1Ob/bISdm2
-8xpWriu2dBTrz/sm4xq6HZYuajtYlIlHVv8loJNwU4PahHQUw2eeBGg6345AWh1K
-Ts9DkTvnVtYAcMtS7nt9rjrnvDH5RfbCYM8TWQIrgMw0R9+53pBlbQLPLJGmpufe
-hRhJfGZOozptqbXuNC66DQO4M99H67FrjSXZm86B0UVGMpZwh94CDklDhbZsc7tk
-6mFBrMnUVN+HL8cisibMn1lUaJ/8viovxFUcdUBgF4UCVTmLfwUCAwEAAaNCMEAw
-DwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJxfAN+q
-AdcwKziIorhtSpzyEZGDMA0GCSqGSIb3DQEBCwUAA4IBAQBLNqaEd2ndOxmfZyMI
-bw5hyf2E3F/YNoHN2BtBLZ9g3ccaaNnRbobhiCPPE95Dz+I0swSdHynVv/heyNXB
-ve6SbzJ08pGCL72CQnqtKrcgfU28elUSwhXqvfdqlS5sdJ/PHLTyxQGjhdByPq1z
-qwubdQxtRbeOlKyWN7Wg0I8VRw7j6IPdj/3vQQF3zCepYoUz8jcI73HPdwbeyBkd
-iEDPfUYd/x7H4c7/I9vG+o1VTqkC50cRRj70/b17KSa7qWFiNyi2LSr2EIZkyXCn
-0q23KXB56jzaYyWf/Wi3MOxw+3WKt21gZ7IeyLnp2KhvAotnDU0mV3HaIPzBSlCN
-sSi6
------END CERTIFICATE-----
-
-# SwissSign Gold CA - G2
------BEGIN CERTIFICATE-----
-MIIFujCCA6KgAwIBAgIJALtAHEP1Xk+wMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV
-BAYTAkNIMRUwEwYDVQQKEwxTd2lzc1NpZ24gQUcxHzAdBgNVBAMTFlN3aXNzU2ln
-biBHb2xkIENBIC0gRzIwHhcNMDYxMDI1MDgzMDM1WhcNMzYxMDI1MDgzMDM1WjBF
-MQswCQYDVQQGEwJDSDEVMBMGA1UEChMMU3dpc3NTaWduIEFHMR8wHQYDVQQDExZT
-d2lzc1NpZ24gR29sZCBDQSAtIEcyMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC
-CgKCAgEAr+TufoskDhJuqVAtFkQ7kpJcyrhdhJJCEyq8ZVeCQD5XJM1QiyUqt2/8
-76LQwB8CJEoTlo8jE+YoWACjR8cGp4QjK7u9lit/VcyLwVcfDmJlD909Vopz2q5+
-bbqBHH5CjCA12UNNhPqE21Is8w4ndwtrvxEvcnifLtg+5hg3Wipy+dpikJKVyh+c
-6bM8K8vzARO/Ws/BtQpgvd21mWRTuKCWs2/iJneRjOBiEAKfNA+k1ZIzUd6+jbqE
-emA8atufK+ze3gE/bk3lUIbLtK/tREDFylqM2tIrfKjuvqblCqoOpd8FUrdVxyJd
-MmqXl2MT28nbeTZ7hTpKxVKJ+STnnXepgv9VHKVxaSvRAiTysybUa9oEVeXBCsdt
-MDeQKuSeFDNeFhdVxVu1yzSJkvGdJo+hB9TGsnhQ2wwMC3wLjEHXuendjIj3o02y
-MszYF9rNt85mndT9Xv+9lz4pded+p2JYryU0pUHHPbwNUMoDAw8IWh+Vc3hiv69y
-FGkOpeUDDniOJihC8AcLYiAQZzlG+qkDzAQ4embvIIO1jEpWjpEA/I5cgt6IoMPi
-aG59je883WX0XaxR7ySArqpWl2/5rX3aYT+YdzylkbYcjCbaZaIJbcHiVOO5ykxM
-gI93e2CaHt+28kgeDrpOVG2Y4OGiGqJ3UM/EY5LsRxmd6+ZrzsECAwEAAaOBrDCB
-qTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUWyV7
-lqRlUX64OfPAeGZe6Drn8O4wHwYDVR0jBBgwFoAUWyV7lqRlUX64OfPAeGZe6Drn
-8O4wRgYDVR0gBD8wPTA7BglghXQBWQECAQEwLjAsBggrBgEFBQcCARYgaHR0cDov
-L3JlcG9zaXRvcnkuc3dpc3NzaWduLmNvbS8wDQYJKoZIhvcNAQEFBQADggIBACe6
-45R88a7A3hfm5djV9VSwg/S7zV4Fe0+fdWavPOhWfvxyeDgD2StiGwC5+OlgzczO
-UYrHUDFu4Up+GC9pWbY9ZIEr44OE5iKHjn3g7gKZYbge9LgriBIWhMIxkziWMaa5
-O1M/wySTVltpkuzFwbs4AOPsF6m43Md8AYOfMke6UiI0HTJ6CVanfCU2qT1L2sCC
-bwq7EsiHSycR+R4tx5M/nttfJmtS2S6K8RTGRI0Vqbe/vd6mGu6uLftIdxf+u+yv
-GPUqUfA5hJeVbG4bwyvEdGB5JbAKJ9/fXtI5z0V9QkvfsywexcZdylU6oJxpmo/a
-77KwPJ+HbBIrZXAVUjEaJM9vMSNQH4xPjyPDdEFjHFWoFN0+4FFQz/EbMFYOkrCC
-hdiDyyJkvC24JdVUorgG6q2SpCSgwYa1ShNqR88uC1aVVMvOmttqtKay20EIhid3
-92qgQmwLOM7XdVAyksLfKzAiSNDVQTglXaTpXZ/GlHXQRf0wl0OPkKsKx4ZzYEpp
-Ld6leNcG2mqeSz53OiATIgHQv2ieY2BrNU0LbbqhPcCT4H8js1WtciVORvnSFu+w
-ZMEBnunKoGqYDs/YYPIvSbjkQuE4NRb0yG5P94FW6LqjviOvrv1vA+ACOzB2+htt
-Qc8Bsem4yWb02ybzOqR08kkkW8mw0FfB+j564ZfJ
------END CERTIFICATE-----
-
-# SwissSign Silver CA - G2
------BEGIN CERTIFICATE-----
-MIIFvTCCA6WgAwIBAgIITxvUL1S7L0swDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UE
-BhMCQ0gxFTATBgNVBAoTDFN3aXNzU2lnbiBBRzEhMB8GA1UEAxMYU3dpc3NTaWdu
-IFNpbHZlciBDQSAtIEcyMB4XDTA2MTAyNTA4MzI0NloXDTM2MTAyNTA4MzI0Nlow
-RzELMAkGA1UEBhMCQ0gxFTATBgNVBAoTDFN3aXNzU2lnbiBBRzEhMB8GA1UEAxMY
-U3dpc3NTaWduIFNpbHZlciBDQSAtIEcyMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A
-MIICCgKCAgEAxPGHf9N4Mfc4yfjDmUO8x/e8N+dOcbpLj6VzHVxumK4DV644N0Mv
-Fz0fyM5oEMF4rhkDKxD6LHmD9ui5aLlV8gREpzn5/ASLHvGiTSf5YXu6t+WiE7br
-YT7QbNHm+/pe7R20nqA1W6GSy/BJkv6FCgU+5tkL4k+73JU3/JHpMjUi0R86TieF
-nbAVlDLaYQ1HTWBCrpJH6INaUFjpiou5XaHc3ZlKHzZnu0jkg7Y360g6rw9njxcH
-6ATK72oxh9TAtvmUcXtnZLi2kUpCe2UuMGoM9ZDulebyzYLs2aFK7PayS+VFheZt
-eJMELpyCbTapxDFkH4aDCyr0NQp4yVXPQbBH6TCfmb5hqAaEuSh6XzjZG6k4sIN/
-c8HDO0gqgg8hm7jMqDXDhBuDsz6+pJVpATqJAHgE2cn0mRmrVn5bi4Y5FZGkECwJ
-MoBgs5PAKrYYC51+jUnyEEp/+dVGLxmSo5mnJqy7jDzmDrxHB9xzUfFwZC8I+bRH
-HTBsROopN4WSaGa8gzj+ezku01DwH/teYLappvonQfGbGHLy9YR0SslnxFSuSGTf
-jNFusB3hB48IHpmccelM2KX3RxIfdNFRnobzwqIjQAtz20um53MGjMGg6cFZrEb6
-5i/4z3GcRm25xBWNOHkDRUjvxF3XCO6HOSKGsg0PWEP3calILv3q1h8CAwEAAaOB
-rDCBqTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU
-F6DNweRBtjpbO8tFnb0cwpj6hlgwHwYDVR0jBBgwFoAUF6DNweRBtjpbO8tFnb0c
-wpj6hlgwRgYDVR0gBD8wPTA7BglghXQBWQEDAQEwLjAsBggrBgEFBQcCARYgaHR0
-cDovL3JlcG9zaXRvcnkuc3dpc3NzaWduLmNvbS8wDQYJKoZIhvcNAQEFBQADggIB
-AHPGgeAn0i0P4JUw4ppBf1AsX19iYamGamkYDHRJ1l2E6kFSGG9YrVBWIGrGvShp
-WJHckRE1qTodvBqlYJ7YH39FkWnZfrt4csEGDyrOj4VwYaygzQu4OSlWhDJOhrs9
-xCrZ1x9y7v5RoSJBsXECYxqCsGKrXlcSH9/L3XWgwF15kIwb4FDm3jH+mHtwX6WQ
-2K34ArZv02DdQEsixT2tOnqfGhpHkXkzuoLcMmkDlm4fS/Bx/uNncqCxv1yL5PqZ
-IseEuRuNI5c/7SXgz2W79WEE790eslpBIlqhn10s6FvJbakMDHiqYMZWjwFaDGi8
-aRl5xB9+lwW/xekkUV7U1UtT7dkjWjYDZaPBA61BMPNGG4WQr2W11bHkFlt4dR2X
-em1ZqSqPe97Dh4kQmUlzeMg9vVE1dCrV8X5pGyq7O70luJpaPXJhkGaH7gzWTdQR
-dAtq/gsD/KNVV4n+SsuuWxcFyPKNIzFTONItaj+CuY0IavdeQXRuwxF+B6wpYJE/
-OMpXEA29MC/HpeZBoNquBYeaoKRlbEwJDIm6uNO5wJOKMPqN5ZprFQFOZ6raYlY+
-hAhm0sQ2fac+EPyI4NSA5QC9qvNOBqN6avlicuMJT+ubDgEj8Z+7fNzcbBGXJbLy
-tGMU0gYqZ4yD9c7qB9iaah7s5Aq7KkzrCWA5zspi2C5u
------END CERTIFICATE-----
-
-# SZAFIR ROOT CA2
------BEGIN CERTIFICATE-----
-MIIDcjCCAlqgAwIBAgIUPopdB+xV0jLVt+O2XwHrLdzk1uQwDQYJKoZIhvcNAQEL
-BQAwUTELMAkGA1UEBhMCUEwxKDAmBgNVBAoMH0tyYWpvd2EgSXpiYSBSb3psaWN6
-ZW5pb3dhIFMuQS4xGDAWBgNVBAMMD1NaQUZJUiBST09UIENBMjAeFw0xNTEwMTkw
-NzQzMzBaFw0zNTEwMTkwNzQzMzBaMFExCzAJBgNVBAYTAlBMMSgwJgYDVQQKDB9L
-cmFqb3dhIEl6YmEgUm96bGljemVuaW93YSBTLkEuMRgwFgYDVQQDDA9TWkFGSVIg
-Uk9PVCBDQTIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC3vD5QqEvN
-QLXOYeeWyrSh2gwisPq1e3YAd4wLz32ohswmUeQgPYUM1ljj5/QqGJ3a0a4m7utT
-3PSQ1hNKDJA8w/Ta0o4NkjrcsbH/ON7Dui1fgLkCvUqdGw+0w8LBZwPd3BucPbOw
-3gAeqDRHu5rr/gsUvTaE2g0gv/pby6kWIK05YO4vdbbnl5z5Pv1+TW9NL++IDWr6
-3fE9biCloBK0TXC5ztdyO4mTp4CEHCdJckm1/zuVnsHMyAHs6A6KCpbns6aH5db5
-BSsNl0BwPLqsdVqc1U2dAgrSS5tmS0YHF2Wtn2yIANwiieDhZNRnvDF5YTy7ykHN
-XGoAyDw4jlivAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQD
-AgEGMB0GA1UdDgQWBBQuFqlKGLXLzPVvUPMjX/hd56zwyDANBgkqhkiG9w0BAQsF
-AAOCAQEAtXP4A9xZWx126aMqe5Aosk3AM0+qmrHUuOQn/6mWmc5G4G18TKI4pAZw
-8PRBEew/R40/cof5O/2kbytTAOD/OblqBw7rHRz2onKQy4I9EYKL0rufKq8h5mOG
-nXkZ7/e7DDWQw4rtTw/1zBLZpD67oPwglV9PJi8RI4NOdQcPv5vRtB3pEAT+ymCP
-oky4rc/hkA/NrgrHXXu3UNLUYfrVFdvXn4dRVOul4+vJhaAlIDf7js4MNIThPIGy
-d05DpYhfhmehPea0XGG2Ptv+tyjFogeutcrKjSoS75ftwjCkySp6+/NNIxuZMzSg
-LvWpCz/UXeHPhJ/iGcJfitYgHuNztw==
------END CERTIFICATE-----
-
-# Taiwan GRCA
------BEGIN CERTIFICATE-----
-MIIFcjCCA1qgAwIBAgIQH51ZWtcvwgZEpYAIaeNe9jANBgkqhkiG9w0BAQUFADA/
-MQswCQYDVQQGEwJUVzEwMC4GA1UECgwnR292ZXJubWVudCBSb290IENlcnRpZmlj
-YXRpb24gQXV0aG9yaXR5MB4XDTAyMTIwNTEzMjMzM1oXDTMyMTIwNTEzMjMzM1ow
-PzELMAkGA1UEBhMCVFcxMDAuBgNVBAoMJ0dvdmVybm1lbnQgUm9vdCBDZXJ0aWZp
-Y2F0aW9uIEF1dGhvcml0eTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB
-AJoluOzMonWoe/fOW1mKydGGEghU7Jzy50b2iPN86aXfTEc2pBsBHH8eV4qNw8XR
-IePaJD9IK/ufLqGU5ywck9G/GwGHU5nOp/UKIXZ3/6m3xnOUT0b3EEk3+qhZSV1q
-gQdW8or5BtD3cCJNtLdBuTK4sfCxw5w/cP1T3YGq2GN49thTbqGsaoQkclSGxtKy
-yhwOeYHWtXBiCAEuTk8O1RGvqa/lmr/czIdtJuTJV6L7lvnM4T9TjGxMfptTCAts
-F/tnyMKtsc2AtJfcdgEWFelq16TheEfOhtX7MfP6Mb40qij7cEwdScevLJ1tZqa2
-jWR+tSBqnTuBto9AAGdLiYa4zGX+FVPpBMHWXx1E1wovJ5pGfaENda1UhhXcSTvx
-ls4Pm6Dso3pdvtUqdULle96ltqqvKKyskKw4t9VoNSZ63Pc78/1Fm9G7Q3hub/FC
-VGqY8A2tl+lSXunVanLeavcbYBT0peS2cWeqH+riTcFCQP5nRhc4L0c/cZyu5SHK
-YS1tB6iEfC3uUSXxY5Ce/eFXiGvviiNtsea9P63RPZYLhY3Naye7twWb7LuRqQoH
-EgKXTiCQ8P8NHuJBO9NAOueNXdpm5AKwB1KYXA6OM5zCppX7VRluTI6uSw+9wThN
-Xo+EHWbNxWCWtFJaBYmOlXqYwZE8lSOyDvR5tMl8wUohAgMBAAGjajBoMB0GA1Ud
-DgQWBBTMzO/MKWCkO7GStjz6MmKPrCUVOzAMBgNVHRMEBTADAQH/MDkGBGcqBwAE
-MTAvMC0CAQAwCQYFKw4DAhoFADAHBgVnKgMAAAQUA5vwIhP/lSg209yewDL7MTqK
-UWUwDQYJKoZIhvcNAQEFBQADggIBAECASvomyc5eMN1PhnR2WPWus4MzeKR6dBcZ
-TulStbngCnRiqmjKeKBMmo4sIy7VahIkv9Ro04rQ2JyftB8M3jh+Vzj8jeJPXgyf
-qzvS/3WXy6TjZwj/5cAWtUgBfen5Cv8b5Wppv3ghqMKnI6mGq3ZW6A4M9hPdKmaK
-ZEk9GhiHkASfQlK3T8v+R0F2Ne//AHY2RTKbxkaFXeIksB7jSJaYV0eUVXoPQbFE
-JPPB/hprv4j9wabak2BegUqZIJxIZhm1AHlUD7gsL0u8qV1bYH+Mh6XgUmMqvtg7
-hUAV/h62ZT/FS9p+tXo1KaMuephgIqP0fSdOLeq0dDzpD6QzDxARvBMB1uUO07+1
-EqLhRSPAzAhuYbeJq4PjJB7mXQfnHyA+z2fI56wwbSdLaG5LKlwCCDTb+HbkZ6Mm
-nD+iMsJKxYEYMRBWqoTvLQr/uB930r+lWKBi5NdLkXWNiYCYfm3LU05er/ayl4WX
-udpVBrkk7tfGOB5jGxI7leFYrPLfhNVfmS8NVVvmONsuP3LpSIXLuykTjx44Vbnz
-ssQwmSNOXfJIoRIM3BKQCZBUkQM8R+XVyWXgt0t97EfTsws+rZ7QdAAO671RrcDe
-LMDDav7v3Aun+kbfYNucpllQdSNpc5Oy+fwC00fmcc4QAu4njIT/rEUNE1yDMuAl
-pYYsfPQS
------END CERTIFICATE-----
-
-# TeliaSonera Root CA v1
------BEGIN CERTIFICATE-----
-MIIFODCCAyCgAwIBAgIRAJW+FqD3LkbxezmCcvqLzZYwDQYJKoZIhvcNAQEFBQAw
-NzEUMBIGA1UECgwLVGVsaWFTb25lcmExHzAdBgNVBAMMFlRlbGlhU29uZXJhIFJv
-b3QgQ0EgdjEwHhcNMDcxMDE4MTIwMDUwWhcNMzIxMDE4MTIwMDUwWjA3MRQwEgYD
-VQQKDAtUZWxpYVNvbmVyYTEfMB0GA1UEAwwWVGVsaWFTb25lcmEgUm9vdCBDQSB2
-MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMK+6yfwIaPzaSZVfp3F
-VRaRXP3vIb9TgHot0pGMYzHw7CTww6XScnwQbfQ3t+XmfHnqjLWCi65ItqwA3GV1
-7CpNX8GH9SBlK4GoRz6JI5UwFpB/6FcHSOcZrr9FZ7E3GwYq/t75rH2D+1665I+X
-Z75Ljo1kB1c4VWk0Nj0TSO9P4tNmHqTPGrdeNjPUtAa9GAH9d4RQAEX1jF3oI7x+
-/jXh7VB7qTCNGdMJjmhnXb88lxhTuylixcpecsHHltTbLaC0H2kD7OriUPEMPPCs
-81Mt8Bz17Ww5OXOAFshSsCPN4D7c3TxHoLs1iuKYaIu+5b9y7tL6pe0S7fyYGKkm
-dtwoSxAgHNN/Fnct7W+A90m7UwW7XWjH1Mh1Fj+JWov3F0fUTPHSiXk+TT2YqGHe
-Oh7S+F4D4MHJHIzTjU3TlTazN19jY5szFPAtJmtTfImMMsJu7D0hADnJoWjiUIMu
-sDor8zagrC/kb2HCUQk5PotTubtn2txTuXZZNp1D5SDgPTJghSJRt8czu90VL6R4
-pgd7gUY2BIbdeTXHlSw7sKMXNeVzH7RcWe/a6hBle3rQf5+ztCo3O3CLm1u5K7fs
-slESl1MpWtTwEhDcTwK7EpIvYtQ/aUN8Ddb8WHUBiJ1YFkveupD/RwGJBmr2X7KQ
-arMCpgKIv7NHfirZ1fpoeDVNAgMBAAGjPzA9MA8GA1UdEwEB/wQFMAMBAf8wCwYD
-VR0PBAQDAgEGMB0GA1UdDgQWBBTwj1k4ALP1j5qWDNXr+nuqF+gTEjANBgkqhkiG
-9w0BAQUFAAOCAgEAvuRcYk4k9AwI//DTDGjkk0kiP0Qnb7tt3oNmzqjMDfz1mgbl
-dxSR651Be5kqhOX//CHBXfDkH1e3damhXwIm/9fH907eT/j3HEbAek9ALCI18Bmx
-0GtnLLCo4MBANzX2hFxc469CeP6nyQ1Q6g2EdvZR74NTxnr/DlZJLo961gzmJ1Tj
-TQpgcmLNkQfWpb/ImWvtxBnmq0wROMVvMeJuScg/doAmAyYp4Db29iBT4xdwNBed
-Y2gea+zDTYa4EzAvXUYNR0PVG6pZDrlcjQZIrXSHX8f8MVRBE+LHIQ6e4B4N4cB7
-Q4WQxYpYxmUKeFfyxiMPAdkgS94P+5KFdSpcc41teyWRyu5FrgZLAMzTsVlQ2jqI
-OylDRl6XK1TOU2+NSueW+r9xDkKLfP0ooNBIytrEgUy7onOTJsjrDNYmiLbAJM+7
-vVvrdX3pCI6GMyx5dwlppYn8s3CQh3aP0yK7Qs69cwsgJirQmz1wHiRszYd2qReW
-t88NkvuOGKmYSdGe/mBEciG5Ge3C9THxOUiIkCR1VBatzvT4aRRkOfujuLpwQMcn
-HL/EVlP6Y2XQ8xwOFvVrhlhNGNTkDY6lnVuR3HYkUD/GKvvZt5y11ubQ2egZixVx
-SK236thZiNSQvxaz2emsWWFUyBy6ysHK4bkgTI86k4mloMy/0/Z1pHWWbVY=
------END CERTIFICATE-----
-
-# thawte Primary Root CA
------BEGIN CERTIFICATE-----
-MIIEIDCCAwigAwIBAgIQNE7VVyDV7exJ9C/ON9srbTANBgkqhkiG9w0BAQUFADCB
-qTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMf
-Q2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIw
-MDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxHzAdBgNV
-BAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwHhcNMDYxMTE3MDAwMDAwWhcNMzYw
-NzE2MjM1OTU5WjCBqTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5j
-LjEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYG
-A1UECxMvKGMpIDIwMDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNl
-IG9ubHkxHzAdBgNVBAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwggEiMA0GCSqG
-SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCsoPD7gFnUnMekz52hWXMJEEUMDSxuaPFs
-W0hoSVk3/AszGcJ3f8wQLZU0HObrTQmnHNK4yZc2AreJ1CRfBsDMRJSUjQJib+ta
-3RGNKJpchJAQeg29dGYvajig4tVUROsdB58Hum/u6f1OCyn1PoSgAfGcq/gcfomk
-6KHYcWUNo1F77rzSImANuVud37r8UVsLr5iy6S7pBOhih94ryNdOwUxkHt3Ph1i6
-Sk/KaAcdHJ1KxtUvkcx8cXIcxcBn6zL9yZJclNqFwJu/U30rCfSMnZEfl2pSy94J
-NqR32HuHUETVPm4pafs5SSYeCaWAe0At6+gnhcn+Yf1+5nyXHdWdAgMBAAGjQjBA
-MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBR7W0XP
-r87Lev0xkhpqtvNG61dIUDANBgkqhkiG9w0BAQUFAAOCAQEAeRHAS7ORtvzw6WfU
-DW5FvlXok9LOAz/t2iWwHVfLHjp2oEzsUHboZHIMpKnxuIvW1oeEuzLlQRHAd9mz
-YJ3rG9XRbkREqaYB7FViHXe4XI5ISXycO1cRrK1zN44veFyQaEfZYGDm/Ac9IiAX
-xPcW6cTYcvnIc3zfFi8VqT79aie2oetaupgf1eNNZAqdE8hhuvU5HIe6uL17In/2
-/qxAeeWsEG89jxt5dovEN7MhGITlNgDrYyCZuen+MwS7QcjBAvlEYyCegc5C09Y/
-LHbTY5xZ3Y+m4Q6gLkH3LpVHz7z9M/P2C2F+fpErgUfCJzDupxBdN49cOSvkBPB7
-jVaMaA==
------END CERTIFICATE-----
-
-# thawte Primary Root CA - G2
------BEGIN CERTIFICATE-----
-MIICiDCCAg2gAwIBAgIQNfwmXNmET8k9Jj1Xm67XVjAKBggqhkjOPQQDAzCBhDEL
-MAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjE4MDYGA1UECxMvKGMp
-IDIwMDcgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxJDAi
-BgNVBAMTG3RoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EgLSBHMjAeFw0wNzExMDUwMDAw
-MDBaFw0zODAxMTgyMzU5NTlaMIGEMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMdGhh
-d3RlLCBJbmMuMTgwNgYDVQQLEy8oYykgMjAwNyB0aGF3dGUsIEluYy4gLSBGb3Ig
-YXV0aG9yaXplZCB1c2Ugb25seTEkMCIGA1UEAxMbdGhhd3RlIFByaW1hcnkgUm9v
-dCBDQSAtIEcyMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEotWcgnuVnfFSeIf+iha/
-BebfowJPDQfGAFG6DAJSLSKkQjnE/o/qycG+1E3/n3qe4rF8mq2nhglzh9HnmuN6
-papu+7qzcMBniKI11KOasf2twu8x+qi58/sIxpHR+ymVo0IwQDAPBgNVHRMBAf8E
-BTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUmtgAMADna3+FGO6Lts6K
-DPgR4bswCgYIKoZIzj0EAwMDaQAwZgIxAN344FdHW6fmCsO99YCKlzUNG4k8VIZ3
-KMqh9HneteY4sPBlcIx/AlTCv//YoT7ZzwIxAMSNlPzcU9LcnXgWHxUzI1NS41ox
-XZ3Krr0TKUQNJ1uo52icEvdYPy5yAlejj6EULg==
------END CERTIFICATE-----
-
-# thawte Primary Root CA - G3
------BEGIN CERTIFICATE-----
-MIIEKjCCAxKgAwIBAgIQYAGXt0an6rS0mtZLL/eQ+zANBgkqhkiG9w0BAQsFADCB
-rjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMf
-Q2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIw
-MDggdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxJDAiBgNV
-BAMTG3RoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EgLSBHMzAeFw0wODA0MDIwMDAwMDBa
-Fw0zNzEyMDEyMzU5NTlaMIGuMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMdGhhd3Rl
-LCBJbmMuMSgwJgYDVQQLEx9DZXJ0aWZpY2F0aW9uIFNlcnZpY2VzIERpdmlzaW9u
-MTgwNgYDVQQLEy8oYykgMjAwOCB0aGF3dGUsIEluYy4gLSBGb3IgYXV0aG9yaXpl
-ZCB1c2Ugb25seTEkMCIGA1UEAxMbdGhhd3RlIFByaW1hcnkgUm9vdCBDQSAtIEcz
-MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsr8nLPvb2FvdeHsbnndm
-gcs+vHyu86YnmjSjaDFxODNi5PNxZnmxqWWjpYvVj2AtP0LMqmsywCPLLEHd5N/8
-YZzic7IilRFDGF/Eth9XbAoFWCLINkw6fKXRz4aviKdEAhN0cXMKQlkC+BsUa0Lf
-b1+6a4KinVvnSr0eAXLbS3ToO39/fR8EtCab4LRarEc9VbjXsCZSKAExQGbY2SS9
-9irY7CFJXJv2eul/VTV+lmuNk5Mny5K76qxAwJ/C+IDPXfRa3M50hqY+bAtTyr2S
-zhkGcuYMXDhpxwTWvGzOW/b3aJzcJRVIiKHpqfiYnODz1TEoYRFsZ5aNOZnLwkUk
-OQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNV
-HQ4EFgQUrWyqlGCc7eT/+j4KdCtjA/e2Wb8wDQYJKoZIhvcNAQELBQADggEBABpA
-2JVlrAmSicY59BDlqQ5mU1143vokkbvnRFHfxhY0Cu9qRFHqKweKA3rD6z8KLFIW
-oCtDuSWQP3CpMyVtRRooOyfPqsMpQhvfO0zAMzRbQYi/aytlryjvsvXDqmbOe1bu
-t8jLZ8HJnBoYuMTDSQPxYA5QzUbF83d597YV4Djbxy8ooAw/dyZ02SUS2jHaGh7c
-KUGRIjxpp7sC8rZcJwOJ9Abqm+RyguOhCcHpABnTPtRwa7pxpqpYrvS76Wy274fM
-m7v/OeZWYdMKp8RcTGB7BXcmer/YB1IsYvdwY9k5vG8cwnncdimvzsUsZAReiDZu
-MdRAGmI0Nj81Aa6sY6A=
------END CERTIFICATE-----
-
-# TrustCor ECA-1
------BEGIN CERTIFICATE-----
-MIIEIDCCAwigAwIBAgIJAISCLF8cYtBAMA0GCSqGSIb3DQEBCwUAMIGcMQswCQYD
-VQQGEwJQQTEPMA0GA1UECAwGUGFuYW1hMRQwEgYDVQQHDAtQYW5hbWEgQ2l0eTEk
-MCIGA1UECgwbVHJ1c3RDb3IgU3lzdGVtcyBTLiBkZSBSLkwuMScwJQYDVQQLDB5U
-cnVzdENvciBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxFzAVBgNVBAMMDlRydXN0Q29y
-IEVDQS0xMB4XDTE2MDIwNDEyMzIzM1oXDTI5MTIzMTE3MjgwN1owgZwxCzAJBgNV
-BAYTAlBBMQ8wDQYDVQQIDAZQYW5hbWExFDASBgNVBAcMC1BhbmFtYSBDaXR5MSQw
-IgYDVQQKDBtUcnVzdENvciBTeXN0ZW1zIFMuIGRlIFIuTC4xJzAlBgNVBAsMHlRy
-dXN0Q29yIENlcnRpZmljYXRlIEF1dGhvcml0eTEXMBUGA1UEAwwOVHJ1c3RDb3Ig
-RUNBLTEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDPj+ARtZ+odnbb
-3w9U73NjKYKtR8aja+3+XzP4Q1HpGjORMRegdMTUpwHmspI+ap3tDvl0mEDTPwOA
-BoJA6LHip1GnHYMma6ve+heRK9jGrB6xnhkB1Zem6g23xFUfJ3zSCNV2HykVh0A5
-3ThFEXXQmqc04L/NyFIduUd+Dbi7xgz2c1cWWn5DkR9VOsZtRASqnKmcp0yJF4Ou
-owReUoCLHhIlERnXDH19MURB6tuvsBzvgdAsxZohmz3tQjtQJvLsznFhBmIhVE5/
-wZ0+fyCMgMsq2JdiyIMzkX2woloPV+g7zPIlstR8L+xNxqE6FXrntl019fZISjZF
-ZtS6mFjBAgMBAAGjYzBhMB0GA1UdDgQWBBREnkj1zG1I1KBLf/5ZJC+Dl5mahjAf
-BgNVHSMEGDAWgBREnkj1zG1I1KBLf/5ZJC+Dl5mahjAPBgNVHRMBAf8EBTADAQH/
-MA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsFAAOCAQEABT41XBVwm8nHc2Fv
-civUwo/yQ10CzsSUuZQRg2dd4mdsdXa/uwyqNsatR5Nj3B5+1t4u/ukZMjgDfxT2
-AHMsWbEhBuH7rBiVDKP/mZb3Kyeb1STMHd3BOuCYRLDE5D53sXOpZCz2HAF8P11F
-hcCF5yWPldwX8zyfGm6wyuMdKulMY/okYWLW2n62HGz1Ah3UKt1VkOsqEUc8Ll50
-soIipX1TH0XsJ5F95yIW6MBoNtjG8U+ARDL54dHRHareqKucBK+tIA5kmE2la8BI
-WJZpTdwHjFGTot+fDz2LYLSCjaoITmJF4PkL0uDgPFveXHEnJcLmA4GLEFPjx1Wi
-tJ/X5g==
------END CERTIFICATE-----
-
-# TrustCor RootCert CA-1
------BEGIN CERTIFICATE-----
-MIIEMDCCAxigAwIBAgIJANqb7HHzA7AZMA0GCSqGSIb3DQEBCwUAMIGkMQswCQYD
-VQQGEwJQQTEPMA0GA1UECAwGUGFuYW1hMRQwEgYDVQQHDAtQYW5hbWEgQ2l0eTEk
-MCIGA1UECgwbVHJ1c3RDb3IgU3lzdGVtcyBTLiBkZSBSLkwuMScwJQYDVQQLDB5U
-cnVzdENvciBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxHzAdBgNVBAMMFlRydXN0Q29y
-IFJvb3RDZXJ0IENBLTEwHhcNMTYwMjA0MTIzMjE2WhcNMjkxMjMxMTcyMzE2WjCB
-pDELMAkGA1UEBhMCUEExDzANBgNVBAgMBlBhbmFtYTEUMBIGA1UEBwwLUGFuYW1h
-IENpdHkxJDAiBgNVBAoMG1RydXN0Q29yIFN5c3RlbXMgUy4gZGUgUi5MLjEnMCUG
-A1UECwweVHJ1c3RDb3IgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MR8wHQYDVQQDDBZU
-cnVzdENvciBSb290Q2VydCBDQS0xMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
-CgKCAQEAv463leLCJhJrMxnHQFgKq1mqjQCj/IDHUHuO1CAmujIS2CNUSSUQIpid
-RtLByZ5OGy4sDjjzGiVoHKZaBeYei0i/mJZ0PmnK6bV4pQa81QBeCQryJ3pS/C3V
-seq0iWEk8xoT26nPUu0MJLq5nux+AHT6k61sKZKuUbS701e/s/OojZz0JEsq1pme
-9J7+wH5COucLlVPat2gOkEz7cD+PSiyU8ybdY2mplNgQTsVHCJCZGxdNuWxu72CV
-EY4hgLW9oHPY0LJ3xEXqWib7ZnZ2+AYfYW0PVcWDtxBWcgYHpfOxGgMFZA6dWorW
-hnAbJN7+KIor0Gqw/Hqi3LJ5DotlDwIDAQABo2MwYTAdBgNVHQ4EFgQU7mtJPHo/
-DeOxCbeKyKsZn3MzUOcwHwYDVR0jBBgwFoAU7mtJPHo/DeOxCbeKyKsZn3MzUOcw
-DwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQELBQAD
-ggEBACUY1JGPE+6PHh0RU9otRCkZoB5rMZ5NDp6tPVxBb5UrJKF5mDo4Nvu7Zp5I
-/5CQ7z3UuJu0h3U/IJvOcs+hVcFNZKIZBqEHMwwLKeXx6quj7LUKdJDHfXLy11yf
-ke+Ri7fc7Waiz45mO7yfOgLgJ90WmMCV1Aqk5IGadZQ1nJBfiDcGrVmVCrDRZ9MZ
-yonnMlo2HD6CqFqTvsbQZJG2z9m2GM/bftJlo6bEjhcxwft+dtvTheNYsnd6djts
-L1Ac59v2Z3kf9YKVmgenFK+P3CghZwnS1k1aHBkcjndcw5QkPTJrS37UeJSDvjdN
-zl/HHk484IkzlQsPpTLWPFp5LBk=
------END CERTIFICATE-----
-
-# TrustCor RootCert CA-2
------BEGIN CERTIFICATE-----
-MIIGLzCCBBegAwIBAgIIJaHfyjPLWQIwDQYJKoZIhvcNAQELBQAwgaQxCzAJBgNV
-BAYTAlBBMQ8wDQYDVQQIDAZQYW5hbWExFDASBgNVBAcMC1BhbmFtYSBDaXR5MSQw
-IgYDVQQKDBtUcnVzdENvciBTeXN0ZW1zIFMuIGRlIFIuTC4xJzAlBgNVBAsMHlRy
-dXN0Q29yIENlcnRpZmljYXRlIEF1dGhvcml0eTEfMB0GA1UEAwwWVHJ1c3RDb3Ig
-Um9vdENlcnQgQ0EtMjAeFw0xNjAyMDQxMjMyMjNaFw0zNDEyMzExNzI2MzlaMIGk
-MQswCQYDVQQGEwJQQTEPMA0GA1UECAwGUGFuYW1hMRQwEgYDVQQHDAtQYW5hbWEg
-Q2l0eTEkMCIGA1UECgwbVHJ1c3RDb3IgU3lzdGVtcyBTLiBkZSBSLkwuMScwJQYD
-VQQLDB5UcnVzdENvciBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxHzAdBgNVBAMMFlRy
-dXN0Q29yIFJvb3RDZXJ0IENBLTIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK
-AoICAQCnIG7CKqJiJJWQdsg4foDSq8GbZQWU9MEKENUCrO2fk8eHyLAnK0IMPQo+
-QVqedd2NyuCb7GgypGmSaIwLgQ5WoD4a3SwlFIIvl9NkRvRUqdw6VC0xK5mC8tkq
-1+9xALgxpL56JAfDQiDyitSSBBtlVkxs1Pu2YVpHI7TYabS3OtB0PAx1oYxOdqHp
-2yqlO/rOsP9+aij9JxzIsekp8VduZLTQwRVtDr4uDkbIXvRR/u8OYzo7cbrPb1nK
-DOObXUm4TOJXsZiKQlecdu/vvdFoqNL0Cbt3Nb4lggjEFixEIFapRBF37120Hape
-az6LMvYHL1cEksr1/p3C6eizjkxLAjHZ5DxIgif3GIJ2SDpxsROhOdUuxTTCHWKF
-3wP+TfSvPd9cW436cOGlfifHhi5qjxLGhF5DUVCcGZt45vz27Ud+ez1m7xMTiF88
-oWP7+ayHNZ/zgp6kPwqcMWmLmaSISo5uZk3vFsQPeSghYA2FFn3XVDjxklb9tTNM
-g9zXEJ9L/cb4Qr26fHMC4P99zVvh1Kxhe1fVSntb1IVYJ12/+CtgrKAmrhQhJ8Z3
-mjOAPF5GP/fDsaOGM8boXg25NSyqRsGFAnWAoOsk+xWq5Gd/bnc/9ASKL3x74xdh
-8N0JqSDIvgmk0H5Ew7IwSjiqqewYmgeCK9u4nBit2uBGF6zPXQIDAQABo2MwYTAd
-BgNVHQ4EFgQU2f4hQG6UnrybPZx9mCAZ5YwwYrIwHwYDVR0jBBgwFoAU2f4hQG6U
-nrybPZx9mCAZ5YwwYrIwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYw
-DQYJKoZIhvcNAQELBQADggIBAJ5Fngw7tu/hOsh80QA9z+LqBrWyOrsGS2h60COX
-dKcs8AjYeVrXWoSK2BKaG9l9XE1wxaX5q+WjiYndAfrs3fnpkpfbsEZC89NiqpX+
-MWcUaViQCqoL7jcjx1BRtPV+nuN79+TMQjItSQzL/0kMmx40/W5ulop5A7Zv2wnL
-/V9lFDfhOPXzYRZY5LVtDQsEGz9QLX+zx3oaFoBg+Iof6Rsqxvm6ARppv9JYx1RX
-CI/hOWB3S6xZhBqI8d3LT3jX5+EzLfzuQfogsL7L9ziUwOHQhQ+77Sxzq+3+knYa
-ZH9bDTMJBzN7Bj8RpFxwPIXAz+OQqIN3+tvmxYxoZxBnpVIt8MSZj3+/0WvitUfW
-2dCFmU2Umw9Lje4AWkcdEQOsQRivh7dvDDqPys/cA8GiCcjl/YBeyGBCARsaU1q7
-N6a3vLqE6R5sGtRk2tRD/pOLS/IseRYQ1JMLiI+h2IYURpFHmygk71dSTlxCnKr3
-Sewn6EAes6aJInKc9Q0ztFijMDvd1GpUk74aTfOTlPf8hAs/hCBcNANExdqtvArB
-As8e5ZTZ845b2EzwnexhF7sUMlQMAimTHpKG9n/v55IFDlndmQguLvqcAFLTxWYp
-5KeXRKQOKIETNcX2b2TmQcTVL8w0RSXPQQCWPUouwpaYT05KnJe32x+SMsj/D1Fu
-1uwJ
------END CERTIFICATE-----
-
-# Trustis FPS Root CA
------BEGIN CERTIFICATE-----
-MIIDZzCCAk+gAwIBAgIQGx+ttiD5JNM2a/fH8YygWTANBgkqhkiG9w0BAQUFADBF
-MQswCQYDVQQGEwJHQjEYMBYGA1UEChMPVHJ1c3RpcyBMaW1pdGVkMRwwGgYDVQQL
-ExNUcnVzdGlzIEZQUyBSb290IENBMB4XDTAzMTIyMzEyMTQwNloXDTI0MDEyMTEx
-MzY1NFowRTELMAkGA1UEBhMCR0IxGDAWBgNVBAoTD1RydXN0aXMgTGltaXRlZDEc
-MBoGA1UECxMTVHJ1c3RpcyBGUFMgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQAD
-ggEPADCCAQoCggEBAMVQe547NdDfxIzNjpvto8A2mfRC6qc+gIMPpqdZh8mQRUN+
-AOqGeSoDvT03mYlmt+WKVoaTnGhLaASMk5MCPjDSNzoiYYkchU59j9WvezX2fihH
-iTHcDnlkH5nSW7r+f2C/revnPDgpai/lkQtV/+xvWNUtyd5MZnGPDNcE2gfmHhjj
-vSkCqPoc4Vu5g6hBSLwacY3nYuUtsuvffM/bq1rKMfFMIvMFE/eC+XN5DL7XSxzA
-0RU8k0Fk0ea+IxciAIleH2ulrG6nS4zto3Lmr2NNL4XSFDWaLk6M6jKYKIahkQlB
-OrTh4/L68MkKokHdqeMDx4gVOxzUGpTXn2RZEm0CAwEAAaNTMFEwDwYDVR0TAQH/
-BAUwAwEB/zAfBgNVHSMEGDAWgBS6+nEleYtXQSUhhgtx67JkDoshZzAdBgNVHQ4E
-FgQUuvpxJXmLV0ElIYYLceuyZA6LIWcwDQYJKoZIhvcNAQEFBQADggEBAH5Y//01
-GX2cGE+esCu8jowU/yyg2kdbw++BLa8F6nRIW/M+TgfHbcWzk88iNVy2P3UnXwmW
-zaD+vkAMXBJV+JOCyinpXj9WV4s4NvdFGkwozZ5BuO1WTISkQMi4sKUraXAEasP4
-1BIy+Q7DsdwyhEQsb8tGD+pmQQ9P8Vilpg0ND2HepZ5dfWWhPBfnqFVO76DH7cZE
-f1T1o+CP8HxVIo8ptoGj4W1OLBuAZ+ytIJ8MYmHVl/9D7S3B2l0pKoU/rGXuhg8F
-jZBf3+6f9L/uHfuY5H+QK4R4EA5sSVPvFVtlRkpdr7r7OnIdzfYliB6XzCGcKQEN
-ZetX2fNXlrtIzYE=
------END CERTIFICATE-----
-
-# T-TeleSec GlobalRoot Class 2
------BEGIN CERTIFICATE-----
-MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUx
-KzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAd
-BgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNl
-YyBHbG9iYWxSb290IENsYXNzIDIwHhcNMDgxMDAxMTA0MDE0WhcNMzMxMDAxMjM1
-OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnBy
-aXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50
-ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDIwggEiMA0G
-CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCqX9obX+hzkeXaXPSi5kfl82hVYAUd
-AqSzm1nzHoqvNK38DcLZSBnuaY/JIPwhqgcZ7bBcrGXHX+0CfHt8LRvWurmAwhiC
-FoT6ZrAIxlQjgeTNuUk/9k9uN0goOA/FvudocP05l03Sx5iRUKrERLMjfTlH6VJi
-1hKTXrcxlkIF+3anHqP1wvzpesVsqXFP6st4vGCvx9702cu+fjOlbpSD8DT6Iavq
-jnKgP6TeMFvvhk1qlVtDRKgQFRzlAVfFmPHmBiiRqiDFt1MmUUOyCxGVWOHAD3bZ
-wI18gfNycJ5v/hqO2V81xrJvNHy+SE/iWjnX2J14np+GPgNeGYtEotXHAgMBAAGj
-QjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS/
-WSA2AHmgoCJrjNXyYdK4LMuCSjANBgkqhkiG9w0BAQsFAAOCAQEAMQOiYQsfdOhy
-NsZt+U2e+iKo4YFWz827n+qrkRk4r6p8FU3ztqONpfSO9kSpp+ghla0+AGIWiPAC
-uvxhI+YzmzB6azZie60EI4RYZeLbK4rnJVM3YlNfvNoBYimipidx5joifsFvHZVw
-IEoHNN/q/xWA5brXethbdXwFeilHfkCoMRN3zUA7tFFHei4R40cR3p1m0IvVVGb6
-g1XqfMIpiRvpb7PO4gWEyS8+eIVibslfwXhjdFjASBgMmTnrpMwatXlajRWc2BQN
-9noHV8cigwUtPJslJj0Ys6lDfMjIq2SPDqO/nBudMNva0Bkuqjzx+zOAduTNrRlP
-BSeOE6Fuwg==
------END CERTIFICATE-----
-
-# T-TeleSec GlobalRoot Class 3
------BEGIN CERTIFICATE-----
-MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUx
-KzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAd
-BgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNl
-YyBHbG9iYWxSb290IENsYXNzIDMwHhcNMDgxMDAxMTAyOTU2WhcNMzMxMDAxMjM1
-OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnBy
-aXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50
-ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDMwggEiMA0G
-CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC9dZPwYiJvJK7genasfb3ZJNW4t/zN
-8ELg63iIVl6bmlQdTQyK9tPPcPRStdiTBONGhnFBSivwKixVA9ZIw+A5OO3yXDw/
-RLyTPWGrTs0NvvAgJ1gORH8EGoel15YUNpDQSXuhdfsaa3Ox+M6pCSzyU9XDFES4
-hqX2iys52qMzVNn6chr3IhUciJFrf2blw2qAsCTz34ZFiP0Zf3WHHx+xGwpzJFu5
-ZeAsVMhg02YXP+HMVDNzkQI6pn97djmiH5a2OK61yJN0HZ65tOVgnS9W0eDrXltM
-EnAMbEQgqxHY9Bn20pxSN+f6tsIxO0rUFJmtxxr1XV/6B7h8DR/Wgx6zAgMBAAGj
-QjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS1
-A/d2O2GCahKqGFPrAyGUv/7OyjANBgkqhkiG9w0BAQsFAAOCAQEAVj3vlNW92nOy
-WL6ukK2YJ5f+AbGwUgC4TeQbIXQbfsDuXmkqJa9c1h3a0nnJ85cp4IaH3gRZD/FZ
-1GSFS5mvJQQeyUapl96Cshtwn5z2r3Ex3XsFpSzTucpH9sry9uetuUg/vBa3wW30
-6gmv7PO15wWeph6KU1HWk4HMdJP2udqmJQV0eVp+QD6CSyYRMG7hP0HHRwA11fXT
-91Q+gT3aSWqas+8QPebrb9HIIkfLzM8BMZLZGOMivgkeGj5asuRrDFR6fUNOuIml
-e9eiPZaGzPImNC1qkp2aGtAw4l1OBLBfiyB+d8E9lYLRRpo7PHi4b6HQDWSieB4p
-TpPDpFQUWw==
------END CERTIFICATE-----
-
-# TUBITAK Kamu SM SSL Kok Sertifikasi - Surum 1
------BEGIN CERTIFICATE-----
-MIIEYzCCA0ugAwIBAgIBATANBgkqhkiG9w0BAQsFADCB0jELMAkGA1UEBhMCVFIx
-GDAWBgNVBAcTD0dlYnplIC0gS29jYWVsaTFCMEAGA1UEChM5VHVya2l5ZSBCaWxp
-bXNlbCB2ZSBUZWtub2xvamlrIEFyYXN0aXJtYSBLdXJ1bXUgLSBUVUJJVEFLMS0w
-KwYDVQQLEyRLYW11IFNlcnRpZmlrYXN5b24gTWVya2V6aSAtIEthbXUgU00xNjA0
-BgNVBAMTLVRVQklUQUsgS2FtdSBTTSBTU0wgS29rIFNlcnRpZmlrYXNpIC0gU3Vy
-dW0gMTAeFw0xMzExMjUwODI1NTVaFw00MzEwMjUwODI1NTVaMIHSMQswCQYDVQQG
-EwJUUjEYMBYGA1UEBxMPR2ViemUgLSBLb2NhZWxpMUIwQAYDVQQKEzlUdXJraXll
-IEJpbGltc2VsIHZlIFRla25vbG9qaWsgQXJhc3Rpcm1hIEt1cnVtdSAtIFRVQklU
-QUsxLTArBgNVBAsTJEthbXUgU2VydGlmaWthc3lvbiBNZXJrZXppIC0gS2FtdSBT
-TTE2MDQGA1UEAxMtVFVCSVRBSyBLYW11IFNNIFNTTCBLb2sgU2VydGlmaWthc2kg
-LSBTdXJ1bSAxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAr3UwM6q7
-a9OZLBI3hNmNe5eA027n/5tQlT6QlVZC1xl8JoSNkvoBHToP4mQ4t4y86Ij5iySr
-LqP1N+RAjhgleYN1Hzv/bKjFxlb4tO2KRKOrbEz8HdDc72i9z+SqzvBV96I01INr
-N3wcwv61A+xXzry0tcXtAA9TNypN9E8Mg/uGz8v+jE69h/mniyFXnHrfA2eJLJ2X
-YacQuFWQfw4tJzh03+f92k4S400VIgLI4OD8D62K18lUUMw7D8oWgITQUVbDjlZ/
-iSIzL+aFCr2lqBs23tPcLG07xxO9WSMs5uWk99gL7eqQQESolbuT1dCANLZGeA4f
-AJNG4e7p+exPFwIDAQABo0IwQDAdBgNVHQ4EFgQUZT/HiobGPN08VFw1+DrtUgxH
-V8gwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL
-BQADggEBACo/4fEyjq7hmFxLXs9rHmoJ0iKpEsdeV31zVmSAhHqT5Am5EM2fKifh
-AHe+SMg1qIGf5LgsyX8OsNJLN13qudULXjS99HMpw+0mFZx+CFOKWI3QSyjfwbPf
-IPP54+M638yclNhOT8NrF7f3cuitZjO1JVOr4PhMqZ398g26rrnZqsZr+ZO7rqu4
-lzwDGrpDxpa5RXI4s6ehlj2Re37AIVNMh+3yC1SVUZPVIqUNivGTDj5UDrDYyU7c
-8jEyVupk+eq1nRZmQnLzf9OxMUP8pI4X8W0jq5Rm+K37DwhuJi1/FwcJsoz7UMCf
-lo3Ptv0AnVoUmr8CRPXBwp8iXqIPoeM=
------END CERTIFICATE-----
-
-# TWCA Global Root CA
------BEGIN CERTIFICATE-----
-MIIFQTCCAymgAwIBAgICDL4wDQYJKoZIhvcNAQELBQAwUTELMAkGA1UEBhMCVFcx
-EjAQBgNVBAoTCVRBSVdBTi1DQTEQMA4GA1UECxMHUm9vdCBDQTEcMBoGA1UEAxMT
-VFdDQSBHbG9iYWwgUm9vdCBDQTAeFw0xMjA2MjcwNjI4MzNaFw0zMDEyMzExNTU5
-NTlaMFExCzAJBgNVBAYTAlRXMRIwEAYDVQQKEwlUQUlXQU4tQ0ExEDAOBgNVBAsT
-B1Jvb3QgQ0ExHDAaBgNVBAMTE1RXQ0EgR2xvYmFsIFJvb3QgQ0EwggIiMA0GCSqG
-SIb3DQEBAQUAA4ICDwAwggIKAoICAQCwBdvI64zEbooh745NnHEKH1Jw7W2CnJfF
-10xORUnLQEK1EjRsGcJ0pDFfhQKX7EMzClPSnIyOt7h52yvVavKOZsTuKwEHktSz
-0ALfUPZVr2YOy+BHYC8rMjk1Ujoog/h7FsYYuGLWRyWRzvAZEk2tY/XTP3VfKfCh
-MBwqoJimFb3u/Rk28OKRQ4/6ytYQJ0lM793B8YVwm8rqqFpD/G2Gb3PpN0Wp8DbH
-zIh1HrtsBv+baz4X7GGqcXzGHaL3SekVtTzWoWH1EfcFbx39Eb7QMAfCKbAJTibc
-46KokWofwpFFiFzlmLhxpRUZyXx1EcxwdE8tmx2RRP1WKKD+u4ZqyPpcC1jcxkt2
-yKsi2XMPpfRaAok/T54igu6idFMqPVMnaR1sjjIsZAAmY2E2TqNGtz99sy2sbZCi
-laLOz9qC5wc0GZbpuCGqKX6mOL6OKUohZnkfs8O1CWfe1tQHRvMq2uYiN2DLgbYP
-oA/pyJV/v1WRBXrPPRXAb94JlAGD1zQbzECl8LibZ9WYkTunhHiVJqRaCPgrdLQA
-BDzfuBSO6N+pjWxnkjMdwLfS7JLIvgm/LCkFbwJrnu+8vyq8W8BQj0FwcYeyTbcE
-qYSjMq+u7msXi7Kx/mzhkIyIqJdIzshNy/MGz19qCkKxHh53L46g5pIOBvwFItIm
-4TFRfTLcDwIDAQABoyMwITAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB
-/zANBgkqhkiG9w0BAQsFAAOCAgEAXzSBdu+WHdXltdkCY4QWwa6gcFGn90xHNcgL
-1yg9iXHZqjNB6hQbbCEAwGxCGX6faVsgQt+i0trEfJdLjbDorMjupWkEmQqSpqsn
-LhpNgb+E1HAerUf+/UqdM+DyucRFCCEK2mlpc3INvjT+lIutwx4116KD7+U4x6WF
-H6vPNOw/KP4M8VeGTslV9xzU2KV9Bnpv1d8Q34FOIWWxtuEXeZVFBs5fzNxGiWNo
-RI2T9GRwoD2dKAXDOXC4Ynsg/eTb6QihuJ49CcdP+yz4k3ZB3lLg4VfSnQO8d57+
-nile98FRYB/e2guyLXW3Q0iT5/Z5xoRdgFlglPx4mI88k1HtQJAH32RjJMtOcQWh
-15QaiDLxInQirqWm2BJpTGCjAu4r7NRjkgtevi92a6O2JryPA9gK8kxkRr05YuWW
-6zRjESjMlfGt7+/cgFhI6Uu46mWs6fyAtbXIRfmswZ/ZuepiiI7E8UuDEq3mi4TW
-nsLrgxifarsbJGAzcMzs9zLzXNl5fe+epP7JI8Mk7hWSsT2RTyaGvWZzJBPqpK5j
-wa19hAM8EHiGG3njxPPyBJUgriOCxLM6AGK/5jYk4Ve6xx6QddVfP5VhK8E7zeWz
-aGHQRiapIVJpLesux+t3zqY6tQMzT3bR51xUAV3LePTJDL/PEo4XLSNolOer/qmy
-KwbQBM0=
------END CERTIFICATE-----
-
-# TWCA Root Certification Authority
------BEGIN CERTIFICATE-----
-MIIDezCCAmOgAwIBAgIBATANBgkqhkiG9w0BAQUFADBfMQswCQYDVQQGEwJUVzES
-MBAGA1UECgwJVEFJV0FOLUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFU
-V0NBIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDgwODI4MDcyNDMz
-WhcNMzAxMjMxMTU1OTU5WjBfMQswCQYDVQQGEwJUVzESMBAGA1UECgwJVEFJV0FO
-LUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFUV0NBIFJvb3QgQ2VydGlm
-aWNhdGlvbiBBdXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
-AQCwfnK4pAOU5qfeCTiRShFAh6d8WWQUe7UREN3+v9XAu1bihSX0NXIP+FPQQeFE
-AcK0HMMxQhZHhTMidrIKbw/lJVBPhYa+v5guEGcevhEFhgWQxFnQfHgQsIBct+HH
-K3XLfJ+utdGdIzdjp9xCoi2SBBtQwXu4PhvJVgSLL1KbralW6cH/ralYhzC2gfeX
-RfwZVzsrb+RH9JlF/h3x+JejiB03HFyP4HYlmlD4oFT/RJB2I9IyxsOrBr/8+7/z
-rX2SYgJbKdM1o5OaQ2RgXbL6Mv87BK9NQGr5x+PvI/1ry+UPizgN7gr8/g+YnzAx
-3WxSZfmLgb4i4RxYA7qRG4kHAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV
-HRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqOFsmjd6LWvJPelSDGRjjCDWmujANBgkq
-hkiG9w0BAQUFAAOCAQEAPNV3PdrfibqHDAhUaiBQkr6wQT25JmSDCi/oQMCXKCeC
-MErJk/9q56YAf4lCmtYR5VPOL8zy2gXE/uJQxDqGfczafhAJO5I1KlOy/usrBdls
-XebQ79NqZp4VKIV66IIArB6nCWlWQtNoURi+VJq/REG6Sb4gumlc7rh3zc5sH62D
-lhh9DrUUOYTxKOkto557HnpyWoOzeW/vtPzQCqVYT0bf+215WfKEIlKuD8z7fDvn
-aspHYcN6+NOSBB+4IIThNlQWx0DeO4pz3N/GCUzf7Nr/1FNCocnyYh0igzyXxfkZ
-YiesZSLX0zzG5Y6yU8xJzrww/nsOM5D77dIUkR8Hrw==
------END CERTIFICATE-----
-
-# USERTrust ECC Certification Authority
------BEGIN CERTIFICATE-----
-MIICjzCCAhWgAwIBAgIQXIuZxVqUxdJxVt7NiYDMJjAKBggqhkjOPQQDAzCBiDEL
-MAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNl
-eSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMT
-JVVTRVJUcnVzdCBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAwMjAx
-MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgT
-Ck5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVUaGUg
-VVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBFQ0MgQ2VydGlm
-aWNhdGlvbiBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQarFRaqflo
-I+d61SRvU8Za2EurxtW20eZzca7dnNYMYf3boIkDuAUU7FfO7l0/4iGzzvfUinng
-o4N+LZfQYcTxmdwlkWOrfzCjtHDix6EznPO/LlxTsV+zfTJ/ijTjeXmjQjBAMB0G
-A1UdDgQWBBQ64QmG1M8ZwpZ2dEl23OA1xmNjmjAOBgNVHQ8BAf8EBAMCAQYwDwYD
-VR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjA2Z6EWCNzklwBBHU6+4WMB
-zzuqQhFkoJ2UOQIReVx7Hfpkue4WQrO/isIJxOzksU0CMQDpKmFHjFJKS04YcPbW
-RNZu9YO6bVi9JNlWSOrvxKJGgYhqOkbRqZtNyWHa0V1Xahg=
------END CERTIFICATE-----
-
-# USERTrust RSA Certification Authority
------BEGIN CERTIFICATE-----
-MIIF3jCCA8agAwIBAgIQAf1tMPyjylGoG7xkDjUDLTANBgkqhkiG9w0BAQwFADCB
-iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0pl
-cnNleSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNV
-BAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAw
-MjAxMDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNV
-BAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVU
-aGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBSU0EgQ2Vy
-dGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK
-AoICAQCAEmUXNg7D2wiz0KxXDXbtzSfTTK1Qg2HiqiBNCS1kCdzOiZ/MPans9s/B
-3PHTsdZ7NygRK0faOca8Ohm0X6a9fZ2jY0K2dvKpOyuR+OJv0OwWIJAJPuLodMkY
-tJHUYmTbf6MG8YgYapAiPLz+E/CHFHv25B+O1ORRxhFnRghRy4YUVD+8M/5+bJz/
-Fp0YvVGONaanZshyZ9shZrHUm3gDwFA66Mzw3LyeTP6vBZY1H1dat//O+T23LLb2
-VN3I5xI6Ta5MirdcmrS3ID3KfyI0rn47aGYBROcBTkZTmzNg95S+UzeQc0PzMsNT
-79uq/nROacdrjGCT3sTHDN/hMq7MkztReJVni+49Vv4M0GkPGw/zJSZrM233bkf6
-c0Plfg6lZrEpfDKEY1WJxA3Bk1QwGROs0303p+tdOmw1XNtB1xLaqUkL39iAigmT
-Yo61Zs8liM2EuLE/pDkP2QKe6xJMlXzzawWpXhaDzLhn4ugTncxbgtNMs+1b/97l
-c6wjOy0AvzVVdAlJ2ElYGn+SNuZRkg7zJn0cTRe8yexDJtC/QV9AqURE9JnnV4ee
-UB9XVKg+/XRjL7FQZQnmWEIuQxpMtPAlR1n6BB6T1CZGSlCBst6+eLf8ZxXhyVeE
-Hg9j1uliutZfVS7qXMYoCAQlObgOK6nyTJccBz8NUvXt7y+CDwIDAQABo0IwQDAd
-BgNVHQ4EFgQUU3m/WqorSs9UgOHYm8Cd8rIDZsswDgYDVR0PAQH/BAQDAgEGMA8G
-A1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAFzUfA3P9wF9QZllDHPF
-Up/L+M+ZBn8b2kMVn54CVVeWFPFSPCeHlCjtHzoBN6J2/FNQwISbxmtOuowhT6KO
-VWKR82kV2LyI48SqC/3vqOlLVSoGIG1VeCkZ7l8wXEskEVX/JJpuXior7gtNn3/3
-ATiUFJVDBwn7YKnuHKsSjKCaXqeYalltiz8I+8jRRa8YFWSQEg9zKC7F4iRO/Fjs
-8PRF/iKz6y+O0tlFYQXBl2+odnKPi4w2r78NBc5xjeambx9spnFixdjQg3IM8WcR
-iQycE0xyNN+81XHfqnHd4blsjDwSXWXavVcStkNr/+XeTWYRUc+ZruwXtuhxkYze
-Sf7dNXGiFSeUHM9h4ya7b6NnJSFd5t0dCy5oGzuCr+yDZ4XUmFF0sbmZgIn/f3gZ
-XHlKYC6SQK5MNyosycdiyA5d9zZbyuAlJQG03RoHnHcAP9Dc1ew91Pq7P8yF1m9/
-qS3fuQL39ZeatTXaw2ewh0qpKJ4jjv9cJ2vhsE/zB+4ALtRZh8tSQZXq9EfX7mRB
-VXyNWQKV3WKdwrnuWih0hKWbt5DHDAff9Yk2dDLWKMGwsAvgnEzDHNb842m1R0aB
-L6KCq9NjRHDEjf8tM7qtj3u1cIiuPhnPQCjY/MiQu12ZIvVS5ljFH4gxQ+6IHdfG
-jjxDah2nGN59PRbxYvnKkKj9
------END CERTIFICATE-----
-
-# Verisign Class 3 Public Primary Certification Authority - G3
------BEGIN CERTIFICATE-----
-MIIEGjCCAwICEQCbfgZJoz5iudXukEhxKe9XMA0GCSqGSIb3DQEBBQUAMIHKMQsw
-CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZl
-cmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWdu
-LCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlT
-aWduIENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3Jp
-dHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMIHKMQswCQYD
-VQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlT
-aWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJ
-bmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWdu
-IENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkg
-LSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMu6nFL8eB8aHm8b
-N3O9+MlrlBIwT/A2R/XQkQr1F8ilYcEWQE37imGQ5XYgwREGfassbqb1EUGO+i2t
-KmFZpGcmTNDovFJbcCAEWNF6yaRpvIMXZK0Fi7zQWM6NjPXr8EJJC52XJ2cybuGu
-kxUccLwgTS8Y3pKI6GyFVxEa6X7jJhFUokWWVYPKMIno3Nij7SqAP395ZVc+FSBm
-CC+Vk7+qRy+oRpfwEuL+wgorUeZ25rdGt+INpsyow0xZVYnm6FNcHOqd8GIWC6fJ
-Xwzw3sJ2zq/3avL6QaaiMxTJ5Xpj055iN9WFZZ4O5lMkdBteHRJTW8cs54NJOxWu
-imi5V5cCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAERSWwauSCPc/L8my/uRan2Te
-2yFPhpk0djZX3dAVL8WtfxUfN2JzPtTnX84XA9s1+ivbrmAJXx5fj267Cz3qWhMe
-DGBvtcC1IyIuBwvLqXTLR7sdwdela8wv0kL9Sd2nic9TutoAWii/gt/4uhMdUIaC
-/Y4wjylGsB49Ndo4YhYYSq3mtlFs3q9i6wHQHiT+eo8SGhJouPtmmRQURVyu565p
-F4ErWjfJXir0xuKhXFSbplQAz/DxwceYMBo7Nhbbo27q/a2ywtrvAkcTisDxszGt
-TxzhT5yvDwyd93gN2PQ1VoDat20Xj50egWTh/sVFuq1ruQp6Tk9LhO5L8X3dEQ==
------END CERTIFICATE-----
-
-# VeriSign Class 3 Public Primary Certification Authority - G4
------BEGIN CERTIFICATE-----
-MIIDhDCCAwqgAwIBAgIQL4D+I4wOIg9IZxIokYesszAKBggqhkjOPQQDAzCByjEL
-MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZW
-ZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNyBWZXJpU2ln
-biwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJp
-U2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9y
-aXR5IC0gRzQwHhcNMDcxMTA1MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCByjELMAkG
-A1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJp
-U2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNyBWZXJpU2lnbiwg
-SW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2ln
-biBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5
-IC0gRzQwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASnVnp8Utpkmw4tXNherJI9/gHm
-GUo9FANL+mAnINmDiWn6VMaaGF5VKmTeBvaNSjutEDxlPZCIBIngMGGzrl0Bp3ve
-fLK+ymVhAIau2o970ImtTR1ZmkGxvEeA3J5iw/mjgbIwga8wDwYDVR0TAQH/BAUw
-AwEB/zAOBgNVHQ8BAf8EBAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJ
-aW1hZ2UvZ2lmMCEwHzAHBgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYj
-aHR0cDovL2xvZ28udmVyaXNpZ24uY29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFLMW
-kf3upm7ktS5Jj4d4gYDs5bG1MAoGCCqGSM49BAMDA2gAMGUCMGYhDBgmYFo4e1ZC
-4Kf8NoRRkSAsdk1DPcQdhCPQrNZ8NQbOzWm9kA3bbEhCHQ6qQgIxAJw9SDkjOVga
-FRJZap7v1VmyHVIsmXHNxynfGyphe3HR3vPA5Q06Sqotp9iGKt0uEA==
------END CERTIFICATE-----
-
-# VeriSign Class 3 Public Primary Certification Authority - G5
------BEGIN CERTIFICATE-----
-MIIE0zCCA7ugAwIBAgIQGNrRniZ96LtKIVjNzGs7SjANBgkqhkiG9w0BAQUFADCB
-yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL
-ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJp
-U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxW
-ZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0
-aG9yaXR5IC0gRzUwHhcNMDYxMTA4MDAwMDAwWhcNMzYwNzE2MjM1OTU5WjCByjEL
-MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZW
-ZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2ln
-biwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJp
-U2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9y
-aXR5IC0gRzUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvJAgIKXo1
-nmAMqudLO07cfLw8RRy7K+D+KQL5VwijZIUVJ/XxrcgxiV0i6CqqpkKzj/i5Vbex
-t0uz/o9+B1fs70PbZmIVYc9gDaTY3vjgw2IIPVQT60nKWVSFJuUrjxuf6/WhkcIz
-SdhDY2pSS9KP6HBRTdGJaXvHcPaz3BJ023tdS1bTlr8Vd6Gw9KIl8q8ckmcY5fQG
-BO+QueQA5N06tRn/Arr0PO7gi+s3i+z016zy9vA9r911kTMZHRxAy3QkGSGT2RT+
-rCpSx4/VBEnkjWNHiDxpg8v+R70rfk/Fla4OndTRQ8Bnc+MUCH7lP59zuDMKz10/
-NIeWiu5T6CUVAgMBAAGjgbIwga8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8E
-BAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2UvZ2lmMCEwHzAH
-BgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVy
-aXNpZ24uY29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFH/TZafC3ey78DAJ80M5+gKv
-MzEzMA0GCSqGSIb3DQEBBQUAA4IBAQCTJEowX2LP2BqYLz3q3JktvXf2pXkiOOzE
-p6B4Eq1iDkVwZMXnl2YtmAl+X6/WzChl8gGqCBpH3vn5fJJaCGkgDdk+bW48DW7Y
-5gaRQBi5+MHt39tBquCWIMnNZBU4gcmU7qKEKQsTb47bDN0lAtukixlE0kF6BWlK
-WE9gyn6CagsCqiUXObXbf+eEZSqVir2G3l6BFoMtEMze/aiCKm0oHw0LxOXnGiYZ
-4fQRbxC1lfznQgUy286dUV4otp6F01vvpX1FQHKOtw5rDgb7MzVIcbidJ4vEZV8N
-hnacRHr2lVz2XTIIM6RUthg/aFzyQkqFOFSDX9HoLPKsEdao7WNq
------END CERTIFICATE-----
-
-# VeriSign Universal Root Certification Authority
------BEGIN CERTIFICATE-----
-MIIEuTCCA6GgAwIBAgIQQBrEZCGzEyEDDrvkEhrFHTANBgkqhkiG9w0BAQsFADCB
-vTELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL
-ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwOCBWZXJp
-U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MTgwNgYDVQQDEy9W
-ZXJpU2lnbiBVbml2ZXJzYWwgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAe
-Fw0wODA0MDIwMDAwMDBaFw0zNzEyMDEyMzU5NTlaMIG9MQswCQYDVQQGEwJVUzEX
-MBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0
-IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAyMDA4IFZlcmlTaWduLCBJbmMuIC0gRm9y
-IGF1dGhvcml6ZWQgdXNlIG9ubHkxODA2BgNVBAMTL1ZlcmlTaWduIFVuaXZlcnNh
-bCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEF
-AAOCAQ8AMIIBCgKCAQEAx2E3XrEBNNti1xWb/1hajCMj1mCOkdeQmIN65lgZOIzF
-9uVkhbSicfvtvbnazU0AtMgtc6XHaXGVHzk8skQHnOgO+k1KxCHfKWGPMiJhgsWH
-H26MfF8WIFFE0XBPV+rjHOPMee5Y2A7Cs0WTwCznmhcrewA3ekEzeOEz4vMQGn+H
-LL729fdC4uW/h2KJXwBL38Xd5HVEMkE6HnFuacsLdUYI0crSK5XQz/u5QGtkjFdN
-/BMReYTtXlT2NJ8IAfMQJQYXStrxHXpma5hgZqTZ79IugvHw7wnqRMkVauIDbjPT
-rJ9VAMf2CGqUuV/c4DPxhGD5WycRtPwW8rtWaoAljQIDAQABo4GyMIGvMA8GA1Ud
-EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMG0GCCsGAQUFBwEMBGEwX6FdoFsw
-WTBXMFUWCWltYWdlL2dpZjAhMB8wBwYFKw4DAhoEFI/l0xqGrI2Oa8PPgGrUSBgs
-exkuMCUWI2h0dHA6Ly9sb2dvLnZlcmlzaWduLmNvbS92c2xvZ28uZ2lmMB0GA1Ud
-DgQWBBS2d/ppSEefUxLVwuoHMnYH0ZcHGTANBgkqhkiG9w0BAQsFAAOCAQEASvj4
-sAPmLGd75JR3Y8xuTPl9Dg3cyLk1uXBPY/ok+myDjEedO2Pzmvl2MpWRsXe8rJq+
-seQxIcaBlVZaDrHC1LGmWazxY8u4TB1ZkErvkBYoH1quEPuBUDgMbMzxPcP1Y+Oz
-4yHJJDnp/RVmRvQbEdBNc6N9Rvk97ahfYtTxP/jgdFcrGJ2BtMQo2pSXpXDrrB2+
-BxHw1dvd5Yzw1TKwg+ZX4o+/vqGqvz0dtdQ46tewXDpPaj+PwGZsY6rp2aQW9IHR
-lRQOfc2VNNnSj3BzgXucfr2YYdhFh5iQxeuGMMY1v/D/w1WIg0vvBZIGcfK4mJO3
-7M2CYfE45k+XmCpajQ==
------END CERTIFICATE-----
-
-# Visa eCommerce Root
------BEGIN CERTIFICATE-----
-MIIDojCCAoqgAwIBAgIQE4Y1TR0/BvLB+WUF1ZAcYjANBgkqhkiG9w0BAQUFADBr
-MQswCQYDVQQGEwJVUzENMAsGA1UEChMEVklTQTEvMC0GA1UECxMmVmlzYSBJbnRl
-cm5hdGlvbmFsIFNlcnZpY2UgQXNzb2NpYXRpb24xHDAaBgNVBAMTE1Zpc2EgZUNv
-bW1lcmNlIFJvb3QwHhcNMDIwNjI2MDIxODM2WhcNMjIwNjI0MDAxNjEyWjBrMQsw
-CQYDVQQGEwJVUzENMAsGA1UEChMEVklTQTEvMC0GA1UECxMmVmlzYSBJbnRlcm5h
-dGlvbmFsIFNlcnZpY2UgQXNzb2NpYXRpb24xHDAaBgNVBAMTE1Zpc2EgZUNvbW1l
-cmNlIFJvb3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvV95WHm6h
-2mCxlCfLF9sHP4CFT8icttD0b0/Pmdjh28JIXDqsOTPHH2qLJj0rNfVIsZHBAk4E
-lpF7sDPwsRROEW+1QK8bRaVK7362rPKgH1g/EkZgPI2h4H3PVz4zHvtH8aoVlwdV
-ZqW1LS7YgFmypw23RuwhY/81q6UCzyr0TP579ZRdhE2o8mCP2w4lPJ9zcc+U30rq
-299yOIzzlr3xF7zSujtFWsan9sYXiwGd/BmoKoMWuDpI/k4+oKsGGelT84ATB+0t
-vz8KPFUgOSwsAGl0lUq8ILKpeeUYiZGo3BxN77t+Nwtd/jmliFKMAGzsGHxBvfaL
-dXe6YJ2E5/4tAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQD
-AgEGMB0GA1UdDgQWBBQVOIMPPyw/cDMezUb+B4wg4NfDtzANBgkqhkiG9w0BAQUF
-AAOCAQEAX/FBfXxcCLkr4NWSR/pnXKUTwwMhmytMiUbPWU3J/qVAtmPN3XEolWcR
-zCSs00Rsca4BIGsDoo8Ytyk6feUWYFN4PMCvFYP3j1IzJL1kk5fui/fbGKhtcbP3
-LBfQdCVp9/5rPJS+TUtBjE7ic9DjkCJzQ83z7+pzzkWKsKZJ/0x9nXGIxHYdkFsd
-7v3M9+79YKWxehZx0RbQfBI8bGmX265fOZpwLwU8GUYEmSA20GBuYQa7FkKMcPcw
-++DbZqMAAb3mLNqRX6BGi01qnD093QVG/na/oAo85ADmJ7f/hC3euiInlhBx6yLt
-398znM/jra6O1I7mT1GvFpLgXPYHDw==
------END CERTIFICATE-----
-
-# XRamp Global CA Root
------BEGIN CERTIFICATE-----
-MIIEMDCCAxigAwIBAgIQUJRs7Bjq1ZxN1ZfvdY+grTANBgkqhkiG9w0BAQUFADCB
-gjELMAkGA1UEBhMCVVMxHjAcBgNVBAsTFXd3dy54cmFtcHNlY3VyaXR5LmNvbTEk
-MCIGA1UEChMbWFJhbXAgU2VjdXJpdHkgU2VydmljZXMgSW5jMS0wKwYDVQQDEyRY
-UmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQxMTAxMTcx
-NDA0WhcNMzUwMTAxMDUzNzE5WjCBgjELMAkGA1UEBhMCVVMxHjAcBgNVBAsTFXd3
-dy54cmFtcHNlY3VyaXR5LmNvbTEkMCIGA1UEChMbWFJhbXAgU2VjdXJpdHkgU2Vy
-dmljZXMgSW5jMS0wKwYDVQQDEyRYUmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBB
-dXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCYJB69FbS6
-38eMpSe2OAtp87ZOqCwuIR1cRN8hXX4jdP5efrRKt6atH67gBhbim1vZZ3RrXYCP
-KZ2GG9mcDZhtdhAoWORlsH9KmHmf4MMxfoArtYzAQDsRhtDLooY2YKTVMIJt2W7Q
-DxIEM5dfT2Fa8OT5kavnHTu86M/0ay00fOJIYRyO82FEzG+gSqmUsE3a56k0enI4
-qEHMPJQRfevIpoy3hsvKMzvZPTeL+3o+hiznc9cKV6xkmxnr9A8ECIqsAxcZZPRa
-JSKNNCyy9mgdEm3Tih4U2sSPpuIjhdV6Db1q4Ons7Be7QhtnqiXtRYMh/MHJfNVi
-PvryxS3T/dRlAgMBAAGjgZ8wgZwwEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0P
-BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFMZPoj0GY4QJnM5i5ASs
-jVy16bYbMDYGA1UdHwQvMC0wK6ApoCeGJWh0dHA6Ly9jcmwueHJhbXBzZWN1cml0
-eS5jb20vWEdDQS5jcmwwEAYJKwYBBAGCNxUBBAMCAQEwDQYJKoZIhvcNAQEFBQAD
-ggEBAJEVOQMBG2f7Shz5CmBbodpNl2L5JFMn14JkTpAuw0kbK5rc/Kh4ZzXxHfAR
-vbdI4xD2Dd8/0sm2qlWkSLoC295ZLhVbO50WfUfXN+pfTXYSNrsf16GBBEYgoyxt
-qZ4Bfj8pzgCT3/3JknOJiWSe5yvkHJEs0rnOfc5vMZnT5r7SHpDwCRR5XCOrTdLa
-IR9NmXmd4c8nnxCbHIgNsIpkQTG4DmyQJKSbXHGPurt+HBvbaoAPIbzp26a3QPSy
-i6mx5O+aGtA9aZnuqCij4Tyz8LIRnM98QObd50N9otg6tamN8jSZxNQQ4Qb9CYQQ
-O+7ETPTsJ3xCwnR8gooJybQDJbw=
------END CERTIFICATE-----
-
-# CAcert Class 3 Root
------BEGIN CERTIFICATE-----
-MIIHWTCCBUGgAwIBAgIDCkGKMA0GCSqGSIb3DQEBCwUAMHkxEDAOBgNVBAoTB1Jv
-b3QgQ0ExHjAcBgNVBAsTFWh0dHA6Ly93d3cuY2FjZXJ0Lm9yZzEiMCAGA1UEAxMZ
-Q0EgQ2VydCBTaWduaW5nIEF1dGhvcml0eTEhMB8GCSqGSIb3DQEJARYSc3VwcG9y
-dEBjYWNlcnQub3JnMB4XDTExMDUyMzE3NDgwMloXDTIxMDUyMDE3NDgwMlowVDEU
-MBIGA1UEChMLQ0FjZXJ0IEluYy4xHjAcBgNVBAsTFWh0dHA6Ly93d3cuQ0FjZXJ0
-Lm9yZzEcMBoGA1UEAxMTQ0FjZXJ0IENsYXNzIDMgUm9vdDCCAiIwDQYJKoZIhvcN
-AQEBBQADggIPADCCAgoCggIBAKtJNRFIfNImflOUz0Op3SjXQiqL84d4GVh8D57a
-iX3h++tykA10oZZkq5+gJJlz2uJVdscXe/UErEa4w75/ZI0QbCTzYZzA8pD6Ueb1
-aQFjww9W4kpCz+JEjCUoqMV5CX1GuYrz6fM0KQhF5Byfy5QEHIGoFLOYZcRD7E6C
-jQnRvapbjZLQ7N6QxX8KwuPr5jFaXnQ+lzNZ6MMDPWAzv/fRb0fEze5ig1JuLgia
-pNkVGJGmhZJHsK5I6223IeyFGmhyNav/8BBdwPSUp2rVO5J+TJAFfpPBLIukjmJ0
-FXFuC3ED6q8VOJrU0gVyb4z5K+taciX5OUbjchs+BMNkJyIQKopPWKcDrb60LhPt
-XapI19V91Cp7XPpGBFDkzA5CW4zt2/LP/JaT4NsRNlRiNDiPDGCbO5dWOK3z0luL
-oFvqTpa4fNfVoIZwQNORKbeiPK31jLvPGpKK5DR7wNhsX+kKwsOnIJpa3yxdUly6
-R9Wb7yQocDggL9V/KcCyQQNokszgnMyXS0XvOhAKq3A6mJVwrTWx6oUrpByAITGp
-rmB6gCZIALgBwJNjVSKRPFbnr9s6JfOPMVTqJouBWfmh0VMRxXudA/Z0EeBtsSw/
-LIaRmXGapneLNGDRFLQsrJ2vjBDTn8Rq+G8T/HNZ92ZCdB6K4/jc0m+YnMtHmJVA
-BfvpAgMBAAGjggINMIICCTAdBgNVHQ4EFgQUdahxYEyIE/B42Yl3tW3Fid+8sXow
-gaMGA1UdIwSBmzCBmIAUFrUyG9TH8+DmjvO90rA67rI5GNGhfaR7MHkxEDAOBgNV
-BAoTB1Jvb3QgQ0ExHjAcBgNVBAsTFWh0dHA6Ly93d3cuY2FjZXJ0Lm9yZzEiMCAG
-A1UEAxMZQ0EgQ2VydCBTaWduaW5nIEF1dGhvcml0eTEhMB8GCSqGSIb3DQEJARYS
-c3VwcG9ydEBjYWNlcnQub3JnggEAMA8GA1UdEwEB/wQFMAMBAf8wXQYIKwYBBQUH
-AQEEUTBPMCMGCCsGAQUFBzABhhdodHRwOi8vb2NzcC5DQWNlcnQub3JnLzAoBggr
-BgEFBQcwAoYcaHR0cDovL3d3dy5DQWNlcnQub3JnL2NhLmNydDBKBgNVHSAEQzBB
-MD8GCCsGAQQBgZBKMDMwMQYIKwYBBQUHAgEWJWh0dHA6Ly93d3cuQ0FjZXJ0Lm9y
-Zy9pbmRleC5waHA/aWQ9MTAwNAYJYIZIAYb4QgEIBCcWJWh0dHA6Ly93d3cuQ0Fj
-ZXJ0Lm9yZy9pbmRleC5waHA/aWQ9MTAwUAYJYIZIAYb4QgENBEMWQVRvIGdldCB5
-b3VyIG93biBjZXJ0aWZpY2F0ZSBmb3IgRlJFRSwgZ28gdG8gaHR0cDovL3d3dy5D
-QWNlcnQub3JnMA0GCSqGSIb3DQEBCwUAA4ICAQApKIWuRKm5r6R5E/CooyuXYPNc
-7uMvwfbiZqARrjY3OnYVBFPqQvX56sAV2KaC2eRhrnILKVyQQ+hBsuF32wITRHhH
-Va9Y/MyY9kW50SD42CEH/m2qc9SzxgfpCYXMO/K2viwcJdVxjDm1Luq+GIG6sJO4
-D+Pm1yaMMVpyA4RS5qb1MyJFCsgLDYq4Nm+QCaGrvdfVTi5xotSu+qdUK+s1jVq3
-VIgv7nSf7UgWyg1I0JTTrKSi9iTfkuO960NAkW4cGI5WtIIS86mTn9S8nK2cde5a
-lxuV53QtHA+wLJef+6kzOXrnAzqSjiL2jA3k2X4Ndhj3AfnvlpaiVXPAPHG0HRpW
-Q7fDCo1y/OIQCQtBzoyUoPkD/XFzS4pXM+WOdH4VAQDmzEoc53+VGS3FpQyLu7Xt
-hbNc09+4ufLKxw0BFKxwWMWMjTPUnWajGlCVI/xI4AZDEtnNp4Y5LzZyo4AQ5OHz
-0ctbGsDkgJp8E3MGT9ujayQKurMcvEp4u+XjdTilSKeiHq921F73OIZWWonO1sOn
-ebJSoMbxhbQljPI/lrMQ2Y1sVzufb4Y6GIIiNsiwkTjbKqGTqoQ/9SdlrnPVyNXT
-d+pLncdBu8fA46A/5H2kjXPmEkvfoXNzczqA6NXLji/L6hOn1kGLrPo8idck9U60
-4GGSt/M3mMS+lqO3ig==
------END CERTIFICATE-----
-
-# CA Cert Signing Authority
------BEGIN CERTIFICATE-----
-MIIHPTCCBSWgAwIBAgIBADANBgkqhkiG9w0BAQQFADB5MRAwDgYDVQQKEwdSb290
-IENBMR4wHAYDVQQLExVodHRwOi8vd3d3LmNhY2VydC5vcmcxIjAgBgNVBAMTGUNB
-IENlcnQgU2lnbmluZyBBdXRob3JpdHkxITAfBgkqhkiG9w0BCQEWEnN1cHBvcnRA
-Y2FjZXJ0Lm9yZzAeFw0wMzAzMzAxMjI5NDlaFw0zMzAzMjkxMjI5NDlaMHkxEDAO
-BgNVBAoTB1Jvb3QgQ0ExHjAcBgNVBAsTFWh0dHA6Ly93d3cuY2FjZXJ0Lm9yZzEi
-MCAGA1UEAxMZQ0EgQ2VydCBTaWduaW5nIEF1dGhvcml0eTEhMB8GCSqGSIb3DQEJ
-ARYSc3VwcG9ydEBjYWNlcnQub3JnMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC
-CgKCAgEAziLA4kZ97DYoB1CW8qAzQIxL8TtmPzHlawI229Z89vGIj053NgVBlfkJ
-8BLPRoZzYLdufujAWGSuzbCtRRcMY/pnCujW0r8+55jE8Ez64AO7NV1sId6eINm6
-zWYyN3L69wj1x81YyY7nDl7qPv4coRQKFWyGhFtkZip6qUtTefWIonvuLwphK42y
-fk1WpRPs6tqSnqxEQR5YYGUFZvjARL3LlPdCfgv3ZWiYUQXw8wWRBB0bF4LsyFe7
-w2t6iPGwcswlWyCR7BYCEo8y6RcYSNDHBS4CMEK4JZwFaz+qOqfrU0j36NK2B5jc
-G8Y0f3/JHIJ6BVgrCFvzOKKrF11myZjXnhCLotLddJr3cQxyYN/Nb5gznZY0dj4k
-epKwDpUeb+agRThHqtdB7Uq3EvbXG4OKDy7YCbZZ16oE/9KTfWgu3YtLq1i6L43q
-laegw1SJpfvbi1EinbLDvhG+LJGGi5Z4rSDTii8aP8bQUWWHIbEZAWV/RRyH9XzQ
-QUxPKZgh/TMfdQwEUfoZd9vUFBzugcMd9Zi3aQaRIt0AUMyBMawSB3s42mhb5ivU
-fslfrejrckzzAeVLIL+aplfKkQABi6F1ITe1Yw1nPkZPcCBnzsXWWdsC4PDSy826
-YreQQejdIOQpvGQpQsgi3Hia/0PsmBsJUUtaWsJx8cTLc6nloQsCAwEAAaOCAc4w
-ggHKMB0GA1UdDgQWBBQWtTIb1Mfz4OaO873SsDrusjkY0TCBowYDVR0jBIGbMIGY
-gBQWtTIb1Mfz4OaO873SsDrusjkY0aF9pHsweTEQMA4GA1UEChMHUm9vdCBDQTEe
-MBwGA1UECxMVaHR0cDovL3d3dy5jYWNlcnQub3JnMSIwIAYDVQQDExlDQSBDZXJ0
-IFNpZ25pbmcgQXV0aG9yaXR5MSEwHwYJKoZIhvcNAQkBFhJzdXBwb3J0QGNhY2Vy
-dC5vcmeCAQAwDwYDVR0TAQH/BAUwAwEB/zAyBgNVHR8EKzApMCegJaAjhiFodHRw
-czovL3d3dy5jYWNlcnQub3JnL3Jldm9rZS5jcmwwMAYJYIZIAYb4QgEEBCMWIWh0
-dHBzOi8vd3d3LmNhY2VydC5vcmcvcmV2b2tlLmNybDA0BglghkgBhvhCAQgEJxYl
-aHR0cDovL3d3dy5jYWNlcnQub3JnL2luZGV4LnBocD9pZD0xMDBWBglghkgBhvhC
-AQ0ESRZHVG8gZ2V0IHlvdXIgb3duIGNlcnRpZmljYXRlIGZvciBGUkVFIGhlYWQg
-b3ZlciB0byBodHRwOi8vd3d3LmNhY2VydC5vcmcwDQYJKoZIhvcNAQEEBQADggIB
-ACjH7pyCArpcgBLKNQodgW+JapnM8mgPf6fhjViVPr3yBsOQWqy1YPaZQwGjiHCc
-nWKdpIevZ1gNMDY75q1I08t0AoZxPuIrA2jxNGJARjtT6ij0rPtmlVOKTV39O9lg
-18p5aTuxZZKmxoGCXJzN600BiqXfEVWqFcofN8CCmHBh22p8lqOOLlQ+TyGpkO/c
-gr/c6EWtTZBzCDyUZbAEmXZ/4rzCahWqlwQ3JNgelE5tDlG+1sSPypZt90Pf6DBl
-Jzt7u0NDY8RD97LsaMzhGY4i+5jhe1o+ATc7iwiwovOVThrLm82asduycPAtStvY
-sONvRUgzEv/+PDIqVPfE94rwiCPCR/5kenHA0R6mY7AHfqQv0wGP3J8rtsYIqQ+T
-SCX8Ev2fQtzzxD72V7DX3WnRBnc0CkvSyqD/HMaMyRa+xMwyN2hzXwj7UfdJUzYF
-CpUCTPJ5GhD22Dp1nPMd8aINcGeGG7MW9S/lpOt5hvk9C8JzC6WZrG/8Z7jlLwum
-GCSNe9FINSkYQKyTYOGWhlC0elnYjyELn8+CkcY7v2vcB5G5l1YjqrZslMZIBjzk
-zk6q5PYvCdxTby78dOs6Y5nCpqyJvKeyRKANihDjbPIky/qbn3BHLt4Ui9SyIAmW
-omTxJBzcoTWcFbLUvFUufQb1nA5V9FrWk9p2rSVzTMVD
------END CERTIFICATE-----
diff --git a/test/integration/targets/certificate_complete_chain/files/roots/COMODO_Certification_Authority.pem b/test/integration/targets/certificate_complete_chain/files/roots/COMODO_Certification_Authority.pem
deleted file mode 100644
index 6146dcb571..0000000000
--- a/test/integration/targets/certificate_complete_chain/files/roots/COMODO_Certification_Authority.pem
+++ /dev/null
@@ -1,25 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIEHTCCAwWgAwIBAgIQToEtioJl4AsC7j41AkblPTANBgkqhkiG9w0BAQUFADCB
-gTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G
-A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxJzAlBgNV
-BAMTHkNPTU9ETyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjEyMDEwMDAw
-MDBaFw0yOTEyMzEyMzU5NTlaMIGBMQswCQYDVQQGEwJHQjEbMBkGA1UECBMSR3Jl
-YXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHEwdTYWxmb3JkMRowGAYDVQQKExFDT01P
-RE8gQ0EgTGltaXRlZDEnMCUGA1UEAxMeQ09NT0RPIENlcnRpZmljYXRpb24gQXV0
-aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0ECLi3LjkRv3
-UcEbVASY06m/weaKXTuH+7uIzg3jLz8GlvCiKVCZrts7oVewdFFxze1CkU1B/qnI
-2GqGd0S7WWaXUF601CxwRM/aN5VCaTwwxHGzUvAhTaHYujl8HJ6jJJ3ygxaYqhZ8
-Q5sVW7euNJH+1GImGEaaP+vB+fGQV+useg2L23IwambV4EajcNxo2f8ESIl33rXp
-+2dtQem8Ob0y2WIC8bGoPW43nOIv4tOiJovGuFVDiOEjPqXSJDlqR6sA1KGzqSX+
-DT+nHbrTUcELpNqsOO9VUCQFZUaTNE8tja3G1CEZ0o7KBWFxB3NH5YoZEr0ETc5O
-nKVIrLsm9wIDAQABo4GOMIGLMB0GA1UdDgQWBBQLWOWLxkwVN6RAqTCpIb5HNlpW
-/zAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zBJBgNVHR8EQjBAMD6g
-PKA6hjhodHRwOi8vY3JsLmNvbW9kb2NhLmNvbS9DT01PRE9DZXJ0aWZpY2F0aW9u
-QXV0aG9yaXR5LmNybDANBgkqhkiG9w0BAQUFAAOCAQEAPpiem/Yb6dc5t3iuHXIY
-SdOH5EOC6z/JqvWote9VfCFSZfnVDeFs9D6Mk3ORLgLETgdxb8CPOGEIqB6BCsAv
-IC9Bi5HcSEW88cbeunZrM8gALTFGTO3nnc+IlP8zwFboJIYmuNg4ON8qa90SzMc/
-RxdMosIGlgnW2/4/PEZB31jiVg88O8EckzXZOFKs7sjsLjBOlDW0JB9LeGna8gI4
-zJVSk/BwJVmcIGfE7vmLV2H0knZ9P4SNVbfo5azV8fUZVqZa+5Acr5Pr5RzUZ5dd
-BA6+C4OmF4O5MBKgxTMVBbkN+8cFduPYSo38NBejxiEovjBFMR7HeL5YYTisO+IB
-ZQ==
------END CERTIFICATE-----
diff --git a/test/integration/targets/certificate_complete_chain/files/roots/COMODO_ECC_Certification_Authority.pem b/test/integration/targets/certificate_complete_chain/files/roots/COMODO_ECC_Certification_Authority.pem
deleted file mode 100644
index 546c95e30d..0000000000
--- a/test/integration/targets/certificate_complete_chain/files/roots/COMODO_ECC_Certification_Authority.pem
+++ /dev/null
@@ -1,16 +0,0 @@
------BEGIN CERTIFICATE-----
-MIICiTCCAg+gAwIBAgIQH0evqmIAcFBUTAGem2OZKjAKBggqhkjOPQQDAzCBhTEL
-MAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE
-BxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMT
-IkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDgwMzA2MDAw
-MDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdy
-ZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09N
-T0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlv
-biBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQDR3svdcmCFYX7deSR
-FtSrYpn1PlILBs5BAH+X4QokPB0BBO490o0JlwzgdeT6+3eKKvUDYEs2ixYjFq0J
-cfRK9ChQtP6IHG4/bC8vCVlbpVsLM5niwz2J+Wos77LTBumjQjBAMB0GA1UdDgQW
-BBR1cacZSBm8nZ3qQUfflMRId5nTeTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/
-BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjEA7wNbeqy3eApyt4jf/7VGFAkK+qDm
-fQjGGoe9GKhzvSbKYAydzpmfz1wPMOG+FDHqAjAU9JM8SaczepBGR7NjfRObTrdv
-GDeAU/7dIOA1mjbRxwG55tzd8/8dLDoWV9mSOdY=
------END CERTIFICATE-----
diff --git a/test/integration/targets/certificate_complete_chain/files/roots/COMODO_RSA_Certification_Authority.pem b/test/integration/targets/certificate_complete_chain/files/roots/COMODO_RSA_Certification_Authority.pem
deleted file mode 100644
index 6508d1e8c3..0000000000
--- a/test/integration/targets/certificate_complete_chain/files/roots/COMODO_RSA_Certification_Authority.pem
+++ /dev/null
@@ -1,34 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIF2DCCA8CgAwIBAgIQTKr5yttjb+Af907YWwOGnTANBgkqhkiG9w0BAQwFADCB
-hTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G
-A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNV
-BAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAwMTE5
-MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0IxGzAZBgNVBAgT
-EkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMR
-Q09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNh
-dGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCR
-6FSS0gpWsawNJN3Fz0RndJkrN6N9I3AAcbxT38T6KhKPS38QVr2fcHK3YX/JSw8X
-pz3jsARh7v8Rl8f0hj4K+j5c+ZPmNHrZFGvnnLOFoIJ6dq9xkNfs/Q36nGz637CC
-9BR++b7Epi9Pf5l/tfxnQ3K9DADWietrLNPtj5gcFKt+5eNu/Nio5JIk2kNrYrhV
-/erBvGy2i/MOjZrkm2xpmfh4SDBF1a3hDTxFYPwyllEnvGfDyi62a+pGx8cgoLEf
-Zd5ICLqkTqnyg0Y3hOvozIFIQ2dOciqbXL1MGyiKXCJ7tKuY2e7gUYPDCUZObT6Z
-+pUX2nwzV0E8jVHtC7ZcryxjGt9XyD+86V3Em69FmeKjWiS0uqlWPc9vqv9JWL7w
-qP/0uK3pN/u6uPQLOvnoQ0IeidiEyxPx2bvhiWC4jChWrBQdnArncevPDt09qZah
-SL0896+1DSJMwBGB7FY79tOi4lu3sgQiUpWAk2nojkxl8ZEDLXB0AuqLZxUpaVIC
-u9ffUGpVRr+goyhhf3DQw6KqLCGqR84onAZFdr+CGCe01a60y1Dma/RMhnEw6abf
-Fobg2P9A3fvQQoh/ozM6LlweQRGBY84YcWsr7KaKtzFcOmpH4MN5WdYgGq/yapiq
-crxXStJLnbsQ/LBMQeXtHT1eKJ2czL+zUdqnR+WEUwIDAQABo0IwQDAdBgNVHQ4E
-FgQUu69+Aj36pvE8hI6t7jiY7NkyMtQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB
-/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAArx1UaEt65Ru2yyTUEUAJNMnMvl
-wFTPoCWOAvn9sKIN9SCYPBMtrFaisNZ+EZLpLrqeLppysb0ZRGxhNaKatBYSaVqM
-4dc+pBroLwP0rmEdEBsqpIt6xf4FpuHA1sj+nq6PK7o9mfjYcwlYRm6mnPTXJ9OV
-2jeDchzTc+CiR5kDOF3VSXkAKRzH7JsgHAckaVd4sjn8OoSgtZx8jb8uk2Intzna
-FxiuvTwJaP+EmzzV1gsD41eeFPfR60/IvYcjt7ZJQ3mFXLrrkguhxuhoqEwWsRqZ
-CuhTLJK7oQkYdQxlqHvLI7cawiiFwxv/0Cti76R7CZGYZ4wUAc1oBmpjIXUDgIiK
-boHGhfKppC3n9KUkEEeDys30jXlYsQab5xoq2Z0B15R97QNKyvDb6KkBPvVWmcke
-jkk9u+UJueBPSZI9FoJAzMxZxuY67RIuaTxslbH9qh17f4a+Hg4yRvv7E491f0yL
-S0Zj/gA0QHDBw7mh3aZw4gSzQbzpgJHqZJx64SIDqZxubw5lT2yHh17zbqD5daWb
-QOhTsiedSrnAdyGN/4fy3ryM7xfft0kL0fJuMAsaDk527RH89elWsn2/x20Kk4yl
-0MC2Hb46TpSi125sC8KKfPog88Tk5c0NqMuRkrF8hey1FGlmDoLnzc7ILaZRfyHB
-NVOFBkpdn627G190
------END CERTIFICATE-----
diff --git a/test/integration/targets/certificate_complete_chain/files/roots/DST_Root_CA_X3.pem b/test/integration/targets/certificate_complete_chain/files/roots/DST_Root_CA_X3.pem
deleted file mode 100644
index b2e43c9381..0000000000
--- a/test/integration/targets/certificate_complete_chain/files/roots/DST_Root_CA_X3.pem
+++ /dev/null
@@ -1,20 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIDSjCCAjKgAwIBAgIQRK+wgNajJ7qJMDmGLvhAazANBgkqhkiG9w0BAQUFADA/
-MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT
-DkRTVCBSb290IENBIFgzMB4XDTAwMDkzMDIxMTIxOVoXDTIxMDkzMDE0MDExNVow
-PzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMRcwFQYDVQQD
-Ew5EU1QgUm9vdCBDQSBYMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
-AN+v6ZdQCINXtMxiZfaQguzH0yxrMMpb7NnDfcdAwRgUi+DoM3ZJKuM/IUmTrE4O
-rz5Iy2Xu/NMhD2XSKtkyj4zl93ewEnu1lcCJo6m67XMuegwGMoOifooUMM0RoOEq
-OLl5CjH9UL2AZd+3UWODyOKIYepLYYHsUmu5ouJLGiifSKOeDNoJjj4XLh7dIN9b
-xiqKqy69cK3FCxolkHRyxXtqqzTWMIn/5WgTe1QLyNau7Fqckh49ZLOMxt+/yUFw
-7BZy1SbsOFU5Q9D8/RhcQPGX69Wam40dutolucbY38EVAjqr2m7xPi71XAicPNaD
-aeQQmxkqtilX4+U9m5/wAl0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNV
-HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMSnsaR7LHH62+FLkHX/xBVghYkQMA0GCSqG
-SIb3DQEBBQUAA4IBAQCjGiybFwBcqR7uKGY3Or+Dxz9LwwmglSBd49lZRNI+DT69
-ikugdB/OEIKcdBodfpga3csTS7MgROSR6cz8faXbauX+5v3gTt23ADq1cEmv8uXr
-AvHRAosZy5Q6XkjEGB5YGV8eAlrwDPGxrancWYaLbumR9YbK+rlmM6pZW87ipxZz
-R8srzJmwN0jP41ZL9c8PDHIyh8bwRLtTcm1D9SZImlJnt1ir/md2cXjbDaJWFBM5
-JDGFoqgCWjBH4d1QB7wCCZAA62RjYJsWvIjJEubSfZGL+T0yjWW06XyxV3bqxbYo
-Ob8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ
------END CERTIFICATE-----
diff --git a/test/integration/targets/certificate_complete_chain/files/roots/ISRG_Root_X1.pem b/test/integration/targets/certificate_complete_chain/files/roots/ISRG_Root_X1.pem
deleted file mode 100644
index b85c8037f6..0000000000
--- a/test/integration/targets/certificate_complete_chain/files/roots/ISRG_Root_X1.pem
+++ /dev/null
@@ -1,31 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw
-TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
-cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4
-WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu
-ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY
-MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc
-h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+
-0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U
-A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW
-T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH
-B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC
-B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv
-KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn
-OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn
-jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw
-qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI
-rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV
-HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq
-hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL
-ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ
-3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK
-NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5
-ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur
-TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC
-jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc
-oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq
-4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA
-mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d
-emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=
------END CERTIFICATE-----
diff --git a/test/integration/targets/certificate_complete_chain/meta/main.yml b/test/integration/targets/certificate_complete_chain/meta/main.yml
deleted file mode 100644
index 1810d4bec9..0000000000
--- a/test/integration/targets/certificate_complete_chain/meta/main.yml
+++ /dev/null
@@ -1,2 +0,0 @@
-dependencies:
- - setup_remote_tmp_dir
diff --git a/test/integration/targets/certificate_complete_chain/tasks/main.yml b/test/integration/targets/certificate_complete_chain/tasks/main.yml
deleted file mode 100644
index 95a1d0514b..0000000000
--- a/test/integration/targets/certificate_complete_chain/tasks/main.yml
+++ /dev/null
@@ -1,97 +0,0 @@
----
-- name: register cryptography version
- command: "{{ ansible_python.executable }} -c 'import cryptography; print(cryptography.__version__)'"
- register: cryptography_version
-
-- block:
- - name: Archive test files
- archive:
- path: "{{ role_path }}/files/"
- dest: "{{ output_dir }}/files.tgz"
-
- - name: Create temporary directory to store files
- file:
- state: directory
- path: "{{ remote_tmp_dir }}/files/"
-
- - name: Unarchive test files on testhost
- unarchive:
- src: "{{ output_dir }}/files.tgz"
- dest: "{{ remote_tmp_dir }}/files/"
-
- # Cert 1: certificate for www.ansible.com, retrieved on 2018-08-15
- - name: Find root for cert 1
- certificate_complete_chain:
- input_chain: "{{ lookup('file', 'cert1-fullchain.pem', rstrip=False) }}"
- root_certificates:
- - "{{ remote_tmp_dir }}/files/roots/"
- register: cert1_root
- - name: Verify root for cert 1
- assert:
- that:
- - "cert1_root.complete_chain | join('') == (lookup('file', 'cert1.pem', rstrip=False) ~ lookup('file', 'cert1-chain.pem', rstrip=False) ~ lookup('file', 'cert1-root.pem', rstrip=False))"
- - "cert1_root.root == lookup('file', 'cert1-root.pem', rstrip=False)"
-
- - name: Find rootchain for cert 1
- certificate_complete_chain:
- input_chain: "{{ lookup('file', 'cert1.pem', rstrip=False) }}"
- intermediate_certificates:
- - "{{ remote_tmp_dir }}/files/cert1-chain.pem"
- root_certificates:
- - "{{ remote_tmp_dir }}/files/roots.pem"
- register: cert1_rootchain
- - name: Verify rootchain for cert 1
- assert:
- that:
- - "cert1_rootchain.complete_chain | join('') == (lookup('file', 'cert1.pem', rstrip=False) ~ lookup('file', 'cert1-chain.pem', rstrip=False) ~ lookup('file', 'cert1-root.pem', rstrip=False))"
- - "cert1_rootchain.chain[:-1] | join('') == lookup('file', 'cert1-chain.pem', rstrip=False)"
- - "cert1_rootchain.root == lookup('file', 'cert1-root.pem', rstrip=False)"
-
- # Cert 2: certificate for letsencrypt.org, retrieved on 2018-08-15
- # Intermediate: cross-signed by IdenTrust
- - name: Find root for cert 2
- certificate_complete_chain:
- input_chain: "{{ lookup('file', 'cert2-fullchain.pem', rstrip=False) }}"
- root_certificates:
- - "{{ remote_tmp_dir }}/files/roots/"
- register: cert2_root
- - name: Verify root for cert 2
- assert:
- that:
- - "cert2_root.complete_chain | join('') == (lookup('file', 'cert2.pem', rstrip=False) ~ lookup('file', 'cert2-chain.pem', rstrip=False) ~ lookup('file', 'cert2-root.pem', rstrip=False))"
- - "cert2_root.root == lookup('file', 'cert2-root.pem', rstrip=False)"
-
- - name: Find rootchain for cert 2
- certificate_complete_chain:
- input_chain: "{{ lookup('file', 'cert2.pem', rstrip=False) }}"
- intermediate_certificates:
- - "{{ remote_tmp_dir }}/files/cert2-chain.pem"
- root_certificates:
- - "{{ remote_tmp_dir }}/files/roots.pem"
- register: cert2_rootchain
- - name: Verify rootchain for cert 2
- assert:
- that:
- - "cert2_rootchain.complete_chain | join('') == (lookup('file', 'cert2.pem', rstrip=False) ~ lookup('file', 'cert2-chain.pem', rstrip=False) ~ lookup('file', 'cert2-root.pem', rstrip=False))"
- - "cert2_rootchain.chain[:-1] | join('') == lookup('file', 'cert2-chain.pem', rstrip=False)"
- - "cert2_rootchain.root == lookup('file', 'cert2-root.pem', rstrip=False)"
-
- # Cert 2: certificate for letsencrypt.org, retrieved on 2018-08-15
- # Intermediate: signed by ISRG root
- - name: Find alternate rootchain for cert 2
- certificate_complete_chain:
- # Remove line ending, make sure it is re-added by code
- input_chain: "{{ lookup('file', 'cert2.pem', rstrip=True) }}"
- intermediate_certificates:
- - "{{ remote_tmp_dir }}/files/cert2-altchain.pem"
- root_certificates:
- - "{{ remote_tmp_dir }}/files/roots.pem"
- register: cert2_rootchain_alt
- - name: Verify rootchain for cert 2
- assert:
- that:
- - "cert2_rootchain_alt.complete_chain | join('') == (lookup('file', 'cert2.pem', rstrip=False) ~ lookup('file', 'cert2-altchain.pem', rstrip=False) ~ lookup('file', 'cert2-altroot.pem', rstrip=False))"
- - "cert2_rootchain_alt.chain[:-1] | join('') == lookup('file', 'cert2-altchain.pem', rstrip=False)"
- - "cert2_rootchain_alt.root == lookup('file', 'cert2-altroot.pem', rstrip=False)"
-
- when: cryptography_version.stdout is version('1.5', '>=')
diff --git a/test/integration/targets/ecs_certificate/aliases b/test/integration/targets/ecs_certificate/aliases
deleted file mode 100644
index f320bbb3fb..0000000000
--- a/test/integration/targets/ecs_certificate/aliases
+++ /dev/null
@@ -1,15 +0,0 @@
-# Not enabled due to lack of access to test environments. May be enabled using custom integration_config.yml
-# Example integation_config.yml
-# ---
-# entrust_api_user:
-# entrust_api_key:
-# entrust_api_client_cert_path: /var/integration-testing/publicCert.pem
-# entrust_api_client_cert_key_path: /var/integration-testing/privateKey.pem
-# entrust_api_ip_address: 127.0.0.1
-# entrust_cloud_ip_address: 127.0.0.1
-# # Used for certificate path validation of QA environments - we chose not to support disabling path validation ever.
-# cacerts_bundle_path_local: /var/integration-testing/cacerts
-
-### WARNING: This test will update HOSTS file and CERTIFICATE STORE of target host, in order to be able to validate
-# to a QA environment. ###
-unsupported
diff --git a/test/integration/targets/ecs_certificate/defaults/main.yml b/test/integration/targets/ecs_certificate/defaults/main.yml
deleted file mode 100644
index 86bc36c32c..0000000000
--- a/test/integration/targets/ecs_certificate/defaults/main.yml
+++ /dev/null
@@ -1,2 +0,0 @@
----
-# defaults file for test_ecs_certificate
diff --git a/test/integration/targets/ecs_certificate/meta/main.yml b/test/integration/targets/ecs_certificate/meta/main.yml
deleted file mode 100644
index a35b330677..0000000000
--- a/test/integration/targets/ecs_certificate/meta/main.yml
+++ /dev/null
@@ -1,3 +0,0 @@
-dependencies:
- - prepare_tests
- - setup_openssl
diff --git a/test/integration/targets/ecs_certificate/tasks/main.yml b/test/integration/targets/ecs_certificate/tasks/main.yml
deleted file mode 100644
index 9cc9b8cf09..0000000000
--- a/test/integration/targets/ecs_certificate/tasks/main.yml
+++ /dev/null
@@ -1,215 +0,0 @@
----
-## Verify that integration_config was specified
-- block:
- - assert:
- that:
- - entrust_api_user is defined
- - entrust_api_key is defined
- - entrust_api_ip_address is defined
- - entrust_cloud_ip_address is defined
- - entrust_api_client_cert_path is defined or entrust_api_client_cert_contents is defined
- - entrust_api_client_cert_key_path is defined or entrust_api_client_cert_key_contents
- - cacerts_bundle_path_local is defined
-
-## SET UP TEST ENVIRONMENT ########################################################################
-- name: copy the files needed for verifying test server certificate to the host
- copy:
- src: '{{ cacerts_bundle_path_local }}/'
- dest: '{{ cacerts_bundle_path }}'
-
-- name: Update the CA certificates for our QA certs (collection may need updating if new QA environments used)
- command: c_rehash {{ cacerts_bundle_path }}
-
-- name: Update hosts file
- lineinfile:
- path: /etc/hosts
- state: present
- regexp: 'api.entrust.net$'
- line: '{{ entrust_api_ip_address }} api.entrust.net'
-
-- name: Update hosts file
- lineinfile:
- path: /etc/hosts
- state: present
- regexp: 'cloud.entrust.net$'
- line: '{{ entrust_cloud_ip_address }} cloud.entrust.net'
-
-- name: Clear out the temporary directory for storing the API connection information
- file:
- path: '{{ tmpdir_path }}'
- state: absent
-
-- name: Create a directory for storing the API connection Information
- file:
- path: '{{ tmpdir_path }}'
- state: directory
-
-- name: Copy the files needed for the connection to entrust API to the host
- copy:
- src: '{{ entrust_api_client_cert_path }}'
- dest: '{{ entrust_api_cert }}'
-
-- name: Copy the files needed for the connection to entrust API to the host
- copy:
- src: '{{ entrust_api_client_cert_key_path }}'
- dest: '{{ entrust_api_cert_key }}'
-
-## SETUP CSR TO REQUEST
-- name: Generate a 2048 bit RSA private key
- openssl_privatekey:
- path: '{{ privatekey_path }}'
- passphrase: '{{ privatekey_passphrase }}'
- cipher: auto
- type: RSA
- size: 2048
-
-- name: Generate a certificate signing request using the generated key
- openssl_csr:
- path: '{{ csr_path }}'
- privatekey_path: '{{ privatekey_path }}'
- privatekey_passphrase: '{{ privatekey_passphrase }}'
- common_name: '{{ common_name }}'
- organization_name: '{{ organization_name | default(omit) }}'
- organizational_unit_name: '{{ organizational_unit_name | default(omit) }}'
- country_name: '{{ country_name | default(omit) }}'
- state_or_province_name: '{{ state_or_province_name | default(omit) }}'
- digest: sha256
-
-- block:
- - name: Have ECS generate a signed certificate
- ecs_certificate:
- backup: True
- path: '{{ example1_cert_path }}'
- full_chain_path: '{{ example1_chain_path }}'
- csr: '{{ csr_path }}'
- cert_type: '{{ example1_cert_type }}'
- requester_name: '{{ entrust_requester_name }}'
- requester_email: '{{ entrust_requester_email }}'
- requester_phone: '{{ entrust_requester_phone }}'
- entrust_api_user: '{{ entrust_api_user }}'
- entrust_api_key: '{{ entrust_api_key }}'
- entrust_api_client_cert_path: '{{ entrust_api_cert }}'
- entrust_api_client_cert_key_path: '{{ entrust_api_cert_key }}'
- register: example1_result
-
- - assert:
- that:
- - example1_result is not failed
- - example1_result.changed
- - example1_result.tracking_id > 0
- - example1_result.serial_number is string
-
- # Internal CA refuses to issue certificates with the same DN in a short time frame
- - name: Sleep for 5 seconds so we don't run into duplicate-request errors
- pause:
- seconds: 5
-
- - name: Attempt to have ECS generate a signed certificate, but existing one is valid
- ecs_certificate:
- backup: True
- path: '{{ example1_cert_path }}'
- full_chain_path: '{{ example1_chain_path }}'
- csr: '{{ csr_path }}'
- cert_type: '{{ example1_cert_type }}'
- requester_name: '{{ entrust_requester_name }}'
- requester_email: '{{ entrust_requester_email }}'
- requester_phone: '{{ entrust_requester_phone }}'
- entrust_api_user: '{{ entrust_api_user }}'
- entrust_api_key: '{{ entrust_api_key }}'
- entrust_api_client_cert_path: '{{ entrust_api_cert }}'
- entrust_api_client_cert_key_path: '{{ entrust_api_cert_key }}'
- register: example2_result
-
- - assert:
- that:
- - example2_result is not failed
- - not example2_result.changed
- - example2_result.backup_file is undefined
- - example2_result.backup_full_chain_file is undefined
- - example2_result.serial_number == example1_result.serial_number
- - example2_result.tracking_id == example1_result.tracking_id
-
- # Internal CA refuses to issue certificates with the same DN in a short time frame
- - name: Sleep for 5 seconds so we don't run into duplicate-request errors
- pause:
- seconds: 5
-
- - name: Force a reissue with no CSR, verify that contents changed
- ecs_certificate:
- backup: True
- force: True
- path: '{{ example1_cert_path }}'
- full_chain_path: '{{ example1_chain_path }}'
- cert_type: '{{ example1_cert_type }}'
- request_type: reissue
- requester_name: '{{ entrust_requester_name }}'
- requester_email: '{{ entrust_requester_email }}'
- requester_phone: '{{ entrust_requester_phone }}'
- entrust_api_user: '{{ entrust_api_user }}'
- entrust_api_key: '{{ entrust_api_key }}'
- entrust_api_client_cert_path: '{{ entrust_api_cert }}'
- entrust_api_client_cert_key_path: '{{ entrust_api_cert_key }}'
- register: example3_result
-
- - assert:
- that:
- - example3_result is not failed
- - example3_result.changed
- - example3_result.backup_file is string
- - example3_result.backup_full_chain_file is string
- - example3_result.tracking_id > 0
- - example3_result.tracking_id != example1_result.tracking_id
- - example3_result.serial_number != example1_result.serial_number
-
- # Internal CA refuses to issue certificates with the same DN in a short time frame
- - name: Sleep for 5 seconds so we don't run into duplicate-request errors
- pause:
- seconds: 5
-
- - name: Test a request with all of the various optional possible fields populated
- ecs_certificate:
- path: '{{ example4_cert_path }}'
- full_chain_path: '{{ example4_full_chain_path }}'
- csr: '{{ csr_path }}'
- subject_alt_name: '{{ example4_subject_alt_name }}'
- eku: '{{ example4_eku }}'
- ct_log: True
- cert_type: '{{ example4_cert_type }}'
- org: '{{ example4_org }}'
- ou: '{{ example4_ou }}'
- tracking_info: '{{ example4_tracking_info }}'
- additional_emails: '{{ example4_additional_emails }}'
- custom_fields: '{{ example4_custom_fields }}'
- cert_expiry: '{{ example4_cert_expiry }}'
- requester_name: '{{ entrust_requester_name }}'
- requester_email: '{{ entrust_requester_email }}'
- requester_phone: '{{ entrust_requester_phone }}'
- entrust_api_user: '{{ entrust_api_user }}'
- entrust_api_key: '{{ entrust_api_key }}'
- entrust_api_client_cert_path: '{{ entrust_api_cert }}'
- entrust_api_client_cert_key_path: '{{ entrust_api_cert_key }}'
- register: example4_result
-
- - assert:
- that:
- - example4_result is not failed
- - example4_result.changed
- - example4_result.backup_file is undefined
- - example4_result.backup_full_chain_file is undefined
- - example4_result.tracking_id > 0
- - example4_result.serial_number is string
-
- # For bug 61738, verify that the full chain is valid
- - name: Verify that the full chain path can be successfully imported
- command: openssl verify "{{ example4_full_chain_path }}"
- register: openssl_result
-
- - assert:
- that:
- - "' OK' in openssl_result.stdout_lines[0]"
-
- always:
- - name: clean-up temporary folder
- file:
- path: '{{ tmpdir_path }}'
- state: absent
diff --git a/test/integration/targets/ecs_certificate/vars/main.yml b/test/integration/targets/ecs_certificate/vars/main.yml
deleted file mode 100644
index 8e61761849..0000000000
--- a/test/integration/targets/ecs_certificate/vars/main.yml
+++ /dev/null
@@ -1,52 +0,0 @@
----
-# vars file for test_ecs_certificate
-
-# Path on various hosts that cacerts need to be put as a prerequisite to API server cert validation.
-# May need to be customized for some environments based on SSL implementations
-# that ansible "urls" module utility is using as a backing.
-cacerts_bundle_path: /etc/pki/tls/certs
-
-common_name: '{{ ansible_date_time.epoch }}.ansint.testcertificates.com'
-organization_name: CMS API, Inc.
-organizational_unit_name: RSA
-country_name: US
-state_or_province_name: MA
-privatekey_passphrase: Passphrase452!
-tmpdir_path: /tmp/ecs_cert_test/{{ ansible_date_time.epoch }}
-privatekey_path: '{{ tmpdir_path }}/testcertificates.key'
-entrust_api_cert: '{{ tmpdir_path }}/authcert.cer'
-entrust_api_cert_key: '{{ tmpdir_path }}/authkey.cer'
-csr_path: '{{ tmpdir_path }}/request.csr'
-
-entrust_requester_name: C Trufan
-entrust_requester_email: CTIntegrationTests@entrustdatacard.com
-entrust_requester_phone: 1-555-555-5555 # e.g. 15555555555
-
-# TEST 1
-example1_cert_path: '{{ tmpdir_path }}/issuedcert_1.pem'
-example1_chain_path: '{{ tmpdir_path }}/issuedcert_1_chain.pem'
-example1_cert_type: EV_SSL
-
-example4_cert_path: '{{ tmpdir_path }}/issuedcert_2.pem'
-example4_subject_alt_name:
- - ansible.testcertificates.com
- - www.testcertificates.com
-example4_eku: SERVER_AND_CLIENT_AUTH
-example4_cert_type: UC_SSL
-# Test a secondary org and special characters
-example4_org: Cañon City, Inc.
-example4_ou:
- - StringrsaString
-example4_tracking_info: Submitted via Ansible Integration
-example4_additional_emails:
- - itsupport@testcertificates.com
- - jsmith@ansible.com
-example4_custom_fields:
- text1: Admin
- text2: Invoice 25
- number1: 342
- date3: '2018-01-01'
- email2: sales@ansible.testcertificates.com
- dropdown2: Dropdown 2 Value 1
-example4_cert_expiry: 2020-08-15
-example4_full_chain_path: '{{ tmpdir_path }}/issuedcert_2_chain.pem'
diff --git a/test/integration/targets/ecs_domain/aliases b/test/integration/targets/ecs_domain/aliases
deleted file mode 100644
index f320bbb3fb..0000000000
--- a/test/integration/targets/ecs_domain/aliases
+++ /dev/null
@@ -1,15 +0,0 @@
-# Not enabled due to lack of access to test environments. May be enabled using custom integration_config.yml
-# Example integation_config.yml
-# ---
-# entrust_api_user:
-# entrust_api_key:
-# entrust_api_client_cert_path: /var/integration-testing/publicCert.pem
-# entrust_api_client_cert_key_path: /var/integration-testing/privateKey.pem
-# entrust_api_ip_address: 127.0.0.1
-# entrust_cloud_ip_address: 127.0.0.1
-# # Used for certificate path validation of QA environments - we chose not to support disabling path validation ever.
-# cacerts_bundle_path_local: /var/integration-testing/cacerts
-
-### WARNING: This test will update HOSTS file and CERTIFICATE STORE of target host, in order to be able to validate
-# to a QA environment. ###
-unsupported
diff --git a/test/integration/targets/ecs_domain/defaults/main.yml b/test/integration/targets/ecs_domain/defaults/main.yml
deleted file mode 100644
index 69034c4b8b..0000000000
--- a/test/integration/targets/ecs_domain/defaults/main.yml
+++ /dev/null
@@ -1,2 +0,0 @@
----
-# defaults file for test_ecs_domain
diff --git a/test/integration/targets/ecs_domain/meta/main.yml b/test/integration/targets/ecs_domain/meta/main.yml
deleted file mode 100644
index 07faa21776..0000000000
--- a/test/integration/targets/ecs_domain/meta/main.yml
+++ /dev/null
@@ -1,2 +0,0 @@
-dependencies:
- - prepare_tests
diff --git a/test/integration/targets/ecs_domain/tasks/main.yml b/test/integration/targets/ecs_domain/tasks/main.yml
deleted file mode 100644
index 4a12e116e2..0000000000
--- a/test/integration/targets/ecs_domain/tasks/main.yml
+++ /dev/null
@@ -1,270 +0,0 @@
----
-## Verify that integration_config was specified
-- block:
- - assert:
- that:
- - entrust_api_user is defined
- - entrust_api_key is defined
- - entrust_api_ip_address is defined
- - entrust_cloud_ip_address is defined
- - entrust_api_client_cert_path is defined or entrust_api_client_cert_contents is defined
- - entrust_api_client_cert_key_path is defined or entrust_api_client_cert_key_contents
- - cacerts_bundle_path_local is defined
-
-## SET UP TEST ENVIRONMENT ########################################################################
-- name: copy the files needed for verifying test server certificate to the host
- copy:
- src: '{{ cacerts_bundle_path_local }}/'
- dest: '{{ cacerts_bundle_path }}'
-
-- name: Update the CA certificates for our QA certs (collection may need updating if new QA environments used)
- command: c_rehash {{ cacerts_bundle_path }}
-
-- name: Update hosts file
- lineinfile:
- path: /etc/hosts
- state: present
- regexp: 'api.entrust.net$'
- line: '{{ entrust_api_ip_address }} api.entrust.net'
-
-- name: Update hosts file
- lineinfile:
- path: /etc/hosts
- state: present
- regexp: 'cloud.entrust.net$'
- line: '{{ entrust_cloud_ip_address }} cloud.entrust.net'
-
-- name: Clear out the temporary directory for storing the API connection information
- file:
- path: '{{ tmpdir_path }}'
- state: absent
-
-- name: Create a directory for storing the API connection Information
- file:
- path: '{{ tmpdir_path }}'
- state: directory
-
-- name: Copy the files needed for the connection to entrust API to the host
- copy:
- src: '{{ entrust_api_client_cert_path }}'
- dest: '{{ entrust_api_cert }}'
-
-- name: Copy the files needed for the connection to entrust API to the host
- copy:
- src: '{{ entrust_api_client_cert_key_path }}'
- dest: '{{ entrust_api_cert_key }}'
-
-- block:
- - name: Have ECS request a domain validation via dns
- ecs_domain:
- domain_name: dns.{{ common_name }}
- verification_method: dns
- entrust_api_user: '{{ entrust_api_user }}'
- entrust_api_key: '{{ entrust_api_key }}'
- entrust_api_client_cert_path: '{{ entrust_api_cert }}'
- entrust_api_client_cert_key_path: '{{ entrust_api_cert_key }}'
- register: dns_result
-
- - assert:
- that:
- - dns_result is not failed
- - dns_result.changed
- - dns_result.domain_status == 'INITIAL_VERIFICATION'
- - dns_result.verification_method == 'dns'
- - dns_result.dns_location is string
- - dns_result.dns_contents is string
- - dns_result.dns_resource_type is string
- - dns_result.file_location is undefined
- - dns_result.file_contents is undefined
- - dns_result.emails is undefined
-
- - name: Have ECS request a domain validation via web_server
- ecs_domain:
- domain_name: FILE.{{ common_name }}
- verification_method: web_server
- entrust_api_user: '{{ entrust_api_user }}'
- entrust_api_key: '{{ entrust_api_key }}'
- entrust_api_client_cert_path: '{{ entrust_api_cert }}'
- entrust_api_client_cert_key_path: '{{ entrust_api_cert_key }}'
- register: file_result
-
- - assert:
- that:
- - file_result is not failed
- - file_result.changed
- - file_result.domain_status == 'INITIAL_VERIFICATION'
- - file_result.verification_method == 'web_server'
- - file_result.dns_location is undefined
- - file_result.dns_contents is undefined
- - file_result.dns_resource_type is undefined
- - file_result.file_location is string
- - file_result.file_contents is string
- - file_result.emails is undefined
-
- - name: Have ECS request a domain validation via email
- ecs_domain:
- domain_name: email.{{ common_name }}
- verification_method: email
- verification_email: admin@testcertificates.com
- entrust_api_user: '{{ entrust_api_user }}'
- entrust_api_key: '{{ entrust_api_key }}'
- entrust_api_client_cert_path: '{{ entrust_api_cert }}'
- entrust_api_client_cert_key_path: '{{ entrust_api_cert_key }}'
- register: email_result
-
- - assert:
- that:
- - email_result is not failed
- - email_result.changed
- - email_result.domain_status == 'INITIAL_VERIFICATION'
- - email_result.verification_method == 'email'
- - email_result.dns_location is undefined
- - email_result.dns_contents is undefined
- - email_result.dns_resource_type is undefined
- - email_result.file_location is undefined
- - email_result.file_contents is undefined
- - email_result.emails[0] == 'admin@testcertificates.com'
-
- - name: Have ECS request a domain validation via email with no address provided
- ecs_domain:
- domain_name: email2.{{ common_name }}
- verification_method: email
- entrust_api_user: '{{ entrust_api_user }}'
- entrust_api_key: '{{ entrust_api_key }}'
- entrust_api_client_cert_path: '{{ entrust_api_cert }}'
- entrust_api_client_cert_key_path: '{{ entrust_api_cert_key }}'
- register: email_result2
-
- - assert:
- that:
- - email_result2 is not failed
- - email_result2.changed
- - email_result2.domain_status == 'INITIAL_VERIFICATION'
- - email_result2.verification_method == 'email'
- - email_result2.dns_location is undefined
- - email_result2.dns_contents is undefined
- - email_result2.dns_resource_type is undefined
- - email_result2.file_location is undefined
- - email_result2.file_contents is undefined
- - email_result2.emails is defined
-
- - name: Have ECS request a domain validation via manual
- ecs_domain:
- domain_name: manual.{{ common_name }}
- verification_method: manual
- entrust_api_user: '{{ entrust_api_user }}'
- entrust_api_key: '{{ entrust_api_key }}'
- entrust_api_client_cert_path: '{{ entrust_api_cert }}'
- entrust_api_client_cert_key_path: '{{ entrust_api_cert_key }}'
- register: manual_result
-
- - assert:
- that:
- - manual_result is not failed
- - manual_result.changed
- - manual_result.domain_status == 'INITIAL_VERIFICATION'
- - manual_result.verification_method == 'manual'
- - manual_result.dns_location is undefined
- - manual_result.dns_contents is undefined
- - manual_result.dns_resource_type is undefined
- - manual_result.file_location is undefined
- - manual_result.file_contents is undefined
- - manual_result.emails is undefined
-
- - name: Have ECS request a domain validation via dns that remains unchanged
- ecs_domain:
- domain_name: dns.{{ common_name }}
- verification_method: dns
- entrust_api_user: '{{ entrust_api_user }}'
- entrust_api_key: '{{ entrust_api_key }}'
- entrust_api_client_cert_path: '{{ entrust_api_cert }}'
- entrust_api_client_cert_key_path: '{{ entrust_api_cert_key }}'
- register: dns_result2
-
- - assert:
- that:
- - dns_result2 is not failed
- - not dns_result2.changed
- - dns_result2.domain_status == 'INITIAL_VERIFICATION'
- - dns_result2.verification_method == 'dns'
- - dns_result2.dns_location is string
- - dns_result2.dns_contents is string
- - dns_result2.dns_resource_type is string
- - dns_result2.file_location is undefined
- - dns_result2.file_contents is undefined
- - dns_result2.emails is undefined
-
- - name: Have ECS request a domain validation via FILE for dns, to change verification method
- ecs_domain:
- domain_name: dns.{{ common_name }}
- verification_method: web_server
- entrust_api_user: '{{ entrust_api_user }}'
- entrust_api_key: '{{ entrust_api_key }}'
- entrust_api_client_cert_path: '{{ entrust_api_cert }}'
- entrust_api_client_cert_key_path: '{{ entrust_api_cert_key }}'
- register: dns_result_now_file
-
- - assert:
- that:
- - dns_result_now_file is not failed
- - dns_result_now_file.changed
- - dns_result_now_file.domain_status == 'INITIAL_VERIFICATION'
- - dns_result_now_file.verification_method == 'web_server'
- - dns_result_now_file.dns_location is undefined
- - dns_result_now_file.dns_contents is undefined
- - dns_result_now_file.dns_resource_type is undefined
- - dns_result_now_file.file_location is string
- - dns_result_now_file.file_contents is string
- - dns_result_now_file.emails is undefined
-
- - name: Request revalidation of an approved domain
- ecs_domain:
- domain_name: '{{ existing_domain_common_name }}'
- verification_method: manual
- entrust_api_user: '{{ entrust_api_user }}'
- entrust_api_key: '{{ entrust_api_key }}'
- entrust_api_client_cert_path: '{{ entrust_api_cert }}'
- entrust_api_client_cert_key_path: '{{ entrust_api_cert_key }}'
- register: manual_existing_domain
-
- - assert:
- that:
- - manual_existing_domain is not failed
- - not manual_existing_domain.changed
- - manual_existing_domain.domain_status == 'RE_VERIFICATION'
- - manual_existing_domain.dns_location is undefined
- - manual_existing_domain.dns_contents is undefined
- - manual_existing_domain.dns_resource_type is undefined
- - manual_existing_domain.file_location is undefined
- - manual_existing_domain.file_contents is undefined
- - manual_existing_domain.emails is undefined
-
- - name: Request revalidation of an approved domain
- ecs_domain:
- domain_name: '{{ existing_domain_common_name }}'
- verification_method: web_server
- entrust_api_user: '{{ entrust_api_user }}'
- entrust_api_key: '{{ entrust_api_key }}'
- entrust_api_client_cert_path: '{{ entrust_api_cert }}'
- entrust_api_client_cert_key_path: '{{ entrust_api_cert_key }}'
- register: file_existing_domain_revalidate
-
- - assert:
- that:
- - file_existing_domain_revalidate is not failed
- - file_existing_domain_revalidate.changed
- - file_existing_domain_revalidate.domain_status == 'RE_VERIFICATION'
- - file_existing_domain_revalidate.verification_method == 'web_server'
- - file_existing_domain_revalidate.dns_location is undefined
- - file_existing_domain_revalidate.dns_contents is undefined
- - file_existing_domain_revalidate.dns_resource_type is undefined
- - file_existing_domain_revalidate.file_location is string
- - file_existing_domain_revalidate.file_contents is string
- - file_existing_domain_revalidate.emails is undefined
-
-
- always:
- - name: clean-up temporary folder
- file:
- path: '{{ tmpdir_path }}'
- state: absent
diff --git a/test/integration/targets/ecs_domain/vars/main.yml b/test/integration/targets/ecs_domain/vars/main.yml
deleted file mode 100644
index 9cf9fdb764..0000000000
--- a/test/integration/targets/ecs_domain/vars/main.yml
+++ /dev/null
@@ -1,15 +0,0 @@
----
-# vars file for test_ecs_certificate
-
-# Path on various hosts that cacerts need to be put as a prerequisite to API server cert validation.
-# May need to be customized for some environments based on SSL implementations
-# that ansible "urls" module utility is using as a backing.
-cacerts_bundle_path: /etc/pki/tls/certs
-
-common_name: '{{ ansible_date_time.epoch }}.testcertificates.com'
-existing_domain_common_name: 'testcertificates.com'
-
-tmpdir_path: /tmp/ecs_cert_test/{{ ansible_date_time.epoch }}
-
-entrust_api_cert: '{{ tmpdir_path }}/authcert.cer'
-entrust_api_cert_key: '{{ tmpdir_path }}/authkey.cer'
diff --git a/test/integration/targets/get_certificate/aliases b/test/integration/targets/get_certificate/aliases
deleted file mode 100644
index 1dcd7efef2..0000000000
--- a/test/integration/targets/get_certificate/aliases
+++ /dev/null
@@ -1,4 +0,0 @@
-shippable/posix/group1
-destructive
-needs/httptester
-skip/aix
diff --git a/test/integration/targets/get_certificate/files/bogus_ca.pem b/test/integration/targets/get_certificate/files/bogus_ca.pem
deleted file mode 100644
index 16119c9edb..0000000000
--- a/test/integration/targets/get_certificate/files/bogus_ca.pem
+++ /dev/null
@@ -1,18 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIC+DCCAeACCQCWuDvGDH3otTANBgkqhkiG9w0BAQsFADA+MQswCQYDVQQGEwJV
-UzEOMAwGA1UECAwFQm9ndXMxEDAOBgNVBAcMB0JhbG9uZXkxDTALBgNVBAoMBEFD
-TUUwHhcNMTgwNzEyMTgxNDA0WhcNMjMwNzExMTgxNDA0WjA+MQswCQYDVQQGEwJV
-UzEOMAwGA1UECAwFQm9ndXMxEDAOBgNVBAcMB0JhbG9uZXkxDTALBgNVBAoMBEFD
-TUUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDLTGCpn8b+/2qdpkvK
-iwXU8PMOXBOmRa+GmzxsxMr1QZcY0m6pY3uuIvqErMFf4qp4BMxQF+VpDLVJUJX/
-1oKCM7J3hEfgmKRD4RmKhBlnWVv5YGZmvlXRJBl1AsDTONZy8iKJB5NYnB3ZyrJq
-H2GAgyJ55aYckoU55vwjRzKp49dZmzX5YS04Kzzzw/SmOuW8kMypZV5TJH+NXqKc
-pw3u3cJ4yJ9DHSU5pnhC5BeKl8XDMO42jRWt5/7C7JDiCbZ9lu5jQiv/4DhsRsHF
-A8/Lgl47sNDaBMbha786I9laPHLlVycpYaP6pwtizhN9ZRTdDOHmWi/vjiamERLL
-FjjLAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAA+1uj3tHaCai+A1H/kOgTN5e0eW
-/wmaxu8gNK5eiHrecNJNAlFxVTrCwhvv4nUW7NXVcW/1WUqSO0QMiPJhCsSLVAMF
-8MuYH73B+ctRqAGdeOAWF+ftCywZTEj5h5F0XiWB+TmkPlTVNShMiPFelDJpLy7u
-9MfiPEJjo4sZotQl8/pZ6R9cY6GpEXWnttcuhLJCEuiB8fWO7epiWYCt/Ak+CVmZ
-OzfI/euV6Upaen22lNu8V3ZwWEFtmU5CioKJ3S8DK5Mw/LJIJw1ZY9E+fTtn8x0k
-xlI4e7urD2FYhTdv2fFUG8Z5arb/3bICgsUYQZ+G1c3wjWtJg9zcy8hpnZQ=
------END CERTIFICATE-----
diff --git a/test/integration/targets/get_certificate/files/process_certs.py b/test/integration/targets/get_certificate/files/process_certs.py
deleted file mode 100644
index 8a21af7117..0000000000
--- a/test/integration/targets/get_certificate/files/process_certs.py
+++ /dev/null
@@ -1,28 +0,0 @@
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-from sys import argv
-from subprocess import Popen, PIPE, STDOUT
-
-p = Popen(["openssl", "s_client", "-host", argv[1], "-port", "443", "-prexit", "-showcerts"], stdin=PIPE, stdout=PIPE, stderr=STDOUT)
-stdout = p.communicate(input=b'\n')[0]
-data = stdout.decode()
-
-certs = []
-cert = ""
-capturing = False
-for line in data.split('\n'):
- if line == '-----BEGIN CERTIFICATE-----':
- capturing = True
-
- if capturing:
- cert = "{0}{1}\n".format(cert, line)
-
- if line == '-----END CERTIFICATE-----':
- capturing = False
- certs.append(cert)
- cert = ""
-
-with open(argv[2], 'w') as f:
- for cert in set(certs):
- f.write(cert)
diff --git a/test/integration/targets/get_certificate/meta/main.yml b/test/integration/targets/get_certificate/meta/main.yml
deleted file mode 100644
index 54be4e6d4d..0000000000
--- a/test/integration/targets/get_certificate/meta/main.yml
+++ /dev/null
@@ -1,3 +0,0 @@
-dependencies:
- - setup_openssl
- - prepare_http_tests
diff --git a/test/integration/targets/get_certificate/tasks/main.yml b/test/integration/targets/get_certificate/tasks/main.yml
deleted file mode 100644
index ac06e1f8bb..0000000000
--- a/test/integration/targets/get_certificate/tasks/main.yml
+++ /dev/null
@@ -1,42 +0,0 @@
----
-- block:
-
- - name: Get servers certificate with backend auto-detection
- get_certificate:
- host: "{{ httpbin_host }}"
- port: 443
-
- when: |
- pyopenssl_version.stdout is version('0.15', '>=') or
- (cryptography_version.stdout is version('1.6', '>=') and (ansible_distribution != 'CentOS' or ansible_distribution_major_version|int > 6))
-
-- block:
-
- - include_tasks: ../tests/validate.yml
- vars:
- select_crypto_backend: pyopenssl
-
- when: pyopenssl_version.stdout is version('0.15', '>=')
-
-- name: Remove output directory
- file:
- path: "{{ output_dir }}"
- state: absent
-
-- name: Re-create output directory
- file:
- path: "{{ output_dir }}"
- state: directory
-
-- block:
-
- - include_tasks: ../tests/validate.yml
- vars:
- select_crypto_backend: cryptography
-
- # The module doesn't work with CentOS 6. Since the pyOpenSSL installed there is too old,
- # we never noticed before. This becomes a problem with the new cryptography backend,
- # since there is a new enough cryptography version...
- when: |
- cryptography_version.stdout is version('1.6', '>=') and
- (ansible_distribution != 'CentOS' or ansible_distribution_major_version|int > 6)
diff --git a/test/integration/targets/get_certificate/tests/validate.yml b/test/integration/targets/get_certificate/tests/validate.yml
deleted file mode 100644
index 5dec56c83b..0000000000
--- a/test/integration/targets/get_certificate/tests/validate.yml
+++ /dev/null
@@ -1,106 +0,0 @@
----
-- name: Get servers certificate
- get_certificate:
- host: "{{ httpbin_host }}"
- port: 443
- select_crypto_backend: "{{ select_crypto_backend }}"
- register: result
-
-- debug: var=result
-
-- assert:
- that:
- # This module should never change anything
- - result is not changed
- - result is not failed
- # We got the correct ST from the cert
- - "'North Carolina' == result.subject.ST"
-
-- name: Connect to http port (will fail because there is no SSL cert to get)
- get_certificate:
- host: "{{ httpbin_host }}"
- port: 80
- select_crypto_backend: "{{ select_crypto_backend }}"
- register: result
- ignore_errors: true
-
-- assert:
- that:
- - result is not changed
- - result is failed
- # We got the expected error message
- - "'The handshake operation timed out' in result.msg or 'unknown protocol' in result.msg or 'wrong version number' in result.msg"
-
-- name: Test timeout option
- get_certificate:
- host: "{{ httpbin_host }}"
- port: 1234
- timeout: 1
- select_crypto_backend: "{{ select_crypto_backend }}"
- register: result
- ignore_errors: true
-
-- assert:
- that:
- - result is not changed
- - result is failed
- # We got the expected error message
- - "'Failed to get cert from port with error: timed out' == result.msg or 'Connection refused' in result.msg"
-
-- name: Test failure if ca_cert is not a valid file
- get_certificate:
- host: "{{ httpbin_host }}"
- port: 443
- ca_cert: dn.e
- select_crypto_backend: "{{ select_crypto_backend }}"
- register: result
- ignore_errors: true
-
-- assert:
- that:
- - result is not changed
- - result is failed
- # We got the correct response from the module
- - "'ca_cert file does not exist' == result.msg"
-
-- name: Download CA Cert as pem from server
- get_url:
- url: "http://ansible.http.tests/cacert.pem"
- dest: "{{ output_dir }}/temp.pem"
-
-- name: Get servers certificate comparing it to its own ca_cert file
- get_certificate:
- ca_cert: '{{ output_dir }}/temp.pem'
- host: "{{ httpbin_host }}"
- port: 443
- select_crypto_backend: "{{ select_crypto_backend }}"
- register: result
-
-- assert:
- that:
- - result is not changed
- - result is not failed
-
-- name: Get a temp directory
- tempfile:
- state: directory
- register: my_temp_dir
-
-- name: Deploy the bogus_ca.pem file
- copy:
- src: "bogus_ca.pem"
- dest: "{{ my_temp_dir.path }}/bogus_ca.pem"
-
-- name: Get servers certificate comparing it to an invalid ca_cert file
- get_certificate:
- ca_cert: '{{ my_temp_dir.path }}/bogus_ca.pem'
- host: "{{ httpbin_host }}"
- port: 443
- select_crypto_backend: "{{ select_crypto_backend }}"
- register: result
- ignore_errors: true
-
-- assert:
- that:
- - result is not changed
- - result.failed
diff --git a/test/integration/targets/incidental_x509_crl/aliases b/test/integration/targets/incidental_x509_crl/aliases
deleted file mode 100644
index 54b54aa59a..0000000000
--- a/test/integration/targets/incidental_x509_crl/aliases
+++ /dev/null
@@ -1,4 +0,0 @@
-x509_crl_info
-shippable/posix/incidental
-destructive
-skip/aix
diff --git a/test/integration/targets/incidental_x509_crl/meta/main.yml b/test/integration/targets/incidental_x509_crl/meta/main.yml
deleted file mode 100644
index 0b241de3cc..0000000000
--- a/test/integration/targets/incidental_x509_crl/meta/main.yml
+++ /dev/null
@@ -1,2 +0,0 @@
-dependencies:
- - incidental_setup_openssl
diff --git a/test/integration/targets/incidental_x509_crl/tasks/impl.yml b/test/integration/targets/incidental_x509_crl/tasks/impl.yml
deleted file mode 100644
index eafb2dad2b..0000000000
--- a/test/integration/targets/incidental_x509_crl/tasks/impl.yml
+++ /dev/null
@@ -1,289 +0,0 @@
----
-- name: Create CRL 1 (check mode)
- x509_crl:
- path: '{{ output_dir }}/ca-crl1.crl'
- privatekey_path: '{{ output_dir }}/ca.key'
- issuer:
- CN: Ansible
- last_update: 20191013000000Z
- next_update: 20191113000000Z
- revoked_certificates:
- - path: '{{ output_dir }}/cert-1.pem'
- revocation_date: 20191013000000Z
- - path: '{{ output_dir }}/cert-2.pem'
- revocation_date: 20191013000000Z
- reason: key_compromise
- reason_critical: yes
- invalidity_date: 20191012000000Z
- - serial_number: 1234
- revocation_date: 20191001000000Z
- check_mode: yes
- register: crl_1_check
-- name: Create CRL 1
- x509_crl:
- path: '{{ output_dir }}/ca-crl1.crl'
- privatekey_path: '{{ output_dir }}/ca.key'
- issuer:
- CN: Ansible
- last_update: 20191013000000Z
- next_update: 20191113000000Z
- revoked_certificates:
- - path: '{{ output_dir }}/cert-1.pem'
- revocation_date: 20191013000000Z
- - path: '{{ output_dir }}/cert-2.pem'
- revocation_date: 20191013000000Z
- reason: key_compromise
- reason_critical: yes
- invalidity_date: 20191012000000Z
- - serial_number: 1234
- revocation_date: 20191001000000Z
- register: crl_1
-- name: Retrieve CRL 1 infos
- x509_crl_info:
- path: '{{ output_dir }}/ca-crl1.crl'
- register: crl_1_info_1
-- name: Retrieve CRL 1 infos via file content
- x509_crl_info:
- content: '{{ lookup("file", output_dir ~ "/ca-crl1.crl") }}'
- register: crl_1_info_2
-- name: Create CRL 1 (idempotent, check mode)
- x509_crl:
- path: '{{ output_dir }}/ca-crl1.crl'
- privatekey_path: '{{ output_dir }}/ca.key'
- issuer:
- CN: Ansible
- last_update: 20191013000000Z
- next_update: 20191113000000Z
- revoked_certificates:
- - path: '{{ output_dir }}/cert-1.pem'
- revocation_date: 20191013000000Z
- - path: '{{ output_dir }}/cert-2.pem'
- revocation_date: 20191013000000Z
- reason: key_compromise
- reason_critical: yes
- invalidity_date: 20191012000000Z
- - serial_number: 1234
- revocation_date: 20191001000000Z
- check_mode: yes
- register: crl_1_idem_check
-- name: Create CRL 1 (idempotent)
- x509_crl:
- path: '{{ output_dir }}/ca-crl1.crl'
- privatekey_path: '{{ output_dir }}/ca.key'
- issuer:
- CN: Ansible
- last_update: 20191013000000Z
- next_update: 20191113000000Z
- revoked_certificates:
- - path: '{{ output_dir }}/cert-1.pem'
- revocation_date: 20191013000000Z
- - path: '{{ output_dir }}/cert-2.pem'
- revocation_date: 20191013000000Z
- reason: key_compromise
- reason_critical: yes
- invalidity_date: 20191012000000Z
- - serial_number: 1234
- revocation_date: 20191001000000Z
- register: crl_1_idem
-- name: Create CRL 1 (idempotent with content, check mode)
- x509_crl:
- path: '{{ output_dir }}/ca-crl1.crl'
- privatekey_content: "{{ lookup('file', output_dir ~ '/ca.key') }}"
- issuer:
- CN: Ansible
- last_update: 20191013000000Z
- next_update: 20191113000000Z
- revoked_certificates:
- - content: "{{ lookup('file', output_dir ~ '/cert-1.pem') }}"
- revocation_date: 20191013000000Z
- - content: "{{ lookup('file', output_dir ~ '/cert-2.pem') }}"
- revocation_date: 20191013000000Z
- reason: key_compromise
- reason_critical: yes
- invalidity_date: 20191012000000Z
- - serial_number: 1234
- revocation_date: 20191001000000Z
- check_mode: yes
- register: crl_1_idem_content_check
-- name: Create CRL 1 (idempotent with content)
- x509_crl:
- path: '{{ output_dir }}/ca-crl1.crl'
- privatekey_content: "{{ lookup('file', output_dir ~ '/ca.key') }}"
- issuer:
- CN: Ansible
- last_update: 20191013000000Z
- next_update: 20191113000000Z
- revoked_certificates:
- - content: "{{ lookup('file', output_dir ~ '/cert-1.pem') }}"
- revocation_date: 20191013000000Z
- - content: "{{ lookup('file', output_dir ~ '/cert-2.pem') }}"
- revocation_date: 20191013000000Z
- reason: key_compromise
- reason_critical: yes
- invalidity_date: 20191012000000Z
- - serial_number: 1234
- revocation_date: 20191001000000Z
- register: crl_1_idem_content
-
-- name: Create CRL 2 (check mode)
- x509_crl:
- path: '{{ output_dir }}/ca-crl2.crl'
- privatekey_path: '{{ output_dir }}/ca.key'
- issuer:
- CN: Ansible
- last_update: +0d
- next_update: +0d
- revoked_certificates:
- - path: '{{ output_dir }}/cert-1.pem'
- - path: '{{ output_dir }}/cert-2.pem'
- reason: key_compromise
- reason_critical: yes
- invalidity_date: 20191012000000Z
- - serial_number: 1234
- check_mode: yes
- register: crl_2_check
-- name: Create CRL 2
- x509_crl:
- path: '{{ output_dir }}/ca-crl2.crl'
- privatekey_path: '{{ output_dir }}/ca.key'
- issuer:
- CN: Ansible
- last_update: +0d
- next_update: +0d
- revoked_certificates:
- - path: '{{ output_dir }}/cert-1.pem'
- - path: '{{ output_dir }}/cert-2.pem'
- reason: key_compromise
- reason_critical: yes
- invalidity_date: 20191012000000Z
- - serial_number: 1234
- register: crl_2
-- name: Create CRL 2 (idempotent, check mode)
- x509_crl:
- path: '{{ output_dir }}/ca-crl2.crl'
- privatekey_path: '{{ output_dir }}/ca.key'
- issuer:
- CN: Ansible
- last_update: +0d
- next_update: +0d
- revoked_certificates:
- - path: '{{ output_dir }}/cert-1.pem'
- - path: '{{ output_dir }}/cert-2.pem'
- reason: key_compromise
- reason_critical: yes
- invalidity_date: 20191012000000Z
- - serial_number: 1234
- ignore_timestamps: yes
- check_mode: yes
- register: crl_2_idem_check
-- name: Create CRL 2 (idempotent)
- x509_crl:
- path: '{{ output_dir }}/ca-crl2.crl'
- privatekey_path: '{{ output_dir }}/ca.key'
- issuer:
- CN: Ansible
- last_update: +0d
- next_update: +0d
- revoked_certificates:
- - path: '{{ output_dir }}/cert-1.pem'
- - path: '{{ output_dir }}/cert-2.pem'
- reason: key_compromise
- reason_critical: yes
- invalidity_date: 20191012000000Z
- - serial_number: 1234
- ignore_timestamps: yes
- register: crl_2_idem
-- name: Create CRL 2 (idempotent update, check mode)
- x509_crl:
- path: '{{ output_dir }}/ca-crl2.crl'
- privatekey_path: '{{ output_dir }}/ca.key'
- issuer:
- CN: Ansible
- last_update: +0d
- next_update: +0d
- revoked_certificates:
- - serial_number: 1235
- ignore_timestamps: yes
- mode: update
- check_mode: yes
- register: crl_2_idem_update_change_check
-- name: Create CRL 2 (idempotent update)
- x509_crl:
- path: '{{ output_dir }}/ca-crl2.crl'
- privatekey_path: '{{ output_dir }}/ca.key'
- issuer:
- CN: Ansible
- last_update: +0d
- next_update: +0d
- revoked_certificates:
- - serial_number: 1235
- ignore_timestamps: yes
- mode: update
- register: crl_2_idem_update_change
-- name: Create CRL 2 (idempotent update, check mode)
- x509_crl:
- path: '{{ output_dir }}/ca-crl2.crl'
- privatekey_path: '{{ output_dir }}/ca.key'
- issuer:
- CN: Ansible
- last_update: +0d
- next_update: +0d
- revoked_certificates:
- - path: '{{ output_dir }}/cert-2.pem'
- reason: key_compromise
- reason_critical: yes
- invalidity_date: 20191012000000Z
- ignore_timestamps: yes
- mode: update
- check_mode: yes
- register: crl_2_idem_update_check
-- name: Create CRL 2 (idempotent update)
- x509_crl:
- path: '{{ output_dir }}/ca-crl2.crl'
- privatekey_path: '{{ output_dir }}/ca.key'
- issuer:
- CN: Ansible
- last_update: +0d
- next_update: +0d
- revoked_certificates:
- - path: '{{ output_dir }}/cert-2.pem'
- reason: key_compromise
- reason_critical: yes
- invalidity_date: 20191012000000Z
- ignore_timestamps: yes
- mode: update
- register: crl_2_idem_update
-- name: Create CRL 2 (changed timestamps, check mode)
- x509_crl:
- path: '{{ output_dir }}/ca-crl2.crl'
- privatekey_path: '{{ output_dir }}/ca.key'
- issuer:
- CN: Ansible
- last_update: +0d
- next_update: +0d
- revoked_certificates:
- - path: '{{ output_dir }}/cert-2.pem'
- reason: key_compromise
- reason_critical: yes
- invalidity_date: 20191012000000Z
- ignore_timestamps: no
- mode: update
- check_mode: yes
- register: crl_2_change_check
-- name: Create CRL 2 (changed timestamps)
- x509_crl:
- path: '{{ output_dir }}/ca-crl2.crl'
- privatekey_path: '{{ output_dir }}/ca.key'
- issuer:
- CN: Ansible
- last_update: +0d
- next_update: +0d
- revoked_certificates:
- - path: '{{ output_dir }}/cert-2.pem'
- reason: key_compromise
- reason_critical: yes
- invalidity_date: 20191012000000Z
- ignore_timestamps: no
- mode: update
- return_content: yes
- register: crl_2_change
diff --git a/test/integration/targets/incidental_x509_crl/tasks/main.yml b/test/integration/targets/incidental_x509_crl/tasks/main.yml
deleted file mode 100644
index 1f82ff9e1b..0000000000
--- a/test/integration/targets/incidental_x509_crl/tasks/main.yml
+++ /dev/null
@@ -1,83 +0,0 @@
----
-- set_fact:
- certificates:
- - name: ca
- subject:
- commonName: Ansible
- is_ca: yes
- - name: ca-2
- subject:
- commonName: Ansible Other CA
- is_ca: yes
- - name: cert-1
- subject_alt_name:
- - DNS:ansible.com
- - name: cert-2
- subject_alt_name:
- - DNS:example.com
- - name: cert-3
- subject_alt_name:
- - DNS:example.org
- - IP:1.2.3.4
- - name: cert-4
- subject_alt_name:
- - DNS:test.ansible.com
- - DNS:b64.ansible.com
-
-- name: Generate private keys
- openssl_privatekey:
- path: '{{ output_dir }}/{{ item.name }}.key'
- type: ECC
- curve: secp256r1
- loop: "{{ certificates }}"
-
-- name: Generate CSRs
- openssl_csr:
- path: '{{ output_dir }}/{{ item.name }}.csr'
- privatekey_path: '{{ output_dir }}/{{ item.name }}.key'
- subject: "{{ item.subject | default(omit) }}"
- subject_alt_name: "{{ item.subject_alt_name | default(omit) }}"
- basic_constraints: "{{ 'CA:TRUE' if item.is_ca | default(false) else omit }}"
- use_common_name_for_san: no
- loop: "{{ certificates }}"
-
-- name: Generate CA certificates
- openssl_certificate:
- path: '{{ output_dir }}/{{ item.name }}.pem'
- csr_path: '{{ output_dir }}/{{ item.name }}.csr'
- privatekey_path: '{{ output_dir }}/{{ item.name }}.key'
- provider: selfsigned
- loop: "{{ certificates }}"
- when: item.is_ca | default(false)
-
-- name: Generate other certificates
- openssl_certificate:
- path: '{{ output_dir }}/{{ item.name }}.pem'
- csr_path: '{{ output_dir }}/{{ item.name }}.csr'
- provider: ownca
- ownca_path: '{{ output_dir }}/ca.pem'
- ownca_privatekey_path: '{{ output_dir }}/ca.key'
- loop: "{{ certificates }}"
- when: not (item.is_ca | default(false))
-
-- name: Get certificate infos
- openssl_certificate_info:
- path: '{{ output_dir }}/{{ item }}.pem'
- loop:
- - cert-1
- - cert-2
- - cert-3
- - cert-4
- register: certificate_infos
-
-- block:
- - name: Running tests with cryptography backend
- include_tasks: impl.yml
- vars:
- select_crypto_backend: cryptography
-
- - import_tasks: ../tests/validate.yml
- vars:
- select_crypto_backend: cryptography
-
- when: cryptography_version.stdout is version('1.2', '>=')
diff --git a/test/integration/targets/incidental_x509_crl/tests/validate.yml b/test/integration/targets/incidental_x509_crl/tests/validate.yml
deleted file mode 100644
index 17b31f34ad..0000000000
--- a/test/integration/targets/incidental_x509_crl/tests/validate.yml
+++ /dev/null
@@ -1,61 +0,0 @@
----
-- name: Validate CRL 1
- assert:
- that:
- - crl_1_check is changed
- - crl_1 is changed
- - crl_1_idem_check is not changed
- - crl_1_idem is not changed
- - crl_1_idem_content_check is not changed
- - crl_1_idem_content is not changed
-
-- name: Validate CRL 1 info
- assert:
- that:
- - crl_1_info_1 == crl_1_info_2
- - crl_1_info_1.digest == 'ecdsa-with-SHA256'
- - crl_1_info_1.issuer | length == 1
- - crl_1_info_1.issuer.commonName == 'Ansible'
- - crl_1_info_1.issuer_ordered | length == 1
- - crl_1_info_1.last_update == '20191013000000Z'
- - crl_1_info_1.next_update == '20191113000000Z'
- - crl_1_info_1.revoked_certificates | length == 3
- - crl_1_info_1.revoked_certificates[0].invalidity_date is none
- - crl_1_info_1.revoked_certificates[0].invalidity_date_critical == false
- - crl_1_info_1.revoked_certificates[0].issuer is none
- - crl_1_info_1.revoked_certificates[0].issuer_critical == false
- - crl_1_info_1.revoked_certificates[0].reason is none
- - crl_1_info_1.revoked_certificates[0].reason_critical == false
- - crl_1_info_1.revoked_certificates[0].revocation_date == '20191013000000Z'
- - crl_1_info_1.revoked_certificates[0].serial_number == certificate_infos.results[0].serial_number
- - crl_1_info_1.revoked_certificates[1].invalidity_date == '20191012000000Z'
- - crl_1_info_1.revoked_certificates[1].invalidity_date_critical == false
- - crl_1_info_1.revoked_certificates[1].issuer is none
- - crl_1_info_1.revoked_certificates[1].issuer_critical == false
- - crl_1_info_1.revoked_certificates[1].reason == 'key_compromise'
- - crl_1_info_1.revoked_certificates[1].reason_critical == true
- - crl_1_info_1.revoked_certificates[1].revocation_date == '20191013000000Z'
- - crl_1_info_1.revoked_certificates[1].serial_number == certificate_infos.results[1].serial_number
- - crl_1_info_1.revoked_certificates[2].invalidity_date is none
- - crl_1_info_1.revoked_certificates[2].invalidity_date_critical == false
- - crl_1_info_1.revoked_certificates[2].issuer is none
- - crl_1_info_1.revoked_certificates[2].issuer_critical == false
- - crl_1_info_1.revoked_certificates[2].reason is none
- - crl_1_info_1.revoked_certificates[2].reason_critical == false
- - crl_1_info_1.revoked_certificates[2].revocation_date == '20191001000000Z'
- - crl_1_info_1.revoked_certificates[2].serial_number == 1234
-
-- name: Validate CRL 2
- assert:
- that:
- - crl_2_check is changed
- - crl_2 is changed
- - crl_2_idem_check is not changed
- - crl_2_idem is not changed
- - crl_2_idem_update_change_check is changed
- - crl_2_idem_update_change is changed
- - crl_2_idem_update_check is not changed
- - crl_2_idem_update is not changed
- - crl_2_change_check is changed
- - crl_2_change is changed
- - crl_2_change.crl == lookup('file', output_dir ~ '/ca-crl2.crl', rstrip=False)
diff --git a/test/integration/targets/luks_device/aliases b/test/integration/targets/luks_device/aliases
deleted file mode 100644
index f9e4266fc1..0000000000
--- a/test/integration/targets/luks_device/aliases
+++ /dev/null
@@ -1,7 +0,0 @@
-shippable/posix/group4
-skip/aix
-skip/osx
-skip/freebsd
-skip/docker
-needs/root
-destructive
diff --git a/test/integration/targets/luks_device/files/keyfile1 b/test/integration/targets/luks_device/files/keyfile1
deleted file mode 100644
index 5e40c08770..0000000000
--- a/test/integration/targets/luks_device/files/keyfile1
+++ /dev/null
@@ -1 +0,0 @@
-asdf \ No newline at end of file
diff --git a/test/integration/targets/luks_device/files/keyfile2 b/test/integration/targets/luks_device/files/keyfile2
deleted file mode 100644
index 5e4f256515..0000000000
--- a/test/integration/targets/luks_device/files/keyfile2
+++ /dev/null
@@ -1 +0,0 @@
-test1234 \ No newline at end of file
diff --git a/test/integration/targets/luks_device/tasks/main.yml b/test/integration/targets/luks_device/tasks/main.yml
deleted file mode 100644
index d3ca75b637..0000000000
--- a/test/integration/targets/luks_device/tasks/main.yml
+++ /dev/null
@@ -1,36 +0,0 @@
----
-- name: Make sure cryptsetup is installed
- package:
- name: cryptsetup
- state: present
- become: yes
-- name: Create cryptfile
- command: dd if=/dev/zero of={{ output_dir.replace('~', ansible_env.HOME) }}/cryptfile bs=1M count=32
-- name: Create lookback device
- command: losetup -f {{ output_dir.replace('~', ansible_env.HOME) }}/cryptfile
- become: yes
-- name: Determine loop device name
- command: losetup -j {{ output_dir.replace('~', ansible_env.HOME) }}/cryptfile --output name
- become: yes
- register: cryptfile_device_output
-- set_fact:
- cryptfile_device: "{{ cryptfile_device_output.stdout_lines[1] }}"
- cryptfile_passphrase1: "uNiJ9vKG2mUOEWDiQVuBHJlfMHE"
- cryptfile_passphrase2: "HW4Ak2HtE2vvne0qjJMPTtmbV4M"
- cryptfile_passphrase3: "qQJqsjabO9pItV792k90VvX84MM"
-- block:
- - include_tasks: run-test.yml
- with_fileglob:
- - "tests/*.yml"
- always:
- - name: Make sure LUKS device is gone
- luks_device:
- device: "{{ cryptfile_device }}"
- state: absent
- become: yes
- ignore_errors: yes
- - command: losetup -d "{{ cryptfile_device }}"
- become: yes
- - file:
- dest: "{{ output_dir }}/cryptfile"
- state: absent
diff --git a/test/integration/targets/luks_device/tasks/run-test.yml b/test/integration/targets/luks_device/tasks/run-test.yml
deleted file mode 100644
index a2ec73b24b..0000000000
--- a/test/integration/targets/luks_device/tasks/run-test.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-- name: Make sure LUKS device is gone
- luks_device:
- device: "{{ cryptfile_device }}"
- state: absent
- become: yes
-- name: "Loading tasks from {{ item }}"
- include_tasks: "{{ item }}"
diff --git a/test/integration/targets/luks_device/tasks/tests/create-destroy.yml b/test/integration/targets/luks_device/tasks/tests/create-destroy.yml
deleted file mode 100644
index 6ac4d1c388..0000000000
--- a/test/integration/targets/luks_device/tasks/tests/create-destroy.yml
+++ /dev/null
@@ -1,187 +0,0 @@
----
-- name: Create (check)
- luks_device:
- device: "{{ cryptfile_device }}"
- state: present
- keyfile: "{{ role_path }}/files/keyfile1"
- check_mode: yes
- become: yes
- register: create_check
-- name: Create
- luks_device:
- device: "{{ cryptfile_device }}"
- state: present
- keyfile: "{{ role_path }}/files/keyfile1"
- become: yes
- register: create
-- name: Create (idempotent)
- luks_device:
- device: "{{ cryptfile_device }}"
- state: present
- keyfile: "{{ role_path }}/files/keyfile1"
- become: yes
- register: create_idem
-- name: Create (idempotent, check)
- luks_device:
- device: "{{ cryptfile_device }}"
- state: present
- keyfile: "{{ role_path }}/files/keyfile1"
- check_mode: yes
- become: yes
- register: create_idem_check
-- assert:
- that:
- - create_check is changed
- - create is changed
- - create_idem is not changed
- - create_idem_check is not changed
-
-- name: Open (check)
- luks_device:
- device: "{{ cryptfile_device }}"
- state: opened
- keyfile: "{{ role_path }}/files/keyfile1"
- check_mode: yes
- become: yes
- register: open_check
-- name: Open
- luks_device:
- device: "{{ cryptfile_device }}"
- state: opened
- keyfile: "{{ role_path }}/files/keyfile1"
- become: yes
- register: open
-- name: Open (idempotent)
- luks_device:
- device: "{{ cryptfile_device }}"
- state: opened
- keyfile: "{{ role_path }}/files/keyfile1"
- become: yes
- register: open_idem
-- name: Open (idempotent, check)
- luks_device:
- device: "{{ cryptfile_device }}"
- state: opened
- keyfile: "{{ role_path }}/files/keyfile1"
- check_mode: yes
- become: yes
- register: open_idem_check
-- assert:
- that:
- - open_check is changed
- - open is changed
- - open_idem is not changed
- - open_idem_check is not changed
-
-- name: Closed (via name, check)
- luks_device:
- name: "{{ open.name }}"
- state: closed
- check_mode: yes
- become: yes
- register: close_check
-- name: Closed (via name)
- luks_device:
- name: "{{ open.name }}"
- state: closed
- become: yes
- register: close
-- name: Closed (via name, idempotent)
- luks_device:
- name: "{{ open.name }}"
- state: closed
- become: yes
- register: close_idem
-- name: Closed (via name, idempotent, check)
- luks_device:
- name: "{{ open.name }}"
- state: closed
- check_mode: yes
- become: yes
- register: close_idem_check
-- assert:
- that:
- - close_check is changed
- - close is changed
- - close_idem is not changed
- - close_idem_check is not changed
-
-- name: Re-open
- luks_device:
- device: "{{ cryptfile_device }}"
- state: opened
- keyfile: "{{ role_path }}/files/keyfile1"
- become: yes
-
-- name: Closed (via device, check)
- luks_device:
- device: "{{ cryptfile_device }}"
- state: closed
- check_mode: yes
- become: yes
- register: close_check
-- name: Closed (via device)
- luks_device:
- device: "{{ cryptfile_device }}"
- state: closed
- become: yes
- register: close
-- name: Closed (via device, idempotent)
- luks_device:
- device: "{{ cryptfile_device }}"
- state: closed
- become: yes
- register: close_idem
-- name: Closed (via device, idempotent, check)
- luks_device:
- device: "{{ cryptfile_device }}"
- state: closed
- check_mode: yes
- become: yes
- register: close_idem_check
-- assert:
- that:
- - close_check is changed
- - close is changed
- - close_idem is not changed
- - close_idem_check is not changed
-
-- name: Re-opened
- luks_device:
- device: "{{ cryptfile_device }}"
- state: opened
- keyfile: "{{ role_path }}/files/keyfile1"
- become: yes
-
-- name: Absent (check)
- luks_device:
- device: "{{ cryptfile_device }}"
- state: absent
- check_mode: yes
- become: yes
- register: absent_check
-- name: Absent
- luks_device:
- device: "{{ cryptfile_device }}"
- state: absent
- become: yes
- register: absent
-- name: Absent (idempotence)
- luks_device:
- device: "{{ cryptfile_device }}"
- state: absent
- become: yes
- register: absent_idem
-- name: Absent (idempotence, check)
- luks_device:
- device: "{{ cryptfile_device }}"
- state: absent
- check_mode: yes
- become: yes
- register: absent_idem_check
-- assert:
- that:
- - absent_check is changed
- - absent is changed
- - absent_idem is not changed
- - absent_idem_check is not changed
diff --git a/test/integration/targets/luks_device/tasks/tests/device-check.yml b/test/integration/targets/luks_device/tasks/tests/device-check.yml
deleted file mode 100644
index 3682a33e6a..0000000000
--- a/test/integration/targets/luks_device/tasks/tests/device-check.yml
+++ /dev/null
@@ -1,48 +0,0 @@
----
-- name: Create with invalid device name (check)
- luks_device:
- device: /dev/asdfasdfasdf
- state: present
- keyfile: "{{ role_path }}/files/keyfile1"
- check_mode: yes
- ignore_errors: yes
- become: yes
- register: create_check
-- name: Create with invalid device name
- luks_device:
- device: /dev/asdfasdfasdf
- state: present
- keyfile: "{{ role_path }}/files/keyfile1"
- ignore_errors: yes
- become: yes
- register: create
-- assert:
- that:
- - create_check is failed
- - create is failed
- - "'o such file or directory' in create_check.msg"
- - "'o such file or directory' in create.msg"
-
-- name: Create with something which is not a device (check)
- luks_device:
- device: /tmp/
- state: present
- keyfile: "{{ role_path }}/files/keyfile1"
- check_mode: yes
- ignore_errors: yes
- become: yes
- register: create_check
-- name: Create with something which is not a device
- luks_device:
- device: /tmp/
- state: present
- keyfile: "{{ role_path }}/files/keyfile1"
- ignore_errors: yes
- become: yes
- register: create
-- assert:
- that:
- - create_check is failed
- - create is failed
- - "'is not a device' in create_check.msg"
- - "'is not a device' in create.msg"
diff --git a/test/integration/targets/luks_device/tasks/tests/key-management.yml b/test/integration/targets/luks_device/tasks/tests/key-management.yml
deleted file mode 100644
index b89d505fa6..0000000000
--- a/test/integration/targets/luks_device/tasks/tests/key-management.yml
+++ /dev/null
@@ -1,168 +0,0 @@
----
-- name: Create with keyfile1
- luks_device:
- device: "{{ cryptfile_device }}"
- state: closed
- keyfile: "{{ role_path }}/files/keyfile1"
- become: yes
-
-# Access: keyfile1
-
-- name: Try to open with keyfile1
- luks_device:
- device: "{{ cryptfile_device }}"
- state: opened
- keyfile: "{{ role_path }}/files/keyfile1"
- become: yes
- ignore_errors: yes
- register: open_try
-- assert:
- that:
- - open_try is not failed
-- name: Close
- luks_device:
- device: "{{ cryptfile_device }}"
- state: closed
- become: yes
-
-- name: Try to open with keyfile2
- luks_device:
- device: "{{ cryptfile_device }}"
- state: opened
- keyfile: "{{ role_path }}/files/keyfile2"
- become: yes
- ignore_errors: yes
- register: open_try
-- assert:
- that:
- - open_try is failed
-
-- name: Give access to keyfile2
- luks_device:
- device: "{{ cryptfile_device }}"
- state: closed
- keyfile: "{{ role_path }}/files/keyfile1"
- new_keyfile: "{{ role_path }}/files/keyfile2"
- become: yes
-
-# Access: keyfile1 and keyfile2
-
-- name: Try to open with keyfile2
- luks_device:
- device: "{{ cryptfile_device }}"
- state: opened
- keyfile: "{{ role_path }}/files/keyfile2"
- become: yes
- ignore_errors: yes
- register: open_try
-- assert:
- that:
- - open_try is not failed
-- name: Close
- luks_device:
- device: "{{ cryptfile_device }}"
- state: closed
- become: yes
-
-- name: Dump LUKS header
- command: "cryptsetup luksDump {{ cryptfile_device }}"
- become: yes
-
-- name: Remove access from keyfile1
- luks_device:
- device: "{{ cryptfile_device }}"
- state: closed
- keyfile: "{{ role_path }}/files/keyfile1"
- remove_keyfile: "{{ role_path }}/files/keyfile1"
- become: yes
-
-# Access: keyfile2
-
-- name: Try to open with keyfile1
- luks_device:
- device: "{{ cryptfile_device }}"
- state: opened
- keyfile: "{{ role_path }}/files/keyfile1"
- become: yes
- ignore_errors: yes
- register: open_try
-- assert:
- that:
- - open_try is failed
-
-- name: Try to open with keyfile2
- luks_device:
- device: "{{ cryptfile_device }}"
- state: opened
- keyfile: "{{ role_path }}/files/keyfile2"
- become: yes
- ignore_errors: yes
- register: open_try
-- assert:
- that:
- - open_try is not failed
-- name: Close
- luks_device:
- device: "{{ cryptfile_device }}"
- state: closed
- become: yes
-
-- name: Dump LUKS header
- command: "cryptsetup luksDump {{ cryptfile_device }}"
- become: yes
-
-- name: Remove access from keyfile2
- luks_device:
- device: "{{ cryptfile_device }}"
- state: closed
- keyfile: "{{ role_path }}/files/keyfile2"
- remove_keyfile: "{{ role_path }}/files/keyfile2"
- become: yes
- ignore_errors: yes
- register: remove_last_key
-- assert:
- that:
- - remove_last_key is failed
- - "'force_remove_last_key' in remove_last_key.msg"
-
-# Access: keyfile2
-
-- name: Try to open with keyfile2
- luks_device:
- device: "{{ cryptfile_device }}"
- state: opened
- keyfile: "{{ role_path }}/files/keyfile2"
- become: yes
- ignore_errors: yes
- register: open_try
-- assert:
- that:
- - open_try is not failed
-- name: Close
- luks_device:
- device: "{{ cryptfile_device }}"
- state: closed
- become: yes
-
-- name: Remove access from keyfile2
- luks_device:
- device: "{{ cryptfile_device }}"
- state: closed
- keyfile: "{{ role_path }}/files/keyfile2"
- remove_keyfile: "{{ role_path }}/files/keyfile2"
- force_remove_last_key: yes
- become: yes
-
-# Access: none
-
-- name: Try to open with keyfile2
- luks_device:
- device: "{{ cryptfile_device }}"
- state: opened
- keyfile: "{{ role_path }}/files/keyfile2"
- become: yes
- ignore_errors: yes
- register: open_try
-- assert:
- that:
- - open_try is failed
diff --git a/test/integration/targets/luks_device/tasks/tests/options.yml b/test/integration/targets/luks_device/tasks/tests/options.yml
deleted file mode 100644
index afee6667d6..0000000000
--- a/test/integration/targets/luks_device/tasks/tests/options.yml
+++ /dev/null
@@ -1,41 +0,0 @@
----
-- name: Create with keysize
- luks_device:
- device: "{{ cryptfile_device }}"
- state: present
- keyfile: "{{ role_path }}/files/keyfile1"
- keysize: 256
- become: yes
- register: create_with_keysize
-- name: Create with keysize (idempotent)
- luks_device:
- device: "{{ cryptfile_device }}"
- state: present
- keyfile: "{{ role_path }}/files/keyfile1"
- keysize: 256
- become: yes
- register: create_idem_with_keysize
-- name: Create with different keysize (idempotent since we do not update keysize)
- luks_device:
- device: "{{ cryptfile_device }}"
- state: present
- keyfile: "{{ role_path }}/files/keyfile1"
- keysize: 512
- become: yes
- register: create_idem_with_diff_keysize
-- name: Create with ambiguous arguments
- luks_device:
- device: "{{ cryptfile_device }}"
- state: present
- keyfile: "{{ role_path }}/files/keyfile1"
- passphrase: "{{ cryptfile_passphrase1 }}"
- ignore_errors: yes
- become: yes
- register: create_with_ambiguous
-
-- assert:
- that:
- - create_with_keysize is changed
- - create_idem_with_keysize is not changed
- - create_idem_with_diff_keysize is not changed
- - create_with_ambiguous is failed
diff --git a/test/integration/targets/luks_device/tasks/tests/passphrase.yml b/test/integration/targets/luks_device/tasks/tests/passphrase.yml
deleted file mode 100644
index 560915252f..0000000000
--- a/test/integration/targets/luks_device/tasks/tests/passphrase.yml
+++ /dev/null
@@ -1,181 +0,0 @@
----
-- name: Create with passphrase1
- luks_device:
- device: "{{ cryptfile_device }}"
- state: closed
- passphrase: "{{ cryptfile_passphrase1 }}"
- become: yes
-
-- name: Open with passphrase1
- luks_device:
- device: "{{ cryptfile_device }}"
- state: opened
- passphrase: "{{ cryptfile_passphrase1 }}"
- become: yes
- ignore_errors: yes
- register: open_try
-- assert:
- that:
- - open_try is not failed
-- name: Close
- luks_device:
- device: "{{ cryptfile_device }}"
- state: closed
- become: yes
-
-- name: Give access with ambiguous new_ arguments
- luks_device:
- device: "{{ cryptfile_device }}"
- state: closed
- passphrase: "{{ cryptfile_passphrase1 }}"
- new_passphrase: "{{ cryptfile_passphrase2 }}"
- new_keyfile: "{{ role_path }}/files/keyfile1"
- become: yes
- ignore_errors: yes
- register: new_try
-- assert:
- that:
- - new_try is failed
-
-- name: Try to open with passphrase2
- luks_device:
- device: "{{ cryptfile_device }}"
- state: opened
- passphrase: "{{ cryptfile_passphrase2 }}"
- become: yes
- ignore_errors: yes
- register: open_try
-- assert:
- that:
- - open_try is failed
-
-- name: Give access to passphrase2
- luks_device:
- device: "{{ cryptfile_device }}"
- state: closed
- passphrase: "{{ cryptfile_passphrase1 }}"
- new_passphrase: "{{ cryptfile_passphrase2 }}"
- become: yes
-
-- name: Open with passphrase2
- luks_device:
- device: "{{ cryptfile_device }}"
- state: opened
- passphrase: "{{ cryptfile_passphrase2 }}"
- become: yes
- ignore_errors: yes
- register: open_try
-- assert:
- that:
- - open_try is not failed
-- name: Close
- luks_device:
- device: "{{ cryptfile_device }}"
- state: closed
- become: yes
-
-- name: Try to open with keyfile1
- luks_device:
- device: "{{ cryptfile_device }}"
- state: opened
- keyfile: "{{ role_path }}/files/keyfile1"
- become: yes
- ignore_errors: yes
- register: open_try
-- assert:
- that:
- - open_try is failed
-
-- name: Give access to keyfile1 from passphrase1
- luks_device:
- device: "{{ cryptfile_device }}"
- state: closed
- passphrase: "{{ cryptfile_passphrase1 }}"
- new_keyfile: "{{ role_path }}/files/keyfile1"
- become: yes
-
-- name: Remove access with ambiguous remove_ arguments
- luks_device:
- device: "{{ cryptfile_device }}"
- state: closed
- remove_keyfile: "{{ role_path }}/files/keyfile1"
- remove_passphrase: "{{ cryptfile_passphrase1 }}"
- become: yes
- ignore_errors: yes
- register: remove_try
-- assert:
- that:
- - remove_try is failed
-
-- name: Open with keyfile1
- luks_device:
- device: "{{ cryptfile_device }}"
- state: opened
- keyfile: "{{ role_path }}/files/keyfile1"
- become: yes
- ignore_errors: yes
- register: open_try
-- assert:
- that:
- - open_try is not failed
-- name: Close
- luks_device:
- device: "{{ cryptfile_device }}"
- state: closed
- become: yes
-
-- name: Remove access for passphrase1
- luks_device:
- device: "{{ cryptfile_device }}"
- state: closed
- remove_passphrase: "{{ cryptfile_passphrase1 }}"
- become: yes
-
-- name: Try to open with passphrase1
- luks_device:
- device: "{{ cryptfile_device }}"
- state: opened
- passphrase: "{{ cryptfile_passphrase1 }}"
- become: yes
- ignore_errors: yes
- register: open_try
-- assert:
- that:
- - open_try is failed
-
-- name: Try to open with passphrase3
- luks_device:
- device: "{{ cryptfile_device }}"
- state: opened
- passphrase: "{{ cryptfile_passphrase3 }}"
- become: yes
- ignore_errors: yes
- register: open_try
-- assert:
- that:
- - open_try is failed
-
-- name: Give access to passphrase3 from keyfile1
- luks_device:
- device: "{{ cryptfile_device }}"
- state: closed
- keyfile: "{{ role_path }}/files/keyfile1"
- new_passphrase: "{{ cryptfile_passphrase3 }}"
- become: yes
-
-- name: Open with passphrase3
- luks_device:
- device: "{{ cryptfile_device }}"
- state: opened
- passphrase: "{{ cryptfile_passphrase3 }}"
- become: yes
- ignore_errors: yes
- register: open_try
-- assert:
- that:
- - open_try is not failed
-- name: Close
- luks_device:
- device: "{{ cryptfile_device }}"
- state: closed
- become: yes
diff --git a/test/integration/targets/openssh_cert/aliases b/test/integration/targets/openssh_cert/aliases
deleted file mode 100644
index 0b484bbab6..0000000000
--- a/test/integration/targets/openssh_cert/aliases
+++ /dev/null
@@ -1,3 +0,0 @@
-shippable/posix/group1
-destructive
-skip/aix
diff --git a/test/integration/targets/openssh_cert/meta/main.yml b/test/integration/targets/openssh_cert/meta/main.yml
deleted file mode 100644
index dc973f4e00..0000000000
--- a/test/integration/targets/openssh_cert/meta/main.yml
+++ /dev/null
@@ -1,2 +0,0 @@
-dependencies:
- - setup_ssh_keygen
diff --git a/test/integration/targets/openssh_cert/tasks/main.yml b/test/integration/targets/openssh_cert/tasks/main.yml
deleted file mode 100644
index 5e4606b9c6..0000000000
--- a/test/integration/targets/openssh_cert/tasks/main.yml
+++ /dev/null
@@ -1,408 +0,0 @@
-- name: openssh_cert integration tests
- when: not (ansible_facts['distribution'] == "CentOS" and ansible_facts['distribution_major_version'] == "6")
- block:
- - name: Generate keypair (check mode)
- openssh_keypair:
- path: '{{ output_dir }}/id_key'
- type: rsa
- check_mode: yes
- - name: Generate keypair
- openssh_keypair:
- path: '{{ output_dir }}/id_key'
- type: rsa
- - name: Generate keypair (idempotent)
- openssh_keypair:
- path: '{{ output_dir }}/id_key'
- type: rsa
- - name: Generate keypair (idempotent, check mode)
- openssh_keypair:
- path: '{{ output_dir }}/id_key'
- type: rsa
- check_mode: yes
- - name: Generate always valid cert (check mode)
- openssh_cert:
- type: user
- signing_key: '{{ output_dir }}/id_key'
- public_key: '{{ output_dir }}/id_key.pub'
- path: '{{ output_dir }}/id_cert'
- valid_from: always
- valid_to: forever
- check_mode: yes
- - name: Generate always valid cert
- openssh_cert:
- type: user
- signing_key: '{{ output_dir }}/id_key'
- public_key: '{{ output_dir }}/id_key.pub'
- path: '{{ output_dir }}/id_cert'
- valid_from: always
- valid_to: forever
- - name: Generate always valid cert (idempotent)
- openssh_cert:
- type: user
- signing_key: '{{ output_dir }}/id_key'
- public_key: '{{ output_dir }}/id_key.pub'
- path: '{{ output_dir }}/id_cert'
- valid_from: always
- valid_to: forever
- - name: Generate always valid cert (idempotent, check mode)
- openssh_cert:
- type: user
- signing_key: '{{ output_dir }}/id_key'
- public_key: '{{ output_dir }}/id_key.pub'
- path: '{{ output_dir }}/id_cert'
- valid_from: always
- valid_to: forever
- check_mode: yes
- - name: Generate restricted validity cert with valid_at (check mode)
- openssh_cert:
- type: host
- signing_key: '{{ output_dir }}/id_key'
- public_key: '{{ output_dir }}/id_key.pub'
- path: '{{ output_dir }}/id_cert'
- valid_from: +0s
- valid_to: +32w
- valid_at: +2w
- check_mode: yes
- - name: Generate restricted validity cert with valid_at
- openssh_cert:
- type: host
- signing_key: '{{ output_dir }}/id_key'
- public_key: '{{ output_dir }}/id_key.pub'
- path: '{{ output_dir }}/id_cert'
- valid_from: +0s
- valid_to: +32w
- valid_at: +2w
- - name: Generate restricted validity cert with valid_at (idempotent)
- openssh_cert:
- type: host
- signing_key: '{{ output_dir }}/id_key'
- public_key: '{{ output_dir }}/id_key.pub'
- path: '{{ output_dir }}/id_cert'
- valid_from: +0s
- valid_to: +32w
- valid_at: +2w
- - name: Generate restricted validity cert with valid_at (idempotent, check mode)
- openssh_cert:
- type: host
- signing_key: '{{ output_dir }}/id_key'
- public_key: '{{ output_dir }}/id_key.pub'
- path: '{{ output_dir }}/id_cert'
- valid_from: +0s
- valid_to: +32w
- valid_at: +2w
- check_mode: yes
- - name: Generate always valid cert only for example.com and examplehost (check mode)
- openssh_cert:
- type: host
- signing_key: '{{ output_dir }}/id_key'
- public_key: '{{ output_dir }}/id_key.pub'
- path: '{{ output_dir }}/id_cert'
- valid_from: always
- valid_to: forever
- principals:
- - example.com
- - examplehost
- check_mode: yes
- - name: Generate always valid cert only for example.com and examplehost
- openssh_cert:
- type: host
- signing_key: '{{ output_dir }}/id_key'
- public_key: '{{ output_dir }}/id_key.pub'
- path: '{{ output_dir }}/id_cert'
- valid_from: always
- valid_to: forever
- principals:
- - example.com
- - examplehost
- - name: Generate always valid cert only for example.com and examplehost (idempotent)
- openssh_cert:
- type: host
- signing_key: '{{ output_dir }}/id_key'
- public_key: '{{ output_dir }}/id_key.pub'
- path: '{{ output_dir }}/id_cert'
- valid_from: always
- valid_to: forever
- principals:
- - example.com
- - examplehost
- - name: Generate always valid cert only for example.com and examplehost (idempotent, check mode)
- openssh_cert:
- type: host
- signing_key: '{{ output_dir }}/id_key'
- public_key: '{{ output_dir }}/id_key.pub'
- path: '{{ output_dir }}/id_cert'
- valid_from: always
- valid_to: forever
- principals:
- - example.com
- - examplehost
- check_mode: yes
- - name: Generate always valid cert only for example.com and examplehost (idempotent, switch)
- openssh_cert:
- type: host
- signing_key: '{{ output_dir }}/id_key'
- public_key: '{{ output_dir }}/id_key.pub'
- path: '{{ output_dir }}/id_cert'
- valid_from: always
- valid_to: forever
- principals:
- - examplehost
- - example.com
- - name: Generate OpenSSH host Certificate that is valid from 21.1.2001 to 21.1.2019 (check mode)
- openssh_cert:
- type: host
- signing_key: '{{ output_dir }}/id_key'
- public_key: '{{ output_dir }}/id_key.pub'
- path: '{{ output_dir }}/id_cert'
- valid_from: "2001-01-21"
- valid_to: "2019-01-21"
- check_mode: yes
- - name: Generate OpenSSH host Certificate that is valid from 21.1.2001 to 21.1.2019
- openssh_cert:
- type: host
- signing_key: '{{ output_dir }}/id_key'
- public_key: '{{ output_dir }}/id_key.pub'
- path: '{{ output_dir }}/id_cert'
- valid_from: "2001-01-21"
- valid_to: "2019-01-21"
- - name: Generate OpenSSH host Certificate that is valid from 21.1.2001 to 21.1.2019 (idempotent)
- openssh_cert:
- type: host
- signing_key: '{{ output_dir }}/id_key'
- public_key: '{{ output_dir }}/id_key.pub'
- path: '{{ output_dir }}/id_cert'
- valid_from: "2001-01-21"
- valid_to: "2019-01-21"
- - name: Generate OpenSSH host Certificate that is valid from 21.1.2001 to 21.1.2019 (idempotent, check mode)
- openssh_cert:
- type: host
- signing_key: '{{ output_dir }}/id_key'
- public_key: '{{ output_dir }}/id_key.pub'
- path: '{{ output_dir }}/id_cert'
- valid_from: "2001-01-21"
- valid_to: "2019-01-21"
- check_mode: yes
- - name: Generate an OpenSSH user Certificate with clear and force-command option (check mode)
- openssh_cert:
- type: user
- signing_key: '{{ output_dir }}/id_key'
- public_key: '{{ output_dir }}/id_key.pub'
- path: '{{ output_dir }}/id_cert'
- options:
- - "clear"
- - "force-command=/tmp/bla/foo"
- valid_from: "2001-01-21"
- valid_to: "2019-01-21"
- check_mode: yes
- - name: Generate an OpenSSH user Certificate with clear and force-command option
- openssh_cert:
- type: user
- signing_key: '{{ output_dir }}/id_key'
- public_key: '{{ output_dir }}/id_key.pub'
- path: '{{ output_dir }}/id_cert'
- options:
- - "clear"
- - "force-command=/tmp/bla/foo"
- valid_from: "2001-01-21"
- valid_to: "2019-01-21"
- - name: Generate an OpenSSH user Certificate with clear and force-command option (idempotent)
- openssh_cert:
- type: user
- signing_key: '{{ output_dir }}/id_key'
- public_key: '{{ output_dir }}/id_key.pub'
- path: '{{ output_dir }}/id_cert'
- options:
- - "clear"
- - "force-command=/tmp/bla/foo"
- valid_from: "2001-01-21"
- valid_to: "2019-01-21"
- - name: Generate an OpenSSH user Certificate with clear and force-command option (idempotent, check mode)
- openssh_cert:
- type: user
- signing_key: '{{ output_dir }}/id_key'
- public_key: '{{ output_dir }}/id_key.pub'
- path: '{{ output_dir }}/id_cert'
- options:
- - "clear"
- - "force-command=/tmp/bla/foo"
- valid_from: "2001-01-21"
- valid_to: "2019-01-21"
- check_mode: yes
- - name: Generate an OpenSSH user Certificate with clear and force-command option (idempotent, switch)
- openssh_cert:
- type: user
- signing_key: '{{ output_dir }}/id_key'
- public_key: '{{ output_dir }}/id_key.pub'
- path: '{{ output_dir }}/id_cert'
- options:
- - "force-command=/tmp/bla/foo"
- - "clear"
- valid_from: "2001-01-21"
- valid_to: "2019-01-21"
- - name: Generate cert without serial
- openssh_cert:
- type: user
- signing_key: '{{ output_dir }}/id_key'
- public_key: '{{ output_dir }}/id_key.pub'
- path: '{{ output_dir }}/id_cert_no_serial'
- valid_from: always
- valid_to: forever
- register: rc_no_serial_number
- - name: check default serial
- assert:
- that:
- - "'Serial: 0' in rc_no_serial_number.info"
- msg: OpenSSH user certificate contains the default serial number.
- - name: Generate cert without serial (idempotent)
- openssh_cert:
- type: user
- signing_key: '{{ output_dir }}/id_key'
- public_key: '{{ output_dir }}/id_key.pub'
- path: '{{ output_dir }}/id_cert_no_serial'
- valid_from: always
- valid_to: forever
- register: rc_no_serial_number_idempotent
- - name: check idempotent
- assert:
- that:
- - rc_no_serial_number_idempotent is not changed
- msg: OpenSSH certificate generation without serial number is idempotent.
- - name: Generate cert with serial 42
- openssh_cert:
- type: user
- signing_key: '{{ output_dir }}/id_key'
- public_key: '{{ output_dir }}/id_key.pub'
- path: '{{ output_dir }}/id_cert_serial_42'
- valid_from: always
- valid_to: forever
- serial_number: 42
- register: rc_serial_number
- - name: check serial 42
- assert:
- that:
- - "'Serial: 42' in rc_serial_number.info"
- msg: OpenSSH user certificate contains the serial number from the params.
- - name: Generate cert with serial 42 (idempotent)
- openssh_cert:
- type: user
- signing_key: '{{ output_dir }}/id_key'
- public_key: '{{ output_dir }}/id_key.pub'
- path: '{{ output_dir }}/id_cert_serial_42'
- valid_from: always
- valid_to: forever
- serial_number: 42
- register: rc_serial_number_idempotent
- - name: check idempotent
- assert:
- that:
- - rc_serial_number_idempotent is not changed
- msg: OpenSSH certificate generation with serial number is idempotent.
- - name: Generate cert with changed serial number
- openssh_cert:
- type: user
- signing_key: '{{ output_dir }}/id_key'
- public_key: '{{ output_dir }}/id_key.pub'
- path: '{{ output_dir }}/id_cert_serial_42'
- valid_from: always
- valid_to: forever
- serial_number: 1337
- register: rc_serial_number_changed
- - name: check changed
- assert:
- that:
- - rc_serial_number_changed is changed
- msg: OpenSSH certificate regenerated upon serial number change.
- - name: Generate cert with removed serial number
- openssh_cert:
- type: user
- signing_key: '{{ output_dir }}/id_key'
- public_key: '{{ output_dir }}/id_key.pub'
- path: '{{ output_dir }}/id_cert_serial_42'
- valid_from: always
- valid_to: forever
- serial_number: 0
- register: rc_serial_number_removed
- - name: check changed
- assert:
- that:
- - rc_serial_number_removed is changed
- msg: OpenSSH certificate regenerated upon serial number removal.
- - name: Generate a new cert with serial number
- openssh_cert:
- type: user
- signing_key: '{{ output_dir }}/id_key'
- public_key: '{{ output_dir }}/id_key.pub'
- path: '{{ output_dir }}/id_cert_serial_ignore'
- valid_from: always
- valid_to: forever
- serial_number: 42
- - name: Generate cert again, omitting the parameter serial_number (idempotent)
- openssh_cert:
- type: user
- signing_key: '{{ output_dir }}/id_key'
- public_key: '{{ output_dir }}/id_key.pub'
- path: '{{ output_dir }}/id_cert_serial_ignore'
- valid_from: always
- valid_to: forever
- register: rc_serial_number_ignored
- - name: check idempotent
- assert:
- that:
- - rc_serial_number_ignored is not changed
- msg: OpenSSH certificate generation with omitted serial number is idempotent.
- - name: Remove certificate (check mode)
- openssh_cert:
- state: absent
- path: '{{ output_dir }}/id_cert'
- #type: user
- #signing_key: '{{ output_dir }}/id_key'
- #public_key: '{{ output_dir }}/id_key.pub'
- #valid_from: "2001-01-21"
- #valid_to: "2019-01-21"
- check_mode: yes
- - name: Remove certificate
- openssh_cert:
- state: absent
- path: '{{ output_dir }}/id_cert'
- #type: user
- #signing_key: '{{ output_dir }}/id_key'
- #public_key: '{{ output_dir }}/id_key.pub'
- #valid_from: "2001-01-21"
- #valid_to: "2019-01-21"
- - name: Remove certificate (idempotent)
- openssh_cert:
- state: absent
- path: '{{ output_dir }}/id_cert'
- #type: user
- #signing_key: '{{ output_dir }}/id_key'
- #public_key: '{{ output_dir }}/id_key.pub'
- #valid_from: "2001-01-21"
- #valid_to: "2019-01-21"
- - name: Remove certificate (idempotent, check mode)
- openssh_cert:
- state: absent
- path: '{{ output_dir }}/id_cert'
- #type: user
- #signing_key: '{{ output_dir }}/id_key'
- #public_key: '{{ output_dir }}/id_key.pub'
- #valid_from: "2001-01-21"
- #valid_to: "2019-01-21"
- check_mode: yes
- - name: Remove keypair (check mode)
- openssh_keypair:
- path: '{{ output_dir }}/id_key'
- state: absent
- check_mode: yes
- - name: Remove keypair
- openssh_keypair:
- path: '{{ output_dir }}/id_key'
- state: absent
- - name: Remove keypair (idempotent)
- openssh_keypair:
- path: '{{ output_dir }}/id_key'
- state: absent
- - name: Remove keypair (idempotent, check mode)
- openssh_keypair:
- path: '{{ output_dir }}/id_key'
- state: absent
- check_mode: yes
diff --git a/test/integration/targets/openssh_keypair/aliases b/test/integration/targets/openssh_keypair/aliases
deleted file mode 100644
index 0b484bbab6..0000000000
--- a/test/integration/targets/openssh_keypair/aliases
+++ /dev/null
@@ -1,3 +0,0 @@
-shippable/posix/group1
-destructive
-skip/aix
diff --git a/test/integration/targets/openssh_keypair/meta/main.yml b/test/integration/targets/openssh_keypair/meta/main.yml
deleted file mode 100644
index dc973f4e00..0000000000
--- a/test/integration/targets/openssh_keypair/meta/main.yml
+++ /dev/null
@@ -1,2 +0,0 @@
-dependencies:
- - setup_ssh_keygen
diff --git a/test/integration/targets/openssh_keypair/tasks/main.yml b/test/integration/targets/openssh_keypair/tasks/main.yml
deleted file mode 100644
index 3458f8ecf1..0000000000
--- a/test/integration/targets/openssh_keypair/tasks/main.yml
+++ /dev/null
@@ -1,375 +0,0 @@
----
-- name: Generate privatekey1 - standard
- openssh_keypair:
- path: '{{ output_dir }}/privatekey1'
- register: privatekey1_result
-
-- name: Generate privatekey1 - standard (idempotent)
- openssh_keypair:
- path: '{{ output_dir }}/privatekey1'
- register: privatekey1_idem_result
-
-- name: Generate privatekey2 - size 2048
- openssh_keypair:
- path: '{{ output_dir }}/privatekey2'
- size: 2048
-
-- name: Generate privatekey3 - type dsa
- openssh_keypair:
- path: '{{ output_dir }}/privatekey3'
- type: dsa
-
-- name: Generate privatekey4 - standard
- openssh_keypair:
- path: '{{ output_dir }}/privatekey4'
-
-- name: Delete privatekey4 - standard
- openssh_keypair:
- state: absent
- path: '{{ output_dir }}/privatekey4'
-
-- name: Generate privatekey5 - standard
- openssh_keypair:
- path: '{{ output_dir }}/privatekey5'
- register: publickey_gen
-
-- name: Generate privatekey6
- openssh_keypair:
- path: '{{ output_dir }}/privatekey6'
- type: rsa
-
-- name: Regenerate privatekey6 via force
- openssh_keypair:
- path: '{{ output_dir }}/privatekey6'
- type: rsa
- force: yes
- register: output_regenerated_via_force
-
-- name: Create broken key
- copy:
- dest: '{{ item }}'
- content: ''
- mode: '0700'
- loop:
- - '{{ output_dir }}/privatekeybroken'
- - '{{ output_dir }}/privatekeybroken.pub'
-
-- name: Regenerate broken key - should fail
- openssh_keypair:
- path: '{{ output_dir }}/privatekeybroken'
- type: rsa
- register: output_broken
- ignore_errors: yes
-
-- name: Regenerate broken key with force
- openssh_keypair:
- path: '{{ output_dir }}/privatekeybroken'
- type: rsa
- force: yes
- register: output_broken_force
-
-- name: Generate read-only private key
- openssh_keypair:
- path: '{{ output_dir }}/privatekeyreadonly'
- type: rsa
- mode: '0200'
-
-- name: Regenerate read-only private key via force
- openssh_keypair:
- path: '{{ output_dir }}/privatekeyreadonly'
- type: rsa
- force: yes
- register: output_read_only
-
-- name: Generate privatekey7 - standard with comment
- openssh_keypair:
- path: '{{ output_dir }}/privatekey7'
- comment: 'test@privatekey7'
- register: privatekey7_result
-
-- name: Modify privatekey7 comment
- openssh_keypair:
- path: '{{ output_dir }}/privatekey7'
- comment: 'test_modified@privatekey7'
- register: privatekey7_modified_result
-
-- name: Generate password protected key
- command: 'ssh-keygen -f {{ output_dir }}/privatekey8 -N password'
-
-- name: Try to modify the password protected key - should fail
- openssh_keypair:
- path: '{{ output_dir }}/privatekey8'
- register: privatekey8_result
- ignore_errors: yes
-
-- name: Try to modify the password protected key with force=yes
- openssh_keypair:
- path: '{{ output_dir }}/privatekey8'
- force: yes
- register: privatekey8_result_force
-
-- import_tasks: ../tests/validate.yml
-
-
-# Test regenerate option
-
-- name: Regenerate - setup simple keys
- openssh_keypair:
- path: '{{ output_dir }}/regenerate-a-{{ item }}'
- type: rsa
- size: 1024
- loop: "{{ regenerate_values }}"
-- name: Regenerate - setup password protected keys
- command: 'ssh-keygen -f {{ output_dir }}/regenerate-b-{{ item }} -N password'
- loop: "{{ regenerate_values }}"
-- name: Regenerate - setup broken keys
- copy:
- dest: '{{ output_dir }}/regenerate-c-{{ item.0 }}{{ item.1 }}'
- content: 'broken key'
- mode: '0700'
- with_nested:
- - "{{ regenerate_values }}"
- - [ '', '.pub' ]
-
-- name: Regenerate - modify broken keys (check mode)
- openssh_keypair:
- path: '{{ output_dir }}/regenerate-c-{{ item }}'
- type: rsa
- size: 1024
- regenerate: '{{ item }}'
- check_mode: yes
- loop: "{{ regenerate_values }}"
- ignore_errors: yes
- register: result
-- assert:
- that:
- - result.results[0] is failed
- - "'Unable to read the key. The key is protected with a passphrase or broken. Will not proceed.' in result.results[0].msg"
- - result.results[1] is failed
- - "'Unable to read the key. The key is protected with a passphrase or broken. Will not proceed.' in result.results[1].msg"
- - result.results[2] is failed
- - "'Unable to read the key. The key is protected with a passphrase or broken. Will not proceed.' in result.results[2].msg"
- - result.results[3] is changed
- - result.results[4] is changed
-
-- name: Regenerate - modify broken keys
- openssh_keypair:
- path: '{{ output_dir }}/regenerate-c-{{ item }}'
- type: rsa
- size: 1024
- regenerate: '{{ item }}'
- loop: "{{ regenerate_values }}"
- ignore_errors: yes
- register: result
-- assert:
- that:
- - result.results[0] is failed
- - "'Unable to read the key. The key is protected with a passphrase or broken. Will not proceed.' in result.results[0].msg"
- - result.results[1] is failed
- - "'Unable to read the key. The key is protected with a passphrase or broken. Will not proceed.' in result.results[1].msg"
- - result.results[2] is failed
- - "'Unable to read the key. The key is protected with a passphrase or broken. Will not proceed.' in result.results[2].msg"
- - result.results[3] is changed
- - result.results[4] is changed
-
-- name: Regenerate - modify password protected keys (check mode)
- openssh_keypair:
- path: '{{ output_dir }}/regenerate-b-{{ item }}'
- type: rsa
- size: 1024
- regenerate: '{{ item }}'
- check_mode: yes
- loop: "{{ regenerate_values }}"
- ignore_errors: yes
- register: result
-- assert:
- that:
- - result.results[0] is failed
- - "'Unable to read the key. The key is protected with a passphrase or broken. Will not proceed.' in result.results[0].msg"
- - result.results[1] is failed
- - "'Unable to read the key. The key is protected with a passphrase or broken. Will not proceed.' in result.results[1].msg"
- - result.results[2] is failed
- - "'Unable to read the key. The key is protected with a passphrase or broken. Will not proceed.' in result.results[2].msg"
- - result.results[3] is changed
- - result.results[4] is changed
-
-- name: Regenerate - modify password protected keys
- openssh_keypair:
- path: '{{ output_dir }}/regenerate-b-{{ item }}'
- type: rsa
- size: 1024
- regenerate: '{{ item }}'
- loop: "{{ regenerate_values }}"
- ignore_errors: yes
- register: result
-- assert:
- that:
- - result.results[0] is failed
- - "'Unable to read the key. The key is protected with a passphrase or broken. Will not proceed.' in result.results[0].msg"
- - result.results[1] is failed
- - "'Unable to read the key. The key is protected with a passphrase or broken. Will not proceed.' in result.results[1].msg"
- - result.results[2] is failed
- - "'Unable to read the key. The key is protected with a passphrase or broken. Will not proceed.' in result.results[2].msg"
- - result.results[3] is changed
- - result.results[4] is changed
-
-- name: Regenerate - not modify regular keys (check mode)
- openssh_keypair:
- path: '{{ output_dir }}/regenerate-a-{{ item }}'
- type: rsa
- size: 1024
- regenerate: '{{ item }}'
- check_mode: yes
- loop: "{{ regenerate_values }}"
- register: result
-- assert:
- that:
- - result.results[0] is not changed
- - result.results[1] is not changed
- - result.results[2] is not changed
- - result.results[3] is not changed
- - result.results[4] is changed
-
-- name: Regenerate - not modify regular keys
- openssh_keypair:
- path: '{{ output_dir }}/regenerate-a-{{ item }}'
- type: rsa
- size: 1024
- regenerate: '{{ item }}'
- loop: "{{ regenerate_values }}"
- register: result
-- assert:
- that:
- - result.results[0] is not changed
- - result.results[1] is not changed
- - result.results[2] is not changed
- - result.results[3] is not changed
- - result.results[4] is changed
-
-- name: Regenerate - adjust key size (check mode)
- openssh_keypair:
- path: '{{ output_dir }}/regenerate-a-{{ item }}'
- type: rsa
- size: 1048
- regenerate: '{{ item }}'
- check_mode: yes
- loop: "{{ regenerate_values }}"
- ignore_errors: yes
- register: result
-- assert:
- that:
- - result.results[0] is success and result.results[0] is not changed
- - result.results[1] is failed
- - "'Key has wrong type and/or size. Will not proceed.' in result.results[1].msg"
- - result.results[2] is changed
- - result.results[3] is changed
- - result.results[4] is changed
-
-- name: Regenerate - adjust key size
- openssh_keypair:
- path: '{{ output_dir }}/regenerate-a-{{ item }}'
- type: rsa
- size: 1048
- regenerate: '{{ item }}'
- loop: "{{ regenerate_values }}"
- ignore_errors: yes
- register: result
-- assert:
- that:
- - result.results[0] is success and result.results[0] is not changed
- - result.results[1] is failed
- - "'Key has wrong type and/or size. Will not proceed.' in result.results[1].msg"
- - result.results[2] is changed
- - result.results[3] is changed
- - result.results[4] is changed
-
-- name: Regenerate - redistribute keys
- copy:
- src: '{{ output_dir }}/regenerate-a-always{{ item.1 }}'
- dest: '{{ output_dir }}/regenerate-a-{{ item.0 }}{{ item.1 }}'
- remote_src: true
- with_nested:
- - "{{ regenerate_values }}"
- - [ '', '.pub' ]
- when: "item.0 != 'always'"
-
-- name: Regenerate - adjust key type (check mode)
- openssh_keypair:
- path: '{{ output_dir }}/regenerate-a-{{ item }}'
- type: dsa
- size: 1024
- regenerate: '{{ item }}'
- check_mode: yes
- loop: "{{ regenerate_values }}"
- ignore_errors: yes
- register: result
-- assert:
- that:
- - result.results[0] is success and result.results[0] is not changed
- - result.results[1] is failed
- - "'Key has wrong type and/or size. Will not proceed.' in result.results[1].msg"
- - result.results[2] is changed
- - result.results[3] is changed
- - result.results[4] is changed
-
-- name: Regenerate - adjust key type
- openssh_keypair:
- path: '{{ output_dir }}/regenerate-a-{{ item }}'
- type: dsa
- size: 1024
- regenerate: '{{ item }}'
- loop: "{{ regenerate_values }}"
- ignore_errors: yes
- register: result
-- assert:
- that:
- - result.results[0] is success and result.results[0] is not changed
- - result.results[1] is failed
- - "'Key has wrong type and/or size. Will not proceed.' in result.results[1].msg"
- - result.results[2] is changed
- - result.results[3] is changed
- - result.results[4] is changed
-
-- name: Regenerate - redistribute keys
- copy:
- src: '{{ output_dir }}/regenerate-a-always{{ item.1 }}'
- dest: '{{ output_dir }}/regenerate-a-{{ item.0 }}{{ item.1 }}'
- remote_src: true
- with_nested:
- - "{{ regenerate_values }}"
- - [ '', '.pub' ]
- when: "item.0 != 'always'"
-
-- name: Regenerate - adjust comment (check mode)
- openssh_keypair:
- path: '{{ output_dir }}/regenerate-a-{{ item }}'
- type: dsa
- size: 1024
- comment: test comment
- regenerate: '{{ item }}'
- check_mode: yes
- loop: "{{ regenerate_values }}"
- ignore_errors: yes
- register: result
-- assert:
- that:
- - result is changed
-
-- name: Regenerate - adjust comment
- openssh_keypair:
- path: '{{ output_dir }}/regenerate-a-{{ item }}'
- type: dsa
- size: 1024
- comment: test comment
- regenerate: '{{ item }}'
- loop: "{{ regenerate_values }}"
- register: result
-- assert:
- that:
- - result is changed
- # for all values but 'always', the key should have not been regenerated.
- # verify this by comparing fingerprints:
- - result.results[0].fingerprint == result.results[1].fingerprint
- - result.results[0].fingerprint == result.results[2].fingerprint
- - result.results[0].fingerprint == result.results[3].fingerprint
- - result.results[0].fingerprint != result.results[4].fingerprint
diff --git a/test/integration/targets/openssh_keypair/tests/validate.yml b/test/integration/targets/openssh_keypair/tests/validate.yml
deleted file mode 100644
index 9f34ab225b..0000000000
--- a/test/integration/targets/openssh_keypair/tests/validate.yml
+++ /dev/null
@@ -1,132 +0,0 @@
----
-- name: Log privatekey1 return values
- debug:
- var: privatekey1_result
-
-- name: Validate privatekey1 return fingerprint
- assert:
- that:
- - privatekey1_result["fingerprint"] is string
- - privatekey1_result["fingerprint"].startswith("SHA256:")
- # only distro old enough that it still gives md5 with no prefix
- when: ansible_distribution != 'CentOS' and ansible_distribution_major_version != '6'
-
-- name: Validate privatekey1 return public_key
- assert:
- that:
- - privatekey1_result["public_key"] is string
- - privatekey1_result["public_key"].startswith("ssh-rsa ")
-
-- name: Validate privatekey1 return size value
- assert:
- that:
- - privatekey1_result["size"]|type_debug == 'int'
- - privatekey1_result["size"] == 4096
-
-- name: Validate privatekey1 return key type
- assert:
- that:
- - privatekey1_result["type"] is string
- - privatekey1_result["type"] == "rsa"
-
-- name: Validate privatekey1 (test - RSA key with size 4096 bits)
- shell: "ssh-keygen -lf {{ output_dir }}/privatekey1 | grep -o -E '^[0-9]+'"
- register: privatekey1
-
-- name: Validate privatekey1 (assert - RSA key with size 4096 bits)
- assert:
- that:
- - privatekey1.stdout == '4096'
-
-- name: Validate privatekey1 idempotence
- assert:
- that:
- - privatekey1_idem_result is not changed
-
-
-- name: Validate privatekey2 (test - RSA key with size 2048 bits)
- shell: "ssh-keygen -lf {{ output_dir }}/privatekey2 | grep -o -E '^[0-9]+'"
- register: privatekey2
-
-- name: Validate privatekey2 (assert - RSA key with size 2048 bits)
- assert:
- that:
- - privatekey2.stdout == '2048'
-
-
-- name: Validate privatekey3 (test - DSA key with size 1024 bits)
- shell: "ssh-keygen -lf {{ output_dir }}/privatekey3 | grep -o -E '^[0-9]+'"
- register: privatekey3
-
-- name: Validate privatekey3 (assert - DSA key with size 4096 bits)
- assert:
- that:
- - privatekey3.stdout == '1024'
-
-
-- name: Validate privatekey4 (test - Ensure key has been removed)
- stat:
- path: '{{ output_dir }}/privatekey4'
- register: privatekey4
-
-- name: Validate privatekey4 (assert - Ensure key has been removed)
- assert:
- that:
- - privatekey4.stat.exists == False
-
-
-- name: Validate privatekey5 (assert - Public key module output equal to the public key on host)
- assert:
- that:
- - "publickey_gen.public_key == lookup('file', output_dir ~ '/privatekey5.pub').strip('\n')"
-
-- name: Verify that privatekey6 will be regenerated via force
- assert:
- that:
- - output_regenerated_via_force is changed
-
-
-- name: Verify that broken key will cause failure
- assert:
- that:
- - output_broken is failed
- - "'Unable to read the key. The key is protected with a passphrase or broken.' in output_broken.msg"
-
-
-- name: Verify that broken key will be regenerated if force=yes is specified
- assert:
- that:
- - output_broken_force is changed
-
-
-- name: Verify that read-only key will be regenerated
- assert:
- that:
- - output_read_only is changed
-
-
-- name: Validate privatekey7 (assert - Public key remains the same after comment change)
- assert:
- that:
- - privatekey7_result.public_key == privatekey7_modified_result.public_key
-
-- name: Validate privatekey7 comment on creation
- assert:
- that:
- - privatekey7_result.comment == 'test@privatekey7'
-
-- name: Validate privatekey7 comment update
- assert:
- that:
- - privatekey7_modified_result.comment == 'test_modified@privatekey7'
-
-- name: Check that password protected key made module fail
- assert:
- that:
- - privatekey8_result is failed
- - "'Unable to read the key. The key is protected with a passphrase or broken.' in privatekey8_result.msg"
-
-- name: Check that password protected key was regenerated with force=yes
- assert:
- that:
- - privatekey8_result_force is changed
diff --git a/test/integration/targets/openssh_keypair/vars/main.yml b/test/integration/targets/openssh_keypair/vars/main.yml
deleted file mode 100644
index 81eb611f8a..0000000000
--- a/test/integration/targets/openssh_keypair/vars/main.yml
+++ /dev/null
@@ -1,7 +0,0 @@
----
-regenerate_values:
- - never
- - fail
- - partial_idempotence
- - full_idempotence
- - always
diff --git a/test/integration/targets/openssl_certificate/aliases b/test/integration/targets/openssl_certificate/aliases
deleted file mode 100644
index d339371c56..0000000000
--- a/test/integration/targets/openssl_certificate/aliases
+++ /dev/null
@@ -1,3 +0,0 @@
-shippable/posix/group5
-destructive
-skip/aix
diff --git a/test/integration/targets/openssl_certificate/meta/main.yml b/test/integration/targets/openssl_certificate/meta/main.yml
deleted file mode 100644
index 800aff6428..0000000000
--- a/test/integration/targets/openssl_certificate/meta/main.yml
+++ /dev/null
@@ -1,2 +0,0 @@
-dependencies:
- - setup_openssl
diff --git a/test/integration/targets/openssl_certificate/tasks/assertonly.yml b/test/integration/targets/openssl_certificate/tasks/assertonly.yml
deleted file mode 100644
index 994e00df81..0000000000
--- a/test/integration/targets/openssl_certificate/tasks/assertonly.yml
+++ /dev/null
@@ -1,152 +0,0 @@
----
-- name: (Assertonly, {{select_crypto_backend}}) - Generate privatekey
- openssl_privatekey:
- path: '{{ output_dir }}/privatekey.pem'
-
-- name: (Assertonly, {{select_crypto_backend}}) - Generate privatekey with password
- openssl_privatekey:
- path: '{{ output_dir }}/privatekeypw.pem'
- passphrase: hunter2
- cipher: auto
- select_crypto_backend: cryptography
-
-- name: (Assertonly, {{select_crypto_backend}}) - Generate CSR (no extensions)
- openssl_csr:
- path: '{{ output_dir }}/csr_noext.csr'
- privatekey_path: '{{ output_dir }}/privatekey.pem'
- subject:
- commonName: www.example.com
- useCommonNameForSAN: no
-
-- name: (Assertonly, {{select_crypto_backend}}) - Generate CSR (with SANs)
- openssl_csr:
- path: '{{ output_dir }}/csr_sans.csr'
- privatekey_path: '{{ output_dir }}/privatekey.pem'
- subject:
- commonName: www.example.com
- subject_alt_name:
- - "DNS:ansible.com"
- - "IP:127.0.0.1"
- - "IP:::1"
- useCommonNameForSAN: no
-
-- name: (Assertonly, {{select_crypto_backend}}) - Generate selfsigned certificate (no extensions)
- openssl_certificate:
- path: '{{ output_dir }}/cert_noext.pem'
- csr_path: '{{ output_dir }}/csr_noext.csr'
- privatekey_path: '{{ output_dir }}/privatekey.pem'
- provider: selfsigned
- selfsigned_digest: sha256
- select_crypto_backend: '{{ select_crypto_backend }}'
-
-- name: (Assertonly, {{select_crypto_backend}}) - Generate selfsigned certificate (with SANs)
- openssl_certificate:
- path: '{{ output_dir }}/cert_sans.pem'
- csr_path: '{{ output_dir }}/csr_sans.csr'
- privatekey_path: '{{ output_dir }}/privatekey.pem'
- provider: selfsigned
- selfsigned_digest: sha256
- select_crypto_backend: '{{ select_crypto_backend }}'
-
-- name: (Assertonly, {{select_crypto_backend}}) - Assert that subject_alt_name is there (should fail)
- openssl_certificate:
- path: '{{ output_dir }}/cert_noext.pem'
- provider: assertonly
- subject_alt_name:
- - "DNS:example.com"
- select_crypto_backend: '{{ select_crypto_backend }}'
- ignore_errors: yes
- register: extension_missing_san
-
-- name: (Assertonly, {{select_crypto_backend}}) - Assert that subject_alt_name is there
- openssl_certificate:
- path: '{{ output_dir }}/cert_sans.pem'
- provider: assertonly
- subject_alt_name:
- - "DNS:ansible.com"
- - "IP:127.0.0.1"
- - "IP:::1"
- select_crypto_backend: '{{ select_crypto_backend }}'
- register: extension_san
-
-- name: (Assertonly, {{select_crypto_backend}}) - Assert that subject_alt_name is there (strict)
- openssl_certificate:
- path: '{{ output_dir }}/cert_sans.pem'
- provider: assertonly
- subject_alt_name:
- - "DNS:ansible.com"
- - "IP:127.0.0.1"
- - "IP:::1"
- subject_alt_name_strict: yes
- select_crypto_backend: '{{ select_crypto_backend }}'
- register: extension_san_strict
-
-- name: (Assertonly, {{select_crypto_backend}}) - Assert that key_usage is there (should fail)
- openssl_certificate:
- path: '{{ output_dir }}/cert_noext.pem'
- provider: assertonly
- key_usage:
- - digitalSignature
- select_crypto_backend: '{{ select_crypto_backend }}'
- ignore_errors: yes
- register: extension_missing_ku
-
-- name: (Assertonly, {{select_crypto_backend}}) - Assert that extended_key_usage is there (should fail)
- openssl_certificate:
- path: '{{ output_dir }}/cert_noext.pem'
- provider: assertonly
- extended_key_usage:
- - biometricInfo
- select_crypto_backend: '{{ select_crypto_backend }}'
- ignore_errors: yes
- register: extension_missing_eku
-
-- assert:
- that:
- - extension_missing_san is failed
- - "'Found no subjectAltName extension' in extension_missing_san.msg"
- - extension_san is succeeded
- - extension_san_strict is succeeded
- - extension_missing_ku is failed
- - "'Found no keyUsage extension' in extension_missing_ku.msg"
- - extension_missing_eku is failed
- - "'Found no extendedKeyUsage extension' in extension_missing_eku.msg"
-
-- name: (Assertonly, {{select_crypto_backend}}) - Check private key passphrase fail 1
- openssl_certificate:
- path: '{{ output_dir }}/cert_noext.pem'
- privatekey_path: '{{ output_dir }}/privatekey.pem'
- privatekey_passphrase: hunter2
- provider: assertonly
- select_crypto_backend: '{{ select_crypto_backend }}'
- ignore_errors: yes
- register: passphrase_error_1
-
-- name: (Assertonly, {{select_crypto_backend}}) - Check private key passphrase fail 2
- openssl_certificate:
- path: '{{ output_dir }}/cert_noext.pem'
- privatekey_path: '{{ output_dir }}/privatekeypw.pem'
- privatekey_passphrase: wrong_password
- provider: assertonly
- select_crypto_backend: '{{ select_crypto_backend }}'
- ignore_errors: yes
- register: passphrase_error_2
-
-- name: (Assertonly, {{select_crypto_backend}}) - Check private key passphrase fail 3
- openssl_certificate:
- path: '{{ output_dir }}/cert_noext.pem'
- privatekey_path: '{{ output_dir }}/privatekeypw.pem'
- provider: assertonly
- select_crypto_backend: '{{ select_crypto_backend }}'
- ignore_errors: yes
- register: passphrase_error_3
-
-- name: (Assertonly, {{select_crypto_backend}}) -
- assert:
- that:
- - passphrase_error_1 is failed
- - "'assphrase' in passphrase_error_1.msg or 'assword' in passphrase_error_1.msg"
- - passphrase_error_2 is failed
- - "'assphrase' in passphrase_error_2.msg or 'assword' in passphrase_error_2.msg or 'serializ' in passphrase_error_2.msg"
- - passphrase_error_3 is failed
- - "'assphrase' in passphrase_error_3.msg or 'assword' in passphrase_error_3.msg or 'serializ' in passphrase_error_3.msg"
diff --git a/test/integration/targets/openssl_certificate/tasks/expired.yml b/test/integration/targets/openssl_certificate/tasks/expired.yml
deleted file mode 100644
index 248ce4a239..0000000000
--- a/test/integration/targets/openssl_certificate/tasks/expired.yml
+++ /dev/null
@@ -1,48 +0,0 @@
----
-- name: (Expired, {{select_crypto_backend}}) Generate privatekey
- openssl_privatekey:
- path: '{{ output_dir }}/has_expired_privatekey.pem'
-
-- name: (Expired, {{select_crypto_backend}}) Generate CSR
- openssl_csr:
- path: '{{ output_dir }}/has_expired_csr.csr'
- privatekey_path: '{{ output_dir }}/has_expired_privatekey.pem'
- subject:
- commonName: www.example.com
-
-- name: (Expired, {{select_crypto_backend}}) Generate expired selfsigned certificate
- openssl_certificate:
- path: '{{ output_dir }}/has_expired_cert.pem'
- csr_path: '{{ output_dir }}/has_expired_csr.csr'
- privatekey_path: '{{ output_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 x509 -req -days -1 -in {{ output_dir }}/has_expired_csr.csr -signkey {{ output_dir }}/has_expired_privatekey.pem -out {{ output_dir }}/has_expired_cert.pem"
- when: select_crypto_backend == 'cryptography' # So we create it with 'command'
-
-- name: "(Expired) Check task fails because cert is expired (has_expired: false)"
- openssl_certificate:
- provider: assertonly
- path: "{{ output_dir }}/has_expired_cert.pem"
- has_expired: false
- select_crypto_backend: '{{ select_crypto_backend }}'
- ignore_errors: true
- register: expired_cert_check
-
-- name: (Expired, {{select_crypto_backend}}) Ensure previous task failed
- assert:
- that: expired_cert_check is failed
-
-- name: "(Expired) Check expired cert check is ignored (has_expired: true)"
- openssl_certificate:
- provider: assertonly
- path: "{{ output_dir }}/has_expired_cert.pem"
- has_expired: true
- select_crypto_backend: '{{ select_crypto_backend }}'
- register: expired_cert_skip
diff --git a/test/integration/targets/openssl_certificate/tasks/impl.yml b/test/integration/targets/openssl_certificate/tasks/impl.yml
deleted file mode 100644
index f215591f60..0000000000
--- a/test/integration/targets/openssl_certificate/tasks/impl.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-- debug:
- msg: "Executing tests with backend {{ select_crypto_backend }}"
-- import_tasks: assertonly.yml
-- import_tasks: expired.yml
-- import_tasks: selfsigned.yml
-- import_tasks: ownca.yml
-- import_tasks: removal.yml
diff --git a/test/integration/targets/openssl_certificate/tasks/main.yml b/test/integration/targets/openssl_certificate/tasks/main.yml
deleted file mode 100644
index f8c1c5fefb..0000000000
--- a/test/integration/targets/openssl_certificate/tasks/main.yml
+++ /dev/null
@@ -1,22 +0,0 @@
----
-- name: Running tests with pyOpenSSL backend
- include_tasks: impl.yml
- vars:
- select_crypto_backend: pyopenssl
- when: pyopenssl_version.stdout is version('0.15', '>=')
-
-- name: Remove output directory
- file:
- path: "{{ output_dir }}"
- state: absent
-
-- name: Re-create output directory
- file:
- path: "{{ output_dir }}"
- state: directory
-
-- name: Running tests with cryptography backend
- include_tasks: impl.yml
- vars:
- select_crypto_backend: cryptography
- when: cryptography_version.stdout is version('1.6', '>=')
diff --git a/test/integration/targets/openssl_certificate/tasks/ownca.yml b/test/integration/targets/openssl_certificate/tasks/ownca.yml
deleted file mode 100644
index 0d1381ff02..0000000000
--- a/test/integration/targets/openssl_certificate/tasks/ownca.yml
+++ /dev/null
@@ -1,578 +0,0 @@
----
-- name: (OwnCA, {{select_crypto_backend}}) Generate CA privatekey
- openssl_privatekey:
- path: '{{ output_dir }}/ca_privatekey.pem'
-
-- name: (OwnCA, {{select_crypto_backend}}) Generate CA privatekey with passphrase
- openssl_privatekey:
- path: '{{ output_dir }}/ca_privatekey_pw.pem'
- passphrase: hunter2
- cipher: auto
- select_crypto_backend: cryptography
-
-- name: (OwnCA, {{select_crypto_backend}}) Generate CA CSR
- openssl_csr:
- path: '{{ output_dir }}/ca_csr.csr'
- privatekey_path: '{{ output_dir }}/ca_privatekey.pem'
- subject:
- commonName: Example CA
- useCommonNameForSAN: no
- basic_constraints:
- - 'CA:TRUE'
- basic_constraints_critical: yes
-
-- name: (OwnCA, {{select_crypto_backend}}) Generate CA CSR (privatekey passphrase)
- openssl_csr:
- path: '{{ output_dir }}/ca_csr_pw.csr'
- privatekey_path: '{{ output_dir }}/ca_privatekey_pw.pem'
- privatekey_passphrase: hunter2
- subject:
- commonName: Example CA
- useCommonNameForSAN: no
- basic_constraints:
- - 'CA:TRUE'
- basic_constraints_critical: yes
-
-- name: (OwnCA, {{select_crypto_backend}}) Generate selfsigned CA certificate
- openssl_certificate:
- path: '{{ output_dir }}/ca_cert.pem'
- csr_path: '{{ output_dir }}/ca_csr.csr'
- privatekey_path: '{{ output_dir }}/ca_privatekey.pem'
- provider: selfsigned
- selfsigned_digest: sha256
- select_crypto_backend: '{{ select_crypto_backend }}'
-
-- name: (OwnCA, {{select_crypto_backend}}) Generate selfsigned CA certificate (privatekey passphrase)
- openssl_certificate:
- path: '{{ output_dir }}/ca_cert_pw.pem'
- csr_path: '{{ output_dir }}/ca_csr_pw.csr'
- privatekey_path: '{{ output_dir }}/ca_privatekey_pw.pem'
- privatekey_passphrase: hunter2
- provider: selfsigned
- selfsigned_digest: sha256
- select_crypto_backend: '{{ select_crypto_backend }}'
-
-- name: (OwnCA, {{select_crypto_backend}}) Generate ownca certificate
- openssl_certificate:
- path: '{{ output_dir }}/ownca_cert.pem'
- csr_path: '{{ output_dir }}/csr.csr'
- privatekey_path: '{{ output_dir }}/privatekey.pem'
- ownca_path: '{{ output_dir }}/ca_cert.pem'
- ownca_privatekey_path: '{{ output_dir }}/ca_privatekey.pem'
- provider: ownca
- ownca_digest: sha256
- select_crypto_backend: '{{ select_crypto_backend }}'
- return_content: yes
- register: ownca_certificate
-
-- name: (OwnCA, {{select_crypto_backend}}) Generate ownca certificate (idempotent)
- openssl_certificate:
- path: '{{ output_dir }}/ownca_cert.pem'
- csr_path: '{{ output_dir }}/csr.csr'
- privatekey_path: '{{ output_dir }}/privatekey.pem'
- ownca_path: '{{ output_dir }}/ca_cert.pem'
- ownca_privatekey_path: '{{ output_dir }}/ca_privatekey.pem'
- provider: ownca
- ownca_digest: sha256
- select_crypto_backend: '{{ select_crypto_backend }}'
- return_content: yes
- register: ownca_certificate_idempotence
-
-- name: (OwnCA, {{select_crypto_backend}}) Generate ownca certificate (check mode)
- openssl_certificate:
- path: '{{ output_dir }}/ownca_cert.pem'
- csr_path: '{{ output_dir }}/csr.csr'
- privatekey_path: '{{ output_dir }}/privatekey.pem'
- ownca_path: '{{ output_dir }}/ca_cert.pem'
- ownca_privatekey_path: '{{ output_dir }}/ca_privatekey.pem'
- provider: ownca
- ownca_digest: sha256
- select_crypto_backend: '{{ select_crypto_backend }}'
- check_mode: yes
-
-- name: (OwnCA, {{select_crypto_backend}}) Check ownca certificate
- openssl_certificate:
- path: '{{ output_dir }}/ownca_cert.pem'
- privatekey_path: '{{ output_dir }}/privatekey.pem'
- provider: assertonly
- has_expired: False
- version: 3
- signature_algorithms:
- - sha256WithRSAEncryption
- - sha256WithECDSAEncryption
- subject:
- commonName: www.example.com
- issuer:
- commonName: Example CA
- select_crypto_backend: '{{ select_crypto_backend }}'
-
-- name: (OwnCA, {{select_crypto_backend}}) Generate ownca v2 certificate
- openssl_certificate:
- path: '{{ output_dir }}/ownca_cert_v2.pem'
- csr_path: '{{ output_dir }}/csr.csr'
- privatekey_path: '{{ output_dir }}/privatekey.pem'
- ownca_path: '{{ output_dir }}/ca_cert.pem'
- ownca_privatekey_path: '{{ output_dir }}/ca_privatekey.pem'
- provider: ownca
- ownca_digest: sha256
- ownca_version: 2
- select_crypto_backend: '{{ select_crypto_backend }}'
- register: ownca_v2_certificate
- ignore_errors: true
-
-- name: (OwnCA, {{select_crypto_backend}}) Generate ownca certificate2
- openssl_certificate:
- path: '{{ output_dir }}/ownca_cert2.pem'
- csr_path: '{{ output_dir }}/csr2.csr'
- privatekey_path: '{{ output_dir }}/privatekey2.pem'
- ownca_path: '{{ output_dir }}/ca_cert.pem'
- ownca_privatekey_path: '{{ output_dir }}/ca_privatekey.pem'
- provider: ownca
- ownca_digest: sha256
- select_crypto_backend: '{{ select_crypto_backend }}'
-
-- name: (OwnCA, {{select_crypto_backend}}) Check ownca certificate2
- openssl_certificate:
- path: '{{ output_dir }}/ownca_cert2.pem'
- privatekey_path: '{{ output_dir }}/privatekey2.pem'
- provider: assertonly
- has_expired: False
- version: 3
- signature_algorithms:
- - sha256WithRSAEncryption
- - sha256WithECDSAEncryption
- subject:
- commonName: www.example.com
- C: US
- ST: California
- L: Los Angeles
- O: ACME Inc.
- OU:
- - Roadrunner pest control
- - Pyrotechnics
- keyUsage:
- - digitalSignature
- extendedKeyUsage:
- - ipsecUser
- - biometricInfo
- issuer:
- commonName: Example CA
- select_crypto_backend: '{{ select_crypto_backend }}'
-
-- name: (OwnCA, {{select_crypto_backend}}) Create ownca certificate with notBefore and notAfter
- openssl_certificate:
- provider: ownca
- ownca_not_before: 20181023133742Z
- ownca_not_after: 20191023133742Z
- path: "{{ output_dir }}/ownca_cert3.pem"
- csr_path: "{{ output_dir }}/csr.csr"
- privatekey_path: "{{ output_dir }}/privatekey3.pem"
- ownca_path: '{{ output_dir }}/ca_cert.pem'
- ownca_privatekey_path: '{{ output_dir }}/ca_privatekey.pem'
- select_crypto_backend: '{{ select_crypto_backend }}'
-
-- name: (OwnCA, {{select_crypto_backend}}) Create ownca certificate with relative notBefore and notAfter
- openssl_certificate:
- provider: ownca
- ownca_not_before: +1s
- ownca_not_after: +52w
- path: "{{ output_dir }}/ownca_cert4.pem"
- csr_path: "{{ output_dir }}/csr.csr"
- privatekey_path: "{{ output_dir }}/privatekey3.pem"
- ownca_path: '{{ output_dir }}/ca_cert.pem'
- ownca_privatekey_path: '{{ output_dir }}/ca_privatekey.pem'
- select_crypto_backend: '{{ select_crypto_backend }}'
-
-- name: (OwnCA, {{select_crypto_backend}}) Generate ownca ECC certificate
- openssl_certificate:
- path: '{{ output_dir }}/ownca_cert_ecc.pem'
- csr_path: '{{ output_dir }}/csr_ecc.csr'
- privatekey_path: '{{ output_dir }}/privatekey_ecc.pem'
- ownca_path: '{{ output_dir }}/ca_cert.pem'
- ownca_privatekey_path: '{{ output_dir }}/ca_privatekey.pem'
- provider: ownca
- ownca_digest: sha256
- select_crypto_backend: '{{ select_crypto_backend }}'
- register: ownca_certificate_ecc
-
-- name: (OwnCA, {{select_crypto_backend}}) Generate selfsigned certificate (privatekey passphrase)
- openssl_certificate:
- path: '{{ output_dir }}/ownca_cert_ecc_2.pem'
- csr_path: '{{ output_dir }}/csr_ecc.csr'
- ownca_path: '{{ output_dir }}/ca_cert_pw.pem'
- ownca_privatekey_path: '{{ output_dir }}/ca_privatekey_pw.pem'
- ownca_privatekey_passphrase: hunter2
- provider: ownca
- ownca_digest: sha256
- select_crypto_backend: '{{ select_crypto_backend }}'
- register: selfsigned_certificate_passphrase
-
-- name: (OwnCA, {{select_crypto_backend}}) Generate ownca certificate (failed passphrase 1)
- openssl_certificate:
- path: '{{ output_dir }}/ownca_cert_pw1.pem'
- csr_path: '{{ output_dir }}/csr_ecc.csr'
- ownca_path: '{{ output_dir }}/ca_cert.pem'
- ownca_privatekey_path: '{{ output_dir }}/ca_privatekey.pem'
- ownca_privatekey_passphrase: hunter2
- provider: ownca
- ownca_digest: sha256
- select_crypto_backend: '{{ select_crypto_backend }}'
- ignore_errors: yes
- register: passphrase_error_1
-
-- name: (OwnCA, {{select_crypto_backend}}) Generate ownca certificate (failed passphrase 2)
- openssl_certificate:
- path: '{{ output_dir }}/ownca_cert_pw2.pem'
- csr_path: '{{ output_dir }}/csr_ecc.csr'
- ownca_path: '{{ output_dir }}/ca_cert.pem'
- ownca_privatekey_path: '{{ output_dir }}/privatekeypw.pem'
- ownca_privatekey_passphrase: wrong_password
- provider: ownca
- ownca_digest: sha256
- select_crypto_backend: '{{ select_crypto_backend }}'
- ignore_errors: yes
- register: passphrase_error_2
-
-- name: (OwnCA, {{select_crypto_backend}}) Generate ownca certificate (failed passphrase 3)
- openssl_certificate:
- path: '{{ output_dir }}/ownca_cert_pw3.pem'
- csr_path: '{{ output_dir }}/csr_ecc.csr'
- ownca_path: '{{ output_dir }}/ca_cert.pem'
- ownca_privatekey_path: '{{ output_dir }}/privatekeypw.pem'
- provider: ownca
- ownca_digest: sha256
- select_crypto_backend: '{{ select_crypto_backend }}'
- ignore_errors: yes
- register: passphrase_error_3
-
-- name: Create broken certificate
- copy:
- dest: "{{ output_dir }}/ownca_broken.pem"
- content: "broken"
-- name: Regenerate broken cert
- openssl_certificate:
- path: '{{ output_dir }}/ownca_broken.pem'
- csr_path: '{{ output_dir }}/csr_ecc.csr'
- privatekey_path: '{{ output_dir }}/privatekey_ecc.pem'
- ownca_path: '{{ output_dir }}/ca_cert.pem'
- ownca_privatekey_path: '{{ output_dir }}/ca_privatekey.pem'
- provider: ownca
- ownca_digest: sha256
- register: ownca_broken
-
-- name: (OwnCA, {{select_crypto_backend}}) Backup test
- openssl_certificate:
- path: '{{ output_dir }}/ownca_cert_backup.pem'
- csr_path: '{{ output_dir }}/csr_ecc.csr'
- ownca_path: '{{ output_dir }}/ca_cert.pem'
- ownca_privatekey_path: '{{ output_dir }}/privatekey.pem'
- provider: ownca
- ownca_digest: sha256
- backup: yes
- select_crypto_backend: '{{ select_crypto_backend }}'
- register: ownca_backup_1
-- name: (OwnCA, {{select_crypto_backend}}) Backup test (idempotent)
- openssl_certificate:
- path: '{{ output_dir }}/ownca_cert_backup.pem'
- csr_path: '{{ output_dir }}/csr_ecc.csr'
- ownca_path: '{{ output_dir }}/ca_cert.pem'
- ownca_privatekey_path: '{{ output_dir }}/privatekey.pem'
- provider: ownca
- ownca_digest: sha256
- backup: yes
- select_crypto_backend: '{{ select_crypto_backend }}'
- register: ownca_backup_2
-- name: (OwnCA, {{select_crypto_backend}}) Backup test (change)
- openssl_certificate:
- path: '{{ output_dir }}/ownca_cert_backup.pem'
- csr_path: '{{ output_dir }}/csr.csr'
- ownca_path: '{{ output_dir }}/ca_cert.pem'
- ownca_privatekey_path: '{{ output_dir }}/privatekey.pem'
- provider: ownca
- ownca_digest: sha256
- backup: yes
- select_crypto_backend: '{{ select_crypto_backend }}'
- register: ownca_backup_3
-- name: (OwnCA, {{select_crypto_backend}}) Backup test (remove)
- openssl_certificate:
- path: '{{ output_dir }}/ownca_cert_backup.pem'
- state: absent
- provider: ownca
- backup: yes
- select_crypto_backend: '{{ select_crypto_backend }}'
- register: ownca_backup_4
-- name: (OwnCA, {{select_crypto_backend}}) Backup test (remove, idempotent)
- openssl_certificate:
- path: '{{ output_dir }}/ownca_cert_backup.pem'
- state: absent
- provider: ownca
- backup: yes
- select_crypto_backend: '{{ select_crypto_backend }}'
- register: ownca_backup_5
-
-- name: (OwnCA, {{select_crypto_backend}}) Create subject key identifier
- openssl_certificate:
- path: '{{ output_dir }}/ownca_cert_ski.pem'
- csr_path: '{{ output_dir }}/csr_ecc.csr'
- ownca_path: '{{ output_dir }}/ca_cert.pem'
- ownca_privatekey_path: '{{ output_dir }}/privatekey.pem'
- provider: ownca
- ownca_digest: sha256
- ownca_create_subject_key_identifier: always_create
- select_crypto_backend: '{{ select_crypto_backend }}'
- when: select_crypto_backend != 'pyopenssl'
- register: ownca_subject_key_identifier_1
-
-- name: (OwnCA, {{select_crypto_backend}}) Create subject key identifier (idempotency)
- openssl_certificate:
- path: '{{ output_dir }}/ownca_cert_ski.pem'
- csr_path: '{{ output_dir }}/csr_ecc.csr'
- ownca_path: '{{ output_dir }}/ca_cert.pem'
- ownca_privatekey_path: '{{ output_dir }}/privatekey.pem'
- provider: ownca
- ownca_digest: sha256
- ownca_create_subject_key_identifier: always_create
- select_crypto_backend: '{{ select_crypto_backend }}'
- when: select_crypto_backend != 'pyopenssl'
- register: ownca_subject_key_identifier_2
-
-- name: (OwnCA, {{select_crypto_backend}}) Create subject key identifier (remove)
- openssl_certificate:
- path: '{{ output_dir }}/ownca_cert_ski.pem'
- csr_path: '{{ output_dir }}/csr_ecc.csr'
- ownca_path: '{{ output_dir }}/ca_cert.pem'
- ownca_privatekey_path: '{{ output_dir }}/privatekey.pem'
- provider: ownca
- ownca_digest: sha256
- ownca_create_subject_key_identifier: never_create
- select_crypto_backend: '{{ select_crypto_backend }}'
- when: select_crypto_backend != 'pyopenssl'
- register: ownca_subject_key_identifier_3
-
-- name: (OwnCA, {{select_crypto_backend}}) Create subject key identifier (remove idempotency)
- openssl_certificate:
- path: '{{ output_dir }}/ownca_cert_ski.pem'
- csr_path: '{{ output_dir }}/csr_ecc.csr'
- ownca_path: '{{ output_dir }}/ca_cert.pem'
- ownca_privatekey_path: '{{ output_dir }}/privatekey.pem'
- provider: ownca
- ownca_digest: sha256
- ownca_create_subject_key_identifier: never_create
- select_crypto_backend: '{{ select_crypto_backend }}'
- when: select_crypto_backend != 'pyopenssl'
- register: ownca_subject_key_identifier_4
-
-- name: (OwnCA, {{select_crypto_backend}}) Create subject key identifier (re-enable)
- openssl_certificate:
- path: '{{ output_dir }}/ownca_cert_ski.pem'
- csr_path: '{{ output_dir }}/csr_ecc.csr'
- ownca_path: '{{ output_dir }}/ca_cert.pem'
- ownca_privatekey_path: '{{ output_dir }}/privatekey.pem'
- provider: ownca
- ownca_digest: sha256
- ownca_create_subject_key_identifier: always_create
- select_crypto_backend: '{{ select_crypto_backend }}'
- when: select_crypto_backend != 'pyopenssl'
- register: ownca_subject_key_identifier_5
-
-- name: (OwnCA, {{select_crypto_backend}}) Create authority key identifier
- openssl_certificate:
- path: '{{ output_dir }}/ownca_cert_aki.pem'
- csr_path: '{{ output_dir }}/csr_ecc.csr'
- ownca_path: '{{ output_dir }}/ca_cert.pem'
- ownca_privatekey_path: '{{ output_dir }}/privatekey.pem'
- provider: ownca
- ownca_digest: sha256
- ownca_create_authority_key_identifier: yes
- select_crypto_backend: '{{ select_crypto_backend }}'
- when: select_crypto_backend != 'pyopenssl'
- register: ownca_authority_key_identifier_1
-
-- name: (OwnCA, {{select_crypto_backend}}) Create authority key identifier (idempotency)
- openssl_certificate:
- path: '{{ output_dir }}/ownca_cert_aki.pem'
- csr_path: '{{ output_dir }}/csr_ecc.csr'
- ownca_path: '{{ output_dir }}/ca_cert.pem'
- ownca_privatekey_path: '{{ output_dir }}/privatekey.pem'
- provider: ownca
- ownca_digest: sha256
- ownca_create_authority_key_identifier: yes
- select_crypto_backend: '{{ select_crypto_backend }}'
- when: select_crypto_backend != 'pyopenssl'
- register: ownca_authority_key_identifier_2
-
-- name: (OwnCA, {{select_crypto_backend}}) Create authority key identifier (remove)
- openssl_certificate:
- path: '{{ output_dir }}/ownca_cert_aki.pem'
- csr_path: '{{ output_dir }}/csr_ecc.csr'
- ownca_path: '{{ output_dir }}/ca_cert.pem'
- ownca_privatekey_path: '{{ output_dir }}/privatekey.pem'
- provider: ownca
- ownca_digest: sha256
- ownca_create_authority_key_identifier: no
- select_crypto_backend: '{{ select_crypto_backend }}'
- when: select_crypto_backend != 'pyopenssl'
- register: ownca_authority_key_identifier_3
-
-- name: (OwnCA, {{select_crypto_backend}}) Create authority key identifier (remove idempotency)
- openssl_certificate:
- path: '{{ output_dir }}/ownca_cert_aki.pem'
- csr_path: '{{ output_dir }}/csr_ecc.csr'
- ownca_path: '{{ output_dir }}/ca_cert.pem'
- ownca_privatekey_path: '{{ output_dir }}/privatekey.pem'
- provider: ownca
- ownca_digest: sha256
- ownca_create_authority_key_identifier: no
- select_crypto_backend: '{{ select_crypto_backend }}'
- when: select_crypto_backend != 'pyopenssl'
- register: ownca_authority_key_identifier_4
-
-- name: (OwnCA, {{select_crypto_backend}}) Create authority key identifier (re-add)
- openssl_certificate:
- path: '{{ output_dir }}/ownca_cert_aki.pem'
- csr_path: '{{ output_dir }}/csr_ecc.csr'
- ownca_path: '{{ output_dir }}/ca_cert.pem'
- ownca_privatekey_path: '{{ output_dir }}/privatekey.pem'
- provider: ownca
- ownca_digest: sha256
- ownca_create_authority_key_identifier: yes
- select_crypto_backend: '{{ select_crypto_backend }}'
- when: select_crypto_backend != 'pyopenssl'
- register: ownca_authority_key_identifier_5
-
-- name: (OwnCA, {{select_crypto_backend}}) Ed25519 and Ed448 tests (for cryptography >= 2.6)
- block:
- - name: (OwnCA, {{select_crypto_backend}}) Generate privatekeys
- openssl_privatekey:
- path: '{{ output_dir }}/privatekey_{{ item }}.pem'
- type: '{{ item }}'
- loop:
- - Ed25519
- - Ed448
- register: ownca_certificate_ed25519_ed448_privatekey
- ignore_errors: yes
-
- - name: (OwnCA, {{select_crypto_backend}}) Generate CSR etc. if private key generation succeeded
- when: ownca_certificate_ed25519_ed448_privatekey is not failed
- block:
-
- - name: (OwnCA, {{select_crypto_backend}}) Generate CSR
- openssl_csr:
- path: '{{ output_dir }}/csr_{{ item }}.csr'
- privatekey_path: '{{ output_dir }}/privatekey_{{ item }}.pem'
- subject:
- commonName: www.ansible.com
- select_crypto_backend: '{{ select_crypto_backend }}'
- loop:
- - Ed25519
- - Ed448
- ignore_errors: yes
-
- - name: (OwnCA, {{select_crypto_backend}}) Generate ownca certificate
- openssl_certificate:
- path: '{{ output_dir }}/ownca_cert_{{ item }}.pem'
- csr_path: '{{ output_dir }}/csr_{{ item }}.csr'
- ownca_path: '{{ output_dir }}/ca_cert.pem'
- ownca_privatekey_path: '{{ output_dir }}/ca_privatekey.pem'
- provider: ownca
- ownca_digest: sha256
- select_crypto_backend: '{{ select_crypto_backend }}'
- loop:
- - Ed25519
- - Ed448
- register: ownca_certificate_ed25519_ed448
- ignore_errors: yes
-
- - name: (OwnCA, {{select_crypto_backend}}) Generate ownca certificate (idempotent)
- openssl_certificate:
- path: '{{ output_dir }}/ownca_cert_{{ item }}.pem'
- csr_path: '{{ output_dir }}/csr_{{ item }}.csr'
- ownca_path: '{{ output_dir }}/ca_cert.pem'
- ownca_privatekey_path: '{{ output_dir }}/ca_privatekey.pem'
- provider: ownca
- ownca_digest: sha256
- select_crypto_backend: '{{ select_crypto_backend }}'
- loop:
- - Ed25519
- - Ed448
- register: ownca_certificate_ed25519_ed448_idempotence
- ignore_errors: yes
-
- - name: (OwnCA, {{select_crypto_backend}}) Generate CA privatekey
- openssl_privatekey:
- path: '{{ output_dir }}/ca_privatekey_{{ item }}.pem'
- type: '{{ item }}'
- cipher: auto
- passphrase: Test123
- ignore_errors: yes
- loop:
- - Ed25519
- - Ed448
-
- - name: (OwnCA, {{select_crypto_backend}}) Generate CA CSR
- openssl_csr:
- path: '{{ output_dir }}/ca_csr_{{ item }}.csr'
- privatekey_path: '{{ output_dir }}/ca_privatekey_{{ item }}.pem'
- privatekey_passphrase: Test123
- subject:
- commonName: Example CA
- useCommonNameForSAN: no
- basic_constraints:
- - 'CA:TRUE'
- basic_constraints_critical: yes
- key_usage:
- - cRLSign
- - keyCertSign
- loop:
- - Ed25519
- - Ed448
- ignore_errors: yes
-
- - name: (OwnCA, {{select_crypto_backend}}) Generate selfsigned CA certificate
- openssl_certificate:
- path: '{{ output_dir }}/ca_cert_{{ item }}.pem'
- csr_path: '{{ output_dir }}/ca_csr_{{ item }}.csr'
- privatekey_path: '{{ output_dir }}/ca_privatekey_{{ item }}.pem'
- privatekey_passphrase: Test123
- provider: selfsigned
- select_crypto_backend: '{{ select_crypto_backend }}'
- loop:
- - Ed25519
- - Ed448
- ignore_errors: yes
-
- - name: (OwnCA, {{select_crypto_backend}}) Generate ownca certificate
- openssl_certificate:
- path: '{{ output_dir }}/ownca_cert_{{ item }}_2.pem'
- csr_path: '{{ output_dir }}/csr.csr'
- ownca_path: '{{ output_dir }}/ca_cert_{{ item }}.pem'
- ownca_privatekey_path: '{{ output_dir }}/ca_privatekey_{{ item }}.pem'
- ownca_privatekey_passphrase: Test123
- provider: ownca
- ownca_digest: sha256
- select_crypto_backend: '{{ select_crypto_backend }}'
- loop:
- - Ed25519
- - Ed448
- register: ownca_certificate_ed25519_ed448_2
- ignore_errors: yes
-
- - name: (OwnCA, {{select_crypto_backend}}) Generate ownca certificate (idempotent)
- openssl_certificate:
- path: '{{ output_dir }}/ownca_cert_{{ item }}_2.pem'
- csr_path: '{{ output_dir }}/csr.csr'
- ownca_path: '{{ output_dir }}/ca_cert_{{ item }}.pem'
- ownca_privatekey_path: '{{ output_dir }}/ca_privatekey_{{ item }}.pem'
- ownca_privatekey_passphrase: Test123
- provider: ownca
- ownca_digest: sha256
- select_crypto_backend: '{{ select_crypto_backend }}'
- loop:
- - Ed25519
- - Ed448
- register: ownca_certificate_ed25519_ed448_2_idempotence
- ignore_errors: yes
-
- when: select_crypto_backend == 'cryptography' and cryptography_version.stdout is version('2.6', '>=')
-
-- import_tasks: ../tests/validate_ownca.yml
diff --git a/test/integration/targets/openssl_certificate/tasks/removal.yml b/test/integration/targets/openssl_certificate/tasks/removal.yml
deleted file mode 100644
index e45e952d93..0000000000
--- a/test/integration/targets/openssl_certificate/tasks/removal.yml
+++ /dev/null
@@ -1,52 +0,0 @@
----
-- name: (Removal, {{select_crypto_backend}}) Generate privatekey
- openssl_privatekey:
- path: '{{ output_dir }}/removal_privatekey.pem'
-
-- name: (Removal, {{select_crypto_backend}}) Generate CSR
- openssl_csr:
- path: '{{ output_dir }}/removal_csr.csr'
- privatekey_path: '{{ output_dir }}/removal_privatekey.pem'
-
-- name: (Removal, {{select_crypto_backend}}) Generate selfsigned certificate
- openssl_certificate:
- path: '{{ output_dir }}/removal_cert.pem'
- csr_path: '{{ output_dir }}/removal_csr.csr'
- privatekey_path: '{{ output_dir }}/removal_privatekey.pem'
- provider: selfsigned
- selfsigned_digest: sha256
- select_crypto_backend: '{{ select_crypto_backend }}'
-
-- name: "(Removal, {{select_crypto_backend}}) Check that file is not gone"
- stat:
- path: "{{ output_dir }}/removal_cert.pem"
- register: removal_1_prestat
-
-- name: "(Removal, {{select_crypto_backend}}) Remove certificate"
- openssl_certificate:
- path: "{{ output_dir }}/removal_cert.pem"
- state: absent
- select_crypto_backend: '{{ select_crypto_backend }}'
- return_content: yes
- register: removal_1
-
-- name: "(Removal, {{select_crypto_backend}}) Check that file is gone"
- stat:
- path: "{{ output_dir }}/removal_cert.pem"
- register: removal_1_poststat
-
-- name: "(Removal, {{select_crypto_backend}}) Remove certificate (idempotent)"
- openssl_certificate:
- path: "{{ output_dir }}/removal_cert.pem"
- state: absent
- select_crypto_backend: '{{ select_crypto_backend }}'
- register: removal_2
-
-- name: (Removal, {{select_crypto_backend}}) Ensure removal worked
- assert:
- that:
- - removal_1_prestat.stat.exists
- - removal_1 is changed
- - not removal_1_poststat.stat.exists
- - removal_2 is not changed
- - removal_1.certificate is none
diff --git a/test/integration/targets/openssl_certificate/tasks/selfsigned.yml b/test/integration/targets/openssl_certificate/tasks/selfsigned.yml
deleted file mode 100644
index 5455d40b00..0000000000
--- a/test/integration/targets/openssl_certificate/tasks/selfsigned.yml
+++ /dev/null
@@ -1,431 +0,0 @@
----
-- name: (Selfsigned, {{select_crypto_backend}}) Generate privatekey
- openssl_privatekey:
- path: '{{ output_dir }}/privatekey.pem'
-
-- name: (Selfsigned, {{select_crypto_backend}}) Generate privatekey with password
- openssl_privatekey:
- path: '{{ output_dir }}/privatekeypw.pem'
- passphrase: hunter2
- cipher: auto
- select_crypto_backend: cryptography
-
-- name: (Selfsigned, {{select_crypto_backend}}) Generate CSR
- openssl_csr:
- path: '{{ output_dir }}/csr.csr'
- privatekey_path: '{{ output_dir }}/privatekey.pem'
- subject:
- commonName: www.example.com
-
-- name: (Selfsigned, {{select_crypto_backend}}) Generate CSR
- openssl_csr:
- path: '{{ output_dir }}/csr_minimal_change.csr'
- privatekey_path: '{{ output_dir }}/privatekey.pem'
- subject:
- commonName: www.example.org
-
-- name: (Selfsigned, {{select_crypto_backend}}) Generate selfsigned certificate
- openssl_certificate:
- path: '{{ output_dir }}/cert.pem'
- csr_path: '{{ output_dir }}/csr.csr'
- privatekey_path: '{{ output_dir }}/privatekey.pem'
- provider: selfsigned
- selfsigned_digest: sha256
- select_crypto_backend: '{{ select_crypto_backend }}'
- return_content: yes
- register: selfsigned_certificate
-
-- name: (Selfsigned, {{select_crypto_backend}}) Generate selfsigned certificate - idempotency
- openssl_certificate:
- path: '{{ output_dir }}/cert.pem'
- csr_path: '{{ output_dir }}/csr.csr'
- privatekey_path: '{{ output_dir }}/privatekey.pem'
- provider: selfsigned
- selfsigned_digest: sha256
- select_crypto_backend: '{{ select_crypto_backend }}'
- return_content: yes
- register: selfsigned_certificate_idempotence
-
-- name: (Selfsigned, {{select_crypto_backend}}) Generate selfsigned certificate (check mode)
- openssl_certificate:
- path: '{{ output_dir }}/cert.pem'
- csr_path: '{{ output_dir }}/csr.csr'
- privatekey_path: '{{ output_dir }}/privatekey.pem'
- provider: selfsigned
- selfsigned_digest: sha256
- select_crypto_backend: '{{ select_crypto_backend }}'
- check_mode: yes
-
-- name: (Selfsigned, {{select_crypto_backend}}) Generate selfsigned certificate (check mode, other CSR)
- openssl_certificate:
- path: '{{ output_dir }}/cert.pem'
- csr_path: '{{ output_dir }}/csr_minimal_change.csr'
- privatekey_path: '{{ output_dir }}/privatekey.pem'
- provider: selfsigned
- selfsigned_digest: sha256
- select_crypto_backend: '{{ select_crypto_backend }}'
- check_mode: yes
- register: selfsigned_certificate_csr_minimal_change
-
-- name: (Selfsigned, {{select_crypto_backend}}) Check selfsigned certificate
- openssl_certificate:
- path: '{{ output_dir }}/cert.pem'
- privatekey_path: '{{ output_dir }}/privatekey.pem'
- provider: assertonly
- has_expired: False
- version: 3
- signature_algorithms:
- - sha256WithRSAEncryption
- - sha256WithECDSAEncryption
- subject:
- commonName: www.example.com
- select_crypto_backend: '{{ select_crypto_backend }}'
-
-- name: (Selfsigned, {{select_crypto_backend}}) Generate selfsigned v2 certificate
- openssl_certificate:
- path: '{{ output_dir }}/cert_v2.pem'
- csr_path: '{{ output_dir }}/csr.csr'
- privatekey_path: '{{ output_dir }}/privatekey.pem'
- provider: selfsigned
- selfsigned_digest: sha256
- selfsigned_version: 2
- select_crypto_backend: "{{ select_crypto_backend }}"
- register: selfsigned_v2_cert
- ignore_errors: true
-
-- name: (Selfsigned, {{select_crypto_backend}}) Generate privatekey2
- openssl_privatekey:
- path: '{{ output_dir }}/privatekey2.pem'
-
-- name: (Selfsigned, {{select_crypto_backend}}) Generate CSR2
- openssl_csr:
- subject:
- CN: www.example.com
- C: US
- ST: California
- L: Los Angeles
- O: ACME Inc.
- OU:
- - Roadrunner pest control
- - Pyrotechnics
- path: '{{ output_dir }}/csr2.csr'
- privatekey_path: '{{ output_dir }}/privatekey2.pem'
- keyUsage:
- - digitalSignature
- extendedKeyUsage:
- - ipsecUser
- - biometricInfo
-
-- name: (Selfsigned, {{select_crypto_backend}}) Generate selfsigned certificate2
- openssl_certificate:
- path: '{{ output_dir }}/cert2.pem'
- csr_path: '{{ output_dir }}/csr2.csr'
- privatekey_path: '{{ output_dir }}/privatekey2.pem'
- provider: selfsigned
- selfsigned_digest: sha256
- select_crypto_backend: '{{ select_crypto_backend }}'
-
-- name: (Selfsigned, {{select_crypto_backend}}) Check selfsigned certificate2
- openssl_certificate:
- path: '{{ output_dir }}/cert2.pem'
- privatekey_path: '{{ output_dir }}/privatekey2.pem'
- provider: assertonly
- has_expired: False
- version: 3
- signature_algorithms:
- - sha256WithRSAEncryption
- - sha256WithECDSAEncryption
- subject:
- commonName: www.example.com
- C: US
- ST: California
- L: Los Angeles
- O: ACME Inc.
- OU:
- - Roadrunner pest control
- - Pyrotechnics
- keyUsage:
- - digitalSignature
- extendedKeyUsage:
- - ipsecUser
- - biometricInfo
- select_crypto_backend: '{{ select_crypto_backend }}'
-
-- name: (Selfsigned, {{select_crypto_backend}}) Create private key 3
- openssl_privatekey:
- path: "{{ output_dir }}/privatekey3.pem"
-
-- name: (Selfsigned, {{select_crypto_backend}}) Create CSR 3
- openssl_csr:
- subject:
- CN: www.example.com
- privatekey_path: "{{ output_dir }}/privatekey3.pem"
- path: "{{ output_dir }}/csr3.pem"
-
-- name: (Selfsigned, {{select_crypto_backend}}) Create certificate3 with notBefore and notAfter
- openssl_certificate:
- provider: selfsigned
- selfsigned_not_before: 20181023133742Z
- selfsigned_not_after: 20191023133742Z
- path: "{{ output_dir }}/cert3.pem"
- csr_path: "{{ output_dir }}/csr3.pem"
- privatekey_path: "{{ output_dir }}/privatekey3.pem"
- select_crypto_backend: '{{ select_crypto_backend }}'
-
-- name: (Selfsigned, {{select_crypto_backend}}) Generate privatekey
- openssl_privatekey:
- path: '{{ output_dir }}/privatekey_ecc.pem'
- type: ECC
- curve: "{{ (ansible_distribution == 'CentOS' and ansible_distribution_major_version == '6') | ternary('secp521r1', 'secp256k1') }}"
- # ^ cryptography on CentOS6 doesn't support secp256k1, so we use secp521r1 instead
-
-- name: (Selfsigned, {{select_crypto_backend}}) Generate CSR
- openssl_csr:
- path: '{{ output_dir }}/csr_ecc.csr'
- privatekey_path: '{{ output_dir }}/privatekey_ecc.pem'
- subject:
- commonName: www.example.com
-
-- name: (Selfsigned, {{select_crypto_backend}}) Generate selfsigned certificate
- openssl_certificate:
- path: '{{ output_dir }}/cert_ecc.pem'
- csr_path: '{{ output_dir }}/csr_ecc.csr'
- privatekey_path: '{{ output_dir }}/privatekey_ecc.pem'
- provider: selfsigned
- selfsigned_digest: sha256
- select_crypto_backend: '{{ select_crypto_backend }}'
- register: selfsigned_certificate_ecc
-
-- name: (Selfsigned, {{select_crypto_backend}}) Generate CSR (privatekey passphrase)
- openssl_csr:
- path: '{{ output_dir }}/csr_pass.csr'
- privatekey_path: '{{ output_dir }}/privatekeypw.pem'
- privatekey_passphrase: hunter2
- subject:
- commonName: www.example.com
-
-- name: (Selfsigned, {{select_crypto_backend}}) Generate selfsigned certificate (privatekey passphrase)
- openssl_certificate:
- path: '{{ output_dir }}/cert_pass.pem'
- csr_path: '{{ output_dir }}/csr_pass.csr'
- privatekey_path: '{{ output_dir }}/privatekeypw.pem'
- privatekey_passphrase: hunter2
- provider: selfsigned
- selfsigned_digest: sha256
- select_crypto_backend: '{{ select_crypto_backend }}'
- register: selfsigned_certificate_passphrase
-
-- name: (Selfsigned, {{select_crypto_backend}}) Generate selfsigned certificate (failed passphrase 1)
- openssl_certificate:
- path: '{{ output_dir }}/cert_pw1.pem'
- csr_path: '{{ output_dir }}/csr_ecc.csr'
- privatekey_path: '{{ output_dir }}/privatekey.pem'
- privatekey_passphrase: hunter2
- provider: selfsigned
- selfsigned_digest: sha256
- select_crypto_backend: '{{ select_crypto_backend }}'
- ignore_errors: yes
- register: passphrase_error_1
-
-- name: (Selfsigned, {{select_crypto_backend}}) Generate selfsigned certificate (failed passphrase 2)
- openssl_certificate:
- path: '{{ output_dir }}/cert_pw2.pem'
- csr_path: '{{ output_dir }}/csr_ecc.csr'
- privatekey_path: '{{ output_dir }}/privatekeypw.pem'
- privatekey_passphrase: wrong_password
- provider: selfsigned
- selfsigned_digest: sha256
- select_crypto_backend: '{{ select_crypto_backend }}'
- ignore_errors: yes
- register: passphrase_error_2
-
-- name: (Selfsigned, {{select_crypto_backend}}) Generate selfsigned certificate (failed passphrase 3)
- openssl_certificate:
- path: '{{ output_dir }}/cert_pw3.pem'
- csr_path: '{{ output_dir }}/csr_ecc.csr'
- privatekey_path: '{{ output_dir }}/privatekeypw.pem'
- provider: selfsigned
- selfsigned_digest: sha256
- select_crypto_backend: '{{ select_crypto_backend }}'
- ignore_errors: yes
- register: passphrase_error_3
-
-- name: Create broken certificate
- copy:
- dest: "{{ output_dir }}/cert_broken.pem"
- content: "broken"
-- name: Regenerate broken cert
- openssl_certificate:
- path: '{{ output_dir }}/cert_broken.pem'
- csr_path: '{{ output_dir }}/csr_ecc.csr'
- privatekey_path: '{{ output_dir }}/privatekey_ecc.pem'
- provider: selfsigned
- selfsigned_digest: sha256
- register: selfsigned_broken
-
-- name: (Selfsigned, {{select_crypto_backend}}) Backup test
- openssl_certificate:
- path: '{{ output_dir }}/selfsigned_cert_backup.pem'
- csr_path: '{{ output_dir }}/csr_ecc.csr'
- privatekey_path: '{{ output_dir }}/privatekey_ecc.pem'
- provider: selfsigned
- selfsigned_digest: sha256
- backup: yes
- select_crypto_backend: '{{ select_crypto_backend }}'
- register: selfsigned_backup_1
-- name: (Selfsigned, {{select_crypto_backend}}) Backup test (idempotent)
- openssl_certificate:
- path: '{{ output_dir }}/selfsigned_cert_backup.pem'
- csr_path: '{{ output_dir }}/csr_ecc.csr'
- privatekey_path: '{{ output_dir }}/privatekey_ecc.pem'
- provider: selfsigned
- selfsigned_digest: sha256
- backup: yes
- select_crypto_backend: '{{ select_crypto_backend }}'
- register: selfsigned_backup_2
-- name: (Selfsigned, {{select_crypto_backend}}) Backup test (change)
- openssl_certificate:
- path: '{{ output_dir }}/selfsigned_cert_backup.pem'
- csr_path: '{{ output_dir }}/csr.csr'
- privatekey_path: '{{ output_dir }}/privatekey.pem'
- provider: selfsigned
- selfsigned_digest: sha256
- backup: yes
- select_crypto_backend: '{{ select_crypto_backend }}'
- register: selfsigned_backup_3
-- name: (Selfsigned, {{select_crypto_backend}}) Backup test (remove)
- openssl_certificate:
- path: '{{ output_dir }}/selfsigned_cert_backup.pem'
- state: absent
- provider: selfsigned
- backup: yes
- select_crypto_backend: '{{ select_crypto_backend }}'
- register: selfsigned_backup_4
-- name: (Selfsigned, {{select_crypto_backend}}) Backup test (remove, idempotent)
- openssl_certificate:
- path: '{{ output_dir }}/selfsigned_cert_backup.pem'
- state: absent
- provider: selfsigned
- backup: yes
- select_crypto_backend: '{{ select_crypto_backend }}'
- register: selfsigned_backup_5
-
-- name: (Selfsigned, {{select_crypto_backend}}) Create subject key identifier test
- openssl_certificate:
- path: '{{ output_dir }}/selfsigned_cert_ski.pem'
- csr_path: '{{ output_dir }}/csr_ecc.csr'
- privatekey_path: '{{ output_dir }}/privatekey_ecc.pem'
- provider: selfsigned
- selfsigned_digest: sha256
- selfsigned_create_subject_key_identifier: always_create
- select_crypto_backend: '{{ select_crypto_backend }}'
- when: select_crypto_backend != 'pyopenssl'
- register: selfsigned_subject_key_identifier_1
-
-- name: (Selfsigned, {{select_crypto_backend}}) Create subject key identifier test (idempotency)
- openssl_certificate:
- path: '{{ output_dir }}/selfsigned_cert_ski.pem'
- csr_path: '{{ output_dir }}/csr_ecc.csr'
- privatekey_path: '{{ output_dir }}/privatekey_ecc.pem'
- provider: selfsigned
- selfsigned_digest: sha256
- selfsigned_create_subject_key_identifier: always_create
- select_crypto_backend: '{{ select_crypto_backend }}'
- when: select_crypto_backend != 'pyopenssl'
- register: selfsigned_subject_key_identifier_2
-
-- name: (Selfsigned, {{select_crypto_backend}}) Create subject key identifier test (remove)
- openssl_certificate:
- path: '{{ output_dir }}/selfsigned_cert_ski.pem'
- csr_path: '{{ output_dir }}/csr_ecc.csr'
- privatekey_path: '{{ output_dir }}/privatekey_ecc.pem'
- provider: selfsigned
- selfsigned_digest: sha256
- selfsigned_create_subject_key_identifier: never_create
- select_crypto_backend: '{{ select_crypto_backend }}'
- when: select_crypto_backend != 'pyopenssl'
- register: selfsigned_subject_key_identifier_3
-
-- name: (Selfsigned, {{select_crypto_backend}}) Create subject key identifier test (remove idempotency)
- openssl_certificate:
- path: '{{ output_dir }}/selfsigned_cert_ski.pem'
- csr_path: '{{ output_dir }}/csr_ecc.csr'
- privatekey_path: '{{ output_dir }}/privatekey_ecc.pem'
- provider: selfsigned
- selfsigned_digest: sha256
- selfsigned_create_subject_key_identifier: never_create
- select_crypto_backend: '{{ select_crypto_backend }}'
- when: select_crypto_backend != 'pyopenssl'
- register: selfsigned_subject_key_identifier_4
-
-- name: (Selfsigned, {{select_crypto_backend}}) Create subject key identifier test (re-enable)
- openssl_certificate:
- path: '{{ output_dir }}/selfsigned_cert_ski.pem'
- csr_path: '{{ output_dir }}/csr_ecc.csr'
- privatekey_path: '{{ output_dir }}/privatekey_ecc.pem'
- provider: selfsigned
- selfsigned_digest: sha256
- selfsigned_create_subject_key_identifier: always_create
- select_crypto_backend: '{{ select_crypto_backend }}'
- when: select_crypto_backend != 'pyopenssl'
- register: selfsigned_subject_key_identifier_5
-
-- name: (Selfsigned, {{select_crypto_backend}}) Ed25519 and Ed448 tests (for cryptography >= 2.6)
- block:
- - name: (Selfsigned, {{select_crypto_backend}}) Generate privatekeys
- openssl_privatekey:
- path: '{{ output_dir }}/privatekey_{{ item }}.pem'
- type: '{{ item }}'
- loop:
- - Ed25519
- - Ed448
- register: selfsigned_certificate_ed25519_ed448_privatekey
- ignore_errors: yes
-
- - name: (Selfsigned, {{select_crypto_backend}}) Generate CSR etc. if private key generation succeeded
- when: selfsigned_certificate_ed25519_ed448_privatekey is not failed
- block:
-
- - name: (Selfsigned, {{select_crypto_backend}}) Generate CSR
- openssl_csr:
- path: '{{ output_dir }}/csr_{{ item }}.csr'
- privatekey_path: '{{ output_dir }}/privatekey_{{ item }}.pem'
- subject:
- commonName: www.ansible.com
- select_crypto_backend: '{{ select_crypto_backend }}'
- loop:
- - Ed25519
- - Ed448
- ignore_errors: yes
-
- - name: (Selfsigned, {{select_crypto_backend}}) Generate selfsigned certificate
- openssl_certificate:
- path: '{{ output_dir }}/cert_{{ item }}.pem'
- csr_path: '{{ output_dir }}/csr_{{ item }}.csr'
- privatekey_path: '{{ output_dir }}/privatekey_{{ item }}.pem'
- provider: selfsigned
- selfsigned_digest: sha256
- select_crypto_backend: '{{ select_crypto_backend }}'
- loop:
- - Ed25519
- - Ed448
- register: selfsigned_certificate_ed25519_ed448
- ignore_errors: yes
-
- - name: (Selfsigned, {{select_crypto_backend}}) Generate selfsigned certificate - idempotency
- openssl_certificate:
- path: '{{ output_dir }}/cert_{{ item }}.pem'
- csr_path: '{{ output_dir }}/csr_{{ item }}.csr'
- privatekey_path: '{{ output_dir }}/privatekey_{{ item }}.pem'
- provider: selfsigned
- selfsigned_digest: sha256
- select_crypto_backend: '{{ select_crypto_backend }}'
- loop:
- - Ed25519
- - Ed448
- register: selfsigned_certificate_ed25519_ed448_idempotence
- ignore_errors: yes
-
- when: select_crypto_backend == 'cryptography' and cryptography_version.stdout is version('2.6', '>=')
-
-- import_tasks: ../tests/validate_selfsigned.yml
diff --git a/test/integration/targets/openssl_certificate/tests/validate_ownca.yml b/test/integration/targets/openssl_certificate/tests/validate_ownca.yml
deleted file mode 100644
index 19ab61988e..0000000000
--- a/test/integration/targets/openssl_certificate/tests/validate_ownca.yml
+++ /dev/null
@@ -1,178 +0,0 @@
----
-- name: (OwnCA validation, {{select_crypto_backend}}) Validate ownca certificate (test - verify CA)
- shell: 'openssl verify -CAfile {{ output_dir }}/ca_cert.pem {{ output_dir }}/ownca_cert.pem | sed "s/.*: \(.*\)/\1/g"'
- register: ownca_verify_ca
-
-- name: (OwnCA validation, {{select_crypto_backend}}) Validate ownca certificate (test - ownca certificate modulus)
- shell: 'openssl x509 -noout -modulus -in {{ output_dir }}/ownca_cert.pem'
- register: ownca_cert_modulus
-
-- name: (OwnCA validation, {{select_crypto_backend}}) Validate ownca certificate (test - ownca issuer value)
- shell: 'openssl x509 -noout -in {{ output_dir}}/ownca_cert.pem -text | grep "Issuer" | sed "s/.*: \(.*\)/\1/g"'
- register: ownca_cert_issuer
-
-- name: (OwnCA validation, {{select_crypto_backend}}) Validate ownca certificate (test - ownca certficate version == default == 3)
- shell: 'openssl x509 -noout -in {{ output_dir}}/ownca_cert.pem -text | grep "Version" | sed "s/.*: \(.*\) .*/\1/g"'
- register: ownca_cert_version
-
-- name: (OwnCA validation, {{select_crypto_backend}}) Validate ownca certificate (assert)
- assert:
- that:
- - ownca_verify_ca.stdout == 'OK'
- - ownca_cert_modulus.stdout == privatekey_modulus.stdout
- - ownca_cert_version.stdout == '3'
- # openssl 1.1.x adds a space between the output
- - ownca_cert_issuer.stdout in ['CN=Example CA', 'CN = Example CA']
-
-- name: (OwnCA validation, {{select_crypto_backend}}) Validate ownca certificate idempotence
- assert:
- that:
- - ownca_certificate.serial_number == ownca_certificate_idempotence.serial_number
- - ownca_certificate.notBefore == ownca_certificate_idempotence.notBefore
- - ownca_certificate.notAfter == ownca_certificate_idempotence.notAfter
-
-- name: (OwnCA validation, {{select_crypto_backend}}) Validate ownca data return
- assert:
- that:
- - ownca_certificate.certificate == lookup('file', output_dir ~ '/ownca_cert.pem', rstrip=False)
- - ownca_certificate.certificate == ownca_certificate_idempotence.certificate
-
-- block:
- - name: (OwnCA validation, {{select_crypto_backend}}) Validate ownca certificate v2 (test - ownca certificate version == 2)
- shell: 'openssl x509 -noout -in {{ output_dir}}/ownca_cert_v2.pem -text | grep "Version" | sed "s/.*: \(.*\) .*/\1/g"'
- register: ownca_cert_v2_version
-
- - name: (OwnCA validation, {{select_crypto_backend}}) Validate ownca certificate version 2 (assert)
- assert:
- that:
- - ownca_cert_v2_version.stdout == '2'
- when: "select_crypto_backend != 'cryptography'"
-
-- name: (OwnCA validation, {{select_crypto_backend}}) Validate ownca certificate v2 (test - ownca certificate version == 2)
- assert:
- that:
- - ownca_v2_certificate is failed
- - "'The cryptography backend does not support v2 certificates' in ownca_v2_certificate.msg"
- when: "select_crypto_backend == 'cryptography'"
-
-
-- name: (OwnCA validation, {{select_crypto_backend}}) Validate ownca certificate2 (test - ownca certificate modulus)
- shell: 'openssl x509 -noout -modulus -in {{ output_dir }}/ownca_cert2.pem'
- register: ownca_cert2_modulus
-
-- name: (OwnCA validation, {{select_crypto_backend}}) Validate ownca certificate2 (assert)
- assert:
- that:
- - ownca_cert2_modulus.stdout == privatekey2_modulus.stdout
-
-- name: (OwnCA validation, {{select_crypto_backend}}) Validate owncal certificate3 (test - notBefore)
- shell: 'openssl x509 -noout -in {{ output_dir }}/ownca_cert3.pem -text | grep "Not Before" | sed "s/.*: \(.*\) .*/\1/g"'
- register: ownca_cert3_notBefore
-
-- name: (OwnCA validation, {{select_crypto_backend}}) Validate ownca certificate3 (test - notAfter)
- shell: 'openssl x509 -noout -in {{ output_dir }}/ownca_cert3.pem -text | grep "Not After" | sed "s/.*: \(.*\) .*/\1/g"'
- register: ownca_cert3_notAfter
-
-- name: (OwnCA validation, {{select_crypto_backend}}) Validate ownca certificate3 (assert - notBefore)
- assert:
- that:
- - ownca_cert3_notBefore.stdout == 'Oct 23 13:37:42 2018'
-
-- name: (OwnCA validation, {{select_crypto_backend}}) Validate ownca certificate3 (assert - notAfter)
- assert:
- that:
- - ownca_cert3_notAfter.stdout == 'Oct 23 13:37:42 2019'
-
-- name: (OwnCA validation, {{select_crypto_backend}}) Validate ownca ECC certificate (test - ownca certificate pubkey)
- shell: 'openssl x509 -noout -pubkey -in {{ output_dir }}/ownca_cert_ecc.pem'
- register: ownca_cert_ecc_pubkey
-
-- name: (OwnCA validation, {{select_crypto_backend}}) Validate ownca ECC certificate (test - ownca issuer value)
- shell: 'openssl x509 -noout -in {{ output_dir}}/ownca_cert_ecc.pem -text | grep "Issuer" | sed "s/.*: \(.*\)/\1/g"'
- register: ownca_cert_ecc_issuer
-
-- name: (OwnCA validation, {{select_crypto_backend}}) Validate ownca ECC certificate (assert)
- assert:
- that:
- - ownca_cert_ecc_pubkey.stdout == privatekey_ecc_pubkey.stdout
- # openssl 1.1.x adds a space between the output
- - ownca_cert_ecc_issuer.stdout in ['CN=Example CA', 'CN = Example CA']
-
-- name: (OwnCA validation, {{select_crypto_backend}})
- assert:
- that:
- - passphrase_error_1 is failed
- - "'assphrase' in passphrase_error_1.msg or 'assword' in passphrase_error_1.msg"
- - passphrase_error_2 is failed
- - "'assphrase' in passphrase_error_2.msg or 'assword' in passphrase_error_2.msg or 'serializ' in passphrase_error_2.msg"
- - passphrase_error_3 is failed
- - "'assphrase' in passphrase_error_3.msg or 'assword' in passphrase_error_3.msg or 'serializ' in passphrase_error_3.msg"
-
-- name: (OwnCA validation, {{select_crypto_backend}})Verify that broken certificate will be regenerated
- assert:
- that:
- - ownca_broken is changed
-
-- name: (OwnCA validation, {{select_crypto_backend}}) Check backup
- assert:
- that:
- - ownca_backup_1 is changed
- - ownca_backup_1.backup_file is undefined
- - ownca_backup_2 is not changed
- - ownca_backup_2.backup_file is undefined
- - ownca_backup_3 is changed
- - ownca_backup_3.backup_file is string
- - ownca_backup_4 is changed
- - ownca_backup_4.backup_file is string
- - ownca_backup_5 is not changed
- - ownca_backup_5.backup_file is undefined
-
-- name: (OwnCA validation, {{select_crypto_backend}}) Check create subject key identifier
- assert:
- that:
- - ownca_subject_key_identifier_1 is changed
- - ownca_subject_key_identifier_2 is not changed
- - ownca_subject_key_identifier_3 is changed
- - ownca_subject_key_identifier_4 is not changed
- - ownca_subject_key_identifier_5 is changed
- when: select_crypto_backend != 'pyopenssl'
-
-- name: (OwnCA validation, {{select_crypto_backend}}) Check create authority key identifier
- assert:
- that:
- - ownca_authority_key_identifier_1 is changed
- - ownca_authority_key_identifier_2 is not changed
- - ownca_authority_key_identifier_3 is changed
- - ownca_authority_key_identifier_4 is not 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)
- assert:
- that:
- - ownca_certificate_ed25519_ed448.results[0] is failed
- - ownca_certificate_ed25519_ed448.results[1] is failed
- - ownca_certificate_ed25519_ed448_idempotence.results[0] is failed
- - ownca_certificate_ed25519_ed448_idempotence.results[1] is failed
- - ownca_certificate_ed25519_ed448_2.results[0] is failed
- - ownca_certificate_ed25519_ed448_2.results[1] is failed
- - ownca_certificate_ed25519_ed448_2_idempotence.results[0] is failed
- - ownca_certificate_ed25519_ed448_2_idempotence.results[1] is failed
- when: select_crypto_backend == 'cryptography' and cryptography_version.stdout is version('2.6', '>=') and cryptography_version.stdout is version('2.8', '<') and ownca_certificate_ed25519_ed448_privatekey is not failed
-
-- name: (OwnCA validation, {{select_crypto_backend}}) Verify Ed25519 and Ed448 tests (for cryptography >= 2.8)
- assert:
- that:
- - ownca_certificate_ed25519_ed448 is succeeded
- - ownca_certificate_ed25519_ed448.results[0] is changed
- - ownca_certificate_ed25519_ed448.results[1] is changed
- - ownca_certificate_ed25519_ed448_idempotence is succeeded
- - ownca_certificate_ed25519_ed448_idempotence.results[0] is not changed
- - ownca_certificate_ed25519_ed448_idempotence.results[1] is not changed
- - ownca_certificate_ed25519_ed448_2 is succeeded
- - ownca_certificate_ed25519_ed448_2.results[0] is changed
- - ownca_certificate_ed25519_ed448_2.results[1] is changed
- - ownca_certificate_ed25519_ed448_2_idempotence is succeeded
- - ownca_certificate_ed25519_ed448_2_idempotence.results[0] is not changed
- - ownca_certificate_ed25519_ed448_2_idempotence.results[1] is not changed
- when: select_crypto_backend == 'cryptography' and cryptography_version.stdout is version('2.8', '>=') and ownca_certificate_ed25519_ed448_privatekey is not failed
diff --git a/test/integration/targets/openssl_certificate/tests/validate_selfsigned.yml b/test/integration/targets/openssl_certificate/tests/validate_selfsigned.yml
deleted file mode 100644
index 85df20c54e..0000000000
--- a/test/integration/targets/openssl_certificate/tests/validate_selfsigned.yml
+++ /dev/null
@@ -1,164 +0,0 @@
----
-- name: (Selfsigned validation, {{select_crypto_backend}}) Validate certificate (test - privatekey modulus)
- shell: 'openssl rsa -noout -modulus -in {{ output_dir }}/privatekey.pem'
- register: privatekey_modulus
-
-- name: (Selfsigned validation, {{select_crypto_backend}}) Validate certificate (test - certificate modulus)
- shell: 'openssl x509 -noout -modulus -in {{ output_dir }}/cert.pem'
- register: cert_modulus
-
-- name: (Selfsigned validation, {{select_crypto_backend}}) Validate certificate (test - issuer value)
- shell: 'openssl x509 -noout -in {{ output_dir}}/cert.pem -text | grep "Issuer" | sed "s/.*: \(.*\)/\1/g; s/ //g;"'
- register: cert_issuer
-
-
-- name: (Selfsigned validation, {{select_crypto_backend}}) Validate certificate (test - certficate version == default == 3)
- shell: 'openssl x509 -noout -in {{ output_dir}}/cert.pem -text | grep "Version" | sed "s/.*: \(.*\) .*/\1/g"'
- register: cert_version
-
-- name: (Selfsigned validation, {{select_crypto_backend}}) Validate certificate (assert)
- assert:
- that:
- - cert_modulus.stdout == privatekey_modulus.stdout
- - cert_version.stdout == '3'
- - cert_issuer.stdout == 'CN=www.example.com'
-
-- name: (Selfsigned validation, {{select_crypto_backend}}) Validate certificate idempotence
- assert:
- that:
- - selfsigned_certificate.serial_number == selfsigned_certificate_idempotence.serial_number
- - selfsigned_certificate.notBefore == selfsigned_certificate_idempotence.notBefore
- - selfsigned_certificate.notAfter == selfsigned_certificate_idempotence.notAfter
-
-- name: (Selfsigned validation, {{select_crypto_backend}}) Validate data retrieval
- assert:
- that:
- - selfsigned_certificate.certificate == lookup('file', output_dir ~ '/cert.pem', rstrip=False)
- - selfsigned_certificate.certificate == selfsigned_certificate_idempotence.certificate
-
-- name: Make sure that changes in CSR are detected even if private key is specified
- assert:
- that:
- - selfsigned_certificate_csr_minimal_change is changed
-
-- block:
- - name: (Selfsigned validation, {{select_crypto_backend}}) Validate certificate v2 (test - certificate version == 2)
- shell: 'openssl x509 -noout -in {{ output_dir}}/cert_v2.pem -text | grep "Version" | sed "s/.*: \(.*\) .*/\1/g"'
- register: cert_v2_version
-
- - name: (Selfsigned validation, {{select_crypto_backend}}) Validate certificate version 2 (assert)
- assert:
- that:
- - cert_v2_version.stdout == '2'
- when: select_crypto_backend != 'cryptography'
-
-- block:
- - name: (Selfsigned validateion, {{ select_crypto_backend }} Validate certificate v2 is failed
- assert:
- that:
- - selfsigned_v2_cert is failed
- - "'The cryptography backend does not support v2 certificates' in selfsigned_v2_cert.msg"
- when: select_crypto_backend == 'cryptography'
-
-- name: (Selfsigned validation, {{select_crypto_backend}}) Validate certificate2 (test - privatekey modulus)
- shell: 'openssl rsa -noout -modulus -in {{ output_dir }}/privatekey2.pem'
- register: privatekey2_modulus
-
-- name: (Selfsigned validation, {{select_crypto_backend}}) Validate certificate2 (test - certificate modulus)
- shell: 'openssl x509 -noout -modulus -in {{ output_dir }}/cert2.pem'
- register: cert2_modulus
-
-- name: (Selfsigned validation, {{select_crypto_backend}}) Validate certificate2 (assert)
- assert:
- that:
- - cert2_modulus.stdout == privatekey2_modulus.stdout
-
-- name: (Selfsigned validation, {{select_crypto_backend}}) Validate certificate3 (test - notBefore)
- shell: 'openssl x509 -noout -in {{ output_dir }}/cert3.pem -text | grep "Not Before" | sed "s/.*: \(.*\) .*/\1/g"'
- register: cert3_notBefore
-
-- name: (Selfsigned validation, {{select_crypto_backend}}) Validate certificate3 (test - notAfter)
- shell: 'openssl x509 -noout -in {{ output_dir }}/cert3.pem -text | grep "Not After" | sed "s/.*: \(.*\) .*/\1/g"'
- register: cert3_notAfter
-
-- name: (Selfsigned validation, {{select_crypto_backend}}) Validate certificate3 (assert - notBefore)
- assert:
- that:
- - cert3_notBefore.stdout == 'Oct 23 13:37:42 2018'
-
-- name: (Selfsigned validation, {{select_crypto_backend}}) Validate certificate3 (assert - notAfter)
- assert:
- that:
- - cert3_notAfter.stdout == 'Oct 23 13:37:42 2019'
-
-- name: (Selfsigned validation, {{select_crypto_backend}}) Validate ECC certificate (test - privatekey's pubkey)
- shell: 'openssl ec -pubout -in {{ output_dir }}/privatekey_ecc.pem'
- register: privatekey_ecc_pubkey
-
-- name: (Selfsigned validation, {{select_crypto_backend}}) Validate ECC certificate (test - certificate pubkey)
- shell: 'openssl x509 -noout -pubkey -in {{ output_dir }}/cert_ecc.pem'
- register: cert_ecc_pubkey
-
-- name: (Selfsigned validation, {{select_crypto_backend}}) Validate ECC certificate (assert)
- assert:
- that:
- - cert_ecc_pubkey.stdout == privatekey_ecc_pubkey.stdout
-
-- name: (Selfsigned validation, {{select_crypto_backend}})
- assert:
- that:
- - passphrase_error_1 is failed
- - "'assphrase' in passphrase_error_1.msg or 'assword' in passphrase_error_1.msg"
- - passphrase_error_2 is failed
- - "'assphrase' in passphrase_error_2.msg or 'assword' in passphrase_error_2.msg or 'serializ' in passphrase_error_2.msg"
- - passphrase_error_3 is failed
- - "'assphrase' in passphrase_error_3.msg or 'assword' in passphrase_error_3.msg or 'serializ' in passphrase_error_3.msg"
-
-- name: (Selfsigned validation, {{select_crypto_backend}}) Verify that broken certificate will be regenerated
- assert:
- that:
- - selfsigned_broken is changed
-
-- name: (Selfsigned validation, {{select_crypto_backend}}) Check backup
- assert:
- that:
- - selfsigned_backup_1 is changed
- - selfsigned_backup_1.backup_file is undefined
- - selfsigned_backup_2 is not changed
- - selfsigned_backup_2.backup_file is undefined
- - selfsigned_backup_3 is changed
- - selfsigned_backup_3.backup_file is string
- - selfsigned_backup_4 is changed
- - selfsigned_backup_4.backup_file is string
- - selfsigned_backup_5 is not changed
- - selfsigned_backup_5.backup_file is undefined
-
-- name: (Selfsigned validation, {{select_crypto_backend}}) Check create subject key identifier
- assert:
- that:
- - selfsigned_subject_key_identifier_1 is changed
- - selfsigned_subject_key_identifier_2 is not changed
- - selfsigned_subject_key_identifier_3 is changed
- - selfsigned_subject_key_identifier_4 is not 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)
- assert:
- that:
- - selfsigned_certificate_ed25519_ed448.results[0] is failed
- - selfsigned_certificate_ed25519_ed448.results[1] is failed
- - selfsigned_certificate_ed25519_ed448_idempotence.results[0] is failed
- - selfsigned_certificate_ed25519_ed448_idempotence.results[1] is failed
- when: select_crypto_backend == 'cryptography' and cryptography_version.stdout is version('2.6', '>=') and cryptography_version.stdout is version('2.8', '<') and selfsigned_certificate_ed25519_ed448_privatekey is not failed
-
-- name: (Selfsigned validation, {{select_crypto_backend}}) Verify Ed25519 and Ed448 tests (for cryptography >= 2.8)
- assert:
- that:
- - selfsigned_certificate_ed25519_ed448 is succeeded
- - selfsigned_certificate_ed25519_ed448.results[0] is changed
- - selfsigned_certificate_ed25519_ed448.results[1] is changed
- - selfsigned_certificate_ed25519_ed448_idempotence is succeeded
- - selfsigned_certificate_ed25519_ed448_idempotence.results[0] is not changed
- - selfsigned_certificate_ed25519_ed448_idempotence.results[1] is not changed
- when: select_crypto_backend == 'cryptography' and cryptography_version.stdout is version('2.8', '>=') and selfsigned_certificate_ed25519_ed448_privatekey is not failed
diff --git a/test/integration/targets/openssl_certificate_info/aliases b/test/integration/targets/openssl_certificate_info/aliases
deleted file mode 100644
index 0b484bbab6..0000000000
--- a/test/integration/targets/openssl_certificate_info/aliases
+++ /dev/null
@@ -1,3 +0,0 @@
-shippable/posix/group1
-destructive
-skip/aix
diff --git a/test/integration/targets/openssl_certificate_info/files/cert1.pem b/test/integration/targets/openssl_certificate_info/files/cert1.pem
deleted file mode 100644
index 834eedc440..0000000000
--- a/test/integration/targets/openssl_certificate_info/files/cert1.pem
+++ /dev/null
@@ -1,45 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIH5jCCBs6gAwIBAgISA2gSCm/BtvCR2e2bIap5YbXaMA0GCSqGSIb3DQEBCwUA
-MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQD
-ExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMzAeFw0xODA3MjcxNzMxMjdaFw0x
-ODEwMjUxNzMxMjdaMB4xHDAaBgNVBAMTE3d3dy5sZXRzZW5jcnlwdC5vcmcwggEi
-MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDpL8ZjVL0MUkUAIbYO9+ZCni+c
-ghGd9WhM2Ztaay6Wyh6lNoCdltdqTwUhE4O+d7UFModjM3G/KMyfuujr06c5iGKL
-3saPmIzLaRPIEOUlB2rKgasKhe8mDRyRLzQSXXgnsaKcTBBuhIHvtP51ZMr05nJJ
-sX/5FGjj96w+KJel6E/Ux1a1ZDOFkAYNSIrJJhA5jjIvUPr+Ri6Oc6UlhF9oueKI
-uWBILxQpC778tBWdHoZeBCNTHA1VvtwC53OeuHvdZm1jB/e30Mgf5DtVizYpFXVD
-mztkrd6z/3B6ZwPyfCE4KgzSf70/byOz971OJxNKTUVWedKHHDlrMxfsPclbAgMB
-AAGjggTwMIIE7DAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEG
-CCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFG1w4j/KDrYSFu7m9DPE
-xRR0E5gzMB8GA1UdIwQYMBaAFKhKamMEfd265tE5t6ZFZe/zqOyhMG8GCCsGAQUF
-BwEBBGMwYTAuBggrBgEFBQcwAYYiaHR0cDovL29jc3AuaW50LXgzLmxldHNlbmNy
-eXB0Lm9yZzAvBggrBgEFBQcwAoYjaHR0cDovL2NlcnQuaW50LXgzLmxldHNlbmNy
-eXB0Lm9yZy8wggHxBgNVHREEggHoMIIB5IIbY2VydC5pbnQteDEubGV0c2VuY3J5
-cHQub3JnghtjZXJ0LmludC14Mi5sZXRzZW5jcnlwdC5vcmeCG2NlcnQuaW50LXgz
-LmxldHNlbmNyeXB0Lm9yZ4IbY2VydC5pbnQteDQubGV0c2VuY3J5cHQub3Jnghxj
-ZXJ0LnJvb3QteDEubGV0c2VuY3J5cHQub3Jngh9jZXJ0LnN0YWdpbmcteDEubGV0
-c2VuY3J5cHQub3Jngh9jZXJ0LnN0Zy1pbnQteDEubGV0c2VuY3J5cHQub3JngiBj
-ZXJ0LnN0Zy1yb290LXgxLmxldHNlbmNyeXB0Lm9yZ4ISY3AubGV0c2VuY3J5cHQu
-b3JnghpjcC5yb290LXgxLmxldHNlbmNyeXB0Lm9yZ4ITY3BzLmxldHNlbmNyeXB0
-Lm9yZ4IbY3BzLnJvb3QteDEubGV0c2VuY3J5cHQub3Jnghtjcmwucm9vdC14MS5s
-ZXRzZW5jcnlwdC5vcmeCD2xldHNlbmNyeXB0Lm9yZ4IWb3JpZ2luLmxldHNlbmNy
-eXB0Lm9yZ4IXb3JpZ2luMi5sZXRzZW5jcnlwdC5vcmeCFnN0YXR1cy5sZXRzZW5j
-cnlwdC5vcmeCE3d3dy5sZXRzZW5jcnlwdC5vcmcwgf4GA1UdIASB9jCB8zAIBgZn
-gQwBAgEwgeYGCysGAQQBgt8TAQEBMIHWMCYGCCsGAQUFBwIBFhpodHRwOi8vY3Bz
-LmxldHNlbmNyeXB0Lm9yZzCBqwYIKwYBBQUHAgIwgZ4MgZtUaGlzIENlcnRpZmlj
-YXRlIG1heSBvbmx5IGJlIHJlbGllZCB1cG9uIGJ5IFJlbHlpbmcgUGFydGllcyBh
-bmQgb25seSBpbiBhY2NvcmRhbmNlIHdpdGggdGhlIENlcnRpZmljYXRlIFBvbGlj
-eSBmb3VuZCBhdCBodHRwczovL2xldHNlbmNyeXB0Lm9yZy9yZXBvc2l0b3J5LzCC
-AQQGCisGAQQB1nkCBAIEgfUEgfIA8AB2AMEWSuCnctLUOS3ICsEHcNTwxJvemRpI
-QMH6B1Fk9jNgAAABZN0ChToAAAQDAEcwRQIgblal8oXnfoopr1+dWVhvBx+sqHT0
-eLYxJHBTaRp3j1QCIQDhFQqMk6DDXUgcU12K36zLVFwJTdAJI4RBisnX+g+W0AB2
-ACk8UZZUyDlluqpQ/FgH1Ldvv1h6KXLcpMMM9OVFR/R4AAABZN0Chz4AAAQDAEcw
-RQIhAImOjvkritUNKJZB7dcUtjoyIbfNwdCspvRiEzXuvVQoAiAZryoyg3TcMun5
-Gb2dEn1cttMnPW9u670/JdRjvjU/wTANBgkqhkiG9w0BAQsFAAOCAQEAGepCmckP
-Tn9Sz268FEwkdD+6wWaPfeYlh+9nacFh90nQ35EYQMOK8a+X7ixHGbRz19On3Wt4
-1fcbPa9SefocTjAintMwwreCxpRTmwGACYojd7vRWEmA6q7+/HO2BfZahWzclOjw
-mSDBycDEm8R0ZK52vYjzVno8x0mrsmSO0403S/6syYB/guH6P17kIBw+Tgx6/i/c
-I1C6MoFkuaAKUUcZmgGGBgE+L/7cWtWjbkVXyA3ZQQy9G7rcBT+N/RrDfBh4iZDq
-jAN5UIIYL8upBhjiMYVuoJrH2nklzEwr5SWKcccJX5eWkGLUwlcY9LGAA8+17l2I
-l1Ou20Dm9TxnNw==
------END CERTIFICATE-----
diff --git a/test/integration/targets/openssl_certificate_info/meta/main.yml b/test/integration/targets/openssl_certificate_info/meta/main.yml
deleted file mode 100644
index 800aff6428..0000000000
--- a/test/integration/targets/openssl_certificate_info/meta/main.yml
+++ /dev/null
@@ -1,2 +0,0 @@
-dependencies:
- - setup_openssl
diff --git a/test/integration/targets/openssl_certificate_info/tasks/impl.yml b/test/integration/targets/openssl_certificate_info/tasks/impl.yml
deleted file mode 100644
index 2ae89ed7fd..0000000000
--- a/test/integration/targets/openssl_certificate_info/tasks/impl.yml
+++ /dev/null
@@ -1,120 +0,0 @@
----
-- debug:
- msg: "Executing tests with backend {{ select_crypto_backend }}"
-
-- name: ({{select_crypto_backend}}) Get certificate info
- openssl_certificate_info:
- path: '{{ output_dir }}/cert_1.pem'
- select_crypto_backend: '{{ select_crypto_backend }}'
- register: result
-
-- name: Check whether issuer and subject behave as expected
- assert:
- that:
- - result.issuer.organizationalUnitName == 'ACME Department'
- - "['organizationalUnitName', 'Crypto Department'] in result.issuer_ordered"
- - "['organizationalUnitName', 'ACME Department'] in result.issuer_ordered"
- - result.subject.organizationalUnitName == 'ACME Department'
- - "['organizationalUnitName', 'Crypto Department'] in result.subject_ordered"
- - "['organizationalUnitName', 'ACME Department'] in result.subject_ordered"
-
-- name: Check SubjectKeyIdentifier and AuthorityKeyIdentifier
- assert:
- that:
- - result.subject_key_identifier == "00:11:22:33"
- - result.authority_key_identifier == "44:55:66:77"
- - result.authority_cert_issuer == expected_authority_cert_issuer
- - result.authority_cert_serial_number == 12345
- vars:
- expected_authority_cert_issuer:
- - "DNS:ca.example.org"
- - "IP:1.2.3.4"
- when: select_crypto_backend != 'pyopenssl' and 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 directly
- openssl_certificate_info:
- content: '{{ lookup("file", output_dir ~ "/cert_1.pem") }}'
- select_crypto_backend: '{{ select_crypto_backend }}'
- register: result_direct
-
-- name: ({{select_crypto_backend}}) Compare output of direct and loaded info
- assert:
- that:
- - result == result_direct
-
-- name: ({{select_crypto_backend}}) Get certificate info
- openssl_certificate_info:
- path: '{{ output_dir }}/cert_2.pem'
- select_crypto_backend: '{{ select_crypto_backend }}'
- valid_at:
- today: "+0d"
- past: "20190101235901Z"
- twentydays: "+20d"
- register: result
-- assert:
- that:
- - result.valid_at.today
- - not result.valid_at.past
- - not result.valid_at.twentydays
-
-- name: Update result list
- set_fact:
- info_results: "{{ info_results + [result] }}"
-
-- name: ({{select_crypto_backend}}) Get certificate info
- openssl_certificate_info:
- path: '{{ output_dir }}/cert_3.pem'
- select_crypto_backend: '{{ select_crypto_backend }}'
- register: result
-
-- name: Check AuthorityKeyIdentifier
- assert:
- that:
- - result.authority_key_identifier is none
- - result.authority_cert_issuer == expected_authority_cert_issuer
- - result.authority_cert_serial_number == 12345
- vars:
- expected_authority_cert_issuer:
- - "DNS:ca.example.org"
- - "IP:1.2.3.4"
- when: select_crypto_backend != 'pyopenssl' and 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
- openssl_certificate_info:
- path: '{{ output_dir }}/cert_4.pem'
- select_crypto_backend: '{{ select_crypto_backend }}'
- register: result
-
-- name: Check AuthorityKeyIdentifier
- assert:
- that:
- - result.authority_key_identifier == "44:55:66:77"
- - result.authority_cert_issuer is none
- - result.authority_cert_serial_number is none
- when: select_crypto_backend != 'pyopenssl' and 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 for packaged cert 1
- openssl_certificate_info:
- path: '{{ role_path }}/files/cert1.pem'
- select_crypto_backend: '{{ select_crypto_backend }}'
- register: result
-- assert:
- that:
- - "'ocsp_uri' in result"
- - "result.ocsp_uri == 'http://ocsp.int-x3.letsencrypt.org'"
-
-- name: Update result list
- set_fact:
- info_results: "{{ info_results + [result] }}"
diff --git a/test/integration/targets/openssl_certificate_info/tasks/main.yml b/test/integration/targets/openssl_certificate_info/tasks/main.yml
deleted file mode 100644
index 8fc2636c22..0000000000
--- a/test/integration/targets/openssl_certificate_info/tasks/main.yml
+++ /dev/null
@@ -1,176 +0,0 @@
----
-- name: Generate privatekey
- openssl_privatekey:
- path: '{{ output_dir }}/privatekey.pem'
-
-- name: Generate privatekey with password
- openssl_privatekey:
- path: '{{ output_dir }}/privatekeypw.pem'
- passphrase: hunter2
- cipher: auto
- select_crypto_backend: cryptography
-
-- name: Generate CSR 1
- openssl_csr:
- path: '{{ output_dir }}/csr_1.csr'
- privatekey_path: '{{ output_dir }}/privatekey.pem'
- subject:
- commonName: www.example.com
- C: de
- L: Somewhere
- ST: Zurich
- streetAddress: Welcome Street
- O: Ansible
- organizationalUnitName:
- - Crypto Department
- - ACME Department
- serialNumber: "1234"
- SN: Last Name
- GN: First Name
- title: Chief
- pseudonym: test
- UID: asdf
- emailAddress: test@example.com
- postalAddress: 1234 Somewhere
- postalCode: "1234"
- useCommonNameForSAN: no
- key_usage:
- - digitalSignature
- - keyAgreement
- - Non Repudiation
- - Key Encipherment
- - dataEncipherment
- - Certificate Sign
- - cRLSign
- - Encipher Only
- - decipherOnly
- key_usage_critical: yes
- extended_key_usage:
- - 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
- 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"
- basic_constraints:
- - "CA:TRUE"
- - "pathlen:23"
- basic_constraints_critical: yes
- ocsp_must_staple: yes
- subject_key_identifier: '{{ "00:11:22:33" 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 }}'
- authority_cert_issuer: '{{ value_for_authority_cert_issuer if cryptography_version.stdout is version("1.3", ">=") else omit }}'
- authority_cert_serial_number: '{{ 12345 if cryptography_version.stdout is version("1.3", ">=") else omit }}'
- vars:
- value_for_authority_cert_issuer:
- - "DNS:ca.example.org"
- - "IP:1.2.3.4"
-
-- name: Generate CSR 2
- openssl_csr:
- path: '{{ output_dir }}/csr_2.csr'
- privatekey_path: '{{ output_dir }}/privatekeypw.pem'
- privatekey_passphrase: hunter2
- useCommonNameForSAN: no
- basic_constraints:
- - "CA:TRUE"
-
-- name: Generate CSR 3
- openssl_csr:
- path: '{{ output_dir }}/csr_3.csr'
- privatekey_path: '{{ output_dir }}/privatekey.pem'
- useCommonNameForSAN: no
- subject_alt_name:
- - "DNS:*.ansible.com"
- - "DNS:*.example.org"
- - "IP:DEAD:BEEF::1"
- basic_constraints:
- - "CA:FALSE"
- authority_cert_issuer: '{{ value_for_authority_cert_issuer if cryptography_version.stdout is version("1.3", ">=") else omit }}'
- authority_cert_serial_number: '{{ 12345 if cryptography_version.stdout is version("1.3", ">=") else omit }}'
- vars:
- value_for_authority_cert_issuer:
- - "DNS:ca.example.org"
- - "IP:1.2.3.4"
-
-- name: Generate CSR 4
- openssl_csr:
- path: '{{ output_dir }}/csr_4.csr'
- privatekey_path: '{{ output_dir }}/privatekey.pem'
- useCommonNameForSAN: no
- authority_key_identifier: '{{ "44:55:66:77" if cryptography_version.stdout is version("1.3", ">=") else omit }}'
-
-- name: Generate selfsigned certificates
- openssl_certificate:
- path: '{{ output_dir }}/cert_{{ item }}.pem'
- csr_path: '{{ output_dir }}/csr_{{ item }}.csr'
- privatekey_path: '{{ output_dir }}/privatekey.pem'
- provider: selfsigned
- selfsigned_digest: sha256
- selfsigned_not_after: "+10d"
- selfsigned_not_before: "-3d"
- loop:
- - 1
- - 2
- - 3
- - 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
- include_tasks: impl.yml
- vars:
- select_crypto_backend: cryptography
- 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
diff --git a/test/integration/targets/openssl_certificate_info/test_plugins/jinja_compatibility.py b/test/integration/targets/openssl_certificate_info/test_plugins/jinja_compatibility.py
deleted file mode 100644
index fc2b5f0fcb..0000000000
--- a/test/integration/targets/openssl_certificate_info/test_plugins/jinja_compatibility.py
+++ /dev/null
@@ -1,15 +0,0 @@
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
-
-
-def compatibility_in_test(a, b):
- return a in b
-
-
-class TestModule:
- ''' Ansible math jinja2 tests '''
-
- def tests(self):
- return {
- 'in': compatibility_in_test,
- }
diff --git a/test/integration/targets/openssl_csr/aliases b/test/integration/targets/openssl_csr/aliases
deleted file mode 100644
index 0b484bbab6..0000000000
--- a/test/integration/targets/openssl_csr/aliases
+++ /dev/null
@@ -1,3 +0,0 @@
-shippable/posix/group1
-destructive
-skip/aix
diff --git a/test/integration/targets/openssl_csr/meta/main.yml b/test/integration/targets/openssl_csr/meta/main.yml
deleted file mode 100644
index 800aff6428..0000000000
--- a/test/integration/targets/openssl_csr/meta/main.yml
+++ /dev/null
@@ -1,2 +0,0 @@
-dependencies:
- - setup_openssl
diff --git a/test/integration/targets/openssl_csr/tasks/impl.yml b/test/integration/targets/openssl_csr/tasks/impl.yml
deleted file mode 100644
index 77d23ff5a8..0000000000
--- a/test/integration/targets/openssl_csr/tasks/impl.yml
+++ /dev/null
@@ -1,767 +0,0 @@
----
-- name: Generate privatekey
- openssl_privatekey:
- path: '{{ output_dir }}/privatekey.pem'
-
-- name: Generate CSR (check mode)
- openssl_csr:
- path: '{{ output_dir }}/csr.csr'
- privatekey_path: '{{ output_dir }}/privatekey.pem'
- subject:
- commonName: www.ansible.com
- select_crypto_backend: '{{ select_crypto_backend }}'
- return_content: yes
- check_mode: yes
- register: generate_csr_check
-
-- name: Generate CSR
- openssl_csr:
- path: '{{ output_dir }}/csr.csr'
- privatekey_path: '{{ output_dir }}/privatekey.pem'
- subject:
- commonName: www.ansible.com
- select_crypto_backend: '{{ select_crypto_backend }}'
- return_content: yes
- register: generate_csr
-
-- name: Generate CSR (idempotent)
- openssl_csr:
- path: '{{ output_dir }}/csr.csr'
- privatekey_path: '{{ output_dir }}/privatekey.pem'
- subject:
- commonName: www.ansible.com
- select_crypto_backend: '{{ select_crypto_backend }}'
- return_content: yes
- register: generate_csr_idempotent
-
-- name: Generate CSR (idempotent, check mode)
- openssl_csr:
- path: '{{ output_dir }}/csr.csr'
- privatekey_path: '{{ output_dir }}/privatekey.pem'
- subject:
- commonName: www.ansible.com
- select_crypto_backend: '{{ select_crypto_backend }}'
- return_content: yes
- check_mode: yes
- register: generate_csr_idempotent_check
-
-- name: Generate CSR without SAN (check mode)
- openssl_csr:
- path: '{{ output_dir }}/csr-nosan.csr'
- privatekey_path: '{{ output_dir }}/privatekey.pem'
- subject:
- commonName: www.ansible.com
- useCommonNameForSAN: no
- select_crypto_backend: '{{ select_crypto_backend }}'
- check_mode: yes
- register: generate_csr_nosan_check
-
-- name: Generate CSR without SAN
- openssl_csr:
- path: '{{ output_dir }}/csr-nosan.csr'
- privatekey_path: '{{ output_dir }}/privatekey.pem'
- subject:
- commonName: www.ansible.com
- useCommonNameForSAN: no
- select_crypto_backend: '{{ select_crypto_backend }}'
- register: generate_csr_nosan
-
-- name: Generate CSR without SAN (idempotent)
- openssl_csr:
- path: '{{ output_dir }}/csr-nosan.csr'
- privatekey_path: '{{ output_dir }}/privatekey.pem'
- subject:
- commonName: www.ansible.com
- useCommonNameForSAN: no
- select_crypto_backend: '{{ select_crypto_backend }}'
- register: generate_csr_nosan_check_idempotent
-
-- name: Generate CSR without SAN (idempotent, check mode)
- openssl_csr:
- path: '{{ output_dir }}/csr-nosan.csr'
- privatekey_path: '{{ output_dir }}/privatekey.pem'
- subject:
- commonName: www.ansible.com
- useCommonNameForSAN: no
- select_crypto_backend: '{{ select_crypto_backend }}'
- check_mode: yes
- register: generate_csr_nosan_check_idempotent_check
-
-# keyUsage longname and shortname should be able to be used
-# interchangeably. Hence the long name is specified here
-# but the short name is used to test idempotency for ipsecuser
-# and vice-versa for biometricInfo
-- name: Generate CSR with KU and XKU
- openssl_csr:
- path: '{{ output_dir }}/csr_ku_xku.csr'
- privatekey_path: '{{ output_dir }}/privatekey.pem'
- subject:
- CN: www.ansible.com
- keyUsage:
- - digitalSignature
- - keyAgreement
- extendedKeyUsage:
- - qcStatements
- - DVCS
- - IPSec User
- - biometricInfo
- select_crypto_backend: '{{ select_crypto_backend }}'
-
-- name: Generate CSR with KU and XKU (test idempotency)
- openssl_csr:
- path: '{{ output_dir }}/csr_ku_xku.csr'
- privatekey_path: '{{ output_dir }}/privatekey.pem'
- subject:
- commonName: 'www.ansible.com'
- keyUsage:
- - Key Agreement
- - digitalSignature
- extendedKeyUsage:
- - ipsecUser
- - qcStatements
- - DVCS
- - Biometric Info
- select_crypto_backend: '{{ select_crypto_backend }}'
- register: csr_ku_xku
-
-- name: Generate CSR with KU and XKU (test XKU change)
- openssl_csr:
- path: '{{ output_dir }}/csr_ku_xku.csr'
- privatekey_path: '{{ output_dir }}/privatekey.pem'
- subject:
- commonName: 'www.ansible.com'
- keyUsage:
- - digitalSignature
- - keyAgreement
- extendedKeyUsage:
- - ipsecUser
- - qcStatements
- - Biometric Info
- select_crypto_backend: '{{ select_crypto_backend }}'
- register: csr_ku_xku_change
-
-- name: Generate CSR with KU and XKU (test KU change)
- openssl_csr:
- path: '{{ output_dir }}/csr_ku_xku.csr'
- privatekey_path: '{{ output_dir }}/privatekey.pem'
- subject:
- commonName: 'www.ansible.com'
- keyUsage:
- - digitalSignature
- extendedKeyUsage:
- - ipsecUser
- - qcStatements
- - Biometric Info
- select_crypto_backend: '{{ select_crypto_backend }}'
- register: csr_ku_xku_change_2
-
-- name: Generate CSR with old API
- openssl_csr:
- path: '{{ output_dir }}/csr_oldapi.csr'
- privatekey_path: '{{ output_dir }}/privatekey.pem'
- commonName: www.ansible.com
- select_crypto_backend: '{{ select_crypto_backend }}'
-
-- name: Generate CSR with invalid SAN
- openssl_csr:
- path: '{{ output_dir }}/csrinvsan.csr'
- privatekey_path: '{{ output_dir }}/privatekey.pem'
- subject_alt_name: invalid-san.example.com
- select_crypto_backend: '{{ select_crypto_backend }}'
- register: generate_csr_invalid_san
- ignore_errors: yes
-
-- name: Generate CSR with OCSP Must Staple
- openssl_csr:
- path: '{{ output_dir }}/csr_ocsp.csr'
- privatekey_path: '{{ output_dir }}/privatekey.pem'
- subject_alt_name: "DNS:www.ansible.com"
- ocsp_must_staple: true
- select_crypto_backend: '{{ select_crypto_backend }}'
-
-- name: Generate CSR with OCSP Must Staple (test idempotency)
- openssl_csr:
- path: '{{ output_dir }}/csr_ocsp.csr'
- privatekey_path: '{{ output_dir }}/privatekey.pem'
- subject_alt_name: "DNS:www.ansible.com"
- ocsp_must_staple: true
- select_crypto_backend: '{{ select_crypto_backend }}'
- register: csr_ocsp_idempotency
-
-- name: Generate ECC privatekey
- openssl_privatekey:
- path: '{{ output_dir }}/privatekey2.pem'
- type: ECC
- curve: secp384r1
-
-- name: Generate CSR with ECC privatekey
- openssl_csr:
- path: '{{ output_dir }}/csr2.csr'
- privatekey_path: '{{ output_dir }}/privatekey2.pem'
- subject:
- commonName: www.ansible.com
- select_crypto_backend: '{{ select_crypto_backend }}'
-
-- name: Generate CSR with text common name
- openssl_csr:
- path: '{{ output_dir }}/csr3.csr'
- privatekey_path: '{{ output_dir }}/privatekey2.pem'
- subject:
- commonName: This is for Ansible
- useCommonNameForSAN: no
- select_crypto_backend: '{{ select_crypto_backend }}'
-
-- name: Generate CSR with country name
- openssl_csr:
- path: '{{ output_dir }}/csr4.csr'
- privatekey_path: '{{ output_dir }}/privatekey2.pem'
- country_name: de
- select_crypto_backend: '{{ select_crypto_backend }}'
- register: country_idempotent_1
-
-- name: Generate CSR with country name (idempotent)
- openssl_csr:
- path: '{{ output_dir }}/csr4.csr'
- privatekey_path: '{{ output_dir }}/privatekey2.pem'
- country_name: de
- select_crypto_backend: '{{ select_crypto_backend }}'
- register: country_idempotent_2
-
-- name: Generate CSR with country name (idempotent 2)
- openssl_csr:
- path: '{{ output_dir }}/csr4.csr'
- privatekey_path: '{{ output_dir }}/privatekey2.pem'
- subject:
- C: de
- select_crypto_backend: '{{ select_crypto_backend }}'
- register: country_idempotent_3
-
-- name: Generate CSR with country name (bad country name)
- openssl_csr:
- path: '{{ output_dir }}/csr4.csr'
- privatekey_path: '{{ output_dir }}/privatekey2.pem'
- subject:
- C: dex
- select_crypto_backend: '{{ select_crypto_backend }}'
- register: country_fail_4
- ignore_errors: yes
-
-- name: Generate privatekey with password
- openssl_privatekey:
- path: '{{ output_dir }}/privatekeypw.pem'
- passphrase: hunter2
- cipher: auto
- select_crypto_backend: cryptography
-
-- name: Generate CSR with privatekey passphrase
- openssl_csr:
- path: '{{ output_dir }}/csr_pw.csr'
- privatekey_path: '{{ output_dir }}/privatekeypw.pem'
- privatekey_passphrase: hunter2
- select_crypto_backend: '{{ select_crypto_backend }}'
- register: passphrase_1
-
-- name: Generate CSR (failed passphrase 1)
- openssl_csr:
- path: '{{ output_dir }}/csr_pw1.csr'
- privatekey_path: '{{ output_dir }}/privatekey.pem'
- privatekey_passphrase: hunter2
- select_crypto_backend: '{{ select_crypto_backend }}'
- ignore_errors: yes
- register: passphrase_error_1
-
-- name: Generate CSR (failed passphrase 2)
- openssl_csr:
- path: '{{ output_dir }}/csr_pw2.csr'
- privatekey_path: '{{ output_dir }}/privatekeypw.pem'
- privatekey_passphrase: wrong_password
- select_crypto_backend: '{{ select_crypto_backend }}'
- ignore_errors: yes
- register: passphrase_error_2
-
-- name: Generate CSR (failed passphrase 3)
- openssl_csr:
- path: '{{ output_dir }}/csr_pw3.csr'
- privatekey_path: '{{ output_dir }}/privatekeypw.pem'
- select_crypto_backend: '{{ select_crypto_backend }}'
- ignore_errors: yes
- register: passphrase_error_3
-
-- name: Create broken CSR
- copy:
- dest: "{{ output_dir }}/csrbroken.csr"
- content: "broken"
-- name: Regenerate broken CSR
- openssl_csr:
- path: '{{ output_dir }}/csrbroken.csr'
- privatekey_path: '{{ output_dir }}/privatekey2.pem'
- subject:
- commonName: This is for Ansible
- useCommonNameForSAN: no
- select_crypto_backend: '{{ select_crypto_backend }}'
- register: output_broken
-
-- name: Generate CSR
- openssl_csr:
- path: '{{ output_dir }}/csr_backup.csr'
- privatekey_path: '{{ output_dir }}/privatekey.pem'
- subject:
- commonName: www.ansible.com
- backup: yes
- select_crypto_backend: '{{ select_crypto_backend }}'
- register: csr_backup_1
-- name: Generate CSR (idempotent)
- openssl_csr:
- path: '{{ output_dir }}/csr_backup.csr'
- privatekey_path: '{{ output_dir }}/privatekey.pem'
- subject:
- commonName: www.ansible.com
- backup: yes
- select_crypto_backend: '{{ select_crypto_backend }}'
- register: csr_backup_2
-- name: Generate CSR (change)
- openssl_csr:
- path: '{{ output_dir }}/csr_backup.csr'
- privatekey_path: '{{ output_dir }}/privatekey.pem'
- subject:
- commonName: ansible.com
- backup: yes
- select_crypto_backend: '{{ select_crypto_backend }}'
- register: csr_backup_3
-- name: Generate CSR (remove)
- openssl_csr:
- path: '{{ output_dir }}/csr_backup.csr'
- state: absent
- backup: yes
- select_crypto_backend: '{{ select_crypto_backend }}'
- return_content: yes
- register: csr_backup_4
-- name: Generate CSR (remove, idempotent)
- openssl_csr:
- path: '{{ output_dir }}/csr_backup.csr'
- state: absent
- backup: yes
- select_crypto_backend: '{{ select_crypto_backend }}'
- register: csr_backup_5
-
-- name: Generate CSR with subject key identifier
- openssl_csr:
- path: '{{ output_dir }}/csr_ski.csr'
- privatekey_path: '{{ output_dir }}/privatekey.pem'
- subject:
- commonName: www.ansible.com
- subject_key_identifier: "00:11:22:33"
- select_crypto_backend: '{{ select_crypto_backend }}'
- when: select_crypto_backend != 'pyopenssl'
- register: subject_key_identifier_1
-
-- name: Generate CSR with subject key identifier (idempotency)
- openssl_csr:
- path: '{{ output_dir }}/csr_ski.csr'
- privatekey_path: '{{ output_dir }}/privatekey.pem'
- subject:
- commonName: www.ansible.com
- subject_key_identifier: "00:11:22:33"
- select_crypto_backend: '{{ select_crypto_backend }}'
- when: select_crypto_backend != 'pyopenssl'
- register: subject_key_identifier_2
-
-- name: Generate CSR with subject key identifier (change)
- openssl_csr:
- path: '{{ output_dir }}/csr_ski.csr'
- privatekey_path: '{{ output_dir }}/privatekey.pem'
- subject:
- commonName: www.ansible.com
- subject_key_identifier: "44:55:66:77:88"
- select_crypto_backend: '{{ select_crypto_backend }}'
- when: select_crypto_backend != 'pyopenssl'
- register: subject_key_identifier_3
-
-- name: Generate CSR with subject key identifier (auto-create)
- openssl_csr:
- path: '{{ output_dir }}/csr_ski.csr'
- privatekey_path: '{{ output_dir }}/privatekey.pem'
- subject:
- commonName: www.ansible.com
- create_subject_key_identifier: yes
- select_crypto_backend: '{{ select_crypto_backend }}'
- when: select_crypto_backend != 'pyopenssl'
- register: subject_key_identifier_4
-
-- name: Generate CSR with subject key identifier (auto-create idempotency)
- openssl_csr:
- path: '{{ output_dir }}/csr_ski.csr'
- privatekey_path: '{{ output_dir }}/privatekey.pem'
- subject:
- commonName: www.ansible.com
- create_subject_key_identifier: yes
- select_crypto_backend: '{{ select_crypto_backend }}'
- when: select_crypto_backend != 'pyopenssl'
- register: subject_key_identifier_5
-
-- name: Generate CSR with subject key identifier (remove)
- openssl_csr:
- path: '{{ output_dir }}/csr_ski.csr'
- privatekey_path: '{{ output_dir }}/privatekey.pem'
- subject:
- commonName: www.ansible.com
- select_crypto_backend: '{{ select_crypto_backend }}'
- when: select_crypto_backend != 'pyopenssl'
- register: subject_key_identifier_6
-
-- name: Generate CSR with authority key identifier
- openssl_csr:
- path: '{{ output_dir }}/csr_aki.csr'
- privatekey_path: '{{ output_dir }}/privatekey.pem'
- subject:
- commonName: www.ansible.com
- authority_key_identifier: "00:11:22:33"
- select_crypto_backend: '{{ select_crypto_backend }}'
- when: select_crypto_backend != 'pyopenssl'
- register: authority_key_identifier_1
-
-- name: Generate CSR with authority key identifier (idempotency)
- openssl_csr:
- path: '{{ output_dir }}/csr_aki.csr'
- privatekey_path: '{{ output_dir }}/privatekey.pem'
- subject:
- commonName: www.ansible.com
- authority_key_identifier: "00:11:22:33"
- select_crypto_backend: '{{ select_crypto_backend }}'
- when: select_crypto_backend != 'pyopenssl'
- register: authority_key_identifier_2
-
-- name: Generate CSR with authority key identifier (change)
- openssl_csr:
- path: '{{ output_dir }}/csr_aki.csr'
- privatekey_path: '{{ output_dir }}/privatekey.pem'
- subject:
- commonName: www.ansible.com
- authority_key_identifier: "44:55:66:77:88"
- select_crypto_backend: '{{ select_crypto_backend }}'
- when: select_crypto_backend != 'pyopenssl'
- register: authority_key_identifier_3
-
-- name: Generate CSR with authority key identifier (remove)
- openssl_csr:
- path: '{{ output_dir }}/csr_aki.csr'
- privatekey_path: '{{ output_dir }}/privatekey.pem'
- subject:
- commonName: www.ansible.com
- select_crypto_backend: '{{ select_crypto_backend }}'
- when: select_crypto_backend != 'pyopenssl'
- register: authority_key_identifier_4
-
-- name: Generate CSR with authority cert issuer / serial number
- openssl_csr:
- path: '{{ output_dir }}/csr_acisn.csr'
- privatekey_path: '{{ output_dir }}/privatekey.pem'
- subject:
- commonName: www.ansible.com
- authority_cert_issuer:
- - "DNS:ca.example.org"
- - "IP:1.2.3.4"
- authority_cert_serial_number: 12345
- select_crypto_backend: '{{ select_crypto_backend }}'
- when: select_crypto_backend != 'pyopenssl'
- register: authority_cert_issuer_sn_1
-
-- name: Generate CSR with authority cert issuer / serial number (idempotency)
- openssl_csr:
- path: '{{ output_dir }}/csr_acisn.csr'
- privatekey_path: '{{ output_dir }}/privatekey.pem'
- subject:
- commonName: www.ansible.com
- authority_cert_issuer:
- - "DNS:ca.example.org"
- - "IP:1.2.3.4"
- authority_cert_serial_number: 12345
- select_crypto_backend: '{{ select_crypto_backend }}'
- when: select_crypto_backend != 'pyopenssl'
- register: authority_cert_issuer_sn_2
-
-- name: Generate CSR with authority cert issuer / serial number (change issuer)
- openssl_csr:
- path: '{{ output_dir }}/csr_acisn.csr'
- privatekey_path: '{{ output_dir }}/privatekey.pem'
- subject:
- commonName: www.ansible.com
- authority_cert_issuer:
- - "IP:1.2.3.4"
- - "DNS:ca.example.org"
- authority_cert_serial_number: 12345
- select_crypto_backend: '{{ select_crypto_backend }}'
- when: select_crypto_backend != 'pyopenssl'
- register: authority_cert_issuer_sn_3
-
-- name: Generate CSR with authority cert issuer / serial number (change serial number)
- openssl_csr:
- path: '{{ output_dir }}/csr_acisn.csr'
- privatekey_path: '{{ output_dir }}/privatekey.pem'
- subject:
- commonName: www.ansible.com
- authority_cert_issuer:
- - "IP:1.2.3.4"
- - "DNS:ca.example.org"
- authority_cert_serial_number: 54321
- select_crypto_backend: '{{ select_crypto_backend }}'
- when: select_crypto_backend != 'pyopenssl'
- register: authority_cert_issuer_sn_4
-
-- name: Generate CSR with authority cert issuer / serial number (remove)
- openssl_csr:
- path: '{{ output_dir }}/csr_acisn.csr'
- privatekey_path: '{{ output_dir }}/privatekey.pem'
- subject:
- commonName: www.ansible.com
- when: select_crypto_backend != 'pyopenssl'
- register: authority_cert_issuer_sn_5
-
-- name: Generate CSR with everything
- openssl_csr:
- path: '{{ output_dir }}/csr_everything.csr'
- privatekey_path: '{{ output_dir }}/privatekey.pem'
- subject:
- commonName: www.example.com
- C: de
- L: Somewhere
- ST: Zurich
- streetAddress: Welcome Street
- O: Ansible
- organizationalUnitName: Crypto Department
- serialNumber: "1234"
- SN: Last Name
- GN: First Name
- title: Chief
- pseudonym: test
- UID: asdf
- emailAddress: test@example.com
- postalAddress: 1234 Somewhere
- postalCode: "1234"
- useCommonNameForSAN: no
- key_usage:
- - digitalSignature
- - keyAgreement
- - Non Repudiation
- - Key Encipherment
- - dataEncipherment
- - Certificate Sign
- - cRLSign
- - Encipher Only
- - decipherOnly
- key_usage_critical: yes
- extended_key_usage:
- - 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
- 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"
- basic_constraints:
- - "CA:TRUE"
- - "pathlen:23"
- basic_constraints_critical: yes
- ocsp_must_staple: yes
- subject_key_identifier: '{{ "00:11:22:33" if select_crypto_backend != "pyopenssl" else omit }}'
- authority_key_identifier: '{{ "44:55:66:77" if select_crypto_backend != "pyopenssl" else omit }}'
- authority_cert_issuer: '{{ value_for_authority_cert_issuer if select_crypto_backend != "pyopenssl" else omit }}'
- authority_cert_serial_number: '{{ 12345 if select_crypto_backend != "pyopenssl" else omit }}'
- select_crypto_backend: '{{ select_crypto_backend }}'
- vars:
- value_for_authority_cert_issuer:
- - "DNS:ca.example.org"
- - "IP:1.2.3.4"
- register: everything_1
-
-- name: Generate CSR with everything (idempotent, check mode)
- openssl_csr:
- path: '{{ output_dir }}/csr_everything.csr'
- privatekey_path: '{{ output_dir }}/privatekey.pem'
- subject:
- commonName: www.example.com
- C: de
- L: Somewhere
- ST: Zurich
- streetAddress: Welcome Street
- O: Ansible
- organizationalUnitName: Crypto Department
- serialNumber: "1234"
- SN: Last Name
- GN: First Name
- title: Chief
- pseudonym: test
- UID: asdf
- emailAddress: test@example.com
- postalAddress: 1234 Somewhere
- postalCode: "1234"
- useCommonNameForSAN: no
- key_usage:
- - digitalSignature
- - keyAgreement
- - Non Repudiation
- - Key Encipherment
- - dataEncipherment
- - Certificate Sign
- - cRLSign
- - Encipher Only
- - decipherOnly
- key_usage_critical: yes
- extended_key_usage:
- - 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
- 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"
- basic_constraints:
- - "CA:TRUE"
- - "pathlen:23"
- basic_constraints_critical: yes
- ocsp_must_staple: yes
- subject_key_identifier: '{{ "00:11:22:33" if select_crypto_backend != "pyopenssl" else omit }}'
- authority_key_identifier: '{{ "44:55:66:77" if select_crypto_backend != "pyopenssl" else omit }}'
- authority_cert_issuer: '{{ value_for_authority_cert_issuer if select_crypto_backend != "pyopenssl" else omit }}'
- authority_cert_serial_number: '{{ 12345 if select_crypto_backend != "pyopenssl" else omit }}'
- select_crypto_backend: '{{ select_crypto_backend }}'
- vars:
- value_for_authority_cert_issuer:
- - "DNS:ca.example.org"
- - "IP:1.2.3.4"
- check_mode: yes
- register: everything_2
-
-- name: Generate CSR with everything (idempotent)
- openssl_csr:
- path: '{{ output_dir }}/csr_everything.csr'
- privatekey_path: '{{ output_dir }}/privatekey.pem'
- subject:
- commonName: www.example.com
- C: de
- L: Somewhere
- ST: Zurich
- streetAddress: Welcome Street
- O: Ansible
- organizationalUnitName: Crypto Department
- serialNumber: "1234"
- SN: Last Name
- GN: First Name
- title: Chief
- pseudonym: test
- UID: asdf
- emailAddress: test@example.com
- postalAddress: 1234 Somewhere
- postalCode: "1234"
- useCommonNameForSAN: no
- key_usage:
- - digitalSignature
- - keyAgreement
- - Non Repudiation
- - Key Encipherment
- - dataEncipherment
- - Certificate Sign
- - cRLSign
- - Encipher Only
- - decipherOnly
- key_usage_critical: yes
- extended_key_usage:
- - 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
- 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"
- basic_constraints:
- - "CA:TRUE"
- - "pathlen:23"
- basic_constraints_critical: yes
- ocsp_must_staple: yes
- subject_key_identifier: '{{ "00:11:22:33" if select_crypto_backend != "pyopenssl" else omit }}'
- authority_key_identifier: '{{ "44:55:66:77" if select_crypto_backend != "pyopenssl" else omit }}'
- authority_cert_issuer: '{{ value_for_authority_cert_issuer if select_crypto_backend != "pyopenssl" else omit }}'
- authority_cert_serial_number: '{{ 12345 if select_crypto_backend != "pyopenssl" else omit }}'
- select_crypto_backend: '{{ select_crypto_backend }}'
- vars:
- value_for_authority_cert_issuer:
- - "DNS:ca.example.org"
- - "IP:1.2.3.4"
- register: everything_3
-
-- name: Ed25519 and Ed448 tests (for cryptography >= 2.6)
- block:
- - name: Generate privatekeys
- openssl_privatekey:
- path: '{{ output_dir }}/privatekey_{{ item }}.pem'
- type: '{{ item }}'
- loop:
- - Ed25519
- - Ed448
- register: generate_csr_ed25519_ed448_privatekey
- ignore_errors: yes
-
- - name: Generate CSR if private key generation succeeded
- when: generate_csr_ed25519_ed448_privatekey is not failed
- block:
-
- - name: Generate CSR
- openssl_csr:
- path: '{{ output_dir }}/csr_{{ item }}.csr'
- privatekey_path: '{{ output_dir }}/privatekey_{{ item }}.pem'
- subject:
- commonName: www.ansible.com
- select_crypto_backend: '{{ select_crypto_backend }}'
- loop:
- - Ed25519
- - Ed448
- register: generate_csr_ed25519_ed448
- ignore_errors: yes
-
- - name: Generate CSR (idempotent)
- openssl_csr:
- path: '{{ output_dir }}/csr_{{ item }}.csr'
- privatekey_path: '{{ output_dir }}/privatekey_{{ item }}.pem'
- subject:
- commonName: www.ansible.com
- select_crypto_backend: '{{ select_crypto_backend }}'
- loop:
- - Ed25519
- - Ed448
- register: generate_csr_ed25519_ed448_idempotent
- ignore_errors: yes
-
- when: select_crypto_backend == 'cryptography' and cryptography_version.stdout is version('2.6', '>=')
diff --git a/test/integration/targets/openssl_csr/tasks/main.yml b/test/integration/targets/openssl_csr/tasks/main.yml
deleted file mode 100644
index e3cc0efb9a..0000000000
--- a/test/integration/targets/openssl_csr/tasks/main.yml
+++ /dev/null
@@ -1,44 +0,0 @@
----
-- name: Prepare private key for backend autodetection test
- openssl_privatekey:
- path: '{{ output_dir }}/privatekey_backend_selection.pem'
-- name: Run module with backend autodetection
- openssl_csr:
- path: '{{ output_dir }}/csr_backend_selection.csr'
- privatekey_path: '{{ output_dir }}/privatekey_backend_selection.pem'
- subject:
- commonName: www.ansible.com
-
-- 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('0.15', '>=')
-
-- name: Remove output directory
- file:
- path: "{{ output_dir }}"
- state: absent
-
-- name: Re-create output directory
- file:
- path: "{{ output_dir }}"
- state: directory
-
-- block:
- - name: Running tests with cryptography backend
- include_tasks: impl.yml
- vars:
- select_crypto_backend: cryptography
-
- - import_tasks: ../tests/validate.yml
- vars:
- select_crypto_backend: cryptography
-
- when: cryptography_version.stdout is version('1.3', '>=')
diff --git a/test/integration/targets/openssl_csr/tests/validate.yml b/test/integration/targets/openssl_csr/tests/validate.yml
deleted file mode 100644
index bdf4b859cc..0000000000
--- a/test/integration/targets/openssl_csr/tests/validate.yml
+++ /dev/null
@@ -1,208 +0,0 @@
----
-- name: Validate CSR (test - privatekey modulus)
- shell: 'openssl rsa -noout -modulus -in {{ output_dir }}/privatekey.pem'
- register: privatekey_modulus
-
-- name: Validate CSR (test - Common Name)
- shell: "openssl req -noout -subject -in {{ output_dir }}/csr.csr -nameopt oneline,-space_eq"
- register: csr_cn
-
-- name: Validate CSR (test - csr modulus)
- shell: 'openssl req -noout -modulus -in {{ output_dir }}/csr.csr'
- register: csr_modulus
-
-- name: Validate CSR (assert)
- assert:
- that:
- - csr_cn.stdout.split('=')[-1] == 'www.ansible.com'
- - csr_modulus.stdout == privatekey_modulus.stdout
-
-- name: Validate CSR (check mode, idempotency)
- assert:
- that:
- - generate_csr_check is changed
- - generate_csr is changed
- - generate_csr_idempotent is not changed
- - generate_csr_idempotent_check is not changed
-
-- name: Validate CSR (data retrieval)
- assert:
- that:
- - generate_csr_check.csr is none
- - generate_csr.csr == lookup('file', output_dir ~ '/csr.csr', rstrip=False)
- - generate_csr.csr == generate_csr_idempotent.csr
- - generate_csr.csr == generate_csr_idempotent_check.csr
-
-- name: Validate CSR without SAN (check mode, idempotency)
- assert:
- that:
- - generate_csr_nosan_check is changed
- - generate_csr_nosan is changed
- - generate_csr_nosan_check_idempotent is not changed
- - generate_csr_nosan_check_idempotent_check is not changed
-
-- name: Validate CSR_KU_XKU (assert idempotency, change)
- assert:
- that:
- - csr_ku_xku is not changed
- - csr_ku_xku_change is changed
- - csr_ku_xku_change_2 is changed
-
-- name: Validate old_API CSR (test - Common Name)
- shell: "openssl req -noout -subject -in {{ output_dir }}/csr_oldapi.csr -nameopt oneline,-space_eq"
- register: csr_oldapi_cn
-
-- name: Validate old_API CSR (test - csr modulus)
- shell: 'openssl req -noout -modulus -in {{ output_dir }}/csr_oldapi.csr'
- register: csr_oldapi_modulus
-
-- name: Validate old_API CSR (assert)
- assert:
- that:
- - csr_oldapi_cn.stdout.split('=')[-1] == 'www.ansible.com'
- - csr_oldapi_modulus.stdout == privatekey_modulus.stdout
-
-- name: Validate invalid SAN
- assert:
- that:
- - generate_csr_invalid_san is failed
- - "'Subject Alternative Name' in generate_csr_invalid_san.msg"
-
-- name: Validate OCSP Must Staple CSR (test - everything)
- shell: "openssl req -noout -in {{ output_dir }}/csr_ocsp.csr -text"
- register: csr_ocsp
-
-- name: Validate OCSP Must Staple CSR (assert)
- assert:
- that:
- - "(csr_ocsp.stdout is search('\\s+TLS Feature:\\s*\\n\\s+status_request\\s+')) or
- (csr_ocsp.stdout is search('\\s+1.3.6.1.5.5.7.1.24:\\s*\\n\\s+0\\.\\.\\.\\.\\s+'))"
-
-- name: Validate OCSP Must Staple CSR (assert idempotency)
- assert:
- that:
- - csr_ocsp_idempotency is not changed
-
-- name: Validate ECC CSR (test - privatekey's public key)
- shell: 'openssl ec -pubout -in {{ output_dir }}/privatekey2.pem'
- register: privatekey_ecc_key
-
-- name: Validate ECC CSR (test - Common Name)
- shell: "openssl req -noout -subject -in {{ output_dir }}/csr2.csr -nameopt oneline,-space_eq"
- register: csr_ecc_cn
-
-- name: Validate ECC CSR (test - CSR pubkey)
- shell: 'openssl req -noout -pubkey -in {{ output_dir }}/csr2.csr'
- register: csr_ecc_pubkey
-
-- name: Validate ECC CSR (assert)
- assert:
- that:
- - csr_ecc_cn.stdout.split('=')[-1] == 'www.ansible.com'
- - csr_ecc_pubkey.stdout == privatekey_ecc_key.stdout
-
-- name: Validate CSR (text common name - Common Name)
- shell: "openssl req -noout -subject -in {{ output_dir }}/csr3.csr -nameopt oneline,-space_eq"
- register: csr3_cn
-
-- name: Validate CSR (assert)
- assert:
- that:
- - csr3_cn.stdout.split('=')[-1] == 'This is for Ansible'
-
-- name: Validate country name idempotency and validation
- assert:
- that:
- - country_idempotent_1 is changed
- - country_idempotent_2 is not changed
- - country_idempotent_3 is not changed
- - country_fail_4 is failed
-
-- name:
- assert:
- that:
- - passphrase_error_1 is failed
- - "'assphrase' in passphrase_error_1.msg or 'assword' in passphrase_error_1.msg"
- - passphrase_error_2 is failed
- - "'assphrase' in passphrase_error_2.msg or 'assword' in passphrase_error_2.msg or 'serializ' in passphrase_error_2.msg"
- - passphrase_error_3 is failed
- - "'assphrase' in passphrase_error_3.msg or 'assword' in passphrase_error_3.msg or 'serializ' in passphrase_error_3.msg"
-
-- name: Verify that broken CSR will be regenerated
- assert:
- that:
- - output_broken is changed
-
-- name: Verify that subject key identifier handling works
- assert:
- that:
- - subject_key_identifier_1 is changed
- - subject_key_identifier_2 is not changed
- - subject_key_identifier_3 is changed
- - subject_key_identifier_4 is changed
- - subject_key_identifier_5 is not changed
- - subject_key_identifier_6 is changed
- when: select_crypto_backend != 'pyopenssl'
-
-- name: Verify that authority key identifier handling works
- assert:
- that:
- - authority_key_identifier_1 is changed
- - authority_key_identifier_2 is not changed
- - authority_key_identifier_3 is changed
- - authority_key_identifier_4 is changed
- when: select_crypto_backend != 'pyopenssl'
-
-- name: Verify that authority cert issuer / serial number handling works
- assert:
- that:
- - authority_cert_issuer_sn_1 is changed
- - authority_cert_issuer_sn_2 is not changed
- - authority_cert_issuer_sn_3 is changed
- - authority_cert_issuer_sn_4 is changed
- - authority_cert_issuer_sn_5 is changed
- when: select_crypto_backend != 'pyopenssl'
-
-- name: Check backup
- assert:
- that:
- - csr_backup_1 is changed
- - csr_backup_1.backup_file is undefined
- - csr_backup_2 is not changed
- - csr_backup_2.backup_file is undefined
- - csr_backup_3 is changed
- - csr_backup_3.backup_file is string
- - csr_backup_4 is changed
- - csr_backup_4.backup_file is string
- - csr_backup_5 is not changed
- - csr_backup_5.backup_file is undefined
- - csr_backup_4.csr is none
-
-- name: Check CSR with everything
- assert:
- that:
- - everything_1 is changed
- - everything_2 is not changed
- - everything_3 is not changed
-
-- name: Verify Ed25519 and Ed448 tests (for cryptography >= 2.6, < 2.8)
- assert:
- that:
- - generate_csr_ed25519_ed448.results[0] is failed
- - generate_csr_ed25519_ed448.results[1] is failed
- - generate_csr_ed25519_ed448.results[0].msg == 'Signing with Ed25519 and Ed448 keys requires cryptography 2.8 or newer.'
- - generate_csr_ed25519_ed448.results[1].msg == 'Signing with Ed25519 and Ed448 keys requires cryptography 2.8 or newer.'
- - generate_csr_ed25519_ed448_idempotent.results[0] is failed
- - generate_csr_ed25519_ed448_idempotent.results[1] is failed
- when: select_crypto_backend == 'cryptography' and cryptography_version.stdout is version('2.6', '>=') and cryptography_version.stdout is version('2.8', '<') and generate_csr_ed25519_ed448_privatekey is not failed
-
-- name: Verify Ed25519 and Ed448 tests (for cryptography >= 2.8)
- assert:
- that:
- - generate_csr_ed25519_ed448 is succeeded
- - generate_csr_ed25519_ed448.results[0] is changed
- - generate_csr_ed25519_ed448.results[1] is changed
- - generate_csr_ed25519_ed448_idempotent is succeeded
- - generate_csr_ed25519_ed448_idempotent.results[0] is not changed
- - generate_csr_ed25519_ed448_idempotent.results[1] is not changed
- when: select_crypto_backend == 'cryptography' and cryptography_version.stdout is version('2.8', '>=') and generate_csr_ed25519_ed448_privatekey is not failed
diff --git a/test/integration/targets/openssl_csr_info/aliases b/test/integration/targets/openssl_csr_info/aliases
deleted file mode 100644
index 0b484bbab6..0000000000
--- a/test/integration/targets/openssl_csr_info/aliases
+++ /dev/null
@@ -1,3 +0,0 @@
-shippable/posix/group1
-destructive
-skip/aix
diff --git a/test/integration/targets/openssl_csr_info/meta/main.yml b/test/integration/targets/openssl_csr_info/meta/main.yml
deleted file mode 100644
index 800aff6428..0000000000
--- a/test/integration/targets/openssl_csr_info/meta/main.yml
+++ /dev/null
@@ -1,2 +0,0 @@
-dependencies:
- - setup_openssl
diff --git a/test/integration/targets/openssl_csr_info/tasks/impl.yml b/test/integration/targets/openssl_csr_info/tasks/impl.yml
deleted file mode 100644
index 2ab8b87bfd..0000000000
--- a/test/integration/targets/openssl_csr_info/tasks/impl.yml
+++ /dev/null
@@ -1,94 +0,0 @@
----
-- debug:
- msg: "Executing tests with backend {{ select_crypto_backend }}"
-
-- name: ({{select_crypto_backend}}) Get CSR info
- openssl_csr_info:
- path: '{{ output_dir }}/csr_1.csr'
- select_crypto_backend: '{{ select_crypto_backend }}'
- register: result
-
-- name: Check whether subject behaves as expected
- assert:
- that:
- - result.subject.organizationalUnitName == 'ACME Department'
- - "['organizationalUnitName', 'Crypto Department'] in result.subject_ordered"
- - "['organizationalUnitName', 'ACME Department'] in result.subject_ordered"
-
-- name: Check SubjectKeyIdentifier and AuthorityKeyIdentifier
- assert:
- that:
- - result.subject_key_identifier == "00:11:22:33"
- - result.authority_key_identifier == "44:55:66:77"
- - result.authority_cert_issuer == expected_authority_cert_issuer
- - result.authority_cert_serial_number == 12345
- vars:
- expected_authority_cert_issuer:
- - "DNS:ca.example.org"
- - "IP:1.2.3.4"
- when: select_crypto_backend != 'pyopenssl' and cryptography_version.stdout is version('1.3', '>=')
-
-- name: Update result list
- set_fact:
- info_results: "{{ info_results + [result] }}"
-
-- name: ({{select_crypto_backend}}) Get CSR info directly
- openssl_csr_info:
- content: '{{ lookup("file", output_dir ~ "/csr_1.csr") }}'
- select_crypto_backend: '{{ select_crypto_backend }}'
- register: result_direct
-
-- name: ({{select_crypto_backend}}) Compare output of direct and loaded info
- assert:
- that:
- - result == result_direct
-
-- name: ({{select_crypto_backend}}) Get CSR info
- openssl_csr_info:
- path: '{{ output_dir }}/csr_2.csr'
- select_crypto_backend: '{{ select_crypto_backend }}'
- register: result
-
-- name: Update result list
- set_fact:
- info_results: "{{ info_results + [result] }}"
-
-- name: ({{select_crypto_backend}}) Get CSR info
- openssl_csr_info:
- path: '{{ output_dir }}/csr_3.csr'
- select_crypto_backend: '{{ select_crypto_backend }}'
- register: result
-
-- name: Check AuthorityKeyIdentifier
- assert:
- that:
- - result.authority_key_identifier is none
- - result.authority_cert_issuer == expected_authority_cert_issuer
- - result.authority_cert_serial_number == 12345
- vars:
- expected_authority_cert_issuer:
- - "DNS:ca.example.org"
- - "IP:1.2.3.4"
- when: select_crypto_backend != 'pyopenssl' and cryptography_version.stdout is version('1.3', '>=')
-
-- name: Update result list
- set_fact:
- info_results: "{{ info_results + [result] }}"
-
-- name: ({{select_crypto_backend}}) Get CSR info
- openssl_csr_info:
- path: '{{ output_dir }}/csr_4.csr'
- select_crypto_backend: '{{ select_crypto_backend }}'
- register: result
-
-- name: Check AuthorityKeyIdentifier
- assert:
- that:
- - result.authority_key_identifier == "44:55:66:77"
- - result.authority_cert_issuer is none
- - result.authority_cert_serial_number is none
- when: select_crypto_backend != 'pyopenssl' and cryptography_version.stdout is version('1.3', '>=')
-
-- name: Update result list
- set_fact:
- info_results: "{{ info_results + [result] }}"
diff --git a/test/integration/targets/openssl_csr_info/tasks/main.yml b/test/integration/targets/openssl_csr_info/tasks/main.yml
deleted file mode 100644
index e1794ad478..0000000000
--- a/test/integration/targets/openssl_csr_info/tasks/main.yml
+++ /dev/null
@@ -1,161 +0,0 @@
----
-- name: Generate privatekey
- openssl_privatekey:
- path: '{{ output_dir }}/privatekey.pem'
-
-- name: Generate privatekey with password
- openssl_privatekey:
- path: '{{ output_dir }}/privatekeypw.pem'
- passphrase: hunter2
- cipher: auto
- select_crypto_backend: cryptography
-
-- name: Generate CSR 1
- openssl_csr:
- path: '{{ output_dir }}/csr_1.csr'
- privatekey_path: '{{ output_dir }}/privatekey.pem'
- subject:
- commonName: www.example.com
- C: de
- L: Somewhere
- ST: Zurich
- streetAddress: Welcome Street
- O: Ansible
- organizationalUnitName:
- - Crypto Department
- - ACME Department
- serialNumber: "1234"
- SN: Last Name
- GN: First Name
- title: Chief
- pseudonym: test
- UID: asdf
- emailAddress: test@example.com
- postalAddress: 1234 Somewhere
- postalCode: "1234"
- useCommonNameForSAN: no
- key_usage:
- - digitalSignature
- - keyAgreement
- - Non Repudiation
- - Key Encipherment
- - dataEncipherment
- - Certificate Sign
- - cRLSign
- - Encipher Only
- - decipherOnly
- key_usage_critical: yes
- extended_key_usage:
- - 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
- 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"
- basic_constraints:
- - "CA:TRUE"
- - "pathlen:23"
- basic_constraints_critical: yes
- ocsp_must_staple: yes
- subject_key_identifier: '{{ "00:11:22:33" 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 }}'
- authority_cert_issuer: '{{ value_for_authority_cert_issuer if cryptography_version.stdout is version("1.3", ">=") else omit }}'
- authority_cert_serial_number: '{{ 12345 if cryptography_version.stdout is version("1.3", ">=") else omit }}'
- vars:
- value_for_authority_cert_issuer:
- - "DNS:ca.example.org"
- - "IP:1.2.3.4"
-
-- name: Generate CSR 2
- openssl_csr:
- path: '{{ output_dir }}/csr_2.csr'
- privatekey_path: '{{ output_dir }}/privatekeypw.pem'
- privatekey_passphrase: hunter2
- useCommonNameForSAN: no
- basic_constraints:
- - "CA:TRUE"
-
-- name: Generate CSR 3
- openssl_csr:
- path: '{{ output_dir }}/csr_3.csr'
- privatekey_path: '{{ output_dir }}/privatekey.pem'
- useCommonNameForSAN: no
- subject_alt_name:
- - "DNS:*.ansible.com"
- - "DNS:*.example.org"
- - "IP:DEAD:BEEF::1"
- basic_constraints:
- - "CA:FALSE"
- authority_cert_issuer: '{{ value_for_authority_cert_issuer if cryptography_version.stdout is version("1.3", ">=") else omit }}'
- authority_cert_serial_number: '{{ 12345 if cryptography_version.stdout is version("1.3", ">=") else omit }}'
- vars:
- value_for_authority_cert_issuer:
- - "DNS:ca.example.org"
- - "IP:1.2.3.4"
-
-- name: Generate CSR 4
- openssl_csr:
- path: '{{ output_dir }}/csr_4.csr'
- privatekey_path: '{{ output_dir }}/privatekey.pem'
- useCommonNameForSAN: no
- 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
- include_tasks: impl.yml
- vars:
- select_crypto_backend: cryptography
- 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
diff --git a/test/integration/targets/openssl_csr_info/test_plugins/jinja_compatibility.py b/test/integration/targets/openssl_csr_info/test_plugins/jinja_compatibility.py
deleted file mode 100644
index fc2b5f0fcb..0000000000
--- a/test/integration/targets/openssl_csr_info/test_plugins/jinja_compatibility.py
+++ /dev/null
@@ -1,15 +0,0 @@
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
-
-
-def compatibility_in_test(a, b):
- return a in b
-
-
-class TestModule:
- ''' Ansible math jinja2 tests '''
-
- def tests(self):
- return {
- 'in': compatibility_in_test,
- }
diff --git a/test/integration/targets/openssl_dhparam/aliases b/test/integration/targets/openssl_dhparam/aliases
deleted file mode 100644
index 0b484bbab6..0000000000
--- a/test/integration/targets/openssl_dhparam/aliases
+++ /dev/null
@@ -1,3 +0,0 @@
-shippable/posix/group1
-destructive
-skip/aix
diff --git a/test/integration/targets/openssl_dhparam/meta/main.yml b/test/integration/targets/openssl_dhparam/meta/main.yml
deleted file mode 100644
index 800aff6428..0000000000
--- a/test/integration/targets/openssl_dhparam/meta/main.yml
+++ /dev/null
@@ -1,2 +0,0 @@
-dependencies:
- - setup_openssl
diff --git a/test/integration/targets/openssl_dhparam/tasks/impl.yml b/test/integration/targets/openssl_dhparam/tasks/impl.yml
deleted file mode 100644
index d367436de4..0000000000
--- a/test/integration/targets/openssl_dhparam/tasks/impl.yml
+++ /dev/null
@@ -1,101 +0,0 @@
----
-# The tests for this module generate unsafe parameters for testing purposes;
-# otherwise tests would be too slow. Use sizes of at least 2048 in production!
-- name: "[{{ select_crypto_backend }}] Generate parameter"
- openssl_dhparam:
- size: 768
- path: '{{ output_dir }}/dh768.pem'
- select_crypto_backend: "{{ select_crypto_backend }}"
- return_content: yes
- register: dhparam
-
-- name: "[{{ select_crypto_backend }}] Don't regenerate parameters with no change"
- openssl_dhparam:
- size: 768
- path: '{{ output_dir }}/dh768.pem'
- select_crypto_backend: "{{ select_crypto_backend }}"
- return_content: yes
- register: dhparam_changed
-
-- name: "[{{ select_crypto_backend }}] Generate parameters with size option"
- openssl_dhparam:
- path: '{{ output_dir }}/dh512.pem'
- size: 512
- select_crypto_backend: "{{ select_crypto_backend }}"
-
-- name: "[{{ select_crypto_backend }}] Don't regenerate parameters with size option and no change"
- openssl_dhparam:
- path: '{{ output_dir }}/dh512.pem'
- size: 512
- select_crypto_backend: "{{ select_crypto_backend }}"
- register: dhparam_changed_512
-
-- copy:
- src: '{{ output_dir }}/dh768.pem'
- remote_src: yes
- dest: '{{ output_dir }}/dh512.pem'
-
-- name: "[{{ select_crypto_backend }}] Re-generate if size is different"
- openssl_dhparam:
- path: '{{ output_dir }}/dh512.pem'
- size: 512
- select_crypto_backend: "{{ select_crypto_backend }}"
- register: dhparam_changed_to_512
-
-- name: "[{{ select_crypto_backend }}] Force re-generate parameters with size option"
- openssl_dhparam:
- path: '{{ output_dir }}/dh512.pem'
- size: 512
- force: yes
- select_crypto_backend: "{{ select_crypto_backend }}"
- register: dhparam_changed_force
-
-- name: "[{{ select_crypto_backend }}] Create broken params"
- copy:
- dest: "{{ output_dir }}/dhbroken.pem"
- content: "broken"
-- name: "[{{ select_crypto_backend }}] Regenerate broken params"
- openssl_dhparam:
- path: '{{ output_dir }}/dhbroken.pem'
- size: 512
- force: yes
- select_crypto_backend: "{{ select_crypto_backend }}"
- register: output_broken
-
-- name: "[{{ select_crypto_backend }}] Generate params"
- openssl_dhparam:
- path: '{{ output_dir }}/dh_backup.pem'
- size: 512
- backup: yes
- select_crypto_backend: "{{ select_crypto_backend }}"
- register: dhparam_backup_1
-- name: "[{{ select_crypto_backend }}] Generate params (idempotent)"
- openssl_dhparam:
- path: '{{ output_dir }}/dh_backup.pem'
- size: 512
- backup: yes
- select_crypto_backend: "{{ select_crypto_backend }}"
- register: dhparam_backup_2
-- name: "[{{ select_crypto_backend }}] Generate params (change)"
- openssl_dhparam:
- path: '{{ output_dir }}/dh_backup.pem'
- size: 512
- force: yes
- backup: yes
- select_crypto_backend: "{{ select_crypto_backend }}"
- register: dhparam_backup_3
-- name: "[{{ select_crypto_backend }}] Generate params (remove)"
- openssl_dhparam:
- path: '{{ output_dir }}/dh_backup.pem'
- state: absent
- backup: yes
- select_crypto_backend: "{{ select_crypto_backend }}"
- return_content: yes
- register: dhparam_backup_4
-- name: "[{{ select_crypto_backend }}] Generate params (remove, idempotent)"
- openssl_dhparam:
- path: '{{ output_dir }}/dh_backup.pem'
- state: absent
- backup: yes
- select_crypto_backend: "{{ select_crypto_backend }}"
- register: dhparam_backup_5
diff --git a/test/integration/targets/openssl_dhparam/tasks/main.yml b/test/integration/targets/openssl_dhparam/tasks/main.yml
deleted file mode 100644
index d8c106b3a2..0000000000
--- a/test/integration/targets/openssl_dhparam/tasks/main.yml
+++ /dev/null
@@ -1,38 +0,0 @@
----
-# The tests for this module generate unsafe parameters for testing purposes;
-# otherwise tests would be too slow. Use sizes of at least 2048 in production!
-
-- name: Run module with backend autodetection
- openssl_dhparam:
- path: '{{ output_dir }}/dh_backend_selection.pem'
- size: 512
-
-- block:
- - name: Running tests with OpenSSL backend
- include_tasks: impl.yml
-
- - include_tasks: ../tests/validate.yml
-
- vars:
- select_crypto_backend: openssl
- # when: openssl_version.stdout is version('1.0.0', '>=')
-
-- name: Remove output directory
- file:
- path: "{{ output_dir }}"
- state: absent
-
-- name: Re-create output directory
- file:
- path: "{{ output_dir }}"
- state: directory
-
-- block:
- - name: Running tests with cryptography backend
- include_tasks: impl.yml
-
- - include_tasks: ../tests/validate.yml
-
- vars:
- select_crypto_backend: cryptography
- when: cryptography_version.stdout is version('2.0', '>=')
diff --git a/test/integration/targets/openssl_dhparam/tests/validate.yml b/test/integration/targets/openssl_dhparam/tests/validate.yml
deleted file mode 100644
index e7d3951a23..0000000000
--- a/test/integration/targets/openssl_dhparam/tests/validate.yml
+++ /dev/null
@@ -1,58 +0,0 @@
----
-- name: "[{{ select_crypto_backend }}] Validate generated params"
- shell: 'openssl dhparam -in {{ output_dir }}/{{ item }}.pem -noout -check'
- with_items:
- - dh768
- - dh512
-
-- name: "[{{ select_crypto_backend }}] Get bit size of 768"
- shell: 'openssl dhparam -noout -in {{ output_dir }}/dh768.pem -text | head -n1 | sed -ne "s@.*(\\([[:digit:]]\{1,\}\\) bit).*@\\1@p"'
- register: bit_size_dhparam
-
-- name: "[{{ select_crypto_backend }}] Check bit size of default"
- assert:
- that:
- - bit_size_dhparam.stdout == "768"
-
-- name: "[{{ select_crypto_backend }}] Get bit size of 512"
- shell: 'openssl dhparam -noout -in {{ output_dir }}/dh512.pem -text | head -n1 | sed -ne "s@.*(\\([[:digit:]]\{1,\}\\) bit).*@\\1@p"'
- register: bit_size_dhparam_512
-
-- name: "[{{ select_crypto_backend }}] Check bit size of default"
- assert:
- that:
- - bit_size_dhparam_512.stdout == "512"
-
-- name: "[{{ select_crypto_backend }}] Check if changed works correctly"
- assert:
- that:
- - dhparam_changed is not changed
- - dhparam_changed_512 is not changed
- - dhparam_changed_to_512 is changed
- - dhparam_changed_force is changed
-
-- name: "[{{ select_crypto_backend }}] Make sure correct values are returned"
- assert:
- that:
- - dhparam.dhparams == lookup('file', output_dir ~ '/dh768.pem', rstrip=False)
- - dhparam.dhparams == dhparam_changed.dhparams
-
-- name: "[{{ select_crypto_backend }}] Verify that broken params will be regenerated"
- assert:
- that:
- - output_broken is changed
-
-- name: "[{{ select_crypto_backend }}] Check backup"
- assert:
- that:
- - dhparam_backup_1 is changed
- - dhparam_backup_1.backup_file is undefined
- - dhparam_backup_2 is not changed
- - dhparam_backup_2.backup_file is undefined
- - dhparam_backup_3 is changed
- - dhparam_backup_3.backup_file is string
- - dhparam_backup_4 is changed
- - dhparam_backup_4.backup_file is string
- - dhparam_backup_5 is not changed
- - dhparam_backup_5.backup_file is undefined
- - dhparam_backup_4.dhparams is none
diff --git a/test/integration/targets/openssl_pkcs12/aliases b/test/integration/targets/openssl_pkcs12/aliases
deleted file mode 100644
index 80cc8d7d79..0000000000
--- a/test/integration/targets/openssl_pkcs12/aliases
+++ /dev/null
@@ -1,4 +0,0 @@
-destructive
-needs/root
-shippable/posix/group1
-skip/aix
diff --git a/test/integration/targets/openssl_pkcs12/meta/main.yml b/test/integration/targets/openssl_pkcs12/meta/main.yml
deleted file mode 100644
index 800aff6428..0000000000
--- a/test/integration/targets/openssl_pkcs12/meta/main.yml
+++ /dev/null
@@ -1,2 +0,0 @@
-dependencies:
- - setup_openssl
diff --git a/test/integration/targets/openssl_pkcs12/tasks/impl.yml b/test/integration/targets/openssl_pkcs12/tasks/impl.yml
deleted file mode 100644
index cffa4a1c93..0000000000
--- a/test/integration/targets/openssl_pkcs12/tasks/impl.yml
+++ /dev/null
@@ -1,254 +0,0 @@
----
-- block:
- - name: 'Generate privatekey'
- openssl_privatekey:
- path: "{{ output_dir }}/ansible_pkey.pem"
-
- - name: 'Generate privatekey2'
- openssl_privatekey:
- path: "{{ output_dir }}/ansible_pkey2.pem"
-
- - name: 'Generate privatekey3'
- openssl_privatekey:
- path: "{{ output_dir }}/ansible_pkey3.pem"
-
- - name: 'Generate CSR'
- openssl_csr:
- path: "{{ output_dir }}/ansible.csr"
- privatekey_path: "{{ output_dir }}/ansible_pkey.pem"
- commonName: 'www.ansible.com'
-
- - name: 'Generate CSR 2'
- openssl_csr:
- path: "{{ output_dir }}/ansible2.csr"
- privatekey_path: "{{ output_dir }}/ansible_pkey2.pem"
- commonName: 'www2.ansible.com'
-
- - name: 'Generate CSR 3'
- openssl_csr:
- path: "{{ output_dir }}/ansible3.csr"
- privatekey_path: "{{ output_dir }}/ansible_pkey3.pem"
- commonName: 'www3.ansible.com'
-
- - name: 'Generate certificate'
- openssl_certificate:
- path: "{{ output_dir }}/{{ item.name }}.crt"
- privatekey_path: "{{ output_dir }}/{{ item.pkey }}"
- csr_path: "{{ output_dir }}/{{ item.name }}.csr"
- provider: selfsigned
- loop:
- - name: ansible
- pkey: ansible_pkey.pem
- - name: ansible2
- pkey: ansible_pkey2.pem
- - name: ansible3
- pkey: ansible_pkey3.pem
-
- - name: 'Generate PKCS#12 file'
- openssl_pkcs12:
- path: "{{ output_dir }}/ansible.p12"
- friendly_name: 'abracadabra'
- privatekey_path: "{{ output_dir }}/ansible_pkey.pem"
- certificate_path: "{{ output_dir }}/ansible.crt"
- state: present
- return_content: yes
- register: p12_standard
-
- - name: 'Generate PKCS#12 file again, idempotency'
- openssl_pkcs12:
- path: "{{ output_dir }}/ansible.p12"
- friendly_name: 'abracadabra'
- privatekey_path: "{{ output_dir }}/ansible_pkey.pem"
- certificate_path: "{{ output_dir }}/ansible.crt"
- state: present
- return_content: yes
- register: p12_standard_idempotency
-
- - name: Read ansible.p12
- slurp:
- src: "{{ output_dir }}/ansible.p12"
- register: ansible_p12_content
-
- - name: 'Validate PKCS#12'
- assert:
- that:
- - p12_standard.pkcs12 == ansible_p12_content.content
- - p12_standard_idempotency.pkcs12 == p12_standard.pkcs12
-
- - name: 'Generate PKCS#12 file (force)'
- openssl_pkcs12:
- path: "{{ output_dir }}/ansible.p12"
- friendly_name: 'abracadabra'
- privatekey_path: "{{ output_dir }}/ansible_pkey.pem"
- certificate_path: "{{ output_dir }}/ansible.crt"
- state: present
- force: True
- register: p12_force
-
- - name: 'Generate PKCS#12 file (force + change mode)'
- openssl_pkcs12:
- path: "{{ output_dir }}/ansible.p12"
- friendly_name: 'abracadabra'
- privatekey_path: "{{ output_dir }}/ansible_pkey.pem"
- certificate_path: "{{ output_dir }}/ansible.crt"
- state: present
- force: True
- mode: 0644
- register: p12_force_and_mode
-
- - name: 'Dump PKCS#12'
- openssl_pkcs12:
- src: "{{ output_dir }}/ansible.p12"
- path: "{{ output_dir }}/ansible_parse.pem"
- action: 'parse'
- state: 'present'
-
- - name: 'Generate PKCS#12 file with multiple certs'
- openssl_pkcs12:
- path: "{{ output_dir }}/ansible_multi_certs.p12"
- friendly_name: 'abracadabra'
- privatekey_path: "{{ output_dir }}/ansible_pkey.pem"
- certificate_path: "{{ output_dir }}/ansible.crt"
- ca_certificates:
- - "{{ output_dir }}/ansible2.crt"
- - "{{ output_dir }}/ansible3.crt"
- state: present
- register: p12_multiple_certs
-
- - name: 'Generate PKCS#12 file with multiple certs, again (idempotency)'
- openssl_pkcs12:
- path: "{{ output_dir }}/ansible_multi_certs.p12"
- friendly_name: 'abracadabra'
- privatekey_path: "{{ output_dir }}/ansible_pkey.pem"
- certificate_path: "{{ output_dir }}/ansible.crt"
- ca_certificates:
- - "{{ output_dir }}/ansible2.crt"
- - "{{ output_dir }}/ansible3.crt"
- state: present
- register: p12_multiple_certs_idempotency
-
- - name: 'Dump PKCS#12 with multiple certs'
- openssl_pkcs12:
- src: "{{ output_dir }}/ansible_multi_certs.p12"
- path: "{{ output_dir }}/ansible_parse_multi_certs.pem"
- action: 'parse'
- state: 'present'
-
- - name: Generate privatekey with password
- openssl_privatekey:
- path: '{{ output_dir }}/privatekeypw.pem'
- passphrase: hunter2
- cipher: auto
- select_crypto_backend: cryptography
-
- - name: 'Generate PKCS#12 file (password fail 1)'
- openssl_pkcs12:
- path: "{{ output_dir }}/ansible_pw1.p12"
- friendly_name: 'abracadabra'
- privatekey_path: "{{ output_dir }}/ansible_pkey.pem"
- privatekey_passphrase: hunter2
- certificate_path: "{{ output_dir }}/ansible.crt"
- state: present
- ignore_errors: yes
- register: passphrase_error_1
-
- - name: 'Generate PKCS#12 file (password fail 2)'
- openssl_pkcs12:
- path: "{{ output_dir }}/ansible_pw2.p12"
- friendly_name: 'abracadabra'
- privatekey_path: '{{ output_dir }}/privatekeypw.pem'
- privatekey_passphrase: wrong_password
- certificate_path: "{{ output_dir }}/ansible.crt"
- state: present
- ignore_errors: yes
- register: passphrase_error_2
-
- - name: 'Generate PKCS#12 file (password fail 3)'
- openssl_pkcs12:
- path: "{{ output_dir }}/ansible_pw3.p12"
- friendly_name: 'abracadabra'
- privatekey_path: '{{ output_dir }}/privatekeypw.pem'
- certificate_path: "{{ output_dir }}/ansible.crt"
- state: present
- ignore_errors: yes
- register: passphrase_error_3
-
- - name: 'Generate PKCS#12 file, no privatekey'
- openssl_pkcs12:
- path: "{{ output_dir }}/ansible_no_pkey.p12"
- friendly_name: 'abracadabra'
- certificate_path: "{{ output_dir }}/ansible.crt"
- state: present
- register: p12_no_pkey
-
- - name: 'Create broken PKCS#12'
- copy:
- dest: "{{ output_dir }}/broken.p12"
- content: "broken"
- - name: 'Regenerate broken PKCS#12'
- openssl_pkcs12:
- path: "{{ output_dir }}/broken.p12"
- friendly_name: 'abracadabra'
- privatekey_path: "{{ output_dir }}/ansible_pkey.pem"
- certificate_path: "{{ output_dir }}/ansible.crt"
- state: present
- force: True
- mode: 0644
- register: output_broken
-
- - name: 'Generate PKCS#12 file'
- openssl_pkcs12:
- path: "{{ output_dir }}/ansible_backup.p12"
- friendly_name: 'abracadabra'
- privatekey_path: "{{ output_dir }}/ansible_pkey.pem"
- certificate_path: "{{ output_dir }}/ansible.crt"
- state: present
- backup: yes
- register: p12_backup_1
- - name: 'Generate PKCS#12 file (idempotent)'
- openssl_pkcs12:
- path: "{{ output_dir }}/ansible_backup.p12"
- friendly_name: 'abracadabra'
- privatekey_path: "{{ output_dir }}/ansible_pkey.pem"
- certificate_path: "{{ output_dir }}/ansible.crt"
- state: present
- backup: yes
- register: p12_backup_2
- - name: 'Generate PKCS#12 file (change)'
- openssl_pkcs12:
- path: "{{ output_dir }}/ansible_backup.p12"
- friendly_name: 'abra'
- privatekey_path: "{{ output_dir }}/ansible_pkey.pem"
- certificate_path: "{{ output_dir }}/ansible.crt"
- state: present
- force: yes # FIXME: idempotency does not work, so we have to force! (https://github.com/ansible/ansible/issues/53221)
- backup: yes
- register: p12_backup_3
- - name: 'Generate PKCS#12 file (remove)'
- openssl_pkcs12:
- path: "{{ output_dir }}/ansible_backup.p12"
- state: absent
- backup: yes
- return_content: yes
- register: p12_backup_4
- - name: 'Generate PKCS#12 file (remove, idempotent)'
- openssl_pkcs12:
- path: "{{ output_dir }}/ansible_backup.p12"
- state: absent
- backup: yes
- register: p12_backup_5
-
- - import_tasks: ../tests/validate.yml
-
- always:
- - name: 'Delete PKCS#12 file'
- openssl_pkcs12:
- state: absent
- path: '{{ output_dir }}/{{ item }}.p12'
- loop:
- - 'ansible'
- - 'ansible_no_pkey'
- - 'ansible_multi_certs'
- - 'ansible_pw1'
- - 'ansible_pw2'
- - 'ansible_pw3'
diff --git a/test/integration/targets/openssl_pkcs12/tasks/main.yml b/test/integration/targets/openssl_pkcs12/tasks/main.yml
deleted file mode 100644
index a11ba75a43..0000000000
--- a/test/integration/targets/openssl_pkcs12/tasks/main.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-- name: Run tests
- include_tasks: impl.yml
- when: pyopenssl_version.stdout is version('17.1.0', '>=')
diff --git a/test/integration/targets/openssl_pkcs12/tests/validate.yml b/test/integration/targets/openssl_pkcs12/tests/validate.yml
deleted file mode 100644
index baff2282c6..0000000000
--- a/test/integration/targets/openssl_pkcs12/tests/validate.yml
+++ /dev/null
@@ -1,57 +0,0 @@
----
-- name: 'Validate PKCS#12'
- command: "openssl pkcs12 -info -in {{ output_dir }}/ansible.p12 -nodes -passin pass:''"
- register: p12
-
-- name: 'Validate PKCS#12 with no private key'
- command: "openssl pkcs12 -info -in {{ output_dir }}/ansible_no_pkey.p12 -nodes -passin pass:''"
- register: p12_validate_no_pkey
-
-- name: 'Validate PKCS#12 with multiple certs'
- shell: "openssl pkcs12 -info -in {{ output_dir }}/ansible_multi_certs.p12 -nodes -passin pass:'' | grep subject"
- register: p12_validate_multi_certs
-
-- name: 'Validate PKCS#12 (assert)'
- assert:
- that:
- - p12.stdout_lines[2].split(':')[-1].strip() == 'abracadabra'
- - p12_standard.mode == '0400'
- - p12_no_pkey.changed
- - p12_validate_no_pkey.stdout_lines[-1] == '-----END CERTIFICATE-----'
- - p12_force.changed
- - p12_force_and_mode.mode == '0644' and p12_force_and_mode.changed
- - not p12_standard_idempotency.changed
- - not p12_multiple_certs_idempotency.changed
- - "'www.' in p12_validate_multi_certs.stdout"
- - "'www2.' in p12_validate_multi_certs.stdout"
- - "'www3.' in p12_validate_multi_certs.stdout"
-
-- name: Check passphrase on private key
- assert:
- that:
- - passphrase_error_1 is failed
- - "'assphrase' in passphrase_error_1.msg or 'assword' in passphrase_error_1.msg"
- - passphrase_error_2 is failed
- - "'assphrase' in passphrase_error_2.msg or 'assword' in passphrase_error_2.msg or 'serializ' in passphrase_error_2.msg"
- - passphrase_error_3 is failed
- - "'assphrase' in passphrase_error_3.msg or 'assword' in passphrase_error_3.msg or 'serializ' in passphrase_error_3.msg"
-
-- name: "Verify that broken PKCS#12 will be regenerated"
- assert:
- that:
- - output_broken is changed
-
-- name: Check backup
- assert:
- that:
- - p12_backup_1 is changed
- - p12_backup_1.backup_file is undefined
- - p12_backup_2 is not changed
- - p12_backup_2.backup_file is undefined
- - p12_backup_3 is changed
- - p12_backup_3.backup_file is string
- - p12_backup_4 is changed
- - p12_backup_4.backup_file is string
- - p12_backup_5 is not changed
- - p12_backup_5.backup_file is undefined
- - p12_backup_4.pkcs12 is none
diff --git a/test/integration/targets/openssl_privatekey/aliases b/test/integration/targets/openssl_privatekey/aliases
deleted file mode 100644
index 0b484bbab6..0000000000
--- a/test/integration/targets/openssl_privatekey/aliases
+++ /dev/null
@@ -1,3 +0,0 @@
-shippable/posix/group1
-destructive
-skip/aix
diff --git a/test/integration/targets/openssl_privatekey/meta/main.yml b/test/integration/targets/openssl_privatekey/meta/main.yml
deleted file mode 100644
index 800aff6428..0000000000
--- a/test/integration/targets/openssl_privatekey/meta/main.yml
+++ /dev/null
@@ -1,2 +0,0 @@
-dependencies:
- - setup_openssl
diff --git a/test/integration/targets/openssl_privatekey/tasks/impl.yml b/test/integration/targets/openssl_privatekey/tasks/impl.yml
deleted file mode 100644
index a1e501cd07..0000000000
--- a/test/integration/targets/openssl_privatekey/tasks/impl.yml
+++ /dev/null
@@ -1,820 +0,0 @@
----
-- name: Generate privatekey1 - standard
- openssl_privatekey:
- path: '{{ output_dir }}/privatekey1.pem'
- select_crypto_backend: '{{ select_crypto_backend }}'
- return_content: yes
- register: privatekey1
-
-- name: Generate privatekey1 - standard (idempotence)
- openssl_privatekey:
- path: '{{ output_dir }}/privatekey1.pem'
- select_crypto_backend: '{{ select_crypto_backend }}'
- return_content: yes
- register: privatekey1_idempotence
-
-- name: Generate privatekey2 - size 2048
- openssl_privatekey:
- path: '{{ output_dir }}/privatekey2.pem'
- size: 2048
- select_crypto_backend: '{{ select_crypto_backend }}'
-
-- name: Generate privatekey3 - type DSA
- openssl_privatekey:
- path: '{{ output_dir }}/privatekey3.pem'
- type: DSA
- size: 3072
- select_crypto_backend: '{{ select_crypto_backend }}'
-
-- name: Generate privatekey4 - standard
- openssl_privatekey:
- path: '{{ output_dir }}/privatekey4.pem'
- select_crypto_backend: '{{ select_crypto_backend }}'
-
-- name: Delete privatekey4 - standard
- openssl_privatekey:
- state: absent
- path: '{{ output_dir }}/privatekey4.pem'
- select_crypto_backend: '{{ select_crypto_backend }}'
- return_content: yes
- register: privatekey4_delete
-
-- name: Delete privatekey4 - standard (idempotence)
- openssl_privatekey:
- state: absent
- path: '{{ output_dir }}/privatekey4.pem'
- select_crypto_backend: '{{ select_crypto_backend }}'
- register: privatekey4_delete_idempotence
-
-- name: Generate privatekey5 - standard - with passphrase
- openssl_privatekey:
- path: '{{ output_dir }}/privatekey5.pem'
- passphrase: ansible
- cipher: "{{ 'aes256' if select_crypto_backend == 'pyopenssl' else 'auto' }}"
- select_crypto_backend: '{{ select_crypto_backend }}'
-
-- name: Generate privatekey5 - standard - idempotence
- openssl_privatekey:
- path: '{{ output_dir }}/privatekey5.pem'
- passphrase: ansible
- cipher: "{{ 'aes256' if select_crypto_backend == 'pyopenssl' else 'auto' }}"
- select_crypto_backend: '{{ select_crypto_backend }}'
- register: privatekey5_idempotence
-
-- name: Generate privatekey6 - standard - with non-ASCII passphrase
- openssl_privatekey:
- path: '{{ output_dir }}/privatekey6.pem'
- passphrase: ànsïblé
- cipher: "{{ 'aes256' if select_crypto_backend == 'pyopenssl' else 'auto' }}"
- select_crypto_backend: '{{ select_crypto_backend }}'
-
-- set_fact:
- ecc_types: []
- when: select_crypto_backend == 'pyopenssl'
-- set_fact:
- ecc_types:
- - curve: secp384r1
- openssl_name: secp384r1
- min_cryptography_version: "0.5"
- - curve: secp521r1
- openssl_name: secp521r1
- min_cryptography_version: "0.5"
- - curve: secp224r1
- openssl_name: secp224r1
- min_cryptography_version: "0.5"
- - curve: secp192r1
- openssl_name: prime192v1
- min_cryptography_version: "0.5"
- - curve: secp256r1
- openssl_name: secp256r1
- min_cryptography_version: "0.5"
- - curve: secp256k1
- openssl_name: secp256k1
- min_cryptography_version: "0.9"
- - curve: brainpoolP256r1
- openssl_name: brainpoolP256r1
- min_cryptography_version: "2.2"
- - curve: brainpoolP384r1
- openssl_name: brainpoolP384r1
- min_cryptography_version: "2.2"
- - curve: brainpoolP512r1
- openssl_name: brainpoolP512r1
- min_cryptography_version: "2.2"
- - curve: sect571k1
- openssl_name: sect571k1
- min_cryptography_version: "0.5"
- - curve: sect409k1
- openssl_name: sect409k1
- min_cryptography_version: "0.5"
- - curve: sect283k1
- openssl_name: sect283k1
- min_cryptography_version: "0.5"
- - curve: sect233k1
- openssl_name: sect233k1
- min_cryptography_version: "0.5"
- - curve: sect163k1
- openssl_name: sect163k1
- min_cryptography_version: "0.5"
- - curve: sect571r1
- openssl_name: sect571r1
- min_cryptography_version: "0.5"
- - curve: sect409r1
- openssl_name: sect409r1
- min_cryptography_version: "0.5"
- - curve: sect283r1
- openssl_name: sect283r1
- min_cryptography_version: "0.5"
- - curve: sect233r1
- openssl_name: sect233r1
- min_cryptography_version: "0.5"
- - curve: sect163r2
- openssl_name: sect163r2
- min_cryptography_version: "0.5"
- when: select_crypto_backend == 'cryptography'
-
-- name: Test ECC key generation
- openssl_privatekey:
- path: '{{ output_dir }}/privatekey-{{ item.curve }}.pem'
- type: ECC
- curve: "{{ item.curve }}"
- select_crypto_backend: '{{ select_crypto_backend }}'
- when: |
- cryptography_version.stdout is version(item.min_cryptography_version, '>=') and
- item.openssl_name in openssl_ecc_list
- loop: "{{ ecc_types }}"
- loop_control:
- label: "{{ item.curve }}"
- register: privatekey_ecc_generate
-
-- name: Test ECC key generation (idempotency)
- openssl_privatekey:
- path: '{{ output_dir }}/privatekey-{{ item.curve }}.pem'
- type: ECC
- curve: "{{ item.curve }}"
- select_crypto_backend: '{{ select_crypto_backend }}'
- when: |
- cryptography_version.stdout is version(item.min_cryptography_version, '>=') and
- item.openssl_name in openssl_ecc_list
- loop: "{{ ecc_types }}"
- loop_control:
- label: "{{ item.curve }}"
- register: privatekey_ecc_idempotency
-
-- block:
- - name: Test other type generation
- openssl_privatekey:
- path: '{{ output_dir }}/privatekey-{{ item.type }}.pem'
- type: "{{ item.type }}"
- select_crypto_backend: '{{ select_crypto_backend }}'
- when: cryptography_version.stdout is version(item.min_version, '>=')
- loop: "{{ types }}"
- loop_control:
- label: "{{ item.type }}"
- ignore_errors: yes
- register: privatekey_t1_generate
-
- - name: Test other type generation (idempotency)
- openssl_privatekey:
- path: '{{ output_dir }}/privatekey-{{ item.type }}.pem'
- type: "{{ item.type }}"
- select_crypto_backend: '{{ select_crypto_backend }}'
- when: cryptography_version.stdout is version(item.min_version, '>=')
- loop: "{{ types }}"
- loop_control:
- label: "{{ item.type }}"
- ignore_errors: yes
- register: privatekey_t1_idempotency
-
- when: select_crypto_backend == 'cryptography'
- vars:
- types:
- - type: X25519
- min_version: '2.5'
- - type: Ed25519
- min_version: '2.6'
- - type: Ed448
- min_version: '2.6'
- - type: X448
- min_version: '2.6'
-
-- name: Generate privatekey with passphrase
- openssl_privatekey:
- path: '{{ output_dir }}/privatekeypw.pem'
- passphrase: hunter2
- cipher: "{{ 'aes256' if select_crypto_backend == 'pyopenssl' else 'auto' }}"
- select_crypto_backend: '{{ select_crypto_backend }}'
- backup: yes
- register: passphrase_1
-
-- name: Generate privatekey with passphrase (idempotent)
- openssl_privatekey:
- path: '{{ output_dir }}/privatekeypw.pem'
- passphrase: hunter2
- cipher: "{{ 'aes256' if select_crypto_backend == 'pyopenssl' else 'auto' }}"
- select_crypto_backend: '{{ select_crypto_backend }}'
- backup: yes
- register: passphrase_2
-
-- name: Regenerate privatekey without passphrase
- openssl_privatekey:
- path: '{{ output_dir }}/privatekeypw.pem'
- select_crypto_backend: '{{ select_crypto_backend }}'
- backup: yes
- register: passphrase_3
-
-- name: Regenerate privatekey without passphrase (idempotent)
- openssl_privatekey:
- path: '{{ output_dir }}/privatekeypw.pem'
- select_crypto_backend: '{{ select_crypto_backend }}'
- backup: yes
- register: passphrase_4
-
-- name: Regenerate privatekey with passphrase
- openssl_privatekey:
- path: '{{ output_dir }}/privatekeypw.pem'
- passphrase: hunter2
- cipher: "{{ 'aes256' if select_crypto_backend == 'pyopenssl' else 'auto' }}"
- select_crypto_backend: '{{ select_crypto_backend }}'
- backup: yes
- register: passphrase_5
-
-- name: Create broken key
- copy:
- dest: "{{ output_dir }}/broken"
- content: "broken"
-- name: Regenerate broken key
- openssl_privatekey:
- path: '{{ output_dir }}/broken.pem'
- select_crypto_backend: '{{ select_crypto_backend }}'
- register: output_broken
-
-- name: Remove module
- openssl_privatekey:
- path: '{{ output_dir }}/privatekeypw.pem'
- passphrase: hunter2
- cipher: "{{ 'aes256' if select_crypto_backend == 'pyopenssl' else 'auto' }}"
- select_crypto_backend: '{{ select_crypto_backend }}'
- backup: yes
- state: absent
- register: remove_1
-
-- name: Remove module (idempotent)
- openssl_privatekey:
- path: '{{ output_dir }}/privatekeypw.pem'
- passphrase: hunter2
- cipher: "{{ 'aes256' if select_crypto_backend == 'pyopenssl' else 'auto' }}"
- select_crypto_backend: '{{ select_crypto_backend }}'
- backup: yes
- state: absent
- register: remove_2
-
-- name: Generate privatekey_mode (mode 0400)
- openssl_privatekey:
- path: '{{ output_dir }}/privatekey_mode.pem'
- mode: '0400'
- select_crypto_backend: '{{ select_crypto_backend }}'
- register: privatekey_mode_1
-- name: Stat for privatekey_mode
- stat:
- path: '{{ output_dir }}/privatekey_mode.pem'
- register: privatekey_mode_1_stat
-
-- name: Generate privatekey_mode (mode 0400, idempotency)
- openssl_privatekey:
- path: '{{ output_dir }}/privatekey_mode.pem'
- mode: '0400'
- select_crypto_backend: '{{ select_crypto_backend }}'
- register: privatekey_mode_2
-
-- name: Generate privatekey_mode (mode 0400, force)
- openssl_privatekey:
- path: '{{ output_dir }}/privatekey_mode.pem'
- mode: '0400'
- force: yes
- select_crypto_backend: '{{ select_crypto_backend }}'
- register: privatekey_mode_3
-- name: Stat for privatekey_mode
- stat:
- path: '{{ output_dir }}/privatekey_mode.pem'
- register: privatekey_mode_3_stat
-
-- block:
- - name: Generate privatekey_fmt_1 - auto format
- openssl_privatekey:
- path: '{{ output_dir }}/privatekey_fmt_1.pem'
- format: auto
- select_crypto_backend: '{{ select_crypto_backend }}'
- register: privatekey_fmt_1_step_1
-
- - name: Generate privatekey_fmt_1 - auto format (idempotent)
- openssl_privatekey:
- path: '{{ output_dir }}/privatekey_fmt_1.pem'
- format: auto
- select_crypto_backend: '{{ select_crypto_backend }}'
- register: privatekey_fmt_1_step_2
-
- - name: Generate privatekey_fmt_1 - PKCS1 format
- openssl_privatekey:
- path: '{{ output_dir }}/privatekey_fmt_1.pem'
- format: pkcs1
- select_crypto_backend: '{{ select_crypto_backend }}'
- register: privatekey_fmt_1_step_3
-
- - name: Generate privatekey_fmt_1 - PKCS8 format
- openssl_privatekey:
- path: '{{ output_dir }}/privatekey_fmt_1.pem'
- format: pkcs8
- select_crypto_backend: '{{ select_crypto_backend }}'
- register: privatekey_fmt_1_step_4
-
- - name: Generate privatekey_fmt_1 - PKCS8 format (idempotent)
- openssl_privatekey:
- path: '{{ output_dir }}/privatekey_fmt_1.pem'
- format: pkcs8
- select_crypto_backend: '{{ select_crypto_backend }}'
- register: privatekey_fmt_1_step_5
-
- - name: Generate privatekey_fmt_1 - auto format (ignore)
- openssl_privatekey:
- path: '{{ output_dir }}/privatekey_fmt_1.pem'
- format: auto_ignore
- select_crypto_backend: '{{ select_crypto_backend }}'
- register: privatekey_fmt_1_step_6
-
- - name: Generate privatekey_fmt_1 - auto format (no ignore)
- openssl_privatekey:
- path: '{{ output_dir }}/privatekey_fmt_1.pem'
- format: auto
- select_crypto_backend: '{{ select_crypto_backend }}'
- register: privatekey_fmt_1_step_7
-
- - name: Generate privatekey_fmt_1 - raw format (fail)
- openssl_privatekey:
- path: '{{ output_dir }}/privatekey_fmt_1.pem'
- format: raw
- select_crypto_backend: '{{ select_crypto_backend }}'
- ignore_errors: yes
- register: privatekey_fmt_1_step_8
-
- - name: Generate privatekey_fmt_1 - PKCS8 format (convert)
- openssl_privatekey_info:
- path: '{{ output_dir }}/privatekey_fmt_1.pem'
- select_crypto_backend: '{{ select_crypto_backend }}'
- register: privatekey_fmt_1_step_9_before
-
- - name: Generate privatekey_fmt_1 - PKCS8 format (convert)
- openssl_privatekey:
- path: '{{ output_dir }}/privatekey_fmt_1.pem'
- format: pkcs8
- format_mismatch: convert
- select_crypto_backend: '{{ select_crypto_backend }}'
- register: privatekey_fmt_1_step_9
-
- - name: Generate privatekey_fmt_1 - PKCS8 format (convert)
- openssl_privatekey_info:
- path: '{{ output_dir }}/privatekey_fmt_1.pem'
- select_crypto_backend: '{{ select_crypto_backend }}'
- register: privatekey_fmt_1_step_9_after
-
- when: 'select_crypto_backend == "cryptography"'
-
-- block:
- - name: Generate privatekey_fmt_2 - PKCS8 format
- openssl_privatekey:
- path: '{{ output_dir }}/privatekey_fmt_2.pem'
- type: X448
- format: pkcs8
- select_crypto_backend: '{{ select_crypto_backend }}'
- ignore_errors: yes
- register: privatekey_fmt_2_step_1
-
- - name: Generate privatekey_fmt_2 - PKCS8 format (idempotent)
- openssl_privatekey:
- path: '{{ output_dir }}/privatekey_fmt_2.pem'
- type: X448
- format: pkcs8
- select_crypto_backend: '{{ select_crypto_backend }}'
- ignore_errors: yes
- register: privatekey_fmt_2_step_2
-
- - name: Generate privatekey_fmt_2 - raw format
- openssl_privatekey:
- path: '{{ output_dir }}/privatekey_fmt_2.pem'
- type: X448
- format: raw
- select_crypto_backend: '{{ select_crypto_backend }}'
- return_content: yes
- ignore_errors: yes
- register: privatekey_fmt_2_step_3
-
- - name: Read privatekey_fmt_2.pem
- slurp:
- src: "{{ output_dir }}/privatekey_fmt_2.pem"
- ignore_errors: yes
- register: content
-
- - name: Generate privatekey_fmt_2 - verify that returned content is base64 encoded
- assert:
- that:
- - privatekey_fmt_2_step_3.privatekey == content.content
- when: privatekey_fmt_2_step_1 is not failed
-
- - name: Generate privatekey_fmt_2 - raw format (idempotent)
- openssl_privatekey:
- path: '{{ output_dir }}/privatekey_fmt_2.pem'
- type: X448
- format: raw
- select_crypto_backend: '{{ select_crypto_backend }}'
- return_content: yes
- ignore_errors: yes
- register: privatekey_fmt_2_step_4
-
- - name: Read privatekey_fmt_2.pem
- slurp:
- src: "{{ output_dir }}/privatekey_fmt_2.pem"
- ignore_errors: yes
- register: content
-
- - name: Generate privatekey_fmt_2 - verify that returned content is base64 encoded
- assert:
- that:
- - privatekey_fmt_2_step_4.privatekey == content.content
- when: privatekey_fmt_2_step_1 is not failed
-
- - name: Generate privatekey_fmt_2 - auto format (ignore)
- openssl_privatekey:
- path: '{{ output_dir }}/privatekey_fmt_2.pem'
- type: X448
- format: auto_ignore
- select_crypto_backend: '{{ select_crypto_backend }}'
- return_content: yes
- ignore_errors: yes
- register: privatekey_fmt_2_step_5
-
- - name: Read privatekey_fmt_2.pem
- slurp:
- src: "{{ output_dir }}/privatekey_fmt_2.pem"
- ignore_errors: yes
- register: content
-
- - name: Generate privatekey_fmt_2 - verify that returned content is base64 encoded
- assert:
- that:
- - privatekey_fmt_2_step_5.privatekey == content.content
- when: privatekey_fmt_2_step_1 is not failed
-
- - name: Generate privatekey_fmt_2 - auto format (no ignore)
- openssl_privatekey:
- path: '{{ output_dir }}/privatekey_fmt_2.pem'
- type: X448
- format: auto
- select_crypto_backend: '{{ select_crypto_backend }}'
- return_content: yes
- ignore_errors: yes
- register: privatekey_fmt_2_step_6
-
- - name: Generate privatekey_fmt_2 - verify that returned content is not base64 encoded
- assert:
- that:
- - privatekey_fmt_2_step_6.privatekey == lookup('file', output_dir ~ '/privatekey_fmt_2.pem', rstrip=False)
- when: privatekey_fmt_2_step_1 is not failed
-
- when: 'select_crypto_backend == "cryptography" and cryptography_version.stdout is version("2.6", ">=")'
-
-
-
-# Test regenerate option
-
-- name: Regenerate - setup simple keys
- openssl_privatekey:
- path: '{{ output_dir }}/regenerate-a-{{ item }}.pem'
- type: RSA
- size: 1024
- select_crypto_backend: '{{ select_crypto_backend }}'
- loop: "{{ regenerate_values }}"
-- name: Regenerate - setup password protected keys
- openssl_privatekey:
- path: '{{ output_dir }}/regenerate-b-{{ item }}.pem'
- type: RSA
- size: 1024
- passphrase: hunter2
- cipher: "{{ 'aes256' if select_crypto_backend == 'pyopenssl' else 'auto' }}"
- select_crypto_backend: '{{ select_crypto_backend }}'
- loop: "{{ regenerate_values }}"
-- name: Regenerate - setup broken keys
- copy:
- dest: '{{ output_dir }}/regenerate-c-{{ item }}.pem'
- content: 'broken key'
- mode: '0700'
- loop: "{{ regenerate_values }}"
-
-- name: Regenerate - modify broken keys (check mode)
- openssl_privatekey:
- path: '{{ output_dir }}/regenerate-c-{{ item }}.pem'
- type: RSA
- size: 1024
- regenerate: '{{ item }}'
- select_crypto_backend: '{{ select_crypto_backend }}'
- check_mode: yes
- loop: "{{ regenerate_values }}"
- ignore_errors: yes
- register: result
-- assert:
- that:
- - result.results[0] is failed
- - "'Unable to read the key. The key is protected with a another passphrase / no passphrase or broken. Will not proceed.' in result.results[0].msg or 'Cannot load raw key' in result.results[0].msg"
- - result.results[1] is failed
- - "'Unable to read the key. The key is protected with a another passphrase / no passphrase or broken. Will not proceed.' in result.results[1].msg or 'Cannot load raw key' in result.results[1].msg"
- - result.results[2] is failed
- - "'Unable to read the key. The key is protected with a another passphrase / no passphrase or broken. Will not proceed.' in result.results[2].msg or 'Cannot load raw key' in result.results[2].msg"
- - result.results[3] is changed
- - result.results[4] is changed
-
-- name: Regenerate - modify broken keys
- openssl_privatekey:
- path: '{{ output_dir }}/regenerate-c-{{ item }}.pem'
- type: RSA
- size: 1024
- regenerate: '{{ item }}'
- select_crypto_backend: '{{ select_crypto_backend }}'
- loop: "{{ regenerate_values }}"
- ignore_errors: yes
- register: result
-- assert:
- that:
- - result.results[0] is failed
- - "'Unable to read the key. The key is protected with a another passphrase / no passphrase or broken. Will not proceed.' in result.results[0].msg or 'Cannot load raw key' in result.results[0].msg"
- - result.results[1] is failed
- - "'Unable to read the key. The key is protected with a another passphrase / no passphrase or broken. Will not proceed.' in result.results[1].msg or 'Cannot load raw key' in result.results[1].msg"
- - result.results[2] is failed
- - "'Unable to read the key. The key is protected with a another passphrase / no passphrase or broken. Will not proceed.' in result.results[2].msg or 'Cannot load raw key' in result.results[2].msg"
- - result.results[3] is changed
- - result.results[4] is changed
-
-- name: Regenerate - modify password protected keys (check mode)
- openssl_privatekey:
- path: '{{ output_dir }}/regenerate-b-{{ item }}.pem'
- type: RSA
- size: 1024
- regenerate: '{{ item }}'
- select_crypto_backend: '{{ select_crypto_backend }}'
- check_mode: yes
- loop: "{{ regenerate_values }}"
- ignore_errors: yes
- register: result
-- assert:
- that:
- - result.results[0] is failed
- - "'Unable to read the key. The key is protected with a another passphrase / no passphrase or broken. Will not proceed.' in result.results[0].msg"
- - result.results[1] is failed
- - "'Unable to read the key. The key is protected with a another passphrase / no passphrase or broken. Will not proceed.' in result.results[1].msg"
- - result.results[2] is failed
- - "'Unable to read the key. The key is protected with a another passphrase / no passphrase or broken. Will not proceed.' in result.results[2].msg"
- - result.results[3] is changed
- - result.results[4] is changed
-
-- name: Regenerate - modify password protected keys
- openssl_privatekey:
- path: '{{ output_dir }}/regenerate-b-{{ item }}.pem'
- type: RSA
- size: 1024
- regenerate: '{{ item }}'
- select_crypto_backend: '{{ select_crypto_backend }}'
- loop: "{{ regenerate_values }}"
- ignore_errors: yes
- register: result
-- assert:
- that:
- - result.results[0] is failed
- - "'Unable to read the key. The key is protected with a another passphrase / no passphrase or broken. Will not proceed.' in result.results[0].msg"
- - result.results[1] is failed
- - "'Unable to read the key. The key is protected with a another passphrase / no passphrase or broken. Will not proceed.' in result.results[1].msg"
- - result.results[2] is failed
- - "'Unable to read the key. The key is protected with a another passphrase / no passphrase or broken. Will not proceed.' in result.results[2].msg"
- - result.results[3] is changed
- - result.results[4] is changed
-
-- name: Regenerate - not modify regular keys (check mode)
- openssl_privatekey:
- path: '{{ output_dir }}/regenerate-a-{{ item }}.pem'
- type: RSA
- size: 1024
- regenerate: '{{ item }}'
- select_crypto_backend: '{{ select_crypto_backend }}'
- check_mode: yes
- loop: "{{ regenerate_values }}"
- register: result
-- assert:
- that:
- - result.results[0] is not changed
- - result.results[1] is not changed
- - result.results[2] is not changed
- - result.results[3] is not changed
- - result.results[4] is changed
-
-- name: Regenerate - not modify regular keys
- openssl_privatekey:
- path: '{{ output_dir }}/regenerate-a-{{ item }}.pem'
- type: RSA
- size: 1024
- regenerate: '{{ item }}'
- select_crypto_backend: '{{ select_crypto_backend }}'
- loop: "{{ regenerate_values }}"
- register: result
-- assert:
- that:
- - result.results[0] is not changed
- - result.results[1] is not changed
- - result.results[2] is not changed
- - result.results[3] is not changed
- - result.results[4] is changed
-
-- name: Regenerate - adjust key size (check mode)
- openssl_privatekey:
- path: '{{ output_dir }}/regenerate-a-{{ item }}.pem'
- type: RSA
- size: 1048
- regenerate: '{{ item }}'
- select_crypto_backend: '{{ select_crypto_backend }}'
- check_mode: yes
- loop: "{{ regenerate_values }}"
- ignore_errors: yes
- register: result
-- assert:
- that:
- - result.results[0] is success and result.results[0] is not changed
- - result.results[1] is failed
- - "'Key has wrong type and/or size. Will not proceed.' in result.results[1].msg"
- - result.results[2] is changed
- - result.results[3] is changed
- - result.results[4] is changed
-
-- name: Regenerate - adjust key size
- openssl_privatekey:
- path: '{{ output_dir }}/regenerate-a-{{ item }}.pem'
- type: RSA
- size: 1048
- regenerate: '{{ item }}'
- select_crypto_backend: '{{ select_crypto_backend }}'
- loop: "{{ regenerate_values }}"
- ignore_errors: yes
- register: result
-- assert:
- that:
- - result.results[0] is success and result.results[0] is not changed
- - result.results[1] is failed
- - "'Key has wrong type and/or size. Will not proceed.' in result.results[1].msg"
- - result.results[2] is changed
- - result.results[3] is changed
- - result.results[4] is changed
-
-- name: Regenerate - redistribute keys
- copy:
- src: '{{ output_dir }}/regenerate-a-always.pem'
- dest: '{{ output_dir }}/regenerate-a-{{ item }}.pem'
- remote_src: true
- loop: "{{ regenerate_values }}"
- when: "item != 'always'"
-
-- name: Regenerate - adjust key type (check mode)
- openssl_privatekey:
- path: '{{ output_dir }}/regenerate-a-{{ item }}.pem'
- type: DSA
- size: 1024
- regenerate: '{{ item }}'
- select_crypto_backend: '{{ select_crypto_backend }}'
- check_mode: yes
- loop: "{{ regenerate_values }}"
- ignore_errors: yes
- register: result
-- assert:
- that:
- - result.results[0] is success and result.results[0] is not changed
- - result.results[1] is failed
- - "'Key has wrong type and/or size. Will not proceed.' in result.results[1].msg"
- - result.results[2] is changed
- - result.results[3] is changed
- - result.results[4] is changed
-
-- name: Regenerate - adjust key type
- openssl_privatekey:
- path: '{{ output_dir }}/regenerate-a-{{ item }}.pem'
- type: DSA
- size: 1024
- regenerate: '{{ item }}'
- select_crypto_backend: '{{ select_crypto_backend }}'
- loop: "{{ regenerate_values }}"
- ignore_errors: yes
- register: result
-- assert:
- that:
- - result.results[0] is success and result.results[0] is not changed
- - result.results[1] is failed
- - "'Key has wrong type and/or size. Will not proceed.' in result.results[1].msg"
- - result.results[2] is changed
- - result.results[3] is changed
- - result.results[4] is changed
-
-- block:
- - name: Regenerate - redistribute keys
- copy:
- src: '{{ output_dir }}/regenerate-a-always.pem'
- dest: '{{ output_dir }}/regenerate-a-{{ item }}.pem'
- remote_src: true
- loop: "{{ regenerate_values }}"
- when: "item != 'always'"
-
- - name: Regenerate - format mismatch (check mode)
- openssl_privatekey:
- path: '{{ output_dir }}/regenerate-a-{{ item }}.pem'
- type: DSA
- size: 1024
- format: pkcs8
- regenerate: '{{ item }}'
- select_crypto_backend: '{{ select_crypto_backend }}'
- check_mode: yes
- loop: "{{ regenerate_values }}"
- ignore_errors: yes
- register: result
- - assert:
- that:
- - result.results[0] is success and result.results[0] is not changed
- - result.results[1] is failed
- - "'Key has wrong format. Will not proceed.' in result.results[1].msg"
- - result.results[2] is changed
- - result.results[3] is changed
- - result.results[4] is changed
-
- - name: Regenerate - format mismatch
- openssl_privatekey:
- path: '{{ output_dir }}/regenerate-a-{{ item }}.pem'
- type: DSA
- size: 1024
- format: pkcs8
- regenerate: '{{ item }}'
- select_crypto_backend: '{{ select_crypto_backend }}'
- loop: "{{ regenerate_values }}"
- ignore_errors: yes
- register: result
- - assert:
- that:
- - result.results[0] is success and result.results[0] is not changed
- - result.results[1] is failed
- - "'Key has wrong format. Will not proceed.' in result.results[1].msg"
- - result.results[2] is changed
- - result.results[3] is changed
- - result.results[4] is changed
-
- - name: Regenerate - redistribute keys
- copy:
- src: '{{ output_dir }}/regenerate-a-always.pem'
- dest: '{{ output_dir }}/regenerate-a-{{ item }}.pem'
- remote_src: true
- loop: "{{ regenerate_values }}"
- when: "item != 'always'"
-
- - name: Regenerate - convert format (check mode)
- openssl_privatekey:
- path: '{{ output_dir }}/regenerate-a-{{ item }}.pem'
- type: DSA
- size: 1024
- format: pkcs1
- format_mismatch: convert
- regenerate: '{{ item }}'
- select_crypto_backend: '{{ select_crypto_backend }}'
- check_mode: yes
- loop: "{{ regenerate_values }}"
- register: result
- - assert:
- that:
- - result.results[0] is changed
- - result.results[1] is changed
- - result.results[2] is changed
- - result.results[3] is changed
- - result.results[4] is changed
-
- - name: Regenerate - convert format
- openssl_privatekey:
- path: '{{ output_dir }}/regenerate-a-{{ item }}.pem'
- type: DSA
- size: 1024
- format: pkcs1
- format_mismatch: convert
- regenerate: '{{ item }}'
- select_crypto_backend: '{{ select_crypto_backend }}'
- loop: "{{ regenerate_values }}"
- register: result
- - assert:
- that:
- - result.results[0] is changed
- - result.results[1] is changed
- - result.results[2] is changed
- - result.results[3] is changed
- - result.results[4] is changed
- # for all values but 'always', the key should have not been regenerated.
- # verify this by comparing fingerprints:
- - result.results[0].fingerprint == result.results[1].fingerprint
- - result.results[0].fingerprint == result.results[2].fingerprint
- - result.results[0].fingerprint == result.results[3].fingerprint
- - result.results[0].fingerprint != result.results[4].fingerprint
- when: 'select_crypto_backend == "cryptography" and cryptography_version.stdout is version("2.6", ">=")'
diff --git a/test/integration/targets/openssl_privatekey/tasks/main.yml b/test/integration/targets/openssl_privatekey/tasks/main.yml
deleted file mode 100644
index f0a8e32bf7..0000000000
--- a/test/integration/targets/openssl_privatekey/tasks/main.yml
+++ /dev/null
@@ -1,108 +0,0 @@
----
-- name: Find out which elliptic curves are supported by installed OpenSSL
- command: openssl ecparam -list_curves
- register: openssl_ecc
-
-- name: Compile list of elliptic curves supported by OpenSSL
- set_fact:
- openssl_ecc_list: |
- {{
- openssl_ecc.stdout_lines
- | map('regex_search', '^ *([a-zA-Z0-9_-]+) *: .*$')
- | select()
- | map('regex_replace', '^ *([a-zA-Z0-9_-]+) *: .*$', '\1')
- | list
- }}
- when: ansible_distribution != 'CentOS' or ansible_distribution_major_version != '6'
- # CentOS comes with a very old jinja2 which does not include the map() filter...
-- name: Compile list of elliptic curves supported by OpenSSL (CentOS 6)
- set_fact:
- openssl_ecc_list:
- - secp384r1
- - secp521r1
- - prime256v1
- when: ansible_distribution == 'CentOS' and ansible_distribution_major_version == '6'
-
-- name: List of elliptic curves supported by OpenSSL
- debug: var=openssl_ecc_list
-
-- name: Run module with backend autodetection
- openssl_privatekey:
- path: '{{ output_dir }}/privatekey_backend_selection.pem'
-
-- 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: "{{ output_dir }}"
- state: absent
-
-- name: Re-create output directory
- file:
- path: "{{ output_dir }}"
- state: directory
-
-- block:
- - name: Running tests with cryptography backend
- include_tasks: impl.yml
- vars:
- select_crypto_backend: cryptography
-
- - import_tasks: ../tests/validate.yml
- vars:
- select_crypto_backend: cryptography
-
- 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: '{{ output_dir }}/fingerprint-{{ item }}.pem'
- type: "{{ item }}"
- size: 1024
- select_crypto_backend: pyopenssl
- loop:
- - RSA
- - DSA
- register: fingerprint_pyopenssl
-
- - name: "Fingerprint comparison: cryptography"
- openssl_privatekey:
- path: '{{ output_dir }}/fingerprint-{{ item }}.pem'
- type: "{{ item }}"
- size: 1024
- 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', '>=')
diff --git a/test/integration/targets/openssl_privatekey/tests/validate.yml b/test/integration/targets/openssl_privatekey/tests/validate.yml
deleted file mode 100644
index 5664971554..0000000000
--- a/test/integration/targets/openssl_privatekey/tests/validate.yml
+++ /dev/null
@@ -1,215 +0,0 @@
----
-- set_fact:
- system_potentially_has_no_algorithm_support: "{{ ansible_os_family == 'FreeBSD' }}"
-
-- name: Validate privatekey1 idempotency and content returned
- assert:
- that:
- - privatekey1_idempotence is not changed
- - privatekey1.privatekey == lookup('file', output_dir ~ '/privatekey1.pem', rstrip=False)
- - privatekey1.privatekey == privatekey1_idempotence.privatekey
-
-
-- name: Validate privatekey1 (test - RSA key with size 4096 bits)
- shell: "openssl rsa -noout -text -in {{ output_dir }}/privatekey1.pem | grep Private | sed 's/\\(RSA *\\)*Private-Key: (\\(.*\\) bit.*)/\\2/'"
- register: privatekey1
-
-- name: Validate privatekey1 (assert - RSA key with size 4096 bits)
- assert:
- that:
- - privatekey1.stdout == '4096'
-
-
-- name: Validate privatekey2 (test - RSA key with size 2048 bits)
- shell: "openssl rsa -noout -text -in {{ output_dir }}/privatekey2.pem | grep Private | sed 's/\\(RSA *\\)*Private-Key: (\\(.*\\) bit.*)/\\2/'"
- register: privatekey2
-
-- name: Validate privatekey2 (assert - RSA key with size 2048 bits)
- assert:
- that:
- - privatekey2.stdout == '2048'
-
-
-- name: Validate privatekey3 (test - DSA key with size 3072 bits)
- shell: "openssl dsa -noout -text -in {{ output_dir }}/privatekey3.pem | grep Private | sed 's/\\(RSA *\\)*Private-Key: (\\(.*\\) bit.*)/\\2/'"
- register: privatekey3
-
-- name: Validate privatekey3 (assert - DSA key with size 3072 bits)
- assert:
- that:
- - privatekey3.stdout == '3072'
-
-
-- name: Validate privatekey4 (test - Ensure key has been removed)
- stat:
- path: '{{ output_dir }}/privatekey4.pem'
- register: privatekey4
-
-- name: Validate privatekey4 (assert - Ensure key has been removed)
- assert:
- that:
- - privatekey4.stat.exists == False
-
-- name: Validate privatekey4 removal behavior
- assert:
- that:
- - privatekey4_delete is changed
- - privatekey4_delete.privatekey is none
- - privatekey4_delete_idempotence is not changed
-
-
-- name: Validate privatekey5 (test - Passphrase protected key + idempotence)
- shell: "openssl rsa -noout -text -in {{ output_dir }}/privatekey5.pem -passin pass:ansible | grep Private | sed 's/\\(RSA *\\)*Private-Key: (\\(.*\\) bit.*)/\\2/'"
- register: privatekey5
- # Current version of OS/X that runs in the CI (10.11) does not have an up to date version of the OpenSSL library
- # leading to this test to fail when run in the CI. However, this test has been run for 10.12 and has returned succesfully.
- when: openssl_version.stdout is version('0.9.8zh', '>=')
-
-- name: Validate privatekey5 (assert - Passphrase protected key + idempotence)
- assert:
- that:
- - privatekey5.stdout == '4096'
- when: openssl_version.stdout is version('0.9.8zh', '>=')
-
-- name: Validate privatekey5 idempotence (assert - Passphrase protected key + idempotence)
- assert:
- that:
- - privatekey5_idempotence is not changed
-
-
-- name: Validate privatekey6 (test - Passphrase protected key with non ascii character)
- shell: "openssl rsa -noout -text -in {{ output_dir }}/privatekey6.pem -passin pass:ànsïblé | grep Private | sed 's/\\(RSA *\\)*Private-Key: (\\(.*\\) bit.*)/\\2/'"
- register: privatekey6
- when: openssl_version.stdout is version('0.9.8zh', '>=')
-
-- name: Validate privatekey6 (assert - Passphrase protected key with non ascii character)
- assert:
- that:
- - privatekey6.stdout == '4096'
- when: openssl_version.stdout is version('0.9.8zh', '>=')
-
-- name: Validate ECC generation (dump with OpenSSL)
- shell: "openssl ec -in {{ output_dir }}/privatekey-{{ item.item.curve }}.pem -noout -text | grep 'ASN1 OID: ' | sed 's/ASN1 OID: \\([^ ]*\\)/\\1/'"
- loop: "{{ privatekey_ecc_generate.results }}"
- register: privatekey_ecc_dump
- when: openssl_version.stdout is version('0.9.8zh', '>=') and 'skip_reason' not in item
- loop_control:
- label: "{{ item.item.curve }}"
-
-- name: Validate ECC generation
- assert:
- that:
- - item is changed
- loop: "{{ privatekey_ecc_generate.results }}"
- when: "'skip_reason' not in item"
- loop_control:
- label: "{{ item.item.curve }}"
-
-- name: Validate ECC generation (curve type)
- assert:
- that:
- - "'skip_reason' in item or item.item.item.openssl_name == item.stdout"
- loop: "{{ privatekey_ecc_dump.results }}"
- when: "'skip_reason' not in item"
- loop_control:
- label: "{{ item.item.item }} - {{ item.stdout if 'stdout' in item else '<unsupported>' }}"
-
-- name: Validate ECC generation idempotency
- assert:
- that:
- - item is not changed
- loop: "{{ privatekey_ecc_idempotency.results }}"
- when: "'skip_reason' not in item"
- loop_control:
- label: "{{ item.item.curve }}"
-
-- name: Validate other type generation (just check changed)
- assert:
- that:
- - (item is succeeded and item is changed) or
- (item is failed and 'Cryptography backend does not support the algorithm required for ' in item.msg and system_potentially_has_no_algorithm_support)
- loop: "{{ privatekey_t1_generate.results }}"
- when: "'skip_reason' not in item"
- loop_control:
- label: "{{ item.item.type }}"
-
-- name: Validate other type generation idempotency
- assert:
- that:
- - (item is succeeded and item is not changed) or
- (item is failed and 'Cryptography backend does not support the algorithm required for ' in item.msg and system_potentially_has_no_algorithm_support)
- loop: "{{ privatekey_t1_idempotency.results }}"
- when: "'skip_reason' not in item"
- loop_control:
- label: "{{ item.item.type }}"
-
-- name: Validate passphrase changing
- assert:
- that:
- - passphrase_1 is changed
- - passphrase_2 is not changed
- - passphrase_3 is changed
- - passphrase_4 is not changed
- - passphrase_5 is changed
- - passphrase_1.backup_file is undefined
- - passphrase_2.backup_file is undefined
- - passphrase_3.backup_file is string
- - passphrase_4.backup_file is undefined
- - passphrase_5.backup_file is string
-
-- name: Verify that broken key will be regenerated
- assert:
- that:
- - output_broken is changed
-
-- name: Validate remove
- assert:
- that:
- - remove_1 is changed
- - remove_2 is not changed
- - remove_1.backup_file is string
- - remove_2.backup_file is undefined
-
-- name: Validate mode
- assert:
- that:
- - privatekey_mode_1 is changed
- - privatekey_mode_1_stat.stat.mode == '0400'
- - privatekey_mode_2 is not changed
- - privatekey_mode_3 is changed
- - privatekey_mode_3_stat.stat.mode == '0400'
- - privatekey_mode_1_stat.stat.mtime != privatekey_mode_3_stat.stat.mtime
-
-- name: Validate format 1
- assert:
- that:
- - privatekey_fmt_1_step_1 is changed
- - privatekey_fmt_1_step_2 is not changed
- - privatekey_fmt_1_step_3 is not changed
- - privatekey_fmt_1_step_4 is changed
- - privatekey_fmt_1_step_5 is not changed
- - privatekey_fmt_1_step_6 is not changed
- - privatekey_fmt_1_step_7 is changed
- - privatekey_fmt_1_step_8 is failed
- - privatekey_fmt_1_step_9 is changed
- - privatekey_fmt_1_step_9_before.public_key == privatekey_fmt_1_step_9_after.public_key
- when: 'select_crypto_backend == "cryptography"'
-
-- name: Validate format 2 (failed)
- assert:
- that:
- - system_potentially_has_no_algorithm_support
- - privatekey_fmt_2_step_1 is failed
- - "'Cryptography backend does not support the algorithm required for ' in privatekey_fmt_2_step_1.msg"
- when: 'select_crypto_backend == "cryptography" and cryptography_version.stdout is version("2.6", ">=") and privatekey_fmt_2_step_1 is failed'
-
-- name: Validate format 2
- assert:
- that:
- - privatekey_fmt_2_step_1 is succeeded and privatekey_fmt_2_step_1 is changed
- - privatekey_fmt_2_step_2 is succeeded and privatekey_fmt_2_step_2 is not changed
- - privatekey_fmt_2_step_3 is succeeded and privatekey_fmt_2_step_3 is changed
- - privatekey_fmt_2_step_4 is succeeded and privatekey_fmt_2_step_4 is not changed
- - privatekey_fmt_2_step_5 is succeeded and privatekey_fmt_2_step_5 is not changed
- - privatekey_fmt_2_step_6 is succeeded and privatekey_fmt_2_step_6 is changed
- when: 'select_crypto_backend == "cryptography" and cryptography_version.stdout is version("2.6", ">=") and privatekey_fmt_2_step_1 is not failed'
diff --git a/test/integration/targets/openssl_privatekey/vars/main.yml b/test/integration/targets/openssl_privatekey/vars/main.yml
deleted file mode 100644
index 81eb611f8a..0000000000
--- a/test/integration/targets/openssl_privatekey/vars/main.yml
+++ /dev/null
@@ -1,7 +0,0 @@
----
-regenerate_values:
- - never
- - fail
- - partial_idempotence
- - full_idempotence
- - always
diff --git a/test/integration/targets/openssl_privatekey_info/aliases b/test/integration/targets/openssl_privatekey_info/aliases
deleted file mode 100644
index 0b484bbab6..0000000000
--- a/test/integration/targets/openssl_privatekey_info/aliases
+++ /dev/null
@@ -1,3 +0,0 @@
-shippable/posix/group1
-destructive
-skip/aix
diff --git a/test/integration/targets/openssl_privatekey_info/meta/main.yml b/test/integration/targets/openssl_privatekey_info/meta/main.yml
deleted file mode 100644
index 800aff6428..0000000000
--- a/test/integration/targets/openssl_privatekey_info/meta/main.yml
+++ /dev/null
@@ -1,2 +0,0 @@
-dependencies:
- - setup_openssl
diff --git a/test/integration/targets/openssl_privatekey_info/tasks/impl.yml b/test/integration/targets/openssl_privatekey_info/tasks/impl.yml
deleted file mode 100644
index c74acdec79..0000000000
--- a/test/integration/targets/openssl_privatekey_info/tasks/impl.yml
+++ /dev/null
@@ -1,178 +0,0 @@
----
-- debug:
- msg: "Executing tests with backend {{ select_crypto_backend }}"
-
-- name: ({{select_crypto_backend}}) Get key 1 info
- openssl_privatekey_info:
- path: '{{ output_dir }}/privatekey_1.pem'
- select_crypto_backend: '{{ select_crypto_backend }}'
- register: result
-
-- name: Check that RSA key info is ok
- assert:
- that:
- - "'public_key' in result"
- - "'public_key_fingerprints' in result"
- - "'type' in result"
- - "result.type == 'RSA'"
- - "'public_data' in result"
- - "2 ** (result.public_data.size - 1) < result.public_data.modulus < 2 ** result.public_data.size"
- - "result.public_data.exponent > 5"
- - "'private_data' not in result"
-
-- name: Update result list
- set_fact:
- info_results: "{{ info_results | combine({'key1': result}) }}"
-
-- name: ({{select_crypto_backend}}) Get key 1 info directly
- openssl_privatekey_info:
- content: '{{ lookup("file", output_dir ~ "/privatekey_1.pem") }}'
- select_crypto_backend: '{{ select_crypto_backend }}'
- register: result_direct
-
-- name: ({{select_crypto_backend}}) Compare output of direct and loaded info
- assert:
- that:
- - result == result_direct
-
-- name: ({{select_crypto_backend}}) Get key 2 info
- openssl_privatekey_info:
- path: '{{ output_dir }}/privatekey_2.pem'
- return_private_key_data: yes
- select_crypto_backend: '{{ select_crypto_backend }}'
- register: result
-
-- name: Check that RSA key info is ok
- assert:
- that:
- - "'public_key' in result"
- - "'public_key_fingerprints' in result"
- - "'type' in result"
- - "result.type == 'RSA'"
- - "'public_data' in result"
- - "result.public_data.size == 2048"
- - "2 ** (result.public_data.size - 1) < result.public_data.modulus < 2 ** result.public_data.size"
- - "result.public_data.exponent > 5"
- - "'private_data' in result"
- - "result.public_data.modulus == result.private_data.p * result.private_data.q"
- - "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)
- openssl_privatekey_info:
- path: '{{ output_dir }}/privatekey_3.pem'
- return_private_key_data: yes
- select_crypto_backend: '{{ select_crypto_backend }}'
- ignore_errors: yes
- register: result
-
-- name: Check that loading passphrase protected key without passphrase failed
- assert:
- that:
- - result is failed
- # Check that return values are there
- - result.can_load_key is defined
- - result.can_parse_key is defined
- # Check that return values are correct
- - result.can_load_key
- - not result.can_parse_key
- # Check that additional data isn't there
- - "'pulic_key' not in result"
- - "'pulic_key_fingerprints' not in result"
- - "'type' not in result"
- - "'public_data' not in result"
- - "'private_data' not in result"
-
-- name: ({{select_crypto_backend}}) Get key 3 info (with passphrase)
- openssl_privatekey_info:
- path: '{{ output_dir }}/privatekey_3.pem'
- passphrase: hunter2
- return_private_key_data: yes
- select_crypto_backend: '{{ select_crypto_backend }}'
- register: result
-
-- name: Check that RSA key info is ok
- assert:
- that:
- - "'public_key' in result"
- - "'public_key_fingerprints' in result"
- - "'type' in result"
- - "result.type == 'RSA'"
- - "'public_data' in result"
- - "2 ** (result.public_data.size - 1) < result.public_data.modulus < 2 ** result.public_data.size"
- - "result.public_data.exponent > 5"
- - "'private_data' in result"
- - "result.public_data.modulus == result.private_data.p * result.private_data.q"
- - "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
- openssl_privatekey_info:
- path: '{{ output_dir }}/privatekey_4.pem'
- return_private_key_data: yes
- select_crypto_backend: '{{ select_crypto_backend }}'
- 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
- assert:
- that:
- - "'public_key' in result"
- - "'public_key_fingerprints' in result"
- - "'type' in result"
- - "result.type.startswith('unknown ')"
- - "'public_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', '>'))
-
-- name: ({{select_crypto_backend}}) Get key 5 info
- openssl_privatekey_info:
- path: '{{ output_dir }}/privatekey_5.pem'
- return_private_key_data: yes
- select_crypto_backend: '{{ select_crypto_backend }}'
- register: result
-
-- name: Check that DSA key info is ok
- assert:
- that:
- - "'public_key' in result"
- - "'public_key_fingerprints' in result"
- - "'type' in result"
- - "result.type == 'DSA'"
- - "'public_data' in result"
- - "result.public_data.p > 2"
- - "result.public_data.q > 2"
- - "result.public_data.g >= 2"
- - "result.public_data.y > 2"
- - "'private_data' in result"
- - "result.private_data.x > 2"
-
-- name: Update result list
- set_fact:
- info_results: "{{ info_results | combine({'key5': result}) }}"
diff --git a/test/integration/targets/openssl_privatekey_info/tasks/main.yml b/test/integration/targets/openssl_privatekey_info/tasks/main.yml
deleted file mode 100644
index 167b88fabf..0000000000
--- a/test/integration/targets/openssl_privatekey_info/tasks/main.yml
+++ /dev/null
@@ -1,71 +0,0 @@
----
-- name: Generate privatekey 1
- openssl_privatekey:
- path: '{{ output_dir }}/privatekey_1.pem'
-
-- name: Generate privatekey 2 (less bits)
- openssl_privatekey:
- path: '{{ output_dir }}/privatekey_2.pem'
- type: RSA
- size: 2048
-
-- name: Generate privatekey 3 (with password)
- openssl_privatekey:
- path: '{{ output_dir }}/privatekey_3.pem'
- passphrase: hunter2
- cipher: auto
- select_crypto_backend: cryptography
-
-- name: Generate privatekey 4 (ECC)
- openssl_privatekey:
- path: '{{ output_dir }}/privatekey_4.pem'
- type: ECC
- curve: "{{ (ansible_distribution == 'CentOS' and ansible_distribution_major_version == '6') | ternary('secp521r1', 'secp256k1') }}"
- # ^ cryptography on CentOS6 doesn't support secp256k1, so we use secp521r1 instead
- select_crypto_backend: cryptography
-
-- name: Generate privatekey 5 (DSA)
- openssl_privatekey:
- path: '{{ output_dir }}/privatekey_5.pem'
- type: DSA
- 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
- include_tasks: impl.yml
- vars:
- select_crypto_backend: cryptography
- 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', '>=')
diff --git a/test/integration/targets/openssl_publickey/aliases b/test/integration/targets/openssl_publickey/aliases
deleted file mode 100644
index 0b484bbab6..0000000000
--- a/test/integration/targets/openssl_publickey/aliases
+++ /dev/null
@@ -1,3 +0,0 @@
-shippable/posix/group1
-destructive
-skip/aix
diff --git a/test/integration/targets/openssl_publickey/meta/main.yml b/test/integration/targets/openssl_publickey/meta/main.yml
deleted file mode 100644
index 800aff6428..0000000000
--- a/test/integration/targets/openssl_publickey/meta/main.yml
+++ /dev/null
@@ -1,2 +0,0 @@
-dependencies:
- - setup_openssl
diff --git a/test/integration/targets/openssl_publickey/tasks/impl.yml b/test/integration/targets/openssl_publickey/tasks/impl.yml
deleted file mode 100644
index 738013ff66..0000000000
--- a/test/integration/targets/openssl_publickey/tasks/impl.yml
+++ /dev/null
@@ -1,186 +0,0 @@
----
-- name: Generate privatekey
- openssl_privatekey:
- path: '{{ output_dir }}/privatekey.pem'
-
-- name: Generate publickey - PEM format
- openssl_publickey:
- path: '{{ output_dir }}/publickey.pub'
- privatekey_path: '{{ output_dir }}/privatekey.pem'
- select_crypto_backend: '{{ select_crypto_backend }}'
- return_content: yes
- register: publickey
-
-- name: Generate publickey - PEM format (idempotence)
- openssl_publickey:
- path: '{{ output_dir }}/publickey.pub'
- privatekey_path: '{{ output_dir }}/privatekey.pem'
- select_crypto_backend: '{{ select_crypto_backend }}'
- return_content: yes
- register: publickey_idempotence
-
-- name: Generate publickey - OpenSSH format
- openssl_publickey:
- path: '{{ output_dir }}/publickey-ssh.pub'
- privatekey_path: '{{ output_dir }}/privatekey.pem'
- format: OpenSSH
- select_crypto_backend: '{{ select_crypto_backend }}'
- when: select_crypto_backend == 'cryptography' and cryptography_version.stdout is version('1.4.0', '>=')
-
-- name: Generate publickey - OpenSSH format - test idempotence (issue 33256)
- openssl_publickey:
- path: '{{ output_dir }}/publickey-ssh.pub'
- privatekey_path: '{{ output_dir }}/privatekey.pem'
- format: OpenSSH
- select_crypto_backend: '{{ select_crypto_backend }}'
- when: select_crypto_backend == 'cryptography' and cryptography_version.stdout is version('1.4.0', '>=')
- register: publickey_ssh_idempotence
-
-- name: Generate publickey2 - standard
- openssl_publickey:
- path: '{{ output_dir }}/publickey2.pub'
- privatekey_path: '{{ output_dir }}/privatekey.pem'
- select_crypto_backend: '{{ select_crypto_backend }}'
-
-- name: Delete publickey2 - standard
- openssl_publickey:
- state: absent
- path: '{{ output_dir }}/publickey2.pub'
- privatekey_path: '{{ output_dir }}/privatekey.pem'
- select_crypto_backend: '{{ select_crypto_backend }}'
- return_content: yes
- register: publickey2_absent
-
-- name: Delete publickey2 - standard (idempotence)
- openssl_publickey:
- state: absent
- path: '{{ output_dir }}/publickey2.pub'
- privatekey_path: '{{ output_dir }}/privatekey.pem'
- select_crypto_backend: '{{ select_crypto_backend }}'
- register: publickey2_absent_idempotence
-
-- name: Generate privatekey3 - with passphrase
- openssl_privatekey:
- path: '{{ output_dir }}/privatekey3.pem'
- passphrase: ansible
- cipher: aes256
-
-- name: Generate publickey3 - with passphrase protected privatekey
- openssl_publickey:
- path: '{{ output_dir }}/publickey3.pub'
- privatekey_path: '{{ output_dir }}/privatekey3.pem'
- privatekey_passphrase: ansible
- select_crypto_backend: '{{ select_crypto_backend }}'
-
-- name: Generate publickey3 - with passphrase protected privatekey - idempotence
- openssl_publickey:
- path: '{{ output_dir }}/publickey3.pub'
- privatekey_path: '{{ output_dir }}/privatekey3.pem'
- privatekey_passphrase: ansible
- select_crypto_backend: '{{ select_crypto_backend }}'
- register: publickey3_idempotence
-
-- name: Generate empty file that will hold a public key (issue 33072)
- file:
- path: '{{ output_dir }}/publickey4.pub'
- state: touch
-
-- name: Generate publickey in empty existing file (issue 33072)
- openssl_publickey:
- path: '{{ output_dir }}/publickey4.pub'
- privatekey_path: '{{ output_dir }}/privatekey.pem'
- select_crypto_backend: '{{ select_crypto_backend }}'
-
-- name: Generate privatekey 5 (ECC)
- openssl_privatekey:
- path: '{{ output_dir }}/privatekey5.pem'
- type: ECC
- curve: secp256r1
-
-- name: Generate publickey 5 - PEM format
- openssl_publickey:
- path: '{{ output_dir }}/publickey5.pub'
- privatekey_path: '{{ output_dir }}/privatekey.pem'
- backup: yes
- select_crypto_backend: '{{ select_crypto_backend }}'
- register: privatekey5_1
-- name: Generate publickey 5 - PEM format (idempotent)
- openssl_publickey:
- path: '{{ output_dir }}/publickey5.pub'
- privatekey_path: '{{ output_dir }}/privatekey.pem'
- backup: yes
- select_crypto_backend: '{{ select_crypto_backend }}'
- register: privatekey5_2
-- name: Generate publickey 5 - PEM format (different private key)
- openssl_publickey:
- path: '{{ output_dir }}/publickey5.pub'
- privatekey_path: '{{ output_dir }}/privatekey5.pem'
- backup: yes
- select_crypto_backend: '{{ select_crypto_backend }}'
- register: privatekey5_3
-
-- name: Generate privatekey with password
- openssl_privatekey:
- path: '{{ output_dir }}/privatekeypw.pem'
- passphrase: hunter2
- cipher: auto
- select_crypto_backend: cryptography
-
-- name: Generate publickey - PEM format (failed passphrase 1)
- openssl_publickey:
- path: '{{ output_dir }}/publickey_pw1.pub'
- privatekey_path: '{{ output_dir }}/privatekey.pem'
- privatekey_passphrase: hunter2
- select_crypto_backend: '{{ select_crypto_backend }}'
- ignore_errors: yes
- register: passphrase_error_1
-
-- name: Generate publickey - PEM format (failed passphrase 2)
- openssl_publickey:
- path: '{{ output_dir }}/publickey_pw2.pub'
- privatekey_path: '{{ output_dir }}/privatekeypw.pem'
- privatekey_passphrase: wrong_password
- select_crypto_backend: '{{ select_crypto_backend }}'
- ignore_errors: yes
- register: passphrase_error_2
-
-- name: Generate publickey - PEM format (failed passphrase 3)
- openssl_publickey:
- path: '{{ output_dir }}/publickey_pw3.pub'
- privatekey_path: '{{ output_dir }}/privatekeypw.pem'
- select_crypto_backend: '{{ select_crypto_backend }}'
- ignore_errors: yes
- register: passphrase_error_3
-
-- name: Create broken key
- copy:
- dest: "{{ output_dir }}/publickeybroken.pub"
- content: "broken"
-- name: Regenerate broken key
- openssl_publickey:
- path: '{{ output_dir }}/publickeybroken.pub'
- privatekey_path: '{{ output_dir }}/privatekey5.pem'
- select_crypto_backend: '{{ select_crypto_backend }}'
- register: output_broken
-
-- name: Generate publickey - PEM format (for removal)
- openssl_publickey:
- path: '{{ output_dir }}/publickey_removal.pub'
- privatekey_path: '{{ output_dir }}/privatekey.pem'
- select_crypto_backend: '{{ select_crypto_backend }}'
-- name: Generate publickey - PEM format (removal)
- openssl_publickey:
- state: absent
- path: '{{ output_dir }}/publickey_removal.pub'
- privatekey_path: '{{ output_dir }}/privatekey.pem'
- backup: yes
- select_crypto_backend: '{{ select_crypto_backend }}'
- register: remove_1
-- name: Generate publickey - PEM format (removal, idempotent)
- openssl_publickey:
- state: absent
- path: '{{ output_dir }}/publickey_removal.pub'
- privatekey_path: '{{ output_dir }}/privatekey.pem'
- backup: yes
- select_crypto_backend: '{{ select_crypto_backend }}'
- register: remove_2
diff --git a/test/integration/targets/openssl_publickey/tasks/main.yml b/test/integration/targets/openssl_publickey/tasks/main.yml
deleted file mode 100644
index b7369c03d7..0000000000
--- a/test/integration/targets/openssl_publickey/tasks/main.yml
+++ /dev/null
@@ -1,48 +0,0 @@
----
-- block:
- - name: Generate privatekey1 - standard
- openssl_privatekey:
- path: '{{ output_dir }}/privatekey_autodetect.pem'
-
- - name: Run module with backend autodetection
- openssl_publickey:
- path: '{{ output_dir }}/privatekey_autodetect_public.pem'
- privatekey_path: '{{ output_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: "{{ output_dir }}"
- state: absent
-
-- name: Re-create output directory
- file:
- path: "{{ output_dir }}"
- state: directory
-
-- block:
- - name: Running tests with cryptography backend
- include_tasks: impl.yml
- vars:
- select_crypto_backend: cryptography
-
- - import_tasks: ../tests/validate.yml
- vars:
- select_crypto_backend: cryptography
-
- when: cryptography_version.stdout is version('1.2.3', '>=')
diff --git a/test/integration/targets/openssl_publickey/tests/validate.yml b/test/integration/targets/openssl_publickey/tests/validate.yml
deleted file mode 100644
index aaf9c10001..0000000000
--- a/test/integration/targets/openssl_publickey/tests/validate.yml
+++ /dev/null
@@ -1,146 +0,0 @@
----
-- name: Validate publickey 1 idempotence and result behavior
- assert:
- that:
- - publickey is changed
- - publickey_idempotence is not changed
- - publickey.publickey == lookup('file', output_dir ~ '/publickey.pub', rstrip=False)
- - publickey.publickey == publickey_idempotence.publickey
-
-- name: Validate public key (test - privatekey modulus)
- shell: 'openssl rsa -noout -modulus -in {{ output_dir }}/privatekey.pem'
- register: privatekey_modulus
-
-- name: Validate public key (test - publickey modulus)
- shell: 'openssl rsa -pubin -noout -modulus < {{ output_dir }}/publickey.pub'
- register: publickey_modulus
-
-- name: Validate public key (assert)
- assert:
- that:
- - publickey_modulus.stdout == privatekey_modulus.stdout
-
-- name: Validate public key - OpenSSH format (test - privatekey's publickey)
- shell: 'ssh-keygen -y -f {{ output_dir }}/privatekey.pem'
- register: privatekey_publickey
- when: select_crypto_backend == 'cryptography' and cryptography_version.stdout is version('1.4.0', '>=')
-
-- name: Validate public key - OpenSSH format (test - publickey)
- slurp:
- src: '{{ output_dir }}/publickey-ssh.pub'
- register: publickey
- when: select_crypto_backend == 'cryptography' and cryptography_version.stdout is version('1.4.0', '>=')
-
-- name: Validate public key - OpenSSH format (assert)
- assert:
- that:
- - privatekey_publickey.stdout == '{{ publickey.content|b64decode }}'
- when: select_crypto_backend == 'cryptography' and cryptography_version.stdout is version('1.4.0', '>=')
-
-- name: Validate public key - OpenSSH format - test idempotence (issue 33256)
- assert:
- that:
- - publickey_ssh_idempotence is not changed
- when: select_crypto_backend == 'cryptography' and cryptography_version.stdout is version('1.4.0', '>=')
-
-- name: Validate publickey2 (test - Ensure key has been removed)
- stat:
- path: '{{ output_dir }}/publickey2.pub'
- register: publickey2
-
-- name: Validate publickey2 (assert - Ensure key has been removed)
- assert:
- that:
- - publickey2.stat.exists == False
-
-- name: Validate publickey2 removal behavior
- assert:
- that:
- - publickey2_absent is changed
- - publickey2_absent_idempotence is not changed
- - publickey2_absent.publickey is none
-
-
-- name: Validate publickey3 (test - privatekey modulus)
- shell: 'openssl rsa -noout -modulus -in {{ output_dir }}/privatekey3.pem -passin pass:ansible'
- register: privatekey3_modulus
- when: openssl_version.stdout is version('0.9.8zh', '>=')
-
-- name: Validate publickey3 (test - publickey modulus)
- shell: 'openssl rsa -pubin -noout -modulus < {{ output_dir }}/publickey3.pub'
- register: publickey3_modulus
- when: openssl_version.stdout is version('0.9.8zh', '>=')
-
-- name: Validate publickey3 (assert)
- assert:
- that:
- - publickey3_modulus.stdout == privatekey3_modulus.stdout
- when: openssl_version.stdout is version('0.9.8zh', '>=')
-
-- name: Validate publickey3 idempotence (assert)
- assert:
- that:
- - publickey3_idempotence is not changed
-
-- name: Validate publickey4 (test - privatekey modulus)
- shell: 'openssl rsa -noout -modulus -in {{ output_dir }}/privatekey.pem'
- register: privatekey4_modulus
- when: openssl_version.stdout is version('0.9.8zh', '>=')
-
-- name: Validate publickey4 (test - publickey modulus)
- shell: 'openssl rsa -pubin -noout -modulus < {{ output_dir }}/publickey4.pub'
- register: publickey4_modulus
- when: openssl_version.stdout is version('0.9.8zh', '>=')
-
-- name: Validate publickey4 (assert)
- assert:
- that:
- - publickey4_modulus.stdout == privatekey4_modulus.stdout
- when: openssl_version.stdout is version('0.9.8zh', '>=')
-
-- name: Validate idempotency and backup
- assert:
- that:
- - privatekey5_1 is changed
- - privatekey5_1.backup_file is undefined
- - privatekey5_2 is not changed
- - privatekey5_2.backup_file is undefined
- - privatekey5_3 is changed
- - privatekey5_3.backup_file is string
-
-- name: Validate public key 5 (test - privatekey's pubkey)
- command: 'openssl ec -in {{ output_dir }}/privatekey5.pem -pubout'
- register: privatekey5_pubkey
-
-- name: Validate public key 5 (test - publickey pubkey)
- # Fancy way of writing "cat {{ output_dir }}/publickey5.pub"
- command: 'openssl ec -pubin -in {{ output_dir }}/publickey5.pub -pubout'
- register: publickey5_pubkey
-
-- name: Validate public key 5 (assert)
- assert:
- that:
- - publickey5_pubkey.stdout == privatekey5_pubkey.stdout
-
-- name:
- assert:
- that:
- - passphrase_error_1 is failed
- - "'assphrase' in passphrase_error_1.msg or 'assword' in passphrase_error_1.msg"
- - passphrase_error_2 is failed
- - "'assphrase' in passphrase_error_2.msg or 'assword' in passphrase_error_2.msg or 'serializ' in passphrase_error_2.msg"
- - passphrase_error_3 is failed
- - "'assphrase' in passphrase_error_3.msg or 'assword' in passphrase_error_3.msg or 'serializ' in passphrase_error_3.msg"
-
-- name: Verify that broken key will be regenerated
- assert:
- that:
- - output_broken is changed
-
-- name: Validate remove
- assert:
- that:
- - remove_1 is changed
- - remove_2 is not changed
- - remove_1.backup_file is string
- - remove_2.backup_file is undefined
diff --git a/test/integration/targets/x509_crl/aliases b/test/integration/targets/x509_crl/aliases
deleted file mode 100644
index d225a8b13a..0000000000
--- a/test/integration/targets/x509_crl/aliases
+++ /dev/null
@@ -1,4 +0,0 @@
-x509_crl_info
-shippable/posix/group1
-destructive
-skip/aix
diff --git a/test/integration/targets/x509_crl/meta/main.yml b/test/integration/targets/x509_crl/meta/main.yml
deleted file mode 100644
index 800aff6428..0000000000
--- a/test/integration/targets/x509_crl/meta/main.yml
+++ /dev/null
@@ -1,2 +0,0 @@
-dependencies:
- - setup_openssl
diff --git a/test/integration/targets/x509_crl/tasks/impl.yml b/test/integration/targets/x509_crl/tasks/impl.yml
deleted file mode 100644
index eafb2dad2b..0000000000
--- a/test/integration/targets/x509_crl/tasks/impl.yml
+++ /dev/null
@@ -1,289 +0,0 @@
----
-- name: Create CRL 1 (check mode)
- x509_crl:
- path: '{{ output_dir }}/ca-crl1.crl'
- privatekey_path: '{{ output_dir }}/ca.key'
- issuer:
- CN: Ansible
- last_update: 20191013000000Z
- next_update: 20191113000000Z
- revoked_certificates:
- - path: '{{ output_dir }}/cert-1.pem'
- revocation_date: 20191013000000Z
- - path: '{{ output_dir }}/cert-2.pem'
- revocation_date: 20191013000000Z
- reason: key_compromise
- reason_critical: yes
- invalidity_date: 20191012000000Z
- - serial_number: 1234
- revocation_date: 20191001000000Z
- check_mode: yes
- register: crl_1_check
-- name: Create CRL 1
- x509_crl:
- path: '{{ output_dir }}/ca-crl1.crl'
- privatekey_path: '{{ output_dir }}/ca.key'
- issuer:
- CN: Ansible
- last_update: 20191013000000Z
- next_update: 20191113000000Z
- revoked_certificates:
- - path: '{{ output_dir }}/cert-1.pem'
- revocation_date: 20191013000000Z
- - path: '{{ output_dir }}/cert-2.pem'
- revocation_date: 20191013000000Z
- reason: key_compromise
- reason_critical: yes
- invalidity_date: 20191012000000Z
- - serial_number: 1234
- revocation_date: 20191001000000Z
- register: crl_1
-- name: Retrieve CRL 1 infos
- x509_crl_info:
- path: '{{ output_dir }}/ca-crl1.crl'
- register: crl_1_info_1
-- name: Retrieve CRL 1 infos via file content
- x509_crl_info:
- content: '{{ lookup("file", output_dir ~ "/ca-crl1.crl") }}'
- register: crl_1_info_2
-- name: Create CRL 1 (idempotent, check mode)
- x509_crl:
- path: '{{ output_dir }}/ca-crl1.crl'
- privatekey_path: '{{ output_dir }}/ca.key'
- issuer:
- CN: Ansible
- last_update: 20191013000000Z
- next_update: 20191113000000Z
- revoked_certificates:
- - path: '{{ output_dir }}/cert-1.pem'
- revocation_date: 20191013000000Z
- - path: '{{ output_dir }}/cert-2.pem'
- revocation_date: 20191013000000Z
- reason: key_compromise
- reason_critical: yes
- invalidity_date: 20191012000000Z
- - serial_number: 1234
- revocation_date: 20191001000000Z
- check_mode: yes
- register: crl_1_idem_check
-- name: Create CRL 1 (idempotent)
- x509_crl:
- path: '{{ output_dir }}/ca-crl1.crl'
- privatekey_path: '{{ output_dir }}/ca.key'
- issuer:
- CN: Ansible
- last_update: 20191013000000Z
- next_update: 20191113000000Z
- revoked_certificates:
- - path: '{{ output_dir }}/cert-1.pem'
- revocation_date: 20191013000000Z
- - path: '{{ output_dir }}/cert-2.pem'
- revocation_date: 20191013000000Z
- reason: key_compromise
- reason_critical: yes
- invalidity_date: 20191012000000Z
- - serial_number: 1234
- revocation_date: 20191001000000Z
- register: crl_1_idem
-- name: Create CRL 1 (idempotent with content, check mode)
- x509_crl:
- path: '{{ output_dir }}/ca-crl1.crl'
- privatekey_content: "{{ lookup('file', output_dir ~ '/ca.key') }}"
- issuer:
- CN: Ansible
- last_update: 20191013000000Z
- next_update: 20191113000000Z
- revoked_certificates:
- - content: "{{ lookup('file', output_dir ~ '/cert-1.pem') }}"
- revocation_date: 20191013000000Z
- - content: "{{ lookup('file', output_dir ~ '/cert-2.pem') }}"
- revocation_date: 20191013000000Z
- reason: key_compromise
- reason_critical: yes
- invalidity_date: 20191012000000Z
- - serial_number: 1234
- revocation_date: 20191001000000Z
- check_mode: yes
- register: crl_1_idem_content_check
-- name: Create CRL 1 (idempotent with content)
- x509_crl:
- path: '{{ output_dir }}/ca-crl1.crl'
- privatekey_content: "{{ lookup('file', output_dir ~ '/ca.key') }}"
- issuer:
- CN: Ansible
- last_update: 20191013000000Z
- next_update: 20191113000000Z
- revoked_certificates:
- - content: "{{ lookup('file', output_dir ~ '/cert-1.pem') }}"
- revocation_date: 20191013000000Z
- - content: "{{ lookup('file', output_dir ~ '/cert-2.pem') }}"
- revocation_date: 20191013000000Z
- reason: key_compromise
- reason_critical: yes
- invalidity_date: 20191012000000Z
- - serial_number: 1234
- revocation_date: 20191001000000Z
- register: crl_1_idem_content
-
-- name: Create CRL 2 (check mode)
- x509_crl:
- path: '{{ output_dir }}/ca-crl2.crl'
- privatekey_path: '{{ output_dir }}/ca.key'
- issuer:
- CN: Ansible
- last_update: +0d
- next_update: +0d
- revoked_certificates:
- - path: '{{ output_dir }}/cert-1.pem'
- - path: '{{ output_dir }}/cert-2.pem'
- reason: key_compromise
- reason_critical: yes
- invalidity_date: 20191012000000Z
- - serial_number: 1234
- check_mode: yes
- register: crl_2_check
-- name: Create CRL 2
- x509_crl:
- path: '{{ output_dir }}/ca-crl2.crl'
- privatekey_path: '{{ output_dir }}/ca.key'
- issuer:
- CN: Ansible
- last_update: +0d
- next_update: +0d
- revoked_certificates:
- - path: '{{ output_dir }}/cert-1.pem'
- - path: '{{ output_dir }}/cert-2.pem'
- reason: key_compromise
- reason_critical: yes
- invalidity_date: 20191012000000Z
- - serial_number: 1234
- register: crl_2
-- name: Create CRL 2 (idempotent, check mode)
- x509_crl:
- path: '{{ output_dir }}/ca-crl2.crl'
- privatekey_path: '{{ output_dir }}/ca.key'
- issuer:
- CN: Ansible
- last_update: +0d
- next_update: +0d
- revoked_certificates:
- - path: '{{ output_dir }}/cert-1.pem'
- - path: '{{ output_dir }}/cert-2.pem'
- reason: key_compromise
- reason_critical: yes
- invalidity_date: 20191012000000Z
- - serial_number: 1234
- ignore_timestamps: yes
- check_mode: yes
- register: crl_2_idem_check
-- name: Create CRL 2 (idempotent)
- x509_crl:
- path: '{{ output_dir }}/ca-crl2.crl'
- privatekey_path: '{{ output_dir }}/ca.key'
- issuer:
- CN: Ansible
- last_update: +0d
- next_update: +0d
- revoked_certificates:
- - path: '{{ output_dir }}/cert-1.pem'
- - path: '{{ output_dir }}/cert-2.pem'
- reason: key_compromise
- reason_critical: yes
- invalidity_date: 20191012000000Z
- - serial_number: 1234
- ignore_timestamps: yes
- register: crl_2_idem
-- name: Create CRL 2 (idempotent update, check mode)
- x509_crl:
- path: '{{ output_dir }}/ca-crl2.crl'
- privatekey_path: '{{ output_dir }}/ca.key'
- issuer:
- CN: Ansible
- last_update: +0d
- next_update: +0d
- revoked_certificates:
- - serial_number: 1235
- ignore_timestamps: yes
- mode: update
- check_mode: yes
- register: crl_2_idem_update_change_check
-- name: Create CRL 2 (idempotent update)
- x509_crl:
- path: '{{ output_dir }}/ca-crl2.crl'
- privatekey_path: '{{ output_dir }}/ca.key'
- issuer:
- CN: Ansible
- last_update: +0d
- next_update: +0d
- revoked_certificates:
- - serial_number: 1235
- ignore_timestamps: yes
- mode: update
- register: crl_2_idem_update_change
-- name: Create CRL 2 (idempotent update, check mode)
- x509_crl:
- path: '{{ output_dir }}/ca-crl2.crl'
- privatekey_path: '{{ output_dir }}/ca.key'
- issuer:
- CN: Ansible
- last_update: +0d
- next_update: +0d
- revoked_certificates:
- - path: '{{ output_dir }}/cert-2.pem'
- reason: key_compromise
- reason_critical: yes
- invalidity_date: 20191012000000Z
- ignore_timestamps: yes
- mode: update
- check_mode: yes
- register: crl_2_idem_update_check
-- name: Create CRL 2 (idempotent update)
- x509_crl:
- path: '{{ output_dir }}/ca-crl2.crl'
- privatekey_path: '{{ output_dir }}/ca.key'
- issuer:
- CN: Ansible
- last_update: +0d
- next_update: +0d
- revoked_certificates:
- - path: '{{ output_dir }}/cert-2.pem'
- reason: key_compromise
- reason_critical: yes
- invalidity_date: 20191012000000Z
- ignore_timestamps: yes
- mode: update
- register: crl_2_idem_update
-- name: Create CRL 2 (changed timestamps, check mode)
- x509_crl:
- path: '{{ output_dir }}/ca-crl2.crl'
- privatekey_path: '{{ output_dir }}/ca.key'
- issuer:
- CN: Ansible
- last_update: +0d
- next_update: +0d
- revoked_certificates:
- - path: '{{ output_dir }}/cert-2.pem'
- reason: key_compromise
- reason_critical: yes
- invalidity_date: 20191012000000Z
- ignore_timestamps: no
- mode: update
- check_mode: yes
- register: crl_2_change_check
-- name: Create CRL 2 (changed timestamps)
- x509_crl:
- path: '{{ output_dir }}/ca-crl2.crl'
- privatekey_path: '{{ output_dir }}/ca.key'
- issuer:
- CN: Ansible
- last_update: +0d
- next_update: +0d
- revoked_certificates:
- - path: '{{ output_dir }}/cert-2.pem'
- reason: key_compromise
- reason_critical: yes
- invalidity_date: 20191012000000Z
- ignore_timestamps: no
- mode: update
- return_content: yes
- register: crl_2_change
diff --git a/test/integration/targets/x509_crl/tasks/main.yml b/test/integration/targets/x509_crl/tasks/main.yml
deleted file mode 100644
index 1f82ff9e1b..0000000000
--- a/test/integration/targets/x509_crl/tasks/main.yml
+++ /dev/null
@@ -1,83 +0,0 @@
----
-- set_fact:
- certificates:
- - name: ca
- subject:
- commonName: Ansible
- is_ca: yes
- - name: ca-2
- subject:
- commonName: Ansible Other CA
- is_ca: yes
- - name: cert-1
- subject_alt_name:
- - DNS:ansible.com
- - name: cert-2
- subject_alt_name:
- - DNS:example.com
- - name: cert-3
- subject_alt_name:
- - DNS:example.org
- - IP:1.2.3.4
- - name: cert-4
- subject_alt_name:
- - DNS:test.ansible.com
- - DNS:b64.ansible.com
-
-- name: Generate private keys
- openssl_privatekey:
- path: '{{ output_dir }}/{{ item.name }}.key'
- type: ECC
- curve: secp256r1
- loop: "{{ certificates }}"
-
-- name: Generate CSRs
- openssl_csr:
- path: '{{ output_dir }}/{{ item.name }}.csr'
- privatekey_path: '{{ output_dir }}/{{ item.name }}.key'
- subject: "{{ item.subject | default(omit) }}"
- subject_alt_name: "{{ item.subject_alt_name | default(omit) }}"
- basic_constraints: "{{ 'CA:TRUE' if item.is_ca | default(false) else omit }}"
- use_common_name_for_san: no
- loop: "{{ certificates }}"
-
-- name: Generate CA certificates
- openssl_certificate:
- path: '{{ output_dir }}/{{ item.name }}.pem'
- csr_path: '{{ output_dir }}/{{ item.name }}.csr'
- privatekey_path: '{{ output_dir }}/{{ item.name }}.key'
- provider: selfsigned
- loop: "{{ certificates }}"
- when: item.is_ca | default(false)
-
-- name: Generate other certificates
- openssl_certificate:
- path: '{{ output_dir }}/{{ item.name }}.pem'
- csr_path: '{{ output_dir }}/{{ item.name }}.csr'
- provider: ownca
- ownca_path: '{{ output_dir }}/ca.pem'
- ownca_privatekey_path: '{{ output_dir }}/ca.key'
- loop: "{{ certificates }}"
- when: not (item.is_ca | default(false))
-
-- name: Get certificate infos
- openssl_certificate_info:
- path: '{{ output_dir }}/{{ item }}.pem'
- loop:
- - cert-1
- - cert-2
- - cert-3
- - cert-4
- register: certificate_infos
-
-- block:
- - name: Running tests with cryptography backend
- include_tasks: impl.yml
- vars:
- select_crypto_backend: cryptography
-
- - import_tasks: ../tests/validate.yml
- vars:
- select_crypto_backend: cryptography
-
- when: cryptography_version.stdout is version('1.2', '>=')
diff --git a/test/integration/targets/x509_crl/tests/validate.yml b/test/integration/targets/x509_crl/tests/validate.yml
deleted file mode 100644
index 17b31f34ad..0000000000
--- a/test/integration/targets/x509_crl/tests/validate.yml
+++ /dev/null
@@ -1,61 +0,0 @@
----
-- name: Validate CRL 1
- assert:
- that:
- - crl_1_check is changed
- - crl_1 is changed
- - crl_1_idem_check is not changed
- - crl_1_idem is not changed
- - crl_1_idem_content_check is not changed
- - crl_1_idem_content is not changed
-
-- name: Validate CRL 1 info
- assert:
- that:
- - crl_1_info_1 == crl_1_info_2
- - crl_1_info_1.digest == 'ecdsa-with-SHA256'
- - crl_1_info_1.issuer | length == 1
- - crl_1_info_1.issuer.commonName == 'Ansible'
- - crl_1_info_1.issuer_ordered | length == 1
- - crl_1_info_1.last_update == '20191013000000Z'
- - crl_1_info_1.next_update == '20191113000000Z'
- - crl_1_info_1.revoked_certificates | length == 3
- - crl_1_info_1.revoked_certificates[0].invalidity_date is none
- - crl_1_info_1.revoked_certificates[0].invalidity_date_critical == false
- - crl_1_info_1.revoked_certificates[0].issuer is none
- - crl_1_info_1.revoked_certificates[0].issuer_critical == false
- - crl_1_info_1.revoked_certificates[0].reason is none
- - crl_1_info_1.revoked_certificates[0].reason_critical == false
- - crl_1_info_1.revoked_certificates[0].revocation_date == '20191013000000Z'
- - crl_1_info_1.revoked_certificates[0].serial_number == certificate_infos.results[0].serial_number
- - crl_1_info_1.revoked_certificates[1].invalidity_date == '20191012000000Z'
- - crl_1_info_1.revoked_certificates[1].invalidity_date_critical == false
- - crl_1_info_1.revoked_certificates[1].issuer is none
- - crl_1_info_1.revoked_certificates[1].issuer_critical == false
- - crl_1_info_1.revoked_certificates[1].reason == 'key_compromise'
- - crl_1_info_1.revoked_certificates[1].reason_critical == true
- - crl_1_info_1.revoked_certificates[1].revocation_date == '20191013000000Z'
- - crl_1_info_1.revoked_certificates[1].serial_number == certificate_infos.results[1].serial_number
- - crl_1_info_1.revoked_certificates[2].invalidity_date is none
- - crl_1_info_1.revoked_certificates[2].invalidity_date_critical == false
- - crl_1_info_1.revoked_certificates[2].issuer is none
- - crl_1_info_1.revoked_certificates[2].issuer_critical == false
- - crl_1_info_1.revoked_certificates[2].reason is none
- - crl_1_info_1.revoked_certificates[2].reason_critical == false
- - crl_1_info_1.revoked_certificates[2].revocation_date == '20191001000000Z'
- - crl_1_info_1.revoked_certificates[2].serial_number == 1234
-
-- name: Validate CRL 2
- assert:
- that:
- - crl_2_check is changed
- - crl_2 is changed
- - crl_2_idem_check is not changed
- - crl_2_idem is not changed
- - crl_2_idem_update_change_check is changed
- - crl_2_idem_update_change is changed
- - crl_2_idem_update_check is not changed
- - crl_2_idem_update is not changed
- - crl_2_change_check is changed
- - crl_2_change is changed
- - crl_2_change.crl == lookup('file', output_dir ~ '/ca-crl2.crl', rstrip=False)
diff --git a/test/sanity/ignore.txt b/test/sanity/ignore.txt
index e545d49f7b..441c0fbb37 100644
--- a/test/sanity/ignore.txt
+++ b/test/sanity/ignore.txt
@@ -1679,8 +1679,6 @@ lib/ansible/modules/commands/command.py validate-modules:nonexistent-parameter-d
lib/ansible/modules/commands/command.py validate-modules:parameter-list-no-elements
lib/ansible/modules/commands/command.py validate-modules:undocumented-parameter
lib/ansible/modules/commands/expect.py validate-modules:doc-missing-type
-lib/ansible/modules/crypto/acme/acme_account_info.py validate-modules:return-syntax-error
-lib/ansible/modules/crypto/acme/acme_certificate.py validate-modules:doc-elements-mismatch
lib/ansible/modules/database/mongodb/mongodb_parameter.py use-argspec-type-path
lib/ansible/modules/database/mongodb/mongodb_replicaset.py use-argspec-type-path
lib/ansible/modules/database/mongodb/mongodb_shard.py use-argspec-type-path
diff --git a/test/units/module_utils/acme/fixtures/cert_1.pem b/test/units/module_utils/acme/fixtures/cert_1.pem
deleted file mode 100644
index bb4aca5197..0000000000
--- a/test/units/module_utils/acme/fixtures/cert_1.pem
+++ /dev/null
@@ -1,11 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIBljCCATugAwIBAgIBATAKBggqhkjOPQQDAjAWMRQwEgYDVQQDEwthbnNpYmxl
-LmNvbTAeFw0xODExMjUxNTI4MjNaFw0xODExMjYxNTI4MjRaMBYxFDASBgNVBAMT
-C2Fuc2libGUuY29tMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEAJz0yAAXAwEm
-OhTRkjXxwgedbWO6gobYM3lWszrS68G8QSzhXR6AmQ3IzZDimnTTXO7XhVylDT8S
-LzE44/Epm6N6MHgwIwYDVR0RBBwwGoILZXhhbXBsZS5jb22CC2V4YW1wbGUub3Jn
-MAwGA1UdEwEB/wQCMAAwDwYDVR0PAQH/BAUDAweAADATBgNVHSUEDDAKBggrBgEF
-BQcDATAdBgNVHQ4EFgQUmNL9PMzNaUX74owwLFRiGDS3B3MwCgYIKoZIzj0EAwID
-SQAwRgIhALz7Ur96ky0OfM5D9MwFmCg2jccqm/UglGI9+4KeOEIyAiEAwFX4tdll
-QSrd1HY/jMsHwdK5wH3JkK/9+fGwyRP11VI=
------END CERTIFICATE-----
diff --git a/test/units/module_utils/acme/fixtures/csr_1.pem b/test/units/module_utils/acme/fixtures/csr_1.pem
deleted file mode 100644
index 8fc37c40c6..0000000000
--- a/test/units/module_utils/acme/fixtures/csr_1.pem
+++ /dev/null
@@ -1,9 +0,0 @@
------BEGIN NEW CERTIFICATE REQUEST-----
-MIIBJTCBzQIBADAWMRQwEgYDVQQDEwthbnNpYmxlLmNvbTBZMBMGByqGSM49AgEG
-CCqGSM49AwEHA0IABACc9MgAFwMBJjoU0ZI18cIHnW1juoKG2DN5VrM60uvBvEEs
-4V0egJkNyM2Q4pp001zu14VcpQ0/Ei8xOOPxKZugVTBTBgkqhkiG9w0BCQ4xRjBE
-MCMGA1UdEQQcMBqCC2V4YW1wbGUuY29tggtleGFtcGxlLm9yZzAMBgNVHRMBAf8E
-AjAAMA8GA1UdDwEB/wQFAwMHgAAwCgYIKoZIzj0EAwIDRwAwRAIgcDyoRmwFVBDl
-FvbFZtiSd5wmJU1ltM6JtcfnLWnjY54CICruOByrropFUkOKKb4xXOYsgaDT93Wr
-URnCJfTLr2T3
------END NEW CERTIFICATE REQUEST-----
diff --git a/test/units/module_utils/acme/fixtures/csr_1.txt b/test/units/module_utils/acme/fixtures/csr_1.txt
deleted file mode 100644
index 37c5cbda72..0000000000
--- a/test/units/module_utils/acme/fixtures/csr_1.txt
+++ /dev/null
@@ -1,28 +0,0 @@
-Certificate Request:
- Data:
- Version: 1 (0x0)
- Subject: CN = ansible.com
- Subject Public Key Info:
- Public Key Algorithm: id-ecPublicKey
- Public-Key: (256 bit)
- pub:
- 04:00:9c:f4:c8:00:17:03:01:26:3a:14:d1:92:35:
- f1:c2:07:9d:6d:63:ba:82:86:d8:33:79:56:b3:3a:
- d2:eb:c1:bc:41:2c:e1:5d:1e:80:99:0d:c8:cd:90:
- e2:9a:74:d3:5c:ee:d7:85:5c:a5:0d:3f:12:2f:31:
- 38:e3:f1:29:9b
- ASN1 OID: prime256v1
- NIST CURVE: P-256
- Attributes:
- Requested Extensions:
- X509v3 Subject Alternative Name:
- DNS:example.com, DNS:example.org
- X509v3 Basic Constraints: critical
- CA:FALSE
- X509v3 Key Usage: critical
- Digital Signature
- Signature Algorithm: ecdsa-with-SHA256
- 30:44:02:20:70:3c:a8:46:6c:05:54:10:e5:16:f6:c5:66:d8:
- 92:77:9c:26:25:4d:65:b4:ce:89:b5:c7:e7:2d:69:e3:63:9e:
- 02:20:2a:ee:38:1c:ab:ae:8a:45:52:43:8a:29:be:31:5c:e6:
- 2c:81:a0:d3:f7:75:ab:51:19:c2:25:f4:cb:af:64:f7
diff --git a/test/units/module_utils/acme/fixtures/csr_2.pem b/test/units/module_utils/acme/fixtures/csr_2.pem
deleted file mode 100644
index 295a26e1cf..0000000000
--- a/test/units/module_utils/acme/fixtures/csr_2.pem
+++ /dev/null
@@ -1,27 +0,0 @@
------BEGIN CERTIFICATE REQUEST-----
-MIIEqjCCApICAQAwADCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANv1
-V7gDsh76O//d9wclBcW6kNpWeR6eAggzThwbMZjcO7GFHQsBZCZGGVdyS37uhejc
-RrIBdtDDWXhoh3Dz+GQxD+6GuwAEFyL1F3MfT0v1HHoO8fE74G5mD6+ZA2HRDeU9
-jf8BPyVWHBtNbCmJGSlSNOFejWCmwvsLARQxqFBuTyRjgos4BkLyWMqZRukrzO1P
-z7IBhuFrB608t+AG4vGnPXZNM7xefhzO8bPOiepT0YS2ERPkFmOy97SnwTGdKykw
-ZYM9oKukYhE4Z+yOaTFpJMBNXwDCI5TMnhtc6eJrf5sOFH92n2E9+YWMoahUOiTw
-G6XV5HfSpySpwORUaTITQRsPAM+bmK9f1jB6ctfFVwpa8uW/h8pSgbHgZvkeD6s6
-rFLh9TQ24t0vrRmhnY7/AMFgbgJoBTBq0l0lEXS4FCGKDGqQOqSws+eHR/pHA4uY
-v8d498SQl9fYsT/c7Uj3/JnMSRVN942yQUFCzwLf0/WzWCi2HTqPM8CPh5ryiJ30
-GAN2eb026/noyTOXm479Tg9o86Tw9qczE0j0CdcRnr6J337RGHQg58PZ7j+hnUmK
-wgyclyvjE10ZFBgToMGSnzYp5UeRcOFZ3bnK6LOsGC75mIvz2OQgSQeO5VQASEnO
-9uhygNyo91sK4BtVroloit8ZCa82LlsHSCj/mMzPAgMBAAGgZTBjBgkqhkiG9w0B
-CQ4xVjBUMFIGA1UdEQRLMEmCC2Fuc2libGUuY29thwR/AAABhxAAAAAAAAAAAAAA
-AAAAAAABhxAgAQ2IrBD+AQAAAAAAAAAAhxAgARI0VnirzZh2VDIQ/ty6MA0GCSqG
-SIb3DQEBCwUAA4ICAQBFRuANzVRcze+iur0YevjtYIXDa03GoWWkgnLuE8u8epTM
-2248duG3TmvVvxWPN4iFrvFcZIvNsevBo+Z7kXJ24m3YldtXvwfAYmCZ062apSoh
-yzgo3Q0KfDehwLcoJPe5bh+jbbgJVGGvJug/QFyHSVl+iGyFUXE7pwafl9LuNDi3
-yfOYZLIQ34mBH4Rsvymj9xSTYliWDEEU/o7RrrZeEqkOxNeLh64LbnifdrYUputz
-yBURg2xs9hpAsytZJX90iJW8aYPM1aQ7eetqTViIRoqUAmIQobnKlNnpOliBHl+p
-RY+AtTnsfAetKUP7OsAZkHRTGAXx0JHJQ1ITY8w5Dcw/v1bDCbAfkDubBP3X+us9
-RQk2h6m74hWFFNu9xOfkNejPf7h4gywfDjo/wGZFSWKyi6avB9V53znZgRUwc009
-p5MM9e37MH8pyBqfnbSwOj4hUoyecRCIAFdywjMb9akP2u15XP3MOtJOEvecyCxN
-TZBxupTg65zB47GeSAufnc8FaTZkE8xPuCtbvqOVOkWYqzlqNdCfK8f3AZdlpwLh
-38wdUm5G7LIu6aQNiY66aQs9qVpoGvqdmxHRkuSwqwZxGgzcY1yJaWGXQ6R4jgC3
-VKlMTUVs1WYV6jrYLHcVt6Rn/2FVTOns3Jn6cTPOdKViYoqF+yW8yCEAqAskZw==
------END CERTIFICATE REQUEST-----
diff --git a/test/units/module_utils/acme/fixtures/csr_2.txt b/test/units/module_utils/acme/fixtures/csr_2.txt
deleted file mode 100644
index 7a54ee3fa2..0000000000
--- a/test/units/module_utils/acme/fixtures/csr_2.txt
+++ /dev/null
@@ -1,78 +0,0 @@
-Certificate Request:
- Data:
- Version: 1 (0x0)
- Subject:
- Subject Public Key Info:
- Public Key Algorithm: rsaEncryption
- RSA Public-Key: (4096 bit)
- Modulus:
- 00:db:f5:57:b8:03:b2:1e:fa:3b:ff:dd:f7:07:25:
- 05:c5:ba:90:da:56:79:1e:9e:02:08:33:4e:1c:1b:
- 31:98:dc:3b:b1:85:1d:0b:01:64:26:46:19:57:72:
- 4b:7e:ee:85:e8:dc:46:b2:01:76:d0:c3:59:78:68:
- 87:70:f3:f8:64:31:0f:ee:86:bb:00:04:17:22:f5:
- 17:73:1f:4f:4b:f5:1c:7a:0e:f1:f1:3b:e0:6e:66:
- 0f:af:99:03:61:d1:0d:e5:3d:8d:ff:01:3f:25:56:
- 1c:1b:4d:6c:29:89:19:29:52:34:e1:5e:8d:60:a6:
- c2:fb:0b:01:14:31:a8:50:6e:4f:24:63:82:8b:38:
- 06:42:f2:58:ca:99:46:e9:2b:cc:ed:4f:cf:b2:01:
- 86:e1:6b:07:ad:3c:b7:e0:06:e2:f1:a7:3d:76:4d:
- 33:bc:5e:7e:1c:ce:f1:b3:ce:89:ea:53:d1:84:b6:
- 11:13:e4:16:63:b2:f7:b4:a7:c1:31:9d:2b:29:30:
- 65:83:3d:a0:ab:a4:62:11:38:67:ec:8e:69:31:69:
- 24:c0:4d:5f:00:c2:23:94:cc:9e:1b:5c:e9:e2:6b:
- 7f:9b:0e:14:7f:76:9f:61:3d:f9:85:8c:a1:a8:54:
- 3a:24:f0:1b:a5:d5:e4:77:d2:a7:24:a9:c0:e4:54:
- 69:32:13:41:1b:0f:00:cf:9b:98:af:5f:d6:30:7a:
- 72:d7:c5:57:0a:5a:f2:e5:bf:87:ca:52:81:b1:e0:
- 66:f9:1e:0f:ab:3a:ac:52:e1:f5:34:36:e2:dd:2f:
- ad:19:a1:9d:8e:ff:00:c1:60:6e:02:68:05:30:6a:
- d2:5d:25:11:74:b8:14:21:8a:0c:6a:90:3a:a4:b0:
- b3:e7:87:47:fa:47:03:8b:98:bf:c7:78:f7:c4:90:
- 97:d7:d8:b1:3f:dc:ed:48:f7:fc:99:cc:49:15:4d:
- f7:8d:b2:41:41:42:cf:02:df:d3:f5:b3:58:28:b6:
- 1d:3a:8f:33:c0:8f:87:9a:f2:88:9d:f4:18:03:76:
- 79:bd:36:eb:f9:e8:c9:33:97:9b:8e:fd:4e:0f:68:
- f3:a4:f0:f6:a7:33:13:48:f4:09:d7:11:9e:be:89:
- df:7e:d1:18:74:20:e7:c3:d9:ee:3f:a1:9d:49:8a:
- c2:0c:9c:97:2b:e3:13:5d:19:14:18:13:a0:c1:92:
- 9f:36:29:e5:47:91:70:e1:59:dd:b9:ca:e8:b3:ac:
- 18:2e:f9:98:8b:f3:d8:e4:20:49:07:8e:e5:54:00:
- 48:49:ce:f6:e8:72:80:dc:a8:f7:5b:0a:e0:1b:55:
- ae:89:68:8a:df:19:09:af:36:2e:5b:07:48:28:ff:
- 98:cc:cf
- Exponent: 65537 (0x10001)
- Attributes:
- Requested Extensions:
- X509v3 Subject Alternative Name:
- DNS:ansible.com, IP Address:127.0.0.1, IP Address:0:0:0:0:0:0:0:1, IP Address:2001:D88:AC10:FE01:0:0:0:0, IP Address:2001:1234:5678:ABCD:9876:5432:10FE:DCBA
- Signature Algorithm: sha256WithRSAEncryption
- 45:46:e0:0d:cd:54:5c:cd:ef:a2:ba:bd:18:7a:f8:ed:60:85:
- c3:6b:4d:c6:a1:65:a4:82:72:ee:13:cb:bc:7a:94:cc:db:6e:
- 3c:76:e1:b7:4e:6b:d5:bf:15:8f:37:88:85:ae:f1:5c:64:8b:
- cd:b1:eb:c1:a3:e6:7b:91:72:76:e2:6d:d8:95:db:57:bf:07:
- c0:62:60:99:d3:ad:9a:a5:2a:21:cb:38:28:dd:0d:0a:7c:37:
- a1:c0:b7:28:24:f7:b9:6e:1f:a3:6d:b8:09:54:61:af:26:e8:
- 3f:40:5c:87:49:59:7e:88:6c:85:51:71:3b:a7:06:9f:97:d2:
- ee:34:38:b7:c9:f3:98:64:b2:10:df:89:81:1f:84:6c:bf:29:
- a3:f7:14:93:62:58:96:0c:41:14:fe:8e:d1:ae:b6:5e:12:a9:
- 0e:c4:d7:8b:87:ae:0b:6e:78:9f:76:b6:14:a6:eb:73:c8:15:
- 11:83:6c:6c:f6:1a:40:b3:2b:59:25:7f:74:88:95:bc:69:83:
- cc:d5:a4:3b:79:eb:6a:4d:58:88:46:8a:94:02:62:10:a1:b9:
- ca:94:d9:e9:3a:58:81:1e:5f:a9:45:8f:80:b5:39:ec:7c:07:
- ad:29:43:fb:3a:c0:19:90:74:53:18:05:f1:d0:91:c9:43:52:
- 13:63:cc:39:0d:cc:3f:bf:56:c3:09:b0:1f:90:3b:9b:04:fd:
- d7:fa:eb:3d:45:09:36:87:a9:bb:e2:15:85:14:db:bd:c4:e7:
- e4:35:e8:cf:7f:b8:78:83:2c:1f:0e:3a:3f:c0:66:45:49:62:
- b2:8b:a6:af:07:d5:79:df:39:d9:81:15:30:73:4d:3d:a7:93:
- 0c:f5:ed:fb:30:7f:29:c8:1a:9f:9d:b4:b0:3a:3e:21:52:8c:
- 9e:71:10:88:00:57:72:c2:33:1b:f5:a9:0f:da:ed:79:5c:fd:
- cc:3a:d2:4e:12:f7:9c:c8:2c:4d:4d:90:71:ba:94:e0:eb:9c:
- c1:e3:b1:9e:48:0b:9f:9d:cf:05:69:36:64:13:cc:4f:b8:2b:
- 5b:be:a3:95:3a:45:98:ab:39:6a:35:d0:9f:2b:c7:f7:01:97:
- 65:a7:02:e1:df:cc:1d:52:6e:46:ec:b2:2e:e9:a4:0d:89:8e:
- ba:69:0b:3d:a9:5a:68:1a:fa:9d:9b:11:d1:92:e4:b0:ab:06:
- 71:1a:0c:dc:63:5c:89:69:61:97:43:a4:78:8e:00:b7:54:a9:
- 4c:4d:45:6c:d5:66:15:ea:3a:d8:2c:77:15:b7:a4:67:ff:61:
- 55:4c:e9:ec:dc:99:fa:71:33:ce:74:a5:62:62:8a:85:fb:25:
- bc:c8:21:00:a8:0b:24:67
diff --git a/test/units/module_utils/acme/fixtures/privatekey_1.pem b/test/units/module_utils/acme/fixtures/privatekey_1.pem
deleted file mode 100644
index 97209eda71..0000000000
--- a/test/units/module_utils/acme/fixtures/privatekey_1.pem
+++ /dev/null
@@ -1,5 +0,0 @@
------BEGIN EC PRIVATE KEY-----
-MHcCAQEEIDWajU0PyhYKeulfy/luNtkAve7DkwQ01bXJ97zbxB66oAoGCCqGSM49
-AwEHoUQDQgAEAJz0yAAXAwEmOhTRkjXxwgedbWO6gobYM3lWszrS68G8QSzhXR6A
-mQ3IzZDimnTTXO7XhVylDT8SLzE44/Epmw==
------END EC PRIVATE KEY-----
diff --git a/test/units/module_utils/acme/fixtures/privatekey_1.txt b/test/units/module_utils/acme/fixtures/privatekey_1.txt
deleted file mode 100644
index e25cfd4516..0000000000
--- a/test/units/module_utils/acme/fixtures/privatekey_1.txt
+++ /dev/null
@@ -1,14 +0,0 @@
-read EC key
-Private-Key: (256 bit)
-priv:
- 35:9a:8d:4d:0f:ca:16:0a:7a:e9:5f:cb:f9:6e:36:
- d9:00:bd:ee:c3:93:04:34:d5:b5:c9:f7:bc:db:c4:
- 1e:ba
-pub:
- 04:00:9c:f4:c8:00:17:03:01:26:3a:14:d1:92:35:
- f1:c2:07:9d:6d:63:ba:82:86:d8:33:79:56:b3:3a:
- d2:eb:c1:bc:41:2c:e1:5d:1e:80:99:0d:c8:cd:90:
- e2:9a:74:d3:5c:ee:d7:85:5c:a5:0d:3f:12:2f:31:
- 38:e3:f1:29:9b
-ASN1 OID: prime256v1
-NIST CURVE: P-256
diff --git a/test/units/module_utils/acme/test_acme.py b/test/units/module_utils/acme/test_acme.py
deleted file mode 100644
index 6db2eb6e55..0000000000
--- a/test/units/module_utils/acme/test_acme.py
+++ /dev/null
@@ -1,216 +0,0 @@
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-import base64
-import datetime
-import os.path
-import pytest
-
-from mock import MagicMock
-
-
-from ansible.module_utils.acme import (
- HAS_CURRENT_CRYPTOGRAPHY,
- nopad_b64,
- write_file,
- read_file,
- pem_to_der,
- _parse_key_openssl,
- # _sign_request_openssl,
- _parse_key_cryptography,
- # _sign_request_cryptography,
- _normalize_ip,
- openssl_get_csr_identifiers,
- cryptography_get_csr_identifiers,
- cryptography_get_cert_days,
-)
-
-
-def load_fixture(name):
- with open(os.path.join(os.path.dirname(__file__), 'fixtures', name)) as f:
- return f.read()
-
-
-################################################
-
-NOPAD_B64 = [
- ("", ""),
- ("\n", "Cg"),
- ("123", "MTIz"),
- ("Lorem?ipsum", "TG9yZW0_aXBzdW0"),
-]
-
-
-@pytest.mark.parametrize("value, result", NOPAD_B64)
-def test_nopad_b64(value, result):
- assert nopad_b64(value.encode('utf-8')) == result
-
-
-################################################
-
-TEST_TEXT = r"""1234
-5678"""
-
-
-def test_read_file(tmpdir):
- fn = tmpdir / 'test.txt'
- fn.write(TEST_TEXT)
- assert read_file(str(fn), 't') == TEST_TEXT
- assert read_file(str(fn), 'b') == TEST_TEXT.encode('utf-8')
-
-
-def test_write_file(tmpdir):
- fn = tmpdir / 'test.txt'
- module = MagicMock()
- write_file(module, str(fn), TEST_TEXT.encode('utf-8'))
- assert fn.read() == TEST_TEXT
-
-
-################################################
-
-TEST_PEM_DERS = [
- (
- load_fixture('privatekey_1.pem'),
- base64.b64decode('MHcCAQEEIDWajU0PyhYKeulfy/luNtkAve7DkwQ01bXJ97zbxB66oAo'
- 'GCCqGSM49AwEHoUQDQgAEAJz0yAAXAwEmOhTRkjXxwgedbWO6gobYM3'
- 'lWszrS68G8QSzhXR6AmQ3IzZDimnTTXO7XhVylDT8SLzE44/Epmw==')
- )
-]
-
-
-@pytest.mark.parametrize("pem, der", TEST_PEM_DERS)
-def test_pem_to_der(pem, der, tmpdir):
- fn = tmpdir / 'test.pem'
- fn.write(pem)
- assert pem_to_der(str(fn)) == der
-
-
-################################################
-
-TEST_KEYS = [
- (
- load_fixture('privatekey_1.pem'),
- {
- 'alg': 'ES256',
- 'hash': 'sha256',
- 'jwk': {
- 'crv': 'P-256',
- 'kty': 'EC',
- 'x': 'AJz0yAAXAwEmOhTRkjXxwgedbWO6gobYM3lWszrS68E',
- 'y': 'vEEs4V0egJkNyM2Q4pp001zu14VcpQ0_Ei8xOOPxKZs',
- },
- 'point_size': 32,
- 'type': 'ec',
- },
- load_fixture('privatekey_1.txt'),
- )
-]
-
-
-@pytest.mark.parametrize("pem, result, openssl_output", TEST_KEYS)
-def test_eckeyparse_openssl(pem, result, openssl_output, tmpdir):
- fn = tmpdir / 'test.key'
- fn.write(pem)
- module = MagicMock()
- module.run_command = MagicMock(return_value=(0, openssl_output, 0))
- error, key = _parse_key_openssl('openssl', module, key_file=str(fn))
- assert error is None
- key.pop('key_file')
- assert key == result
-
-
-if HAS_CURRENT_CRYPTOGRAPHY:
- @pytest.mark.parametrize("pem, result, dummy", TEST_KEYS)
- def test_eckeyparse_cryptography(pem, result, dummy):
- module = MagicMock()
- error, key = _parse_key_cryptography(module, key_content=pem)
- assert error is None
- key.pop('key_obj')
- assert key == result
-
-
-################################################
-
-TEST_IPS = [
- ("0:0:0:0:0:0:0:1", "::1"),
- ("1::0:2", "1::2"),
- ("0000:0001:0000:0000:0000:0000:0000:0001", "0:1::1"),
- ("0000:0001:0000:0000:0001:0000:0000:0001", "0:1::1:0:0:1"),
- ("0000:0001:0000:0001:0000:0001:0000:0001", "0:1:0:1:0:1:0:1"),
- ("0.0.0.0", "0.0.0.0"),
- ("000.001.000.000", "0.1.0.0"),
- ("2001:d88:ac10:fe01:0:0:0:0", "2001:d88:ac10:fe01::"),
- ("0000:0000:0000:0000:0000:0000:0000:0000", "::"),
-]
-
-
-@pytest.mark.parametrize("ip, result", TEST_IPS)
-def test_normalize_ip(ip, result):
- assert _normalize_ip(ip) == result
-
-
-################################################
-
-TEST_CSRS = [
- (
- load_fixture('csr_1.pem'),
- set([
- ('dns', 'ansible.com'),
- ('dns', 'example.com'),
- ('dns', 'example.org')
- ]),
- load_fixture('csr_1.txt'),
- ),
- (
- load_fixture('csr_2.pem'),
- set([
- ('dns', 'ansible.com'),
- ('ip', '127.0.0.1'),
- ('ip', '::1'),
- ('ip', '2001:d88:ac10:fe01::'),
- ('ip', '2001:1234:5678:abcd:9876:5432:10fe:dcba')
- ]),
- load_fixture('csr_2.txt'),
- ),
-]
-
-
-@pytest.mark.parametrize("csr, result, openssl_output", TEST_CSRS)
-def test_csridentifiers_openssl(csr, result, openssl_output, tmpdir):
- fn = tmpdir / 'test.csr'
- fn.write(csr)
- module = MagicMock()
- module.run_command = MagicMock(return_value=(0, openssl_output, 0))
- identifiers = openssl_get_csr_identifiers('openssl', module, str(fn))
- assert identifiers == result
-
-
-if HAS_CURRENT_CRYPTOGRAPHY:
- @pytest.mark.parametrize("csr, result, openssl_output", TEST_CSRS)
- def test_csridentifiers_cryptography(csr, result, openssl_output, tmpdir):
- fn = tmpdir / 'test.csr'
- fn.write(csr)
- module = MagicMock()
- identifiers = cryptography_get_csr_identifiers(module, str(fn))
- assert identifiers == result
-
-
-################################################
-
-TEST_CERT = load_fixture("cert_1.pem")
-
-TEST_CERT_DAYS = [
- (datetime.datetime(2018, 11, 15, 1, 2, 3), 11),
- (datetime.datetime(2018, 11, 25, 15, 20, 0), 1),
- (datetime.datetime(2018, 11, 25, 15, 30, 0), 0),
-]
-
-
-if HAS_CURRENT_CRYPTOGRAPHY:
- @pytest.mark.parametrize("now, expected_days", TEST_CERT_DAYS)
- def test_certdays_cryptography(now, expected_days, tmpdir):
- fn = tmpdir / 'test-cert.pem'
- fn.write(TEST_CERT)
- module = MagicMock()
- days = cryptography_get_cert_days(module, str(fn), now=now)
- assert days == expected_days
diff --git a/test/units/modules/crypto/test_luks_device.py b/test/units/modules/crypto/test_luks_device.py
deleted file mode 100644
index e6449814a0..0000000000
--- a/test/units/modules/crypto/test_luks_device.py
+++ /dev/null
@@ -1,305 +0,0 @@
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-import pytest
-from ansible.modules.crypto import luks_device
-from units.compat.mock import patch
-from ansible.module_utils import basic
-
-
-class DummyModule(object):
- # module to mock AnsibleModule class
- def __init__(self):
- self.params = dict()
-
- def fail_json(self, msg=""):
- raise ValueError(msg)
-
- def get_bin_path(self, command, dummy):
- return command
-
-
-# ===== Handler & CryptHandler methods tests =====
-
-def test_generate_luks_name(monkeypatch):
- module = DummyModule()
- monkeypatch.setattr(luks_device.Handler, "_run_command",
- lambda x, y: [0, "UUID", ""])
- crypt = luks_device.CryptHandler(module)
- assert crypt.generate_luks_name("/dev/dummy") == "luks-UUID"
-
-
-def test_get_container_name_by_device(monkeypatch):
- module = DummyModule()
- monkeypatch.setattr(luks_device.Handler, "_run_command",
- lambda x, y: [0, "crypt container_name", ""])
- crypt = luks_device.CryptHandler(module)
- assert crypt.get_container_name_by_device("/dev/dummy") == "container_name"
-
-
-def test_get_container_device_by_name(monkeypatch):
- module = DummyModule()
- monkeypatch.setattr(luks_device.Handler, "_run_command",
- lambda x, y: [0, "device: /dev/luksdevice", ""])
- crypt = luks_device.CryptHandler(module)
- assert crypt.get_container_device_by_name("dummy") == "/dev/luksdevice"
-
-
-def test_run_luks_remove(monkeypatch):
- def run_command_check(self, command):
- # check that wipefs command is actually called
- assert command[0] == "wipefs"
- return [0, "", ""]
-
- module = DummyModule()
- monkeypatch.setattr(luks_device.CryptHandler,
- "get_container_name_by_device",
- lambda x, y: None)
- monkeypatch.setattr(luks_device.Handler,
- "_run_command",
- run_command_check)
- crypt = luks_device.CryptHandler(module)
- crypt.run_luks_remove("dummy")
-
-
-# ===== ConditionsHandler methods data and tests =====
-
-# device, key, passphrase, state, is_luks, label, expected
-LUKS_CREATE_DATA = (
- ("dummy", "key", None, "present", False, None, True),
- (None, "key", None, "present", False, None, False),
- (None, "key", None, "present", False, "labelName", True),
- ("dummy", None, None, "present", False, None, False),
- ("dummy", "key", None, "absent", False, None, False),
- ("dummy", "key", None, "opened", True, None, False),
- ("dummy", "key", None, "closed", True, None, False),
- ("dummy", "key", None, "present", True, None, False),
- ("dummy", None, "foo", "present", False, None, True),
- (None, None, "bar", "present", False, None, False),
- (None, None, "baz", "present", False, "labelName", True),
- ("dummy", None, None, "present", False, None, False),
- ("dummy", None, "quz", "absent", False, None, False),
- ("dummy", None, "qux", "opened", True, None, False),
- ("dummy", None, "quux", "closed", True, None, False),
- ("dummy", None, "corge", "present", True, None, False))
-
-# device, state, is_luks, expected
-LUKS_REMOVE_DATA = (
- ("dummy", "absent", True, True),
- (None, "absent", True, False),
- ("dummy", "present", True, False),
- ("dummy", "absent", False, False))
-
-# device, key, passphrase, state, name, name_by_dev, expected
-LUKS_OPEN_DATA = (
- ("dummy", "key", None, "present", "name", None, False),
- ("dummy", "key", None, "absent", "name", None, False),
- ("dummy", "key", None, "closed", "name", None, False),
- ("dummy", "key", None, "opened", "name", None, True),
- (None, "key", None, "opened", "name", None, False),
- ("dummy", None, None, "opened", "name", None, False),
- ("dummy", "key", None, "opened", "name", "name", False),
- ("dummy", "key", None, "opened", "beer", "name", "exception"),
- ("dummy", None, "foo", "present", "name", None, False),
- ("dummy", None, "bar", "absent", "name", None, False),
- ("dummy", None, "baz", "closed", "name", None, False),
- ("dummy", None, "qux", "opened", "name", None, True),
- (None, None, "quux", "opened", "name", None, False),
- ("dummy", None, None, "opened", "name", None, False),
- ("dummy", None, "quuz", "opened", "name", "name", False),
- ("dummy", None, "corge", "opened", "beer", "name", "exception"))
-
-# device, dev_by_name, name, name_by_dev, state, label, expected
-LUKS_CLOSE_DATA = (
- ("dummy", "dummy", "name", "name", "present", None, False),
- ("dummy", "dummy", "name", "name", "absent", None, False),
- ("dummy", "dummy", "name", "name", "opened", None, False),
- ("dummy", "dummy", "name", "name", "closed", None, True),
- (None, "dummy", "name", "name", "closed", None, True),
- ("dummy", "dummy", None, "name", "closed", None, True),
- (None, "dummy", None, "name", "closed", None, False))
-
-# device, key, passphrase, new_key, new_passphrase, state, label, expected
-LUKS_ADD_KEY_DATA = (
- ("dummy", "key", None, "new_key", None, "present", None, True),
- (None, "key", None, "new_key", None, "present", "labelName", True),
- (None, "key", None, "new_key", None, "present", None, False),
- ("dummy", None, None, "new_key", None, "present", None, False),
- ("dummy", "key", None, None, None, "present", None, False),
- ("dummy", "key", None, "new_key", None, "absent", None, "exception"),
- ("dummy", None, "pass", "new_key", None, "present", None, True),
- (None, None, "pass", "new_key", None, "present", "labelName", True),
- ("dummy", "key", None, None, "new_pass", "present", None, True),
- (None, "key", None, None, "new_pass", "present", "labelName", True),
- (None, "key", None, None, "new_pass", "present", None, False),
- ("dummy", None, None, None, "new_pass", "present", None, False),
- ("dummy", "key", None, None, None, "present", None, False),
- ("dummy", "key", None, None, "new_pass", "absent", None, "exception"),
- ("dummy", None, "pass", None, "new_pass", "present", None, True),
- (None, None, "pass", None, "new_pass", "present", "labelName", True))
-
-# device, remove_key, remove_passphrase, state, label, expected
-LUKS_REMOVE_KEY_DATA = (
- ("dummy", "key", None, "present", None, True),
- (None, "key", None, "present", None, False),
- (None, "key", None, "present", "labelName", True),
- ("dummy", None, None, "present", None, False),
- ("dummy", "key", None, "absent", None, "exception"),
- ("dummy", None, "foo", "present", None, True),
- (None, None, "foo", "present", None, False),
- (None, None, "foo", "present", "labelName", True),
- ("dummy", None, None, "present", None, False),
- ("dummy", None, "foo", "absent", None, "exception"))
-
-
-@pytest.mark.parametrize("device, keyfile, passphrase, state, is_luks, " +
- "label, expected",
- ((d[0], d[1], d[2], d[3], d[4], d[5], d[6])
- for d in LUKS_CREATE_DATA))
-def test_luks_create(device, keyfile, passphrase, state, is_luks, label,
- expected, monkeypatch):
- module = DummyModule()
-
- module.params["device"] = device
- module.params["keyfile"] = keyfile
- module.params["passphrase"] = passphrase
- module.params["state"] = state
- module.params["label"] = label
-
- monkeypatch.setattr(luks_device.CryptHandler, "is_luks",
- lambda x, y: is_luks)
- crypt = luks_device.CryptHandler(module)
- if device is None:
- monkeypatch.setattr(luks_device.Handler, "get_device_by_label",
- lambda x, y: [0, "/dev/dummy", ""])
- try:
- conditions = luks_device.ConditionsHandler(module, crypt)
- assert conditions.luks_create() == expected
- except ValueError:
- assert expected == "exception"
-
-
-@pytest.mark.parametrize("device, state, is_luks, expected",
- ((d[0], d[1], d[2], d[3])
- for d in LUKS_REMOVE_DATA))
-def test_luks_remove(device, state, is_luks, expected, monkeypatch):
- module = DummyModule()
-
- module.params["device"] = device
- module.params["state"] = state
-
- monkeypatch.setattr(luks_device.CryptHandler, "is_luks",
- lambda x, y: is_luks)
- crypt = luks_device.CryptHandler(module)
- try:
- conditions = luks_device.ConditionsHandler(module, crypt)
- assert conditions.luks_remove() == expected
- except ValueError:
- assert expected == "exception"
-
-
-@pytest.mark.parametrize("device, keyfile, passphrase, state, name, "
- "name_by_dev, expected",
- ((d[0], d[1], d[2], d[3], d[4], d[5], d[6])
- for d in LUKS_OPEN_DATA))
-def test_luks_open(device, keyfile, passphrase, state, name, name_by_dev,
- expected, monkeypatch):
- module = DummyModule()
- module.params["device"] = device
- module.params["keyfile"] = keyfile
- module.params["passphrase"] = passphrase
- module.params["state"] = state
- module.params["name"] = name
-
- monkeypatch.setattr(luks_device.CryptHandler,
- "get_container_name_by_device",
- lambda x, y: name_by_dev)
- monkeypatch.setattr(luks_device.CryptHandler,
- "get_container_device_by_name",
- lambda x, y: device)
- monkeypatch.setattr(luks_device.Handler, "_run_command",
- lambda x, y: [0, device, ""])
- crypt = luks_device.CryptHandler(module)
- try:
- conditions = luks_device.ConditionsHandler(module, crypt)
- assert conditions.luks_open() == expected
- except ValueError:
- assert expected == "exception"
-
-
-@pytest.mark.parametrize("device, dev_by_name, name, name_by_dev, "
- "state, label, expected",
- ((d[0], d[1], d[2], d[3], d[4], d[5], d[6])
- for d in LUKS_CLOSE_DATA))
-def test_luks_close(device, dev_by_name, name, name_by_dev, state,
- label, expected, monkeypatch):
- module = DummyModule()
- module.params["device"] = device
- module.params["name"] = name
- module.params["state"] = state
- module.params["label"] = label
-
- monkeypatch.setattr(luks_device.CryptHandler,
- "get_container_name_by_device",
- lambda x, y: name_by_dev)
- monkeypatch.setattr(luks_device.CryptHandler,
- "get_container_device_by_name",
- lambda x, y: dev_by_name)
- crypt = luks_device.CryptHandler(module)
- try:
- conditions = luks_device.ConditionsHandler(module, crypt)
- assert conditions.luks_close() == expected
- except ValueError:
- assert expected == "exception"
-
-
-@pytest.mark.parametrize("device, keyfile, passphrase, new_keyfile, " +
- "new_passphrase, state, label, expected",
- ((d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7])
- for d in LUKS_ADD_KEY_DATA))
-def test_luks_add_key(device, keyfile, passphrase, new_keyfile, new_passphrase,
- state, label, expected, monkeypatch):
- module = DummyModule()
- module.params["device"] = device
- module.params["keyfile"] = keyfile
- module.params["passphrase"] = passphrase
- module.params["new_keyfile"] = new_keyfile
- module.params["new_passphrase"] = new_passphrase
- module.params["state"] = state
- module.params["label"] = label
-
- monkeypatch.setattr(luks_device.Handler, "get_device_by_label",
- lambda x, y: [0, "/dev/dummy", ""])
-
- try:
- conditions = luks_device.ConditionsHandler(module, module)
- assert conditions.luks_add_key() == expected
- except ValueError:
- assert expected == "exception"
-
-
-@pytest.mark.parametrize("device, remove_keyfile, remove_passphrase, state, " +
- "label, expected",
- ((d[0], d[1], d[2], d[3], d[4], d[5])
- for d in LUKS_REMOVE_KEY_DATA))
-def test_luks_remove_key(device, remove_keyfile, remove_passphrase, state,
- label, expected, monkeypatch):
-
- module = DummyModule()
- module.params["device"] = device
- module.params["remove_keyfile"] = remove_keyfile
- module.params["remove_passphrase"] = remove_passphrase
- module.params["state"] = state
- module.params["label"] = label
-
- monkeypatch.setattr(luks_device.Handler, "get_device_by_label",
- lambda x, y: [0, "/dev/dummy", ""])
- monkeypatch.setattr(luks_device.Handler, "_run_command",
- lambda x, y: [0, device, ""])
-
- crypt = luks_device.CryptHandler(module)
- try:
- conditions = luks_device.ConditionsHandler(module, crypt)
- assert conditions.luks_remove_key() == expected
- except ValueError:
- assert expected == "exception"