aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/cryptography/hazmat/backends/openssl/decode_asn1.py10
-rw-r--r--src/cryptography/hazmat/backends/openssl/encode_asn1.py4
-rw-r--r--src/cryptography/x509/general_name.py67
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)