diff options
-rw-r--r-- | docs/x509.rst | 14 | ||||
-rw-r--r-- | src/_cffi_src/openssl/asn1.py | 2 | ||||
-rw-r--r-- | src/cryptography/hazmat/backends/openssl/x509.py | 17 | ||||
-rw-r--r-- | src/cryptography/x509.py | 28 | ||||
-rw-r--r-- | tests/test_x509_ext.py | 24 |
5 files changed, 84 insertions, 1 deletions
diff --git a/docs/x509.rst b/docs/x509.rst index f94f50eb..eab1f53c 100644 --- a/docs/x509.rst +++ b/docs/x509.rst @@ -695,6 +695,20 @@ General Name Classes :type: :class:`ObjectIdentifier` +.. class:: OtherName + + .. versionadded:: 1.0 + + This corresponds to an "otherName." An otherName has a type identifier and a value represented in binary DER format. + + .. attribute:: type_id + + :type: :class:`ObjectIdentifier` + + .. attribute:: value + + :type: `bytes` + X.509 Extensions ~~~~~~~~~~~~~~~~ diff --git a/src/_cffi_src/openssl/asn1.py b/src/_cffi_src/openssl/asn1.py index 5210c7c9..01d6f4c2 100644 --- a/src/_cffi_src/openssl/asn1.py +++ b/src/_cffi_src/openssl/asn1.py @@ -155,6 +155,8 @@ int ASN1_UTCTIME_check(ASN1_UTCTIME *); /* Not a macro, const on openssl 1.0 */ int ASN1_STRING_set_default_mask_asc(char *); + +int i2d_ASN1_TYPE(ASN1_TYPE *, unsigned char **); """ CUSTOMIZATIONS = """ diff --git a/src/cryptography/hazmat/backends/openssl/x509.py b/src/cryptography/hazmat/backends/openssl/x509.py index 80e5f2b1..e720bfdb 100644 --- a/src/cryptography/hazmat/backends/openssl/x509.py +++ b/src/cryptography/hazmat/backends/openssl/x509.py @@ -47,6 +47,17 @@ def _asn1_string_to_utf8(backend, asn1_string): return backend._ffi.buffer(buf[0], res)[:].decode('utf8') +def _asn1_to_der(backend, asn1_type): + buf = backend._ffi.new("unsigned char **") + res = backend._lib.i2d_ASN1_TYPE(asn1_type, buf) + assert res >= 0 + assert buf[0] != backend._ffi.NULL + buf = backend._ffi.gc( + buf, lambda buffer: backend._lib.OPENSSL_free(buffer[0]) + ) + return backend._ffi.buffer(buf[0], res)[:] + + def _decode_x509_name_entry(backend, x509_name_entry): obj = backend._lib.X509_NAME_ENTRY_get_object(x509_name_entry) assert obj != backend._ffi.NULL @@ -157,8 +168,12 @@ def _decode_general_name(backend, gn): return x509.RFC822Name( parts[0] + u"@" + idna.decode(parts[1]) ) + elif gn.type == backend._lib.GEN_OTHERNAME: + type_id = _obj2txt(backend, gn.d.otherName.type_id) + value = _asn1_to_der(backend, gn.d.otherName.value) + return x509.OtherName(x509.ObjectIdentifier(type_id), value) else: - # otherName, x400Address or ediPartyName + # x400Address or ediPartyName raise x509.UnsupportedGeneralNameType( "{0} is not a supported type".format( x509._GENERAL_NAMES.get(gn.type, gn.type) diff --git a/src/cryptography/x509.py b/src/cryptography/x509.py index afd28f20..48949b61 100644 --- a/src/cryptography/x509.py +++ b/src/cryptography/x509.py @@ -1045,6 +1045,34 @@ class IPAddress(object): return not self == other +@utils.register_interface(GeneralName) +class OtherName(object): + def __init__(self, type_id, value): + if not isinstance(type_id, ObjectIdentifier): + raise TypeError("type_id must be an ObjectIdentifier") + if not isinstance(value, bytes): + raise TypeError("value must be a binary string") + + self._type_id = type_id + self._value = value + + type_id = utils.read_only_property("_type_id") + value = utils.read_only_property("_value") + + def __repr__(self): + return "<OtherName(type_id={0}, value={1!r})>".format( + self.type_id, self.value) + + def __eq__(self, other): + if not isinstance(other, OtherName): + return NotImplemented + + return self.type_id == other.type_id and self.value == other.value + + def __ne__(self, other): + return not self == other + + class GeneralNames(object): def __init__(self, general_names): if not all(isinstance(x, GeneralName) for x in general_names): diff --git a/tests/test_x509_ext.py b/tests/test_x509_ext.py index d15d6669..06adaa37 100644 --- a/tests/test_x509_ext.py +++ b/tests/test_x509_ext.py @@ -1578,6 +1578,30 @@ class TestRSASubjectAlternativeNameExtension(object): assert 'Invalid rfc822name value' in str(exc.value) + def test_other_name(self, backend): + cert = _load_cert( + os.path.join( + "x509", "custom", "san_other_name.pem" + ), + x509.load_pem_x509_certificate, + backend + ) + + ext = cert.extensions.get_extension_for_oid( + x509.OID_SUBJECT_ALTERNATIVE_NAME + ) + assert ext is not None + assert ext.critical is False + + assert len(ext.value) == 1 + assert list(ext.value)[0] == \ + x509.OtherName( + x509.ObjectIdentifier("1.2.3.4"), + b'\x16\x0bHello World') + + othernames = ext.value.get_values_for_type(x509.OtherName) + assert othernames == [b'\x16\x0bHello World'] + @pytest.mark.requires_backend_interface(interface=RSABackend) @pytest.mark.requires_backend_interface(interface=X509Backend) |