From 0ef595f1d9b5336872dc24d7d67c8cd127b31cea Mon Sep 17 00:00:00 2001 From: Andre Caron Date: Mon, 18 May 2015 13:53:43 -0400 Subject: Adds CSR builder. --- CHANGELOG.rst | 2 + docs/x509.rst | 41 ++++++ .../hazmat/backends/openssl/backend.py | 145 +++++++++++++++++++- src/cryptography/x509.py | 43 ++++++ tests/test_x509.py | 147 +++++++++++++++++++++ 5 files changed, 377 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 5c9d08ea..bc95cf74 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -24,6 +24,8 @@ Changelog and :class:`~cryptography.hazmat.primitives.kdf.concatkdf.ConcatKDFHMAC`. * Raise a ``TypeError`` when passing objects that are not text as the value to :class:`~cryptography.x509.NameAttribute`. +* Add support for creating certificate signing requests with + :class:`~cryptography.x509.CertificateSigningRequestBuilder`. 0.9.1 - 2015-06-06 ~~~~~~~~~~~~~~~~~~ diff --git a/docs/x509.rst b/docs/x509.rst index b8e3c8ee..8507edc1 100644 --- a/docs/x509.rst +++ b/docs/x509.rst @@ -468,6 +468,47 @@ X.509 Revoked Certificate Object The extensions encoded in the revoked certificate. +X.509 CSR (Certificate Signing Request) Builder Object +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. class:: CertificateSigningRequestBuilder + + .. method:: __init__() + + Creates an empty certificate signing request. + + .. method:: set_version(version) + + :param version: The :class:`Version` of the X.509 protocol. + + .. method:: set_subject_name(name) + + :param name: The :class:`Name` of the certificate subject. + + .. method:: add_extension(extension) + + :param extension: The :class:`Extension` to add to the request. + + .. method:: sign(backend, private_key, algorithm) + + :param backend: Backend that will be used to sign the request. + Must support the + :class:`~cryptography.hazmat.backends.interfaces.X509Backend` + interface. + + :param private_key: The + :class:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateKey` + that will be used to sign the request. When the request is + signed by a certificate authority, the private key's associated + public key will be stored in the resulting certificate. + + :param algorithm: The + :class:`~cryptography.hazmat.primitives.hashes.HashAlgorithm` + that will be used to generate the request signature. + + :type: :class:`CertificateSigningRequest` + + .. class:: Name .. versionadded:: 0.8 diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py index 4d469c40..f03ca077 100644 --- a/src/cryptography/hazmat/backends/openssl/backend.py +++ b/src/cryptography/hazmat/backends/openssl/backend.py @@ -10,7 +10,7 @@ from contextlib import contextmanager import six -from cryptography import utils +from cryptography import utils, x509 from cryptography.exceptions import ( InternalError, UnsupportedAlgorithm, _Reasons ) @@ -56,6 +56,88 @@ _OpenSSLError = collections.namedtuple("_OpenSSLError", ["code", "lib", "func", "reason"]) +def _encode_asn1_int(backend, x): + i = backend._lib.ASN1_INTEGER_new() + # i = backend._ffi.gc(i, backend._lib.ASN1_INTEGER_free) + backend._lib.ASN1_INTEGER_set(i, x) + return i + + +def _encode_asn1_str(backend, x, n): + s = backend._lib.ASN1_OCTET_STRING_new() + # s = backend._ffi.gc(s, backend._lib.ASN1_OCTET_STRING_free) + backend._lib.ASN1_OCTET_STRING_set(s, x, n) + return s + + +def _encode_name(backend, attributes): + resolve = { + x509.OID_COMMON_NAME: b'CN', + x509.OID_COUNTRY_NAME: b'C', + x509.OID_STATE_OR_PROVINCE_NAME: b'ST', + x509.OID_LOCALITY_NAME: b'L', + x509.OID_ORGANIZATION_NAME: b'O', + x509.OID_ORGANIZATIONAL_UNIT_NAME: b'OU', + } + subject = backend._lib.X509_NAME_new() + subject = backend._ffi.gc(subject, backend._lib.X509_NAME_free) + for attribute in attributes: + value = attribute.value + if isinstance(value, six.text_type): + value = value.encode('ascii') + res = backend._lib.X509_NAME_add_entry_by_txt( + subject, + resolve[attribute.oid], + backend._lib.MBSTRING_ASC, + value, + -1, -1, 0, + ) + assert res == 1 + return subject + + +def _txt2obj(backend, name): + if isinstance(name, six.text_type): + name = name.encode('ascii') + obj = backend._lib.OBJ_txt2obj(name, 1) + assert obj != backend._ffi.NULL + # NOTE: not sure if we should GC... + return obj + + +def _encode_basic_constraints(backend, ca=False, pathlen=0, critical=False): + obj = _txt2obj(backend, x509.OID_BASIC_CONSTRAINTS.dotted_string) + assert obj is not None + constraints = backend._lib.BASIC_CONSTRAINTS_new() + constraints.ca = 255 if ca else 0 + if ca: + constraints.pathlen = _encode_asn1_int(backend, pathlen) + + # Allocate a buffer for encoded payload. + cdata = backend._ffi.new( + 'unsigned char[]', + 2048, # TODO: shrink to fit! + ) + assert cdata != backend._ffi.NULL + + # Fetch the encoded payload. + p = backend._ffi.new('unsigned char*[1]') + assert p != backend._ffi.NULL + p[0] = cdata + r = backend._lib.i2d_BASIC_CONSTRAINTS(constraints, p) + assert r > 0 + + # Wrap that in an X509 extension object. + extension = backend._lib.X509_EXTENSION_create_by_OBJ( + backend._ffi.NULL, + obj, + 1 if critical else 0, + _encode_asn1_str(backend, cdata, r), + ) + assert extension != backend._ffi.NULL + return extension + + @utils.register_interface(CipherBackend) @utils.register_interface(CMACBackend) @utils.register_interface(DERSerializationBackend) @@ -710,6 +792,67 @@ class Backend(object): def create_cmac_ctx(self, algorithm): return _CMACContext(self, algorithm) + def sign_x509_request(self, builder, private_key, algorithm): + # TODO: check type of private key parameter. + if not isinstance(algorithm, hashes.HashAlgorithm): + raise TypeError('Algorithm must be a registered hash algorithm.') + + # Resolve the signature algorithm. + evp_md = self._lib.EVP_get_digestbyname( + algorithm.name.encode('ascii') + ) + assert evp_md != self._ffi.NULL + + # Create an empty request. + x509_req = self._lib.X509_REQ_new() + assert x509_req != self._ffi.NULL + + # Set x509 version. + res = self._lib.X509_REQ_set_version(x509_req, builder._version.value) + assert res == 1 + + # Set subject name. + res = self._lib.X509_REQ_set_subject_name( + x509_req, _encode_name(self, list(builder._subject_name)) + ) + assert res == 1 + + # Set subject public key. + public_key = private_key.public_key() + res = self._lib.X509_REQ_set_pubkey( + x509_req, public_key._evp_pkey + ) + assert res == 1 + + # Add extensions. + extensions = self._lib.sk_X509_EXTENSION_new_null() + extensions = self._ffi.gc( + extensions, + self._lib.sk_X509_EXTENSION_free, + ) + for extension in builder._extensions: + if isinstance(extension.value, x509.BasicConstraints): + extension = _encode_basic_constraints( + self, + extension.value.ca, + extension.value.path_length, + extension.critical + ) + else: + raise ValueError('Extension not yet supported.') + res = self._lib.sk_X509_EXTENSION_push(extensions, extension) + assert res == 1 + res = self._lib.X509_REQ_add_extensions(x509_req, extensions) + assert res == 1 + + # Sign the request using the requester's private key. + res = self._lib.X509_REQ_sign( + x509_req, private_key._evp_pkey, evp_md + ) + assert res > 0 + + return _CertificateSigningRequest(self, x509_req) + def load_pem_private_key(self, data, password): return self._load_key( self._lib.PEM_read_bio_PrivateKey, diff --git a/src/cryptography/x509.py b/src/cryptography/x509.py index 4b030ca9..012c13ba 100644 --- a/src/cryptography/x509.py +++ b/src/cryptography/x509.py @@ -1442,3 +1442,46 @@ class RevokedCertificate(object): """ Returns an Extensions object containing a list of Revoked extensions. """ + + +class CertificateSigningRequestBuilder(object): + def __init__(self): + """ + Creates an empty X.509 certificate request (v1). + """ + self._version = Version.v1 + self._subject_name = None + self._extensions = [] + + def set_version(self, version): + """ + Sets the X.509 version. + """ + if not isinstance(version, Version): + raise TypeError('Expecting x509.Version object.') + self._version = version + + def set_subject_name(self, name): + """ + Sets the certificate requestor's distinguished name. + """ + if not isinstance(name, Name): + raise TypeError('Expecting x509.Name object.') + self._subject_name = name + + def add_extension(self, extension): + """ + Adds an X.509 extension to the certificate request. + """ + if not isinstance(extension, Extension): + raise TypeError('Expecting x509.Extension object.') + for e in self._extensions: + if e.oid == extension.oid: + raise ValueError('This extension has already been set.') + self._extensions.append(extension) + + def sign(self, backend, private_key, algorithm): + """ + Signs the request using the requestor's private key. + """ + return backend.sign_x509_request(self, private_key, algorithm) diff --git a/tests/test_x509.py b/tests/test_x509.py index cf3499bf..85ef4b5c 100644 --- a/tests/test_x509.py +++ b/tests/test_x509.py @@ -679,6 +679,153 @@ class TestRSACertificateRequest(object): assert serialized == request_bytes +@pytest.mark.requires_backend_interface(interface=RSABackend) +@pytest.mark.requires_backend_interface(interface=X509Backend) +class TestCertificateSigningRequestBuilder(object): + def test_sign_invalid_hash_algorithm(self, backend): + private_key = rsa.generate_private_key( + public_exponent=65537, + key_size=2048, + backend=backend, + ) + builder = x509.CertificateSigningRequestBuilder() + with pytest.raises(TypeError): + builder.sign(backend, private_key, 'NotAHash') + + def test_build_ca_request(self, backend): + private_key = rsa.generate_private_key( + public_exponent=65537, + key_size=2048, + backend=backend, + ) + + builder = x509.CertificateSigningRequestBuilder() + builder.set_version(x509.Version.v3) + builder.set_subject_name(x509.Name([ + x509.NameAttribute(x509.OID_COUNTRY_NAME, 'US'), + x509.NameAttribute(x509.OID_STATE_OR_PROVINCE_NAME, 'Texas'), + x509.NameAttribute(x509.OID_LOCALITY_NAME, 'Austin'), + x509.NameAttribute(x509.OID_ORGANIZATION_NAME, 'PyCA'), + x509.NameAttribute(x509.OID_COMMON_NAME, 'cryptography.io'), + ])) + builder.add_extension(x509.Extension( + x509.OID_BASIC_CONSTRAINTS, + True, + x509.BasicConstraints(True, 2), + )) + request = builder.sign(backend, private_key, hashes.SHA1()) + + assert isinstance(request.signature_hash_algorithm, hashes.SHA1) + public_key = request.public_key() + assert isinstance(public_key, rsa.RSAPublicKey) + subject = request.subject + assert isinstance(subject, x509.Name) + assert list(subject) == [ + x509.NameAttribute(x509.OID_COUNTRY_NAME, 'US'), + x509.NameAttribute(x509.OID_STATE_OR_PROVINCE_NAME, 'Texas'), + x509.NameAttribute(x509.OID_LOCALITY_NAME, 'Austin'), + x509.NameAttribute(x509.OID_ORGANIZATION_NAME, 'PyCA'), + x509.NameAttribute(x509.OID_COMMON_NAME, 'cryptography.io'), + ] + basic_constraints = request.extensions.get_extension_for_oid( + x509.OID_BASIC_CONSTRAINTS + ) + assert basic_constraints.value.ca is True + assert basic_constraints.value.path_length == 2 + + def test_build_nonca_request(self, backend): + private_key = rsa.generate_private_key( + public_exponent=65537, + key_size=2048, + backend=backend, + ) + + builder = x509.CertificateSigningRequestBuilder() + builder.set_version(x509.Version.v3) + builder.set_subject_name(x509.Name([ + x509.NameAttribute(x509.OID_COUNTRY_NAME, 'US'), + x509.NameAttribute(x509.OID_STATE_OR_PROVINCE_NAME, 'Texas'), + x509.NameAttribute(x509.OID_LOCALITY_NAME, 'Austin'), + x509.NameAttribute(x509.OID_ORGANIZATION_NAME, 'PyCA'), + x509.NameAttribute(x509.OID_COMMON_NAME, 'cryptography.io'), + ])) + builder.add_extension(x509.Extension( + x509.OID_BASIC_CONSTRAINTS, + True, + x509.BasicConstraints(False, None), + )) + request = builder.sign(backend, private_key, hashes.SHA1()) + + assert isinstance(request.signature_hash_algorithm, hashes.SHA1) + public_key = request.public_key() + assert isinstance(public_key, rsa.RSAPublicKey) + subject = request.subject + assert isinstance(subject, x509.Name) + assert list(subject) == [ + x509.NameAttribute(x509.OID_COUNTRY_NAME, 'US'), + x509.NameAttribute(x509.OID_STATE_OR_PROVINCE_NAME, 'Texas'), + x509.NameAttribute(x509.OID_LOCALITY_NAME, 'Austin'), + x509.NameAttribute(x509.OID_ORGANIZATION_NAME, 'PyCA'), + x509.NameAttribute(x509.OID_COMMON_NAME, 'cryptography.io'), + ] + basic_constraints = request.extensions.get_extension_for_oid( + x509.OID_BASIC_CONSTRAINTS + ) + assert basic_constraints.value.ca is False + assert basic_constraints.value.path_length is None + + def test_add_duplicate_extension(self, backend): + builder = x509.CertificateSigningRequestBuilder() + builder.add_extension(x509.Extension( + x509.OID_BASIC_CONSTRAINTS, + True, + x509.BasicConstraints(True, 2), + )) + with pytest.raises(ValueError): + builder.add_extension(x509.Extension( + x509.OID_BASIC_CONSTRAINTS, + True, + x509.BasicConstraints(True, 2), + )) + + def test_add_invalid_extension(self, backend): + builder = x509.CertificateSigningRequestBuilder() + with pytest.raises(TypeError): + builder.add_extension('NotAnExtension') + + def test_set_invalid_subject(self, backend): + builder = x509.CertificateSigningRequestBuilder() + with pytest.raises(TypeError): + builder.set_subject_name('NotAName') + + def test_set_invalid_version(self, backend): + builder = x509.CertificateSigningRequestBuilder() + with pytest.raises(TypeError): + builder.set_version('NotAVersion') + + def test_add_unsupported_extension(self, backend): + private_key = rsa.generate_private_key( + public_exponent=65537, + key_size=2048, + backend=backend, + ) + builder = x509.CertificateSigningRequestBuilder() + builder.set_subject_name(x509.Name([ + x509.NameAttribute(x509.OID_COUNTRY_NAME, u'US'), + x509.NameAttribute(x509.OID_STATE_OR_PROVINCE_NAME, u'Texas'), + x509.NameAttribute(x509.OID_LOCALITY_NAME, u'Austin'), + x509.NameAttribute(x509.OID_ORGANIZATION_NAME, u'PyCA'), + x509.NameAttribute(x509.OID_COMMON_NAME, u'cryptography.io'), + ])) + builder.add_extension(x509.Extension( + x509.ObjectIdentifier('1.2.3.4'), + False, + 'value', + )) + with pytest.raises(ValueError): + builder.sign(backend, private_key, hashes.SHA1()) + + @pytest.mark.requires_backend_interface(interface=DSABackend) @pytest.mark.requires_backend_interface(interface=X509Backend) class TestDSACertificate(object): -- cgit v1.2.3 From ca4c4462dcdbb72f653801f608c85696a4630349 Mon Sep 17 00:00:00 2001 From: Andre Caron Date: Tue, 19 May 2015 08:48:12 -0400 Subject: Fixes memory allocation. --- src/cryptography/hazmat/backends/openssl/backend.py | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py index f03ca077..bf838ead 100644 --- a/src/cryptography/hazmat/backends/openssl/backend.py +++ b/src/cryptography/hazmat/backends/openssl/backend.py @@ -113,18 +113,10 @@ def _encode_basic_constraints(backend, ca=False, pathlen=0, critical=False): if ca: constraints.pathlen = _encode_asn1_int(backend, pathlen) - # Allocate a buffer for encoded payload. - cdata = backend._ffi.new( - 'unsigned char[]', - 2048, # TODO: shrink to fit! - ) - assert cdata != backend._ffi.NULL - # Fetch the encoded payload. - p = backend._ffi.new('unsigned char*[1]') - assert p != backend._ffi.NULL - p[0] = cdata - r = backend._lib.i2d_BASIC_CONSTRAINTS(constraints, p) + pp = backend._ffi.new('unsigned char**') + assert pp != backend._ffi.NULL + r = backend._lib.i2d_BASIC_CONSTRAINTS(constraints, pp) assert r > 0 # Wrap that in an X509 extension object. @@ -132,7 +124,7 @@ def _encode_basic_constraints(backend, ca=False, pathlen=0, critical=False): backend._ffi.NULL, obj, 1 if critical else 0, - _encode_asn1_str(backend, cdata, r), + _encode_asn1_str(backend, pp[0], r), ) assert extension != backend._ffi.NULL return extension -- cgit v1.2.3 From a33ea283d74c85076a21e60e1f09e4998f5c7c48 Mon Sep 17 00:00:00 2001 From: Andre Caron Date: Sun, 31 May 2015 16:32:26 -0400 Subject: Renames sign_509_request to create_x509_csr. --- src/cryptography/hazmat/backends/interfaces.py | 6 ++++++ src/cryptography/hazmat/backends/multibackend.py | 9 +++++++++ src/cryptography/hazmat/backends/openssl/backend.py | 2 +- src/cryptography/x509.py | 2 +- tests/hazmat/backends/test_multibackend.py | 6 ++++++ 5 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/cryptography/hazmat/backends/interfaces.py b/src/cryptography/hazmat/backends/interfaces.py index eca7ddf4..512cb6e3 100644 --- a/src/cryptography/hazmat/backends/interfaces.py +++ b/src/cryptography/hazmat/backends/interfaces.py @@ -274,6 +274,12 @@ class X509Backend(object): Load an X.509 CSR from PEM encoded data. """ + @abc.abstractmethod + def create_x509_csr(self, builder, private_key, algorithm): + """ + Create and sign an X.509 CSR from a CSR buidler 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 784ab84d..6e911fd5 100644 --- a/src/cryptography/hazmat/backends/multibackend.py +++ b/src/cryptography/hazmat/backends/multibackend.py @@ -342,3 +342,12 @@ class MultiBackend(object): "This backend does not support X.509.", _Reasons.UNSUPPORTED_X509 ) + + def create_x509_csr(self, builder, private_key, algorithm): + for b in self._filtered_backends(X509Backend): + return b.create_x509_csr(builder, private_key, algorithm) + + raise UnsupportedAlgorithm( + "This backend does not support X.509.", + _Reasons.UNSUPPORTED_X509 + ) diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py index bf838ead..b8b2ab6b 100644 --- a/src/cryptography/hazmat/backends/openssl/backend.py +++ b/src/cryptography/hazmat/backends/openssl/backend.py @@ -784,7 +784,7 @@ class Backend(object): def create_cmac_ctx(self, algorithm): return _CMACContext(self, algorithm) - def sign_x509_request(self, builder, private_key, algorithm): + def create_x509_csr(self, builder, private_key, algorithm): # TODO: check type of private key parameter. if not isinstance(algorithm, hashes.HashAlgorithm): raise TypeError('Algorithm must be a registered hash algorithm.') diff --git a/src/cryptography/x509.py b/src/cryptography/x509.py index 012c13ba..2ee1c3ef 100644 --- a/src/cryptography/x509.py +++ b/src/cryptography/x509.py @@ -1484,4 +1484,4 @@ class CertificateSigningRequestBuilder(object): """ Signs the request using the requestor's private key. """ - return backend.sign_x509_request(self, private_key, algorithm) + return backend.create_x509_csr(self, private_key, algorithm) diff --git a/tests/hazmat/backends/test_multibackend.py b/tests/hazmat/backends/test_multibackend.py index 5871e6c8..3c05cdfa 100644 --- a/tests/hazmat/backends/test_multibackend.py +++ b/tests/hazmat/backends/test_multibackend.py @@ -203,6 +203,9 @@ class DummyX509Backend(object): def load_der_x509_csr(self, data): pass + def create_x509_csr(self, builder, private_key, algorithm): + pass + class TestMultiBackend(object): def test_ciphers(self): @@ -480,6 +483,7 @@ class TestMultiBackend(object): backend.load_der_x509_certificate(b"certdata") backend.load_pem_x509_csr(b"reqdata") backend.load_der_x509_csr(b"reqdata") + backend.create_x509_csr(object(), b"privatekey", hashes.SHA1()) backend = MultiBackend([]) with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_X509): @@ -490,3 +494,5 @@ class TestMultiBackend(object): backend.load_pem_x509_csr(b"reqdata") with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_X509): backend.load_der_x509_csr(b"reqdata") + with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_X509): + backend.create_x509_csr(object(), b"privatekey", hashes.SHA1()) -- cgit v1.2.3 From b1103d25a72818d7fd055f17d8008d0ac99f8b95 Mon Sep 17 00:00:00 2001 From: Andre Caron Date: Sun, 31 May 2015 16:58:22 -0400 Subject: Cleans up some GC semantics. --- src/cryptography/hazmat/backends/openssl/backend.py | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py index b8b2ab6b..08e56d07 100644 --- a/src/cryptography/hazmat/backends/openssl/backend.py +++ b/src/cryptography/hazmat/backends/openssl/backend.py @@ -57,15 +57,24 @@ _OpenSSLError = collections.namedtuple("_OpenSSLError", def _encode_asn1_int(backend, x): + """ + Converts a python integer to a ASN1_INTEGER. The returned ASN1_INTEGER will + not be garbage collected (to support adding them to structs that take + ownership of the object). Be sure to register it for GC if it will be + discarded after use. + + """ i = backend._lib.ASN1_INTEGER_new() - # i = backend._ffi.gc(i, backend._lib.ASN1_INTEGER_free) backend._lib.ASN1_INTEGER_set(i, x) return i def _encode_asn1_str(backend, x, n): + """ + Create an ASN1_OCTET_STRING from a Python byte string. + """ s = backend._lib.ASN1_OCTET_STRING_new() - # s = backend._ffi.gc(s, backend._lib.ASN1_OCTET_STRING_free) + s = backend._ffi.gc(s, backend._lib.ASN1_OCTET_STRING_free) backend._lib.ASN1_OCTET_STRING_set(s, x, n) return s @@ -97,11 +106,15 @@ def _encode_name(backend, attributes): def _txt2obj(backend, name): + """ + Converts a Python string with an ASN.1 object ID in dotted form to a + ASN1_OBJECT. + """ if isinstance(name, six.text_type): name = name.encode('ascii') obj = backend._lib.OBJ_txt2obj(name, 1) assert obj != backend._ffi.NULL - # NOTE: not sure if we should GC... + obj = backend._ffi.gc(obj, backend._lib.ASN1_OBJECT_free) return obj -- cgit v1.2.3 From eebe700861774a640f82391d2a4597d2fe6ff399 Mon Sep 17 00:00:00 2001 From: Andre Caron Date: Sun, 31 May 2015 17:15:36 -0400 Subject: Removes OID to txt to OID conversion. --- src/cryptography/hazmat/backends/openssl/backend.py | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py index 08e56d07..c509ddb3 100644 --- a/src/cryptography/hazmat/backends/openssl/backend.py +++ b/src/cryptography/hazmat/backends/openssl/backend.py @@ -80,23 +80,16 @@ def _encode_asn1_str(backend, x, n): def _encode_name(backend, attributes): - resolve = { - x509.OID_COMMON_NAME: b'CN', - x509.OID_COUNTRY_NAME: b'C', - x509.OID_STATE_OR_PROVINCE_NAME: b'ST', - x509.OID_LOCALITY_NAME: b'L', - x509.OID_ORGANIZATION_NAME: b'O', - x509.OID_ORGANIZATIONAL_UNIT_NAME: b'OU', - } subject = backend._lib.X509_NAME_new() subject = backend._ffi.gc(subject, backend._lib.X509_NAME_free) for attribute in attributes: value = attribute.value if isinstance(value, six.text_type): value = value.encode('ascii') - res = backend._lib.X509_NAME_add_entry_by_txt( + obj = _txt2obj(backend, attribute.oid.dotted_string) + res = backend._lib.X509_NAME_add_entry_by_OBJ( subject, - resolve[attribute.oid], + obj, backend._lib.MBSTRING_ASC, value, -1, -1, 0, -- cgit v1.2.3 From fc164c5e4fce2f6617b35887a5799ec10082b906 Mon Sep 17 00:00:00 2001 From: Andre Caron Date: Sun, 31 May 2015 17:36:18 -0400 Subject: Adds method chaining to CSR builder. --- src/cryptography/x509.py | 18 ++++++++++----- tests/test_x509.py | 59 ++++++++++++++++++++++++------------------------ 2 files changed, 42 insertions(+), 35 deletions(-) diff --git a/src/cryptography/x509.py b/src/cryptography/x509.py index 2ee1c3ef..c59de606 100644 --- a/src/cryptography/x509.py +++ b/src/cryptography/x509.py @@ -1445,13 +1445,13 @@ class RevokedCertificate(object): class CertificateSigningRequestBuilder(object): - def __init__(self): + def __init__(self, version=Version.v1, subject_name=None, extensions=[]): """ Creates an empty X.509 certificate request (v1). """ self._version = Version.v1 - self._subject_name = None - self._extensions = [] + self._subject_name = subject_name + self._extensions = extensions[:] def set_version(self, version): """ @@ -1459,7 +1459,9 @@ class CertificateSigningRequestBuilder(object): """ if not isinstance(version, Version): raise TypeError('Expecting x509.Version object.') - self._version = version + return CertificateSigningRequestBuilder( + version, self._subject_name, self._extensions + ) def set_subject_name(self, name): """ @@ -1467,7 +1469,9 @@ class CertificateSigningRequestBuilder(object): """ if not isinstance(name, Name): raise TypeError('Expecting x509.Name object.') - self._subject_name = name + return CertificateSigningRequestBuilder( + self._version, name, self._extensions + ) def add_extension(self, extension): """ @@ -1478,7 +1482,9 @@ class CertificateSigningRequestBuilder(object): for e in self._extensions: if e.oid == extension.oid: raise ValueError('This extension has already been set.') - self._extensions.append(extension) + return CertificateSigningRequestBuilder( + self._version, self._subject_name, self._extensions + [extension] + ) def sign(self, backend, private_key, algorithm): """ diff --git a/tests/test_x509.py b/tests/test_x509.py index 85ef4b5c..981ad528 100644 --- a/tests/test_x509.py +++ b/tests/test_x509.py @@ -699,21 +699,21 @@ class TestCertificateSigningRequestBuilder(object): backend=backend, ) - builder = x509.CertificateSigningRequestBuilder() - builder.set_version(x509.Version.v3) - builder.set_subject_name(x509.Name([ + request = x509.CertificateSigningRequestBuilder().set_version( + x509.Version.v3 + ).set_subject_name(x509.Name([ x509.NameAttribute(x509.OID_COUNTRY_NAME, 'US'), x509.NameAttribute(x509.OID_STATE_OR_PROVINCE_NAME, 'Texas'), x509.NameAttribute(x509.OID_LOCALITY_NAME, 'Austin'), x509.NameAttribute(x509.OID_ORGANIZATION_NAME, 'PyCA'), x509.NameAttribute(x509.OID_COMMON_NAME, 'cryptography.io'), - ])) - builder.add_extension(x509.Extension( + ])).add_extension(x509.Extension( x509.OID_BASIC_CONSTRAINTS, True, x509.BasicConstraints(True, 2), - )) - request = builder.sign(backend, private_key, hashes.SHA1()) + )).sign( + backend, private_key, hashes.SHA1() + ) assert isinstance(request.signature_hash_algorithm, hashes.SHA1) public_key = request.public_key() @@ -740,21 +740,21 @@ class TestCertificateSigningRequestBuilder(object): backend=backend, ) - builder = x509.CertificateSigningRequestBuilder() - builder.set_version(x509.Version.v3) - builder.set_subject_name(x509.Name([ + request = x509.CertificateSigningRequestBuilder().set_version( + x509.Version.v3 + ).set_subject_name(x509.Name([ x509.NameAttribute(x509.OID_COUNTRY_NAME, 'US'), x509.NameAttribute(x509.OID_STATE_OR_PROVINCE_NAME, 'Texas'), x509.NameAttribute(x509.OID_LOCALITY_NAME, 'Austin'), x509.NameAttribute(x509.OID_ORGANIZATION_NAME, 'PyCA'), x509.NameAttribute(x509.OID_COMMON_NAME, 'cryptography.io'), - ])) - builder.add_extension(x509.Extension( + ])).add_extension(x509.Extension( x509.OID_BASIC_CONSTRAINTS, True, x509.BasicConstraints(False, None), - )) - request = builder.sign(backend, private_key, hashes.SHA1()) + )).sign( + backend, private_key, hashes.SHA1() + ) assert isinstance(request.signature_hash_algorithm, hashes.SHA1) public_key = request.public_key() @@ -775,12 +775,13 @@ class TestCertificateSigningRequestBuilder(object): assert basic_constraints.value.path_length is None def test_add_duplicate_extension(self, backend): - builder = x509.CertificateSigningRequestBuilder() - builder.add_extension(x509.Extension( - x509.OID_BASIC_CONSTRAINTS, - True, - x509.BasicConstraints(True, 2), - )) + builder = x509.CertificateSigningRequestBuilder().add_extension( + x509.Extension( + x509.OID_BASIC_CONSTRAINTS, + True, + x509.BasicConstraints(True, 2), + ) + ) with pytest.raises(ValueError): builder.add_extension(x509.Extension( x509.OID_BASIC_CONSTRAINTS, @@ -809,15 +810,15 @@ class TestCertificateSigningRequestBuilder(object): key_size=2048, backend=backend, ) - builder = x509.CertificateSigningRequestBuilder() - builder.set_subject_name(x509.Name([ - x509.NameAttribute(x509.OID_COUNTRY_NAME, u'US'), - x509.NameAttribute(x509.OID_STATE_OR_PROVINCE_NAME, u'Texas'), - x509.NameAttribute(x509.OID_LOCALITY_NAME, u'Austin'), - x509.NameAttribute(x509.OID_ORGANIZATION_NAME, u'PyCA'), - x509.NameAttribute(x509.OID_COMMON_NAME, u'cryptography.io'), - ])) - builder.add_extension(x509.Extension( + builder = x509.CertificateSigningRequestBuilder().set_subject_name( + x509.Name([ + x509.NameAttribute(x509.OID_COUNTRY_NAME, u'US'), + x509.NameAttribute(x509.OID_STATE_OR_PROVINCE_NAME, u'Texas'), + x509.NameAttribute(x509.OID_LOCALITY_NAME, u'Austin'), + x509.NameAttribute(x509.OID_ORGANIZATION_NAME, u'PyCA'), + x509.NameAttribute(x509.OID_COMMON_NAME, u'cryptography.io'), + ]) + ).add_extension(x509.Extension( x509.ObjectIdentifier('1.2.3.4'), False, 'value', -- cgit v1.2.3 From 6d7e39b039c6c1f56084088b96a4a73566b25844 Mon Sep 17 00:00:00 2001 From: Andre Caron Date: Sun, 31 May 2015 18:10:04 -0400 Subject: Adds updates docs for method chaining in CSR builder. --- docs/x509.rst | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/x509.rst b/docs/x509.rst index 8507edc1..f79f630c 100644 --- a/docs/x509.rst +++ b/docs/x509.rst @@ -479,15 +479,18 @@ X.509 CSR (Certificate Signing Request) Builder Object .. method:: set_version(version) - :param version: The :class:`Version` of the X.509 protocol. + :param version: The :class:`Version` of the X.509 protocol. + :returns: A new `CertificateSigningRequestBuilder`. .. method:: set_subject_name(name) :param name: The :class:`Name` of the certificate subject. + :returns: A new `CertificateSigningRequestBuilder`. .. method:: add_extension(extension) :param extension: The :class:`Extension` to add to the request. + :returns: A new `CertificateSigningRequestBuilder`. .. method:: sign(backend, private_key, algorithm) -- cgit v1.2.3 From dafd5c28b9d512547be1018db08b2d3a815a76b4 Mon Sep 17 00:00:00 2001 From: Andre Caron Date: Sun, 31 May 2015 23:06:26 -0400 Subject: Fixes docstring typo. --- src/cryptography/hazmat/backends/interfaces.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cryptography/hazmat/backends/interfaces.py b/src/cryptography/hazmat/backends/interfaces.py index 512cb6e3..4d378e6b 100644 --- a/src/cryptography/hazmat/backends/interfaces.py +++ b/src/cryptography/hazmat/backends/interfaces.py @@ -277,7 +277,7 @@ class X509Backend(object): @abc.abstractmethod def create_x509_csr(self, builder, private_key, algorithm): """ - Create and sign an X.509 CSR from a CSR buidler object. + Create and sign an X.509 CSR from a CSR builder object. """ -- cgit v1.2.3 From ce02de706e4ec0a3945240096d4fd4d92478effe Mon Sep 17 00:00:00 2001 From: Andre Caron Date: Sun, 31 May 2015 23:06:53 -0400 Subject: Extends supported range for integer conversion, --- src/cryptography/hazmat/backends/openssl/backend.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py index c509ddb3..70ed25d6 100644 --- a/src/cryptography/hazmat/backends/openssl/backend.py +++ b/src/cryptography/hazmat/backends/openssl/backend.py @@ -64,8 +64,15 @@ def _encode_asn1_int(backend, x): discarded after use. """ - i = backend._lib.ASN1_INTEGER_new() - backend._lib.ASN1_INTEGER_set(i, x) + # Convert Python integer to OpenSSL "bignum" in case value exceeds + # machine's native integer limits (note: `int_to_bn` doesn't automatically + # GC). + i = backend._int_to_bn(x) + i = backend._ffi.gc(i, backend._lib.BN_free) + + # Wrap in a ASN.1 integer. Don't GC -- as documented. + i = backend._lib.BN_to_ASN1_INTEGER(i, backend._ffi.NULL) + assert i != backend._ffi.NULL return i -- cgit v1.2.3 From 0fdf009ea0dc96a1d70ab0cded11d5846b03d4e2 Mon Sep 17 00:00:00 2001 From: Andre Caron Date: Sun, 31 May 2015 23:07:06 -0400 Subject: Fixes memory leak, --- src/cryptography/hazmat/backends/openssl/backend.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py index 70ed25d6..a6acb076 100644 --- a/src/cryptography/hazmat/backends/openssl/backend.py +++ b/src/cryptography/hazmat/backends/openssl/backend.py @@ -140,6 +140,12 @@ def _encode_basic_constraints(backend, ca=False, pathlen=0, critical=False): _encode_asn1_str(backend, pp[0], r), ) assert extension != backend._ffi.NULL + + # Release acquired memory. + backend._lib.OPENSSL_free(pp[0]) + pp[0] = backend._ffi.NULL + + # Return the wrapped extension. return extension -- cgit v1.2.3 From 99d0f90ff256b540acb007458bbb07c467642368 Mon Sep 17 00:00:00 2001 From: Andre Caron Date: Mon, 1 Jun 2015 08:36:59 -0400 Subject: Removes CSR builder version setter. --- .../hazmat/backends/openssl/backend.py | 2 +- src/cryptography/x509.py | 19 ++-------- tests/test_x509.py | 41 ++++++++++------------ 3 files changed, 22 insertions(+), 40 deletions(-) diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py index a6acb076..c32b5270 100644 --- a/src/cryptography/hazmat/backends/openssl/backend.py +++ b/src/cryptography/hazmat/backends/openssl/backend.py @@ -819,7 +819,7 @@ class Backend(object): assert x509_req != self._ffi.NULL # Set x509 version. - res = self._lib.X509_REQ_set_version(x509_req, builder._version.value) + res = self._lib.X509_REQ_set_version(x509_req, x509.Version.v1.value) assert res == 1 # Set subject name. diff --git a/src/cryptography/x509.py b/src/cryptography/x509.py index c59de606..b1aa0679 100644 --- a/src/cryptography/x509.py +++ b/src/cryptography/x509.py @@ -1445,33 +1445,20 @@ class RevokedCertificate(object): class CertificateSigningRequestBuilder(object): - def __init__(self, version=Version.v1, subject_name=None, extensions=[]): + def __init__(self, subject_name=None, extensions=[]): """ Creates an empty X.509 certificate request (v1). """ - self._version = Version.v1 self._subject_name = subject_name self._extensions = extensions[:] - def set_version(self, version): - """ - Sets the X.509 version. - """ - if not isinstance(version, Version): - raise TypeError('Expecting x509.Version object.') - return CertificateSigningRequestBuilder( - version, self._subject_name, self._extensions - ) - def set_subject_name(self, name): """ Sets the certificate requestor's distinguished name. """ if not isinstance(name, Name): raise TypeError('Expecting x509.Name object.') - return CertificateSigningRequestBuilder( - self._version, name, self._extensions - ) + return CertificateSigningRequestBuilder(name, self._extensions) def add_extension(self, extension): """ @@ -1483,7 +1470,7 @@ class CertificateSigningRequestBuilder(object): if e.oid == extension.oid: raise ValueError('This extension has already been set.') return CertificateSigningRequestBuilder( - self._version, self._subject_name, self._extensions + [extension] + self._subject_name, self._extensions + [extension] ) def sign(self, backend, private_key, algorithm): diff --git a/tests/test_x509.py b/tests/test_x509.py index 981ad528..aadbed02 100644 --- a/tests/test_x509.py +++ b/tests/test_x509.py @@ -699,15 +699,15 @@ class TestCertificateSigningRequestBuilder(object): backend=backend, ) - request = x509.CertificateSigningRequestBuilder().set_version( - x509.Version.v3 - ).set_subject_name(x509.Name([ - x509.NameAttribute(x509.OID_COUNTRY_NAME, 'US'), - x509.NameAttribute(x509.OID_STATE_OR_PROVINCE_NAME, 'Texas'), - x509.NameAttribute(x509.OID_LOCALITY_NAME, 'Austin'), - x509.NameAttribute(x509.OID_ORGANIZATION_NAME, 'PyCA'), - x509.NameAttribute(x509.OID_COMMON_NAME, 'cryptography.io'), - ])).add_extension(x509.Extension( + request = x509.CertificateSigningRequestBuilder().set_subject_name( + x509.Name([ + x509.NameAttribute(x509.OID_COUNTRY_NAME, 'US'), + x509.NameAttribute(x509.OID_STATE_OR_PROVINCE_NAME, 'Texas'), + x509.NameAttribute(x509.OID_LOCALITY_NAME, 'Austin'), + x509.NameAttribute(x509.OID_ORGANIZATION_NAME, 'PyCA'), + x509.NameAttribute(x509.OID_COMMON_NAME, 'cryptography.io'), + ]) + ).add_extension(x509.Extension( x509.OID_BASIC_CONSTRAINTS, True, x509.BasicConstraints(True, 2), @@ -740,15 +740,15 @@ class TestCertificateSigningRequestBuilder(object): backend=backend, ) - request = x509.CertificateSigningRequestBuilder().set_version( - x509.Version.v3 - ).set_subject_name(x509.Name([ - x509.NameAttribute(x509.OID_COUNTRY_NAME, 'US'), - x509.NameAttribute(x509.OID_STATE_OR_PROVINCE_NAME, 'Texas'), - x509.NameAttribute(x509.OID_LOCALITY_NAME, 'Austin'), - x509.NameAttribute(x509.OID_ORGANIZATION_NAME, 'PyCA'), - x509.NameAttribute(x509.OID_COMMON_NAME, 'cryptography.io'), - ])).add_extension(x509.Extension( + request = x509.CertificateSigningRequestBuilder().set_subject_name( + x509.Name([ + x509.NameAttribute(x509.OID_COUNTRY_NAME, 'US'), + x509.NameAttribute(x509.OID_STATE_OR_PROVINCE_NAME, 'Texas'), + x509.NameAttribute(x509.OID_LOCALITY_NAME, 'Austin'), + x509.NameAttribute(x509.OID_ORGANIZATION_NAME, 'PyCA'), + x509.NameAttribute(x509.OID_COMMON_NAME, 'cryptography.io'), + ]) + ).add_extension(x509.Extension( x509.OID_BASIC_CONSTRAINTS, True, x509.BasicConstraints(False, None), @@ -799,11 +799,6 @@ class TestCertificateSigningRequestBuilder(object): with pytest.raises(TypeError): builder.set_subject_name('NotAName') - def test_set_invalid_version(self, backend): - builder = x509.CertificateSigningRequestBuilder() - with pytest.raises(TypeError): - builder.set_version('NotAVersion') - def test_add_unsupported_extension(self, backend): private_key = rsa.generate_private_key( public_exponent=65537, -- cgit v1.2.3 From d259ee51abae5a35e34f16ad74bfb1c62aa433d7 Mon Sep 17 00:00:00 2001 From: Andre Caron Date: Tue, 2 Jun 2015 23:47:11 -0400 Subject: Polishes builder documentation, --- docs/x509.rst | 33 ++++++++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/docs/x509.rst b/docs/x509.rst index f79f630c..a2a3ded7 100644 --- a/docs/x509.rst +++ b/docs/x509.rst @@ -473,14 +473,37 @@ X.509 CSR (Certificate Signing Request) Builder Object .. class:: CertificateSigningRequestBuilder - .. method:: __init__() + .. versionadded:: 1.0 - Creates an empty certificate signing request. + .. doctest:: - .. method:: set_version(version) + >>> from cryptography import x509 + >>> from cryptography.hazmat.backends import default_backend + >>> from cryptography.hazmat.primitives import hashes + >>> from cryptography.hazmat.primitives.asymmetric import rsa + >>> private_key = rsa.generate_private_key( + ... public_exponent=65537, + ... key_size=2048, + ... backend=default_backend() + ... ) + >>> builder = x509.CertificateSigningRequestBuilder() + >>> builder = builder.set_subject_name(x509.Name([ + ... x509.NameAttribute(x509.OID_COMMON_NAME, 'cryptography.io'), + ... ])) + >>> buidlder = builder.add_extension(x509.Extension( + ... x509.OID_BASIC_CONSTRAINTS, + ... True, + ... x509.BasicConstraints(False, None), + ... )) + >>> request = builder.sign( + ... default_backend(), private_key, hashes.SHA1() + ... ) + >>> isinstance(request, x509.CertificateSigningRequest) + True - :param version: The :class:`Version` of the X.509 protocol. - :returns: A new `CertificateSigningRequestBuilder`. + .. method:: __init__() + + Creates an empty certificate signing request. .. method:: set_subject_name(name) -- cgit v1.2.3 From 472fd6991e05735e00fdca7fbe2573a44fdabd17 Mon Sep 17 00:00:00 2001 From: Andre Caron Date: Sat, 6 Jun 2015 20:04:44 -0400 Subject: Changes builder extension API. --- docs/x509.rst | 12 ++++++------ src/cryptography/x509.py | 8 +++++--- tests/test_x509.py | 45 ++++++++++++++------------------------------- 3 files changed, 25 insertions(+), 40 deletions(-) diff --git a/docs/x509.rst b/docs/x509.rst index a2a3ded7..52117c84 100644 --- a/docs/x509.rst +++ b/docs/x509.rst @@ -490,11 +490,9 @@ X.509 CSR (Certificate Signing Request) Builder Object >>> builder = builder.set_subject_name(x509.Name([ ... x509.NameAttribute(x509.OID_COMMON_NAME, 'cryptography.io'), ... ])) - >>> buidlder = builder.add_extension(x509.Extension( - ... x509.OID_BASIC_CONSTRAINTS, - ... True, - ... x509.BasicConstraints(False, None), - ... )) + >>> buidlder = builder.add_extension( + ... x509.BasicConstraints(False, None), critical=True, + ... ) >>> request = builder.sign( ... default_backend(), private_key, hashes.SHA1() ... ) @@ -510,9 +508,11 @@ X.509 CSR (Certificate Signing Request) Builder Object :param name: The :class:`Name` of the certificate subject. :returns: A new `CertificateSigningRequestBuilder`. - .. method:: add_extension(extension) + .. method:: add_extension(extension, critical=False) :param extension: The :class:`Extension` to add to the request. + :param critical: Set to `True` if the extension must be understood and + handled by whoever reads the certificate. :returns: A new `CertificateSigningRequestBuilder`. .. method:: sign(backend, private_key, algorithm) diff --git a/src/cryptography/x509.py b/src/cryptography/x509.py index b1aa0679..f518b68e 100644 --- a/src/cryptography/x509.py +++ b/src/cryptography/x509.py @@ -1460,12 +1460,14 @@ class CertificateSigningRequestBuilder(object): raise TypeError('Expecting x509.Name object.') return CertificateSigningRequestBuilder(name, self._extensions) - def add_extension(self, extension): + def add_extension(self, extension, critical=False): """ Adds an X.509 extension to the certificate request. """ - if not isinstance(extension, Extension): - raise TypeError('Expecting x509.Extension object.') + if isinstance(extension, BasicConstraints): + extension = Extension(OID_BASIC_CONSTRAINTS, critical, extension) + else: + raise ValueError('Unsupported X.509 extension.') for e in self._extensions: if e.oid == extension.oid: raise ValueError('This extension has already been set.') diff --git a/tests/test_x509.py b/tests/test_x509.py index aadbed02..663b83b2 100644 --- a/tests/test_x509.py +++ b/tests/test_x509.py @@ -707,11 +707,9 @@ class TestCertificateSigningRequestBuilder(object): x509.NameAttribute(x509.OID_ORGANIZATION_NAME, 'PyCA'), x509.NameAttribute(x509.OID_COMMON_NAME, 'cryptography.io'), ]) - ).add_extension(x509.Extension( - x509.OID_BASIC_CONSTRAINTS, - True, - x509.BasicConstraints(True, 2), - )).sign( + ).add_extension( + x509.BasicConstraints(True, 2), critical=True + ).sign( backend, private_key, hashes.SHA1() ) @@ -748,11 +746,9 @@ class TestCertificateSigningRequestBuilder(object): x509.NameAttribute(x509.OID_ORGANIZATION_NAME, 'PyCA'), x509.NameAttribute(x509.OID_COMMON_NAME, 'cryptography.io'), ]) - ).add_extension(x509.Extension( - x509.OID_BASIC_CONSTRAINTS, - True, - x509.BasicConstraints(False, None), - )).sign( + ).add_extension( + x509.BasicConstraints(False, None), critical=True, + ).sign( backend, private_key, hashes.SHA1() ) @@ -776,23 +772,12 @@ class TestCertificateSigningRequestBuilder(object): def test_add_duplicate_extension(self, backend): builder = x509.CertificateSigningRequestBuilder().add_extension( - x509.Extension( - x509.OID_BASIC_CONSTRAINTS, - True, - x509.BasicConstraints(True, 2), - ) + x509.BasicConstraints(True, 2), critical=True, ) with pytest.raises(ValueError): - builder.add_extension(x509.Extension( - x509.OID_BASIC_CONSTRAINTS, - True, - x509.BasicConstraints(True, 2), - )) - - def test_add_invalid_extension(self, backend): - builder = x509.CertificateSigningRequestBuilder() - with pytest.raises(TypeError): - builder.add_extension('NotAnExtension') + builder.add_extension( + x509.BasicConstraints(True, 2), critical=True, + ) def test_set_invalid_subject(self, backend): builder = x509.CertificateSigningRequestBuilder() @@ -813,13 +798,11 @@ class TestCertificateSigningRequestBuilder(object): x509.NameAttribute(x509.OID_ORGANIZATION_NAME, u'PyCA'), x509.NameAttribute(x509.OID_COMMON_NAME, u'cryptography.io'), ]) - ).add_extension(x509.Extension( - x509.ObjectIdentifier('1.2.3.4'), - False, - 'value', - )) + ) with pytest.raises(ValueError): - builder.sign(backend, private_key, hashes.SHA1()) + builder.add_extension( + x509.AuthorityKeyIdentifier('keyid', None, None) + ) @pytest.mark.requires_backend_interface(interface=DSABackend) -- cgit v1.2.3 From 341ff85a7ceee3fb2e39e3ea11768b37e195445f Mon Sep 17 00:00:00 2001 From: Andre Caron Date: Sat, 6 Jun 2015 20:14:31 -0400 Subject: Fully qualifies symbols in doc references. --- docs/x509.rst | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/docs/x509.rst b/docs/x509.rst index 52117c84..06a363fc 100644 --- a/docs/x509.rst +++ b/docs/x509.rst @@ -505,15 +505,19 @@ X.509 CSR (Certificate Signing Request) Builder Object .. method:: set_subject_name(name) - :param name: The :class:`Name` of the certificate subject. - :returns: A new `CertificateSigningRequestBuilder`. + :param name: The :class:`~cryptography.x509.Name` of the certificate + subject. + :returns: A new + :class:`~cryptography.x509.CertificateSigningRequestBuilder`. .. method:: add_extension(extension, critical=False) - :param extension: The :class:`Extension` to add to the request. + :param extension: The :class:`~cryptography.x509.Extension` to add to + the request. :param critical: Set to `True` if the extension must be understood and handled by whoever reads the certificate. - :returns: A new `CertificateSigningRequestBuilder`. + :returns: A new + :class:`~cryptography.x509.CertificateSigningRequestBuilder`. .. method:: sign(backend, private_key, algorithm) @@ -532,7 +536,8 @@ X.509 CSR (Certificate Signing Request) Builder Object :class:`~cryptography.hazmat.primitives.hashes.HashAlgorithm` that will be used to generate the request signature. - :type: :class:`CertificateSigningRequest` + :returns: A new + :class:`~cryptography.x509.CertificateSigningRequest`. .. class:: Name -- cgit v1.2.3 From 24f9bf4ba52c64f10ea6895a7e37dd0b4761b589 Mon Sep 17 00:00:00 2001 From: Andre Caron Date: Sat, 6 Jun 2015 20:14:54 -0400 Subject: Lists support for DSA and EC in CSR builder. --- docs/x509.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/x509.rst b/docs/x509.rst index 06a363fc..d7ab6478 100644 --- a/docs/x509.rst +++ b/docs/x509.rst @@ -527,7 +527,9 @@ X.509 CSR (Certificate Signing Request) Builder Object interface. :param private_key: The - :class:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateKey` + :class:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateKey`, + :class:`~cryptography.hazmat.primitives.asymmetric.dsa.DSAPrivateKey` or + :class:`~cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePrivateKey` that will be used to sign the request. When the request is signed by a certificate authority, the private key's associated public key will be stored in the resulting certificate. -- cgit v1.2.3 From a9a5117f9aae4f0aa3e2e1bd3dcd6a93867c67a4 Mon Sep 17 00:00:00 2001 From: Andre Caron Date: Sat, 6 Jun 2015 20:18:44 -0400 Subject: Removes set_ prefix on CSR builder method. --- docs/x509.rst | 4 ++-- src/cryptography/x509.py | 2 +- tests/test_x509.py | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/x509.rst b/docs/x509.rst index d7ab6478..0f55e8e4 100644 --- a/docs/x509.rst +++ b/docs/x509.rst @@ -487,7 +487,7 @@ X.509 CSR (Certificate Signing Request) Builder Object ... backend=default_backend() ... ) >>> builder = x509.CertificateSigningRequestBuilder() - >>> builder = builder.set_subject_name(x509.Name([ + >>> builder = builder.subject_name(x509.Name([ ... x509.NameAttribute(x509.OID_COMMON_NAME, 'cryptography.io'), ... ])) >>> buidlder = builder.add_extension( @@ -503,7 +503,7 @@ X.509 CSR (Certificate Signing Request) Builder Object Creates an empty certificate signing request. - .. method:: set_subject_name(name) + .. method:: subject_name(name) :param name: The :class:`~cryptography.x509.Name` of the certificate subject. diff --git a/src/cryptography/x509.py b/src/cryptography/x509.py index f518b68e..ab4f3c7e 100644 --- a/src/cryptography/x509.py +++ b/src/cryptography/x509.py @@ -1452,7 +1452,7 @@ class CertificateSigningRequestBuilder(object): self._subject_name = subject_name self._extensions = extensions[:] - def set_subject_name(self, name): + def subject_name(self, name): """ Sets the certificate requestor's distinguished name. """ diff --git a/tests/test_x509.py b/tests/test_x509.py index 663b83b2..150eb6fc 100644 --- a/tests/test_x509.py +++ b/tests/test_x509.py @@ -699,7 +699,7 @@ class TestCertificateSigningRequestBuilder(object): backend=backend, ) - request = x509.CertificateSigningRequestBuilder().set_subject_name( + request = x509.CertificateSigningRequestBuilder().subject_name( x509.Name([ x509.NameAttribute(x509.OID_COUNTRY_NAME, 'US'), x509.NameAttribute(x509.OID_STATE_OR_PROVINCE_NAME, 'Texas'), @@ -738,7 +738,7 @@ class TestCertificateSigningRequestBuilder(object): backend=backend, ) - request = x509.CertificateSigningRequestBuilder().set_subject_name( + request = x509.CertificateSigningRequestBuilder().subject_name( x509.Name([ x509.NameAttribute(x509.OID_COUNTRY_NAME, 'US'), x509.NameAttribute(x509.OID_STATE_OR_PROVINCE_NAME, 'Texas'), @@ -782,7 +782,7 @@ class TestCertificateSigningRequestBuilder(object): def test_set_invalid_subject(self, backend): builder = x509.CertificateSigningRequestBuilder() with pytest.raises(TypeError): - builder.set_subject_name('NotAName') + builder.subject_name('NotAName') def test_add_unsupported_extension(self, backend): private_key = rsa.generate_private_key( @@ -790,7 +790,7 @@ class TestCertificateSigningRequestBuilder(object): key_size=2048, backend=backend, ) - builder = x509.CertificateSigningRequestBuilder().set_subject_name( + builder = x509.CertificateSigningRequestBuilder().subject_name( x509.Name([ x509.NameAttribute(x509.OID_COUNTRY_NAME, u'US'), x509.NameAttribute(x509.OID_STATE_OR_PROVINCE_NAME, u'Texas'), -- cgit v1.2.3 From f0a50ae80aa613aa5cc6c4696113a844bf338ecb Mon Sep 17 00:00:00 2001 From: Andre Caron Date: Sat, 6 Jun 2015 20:44:06 -0400 Subject: Fixes PEP8 issue in tests. --- tests/test_x509.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/tests/test_x509.py b/tests/test_x509.py index 150eb6fc..dc45815c 100644 --- a/tests/test_x509.py +++ b/tests/test_x509.py @@ -785,11 +785,6 @@ class TestCertificateSigningRequestBuilder(object): builder.subject_name('NotAName') def test_add_unsupported_extension(self, backend): - private_key = rsa.generate_private_key( - public_exponent=65537, - key_size=2048, - backend=backend, - ) builder = x509.CertificateSigningRequestBuilder().subject_name( x509.Name([ x509.NameAttribute(x509.OID_COUNTRY_NAME, u'US'), -- cgit v1.2.3 From 0112b0242717e394ec35aad8d0c8311a47dfa577 Mon Sep 17 00:00:00 2001 From: Ian Cordasco Date: Tue, 16 Jun 2015 17:51:18 -0500 Subject: Address code review regarding style and gc - Use keyword arguments for x509.BasicConstraints in several places - Use SHA256 instead of SHA1 in documented examples - Give function variables meaningful names in _encode_asn1_str - Accept a x509.BasicConstraints object in _encode_basic_constraints - Properly garbage-collect some things - Raise a NotImplementedError instead of a ValueError --- docs/x509.rst | 8 ++----- .../hazmat/backends/openssl/backend.py | 25 +++++++++++----------- tests/test_x509.py | 4 ++-- 3 files changed, 17 insertions(+), 20 deletions(-) diff --git a/docs/x509.rst b/docs/x509.rst index 0f55e8e4..aa8e2593 100644 --- a/docs/x509.rst +++ b/docs/x509.rst @@ -491,18 +491,14 @@ X.509 CSR (Certificate Signing Request) Builder Object ... x509.NameAttribute(x509.OID_COMMON_NAME, 'cryptography.io'), ... ])) >>> buidlder = builder.add_extension( - ... x509.BasicConstraints(False, None), critical=True, + ... x509.BasicConstraints(ca=False, path_length=None), critical=True, ... ) >>> request = builder.sign( - ... default_backend(), private_key, hashes.SHA1() + ... default_backend(), private_key, hashes.SHA256() ... ) >>> isinstance(request, x509.CertificateSigningRequest) True - .. method:: __init__() - - Creates an empty certificate signing request. - .. method:: subject_name(name) :param name: The :class:`~cryptography.x509.Name` of the certificate diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py index c32b5270..1861d182 100644 --- a/src/cryptography/hazmat/backends/openssl/backend.py +++ b/src/cryptography/hazmat/backends/openssl/backend.py @@ -76,13 +76,13 @@ def _encode_asn1_int(backend, x): return i -def _encode_asn1_str(backend, x, n): +def _encode_asn1_str(backend, data, length): """ Create an ASN1_OCTET_STRING from a Python byte string. """ s = backend._lib.ASN1_OCTET_STRING_new() s = backend._ffi.gc(s, backend._lib.ASN1_OCTET_STRING_free) - backend._lib.ASN1_OCTET_STRING_set(s, x, n) + backend._lib.ASN1_OCTET_STRING_set(s, data, length) return s @@ -118,17 +118,18 @@ def _txt2obj(backend, name): return obj -def _encode_basic_constraints(backend, ca=False, pathlen=0, critical=False): +def _encode_basic_constraints(backend, basic_constraints, critical): obj = _txt2obj(backend, x509.OID_BASIC_CONSTRAINTS.dotted_string) assert obj is not None constraints = backend._lib.BASIC_CONSTRAINTS_new() - constraints.ca = 255 if ca else 0 - if ca: - constraints.pathlen = _encode_asn1_int(backend, pathlen) + constraints.ca = 255 if basic_constraints.ca else 0 + if basic_constraints.ca: + constraints.pathlen = _encode_asn1_int( + backend, basic_constraints.path_length + ) # Fetch the encoded payload. - pp = backend._ffi.new('unsigned char**') - assert pp != backend._ffi.NULL + pp = backend._ffi.new('unsigned char **') r = backend._lib.i2d_BASIC_CONSTRAINTS(constraints, pp) assert r > 0 @@ -141,8 +142,8 @@ def _encode_basic_constraints(backend, ca=False, pathlen=0, critical=False): ) assert extension != backend._ffi.NULL + pp[0] = backend._ffi.gc(pp[0], backend._lib.OPENSSL_free) # Release acquired memory. - backend._lib.OPENSSL_free(pp[0]) pp[0] = backend._ffi.NULL # Return the wrapped extension. @@ -816,6 +817,7 @@ class Backend(object): # Create an empty request. x509_req = self._lib.X509_REQ_new() + x509_req = self._ffi.gc(x509_req, self._lib.X509_REQ_free) assert x509_req != self._ffi.NULL # Set x509 version. @@ -845,12 +847,11 @@ class Backend(object): if isinstance(extension.value, x509.BasicConstraints): extension = _encode_basic_constraints( self, - extension.value.ca, - extension.value.path_length, + extension.value, extension.critical ) else: - raise ValueError('Extension not yet supported.') + raise NotImplementedError('Extension not yet supported.') res = self._lib.sk_X509_EXTENSION_push(extensions, extension) assert res == 1 res = self._lib.X509_REQ_add_extensions(x509_req, extensions) diff --git a/tests/test_x509.py b/tests/test_x509.py index dc45815c..fcd57229 100644 --- a/tests/test_x509.py +++ b/tests/test_x509.py @@ -586,7 +586,7 @@ class TestRSACertificateRequest(object): x509.Extension( x509.OID_BASIC_CONSTRAINTS, True, - x509.BasicConstraints(True, 1), + x509.BasicConstraints(ca=True, path_length=1), ), ] @@ -747,7 +747,7 @@ class TestCertificateSigningRequestBuilder(object): x509.NameAttribute(x509.OID_COMMON_NAME, 'cryptography.io'), ]) ).add_extension( - x509.BasicConstraints(False, None), critical=True, + x509.BasicConstraints(ca=False, path_length=None), critical=True, ).sign( backend, private_key, hashes.SHA1() ) -- cgit v1.2.3 From 94b34d3dd621074bc4d15cdaa548b230886f5d57 Mon Sep 17 00:00:00 2001 From: Ian Cordasco Date: Wed, 17 Jun 2015 10:55:07 -0500 Subject: Fix new tests to pass text value to NameAttribute --- tests/test_x509.py | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/tests/test_x509.py b/tests/test_x509.py index fcd57229..b2babc66 100644 --- a/tests/test_x509.py +++ b/tests/test_x509.py @@ -701,11 +701,11 @@ class TestCertificateSigningRequestBuilder(object): request = x509.CertificateSigningRequestBuilder().subject_name( x509.Name([ - x509.NameAttribute(x509.OID_COUNTRY_NAME, 'US'), - x509.NameAttribute(x509.OID_STATE_OR_PROVINCE_NAME, 'Texas'), - x509.NameAttribute(x509.OID_LOCALITY_NAME, 'Austin'), - x509.NameAttribute(x509.OID_ORGANIZATION_NAME, 'PyCA'), - x509.NameAttribute(x509.OID_COMMON_NAME, 'cryptography.io'), + x509.NameAttribute(x509.OID_COUNTRY_NAME, u'US'), + x509.NameAttribute(x509.OID_STATE_OR_PROVINCE_NAME, u'Texas'), + x509.NameAttribute(x509.OID_LOCALITY_NAME, u'Austin'), + x509.NameAttribute(x509.OID_ORGANIZATION_NAME, u'PyCA'), + x509.NameAttribute(x509.OID_COMMON_NAME, u'cryptography.io'), ]) ).add_extension( x509.BasicConstraints(True, 2), critical=True @@ -719,11 +719,11 @@ class TestCertificateSigningRequestBuilder(object): subject = request.subject assert isinstance(subject, x509.Name) assert list(subject) == [ - x509.NameAttribute(x509.OID_COUNTRY_NAME, 'US'), - x509.NameAttribute(x509.OID_STATE_OR_PROVINCE_NAME, 'Texas'), - x509.NameAttribute(x509.OID_LOCALITY_NAME, 'Austin'), - x509.NameAttribute(x509.OID_ORGANIZATION_NAME, 'PyCA'), - x509.NameAttribute(x509.OID_COMMON_NAME, 'cryptography.io'), + x509.NameAttribute(x509.OID_COUNTRY_NAME, u'US'), + x509.NameAttribute(x509.OID_STATE_OR_PROVINCE_NAME, u'Texas'), + x509.NameAttribute(x509.OID_LOCALITY_NAME, u'Austin'), + x509.NameAttribute(x509.OID_ORGANIZATION_NAME, u'PyCA'), + x509.NameAttribute(x509.OID_COMMON_NAME, u'cryptography.io'), ] basic_constraints = request.extensions.get_extension_for_oid( x509.OID_BASIC_CONSTRAINTS @@ -740,11 +740,11 @@ class TestCertificateSigningRequestBuilder(object): request = x509.CertificateSigningRequestBuilder().subject_name( x509.Name([ - x509.NameAttribute(x509.OID_COUNTRY_NAME, 'US'), - x509.NameAttribute(x509.OID_STATE_OR_PROVINCE_NAME, 'Texas'), - x509.NameAttribute(x509.OID_LOCALITY_NAME, 'Austin'), - x509.NameAttribute(x509.OID_ORGANIZATION_NAME, 'PyCA'), - x509.NameAttribute(x509.OID_COMMON_NAME, 'cryptography.io'), + x509.NameAttribute(x509.OID_COUNTRY_NAME, u'US'), + x509.NameAttribute(x509.OID_STATE_OR_PROVINCE_NAME, u'Texas'), + x509.NameAttribute(x509.OID_LOCALITY_NAME, u'Austin'), + x509.NameAttribute(x509.OID_ORGANIZATION_NAME, u'PyCA'), + x509.NameAttribute(x509.OID_COMMON_NAME, u'cryptography.io'), ]) ).add_extension( x509.BasicConstraints(ca=False, path_length=None), critical=True, @@ -758,11 +758,11 @@ class TestCertificateSigningRequestBuilder(object): subject = request.subject assert isinstance(subject, x509.Name) assert list(subject) == [ - x509.NameAttribute(x509.OID_COUNTRY_NAME, 'US'), - x509.NameAttribute(x509.OID_STATE_OR_PROVINCE_NAME, 'Texas'), - x509.NameAttribute(x509.OID_LOCALITY_NAME, 'Austin'), - x509.NameAttribute(x509.OID_ORGANIZATION_NAME, 'PyCA'), - x509.NameAttribute(x509.OID_COMMON_NAME, 'cryptography.io'), + x509.NameAttribute(x509.OID_COUNTRY_NAME, u'US'), + x509.NameAttribute(x509.OID_STATE_OR_PROVINCE_NAME, u'Texas'), + x509.NameAttribute(x509.OID_LOCALITY_NAME, u'Austin'), + x509.NameAttribute(x509.OID_ORGANIZATION_NAME, u'PyCA'), + x509.NameAttribute(x509.OID_COMMON_NAME, u'cryptography.io'), ] basic_constraints = request.extensions.get_extension_for_oid( x509.OID_BASIC_CONSTRAINTS -- cgit v1.2.3 From 41f51ce4690472ae930ccffd1a0b9e198945aa84 Mon Sep 17 00:00:00 2001 From: Ian Cordasco Date: Wed, 17 Jun 2015 11:49:11 -0500 Subject: Update CSR tests and implementation - Use keyword arguments for x509.BasicConstraints in tests (missed in b790edbdc8fb9a026353d6fb99994326197705c7). - Place X509_request garbage collection under assertion. - Assert that X509 extensions created are not null. - Don't copy the extensions list in CertificateSigningBuilder. They're never appended to, so copying isn't necessary. - Use RSA key fixtures instead of generating new ones on each test run --- .../hazmat/backends/openssl/backend.py | 3 ++- src/cryptography/x509.py | 2 +- tests/test_x509.py | 26 +++++----------------- 3 files changed, 8 insertions(+), 23 deletions(-) diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py index 1861d182..406117b9 100644 --- a/src/cryptography/hazmat/backends/openssl/backend.py +++ b/src/cryptography/hazmat/backends/openssl/backend.py @@ -817,8 +817,8 @@ class Backend(object): # Create an empty request. x509_req = self._lib.X509_REQ_new() - x509_req = self._ffi.gc(x509_req, self._lib.X509_REQ_free) assert x509_req != self._ffi.NULL + x509_req = self._ffi.gc(x509_req, self._lib.X509_REQ_free) # Set x509 version. res = self._lib.X509_REQ_set_version(x509_req, x509.Version.v1.value) @@ -839,6 +839,7 @@ class Backend(object): # Add extensions. extensions = self._lib.sk_X509_EXTENSION_new_null() + assert extensions != self._ffi.NULL extensions = self._ffi.gc( extensions, self._lib.sk_X509_EXTENSION_free, diff --git a/src/cryptography/x509.py b/src/cryptography/x509.py index ab4f3c7e..7e1e34e2 100644 --- a/src/cryptography/x509.py +++ b/src/cryptography/x509.py @@ -1450,7 +1450,7 @@ class CertificateSigningRequestBuilder(object): Creates an empty X.509 certificate request (v1). """ self._subject_name = subject_name - self._extensions = extensions[:] + self._extensions = extensions def subject_name(self, name): """ diff --git a/tests/test_x509.py b/tests/test_x509.py index b2babc66..650c5646 100644 --- a/tests/test_x509.py +++ b/tests/test_x509.py @@ -21,6 +21,7 @@ from cryptography.hazmat.primitives import hashes, serialization from cryptography.hazmat.primitives.asymmetric import dsa, ec, rsa from .hazmat.primitives.test_ec import _skip_curve_unsupported +from .hazmat.primitives.fixtures_rsa import RSA_KEY_2048 from .utils import load_vectors_from_file @@ -683,22 +684,11 @@ class TestRSACertificateRequest(object): @pytest.mark.requires_backend_interface(interface=X509Backend) class TestCertificateSigningRequestBuilder(object): def test_sign_invalid_hash_algorithm(self, backend): - private_key = rsa.generate_private_key( - public_exponent=65537, - key_size=2048, - backend=backend, - ) builder = x509.CertificateSigningRequestBuilder() with pytest.raises(TypeError): - builder.sign(backend, private_key, 'NotAHash') + builder.sign(backend, RSA_KEY_2048, 'NotAHash') def test_build_ca_request(self, backend): - private_key = rsa.generate_private_key( - public_exponent=65537, - key_size=2048, - backend=backend, - ) - request = x509.CertificateSigningRequestBuilder().subject_name( x509.Name([ x509.NameAttribute(x509.OID_COUNTRY_NAME, u'US'), @@ -708,9 +698,9 @@ class TestCertificateSigningRequestBuilder(object): x509.NameAttribute(x509.OID_COMMON_NAME, u'cryptography.io'), ]) ).add_extension( - x509.BasicConstraints(True, 2), critical=True + x509.BasicConstraints(ca=True, path_length=2), critical=True ).sign( - backend, private_key, hashes.SHA1() + backend, RSA_KEY_2048, hashes.SHA1() ) assert isinstance(request.signature_hash_algorithm, hashes.SHA1) @@ -732,12 +722,6 @@ class TestCertificateSigningRequestBuilder(object): assert basic_constraints.value.path_length == 2 def test_build_nonca_request(self, backend): - private_key = rsa.generate_private_key( - public_exponent=65537, - key_size=2048, - backend=backend, - ) - request = x509.CertificateSigningRequestBuilder().subject_name( x509.Name([ x509.NameAttribute(x509.OID_COUNTRY_NAME, u'US'), @@ -749,7 +733,7 @@ class TestCertificateSigningRequestBuilder(object): ).add_extension( x509.BasicConstraints(ca=False, path_length=None), critical=True, ).sign( - backend, private_key, hashes.SHA1() + backend, RSA_KEY_2048, hashes.SHA1() ) assert isinstance(request.signature_hash_algorithm, hashes.SHA1) -- cgit v1.2.3 From f0388d068fd07209068de6b3ca0f8372e01ac086 Mon Sep 17 00:00:00 2001 From: Ian Cordasco Date: Wed, 17 Jun 2015 11:53:58 -0500 Subject: Update registering pp with ffi.gc This makes it more in-line with existing functions, e.g., L40-L47 of src/cryptography/hazmat/backends/openssl/x509.py @ b0e8ffa --- src/cryptography/hazmat/backends/openssl/backend.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py index 406117b9..f4cfa940 100644 --- a/src/cryptography/hazmat/backends/openssl/backend.py +++ b/src/cryptography/hazmat/backends/openssl/backend.py @@ -132,6 +132,9 @@ def _encode_basic_constraints(backend, basic_constraints, critical): pp = backend._ffi.new('unsigned char **') r = backend._lib.i2d_BASIC_CONSTRAINTS(constraints, pp) assert r > 0 + pp = backend._ffi.gc( + pp, lambda pointer: backend._lib.OPENSSL_free(pointer[0]) + ) # Wrap that in an X509 extension object. extension = backend._lib.X509_EXTENSION_create_by_OBJ( @@ -142,10 +145,6 @@ def _encode_basic_constraints(backend, basic_constraints, critical): ) assert extension != backend._ffi.NULL - pp[0] = backend._ffi.gc(pp[0], backend._lib.OPENSSL_free) - # Release acquired memory. - pp[0] = backend._ffi.NULL - # Return the wrapped extension. return extension -- cgit v1.2.3 From 4d46eb7217d1effa3043da0def8c365c199b5b7f Mon Sep 17 00:00:00 2001 From: Ian Cordasco Date: Wed, 17 Jun 2015 12:08:27 -0500 Subject: Properly use RSA fixtures to generate private keys --- docs/x509.rst | 2 +- tests/test_x509.py | 14 ++++++++++---- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/docs/x509.rst b/docs/x509.rst index aa8e2593..84b3b8b4 100644 --- a/docs/x509.rst +++ b/docs/x509.rst @@ -488,7 +488,7 @@ X.509 CSR (Certificate Signing Request) Builder Object ... ) >>> builder = x509.CertificateSigningRequestBuilder() >>> builder = builder.subject_name(x509.Name([ - ... x509.NameAttribute(x509.OID_COMMON_NAME, 'cryptography.io'), + ... x509.NameAttribute(x509.OID_COMMON_NAME, u'cryptography.io'), ... ])) >>> buidlder = builder.add_extension( ... x509.BasicConstraints(ca=False, path_length=None), critical=True, diff --git a/tests/test_x509.py b/tests/test_x509.py index 650c5646..441d634b 100644 --- a/tests/test_x509.py +++ b/tests/test_x509.py @@ -20,8 +20,8 @@ from cryptography.hazmat.backends.interfaces import ( from cryptography.hazmat.primitives import hashes, serialization from cryptography.hazmat.primitives.asymmetric import dsa, ec, rsa -from .hazmat.primitives.test_ec import _skip_curve_unsupported from .hazmat.primitives.fixtures_rsa import RSA_KEY_2048 +from .hazmat.primitives.test_ec import _skip_curve_unsupported from .utils import load_vectors_from_file @@ -684,11 +684,15 @@ class TestRSACertificateRequest(object): @pytest.mark.requires_backend_interface(interface=X509Backend) class TestCertificateSigningRequestBuilder(object): def test_sign_invalid_hash_algorithm(self, backend): + private_key = RSA_KEY_2048.private_key(backend) + builder = x509.CertificateSigningRequestBuilder() with pytest.raises(TypeError): - builder.sign(backend, RSA_KEY_2048, 'NotAHash') + builder.sign(backend, private_key, 'NotAHash') def test_build_ca_request(self, backend): + private_key = RSA_KEY_2048.private_key(backend) + request = x509.CertificateSigningRequestBuilder().subject_name( x509.Name([ x509.NameAttribute(x509.OID_COUNTRY_NAME, u'US'), @@ -700,7 +704,7 @@ class TestCertificateSigningRequestBuilder(object): ).add_extension( x509.BasicConstraints(ca=True, path_length=2), critical=True ).sign( - backend, RSA_KEY_2048, hashes.SHA1() + backend, private_key, hashes.SHA1() ) assert isinstance(request.signature_hash_algorithm, hashes.SHA1) @@ -722,6 +726,8 @@ class TestCertificateSigningRequestBuilder(object): assert basic_constraints.value.path_length == 2 def test_build_nonca_request(self, backend): + private_key = RSA_KEY_2048.private_key(backend) + request = x509.CertificateSigningRequestBuilder().subject_name( x509.Name([ x509.NameAttribute(x509.OID_COUNTRY_NAME, u'US'), @@ -733,7 +739,7 @@ class TestCertificateSigningRequestBuilder(object): ).add_extension( x509.BasicConstraints(ca=False, path_length=None), critical=True, ).sign( - backend, RSA_KEY_2048, hashes.SHA1() + backend, private_key, hashes.SHA1() ) assert isinstance(request.signature_hash_algorithm, hashes.SHA1) -- cgit v1.2.3 From b2b4b6eb91e5330918a622a1a69e52dcf239b1bc Mon Sep 17 00:00:00 2001 From: Ian Cordasco Date: Wed, 17 Jun 2015 12:08:44 -0500 Subject: Unconditionally encode values to ascii --- src/cryptography/hazmat/backends/openssl/backend.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py index f4cfa940..a6dc0d4e 100644 --- a/src/cryptography/hazmat/backends/openssl/backend.py +++ b/src/cryptography/hazmat/backends/openssl/backend.py @@ -90,9 +90,7 @@ def _encode_name(backend, attributes): subject = backend._lib.X509_NAME_new() subject = backend._ffi.gc(subject, backend._lib.X509_NAME_free) for attribute in attributes: - value = attribute.value - if isinstance(value, six.text_type): - value = value.encode('ascii') + value = attribute.value.encode('ascii') obj = _txt2obj(backend, attribute.oid.dotted_string) res = backend._lib.X509_NAME_add_entry_by_OBJ( subject, @@ -110,8 +108,7 @@ def _txt2obj(backend, name): Converts a Python string with an ASN.1 object ID in dotted form to a ASN1_OBJECT. """ - if isinstance(name, six.text_type): - name = name.encode('ascii') + name = name.encode('ascii') obj = backend._lib.OBJ_txt2obj(name, 1) assert obj != backend._ffi.NULL obj = backend._ffi.gc(obj, backend._lib.ASN1_OBJECT_free) -- cgit v1.2.3 From d09ec37cb4c2995b1e2e2f88d187f4fbaef68ad8 Mon Sep 17 00:00:00 2001 From: Ian Cordasco Date: Wed, 17 Jun 2015 21:37:51 -0500 Subject: Only allow subject_name to be set once on a Builder --- src/cryptography/x509.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/cryptography/x509.py b/src/cryptography/x509.py index 7e1e34e2..f59ea78a 100644 --- a/src/cryptography/x509.py +++ b/src/cryptography/x509.py @@ -1458,6 +1458,8 @@ class CertificateSigningRequestBuilder(object): """ if not isinstance(name, Name): raise TypeError('Expecting x509.Name object.') + if self._subject_name is not None: + raise ValueError('The subject name may only be set once.') return CertificateSigningRequestBuilder(name, self._extensions) def add_extension(self, extension, critical=False): -- cgit v1.2.3 From f06b6be82300d9339bcfb062aedd7d7a3865aec9 Mon Sep 17 00:00:00 2001 From: Ian Cordasco Date: Sun, 21 Jun 2015 10:09:18 -0500 Subject: Address review comments around add_extension method - Fix typo in the docs (s/buidlder/builder/) - Remove default from the method declaration and docs - Replace ValueError with NotImpelementedError for unsupported X.509 extensions - Add TODO comment as requested by Alex - Fix test to pass critical=False since it no longer is a default value --- docs/x509.rst | 4 ++-- src/cryptography/x509.py | 5 +++-- tests/test_x509.py | 5 +++-- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/docs/x509.rst b/docs/x509.rst index 84b3b8b4..c4c441e7 100644 --- a/docs/x509.rst +++ b/docs/x509.rst @@ -490,7 +490,7 @@ X.509 CSR (Certificate Signing Request) Builder Object >>> builder = builder.subject_name(x509.Name([ ... x509.NameAttribute(x509.OID_COMMON_NAME, u'cryptography.io'), ... ])) - >>> buidlder = builder.add_extension( + >>> builder = builder.add_extension( ... x509.BasicConstraints(ca=False, path_length=None), critical=True, ... ) >>> request = builder.sign( @@ -506,7 +506,7 @@ X.509 CSR (Certificate Signing Request) Builder Object :returns: A new :class:`~cryptography.x509.CertificateSigningRequestBuilder`. - .. method:: add_extension(extension, critical=False) + .. method:: add_extension(extension, critical) :param extension: The :class:`~cryptography.x509.Extension` to add to the request. diff --git a/src/cryptography/x509.py b/src/cryptography/x509.py index f59ea78a..21e18ddd 100644 --- a/src/cryptography/x509.py +++ b/src/cryptography/x509.py @@ -1462,14 +1462,15 @@ class CertificateSigningRequestBuilder(object): raise ValueError('The subject name may only be set once.') return CertificateSigningRequestBuilder(name, self._extensions) - def add_extension(self, extension, critical=False): + def add_extension(self, extension, critical): """ Adds an X.509 extension to the certificate request. """ if isinstance(extension, BasicConstraints): extension = Extension(OID_BASIC_CONSTRAINTS, critical, extension) else: - raise ValueError('Unsupported X.509 extension.') + raise NotImplementedError('Unsupported X.509 extension.') + # TODO: This is quadratic in the number of extensions for e in self._extensions: if e.oid == extension.oid: raise ValueError('This extension has already been set.') diff --git a/tests/test_x509.py b/tests/test_x509.py index 441d634b..78def5f8 100644 --- a/tests/test_x509.py +++ b/tests/test_x509.py @@ -784,9 +784,10 @@ class TestCertificateSigningRequestBuilder(object): x509.NameAttribute(x509.OID_COMMON_NAME, u'cryptography.io'), ]) ) - with pytest.raises(ValueError): + with pytest.raises(NotImplementedError): builder.add_extension( - x509.AuthorityKeyIdentifier('keyid', None, None) + x509.AuthorityKeyIdentifier('keyid', None, None), + critical=False, ) -- cgit v1.2.3 From 34853f362f19bab9212824a1235a2c30f84234a3 Mon Sep 17 00:00:00 2001 From: Ian Cordasco Date: Sun, 21 Jun 2015 10:50:53 -0500 Subject: Simplify test for unsupported extensions --- tests/test_x509.py | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/tests/test_x509.py b/tests/test_x509.py index 78def5f8..7ce48688 100644 --- a/tests/test_x509.py +++ b/tests/test_x509.py @@ -775,15 +775,7 @@ class TestCertificateSigningRequestBuilder(object): builder.subject_name('NotAName') def test_add_unsupported_extension(self, backend): - builder = x509.CertificateSigningRequestBuilder().subject_name( - x509.Name([ - x509.NameAttribute(x509.OID_COUNTRY_NAME, u'US'), - x509.NameAttribute(x509.OID_STATE_OR_PROVINCE_NAME, u'Texas'), - x509.NameAttribute(x509.OID_LOCALITY_NAME, u'Austin'), - x509.NameAttribute(x509.OID_ORGANIZATION_NAME, u'PyCA'), - x509.NameAttribute(x509.OID_COMMON_NAME, u'cryptography.io'), - ]) - ) + builder = x509.CertificateSigningRequestBuilder() with pytest.raises(NotImplementedError): builder.add_extension( x509.AuthorityKeyIdentifier('keyid', None, None), -- cgit v1.2.3 From 8ed8edce1764ea17800ef83f422c7a73bfdfa74b Mon Sep 17 00:00:00 2001 From: Ian Cordasco Date: Mon, 22 Jun 2015 20:11:17 -0500 Subject: Add tests to the CSR Builder for EC and DSA keys This skips certain tests on certain versions of differences in how X509_REQ_sign works on those versions. A separate pull request will address those differences. --- .../hazmat/backends/openssl/backend.py | 13 +++- tests/hazmat/backends/test_openssl.py | 25 ++++++- tests/test_x509.py | 87 +++++++++++++++++++++- 3 files changed, 120 insertions(+), 5 deletions(-) diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py index a6dc0d4e..7963b5d3 100644 --- a/src/cryptography/hazmat/backends/openssl/backend.py +++ b/src/cryptography/hazmat/backends/openssl/backend.py @@ -801,10 +801,21 @@ class Backend(object): return _CMACContext(self, algorithm) def create_x509_csr(self, builder, private_key, algorithm): - # TODO: check type of private key parameter. if not isinstance(algorithm, hashes.HashAlgorithm): raise TypeError('Algorithm must be a registered hash algorithm.') + if self._lib.OPENSSL_VERSION_NUMBER <= 0x10001000: + if isinstance(private_key, _DSAPrivateKey): + raise NotImplementedError( + "Certificate signing requests aren't implemented for DSA" + " keys on OpenSSL versions less than 1.0.1." + ) + if isinstance(private_key, _EllipticCurvePrivateKey): + raise NotImplementedError( + "Certificate signing requests aren't implemented for EC" + " keys on OpenSSL versions less than 1.0.1." + ) + # Resolve the signature algorithm. evp_md = self._lib.EVP_get_digestbyname( algorithm.name.encode('ascii') diff --git a/tests/hazmat/backends/test_openssl.py b/tests/hazmat/backends/test_openssl.py index b35e7670..4275b593 100644 --- a/tests/hazmat/backends/test_openssl.py +++ b/tests/hazmat/backends/test_openssl.py @@ -21,13 +21,14 @@ from cryptography.hazmat.backends.openssl.backend import ( ) from cryptography.hazmat.backends.openssl.ec import _sn_to_elliptic_curve from cryptography.hazmat.primitives import hashes, serialization -from cryptography.hazmat.primitives.asymmetric import dsa, padding +from cryptography.hazmat.primitives.asymmetric import dsa, ec, padding from cryptography.hazmat.primitives.ciphers import ( BlockCipherAlgorithm, Cipher, CipherAlgorithm ) from cryptography.hazmat.primitives.ciphers.algorithms import AES from cryptography.hazmat.primitives.ciphers.modes import CBC, CTR, Mode +from ..primitives.fixtures_dsa import DSA_KEY_2048 from ..primitives.fixtures_rsa import RSA_KEY_2048, RSA_KEY_512 from ...utils import load_vectors_from_file, raises_unsupported_algorithm @@ -453,6 +454,28 @@ class TestOpenSSLCMAC(object): backend.create_cmac_ctx(FakeAlgorithm()) +class TestOpenSSLCreateX509CSR(object): + @pytest.mark.skipif( + backend._lib.OPENSSL_VERSION_NUMBER >= 0x10001000, + reason="Requires an older OpenSSL. Must be < 1.0.1" + ) + def test_unsupported_dsa_keys(self): + private_key = DSA_KEY_2048.private_key(backend) + + with pytest.raises(NotImplementedError): + backend.create_x509_csr(object(), private_key, hashes.SHA1()) + + @pytest.mark.skipif( + backend._lib.OPENSSL_VERSION_NUMBER >= 0x10001000, + reason="Requires an older OpenSSL. Must be < 1.0.1" + ) + def test_unsupported_ec_keys(self): + private_key = ec.generate_private_key(ec.SECT283K1(), backend) + + with pytest.raises(NotImplementedError): + backend.create_x509_csr(object(), private_key, hashes.SHA1()) + + class TestOpenSSLSerialisationWithOpenSSL(object): def test_pem_password_cb_buffer_too_small(self): ffi_cb, cb = backend._pem_password_cb(b"aa") diff --git a/tests/test_x509.py b/tests/test_x509.py index 7ce48688..5d6f174c 100644 --- a/tests/test_x509.py +++ b/tests/test_x509.py @@ -20,6 +20,7 @@ from cryptography.hazmat.backends.interfaces import ( from cryptography.hazmat.primitives import hashes, serialization from cryptography.hazmat.primitives.asymmetric import dsa, ec, rsa +from .hazmat.primitives.fixtures_dsa import DSA_KEY_2048 from .hazmat.primitives.fixtures_rsa import RSA_KEY_2048 from .hazmat.primitives.test_ec import _skip_curve_unsupported from .utils import load_vectors_from_file @@ -680,9 +681,9 @@ class TestRSACertificateRequest(object): assert serialized == request_bytes -@pytest.mark.requires_backend_interface(interface=RSABackend) @pytest.mark.requires_backend_interface(interface=X509Backend) class TestCertificateSigningRequestBuilder(object): + @pytest.mark.requires_backend_interface(interface=RSABackend) def test_sign_invalid_hash_algorithm(self, backend): private_key = RSA_KEY_2048.private_key(backend) @@ -690,7 +691,8 @@ class TestCertificateSigningRequestBuilder(object): with pytest.raises(TypeError): builder.sign(backend, private_key, 'NotAHash') - def test_build_ca_request(self, backend): + @pytest.mark.requires_backend_interface(interface=RSABackend) + def test_build_ca_request_with_rsa(self, backend): private_key = RSA_KEY_2048.private_key(backend) request = x509.CertificateSigningRequestBuilder().subject_name( @@ -725,7 +727,8 @@ class TestCertificateSigningRequestBuilder(object): assert basic_constraints.value.ca is True assert basic_constraints.value.path_length == 2 - def test_build_nonca_request(self, backend): + @pytest.mark.requires_backend_interface(interface=RSABackend) + def test_build_nonca_request_with_rsa(self, backend): private_key = RSA_KEY_2048.private_key(backend) request = x509.CertificateSigningRequestBuilder().subject_name( @@ -760,6 +763,84 @@ class TestCertificateSigningRequestBuilder(object): assert basic_constraints.value.ca is False assert basic_constraints.value.path_length is None + @pytest.mark.requires_backend_interface(interface=EllipticCurveBackend) + def test_build_ca_request_with_ec(self, backend): + if backend._lib.OPENSSL_VERSION_NUMBER < 0x10001000: + pytest.skip("Requires a newer OpenSSL. Must be >= 1.0.1") + + private_key = ec.generate_private_key(ec.SECT283K1(), backend) + + request = x509.CertificateSigningRequestBuilder().subject_name( + x509.Name([ + x509.NameAttribute(x509.OID_COUNTRY_NAME, u'US'), + x509.NameAttribute(x509.OID_STATE_OR_PROVINCE_NAME, u'Texas'), + x509.NameAttribute(x509.OID_LOCALITY_NAME, u'Austin'), + x509.NameAttribute(x509.OID_ORGANIZATION_NAME, u'PyCA'), + x509.NameAttribute(x509.OID_COMMON_NAME, u'cryptography.io'), + ]) + ).add_extension( + x509.BasicConstraints(ca=True, path_length=2), critical=True + ).sign( + backend, private_key, hashes.SHA1() + ) + + assert isinstance(request.signature_hash_algorithm, hashes.SHA1) + public_key = request.public_key() + assert isinstance(public_key, ec.EllipticCurvePublicKey) + subject = request.subject + assert isinstance(subject, x509.Name) + assert list(subject) == [ + x509.NameAttribute(x509.OID_COUNTRY_NAME, u'US'), + x509.NameAttribute(x509.OID_STATE_OR_PROVINCE_NAME, u'Texas'), + x509.NameAttribute(x509.OID_LOCALITY_NAME, u'Austin'), + x509.NameAttribute(x509.OID_ORGANIZATION_NAME, u'PyCA'), + x509.NameAttribute(x509.OID_COMMON_NAME, u'cryptography.io'), + ] + basic_constraints = request.extensions.get_extension_for_oid( + x509.OID_BASIC_CONSTRAINTS + ) + assert basic_constraints.value.ca is True + assert basic_constraints.value.path_length == 2 + + @pytest.mark.requires_backend_interface(interface=DSABackend) + def test_build_ca_request_with_dsa(self, backend): + if backend._lib.OPENSSL_VERSION_NUMBER < 0x10001000: + pytest.skip("Requires a newer OpenSSL. Must be >= 1.0.1") + + private_key = DSA_KEY_2048.private_key(backend) + + request = x509.CertificateSigningRequestBuilder().subject_name( + x509.Name([ + x509.NameAttribute(x509.OID_COUNTRY_NAME, u'US'), + x509.NameAttribute(x509.OID_STATE_OR_PROVINCE_NAME, u'Texas'), + x509.NameAttribute(x509.OID_LOCALITY_NAME, u'Austin'), + x509.NameAttribute(x509.OID_ORGANIZATION_NAME, u'PyCA'), + x509.NameAttribute(x509.OID_COMMON_NAME, u'cryptography.io'), + ]) + ).add_extension( + x509.BasicConstraints(ca=True, path_length=2), critical=True + ).sign( + backend, private_key, hashes.SHA1() + ) + + assert isinstance(request.signature_hash_algorithm, hashes.SHA1) + public_key = request.public_key() + assert isinstance(public_key, dsa.DSAPublicKey) + subject = request.subject + assert isinstance(subject, x509.Name) + assert list(subject) == [ + x509.NameAttribute(x509.OID_COUNTRY_NAME, u'US'), + x509.NameAttribute(x509.OID_STATE_OR_PROVINCE_NAME, u'Texas'), + x509.NameAttribute(x509.OID_LOCALITY_NAME, u'Austin'), + x509.NameAttribute(x509.OID_ORGANIZATION_NAME, u'PyCA'), + x509.NameAttribute(x509.OID_COMMON_NAME, u'cryptography.io'), + ] + basic_constraints = request.extensions.get_extension_for_oid( + x509.OID_BASIC_CONSTRAINTS + ) + assert basic_constraints.value.ca is True + assert basic_constraints.value.path_length == 2 + def test_add_duplicate_extension(self, backend): builder = x509.CertificateSigningRequestBuilder().add_extension( x509.BasicConstraints(True, 2), critical=True, -- cgit v1.2.3 From 87d61f8a6efdf1f9b0a0d6e8c59e323e6004ec72 Mon Sep 17 00:00:00 2001 From: Ian Cordasco Date: Wed, 24 Jun 2015 19:31:26 -0500 Subject: Use utf8 to encode attribute values instead of ascii --- src/cryptography/hazmat/backends/openssl/backend.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py index 7963b5d3..78de79d1 100644 --- a/src/cryptography/hazmat/backends/openssl/backend.py +++ b/src/cryptography/hazmat/backends/openssl/backend.py @@ -90,12 +90,12 @@ def _encode_name(backend, attributes): subject = backend._lib.X509_NAME_new() subject = backend._ffi.gc(subject, backend._lib.X509_NAME_free) for attribute in attributes: - value = attribute.value.encode('ascii') + value = attribute.value.encode('utf8') obj = _txt2obj(backend, attribute.oid.dotted_string) res = backend._lib.X509_NAME_add_entry_by_OBJ( subject, obj, - backend._lib.MBSTRING_ASC, + backend._lib.MBSTRING_UTF8, value, -1, -1, 0, ) -- cgit v1.2.3 From 22e69b58a95d69cc0001d16f888411cf52db96e1 Mon Sep 17 00:00:00 2001 From: Ian Cordasco Date: Wed, 24 Jun 2015 20:09:43 -0500 Subject: Skip tests when the EC curve is unsupported --- tests/hazmat/backends/test_openssl.py | 2 ++ tests/test_x509.py | 1 + 2 files changed, 3 insertions(+) diff --git a/tests/hazmat/backends/test_openssl.py b/tests/hazmat/backends/test_openssl.py index 4275b593..6c741c89 100644 --- a/tests/hazmat/backends/test_openssl.py +++ b/tests/hazmat/backends/test_openssl.py @@ -30,6 +30,7 @@ from cryptography.hazmat.primitives.ciphers.modes import CBC, CTR, Mode from ..primitives.fixtures_dsa import DSA_KEY_2048 from ..primitives.fixtures_rsa import RSA_KEY_2048, RSA_KEY_512 +from ..primitives.test_ec import _skip_curve_unsupported from ...utils import load_vectors_from_file, raises_unsupported_algorithm @@ -470,6 +471,7 @@ class TestOpenSSLCreateX509CSR(object): reason="Requires an older OpenSSL. Must be < 1.0.1" ) def test_unsupported_ec_keys(self): + _skip_curve_unsupported(backend, ec.SECT283K1()) private_key = ec.generate_private_key(ec.SECT283K1(), backend) with pytest.raises(NotImplementedError): diff --git a/tests/test_x509.py b/tests/test_x509.py index 5d6f174c..fb7f17d4 100644 --- a/tests/test_x509.py +++ b/tests/test_x509.py @@ -768,6 +768,7 @@ class TestCertificateSigningRequestBuilder(object): if backend._lib.OPENSSL_VERSION_NUMBER < 0x10001000: pytest.skip("Requires a newer OpenSSL. Must be >= 1.0.1") + _skip_curve_unsupported(backend, ec.SECT283K1()) private_key = ec.generate_private_key(ec.SECT283K1(), backend) request = x509.CertificateSigningRequestBuilder().subject_name( -- cgit v1.2.3 From 13cdc7bf087dc017ca5cfdfc3c0afdfd99b7979b Mon Sep 17 00:00:00 2001 From: Ian Cordasco Date: Wed, 24 Jun 2015 21:45:55 -0500 Subject: Add test for unicode attributes in CSRs This creates a CSR, converts it to bytes, and then loads it again to ensure that the unicode strings are parsed properly. --- tests/test_x509.py | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/tests/test_x509.py b/tests/test_x509.py index fb7f17d4..5be51773 100644 --- a/tests/test_x509.py +++ b/tests/test_x509.py @@ -727,6 +727,38 @@ class TestCertificateSigningRequestBuilder(object): assert basic_constraints.value.ca is True assert basic_constraints.value.path_length == 2 + @pytest.mark.requires_backend_interface(interface=RSABackend) + def test_build_ca_request_with_unicode(self, backend): + private_key = RSA_KEY_2048.private_key(backend) + + request = x509.CertificateSigningRequestBuilder().subject_name( + x509.Name([ + x509.NameAttribute(x509.OID_COUNTRY_NAME, u'US'), + x509.NameAttribute(x509.OID_STATE_OR_PROVINCE_NAME, u'Texas'), + x509.NameAttribute(x509.OID_LOCALITY_NAME, u'Austin'), + x509.NameAttribute(x509.OID_ORGANIZATION_NAME, + u'PyCA\U0001f37a'), + x509.NameAttribute(x509.OID_COMMON_NAME, u'cryptography.io'), + ]) + ).add_extension( + x509.BasicConstraints(ca=True, path_length=2), critical=True + ).sign( + backend, private_key, hashes.SHA1() + ) + + loaded_request = x509.load_pem_x509_csr( + request.public_bytes(encoding=serialization.Encoding.PEM), backend + ) + subject = loaded_request.subject + assert isinstance(subject, x509.Name) + assert list(subject) == [ + x509.NameAttribute(x509.OID_COUNTRY_NAME, u'US'), + x509.NameAttribute(x509.OID_STATE_OR_PROVINCE_NAME, u'Texas'), + x509.NameAttribute(x509.OID_LOCALITY_NAME, u'Austin'), + x509.NameAttribute(x509.OID_ORGANIZATION_NAME, u'PyCA\U0001f37a'), + x509.NameAttribute(x509.OID_COMMON_NAME, u'cryptography.io'), + ] + @pytest.mark.requires_backend_interface(interface=RSABackend) def test_build_nonca_request_with_rsa(self, backend): private_key = RSA_KEY_2048.private_key(backend) -- cgit v1.2.3 From 8cdcdfc1bd11ee57b7f53c631af2f88e0861d168 Mon Sep 17 00:00:00 2001 From: Ian Cordasco Date: Wed, 24 Jun 2015 22:00:26 -0500 Subject: Use SECP256R1 instead of SECT283K1 in CSR tests --- tests/hazmat/backends/test_openssl.py | 4 ++-- tests/test_x509.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/hazmat/backends/test_openssl.py b/tests/hazmat/backends/test_openssl.py index 6c741c89..34fff277 100644 --- a/tests/hazmat/backends/test_openssl.py +++ b/tests/hazmat/backends/test_openssl.py @@ -471,8 +471,8 @@ class TestOpenSSLCreateX509CSR(object): reason="Requires an older OpenSSL. Must be < 1.0.1" ) def test_unsupported_ec_keys(self): - _skip_curve_unsupported(backend, ec.SECT283K1()) - private_key = ec.generate_private_key(ec.SECT283K1(), backend) + _skip_curve_unsupported(backend, ec.SECP256R1()) + private_key = ec.generate_private_key(ec.SECP256R1(), backend) with pytest.raises(NotImplementedError): backend.create_x509_csr(object(), private_key, hashes.SHA1()) diff --git a/tests/test_x509.py b/tests/test_x509.py index 5be51773..429f2d25 100644 --- a/tests/test_x509.py +++ b/tests/test_x509.py @@ -800,8 +800,8 @@ class TestCertificateSigningRequestBuilder(object): if backend._lib.OPENSSL_VERSION_NUMBER < 0x10001000: pytest.skip("Requires a newer OpenSSL. Must be >= 1.0.1") - _skip_curve_unsupported(backend, ec.SECT283K1()) - private_key = ec.generate_private_key(ec.SECT283K1(), backend) + _skip_curve_unsupported(backend, ec.SECP256R1()) + private_key = ec.generate_private_key(ec.SECP256R1(), backend) request = x509.CertificateSigningRequestBuilder().subject_name( x509.Name([ -- cgit v1.2.3