diff options
-rw-r--r-- | src/_cffi_src/openssl/asn1.py | 5 | ||||
-rw-r--r-- | src/_cffi_src/openssl/x509v3.py | 6 | ||||
-rw-r--r-- | src/cryptography/hazmat/backends/openssl/backend.py | 63 | ||||
-rw-r--r-- | src/cryptography/hazmat/backends/openssl/x509.py | 1 | ||||
-rw-r--r-- | src/cryptography/x509.py | 4 | ||||
-rw-r--r-- | tests/test_x509.py | 36 |
6 files changed, 96 insertions, 19 deletions
diff --git a/src/_cffi_src/openssl/asn1.py b/src/_cffi_src/openssl/asn1.py index c18708c5..5210c7c9 100644 --- a/src/_cffi_src/openssl/asn1.py +++ b/src/_cffi_src/openssl/asn1.py @@ -42,7 +42,7 @@ typedef struct asn1_string_st ASN1_OCTET_STRING; typedef struct asn1_string_st ASN1_IA5STRING; typedef ... ASN1_BIT_STRING; typedef ... ASN1_OBJECT; -typedef ... ASN1_STRING; +typedef struct asn1_string_st ASN1_STRING; typedef ... ASN1_TYPE; typedef ... ASN1_GENERALIZEDTIME; typedef ... ASN1_ENUMERATED; @@ -87,6 +87,9 @@ ASN1_OCTET_STRING *ASN1_OCTET_STRING_new(void); void ASN1_OCTET_STRING_free(ASN1_OCTET_STRING *); int ASN1_OCTET_STRING_set(ASN1_OCTET_STRING *, const unsigned char *, int); +/* ASN1 IA5STRING */ +ASN1_IA5STRING *ASN1_IA5STRING_new(void); + /* ASN1 INTEGER */ ASN1_INTEGER *ASN1_INTEGER_new(void); void ASN1_INTEGER_free(ASN1_INTEGER *); diff --git a/src/_cffi_src/openssl/x509v3.py b/src/_cffi_src/openssl/x509v3.py index 166fa59d..36d90911 100644 --- a/src/_cffi_src/openssl/x509v3.py +++ b/src/_cffi_src/openssl/x509v3.py @@ -172,7 +172,9 @@ FUNCTIONS = """ int X509V3_EXT_add_alias(int, int); void X509V3_set_ctx(X509V3_CTX *, X509 *, X509 *, X509_REQ *, X509_CRL *, int); X509_EXTENSION *X509V3_EXT_nconf(CONF *, X509V3_CTX *, char *, char *); +GENERAL_NAME *GENERAL_NAME_new(void); int GENERAL_NAME_print(BIO *, GENERAL_NAME *); +GENERAL_NAMES *GENERAL_NAMES_new(void); void GENERAL_NAMES_free(GENERAL_NAMES *); void *X509V3_EXT_d2i(X509_EXTENSION *); """ @@ -183,6 +185,7 @@ MACROS = """ int i2d_BASIC_CONSTRAINTS(BASIC_CONSTRAINTS *, unsigned char **); BASIC_CONSTRAINTS *BASIC_CONSTRAINTS_new(void); void BASIC_CONSTRAINTS_free(BASIC_CONSTRAINTS *); + /* This is a macro defined by a call to DECLARE_ASN1_FUNCTIONS in the x509v3.h header. */ void AUTHORITY_KEYID_free(AUTHORITY_KEYID *); @@ -191,6 +194,9 @@ NAME_CONSTRAINTS *NAME_CONSTRAINTS_new(void); void NAME_CONSTRAINTS_free(NAME_CONSTRAINTS *); void *X509V3_set_ctx_nodb(X509V3_CTX *); + +int i2d_GENERAL_NAMES(GENERAL_NAMES *, unsigned char **); + int sk_GENERAL_NAME_num(struct stack_st_GENERAL_NAME *); int sk_GENERAL_NAME_push(struct stack_st_GENERAL_NAME *, GENERAL_NAME *); GENERAL_NAME *sk_GENERAL_NAME_value(struct stack_st_GENERAL_NAME *, int); diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py index 88a17de0..51f68899 100644 --- a/src/cryptography/hazmat/backends/openssl/backend.py +++ b/src/cryptography/hazmat/backends/openssl/backend.py @@ -115,9 +115,7 @@ def _txt2obj(backend, name): return obj -def _encode_basic_constraints(backend, basic_constraints, critical): - obj = _txt2obj(backend, x509.OID_BASIC_CONSTRAINTS.dotted_string) - assert obj is not None +def _encode_basic_constraints(backend, basic_constraints): constraints = backend._lib.BASIC_CONSTRAINTS_new() constraints = backend._ffi.gc( constraints, backend._lib.BASIC_CONSTRAINTS_free @@ -135,18 +133,37 @@ def _encode_basic_constraints(backend, basic_constraints, critical): pp = backend._ffi.gc( pp, lambda pointer: backend._lib.OPENSSL_free(pointer[0]) ) + return pp, r + + +def _encode_subject_alt_name(backend, san): + general_names = backend._lib.GENERAL_NAMES_new() + assert general_names != backend._ffi.NULL + # TODO: GC + + for alt_name in san: + assert isinstance(alt_name, x509.DNSName) + gn = backend._lib.GENERAL_NAME_new() + assert gn != backend._ffi.NULL + gn.type = backend._lib.GEN_DNS + ia5 = backend._lib.ASN1_IA5STRING_new() + assert ia5 != backend._ffi.NULL + gn.d.dNSName = ia5 + # TODO: idna + value = alt_name.value.encode("ascii") + res = backend._lib.ASN1_STRING_set(gn.d.dNSName, value, len(value)) + assert res == 1 - # 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, pp[0], r), - ) - assert extension != backend._ffi.NULL + res = backend._lib.sk_GENERAL_NAME_push(general_names, gn) + assert res == 1 - # Return the wrapped extension. - return extension + pp = backend._ffi.new("unsigned char **") + r = backend._lib.i2d_GENERAL_NAMES(general_names, pp) + assert r > 0 + pp = backend._ffi.gc( + pp, lambda pointer: backend._lib.OPENSSL_free(pointer[0]) + ) + return pp, r @utils.register_interface(CipherBackend) @@ -855,13 +872,25 @@ class Backend(object): ) for extension in builder._extensions: if isinstance(extension.value, x509.BasicConstraints): - extension = _encode_basic_constraints( - self, - extension.value, - extension.critical + pp, r = _encode_basic_constraints( + self, extension.value, + ) + elif isinstance(extension.value, x509.SubjectAlternativeName): + pp, r = _encode_subject_alt_name( + self, extension.value, ) else: raise NotImplementedError('Extension not yet supported.') + + obj = _txt2obj(self, extension.oid.dotted_string) + extension = backend._lib.X509_EXTENSION_create_by_OBJ( + backend._ffi.NULL, + obj, + 1 if extension.critical else 0, + _encode_asn1_str(backend, pp[0], r) + ) + assert extension != backend._ffi.NULL + 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/src/cryptography/hazmat/backends/openssl/x509.py b/src/cryptography/hazmat/backends/openssl/x509.py index cc805755..804bce66 100644 --- a/src/cryptography/hazmat/backends/openssl/x509.py +++ b/src/cryptography/hazmat/backends/openssl/x509.py @@ -736,5 +736,6 @@ _CSR_EXTENSION_PARSER = _X509ExtensionParser( get_ext=lambda backend, x, i: backend._lib.sk_X509_EXTENSION_value(x, i), handlers={ x509.OID_BASIC_CONSTRAINTS: _decode_basic_constraints, + x509.OID_SUBJECT_ALTERNATIVE_NAME: _decode_subject_alt_name, } ) diff --git a/src/cryptography/x509.py b/src/cryptography/x509.py index 668bc2ef..a091cd78 100644 --- a/src/cryptography/x509.py +++ b/src/cryptography/x509.py @@ -1472,6 +1472,10 @@ class CertificateSigningRequestBuilder(object): extension = Extension( OID_SUBJECT_ALTERNATIVE_NAME, critical, extension ) + elif isinstance(extension, KeyUsage): + extension = Extension( + OID_KEY_USAGE, critical, extension + ) else: raise NotImplementedError('Unsupported X.509 extension.') # TODO: This is quadratic in the number of extensions diff --git a/tests/test_x509.py b/tests/test_x509.py index df315cc3..133f0535 100644 --- a/tests/test_x509.py +++ b/tests/test_x509.py @@ -864,7 +864,17 @@ class TestCertificateSigningRequestBuilder(object): x509.NameAttribute(x509.OID_COUNTRY_NAME, u'US'), ]) ).add_extension( - x509.SubjectAlternativeName([x509.DNSName(u"cryptography.io")]), + x509.KeyUsage( + digital_signature=True, + content_commitment=True, + key_encipherment=False, + data_encipherment=False, + key_agreement=False, + key_cert_sign=True, + crl_sign=False, + encipher_only=False, + decipher_only=False + ), critical=False, ) with pytest.raises(NotImplementedError): @@ -884,6 +894,30 @@ class TestCertificateSigningRequestBuilder(object): ]) ) + def test_subject_alt_names(self, backend): + private_key = RSA_KEY_2048.private_key(backend) + + csr = x509.CertificateSigningRequestBuilder().subject_name( + x509.Name([ + x509.NameAttribute(x509.OID_COMMON_NAME, u"SAN"), + ]) + ).add_extension( + x509.SubjectAlternativeName([ + x509.DNSName(u"google.com"), + ]), + critical=False, + ).sign(private_key, hashes.SHA256(), backend) + + assert len(csr.extensions) == 1 + ext = csr.extensions.get_extension_for_oid( + x509.OID_SUBJECT_ALTERNATIVE_NAME + ) + assert not ext.critical + assert ext.oid == x509.OID_SUBJECT_ALTERNATIVE_NAME + assert list(ext.value) == [ + x509.DNSName(u"google.com"), + ] + @pytest.mark.requires_backend_interface(interface=DSABackend) @pytest.mark.requires_backend_interface(interface=X509Backend) |