Allow to configure encryption level. (#523)
parent
4533b3e934
commit
e4e2b804bc
|
@ -0,0 +1,4 @@
|
|||
minor_changes:
|
||||
- "openssl_pkcs12 - add option ``encryption_level`` which allows to chose ``compatibility2022`` when cryptography >= 38.0.0 is used
|
||||
to enable a more backwards compatible encryption algorithm. If cryptography uses OpenSSL 3.0.0 or newer, the default algorithm
|
||||
is not compatible with older software (https://github.com/ansible-collections/community.crypto/pull/523)."
|
|
@ -65,15 +65,30 @@ options:
|
|||
iter_size:
|
||||
description:
|
||||
- Number of times to repeat the encryption step.
|
||||
- This is not considered during idempotency checks.
|
||||
- This is only used by the C(pyopenssl) backend. When using it, the default is C(2048).
|
||||
- This is B(not considered during idempotency checks).
|
||||
- This is only used by the C(pyopenssl) backend, or when I(encryption_level=compatibility2022).
|
||||
- When using it, the default is C(2048) for C(pyopenssl) and C(50000) for C(cryptography).
|
||||
type: int
|
||||
maciter_size:
|
||||
description:
|
||||
- Number of times to repeat the MAC step.
|
||||
- This is not considered during idempotency checks.
|
||||
- This is B(not considered during idempotency checks).
|
||||
- This is only used by the C(pyopenssl) backend. When using it, the default is C(1).
|
||||
type: int
|
||||
encryption_level:
|
||||
description:
|
||||
- Determines the encryption level used.
|
||||
- C(auto) uses the default of the selected backend. For C(cryptography), this is what the
|
||||
cryptography library's specific version considers the best available encryption.
|
||||
- C(compatibility2022) uses compatibility settings for older software in 2022.
|
||||
This is only supported by the C(cryptography) backend if cryptography >= 38.0.0 is available.
|
||||
- B(Note) that this option is B(not used for idempotency).
|
||||
choices:
|
||||
- auto
|
||||
- compatibility2022
|
||||
default: auto
|
||||
type: str
|
||||
version_added: 2.8.0
|
||||
passphrase:
|
||||
description:
|
||||
- The PKCS#12 password.
|
||||
|
@ -128,8 +143,8 @@ options:
|
|||
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 one of I(iter_size) or I(maciter_size) is used, C(auto) will always result in C(pyopenssl) to be chosen
|
||||
for backwards compatibility.
|
||||
If I(iter_size) is used together with I(encryption_level != compatibility2022), or if I(maciter_size) is used,
|
||||
C(auto) will always result in C(pyopenssl) to be chosen for backwards compatibility.
|
||||
- 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 community.crypto x.y.0, and will be
|
||||
|
@ -302,6 +317,18 @@ except ImportError:
|
|||
else:
|
||||
CRYPTOGRAPHY_FOUND = True
|
||||
|
||||
CRYPTOGRAPHY_COMPATIBILITY2022_ERR = None
|
||||
try:
|
||||
from cryptography.hazmat.primitives import hashes
|
||||
from cryptography.hazmat.primitives.serialization.pkcs12 import PBES
|
||||
# Try to build encryption builder for compatibility2022
|
||||
serialization.PrivateFormat.PKCS12.encryption_builder().key_cert_algorithm(PBES.PBESv1SHA1And3KeyTripleDESCBC).hmac_hash(hashes.SHA1())
|
||||
except Exception:
|
||||
CRYPTOGRAPHY_COMPATIBILITY2022_ERR = traceback.format_exc()
|
||||
CRYPTOGRAPHY_HAS_COMPATIBILITY2022 = False
|
||||
else:
|
||||
CRYPTOGRAPHY_HAS_COMPATIBILITY2022 = True
|
||||
|
||||
|
||||
def load_certificate_set(filename, backend):
|
||||
'''
|
||||
|
@ -317,7 +344,7 @@ class PkcsError(OpenSSLObjectError):
|
|||
|
||||
|
||||
class Pkcs(OpenSSLObject):
|
||||
def __init__(self, module, backend):
|
||||
def __init__(self, module, backend, iter_size_default=2048):
|
||||
super(Pkcs, self).__init__(
|
||||
module.params['path'],
|
||||
module.params['state'],
|
||||
|
@ -330,8 +357,9 @@ class Pkcs(OpenSSLObject):
|
|||
self.other_certificates_parse_all = module.params['other_certificates_parse_all']
|
||||
self.certificate_path = module.params['certificate_path']
|
||||
self.friendly_name = module.params['friendly_name']
|
||||
self.iter_size = module.params['iter_size'] or 2048
|
||||
self.iter_size = module.params['iter_size'] or iter_size_default
|
||||
self.maciter_size = module.params['maciter_size'] or 1
|
||||
self.encryption_level = module.params['encryption_level']
|
||||
self.passphrase = module.params['passphrase']
|
||||
self.pkcs12 = None
|
||||
self.privatekey_passphrase = module.params['privatekey_passphrase']
|
||||
|
@ -508,6 +536,8 @@ class Pkcs(OpenSSLObject):
|
|||
class PkcsPyOpenSSL(Pkcs):
|
||||
def __init__(self, module):
|
||||
super(PkcsPyOpenSSL, self).__init__(module, 'pyopenssl')
|
||||
if self.encryption_level != 'auto':
|
||||
module.fail_json(msg='The PyOpenSSL backend only supports encryption_level = auto')
|
||||
|
||||
def generate_bytes(self, module):
|
||||
"""Generate PKCS#12 file archive."""
|
||||
|
@ -573,7 +603,12 @@ class PkcsPyOpenSSL(Pkcs):
|
|||
|
||||
class PkcsCryptography(Pkcs):
|
||||
def __init__(self, module):
|
||||
super(PkcsCryptography, self).__init__(module, 'cryptography')
|
||||
super(PkcsCryptography, self).__init__(module, 'cryptography', iter_size_default=50000)
|
||||
if self.encryption_level == 'compatibility2022' and not CRYPTOGRAPHY_HAS_COMPATIBILITY2022:
|
||||
module.fail_json(
|
||||
msg='The installed cryptography version does not support encryption_level = compatibility2022.'
|
||||
' You need cryptography >= 38.0.0 and support for SHA1',
|
||||
exception=CRYPTOGRAPHY_COMPATIBILITY2022_ERR)
|
||||
|
||||
def generate_bytes(self, module):
|
||||
"""Generate PKCS#12 file archive."""
|
||||
|
@ -593,13 +628,25 @@ class PkcsCryptography(Pkcs):
|
|||
# Store fake object which can be used to retrieve the components back
|
||||
self.pkcs12 = (pkey, cert, self.other_certificates, friendly_name)
|
||||
|
||||
if not self.passphrase:
|
||||
encryption = serialization.NoEncryption()
|
||||
elif self.encryption_level == 'compatibility2022':
|
||||
encryption = (
|
||||
serialization.PrivateFormat.PKCS12.encryption_builder().
|
||||
kdf_rounds(self.iter_size).
|
||||
key_cert_algorithm(PBES.PBESv1SHA1And3KeyTripleDESCBC).
|
||||
hmac_hash(hashes.SHA1()).
|
||||
build(to_bytes(self.passphrase))
|
||||
)
|
||||
else:
|
||||
encryption = serialization.BestAvailableEncryption(to_bytes(self.passphrase))
|
||||
|
||||
return serialize_key_and_certificates(
|
||||
friendly_name,
|
||||
pkey,
|
||||
cert,
|
||||
self.other_certificates,
|
||||
serialization.BestAvailableEncryption(to_bytes(self.passphrase))
|
||||
if self.passphrase else serialization.NoEncryption(),
|
||||
encryption,
|
||||
)
|
||||
|
||||
def parse_bytes(self, pkcs12_content):
|
||||
|
@ -658,8 +705,11 @@ def select_backend(module, backend):
|
|||
can_use_pyopenssl = PYOPENSSL_FOUND and PYOPENSSL_VERSION >= LooseVersion(MINIMAL_PYOPENSSL_VERSION)
|
||||
|
||||
# If no restrictions are provided, first try cryptography, then pyOpenSSL
|
||||
if module.params['iter_size'] is not None or module.params['maciter_size'] is not None:
|
||||
# If iter_size or maciter_size is specified, use pyOpenSSL backend
|
||||
if (
|
||||
(module.params['iter_size'] is not None and module.params['encryption_level'] != 'compatibility2022')
|
||||
or module.params['maciter_size'] is not None
|
||||
):
|
||||
# If iter_size (for encryption_level != compatibility2022) or maciter_size is specified, use pyOpenSSL backend
|
||||
backend = 'pyopenssl'
|
||||
elif can_use_cryptography:
|
||||
backend = 'cryptography'
|
||||
|
@ -697,6 +747,7 @@ def main():
|
|||
certificate_path=dict(type='path'),
|
||||
force=dict(type='bool', default=False),
|
||||
friendly_name=dict(type='str', aliases=['name']),
|
||||
encryption_level=dict(type='str', choices=['auto', 'compatibility2022'], default='auto'),
|
||||
iter_size=dict(type='int'),
|
||||
maciter_size=dict(type='int'),
|
||||
passphrase=dict(type='str', no_log=True),
|
||||
|
|
|
@ -330,6 +330,25 @@
|
|||
path: '{{ remote_tmp_dir }}/ansible_empty.pem'
|
||||
action: parse
|
||||
|
||||
- name: "({{ select_crypto_backend }}) Generate PKCS#12 file passphrase and compatibility encryption"
|
||||
openssl_pkcs12:
|
||||
select_crypto_backend: '{{ select_crypto_backend }}'
|
||||
path: '{{ remote_tmp_dir }}/ansible_compatibility2022.p12'
|
||||
friendly_name: compat_fn
|
||||
encryption_level: compatibility2022
|
||||
iter_size: 3210
|
||||
passphrase: magicpassword
|
||||
privatekey_path: '{{ remote_tmp_dir }}/ansible_pkey1.pem'
|
||||
certificate_path: '{{ remote_tmp_dir }}/ansible1.crt'
|
||||
other_certificates:
|
||||
- '{{ remote_tmp_dir }}/ansible2.crt'
|
||||
- '{{ remote_tmp_dir }}/ansible3.crt'
|
||||
state: present
|
||||
register: p12_compatibility2022
|
||||
when:
|
||||
- select_crypto_backend == 'cryptography'
|
||||
- cryptography_version.stdout is version('38.0.0', '>=')
|
||||
|
||||
- import_tasks: ../tests/validate.yml
|
||||
|
||||
always:
|
||||
|
@ -345,3 +364,4 @@
|
|||
- ansible_pw2
|
||||
- ansible_pw3
|
||||
- ansible_empty
|
||||
- ansible_compatibility2022
|
||||
|
|
|
@ -90,3 +90,23 @@
|
|||
- p12_empty_idem is not changed
|
||||
- p12_empty_concat_idem is not changed
|
||||
- (empty_contents == empty_expected_cryptography) or (empty_contents == empty_expected_pyopenssl and select_crypto_backend == 'pyopenssl')
|
||||
|
||||
- name: '({{ select_crypto_backend }}) PKCS#12 with compatibility2022 settings'
|
||||
when:
|
||||
- select_crypto_backend == 'cryptography'
|
||||
- cryptography_version.stdout is version('38.0.0', '>=')
|
||||
block:
|
||||
- name: '({{ select_crypto_backend }}) Validate PKCS#12 with compatibility2022 settings'
|
||||
shell: "{{ openssl_binary }} pkcs12 -info -in {{ remote_tmp_dir }}/ansible_compatibility2022.p12 -nodes -passin pass:'magicpassword'"
|
||||
register: p12_validate_compatibility2022
|
||||
|
||||
- name: '({{ select_crypto_backend }}) Check PKCS#12 with compatibility2022 settings'
|
||||
assert:
|
||||
that:
|
||||
- p12_compatibility2022 is changed
|
||||
- >-
|
||||
'PKCS7 Encrypted data: pbeWithSHA1And3-KeyTripleDES-CBC, Iteration 3210' in p12_validate_compatibility2022.stderr_lines
|
||||
- >-
|
||||
'Shrouded Keybag: pbeWithSHA1And3-KeyTripleDES-CBC, Iteration 3210' in p12_validate_compatibility2022.stderr_lines
|
||||
- >-
|
||||
'friendlyName: compat_fn' in p12_validate_compatibility2022.stdout
|
||||
|
|
Loading…
Reference in New Issue