diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/cryptography/hazmat/backends/openssl/decode_asn1.py | 10 | ||||
-rw-r--r-- | src/cryptography/hazmat/backends/openssl/encode_asn1.py | 4 | ||||
-rw-r--r-- | src/cryptography/x509/general_name.py | 67 |
3 files changed, 30 insertions, 51 deletions
diff --git a/src/cryptography/hazmat/backends/openssl/decode_asn1.py b/src/cryptography/hazmat/backends/openssl/decode_asn1.py index 2665fb22..aefb2422 100644 --- a/src/cryptography/hazmat/backends/openssl/decode_asn1.py +++ b/src/cryptography/hazmat/backends/openssl/decode_asn1.py @@ -88,8 +88,14 @@ def _decode_general_names(backend, gns): def _decode_general_name(backend, gn): if gn.type == backend._lib.GEN_DNS: - data = _asn1_string_to_bytes(backend, gn.d.dNSName) - return x509.DNSName(data) + # Convert to bytes and then decode to utf8. We don't use + # asn1_string_to_utf8 here because it doesn't properly convert + # utf8 from ia5strings. + data = _asn1_string_to_bytes(backend, gn.d.dNSName).decode("utf8") + # We don't use the constructor for DNSName so we can bypass validation + # This allows us to create DNSName objects that have unicode chars + # when a certificate (against the RFC) contains them. + return x509.DNSName._init_without_validation(data) elif gn.type == backend._lib.GEN_URI: data = _asn1_string_to_bytes(backend, gn.d.uniformResourceIdentifier) return x509.UniformResourceIdentifier(data) diff --git a/src/cryptography/hazmat/backends/openssl/encode_asn1.py b/src/cryptography/hazmat/backends/openssl/encode_asn1.py index e45e1050..3177cf96 100644 --- a/src/cryptography/hazmat/backends/openssl/encode_asn1.py +++ b/src/cryptography/hazmat/backends/openssl/encode_asn1.py @@ -368,7 +368,9 @@ def _encode_general_name(backend, name): ia5 = backend._lib.ASN1_IA5STRING_new() backend.openssl_assert(ia5 != backend._ffi.NULL) - value = name.bytes_value + # ia5strings are supposed to be ITU T.50 but to allow round-tripping + # of broken certs that encode utf8 we'll encode utf8 here too. + value = name.value.encode("utf8") res = backend._lib.ASN1_STRING_set(ia5, value, len(value)) backend.openssl_assert(res == 1) diff --git a/src/cryptography/x509/general_name.py b/src/cryptography/x509/general_name.py index 768be3bb..d4d92c88 100644 --- a/src/cryptography/x509/general_name.py +++ b/src/cryptography/x509/general_name.py @@ -131,8 +131,8 @@ def _idna_encode(value): for prefix in ['*.', '.']: if value.startswith(prefix): value = value[len(prefix):] - return prefix.encode('ascii') + idna.encode(value) - return idna.encode(value) + return prefix + idna.encode(value).decode("ascii") + return idna.encode(value).decode("ascii") @utils.register_interface(GeneralName) @@ -140,73 +140,44 @@ class DNSName(object): def __init__(self, value): if isinstance(value, six.text_type): try: - value = value.encode("ascii") + value.encode("ascii") except UnicodeEncodeError: value = _idna_encode(value) warnings.warn( - "DNSName values should be passed as idna-encoded bytes, " - "not strings. Support for passing unicode strings will be " - "removed in a future version.", - utils.DeprecatedIn21, - stacklevel=2, - ) - else: - warnings.warn( - "DNSName values should be passed as bytes, not strings. " - "Support for passing unicode strings will be removed in a " - "future version.", + "DNSName values should be passed as an A-label string. " + "This means unicode characters should be encoded via " + "idna. Support for passing unicode strings (aka U-label) " + " will be removed in a future version.", utils.DeprecatedIn21, stacklevel=2, ) - elif not isinstance(value, bytes): - raise TypeError("value must be bytes") + else: + raise TypeError("value must be string") - self._bytes_value = value + self._value = value - bytes_value = utils.read_only_property("_bytes_value") + value = utils.read_only_property("_value") - @property - def value(self): - warnings.warn( - "DNSName.bytes_value should be used instead of DNSName.value; it " - "contains the DNS name as raw bytes, instead of as an idna-decoded" - " unicode string. DNSName.value will be removed in a future " - "version.", - utils.DeprecatedIn21, - stacklevel=2 - ) - data = self._bytes_value - if not data: - decoded = u"" - elif data.startswith(b"*."): - # This is a wildcard name. We need to remove the leading wildcard, - # IDNA decode, then re-add the wildcard. Wildcard characters should - # always be left-most (RFC 2595 section 2.4). - decoded = u"*." + idna.decode(data[2:]) - else: - # Not a wildcard, decode away. If the string has a * in it anywhere - # invalid this will raise an InvalidCodePoint - decoded = idna.decode(data) - if data.startswith(b"."): - # idna strips leading periods. Name constraints can have that - # so we need to re-add it. Sigh. - decoded = u"." + decoded - return decoded + @classmethod + def _init_without_validation(cls, value): + instance = cls.__new__(cls) + instance._value = value + return instance def __repr__(self): - return "<DNSName(bytes_value={0!r})>".format(self.bytes_value) + return "<DNSName(value={0!r})>".format(self.value) def __eq__(self, other): if not isinstance(other, DNSName): return NotImplemented - return self.bytes_value == other.bytes_value + return self.value == other.value def __ne__(self, other): return not self == other def __hash__(self): - return hash(self.bytes_value) + return hash(self.value) @utils.register_interface(GeneralName) |