mirror of
https://github.com/ansible-collections/community.general.git
synced 2025-07-23 13:20:23 -07:00
openssl_*: improve passphrase handling for private keys in PyOpenSSL (#53489)
* Raise OpenSSLBadPassphraseError if passphrase is wrong. * Improve handling of passphrase errors. Current behavior for modules is: if passphrase is wrong (or wrongly specified), fail. Current behavior for openssl_privatekey is: if passphrase is worng (or wrongly specified), regenerate. * Add changelog. * Add tests. * Adjustments for some versions of PyOpenSSL. * Update lib/ansible/modules/crypto/openssl_certificate.py Improve text. Co-Authored-By: felixfontein <felix@fontein.de>
This commit is contained in:
parent
1d91e03119
commit
caf7fd2245
20 changed files with 427 additions and 36 deletions
|
@ -0,0 +1,6 @@
|
||||||
|
bugfixes:
|
||||||
|
- "openssl_privatekey - no longer hang or crash when passphrase does not match or was
|
||||||
|
not specified, but key is protected with one. Also regenerate key if passphrase is
|
||||||
|
specified but existing key has no passphrase."
|
||||||
|
- "openssl_csr, openssl_certificate, openssl_publickey - properly validate private key
|
||||||
|
passphrase; if it doesn't match, fail (and not crash or ignore)."
|
|
@ -38,6 +38,10 @@ class OpenSSLObjectError(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class OpenSSLBadPassphraseError(OpenSSLObjectError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
def get_fingerprint_of_bytes(source):
|
def get_fingerprint_of_bytes(source):
|
||||||
"""Generate the fingerprint of the given bytes."""
|
"""Generate the fingerprint of the given bytes."""
|
||||||
|
|
||||||
|
@ -67,7 +71,7 @@ def get_fingerprint_of_bytes(source):
|
||||||
def get_fingerprint(path, passphrase=None):
|
def get_fingerprint(path, passphrase=None):
|
||||||
"""Generate the fingerprint of the public key. """
|
"""Generate the fingerprint of the public key. """
|
||||||
|
|
||||||
privatekey = load_privatekey(path, passphrase)
|
privatekey = load_privatekey(path, passphrase, check_passphrase=False)
|
||||||
try:
|
try:
|
||||||
publickey = crypto.dump_publickey(crypto.FILETYPE_ASN1, privatekey)
|
publickey = crypto.dump_publickey(crypto.FILETYPE_ASN1, privatekey)
|
||||||
return get_fingerprint_of_bytes(publickey)
|
return get_fingerprint_of_bytes(publickey)
|
||||||
|
@ -78,20 +82,46 @@ def get_fingerprint(path, passphrase=None):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def load_privatekey(path, passphrase=None):
|
def load_privatekey(path, passphrase=None, check_passphrase=True):
|
||||||
"""Load the specified OpenSSL private key."""
|
"""Load the specified OpenSSL private key."""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with open(path, 'rb') as b_priv_key_fh:
|
with open(path, 'rb') as b_priv_key_fh:
|
||||||
priv_key_detail = b_priv_key_fh.read()
|
priv_key_detail = b_priv_key_fh.read()
|
||||||
|
|
||||||
if passphrase:
|
# First try: try to load with real passphrase (resp. empty string)
|
||||||
return crypto.load_privatekey(crypto.FILETYPE_PEM,
|
# Will work if this is the correct passphrase, or the key is not
|
||||||
priv_key_detail,
|
# password-protected.
|
||||||
to_bytes(passphrase))
|
try:
|
||||||
else:
|
result = crypto.load_privatekey(crypto.FILETYPE_PEM,
|
||||||
return crypto.load_privatekey(crypto.FILETYPE_PEM,
|
priv_key_detail,
|
||||||
priv_key_detail)
|
to_bytes(passphrase or ''))
|
||||||
|
except crypto.Error as e:
|
||||||
|
if len(e.args) > 0 and len(e.args[0]) > 0 and e.args[0][0][2] == 'bad decrypt':
|
||||||
|
# This happens in case we have the wrong passphrase.
|
||||||
|
if passphrase is not None:
|
||||||
|
raise OpenSSLBadPassphraseError('Wrong passphrase provided for private key!')
|
||||||
|
else:
|
||||||
|
raise OpenSSLBadPassphraseError('No passphrase provided, but private key is password-protected!')
|
||||||
|
raise
|
||||||
|
if check_passphrase:
|
||||||
|
# Next we want to make sure that the key is actually protected by
|
||||||
|
# a passphrase (in case we did try the empty string before, make
|
||||||
|
# sure that the key is not protected by the empty string)
|
||||||
|
try:
|
||||||
|
crypto.load_privatekey(crypto.FILETYPE_PEM,
|
||||||
|
priv_key_detail,
|
||||||
|
to_bytes('y' if passphrase == 'x' else 'x'))
|
||||||
|
if passphrase is not None:
|
||||||
|
# Since we can load the key without an exception, the
|
||||||
|
# key isn't password-protected
|
||||||
|
raise OpenSSLBadPassphraseError('Passphrase provided, but private key is not password-protected!')
|
||||||
|
except crypto.Error:
|
||||||
|
if passphrase is None and len(e.args) > 0 and len(e.args[0]) > 0 and e.args[0][0][2] == 'bad decrypt':
|
||||||
|
# The key is obviously protected by the empty string.
|
||||||
|
# Don't do this at home (if it's possible at all)...
|
||||||
|
raise OpenSSLBadPassphraseError('No passphrase provided, but private key is password-protected!')
|
||||||
|
return result
|
||||||
except (IOError, OSError) as exc:
|
except (IOError, OSError) as exc:
|
||||||
raise OpenSSLObjectError(exc)
|
raise OpenSSLObjectError(exc)
|
||||||
|
|
||||||
|
|
|
@ -596,10 +596,13 @@ class Certificate(crypto_utils.OpenSSLObject):
|
||||||
self.cert = crypto_utils.load_certificate(self.path)
|
self.cert = crypto_utils.load_certificate(self.path)
|
||||||
|
|
||||||
if self.privatekey_path:
|
if self.privatekey_path:
|
||||||
self.privatekey = crypto_utils.load_privatekey(
|
try:
|
||||||
self.privatekey_path,
|
self.privatekey = crypto_utils.load_privatekey(
|
||||||
self.privatekey_passphrase
|
self.privatekey_path,
|
||||||
)
|
self.privatekey_passphrase
|
||||||
|
)
|
||||||
|
except crypto_utils.OpenSSLBadPassphraseError as exc:
|
||||||
|
raise CertificateError(exc)
|
||||||
return _validate_privatekey()
|
return _validate_privatekey()
|
||||||
|
|
||||||
if self.csr_path:
|
if self.csr_path:
|
||||||
|
@ -621,9 +624,12 @@ class SelfSignedCertificate(Certificate):
|
||||||
self.version = module.params['selfsigned_version']
|
self.version = module.params['selfsigned_version']
|
||||||
self.serial_number = randint(1000, 99999)
|
self.serial_number = randint(1000, 99999)
|
||||||
self.csr = crypto_utils.load_certificate_request(self.csr_path)
|
self.csr = crypto_utils.load_certificate_request(self.csr_path)
|
||||||
self.privatekey = crypto_utils.load_privatekey(
|
try:
|
||||||
self.privatekey_path, self.privatekey_passphrase
|
self.privatekey = crypto_utils.load_privatekey(
|
||||||
)
|
self.privatekey_path, self.privatekey_passphrase
|
||||||
|
)
|
||||||
|
except crypto_utils.OpenSSLBadPassphraseError as exc:
|
||||||
|
module.fail_json(msg=str(exc))
|
||||||
|
|
||||||
def generate(self, module):
|
def generate(self, module):
|
||||||
|
|
||||||
|
@ -703,9 +709,12 @@ class OwnCACertificate(Certificate):
|
||||||
self.ca_privatekey_passphrase = module.params['ownca_privatekey_passphrase']
|
self.ca_privatekey_passphrase = module.params['ownca_privatekey_passphrase']
|
||||||
self.csr = crypto_utils.load_certificate_request(self.csr_path)
|
self.csr = crypto_utils.load_certificate_request(self.csr_path)
|
||||||
self.ca_cert = crypto_utils.load_certificate(self.ca_cert_path)
|
self.ca_cert = crypto_utils.load_certificate(self.ca_cert_path)
|
||||||
self.ca_privatekey = crypto_utils.load_privatekey(
|
try:
|
||||||
self.ca_privatekey_path, self.ca_privatekey_passphrase
|
self.ca_privatekey = crypto_utils.load_privatekey(
|
||||||
)
|
self.ca_privatekey_path, self.ca_privatekey_passphrase
|
||||||
|
)
|
||||||
|
except crypto_utils.OpenSSLBadPassphraseError as exc:
|
||||||
|
module.fail_json(msg=str(exc))
|
||||||
|
|
||||||
def generate(self, module):
|
def generate(self, module):
|
||||||
|
|
||||||
|
@ -1003,10 +1012,15 @@ class AssertOnlyCertificate(Certificate):
|
||||||
|
|
||||||
self.assertonly()
|
self.assertonly()
|
||||||
|
|
||||||
if self.privatekey_path and \
|
try:
|
||||||
not super(AssertOnlyCertificate, self).check(module, perms_required=False):
|
if self.privatekey_path and \
|
||||||
|
not super(AssertOnlyCertificate, self).check(module, perms_required=False):
|
||||||
|
self.message.append(
|
||||||
|
'Certificate %s and private key %s do not match' % (self.path, self.privatekey_path)
|
||||||
|
)
|
||||||
|
except CertificateError as e:
|
||||||
self.message.append(
|
self.message.append(
|
||||||
'Certificate %s and private key %s does not match' % (self.path, self.privatekey_path)
|
'Error while reading private key %s: %s' % (self.privatekey_path, str(e))
|
||||||
)
|
)
|
||||||
|
|
||||||
if len(self.message):
|
if len(self.message):
|
||||||
|
|
|
@ -528,7 +528,10 @@ class CertificateSigningRequestPyOpenSSL(CertificateSigningRequestBase):
|
||||||
return crypto.dump_certificate_request(crypto.FILETYPE_PEM, self.request)
|
return crypto.dump_certificate_request(crypto.FILETYPE_PEM, self.request)
|
||||||
|
|
||||||
def _load_private_key(self):
|
def _load_private_key(self):
|
||||||
self.privatekey = crypto_utils.load_privatekey(self.privatekey_path, self.privatekey_passphrase)
|
try:
|
||||||
|
self.privatekey = crypto_utils.load_privatekey(self.privatekey_path, self.privatekey_passphrase)
|
||||||
|
except crypto_utils.OpenSSLBadPassphraseError as exc:
|
||||||
|
raise CertificateSigningRequestError(exc)
|
||||||
|
|
||||||
def _check_csr(self):
|
def _check_csr(self):
|
||||||
def _check_subject(csr):
|
def _check_subject(csr):
|
||||||
|
@ -776,7 +779,7 @@ class CertificateSigningRequestCryptography(CertificateSigningRequestBase):
|
||||||
cryptography.x509.NameAttribute(self._get_name_oid(entry[0]), to_text(entry[1])) for entry in self.subject
|
cryptography.x509.NameAttribute(self._get_name_oid(entry[0]), to_text(entry[1])) for entry in self.subject
|
||||||
]))
|
]))
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
raise CertificateSigningRequestError(str(e))
|
raise CertificateSigningRequestError(e)
|
||||||
|
|
||||||
if self.subjectAltName:
|
if self.subjectAltName:
|
||||||
csr = csr.add_extension(cryptography.x509.SubjectAlternativeName([
|
csr = csr.add_extension(cryptography.x509.SubjectAlternativeName([
|
||||||
|
|
|
@ -215,6 +215,8 @@ class Pkcs(crypto_utils.OpenSSLObject):
|
||||||
self.privatekey_passphrase)
|
self.privatekey_passphrase)
|
||||||
except crypto.Error:
|
except crypto.Error:
|
||||||
return False
|
return False
|
||||||
|
except crypto_utils.OpenSSLBadPassphraseError:
|
||||||
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
if not state_and_perms:
|
if not state_and_perms:
|
||||||
|
@ -256,10 +258,13 @@ class Pkcs(crypto_utils.OpenSSLObject):
|
||||||
self.pkcs12.set_friendlyname(to_bytes(self.friendly_name))
|
self.pkcs12.set_friendlyname(to_bytes(self.friendly_name))
|
||||||
|
|
||||||
if self.privatekey_path:
|
if self.privatekey_path:
|
||||||
self.pkcs12.set_privatekey(crypto_utils.load_privatekey(
|
try:
|
||||||
self.privatekey_path,
|
self.pkcs12.set_privatekey(crypto_utils.load_privatekey(
|
||||||
self.privatekey_passphrase)
|
self.privatekey_path,
|
||||||
)
|
self.privatekey_passphrase)
|
||||||
|
)
|
||||||
|
except crypto_utils.OpenSSLBadPassphraseError as exc:
|
||||||
|
raise PkcsError(exc)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
pkcs12_file = os.open(self.path,
|
pkcs12_file = os.open(self.path,
|
||||||
|
|
|
@ -365,6 +365,8 @@ class PrivateKeyPyOpenSSL(PrivateKeyBase):
|
||||||
return True
|
return True
|
||||||
except crypto.Error:
|
except crypto.Error:
|
||||||
return False
|
return False
|
||||||
|
except crypto_utils.OpenSSLBadPassphraseError as exc:
|
||||||
|
return False
|
||||||
|
|
||||||
def _check_size_and_type(self):
|
def _check_size_and_type(self):
|
||||||
def _check_size(privatekey):
|
def _check_size(privatekey):
|
||||||
|
@ -373,7 +375,10 @@ class PrivateKeyPyOpenSSL(PrivateKeyBase):
|
||||||
def _check_type(privatekey):
|
def _check_type(privatekey):
|
||||||
return self.type == privatekey.type()
|
return self.type == privatekey.type()
|
||||||
|
|
||||||
privatekey = crypto_utils.load_privatekey(self.path, self.passphrase)
|
try:
|
||||||
|
privatekey = crypto_utils.load_privatekey(self.path, self.passphrase)
|
||||||
|
except crypto_utils.OpenSSLBadPassphraseError as exc:
|
||||||
|
raise PrivateKeyError(exc)
|
||||||
|
|
||||||
return _check_size(privatekey) and _check_type(privatekey)
|
return _check_size(privatekey) and _check_type(privatekey)
|
||||||
|
|
||||||
|
@ -513,10 +518,19 @@ class PrivateKeyCryptography(PrivateKeyBase):
|
||||||
|
|
||||||
def _check_passphrase(self):
|
def _check_passphrase(self):
|
||||||
try:
|
try:
|
||||||
self._load_privatekey()
|
with open(self.path, 'rb') as f:
|
||||||
|
return cryptography.hazmat.primitives.serialization.load_pem_private_key(
|
||||||
|
f.read(),
|
||||||
|
None if self.passphrase is None else to_bytes(self.passphrase),
|
||||||
|
backend=self.cryptography_backend
|
||||||
|
)
|
||||||
return True
|
return True
|
||||||
except crypto.Error:
|
except TypeError as e:
|
||||||
return False
|
if 'Password' in str(e) and 'encrypted' in str(e):
|
||||||
|
return False
|
||||||
|
raise PrivateKeyError(e)
|
||||||
|
except Exception as e:
|
||||||
|
raise PrivateKeyError(e)
|
||||||
|
|
||||||
def _check_size_and_type(self):
|
def _check_size_and_type(self):
|
||||||
privatekey = self._load_privatekey()
|
privatekey = self._load_privatekey()
|
||||||
|
|
|
@ -200,6 +200,8 @@ class PublicKey(crypto_utils.OpenSSLObject):
|
||||||
publickey_file.write(publickey_content)
|
publickey_file.write(publickey_content)
|
||||||
|
|
||||||
self.changed = True
|
self.changed = True
|
||||||
|
except crypto_utils.OpenSSLBadPassphraseError as exc:
|
||||||
|
raise PublicKeyError(exc)
|
||||||
except (IOError, OSError) as exc:
|
except (IOError, OSError) as exc:
|
||||||
raise PublicKeyError(exc)
|
raise PublicKeyError(exc)
|
||||||
except AttributeError as exc:
|
except AttributeError as exc:
|
||||||
|
@ -237,10 +239,13 @@ class PublicKey(crypto_utils.OpenSSLObject):
|
||||||
except (crypto.Error, ValueError):
|
except (crypto.Error, ValueError):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
desired_publickey = crypto.dump_publickey(
|
try:
|
||||||
crypto.FILETYPE_ASN1,
|
desired_publickey = crypto.dump_publickey(
|
||||||
crypto_utils.load_privatekey(self.privatekey_path, self.privatekey_passphrase)
|
crypto.FILETYPE_ASN1,
|
||||||
)
|
crypto_utils.load_privatekey(self.privatekey_path, self.privatekey_passphrase)
|
||||||
|
)
|
||||||
|
except crypto_utils.OpenSSLBadPassphraseError as exc:
|
||||||
|
raise PublicKeyError(exc)
|
||||||
|
|
||||||
return current_publickey == desired_publickey
|
return current_publickey == desired_publickey
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,13 @@
|
||||||
openssl_privatekey:
|
openssl_privatekey:
|
||||||
path: '{{ output_dir }}/privatekey.pem'
|
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 (no extensions)
|
- name: Generate CSR (no extensions)
|
||||||
openssl_csr:
|
openssl_csr:
|
||||||
path: '{{ output_dir }}/csr_noext.csr'
|
path: '{{ output_dir }}/csr_noext.csr'
|
||||||
|
@ -54,3 +61,39 @@
|
||||||
- "'Found no keyUsage extension' in extension_missing_ku.msg"
|
- "'Found no keyUsage extension' in extension_missing_ku.msg"
|
||||||
- extension_missing_eku is failed
|
- extension_missing_eku is failed
|
||||||
- "'Found no extendedKeyUsage extension' in extension_missing_eku.msg"
|
- "'Found no extendedKeyUsage extension' in extension_missing_eku.msg"
|
||||||
|
|
||||||
|
- name: 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
|
||||||
|
ignore_errors: yes
|
||||||
|
register: passphrase_error_1
|
||||||
|
|
||||||
|
- name: 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
|
||||||
|
ignore_errors: yes
|
||||||
|
register: passphrase_error_2
|
||||||
|
|
||||||
|
- name: Check private key passphrase fail 3
|
||||||
|
openssl_certificate:
|
||||||
|
path: '{{ output_dir }}/cert_noext.pem'
|
||||||
|
privatekey_path: '{{ output_dir }}/privatekeypw.pem'
|
||||||
|
provider: assertonly
|
||||||
|
ignore_errors: yes
|
||||||
|
register: passphrase_error_3
|
||||||
|
|
||||||
|
- 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_1.msg or 'assword' in passphrase_error_2.msg"
|
||||||
|
- passphrase_error_3 is failed
|
||||||
|
- "'assphrase' in passphrase_error_1.msg or 'assword' in passphrase_error_3.msg"
|
||||||
|
|
|
@ -151,4 +151,39 @@
|
||||||
ownca_digest: sha256
|
ownca_digest: sha256
|
||||||
register: ownca_certificate_ecc
|
register: ownca_certificate_ecc
|
||||||
|
|
||||||
|
- name: 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
|
||||||
|
ignore_errors: yes
|
||||||
|
register: passphrase_error_1
|
||||||
|
|
||||||
|
- name: Generate ownca certificate (failed passphrase 2)
|
||||||
|
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 }}/privatekeypw.pem'
|
||||||
|
ownca_privatekey_passphrase: wrong_password
|
||||||
|
provider: ownca
|
||||||
|
ownca_digest: sha256
|
||||||
|
ignore_errors: yes
|
||||||
|
register: passphrase_error_2
|
||||||
|
|
||||||
|
- name: 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
|
||||||
|
ignore_errors: yes
|
||||||
|
register: passphrase_error_3
|
||||||
|
|
||||||
- import_tasks: ../tests/validate_ownca.yml
|
- import_tasks: ../tests/validate_ownca.yml
|
||||||
|
|
|
@ -3,6 +3,13 @@
|
||||||
openssl_privatekey:
|
openssl_privatekey:
|
||||||
path: '{{ output_dir }}/privatekey.pem'
|
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
|
- name: Generate CSR
|
||||||
openssl_csr:
|
openssl_csr:
|
||||||
path: '{{ output_dir }}/csr.csr'
|
path: '{{ output_dir }}/csr.csr'
|
||||||
|
@ -157,4 +164,36 @@
|
||||||
selfsigned_digest: sha256
|
selfsigned_digest: sha256
|
||||||
register: selfsigned_certificate_ecc
|
register: selfsigned_certificate_ecc
|
||||||
|
|
||||||
|
- name: 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
|
||||||
|
ignore_errors: yes
|
||||||
|
register: passphrase_error_1
|
||||||
|
|
||||||
|
- name: 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
|
||||||
|
ignore_errors: yes
|
||||||
|
register: passphrase_error_2
|
||||||
|
|
||||||
|
- name: 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
|
||||||
|
ignore_errors: yes
|
||||||
|
register: passphrase_error_3
|
||||||
|
|
||||||
- import_tasks: ../tests/validate_selfsigned.yml
|
- import_tasks: ../tests/validate_selfsigned.yml
|
||||||
|
|
|
@ -81,3 +81,13 @@
|
||||||
- ownca_cert_ecc_pubkey.stdout == privatekey_ecc_pubkey.stdout
|
- ownca_cert_ecc_pubkey.stdout == privatekey_ecc_pubkey.stdout
|
||||||
# openssl 1.1.x adds a space between the output
|
# openssl 1.1.x adds a space between the output
|
||||||
- ownca_cert_ecc_issuer.stdout in ['CN=Example CA', 'CN = Example CA']
|
- ownca_cert_ecc_issuer.stdout in ['CN=Example CA', 'CN = Example CA']
|
||||||
|
|
||||||
|
- 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_1.msg or 'assword' in passphrase_error_2.msg"
|
||||||
|
- passphrase_error_3 is failed
|
||||||
|
- "'assphrase' in passphrase_error_1.msg or 'assword' in passphrase_error_3.msg"
|
||||||
|
|
|
@ -82,3 +82,13 @@
|
||||||
assert:
|
assert:
|
||||||
that:
|
that:
|
||||||
- cert_ecc_pubkey.stdout == privatekey_ecc_pubkey.stdout
|
- cert_ecc_pubkey.stdout == privatekey_ecc_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_1.msg or 'assword' in passphrase_error_2.msg"
|
||||||
|
- passphrase_error_3 is failed
|
||||||
|
- "'assphrase' in passphrase_error_1.msg or 'assword' in passphrase_error_3.msg"
|
||||||
|
|
|
@ -241,3 +241,36 @@
|
||||||
select_crypto_backend: '{{ select_crypto_backend }}'
|
select_crypto_backend: '{{ select_crypto_backend }}'
|
||||||
register: country_fail_4
|
register: country_fail_4
|
||||||
ignore_errors: yes
|
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 publickey - PEM format
|
||||||
|
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 publickey - PEM format
|
||||||
|
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 publickey - PEM format
|
||||||
|
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
|
||||||
|
|
|
@ -109,3 +109,13 @@
|
||||||
- country_idempotent_2 is not changed
|
- country_idempotent_2 is not changed
|
||||||
- country_idempotent_3 is not changed
|
- country_idempotent_3 is not changed
|
||||||
- country_fail_4 is failed
|
- 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_1.msg or 'assword' in passphrase_error_2.msg"
|
||||||
|
- passphrase_error_3 is failed
|
||||||
|
- "'assphrase' in passphrase_error_1.msg or 'assword' in passphrase_error_3.msg"
|
||||||
|
|
|
@ -53,6 +53,45 @@
|
||||||
action: 'parse'
|
action: 'parse'
|
||||||
state: 'present'
|
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
|
||||||
|
|
||||||
- import_tasks: ../tests/validate.yml
|
- import_tasks: ../tests/validate.yml
|
||||||
|
|
||||||
always:
|
always:
|
||||||
|
|
|
@ -14,3 +14,13 @@
|
||||||
- p12_standard.mode == '0400'
|
- p12_standard.mode == '0400'
|
||||||
- p12_force.changed
|
- p12_force.changed
|
||||||
- p12_force_and_mode.mode == '0644' and p12_force_and_mode.changed
|
- p12_force_and_mode.mode == '0644' and p12_force_and_mode.changed
|
||||||
|
|
||||||
|
- 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_1.msg or 'assword' in passphrase_error_2.msg"
|
||||||
|
- passphrase_error_3 is failed
|
||||||
|
- "'assphrase' in passphrase_error_1.msg or 'assword' in passphrase_error_3.msg"
|
||||||
|
|
|
@ -142,3 +142,39 @@
|
||||||
loop_control:
|
loop_control:
|
||||||
label: "{{ item.curve }}"
|
label: "{{ item.curve }}"
|
||||||
register: privatekey_ecc_idempotency
|
register: privatekey_ecc_idempotency
|
||||||
|
|
||||||
|
- 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 }}'
|
||||||
|
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 }}'
|
||||||
|
register: passphrase_2
|
||||||
|
|
||||||
|
- name: Regenerate privatekey without passphrase
|
||||||
|
openssl_privatekey:
|
||||||
|
path: '{{ output_dir }}/privatekeypw.pem'
|
||||||
|
select_crypto_backend: '{{ select_crypto_backend }}'
|
||||||
|
register: passphrase_3
|
||||||
|
|
||||||
|
- name: Regenerate privatekey without passphrase (idempotent)
|
||||||
|
openssl_privatekey:
|
||||||
|
path: '{{ output_dir }}/privatekeypw.pem'
|
||||||
|
select_crypto_backend: '{{ select_crypto_backend }}'
|
||||||
|
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 }}'
|
||||||
|
register: passphrase_5
|
||||||
|
|
|
@ -104,3 +104,12 @@
|
||||||
when: "'skip_reason' not in item"
|
when: "'skip_reason' not in item"
|
||||||
loop_control:
|
loop_control:
|
||||||
label: "{{ item.item.curve }}"
|
label: "{{ item.item.curve }}"
|
||||||
|
|
||||||
|
- 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
|
||||||
|
|
|
@ -78,6 +78,36 @@
|
||||||
path: '{{ output_dir }}/publickey5.pub'
|
path: '{{ output_dir }}/publickey5.pub'
|
||||||
privatekey_path: '{{ output_dir }}/privatekey5.pem'
|
privatekey_path: '{{ output_dir }}/privatekey5.pem'
|
||||||
|
|
||||||
|
- 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
|
||||||
|
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
|
||||||
|
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'
|
||||||
|
ignore_errors: yes
|
||||||
|
register: passphrase_error_3
|
||||||
|
|
||||||
- import_tasks: ../tests/validate.yml
|
- import_tasks: ../tests/validate.yml
|
||||||
|
|
||||||
when: pyopenssl_version.stdout is version('16.0.0', '>=')
|
when: pyopenssl_version.stdout is version('16.0.0', '>=')
|
||||||
|
|
|
@ -96,3 +96,13 @@
|
||||||
assert:
|
assert:
|
||||||
that:
|
that:
|
||||||
- publickey5_pubkey.stdout == privatekey5_pubkey.stdout
|
- 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_1.msg or 'assword' in passphrase_error_2.msg"
|
||||||
|
- passphrase_error_3 is failed
|
||||||
|
- "'assphrase' in passphrase_error_1.msg or 'assword' in passphrase_error_3.msg"
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue