diff options
author | Paul Kehrer <paul.l.kehrer@gmail.com> | 2015-04-20 15:00:16 -0500 |
---|---|---|
committer | Paul Kehrer <paul.l.kehrer@gmail.com> | 2015-04-20 22:12:32 -0500 |
commit | 40f8338e986610f57ef905e510e0bf8f796e43e8 (patch) | |
tree | 1b0444a8d78b9a85831c587ffaa0730a5524240a | |
parent | bd11e028dcf763171097f5366f87f95ad0371a03 (diff) | |
download | cryptography-40f8338e986610f57ef905e510e0bf8f796e43e8.tar.gz cryptography-40f8338e986610f57ef905e510e0bf8f796e43e8.tar.bz2 cryptography-40f8338e986610f57ef905e510e0bf8f796e43e8.zip |
Support Subject Alternative Name in the OpenSSL backend
Adds only DNS support first
-rw-r--r-- | setup.py | 1 | ||||
-rw-r--r-- | src/cryptography/hazmat/backends/openssl/x509.py | 30 | ||||
-rw-r--r-- | src/cryptography/x509.py | 17 | ||||
-rw-r--r-- | tests/test_x509_ext.py | 21 |
4 files changed, 69 insertions, 0 deletions
@@ -32,6 +32,7 @@ with open(os.path.join(src_dir, "cryptography", "__about__.py")) as f: VECTORS_DEPENDENCY = "cryptography_vectors=={0}".format(about['__version__']) requirements = [ + "idna", "pyasn1", "six>=1.4.1", "setuptools" diff --git a/src/cryptography/hazmat/backends/openssl/x509.py b/src/cryptography/hazmat/backends/openssl/x509.py index 57e6146b..363d3df1 100644 --- a/src/cryptography/hazmat/backends/openssl/x509.py +++ b/src/cryptography/hazmat/backends/openssl/x509.py @@ -15,6 +15,8 @@ from __future__ import absolute_import, division, print_function import datetime +import idna + from cryptography import utils, x509 from cryptography.exceptions import UnsupportedAlgorithm from cryptography.hazmat.primitives import hashes @@ -57,6 +59,14 @@ def _build_x509_name(backend, x509_name): return x509.Name(attributes) +def _build_general_name(backend, gn): + if gn.type == backend._lib.GEN_DNS: + data = backend._ffi.buffer( + gn.d.dNSName.data, gn.d.dNSName.length + )[:].decode("ascii") + return x509.DNSName(idna.decode(data)) + + @utils.register_interface(x509.Certificate) class _Certificate(object): def __init__(self, backend, x509): @@ -173,6 +183,8 @@ class _Certificate(object): value = self._build_subject_key_identifier(ext) elif oid == x509.OID_KEY_USAGE: value = self._build_key_usage(ext) + elif oid == x509.OID_SUBJECT_ALTERNATIVE_NAME: + value = self._build_subject_alt_name(ext) elif critical: raise x509.UnsupportedExtension( "{0} is not currently supported".format(oid), oid @@ -254,6 +266,24 @@ class _Certificate(object): decipher_only ) + def _build_subject_alt_name(self, ext): + gns = self._backend._ffi.cast( + "GENERAL_NAMES *", self._backend._lib.X509V3_EXT_d2i(ext) + ) + assert gns != self._backend._ffi.NULL + gns = self._backend._ffi.gc(gns, self._backend._lib.GENERAL_NAMES_free) + num = self._backend._lib.sk_GENERAL_NAME_num(gns) + general_names = [] + + for i in range(0, num): + gn = self._backend._lib.sk_GENERAL_NAME_value(gns, i) + assert gn != self._backend._ffi.NULL + value = _build_general_name(self._backend, gn) + + general_names.append(value) + + return x509.SubjectAlternativeName(general_names) + @utils.register_interface(x509.CertificateSigningRequest) class _CertificateSigningRequest(object): diff --git a/src/cryptography/x509.py b/src/cryptography/x509.py index cdc0e430..9e4b5eea 100644 --- a/src/cryptography/x509.py +++ b/src/cryptography/x509.py @@ -70,6 +70,19 @@ _OID_NAMES = { } +_GENERAL_NAMES = { + 0: "otherName", + 1: "rfc822Name", + 2: "dNSName", + 3: "x400Address", + 4: "directoryName", + 5: "ediPartyName", + 6: "uniformResourceIdentifier", + 7: "iPAddress", + 8: "registeredID", +} + + class Version(Enum): v1 = 0 v3 = 2 @@ -115,6 +128,10 @@ class ExtensionNotFound(Exception): self.oid = oid +class UnsupportedGeneralNameType(Exception): + pass + + class NameAttribute(object): def __init__(self, oid, value): if not isinstance(oid, ObjectIdentifier): diff --git a/tests/test_x509_ext.py b/tests/test_x509_ext.py index 8516a339..2fa659ef 100644 --- a/tests/test_x509_ext.py +++ b/tests/test_x509_ext.py @@ -730,3 +730,24 @@ class TestSubjectAlternativeName(object): assert repr(san) == ( "<SubjectAlternativeName([<DNSName(value=cryptography.io)>])>" ) + + +@pytest.mark.requires_backend_interface(interface=RSABackend) +@pytest.mark.requires_backend_interface(interface=X509Backend) +class TestRSASubjectAlternativeNameExtension(object): + def test_dns_name(self, backend): + cert = _load_cert( + os.path.join("x509", "cryptography.io.pem"), + x509.load_pem_x509_certificate, + backend + ) + ext = cert.extensions.get_extension_for_oid( + x509.OID_SUBJECT_ALTERNATIVE_NAME + ) + assert ext is not None + assert ext.critical is False + + san = ext.value + + dns = san.get_values_for_type(x509.DNSName) + assert dns == [u"www.cryptography.io", u"cryptography.io"] |