From eb8dabce84302621351b409e7e52594e03937a17 Mon Sep 17 00:00:00 2001 From: Felix Fontein Date: Fri, 22 Oct 2021 07:15:20 +0200 Subject: [PATCH] Improve Python 2 Unicode handling. (#313) --- changelogs/fragments/313-unicode-names.yml | 2 ++ .../crypto/cryptography_support.py | 34 +++++++++---------- .../x509_certificate_info/tasks/impl.yml | 8 +++++ .../x509_certificate_info/tasks/main.yml | 1 + 4 files changed, 28 insertions(+), 17 deletions(-) create mode 100644 changelogs/fragments/313-unicode-names.yml diff --git a/changelogs/fragments/313-unicode-names.yml b/changelogs/fragments/313-unicode-names.yml new file mode 100644 index 00000000..759b4595 --- /dev/null +++ b/changelogs/fragments/313-unicode-names.yml @@ -0,0 +1,2 @@ +bugfixes: + - "cryptography backend - improve Unicode handling for Python 2 (https://github.com/ansible-collections/community.crypto/pull/313)." diff --git a/plugins/module_utils/crypto/cryptography_support.py b/plugins/module_utils/crypto/cryptography_support.py index edec7853..29e6624e 100644 --- a/plugins/module_utils/crypto/cryptography_support.py +++ b/plugins/module_utils/crypto/cryptography_support.py @@ -345,14 +345,14 @@ def _dn_escape_value(value): ''' Escape Distinguished Name's attribute value. ''' - value = value.replace('\\', r'\\') - for ch in [',', '+', '<', '>', ';', '"']: - value = value.replace(ch, r'\%s' % ch) - value = value.replace('\0', r'\00') - if value.startswith((' ', '#')): - value = r'\%s' % value[0] + value[1:] - if value.endswith(' '): - value = value[:-1] + r'\ ' + value = value.replace(u'\\', u'\\\\') + for ch in [u',', u'+', u'<', u'>', u';', u'"']: + value = value.replace(ch, u'\\%s' % ch) + value = value.replace(u'\0', u'\\00') + if value.startswith((u' ', u'#')): + value = u'\\%s' % value[0] + value[1:] + if value.endswith(u' '): + value = value[:-1] + u'\\ ' return value @@ -362,26 +362,26 @@ def cryptography_decode_name(name): Raises an OpenSSLObjectError if the name is not supported. ''' if isinstance(name, x509.DNSName): - return 'DNS:{0}'.format(name.value) + return u'DNS:{0}'.format(name.value) if isinstance(name, x509.IPAddress): if isinstance(name.value, (ipaddress.IPv4Network, ipaddress.IPv6Network)): - return 'IP:{0}/{1}'.format(name.value.network_address.compressed, name.value.prefixlen) - return 'IP:{0}'.format(name.value.compressed) + return u'IP:{0}/{1}'.format(name.value.network_address.compressed, name.value.prefixlen) + return u'IP:{0}'.format(name.value.compressed) if isinstance(name, x509.RFC822Name): - return 'email:{0}'.format(name.value) + return u'email:{0}'.format(name.value) if isinstance(name, x509.UniformResourceIdentifier): - return 'URI:{0}'.format(name.value) + return u'URI:{0}'.format(name.value) if isinstance(name, x509.DirectoryName): # According to https://datatracker.ietf.org/doc/html/rfc4514.html#section-2.1 the # list needs to be reversed, and joined by commas - return 'dirName:' + ','.join([ - '{0}={1}'.format(cryptography_oid_to_name(attribute.oid, short=True), _dn_escape_value(attribute.value)) + return u'dirName:' + ','.join([ + u'{0}={1}'.format(to_text(cryptography_oid_to_name(attribute.oid, short=True)), _dn_escape_value(attribute.value)) for attribute in reversed(list(name.value)) ]) if isinstance(name, x509.RegisteredID): - return 'RID:{0}'.format(name.value.dotted_string) + return u'RID:{0}'.format(name.value.dotted_string) if isinstance(name, x509.OtherName): - return 'otherName:{0};{1}'.format(name.type_id.dotted_string, _get_hex(name.value)) + return u'otherName:{0};{1}'.format(name.type_id.dotted_string, _get_hex(name.value)) raise OpenSSLObjectError('Cannot decode name "{0}"'.format(name)) diff --git a/tests/integration/targets/x509_certificate_info/tasks/impl.yml b/tests/integration/targets/x509_certificate_info/tasks/impl.yml index b0b0cb3b..57e668cd 100644 --- a/tests/integration/targets/x509_certificate_info/tasks/impl.yml +++ b/tests/integration/targets/x509_certificate_info/tasks/impl.yml @@ -19,6 +19,14 @@ - "['organizationalUnitName', 'ACME Department'] in result.subject_ordered" - result.public_key_type == 'RSA' - result.public_key_data.size == (default_rsa_key_size_certifiates | int) + - "result.subject_alt_name == [ + 'DNS:www.ansible.com', + 'DNS:' ~ ('öç' if cryptography_version.stdout is version('2.1', '<') else 'xn--74h') ~ '.com', + 'IP:1.2.3.4', + 'IP:::1', + 'email:test@example.org', + 'URI:https://example.org/test/index.html' + ]" - name: Check SubjectKeyIdentifier and AuthorityKeyIdentifier assert: diff --git a/tests/integration/targets/x509_certificate_info/tasks/main.yml b/tests/integration/targets/x509_certificate_info/tasks/main.yml index 04d05c4a..96e7d2a0 100644 --- a/tests/integration/targets/x509_certificate_info/tasks/main.yml +++ b/tests/integration/targets/x509_certificate_info/tasks/main.yml @@ -67,6 +67,7 @@ - biometricInfo subject_alt_name: - "DNS:www.ansible.com" + - "DNS:{{ 'öç' if cryptography_version.stdout is version('2.1', '<') else 'xn--74h' }}.com" - "IP:1.2.3.4" - "IP:::1" - "email:test@example.org"