diff options
author | Paul Kehrer <paul.l.kehrer@gmail.com> | 2016-11-13 14:30:11 -0800 |
---|---|---|
committer | Alex Gaynor <alex.gaynor@gmail.com> | 2016-11-13 17:30:11 -0500 |
commit | 3a15b03e92c9fdeadff04ddd2ce505028b279b86 (patch) | |
tree | e314732deed27d3f3f4dba9b4c1f40dcca3e54fa | |
parent | d862933de5c344fcdf99ab2f43f3bf8da65f3e41 (diff) | |
download | cryptography-3a15b03e92c9fdeadff04ddd2ce505028b279b86.tar.gz cryptography-3a15b03e92c9fdeadff04ddd2ce505028b279b86.tar.bz2 cryptography-3a15b03e92c9fdeadff04ddd2ce505028b279b86.zip |
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
-rw-r--r-- | CHANGELOG.rst | 1 | ||||
-rw-r--r-- | docs/hazmat/backends/interfaces.rst | 8 | ||||
-rw-r--r-- | docs/x509/reference.rst | 10 | ||||
-rw-r--r-- | src/cryptography/hazmat/backends/interfaces.py | 6 | ||||
-rw-r--r-- | src/cryptography/hazmat/backends/multibackend.py | 9 | ||||
-rw-r--r-- | src/cryptography/hazmat/backends/openssl/backend.py | 11 | ||||
-rw-r--r-- | src/cryptography/x509/name.py | 3 | ||||
-rw-r--r-- | tests/hazmat/backends/test_multibackend.py | 6 | ||||
-rw-r--r-- | tests/test_x509.py | 11 |
9 files changed, 65 insertions, 0 deletions
diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 1c92cebf..9ca4c120 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -26,6 +26,7 @@ Changelog :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 0a0d1456..942a359c 100644 --- a/docs/hazmat/backends/interfaces.rst +++ b/docs/hazmat/backends/interfaces.rst @@ -585,6 +585,14 @@ A specific ``backend`` may provide one or more of these interfaces. :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 bc408ae6..d3e72c0a 100644 --- a/docs/x509/reference.rst +++ b/docs/x509/reference.rst @@ -1142,6 +1142,16 @@ X.509 CSR (Certificate Signing Request) Builder Object >>> 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 ad4a4364..7417f6ca 100644 --- a/src/cryptography/hazmat/backends/interfaces.py +++ b/src/cryptography/hazmat/backends/interfaces.py @@ -312,6 +312,12 @@ class X509Backend(object): 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 ab9127f7..36a83537 100644 --- a/src/cryptography/hazmat/backends/multibackend.py +++ b/src/cryptography/hazmat/backends/multibackend.py @@ -424,6 +424,15 @@ class MultiBackend(object): _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 79914293..b8e407b0 100644 --- a/src/cryptography/hazmat/backends/openssl/backend.py +++ b/src/cryptography/hazmat/backends/openssl/backend.py @@ -1729,6 +1729,17 @@ class Backend(object): 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 fedfd78f..277128fa 100644 --- a/src/cryptography/x509/name.py +++ b/src/cryptography/x509/name.py @@ -109,6 +109,9 @@ class Name(object): 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 319edf7d..7ffc4236 100644 --- a/tests/hazmat/backends/test_multibackend.py +++ b/tests/hazmat/backends/test_multibackend.py @@ -240,6 +240,9 @@ class DummyX509Backend(object): 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 @@ class TestMultiBackend(object): 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 @@ class TestMultiBackend(object): ) 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 f375ac55..5d334242 100644 --- a/tests/test_x509.py +++ b/tests/test_x509.py @@ -3842,6 +3842,17 @@ class TestName(object): 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) |