Add a bytes method to get the DER ASN.1 encoding of an X509 name. (#3236)

* Add a bytes method to get the DER ASN.1 encoding of an X509 name.

This is useful for creating an OpenSSL style subject_name_hash (#3011)

* add to backend interface and update multibackend

* bytes -> public_bytes
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 1c92ceb..9ca4c12 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -26,6 +26,7 @@
   :meth:`~cryptography.x509.random_serial_number`.
 * Added support for encoding ``IPv4Network`` and ``IPv6Network`` in X.509
   certificates for use with :class:`~cryptography.x509.NameConstraints`.
+* Added :meth:`~cryptography.x509.Name.public_bytes`.
 * Added :class:`~cryptography.x509.RelativeDistinguishedName`
 * :class:`~cryptography.x509.DistributionPoint` now accepts
   :class:`~cryptography.x509.RelativeDistinguishedName` for
diff --git a/docs/hazmat/backends/interfaces.rst b/docs/hazmat/backends/interfaces.rst
index 0a0d145..942a359 100644
--- a/docs/hazmat/backends/interfaces.rst
+++ b/docs/hazmat/backends/interfaces.rst
@@ -585,6 +585,14 @@
         :returns: A new instance of
             :class:`~cryptography.x509.RevokedCertificate`.
 
+    .. method:: x509_name_bytes(name)
+
+        .. versionadded:: 1.6
+
+        :param name: An instance of :class:`~cryptography.x509.Name`.
+
+        :return bytes: The DER encoded bytes.
+
 .. class:: DHBackend
 
     .. versionadded:: 0.9
diff --git a/docs/x509/reference.rst b/docs/x509/reference.rst
index bc408ae..d3e72c0 100644
--- a/docs/x509/reference.rst
+++ b/docs/x509/reference.rst
@@ -1142,6 +1142,16 @@
             >>> cert.subject.get_attributes_for_oid(NameOID.COMMON_NAME)
             [<NameAttribute(oid=<ObjectIdentifier(oid=2.5.4.3, name=commonName)>, value=u'Good CA')>]
 
+    .. method:: public_bytes(backend)
+
+        .. versionadded:: 1.6
+
+        :param backend: A backend supporting the
+            :class:`~cryptography.hazmat.backends.interfaces.X509Backend`
+            interface.
+
+        :return bytes: The DER encoded name.
+
 .. class:: Version
 
     .. versionadded:: 0.7
diff --git a/src/cryptography/hazmat/backends/interfaces.py b/src/cryptography/hazmat/backends/interfaces.py
index ad4a436..7417f6c 100644
--- a/src/cryptography/hazmat/backends/interfaces.py
+++ b/src/cryptography/hazmat/backends/interfaces.py
@@ -312,6 +312,12 @@
         object.
         """
 
+    @abc.abstractmethod
+    def x509_name_bytes(self, name):
+        """
+        Compute the DER encoded bytes of an X509 Name object.
+        """
+
 
 @six.add_metaclass(abc.ABCMeta)
 class DHBackend(object):
diff --git a/src/cryptography/hazmat/backends/multibackend.py b/src/cryptography/hazmat/backends/multibackend.py
index ab9127f..36a8353 100644
--- a/src/cryptography/hazmat/backends/multibackend.py
+++ b/src/cryptography/hazmat/backends/multibackend.py
@@ -424,6 +424,15 @@
             _Reasons.UNSUPPORTED_X509
         )
 
+    def x509_name_bytes(self, name):
+        for b in self._filtered_backends(X509Backend):
+            return b.x509_name_bytes(name)
+
+        raise UnsupportedAlgorithm(
+            "This backend does not support X.509.",
+            _Reasons.UNSUPPORTED_X509
+        )
+
     def derive_scrypt(self, key_material, salt, length, n, r, p):
         for b in self._filtered_backends(ScryptBackend):
             return b.derive_scrypt(key_material, salt, length, n, r, p)
diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py
index 7991429..b8e407b 100644
--- a/src/cryptography/hazmat/backends/openssl/backend.py
+++ b/src/cryptography/hazmat/backends/openssl/backend.py
@@ -1729,6 +1729,17 @@
                 serialization._ssh_write_string(public_numbers.encode_point())
             )
 
+    def x509_name_bytes(self, name):
+        x509_name = _encode_name_gc(self, name)
+        pp = self._ffi.new("unsigned char **")
+        res = self._lib.i2d_X509_NAME(x509_name, pp)
+        self.openssl_assert(pp[0] != self._ffi.NULL)
+        pp = self._ffi.gc(
+            pp, lambda pointer: self._lib.OPENSSL_free(pointer[0])
+        )
+        self.openssl_assert(res > 0)
+        return self._ffi.buffer(pp[0], res)[:]
+
     def derive_scrypt(self, key_material, salt, length, n, r, p):
         buf = self._ffi.new("unsigned char[]", length)
         res = self._lib.EVP_PBE_scrypt(key_material, len(key_material), salt,
diff --git a/src/cryptography/x509/name.py b/src/cryptography/x509/name.py
index fedfd78..277128f 100644
--- a/src/cryptography/x509/name.py
+++ b/src/cryptography/x509/name.py
@@ -109,6 +109,9 @@
     def rdns(self):
         return self._attributes
 
+    def public_bytes(self, backend):
+        return backend.x509_name_bytes(self)
+
     def __eq__(self, other):
         if not isinstance(other, Name):
             return NotImplemented
diff --git a/tests/hazmat/backends/test_multibackend.py b/tests/hazmat/backends/test_multibackend.py
index 319edf7..7ffc423 100644
--- a/tests/hazmat/backends/test_multibackend.py
+++ b/tests/hazmat/backends/test_multibackend.py
@@ -240,6 +240,9 @@
     def create_x509_revoked_certificate(self, builder):
         pass
 
+    def x509_name_bytes(self, name):
+        pass
+
 
 @utils.register_interface(ScryptBackend)
 class DummyScryptBackend(object):
@@ -554,6 +557,7 @@
         backend.create_x509_certificate(object(), b"privatekey", hashes.SHA1())
         backend.create_x509_crl(object(), b"privatekey", hashes.SHA1())
         backend.create_x509_revoked_certificate(object())
+        backend.x509_name_bytes(object())
 
         backend = MultiBackend([DummyBackend()])
         with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_X509):
@@ -580,6 +584,8 @@
             )
         with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_X509):
             backend.create_x509_revoked_certificate(object())
+        with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_X509):
+            backend.x509_name_bytes(object())
 
     def test_scrypt(self):
         backend = MultiBackend([DummyScryptBackend()])
diff --git a/tests/test_x509.py b/tests/test_x509.py
index f375ac5..5d33424 100644
--- a/tests/test_x509.py
+++ b/tests/test_x509.py
@@ -3842,6 +3842,17 @@
         with pytest.raises(TypeError):
             x509.Name(["not-a-NameAttribute"])
 
+    @pytest.mark.requires_backend_interface(interface=X509Backend)
+    def test_bytes(self, backend):
+        name = x509.Name([
+            x509.NameAttribute(NameOID.COMMON_NAME, u'cryptography.io'),
+            x509.NameAttribute(NameOID.ORGANIZATION_NAME, u'PyCA'),
+        ])
+        assert name.public_bytes(backend) == binascii.unhexlify(
+            b"30293118301606035504030c0f63727970746f6772617068792e696f310d300"
+            b"b060355040a0c0450794341"
+        )
+
 
 def test_random_serial_number(monkeypatch):
     sample_data = os.urandom(20)