aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/_cffi_src/openssl/asn1.py5
-rw-r--r--src/_cffi_src/openssl/x509v3.py1
-rw-r--r--src/cryptography/hazmat/backends/openssl/backend.py34
-rw-r--r--src/cryptography/hazmat/backends/openssl/x509.py1
-rw-r--r--tests/test_x509.py36
5 files changed, 74 insertions, 3 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 52287459..36d90911 100644
--- a/src/_cffi_src/openssl/x509v3.py
+++ b/src/_cffi_src/openssl/x509v3.py
@@ -185,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 *);
diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py
index 73a58637..f05b0515 100644
--- a/src/cryptography/hazmat/backends/openssl/backend.py
+++ b/src/cryptography/hazmat/backends/openssl/backend.py
@@ -136,6 +136,36 @@ def _encode_basic_constraints(backend, basic_constraints):
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
+
+ res = backend._lib.sk_GENERAL_NAME_push(general_names, gn)
+ assert res == 1
+
+ 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)
@utils.register_interface(CMACBackend)
@utils.register_interface(DERSerializationBackend)
@@ -841,12 +871,14 @@ class Backend(object):
self._lib.sk_X509_EXTENSION_free,
)
for extension in builder._extensions:
- obj = _txt2obj(self, extension.oid.dotted_string)
if isinstance(extension.value, x509.BasicConstraints):
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 = self._lib.X509_EXTENSION_create_by_OBJ(
self._ffi.NULL,
obj,
diff --git a/src/cryptography/hazmat/backends/openssl/x509.py b/src/cryptography/hazmat/backends/openssl/x509.py
index c7ca2ad1..80e5f2b1 100644
--- a/src/cryptography/hazmat/backends/openssl/x509.py
+++ b/src/cryptography/hazmat/backends/openssl/x509.py
@@ -770,5 +770,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/tests/test_x509.py b/tests/test_x509.py
index ac910392..3491446d 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,
).add_extension(
x509.KeyUsage(
@@ -897,6 +907,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)