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
This commit is contained in:
Paul Kehrer 2016-11-13 14:30:11 -08:00 committed by Alex Gaynor
parent d862933de5
commit 3a15b03e92
9 changed files with 65 additions and 0 deletions

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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):

View file

@ -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)

View file

@ -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,

View file

@ -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

View file

@ -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()])

View file

@ -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)