diff options
-rw-r--r-- | src/cryptography/hazmat/backends/openssl/backend.py | 84 | ||||
-rw-r--r-- | src/cryptography/hazmat/backends/openssl/encode_asn1.py | 4 | ||||
-rw-r--r-- | tests/test_x509.py | 33 |
3 files changed, 88 insertions, 33 deletions
diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py index c6ede932..ab1dcae9 100644 --- a/src/cryptography/hazmat/backends/openssl/backend.py +++ b/src/cryptography/hazmat/backends/openssl/backend.py @@ -32,7 +32,7 @@ from cryptography.hazmat.backends.openssl.encode_asn1 import ( _CRL_ENTRY_EXTENSION_ENCODE_HANDLERS, _CRL_EXTENSION_ENCODE_HANDLERS, _EXTENSION_ENCODE_HANDLERS, _encode_asn1_int_gc, _encode_asn1_str_gc, _encode_name_gc, - _txt2obj_gc, + _encode_unrecognized_extension, _txt2obj_gc, ) from cryptography.hazmat.backends.openssl.hashes import _HashContext from cryptography.hazmat.backends.openssl.hmac import _HMACContext @@ -968,42 +968,47 @@ class Backend(object): def _create_x509_extensions(self, extensions, handlers, x509_obj, add_func, gc): for i, extension in enumerate(extensions): - try: - encode = handlers[extension.oid] - except KeyError: - raise NotImplementedError( - 'Extension not supported: {0}'.format(extension.oid) + if isinstance(extension.value, x509.UnrecognizedExtension): + x509_extension = self._create_unrecognized_x509_extension( + extension ) + else: + try: + encode = handlers[extension.oid] + except KeyError: + raise NotImplementedError( + 'Extension not supported: {0}'.format(extension.oid) + ) - ext_struct = encode(self, extension.value) - nid = self._lib.OBJ_txt2nid( - extension.oid.dotted_string.encode("ascii") - ) - backend.openssl_assert(nid != self._lib.NID_undef) - x509_extension = self._lib.X509V3_EXT_i2d( - nid, 1 if extension.critical else 0, ext_struct - ) - if ( - x509_extension == self._ffi.NULL and - extension.oid == x509.OID_CERTIFICATE_ISSUER - ): - # This path exists to support OpenSSL 0.9.8, which does - # not know how to encode a CERTIFICATE_ISSUER for CRLs. Once we - # drop 0.9.8 support we can remove this. - self._consume_errors() - pp = backend._ffi.new("unsigned char **") - r = self._lib.i2d_GENERAL_NAMES(ext_struct, pp) - backend.openssl_assert(r > 0) - pp = backend._ffi.gc( - pp, lambda pointer: backend._lib.OPENSSL_free(pointer[0]) + ext_struct = encode(self, extension.value) + nid = self._lib.OBJ_txt2nid( + extension.oid.dotted_string.encode("ascii") ) - obj = _txt2obj_gc(self, extension.oid.dotted_string) - x509_extension = self._lib.X509_EXTENSION_create_by_OBJ( - self._ffi.NULL, - obj, - 1 if extension.critical else 0, - _encode_asn1_str_gc(self, pp[0], r) + backend.openssl_assert(nid != self._lib.NID_undef) + x509_extension = self._lib.X509V3_EXT_i2d( + nid, 1 if extension.critical else 0, ext_struct ) + if ( + x509_extension == self._ffi.NULL and + extension.oid == x509.OID_CERTIFICATE_ISSUER + ): + # This path exists to support OpenSSL 0.9.8, which does not + # know how to encode a CERTIFICATE_ISSUER for CRLs. Once we + # drop 0.9.8 support we can remove this. + self._consume_errors() + pp = backend._ffi.new("unsigned char **") + r = self._lib.i2d_GENERAL_NAMES(ext_struct, pp) + backend.openssl_assert(r > 0) + pp = backend._ffi.gc( + pp, lambda pointer: backend._lib.OPENSSL_free(pointer[0]) + ) + obj = _txt2obj_gc(self, extension.oid.dotted_string) + x509_extension = self._lib.X509_EXTENSION_create_by_OBJ( + self._ffi.NULL, + obj, + 1 if extension.critical else 0, + _encode_asn1_str_gc(self, pp[0], r) + ) self.openssl_assert(x509_extension != self._ffi.NULL) @@ -1014,6 +1019,19 @@ class Backend(object): res = add_func(x509_obj, x509_extension, i) self.openssl_assert(res >= 1) + def _create_unrecognized_x509_extension(self, extension): + obj = _txt2obj_gc(self, extension.oid.dotted_string) + value = _encode_asn1_str_gc( + self, extension.value.value, len(extension.value.value) + ) + return self._lib.X509_EXTENSION_create_by_OBJ( + self._ffi.NULL, + obj, + 1 if extension.critical else 0, + value + ) + + def create_x509_revoked_certificate(self, builder): if not isinstance(builder, x509.RevokedCertificateBuilder): raise TypeError('Builder type mismatch.') diff --git a/src/cryptography/hazmat/backends/openssl/encode_asn1.py b/src/cryptography/hazmat/backends/openssl/encode_asn1.py index b0e2e73e..5394c2db 100644 --- a/src/cryptography/hazmat/backends/openssl/encode_asn1.py +++ b/src/cryptography/hazmat/backends/openssl/encode_asn1.py @@ -557,6 +557,10 @@ def _encode_general_subtree(backend, subtrees): return general_subtrees +def _encode_unrecognized_extension(backend, ext): + return ext.value + + _EXTENSION_ENCODE_HANDLERS = { ExtensionOID.BASIC_CONSTRAINTS: _encode_basic_constraints, ExtensionOID.SUBJECT_KEY_IDENTIFIER: _encode_subject_key_identifier, diff --git a/tests/test_x509.py b/tests/test_x509.py index a6398bb3..aaeefae9 100644 --- a/tests/test_x509.py +++ b/tests/test_x509.py @@ -2382,6 +2382,39 @@ class TestCertificateBuilder(object): ) assert basic_constraints.value.path_length is None + @pytest.mark.parametrize( + "unrecognized", [ + x509.UnrecognizedExtension( + x509.ObjectIdentifier("1.2.3.4.5"), + b"abcdef", + ) + ] + ) + @pytest.mark.requires_backend_interface(interface=RSABackend) + @pytest.mark.requires_backend_interface(interface=X509Backend) + def test_unrecognized_extension(self, backend, unrecognized): + private_key = RSA_KEY_2048.private_key(backend) + + cert = x509.CertificateBuilder().subject_name( + x509.Name([x509.NameAttribute(x509.OID_COUNTRY_NAME, u'US')]) + ).issuer_name( + x509.Name([x509.NameAttribute(x509.OID_COUNTRY_NAME, u'US')]) + ).not_valid_before( + datetime.datetime(2002, 1, 1, 12, 1) + ).not_valid_after( + datetime.datetime(2030, 12, 31, 8, 30) + ).public_key( + private_key.public_key() + ).serial_number( + 123 + ).add_extension( + unrecognized, critical=False + ).sign(private_key, hashes.SHA256(), backend) + + ext = cert.extensions.get_extension_for_oid(unrecognized.oid) + + assert ext.value == unrecognized + @pytest.mark.requires_backend_interface(interface=X509Backend) class TestCertificateSigningRequestBuilder(object): |