Merge pull request #2204 from reaperhulk/ski-classmethod

SubjectKeyIdentifier classmethod
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index f07a908..63877d8 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -11,6 +11,7 @@
   memory consumption. Due to this change we no longer support PyPy releases
   older than 2.6 nor do we support any released version of PyPy3 (until a
   version supporting cffi 1.0 comes out).
+* Fix parsing of OpenSSH public keys that have spaces in comments.
 * Support serialization of certificate signing requests using the
   ``public_bytes`` method of
   :class:`~cryptography.x509.CertificateSigningRequest`.
diff --git a/docs/development/test-vectors.rst b/docs/development/test-vectors.rst
index d400e66..1c84435 100644
--- a/docs/development/test-vectors.rst
+++ b/docs/development/test-vectors.rst
@@ -102,6 +102,8 @@
   tree.
 * ``cryptography.io.pem`` - A leaf certificate issued by RapidSSL for the
   cryptography website.
+* ``rapidssl_sha256_ca_g3.pem`` - The intermediate CA that issued the
+  ``cryptography.io.pem`` certificate.
 * ``wildcard_san.pem`` - A leaf certificate issued by a public CA for
   ``langui.sh`` that contains wildcard entries in the SAN extension.
 * ``san_edipartyname.der`` - A DSA certificate from a `Mozilla bug`_
diff --git a/docs/hazmat/backends/interfaces.rst b/docs/hazmat/backends/interfaces.rst
index fb3786c..442bd0d 100644
--- a/docs/hazmat/backends/interfaces.rst
+++ b/docs/hazmat/backends/interfaces.rst
@@ -550,6 +550,26 @@
         :returns: A new object with the
             :class:`~cryptography.x509.CertificateSigningRequest` interface.
 
+    .. method:: create_x509_certificate(builder, private_key, algorithm)
+
+        .. versionadded:: 1.0
+
+        :param builder: An instance of
+            :class:`~cryptography.x509.CertificateBuilder`.
+
+        :param private_key: The
+            :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 certificate.
+
+        :param algorithm: The
+            :class:`~cryptography.hazmat.primitives.hashes.HashAlgorithm`
+            that will be used to generate the certificate signature.
+
+        :returns: A new object with the
+            :class:`~cryptography.x509.Certificate` interface.
+
 
 .. class:: DHBackend
 
diff --git a/docs/x509/reference.rst b/docs/x509/reference.rst
index e83a4ac..dfa91fa 100644
--- a/docs/x509/reference.rst
+++ b/docs/x509/reference.rst
@@ -909,14 +909,28 @@
 
         Returns an instance of the extension type corresponding to the OID.
 
+.. class:: ExtensionType
+
+    .. versionadded:: 1.0
+
+    This is the interface against which all the following extension types are
+    registered.
+
 .. class:: KeyUsage
 
     .. versionadded:: 0.9
 
     The key usage extension defines the purpose of the key contained in the
     certificate.  The usage restriction might be employed when a key that could
-    be used for more than one operation is to be restricted. It corresponds to
-    :data:`OID_KEY_USAGE`.
+    be used for more than one operation is to be restricted.
+
+    .. attribute:: oid
+
+        .. versionadded:: 1.0
+
+        :type: :class:`ObjectIdentifier`
+
+        Returns :data:`OID_KEY_USAGE`.
 
     .. attribute:: digital_signature
 
@@ -1007,8 +1021,15 @@
 
     Basic constraints is an X.509 extension type that defines whether a given
     certificate is allowed to sign additional certificates and what path
-    length restrictions may exist. It corresponds to
-    :data:`OID_BASIC_CONSTRAINTS`.
+    length restrictions may exist.
+
+    .. attribute:: oid
+
+        .. versionadded:: 1.0
+
+        :type: :class:`ObjectIdentifier`
+
+        Returns :data:`OID_BASIC_CONSTRAINTS`.
 
     .. attribute:: ca
 
@@ -1038,6 +1059,15 @@
     purposes indicated in the key usage extension. The object is
     iterable to obtain the list of :ref:`extended key usage OIDs <eku_oids>`.
 
+    .. attribute:: oid
+
+        .. versionadded:: 1.0
+
+        :type: :class:`ObjectIdentifier`
+
+        Returns :data:`OID_EXTENDED_KEY_USAGE`.
+
+
 .. class:: OCSPNoCheck
 
     .. versionadded:: 1.0
@@ -1051,6 +1081,14 @@
     extension is only relevant when the certificate is an authorized OCSP
     responder.
 
+    .. attribute:: oid
+
+        .. versionadded:: 1.0
+
+        :type: :class:`ObjectIdentifier`
+
+        Returns :data:`OID_OCSP_NO_CHECK`.
+
 .. class:: NameConstraints
 
     .. versionadded:: 1.0
@@ -1060,6 +1098,14 @@
     beneath the CA certificate must (or must not) be in. For specific details
     on the way this extension should be processed see :rfc:`5280`.
 
+    .. attribute:: oid
+
+        .. versionadded:: 1.0
+
+        :type: :class:`ObjectIdentifier`
+
+        Returns :data:`OID_NAME_CONSTRAINTS`.
+
     .. attribute:: permitted_subtrees
 
         :type: list of :class:`GeneralName` objects or None
@@ -1087,6 +1133,14 @@
     certificate chain. For more information about generation and use of this
     extension see `RFC 5280 section 4.2.1.1`_.
 
+    .. attribute:: oid
+
+        .. versionadded:: 1.0
+
+        :type: :class:`ObjectIdentifier`
+
+        Returns :data:`OID_AUTHORITY_KEY_IDENTIFIER`.
+
     .. attribute:: key_identifier
 
         :type: bytes
@@ -1113,6 +1167,14 @@
     The subject key identifier extension provides a means of identifying
     certificates that contain a particular public key.
 
+    .. attribute:: oid
+
+        .. versionadded:: 1.0
+
+        :type: :class:`ObjectIdentifier`
+
+        Returns :data:`OID_SUBJECT_KEY_IDENTIFIER`.
+
     .. attribute:: digest
 
         :type: bytes
@@ -1145,6 +1207,14 @@
     of identities for which the certificate is valid. The object is iterable to
     get every element.
 
+    .. attribute:: oid
+
+        .. versionadded:: 1.0
+
+        :type: :class:`ObjectIdentifier`
+
+        Returns :data:`OID_SUBJECT_ALTERNATIVE_NAME`.
+
     .. method:: get_values_for_type(type)
 
         :param type: A :class:`GeneralName` provider. This is one of the
@@ -1175,6 +1245,14 @@
     of identities for the certificate issuer. The object is iterable to
     get every element.
 
+    .. attribute:: oid
+
+        .. versionadded:: 1.0
+
+        :type: :class:`ObjectIdentifier`
+
+        Returns :data:`OID_ISSUER_ALTERNATIVE_NAME`.
+
     .. method:: get_values_for_type(type)
 
         :param type: A :class:`GeneralName` provider. This is one of the
@@ -1193,6 +1271,14 @@
     validation services (such as OCSP) and issuer data. It is an iterable,
     containing one or more :class:`AccessDescription` instances.
 
+    .. attribute:: oid
+
+        .. versionadded:: 1.0
+
+        :type: :class:`ObjectIdentifier`
+
+        Returns :data:`OID_AUTHORITY_INFORMATION_ACCESS`.
+
 
 .. class:: AccessDescription
 
@@ -1223,6 +1309,14 @@
     obtained. It is an iterable, containing one or more
     :class:`DistributionPoint` instances.
 
+    .. attribute:: oid
+
+        .. versionadded:: 1.0
+
+        :type: :class:`ObjectIdentifier`
+
+        Returns :data:`OID_CRL_DISTRIBUTION_POINTS`.
+
 .. class:: DistributionPoint
 
     .. versionadded:: 0.9
@@ -1321,6 +1415,14 @@
     certificates issued by the subject of this certificate, but not in
     additional certificates in the path.
 
+    .. attribute:: oid
+
+        .. versionadded:: 1.0
+
+        :type: :class:`ObjectIdentifier`
+
+        Returns :data:`OID_INHIBIT_ANY_POLICY`.
+
     .. attribute:: skip_certs
 
         :type: int
@@ -1332,6 +1434,14 @@
     The certificate policies extension is an iterable, containing one or more
     :class:`PolicyInformation` instances.
 
+    .. attribute:: oid
+
+        .. versionadded:: 1.0
+
+        :type: :class:`ObjectIdentifier`
+
+        Returns :data:`OID_CERTIFICATE_POLICIES`.
+
 Certificate Policies Classes
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
@@ -1685,6 +1795,11 @@
     Corresponds to the dotted string ``"1.3.6.1.5.5.7.1.1"``. The identifier
     for the :class:`AuthorityInformationAccess` extension type.
 
+.. data:: OID_INHIBIT_ANY_POLICY
+
+    Corresponds to the dotted string ``"2.5.29.54"``. The identifier
+    for the :class:`InhibitAnyPolicy` extension type.
+
 .. data:: OID_OCSP_NO_CHECK
 
     Corresponds to the dotted string ``"1.3.6.1.5.5.7.48.1.5"``. The identifier
diff --git a/src/_cffi_src/openssl/x509name.py b/src/_cffi_src/openssl/x509name.py
index 2d87db4..7b833d6 100644
--- a/src/_cffi_src/openssl/x509name.py
+++ b/src/_cffi_src/openssl/x509name.py
@@ -15,10 +15,13 @@
 """
 
 TYPES = """
-typedef ... X509_NAME;
+typedef ... Cryptography_STACK_OF_X509_NAME_ENTRY;
+typedef struct {
+    Cryptography_STACK_OF_X509_NAME_ENTRY *entries;
+    ...;
+} X509_NAME;
 typedef ... X509_NAME_ENTRY;
 typedef ... Cryptography_STACK_OF_X509_NAME;
-typedef ... Cryptography_STACK_OF_X509_NAME_ENTRY;
 """
 
 FUNCTIONS = """
@@ -55,6 +58,9 @@
 int sk_X509_NAME_ENTRY_num(Cryptography_STACK_OF_X509_NAME_ENTRY *);
 X509_NAME_ENTRY *sk_X509_NAME_ENTRY_value(
     Cryptography_STACK_OF_X509_NAME_ENTRY *, int);
+Cryptography_STACK_OF_X509_NAME_ENTRY *sk_X509_NAME_ENTRY_dup(
+    Cryptography_STACK_OF_X509_NAME_ENTRY *
+);
 """
 
 CUSTOMIZATIONS = """
diff --git a/src/_cffi_src/openssl/x509v3.py b/src/_cffi_src/openssl/x509v3.py
index f6a1890..6e35dac 100644
--- a/src/_cffi_src/openssl/x509v3.py
+++ b/src/_cffi_src/openssl/x509v3.py
@@ -189,7 +189,9 @@
 void BASIC_CONSTRAINTS_free(BASIC_CONSTRAINTS *);
 /* This is a macro defined by a call to DECLARE_ASN1_FUNCTIONS in the
    x509v3.h header. */
+AUTHORITY_KEYID *AUTHORITY_KEYID_new(void);
 void AUTHORITY_KEYID_free(AUTHORITY_KEYID *);
+int i2d_AUTHORITY_KEYID(AUTHORITY_KEYID *, unsigned char **);
 
 NAME_CONSTRAINTS *NAME_CONSTRAINTS_new(void);
 void NAME_CONSTRAINTS_free(NAME_CONSTRAINTS *);
@@ -283,6 +285,9 @@
 DIST_POINT *DIST_POINT_new(void);
 void DIST_POINT_free(DIST_POINT *);
 
+DIST_POINT_NAME *DIST_POINT_NAME_new(void);
+void DIST_POINT_NAME_free(DIST_POINT_NAME *);
+
 int i2d_CRL_DIST_POINTS(Cryptography_STACK_OF_DIST_POINT *, unsigned char **);
 """
 
diff --git a/src/cryptography/hazmat/backends/interfaces.py b/src/cryptography/hazmat/backends/interfaces.py
index 49ccda1..a43621a 100644
--- a/src/cryptography/hazmat/backends/interfaces.py
+++ b/src/cryptography/hazmat/backends/interfaces.py
@@ -281,9 +281,9 @@
         """
 
     @abc.abstractmethod
-    def sign_x509_certificate(self, builder, private_key, algorithm):
+    def create_x509_certificate(self, builder, private_key, algorithm):
         """
-        Sign an X.509 Certificate from a CertificateBuilder object.
+        Create and sign an X.509 certificate from a CertificateBuilder object.
         """
 
 
diff --git a/src/cryptography/hazmat/backends/multibackend.py b/src/cryptography/hazmat/backends/multibackend.py
index 8008989..9db32aa 100644
--- a/src/cryptography/hazmat/backends/multibackend.py
+++ b/src/cryptography/hazmat/backends/multibackend.py
@@ -352,9 +352,9 @@
             _Reasons.UNSUPPORTED_X509
         )
 
-    def sign_x509_certificate(self, builder, private_key, algorithm):
+    def create_x509_certificate(self, builder, private_key, algorithm):
         for b in self._filtered_backends(X509Backend):
-            return b.sign_x509_certificate(builder, private_key, algorithm)
+            return b.create_x509_certificate(builder, private_key, algorithm)
 
         raise UnsupportedAlgorithm(
             "This backend does not support X.509.",
diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py
index f9da9ea..2752d98 100644
--- a/src/cryptography/hazmat/backends/openssl/backend.py
+++ b/src/cryptography/hazmat/backends/openssl/backend.py
@@ -38,7 +38,8 @@
     _RSAPrivateKey, _RSAPublicKey
 )
 from cryptography.hazmat.backends.openssl.x509 import (
-    _Certificate, _CertificateSigningRequest
+    _Certificate, _CertificateSigningRequest, _DISTPOINT_TYPE_FULLNAME,
+    _DISTPOINT_TYPE_RELATIVENAME
 )
 from cryptography.hazmat.bindings.openssl.binding import Binding
 from cryptography.hazmat.primitives import hashes, serialization
@@ -228,18 +229,22 @@
     return pp, r
 
 
-def _encode_subject_alt_name(backend, san):
+def _encode_general_names(backend, names):
     general_names = backend._lib.GENERAL_NAMES_new()
     assert general_names != backend._ffi.NULL
-    general_names = backend._ffi.gc(
-        general_names, backend._lib.GENERAL_NAMES_free
-    )
-
-    for alt_name in san:
-        gn = _encode_general_name(backend, alt_name)
+    for name in names:
+        gn = _encode_general_name(backend, name)
         res = backend._lib.sk_GENERAL_NAME_push(general_names, gn)
         assert res != 0
 
+    return general_names
+
+
+def _encode_subject_alt_name(backend, san):
+    general_names = _encode_general_names(backend, san)
+    general_names = backend._ffi.gc(
+        general_names, backend._lib.GENERAL_NAMES_free
+    )
     pp = backend._ffi.new("unsigned char **")
     r = backend._lib.i2d_GENERAL_NAMES(general_names, pp)
     assert r > 0
@@ -355,6 +360,67 @@
     return pp, r
 
 
+_CRLREASONFLAGS = {
+    x509.ReasonFlags.key_compromise: 1,
+    x509.ReasonFlags.ca_compromise: 2,
+    x509.ReasonFlags.affiliation_changed: 3,
+    x509.ReasonFlags.superseded: 4,
+    x509.ReasonFlags.cessation_of_operation: 5,
+    x509.ReasonFlags.certificate_hold: 6,
+    x509.ReasonFlags.privilege_withdrawn: 7,
+    x509.ReasonFlags.aa_compromise: 8,
+}
+
+
+def _encode_crl_distribution_points(backend, crl_distribution_points):
+    cdp = backend._lib.sk_DIST_POINT_new_null()
+    cdp = backend._ffi.gc(cdp, backend._lib.sk_DIST_POINT_free)
+    for point in crl_distribution_points:
+        dp = backend._lib.DIST_POINT_new()
+        assert dp != backend._ffi.NULL
+
+        if point.reasons:
+            bitmask = backend._lib.ASN1_BIT_STRING_new()
+            assert bitmask != backend._ffi.NULL
+            dp.reasons = bitmask
+            for reason in point.reasons:
+                res = backend._lib.ASN1_BIT_STRING_set_bit(
+                    bitmask, _CRLREASONFLAGS[reason], 1
+                )
+                assert res == 1
+
+        if point.full_name:
+            dpn = backend._lib.DIST_POINT_NAME_new()
+            assert dpn != backend._ffi.NULL
+            dpn.type = _DISTPOINT_TYPE_FULLNAME
+            dpn.name.fullname = _encode_general_names(backend, point.full_name)
+            dp.distpoint = dpn
+
+        if point.relative_name:
+            dpn = backend._lib.DIST_POINT_NAME_new()
+            assert dpn != backend._ffi.NULL
+            dpn.type = _DISTPOINT_TYPE_RELATIVENAME
+            name = _encode_name_gc(backend, point.relative_name)
+            relativename = backend._lib.sk_X509_NAME_ENTRY_dup(name.entries)
+            assert relativename != backend._ffi.NULL
+            dpn.name.relativename = relativename
+            dp.distpoint = dpn
+
+        if point.crl_issuer:
+            dp.CRLissuer = _encode_general_names(backend, point.crl_issuer)
+
+        res = backend._lib.sk_DIST_POINT_push(cdp, dp)
+        assert res >= 1
+
+    pp = backend._ffi.new('unsigned char **')
+    r = backend._lib.i2d_CRL_DIST_POINTS(cdp, 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)
@@ -1096,7 +1162,7 @@
 
         return _CertificateSigningRequest(self, x509_req)
 
-    def sign_x509_certificate(self, builder, private_key, algorithm):
+    def create_x509_certificate(self, builder, private_key, algorithm):
         if not isinstance(builder, x509.CertificateBuilder):
             raise TypeError('Builder type mismatch.')
         if not isinstance(algorithm, hashes.HashAlgorithm):
@@ -1173,16 +1239,22 @@
                 pp, r = _encode_authority_information_access(
                     self, extension.value
                 )
+            elif isinstance(extension.value, x509.CRLDistributionPoints):
+                pp, r = _encode_crl_distribution_points(
+                    self, extension.value
+                )
             else:
                 raise NotImplementedError('Extension not yet supported.')
 
-            obj = _txt2obj(self, extension.oid.dotted_string)
+            obj = _txt2obj_gc(self, extension.oid.dotted_string)
             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)
             )
+            assert extension != self._ffi.NULL
+            extension = self._ffi.gc(extension, self._lib.X509_EXTENSION_free)
             res = self._lib.X509_add_ext(x509_cert, extension, i)
             assert res == 1
 
diff --git a/src/cryptography/hazmat/backends/openssl/x509.py b/src/cryptography/hazmat/backends/openssl/x509.py
index ee9a3bb..564b268 100644
--- a/src/cryptography/hazmat/backends/openssl/x509.py
+++ b/src/cryptography/hazmat/backends/openssl/x509.py
@@ -592,6 +592,10 @@
     return x509.ExtendedKeyUsage(ekus)
 
 
+_DISTPOINT_TYPE_FULLNAME = 0
+_DISTPOINT_TYPE_RELATIVENAME = 1
+
+
 def _decode_crl_distribution_points(backend, cdps):
     cdps = backend._ffi.cast("Cryptography_STACK_OF_DIST_POINT *", cdps)
     cdps = backend._ffi.gc(cdps, backend._lib.sk_DIST_POINT_free)
@@ -651,7 +655,7 @@
         # point so make sure it's not null.
         if cdp.distpoint != backend._ffi.NULL:
             # Type 0 is fullName, there is no #define for it in the code.
-            if cdp.distpoint.type == 0:
+            if cdp.distpoint.type == _DISTPOINT_TYPE_FULLNAME:
                 full_name = _decode_general_names(
                     backend, cdp.distpoint.name.fullname
                 )
diff --git a/src/cryptography/x509.py b/src/cryptography/x509.py
index 82b8bd3..5ed3c09 100644
--- a/src/cryptography/x509.py
+++ b/src/cryptography/x509.py
@@ -324,7 +324,19 @@
         return not self == other
 
 
+@six.add_metaclass(abc.ABCMeta)
+class ExtensionType(object):
+    @abc.abstractproperty
+    def oid(self):
+        """
+        Returns the oid associated with the given extension type.
+        """
+
+
+@utils.register_interface(ExtensionType)
 class ExtendedKeyUsage(object):
+    oid = OID_EXTENDED_KEY_USAGE
+
     def __init__(self, usages):
         if not all(isinstance(x, ObjectIdentifier) for x in usages):
             raise TypeError(
@@ -352,11 +364,15 @@
         return not self == other
 
 
+@utils.register_interface(ExtensionType)
 class OCSPNoCheck(object):
-    pass
+    oid = OID_OCSP_NO_CHECK
 
 
+@utils.register_interface(ExtensionType)
 class BasicConstraints(object):
+    oid = OID_BASIC_CONSTRAINTS
+
     def __init__(self, ca, path_length):
         if not isinstance(ca, bool):
             raise TypeError("ca must be a boolean value")
@@ -392,7 +408,10 @@
         return not self == other
 
 
+@utils.register_interface(ExtensionType)
 class KeyUsage(object):
+    oid = OID_KEY_USAGE
+
     def __init__(self, digital_signature, content_commitment, key_encipherment,
                  data_encipherment, key_agreement, key_cert_sign, crl_sign,
                  encipher_only, decipher_only):
@@ -475,7 +494,10 @@
         return not self == other
 
 
+@utils.register_interface(ExtensionType)
 class AuthorityInformationAccess(object):
+    oid = OID_AUTHORITY_INFORMATION_ACCESS
+
     def __init__(self, descriptions):
         if not all(isinstance(x, AccessDescription) for x in descriptions):
             raise TypeError(
@@ -539,7 +561,10 @@
     access_location = utils.read_only_property("_access_location")
 
 
+@utils.register_interface(ExtensionType)
 class CertificatePolicies(object):
+    oid = OID_CERTIFICATE_POLICIES
+
     def __init__(self, policies):
         if not all(isinstance(x, PolicyInformation) for x in policies):
             raise TypeError(
@@ -676,7 +701,10 @@
     notice_numbers = utils.read_only_property("_notice_numbers")
 
 
+@utils.register_interface(ExtensionType)
 class SubjectKeyIdentifier(object):
+    oid = OID_SUBJECT_KEY_IDENTIFIER
+
     def __init__(self, digest):
         self._digest = digest
 
@@ -718,7 +746,10 @@
         return not self == other
 
 
+@utils.register_interface(ExtensionType)
 class NameConstraints(object):
+    oid = OID_NAME_CONSTRAINTS
+
     def __init__(self, permitted_subtrees, excluded_subtrees):
         if permitted_subtrees is not None:
             if not all(
@@ -782,7 +813,10 @@
     excluded_subtrees = utils.read_only_property("_excluded_subtrees")
 
 
+@utils.register_interface(ExtensionType)
 class CRLDistributionPoints(object):
+    oid = OID_CRL_DISTRIBUTION_POINTS
+
     def __init__(self, distribution_points):
         if not all(
             isinstance(x, DistributionPoint) for x in distribution_points
@@ -817,7 +851,8 @@
     def __init__(self, full_name, relative_name, reasons, crl_issuer):
         if full_name and relative_name:
             raise ValueError(
-                "At least one of full_name and relative_name must be None"
+                "You cannot provide both full_name and relative_name, at "
+                "least one must be None."
             )
 
         if full_name and not all(
@@ -902,7 +937,10 @@
     remove_from_crl = "removeFromCRL"
 
 
+@utils.register_interface(ExtensionType)
 class InhibitAnyPolicy(object):
+    oid = OID_INHIBIT_ANY_POLICY
+
     def __init__(self, skip_certs):
         if not isinstance(skip_certs, six.integer_types):
             raise TypeError("skip_certs must be an integer")
@@ -1192,7 +1230,10 @@
         return not self == other
 
 
+@utils.register_interface(ExtensionType)
 class SubjectAlternativeName(object):
+    oid = OID_SUBJECT_ALTERNATIVE_NAME
+
     def __init__(self, general_names):
         self._general_names = GeneralNames(general_names)
 
@@ -1218,7 +1259,10 @@
         return not self == other
 
 
+@utils.register_interface(ExtensionType)
 class IssuerAlternativeName(object):
+    oid = OID_ISSUER_ALTERNATIVE_NAME
+
     def __init__(self, general_names):
         self._general_names = GeneralNames(general_names)
 
@@ -1244,7 +1288,10 @@
         return not self == other
 
 
+@utils.register_interface(ExtensionType)
 class AuthorityKeyIdentifier(object):
+    oid = OID_AUTHORITY_KEY_IDENTIFIER
+
     def __init__(self, key_identifier, authority_cert_issuer,
                  authority_cert_serial_number):
         if authority_cert_issuer or authority_cert_serial_number:
@@ -1766,6 +1813,10 @@
             )
         elif isinstance(extension, InhibitAnyPolicy):
             extension = Extension(OID_INHIBIT_ANY_POLICY, critical, extension)
+        elif isinstance(extension, CRLDistributionPoints):
+            extension = Extension(
+                OID_CRL_DISTRIBUTION_POINTS, critical, extension
+            )
         else:
             raise NotImplementedError('Unsupported X.509 extension.')
 
@@ -1784,4 +1835,22 @@
         """
         Signs the certificate using the CA's private key.
         """
-        return backend.sign_x509_certificate(self, private_key, algorithm)
+        if self._subject_name is None:
+            raise ValueError("A certificate must have a subject name")
+
+        if self._issuer_name is None:
+            raise ValueError("A certificate must have an issuer name")
+
+        if self._serial_number is None:
+            raise ValueError("A certificate must have a serial number")
+
+        if self._not_valid_before is None:
+            raise ValueError("A certificate must have a not valid before time")
+
+        if self._not_valid_after is None:
+            raise ValueError("A certificate must have a not valid after time")
+
+        if self._public_key is None:
+            raise ValueError("A certificate must have a public key")
+
+        return backend.create_x509_certificate(self, private_key, algorithm)
diff --git a/tests/hazmat/backends/test_multibackend.py b/tests/hazmat/backends/test_multibackend.py
index d516af1..cc59a8d 100644
--- a/tests/hazmat/backends/test_multibackend.py
+++ b/tests/hazmat/backends/test_multibackend.py
@@ -206,7 +206,7 @@
     def create_x509_csr(self, builder, private_key, algorithm):
         pass
 
-    def sign_x509_certificate(self, builder, private_key, algorithm):
+    def create_x509_certificate(self, builder, private_key, algorithm):
         pass
 
 
@@ -487,7 +487,7 @@
         backend.load_pem_x509_csr(b"reqdata")
         backend.load_der_x509_csr(b"reqdata")
         backend.create_x509_csr(object(), b"privatekey", hashes.SHA1())
-        backend.sign_x509_certificate(object(), b"privatekey", hashes.SHA1())
+        backend.create_x509_certificate(object(), b"privatekey", hashes.SHA1())
 
         backend = MultiBackend([])
         with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_X509):
@@ -501,6 +501,6 @@
         with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_X509):
             backend.create_x509_csr(object(), b"privatekey", hashes.SHA1())
         with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_X509):
-            backend.sign_x509_certificate(
+            backend.create_x509_certificate(
                 object(), b"privatekey", hashes.SHA1()
             )
diff --git a/tests/hazmat/backends/test_openssl.py b/tests/hazmat/backends/test_openssl.py
index 0f2c80a..051827a 100644
--- a/tests/hazmat/backends/test_openssl.py
+++ b/tests/hazmat/backends/test_openssl.py
@@ -512,12 +512,14 @@
         private_key = RSA_KEY_2048.private_key(backend)
 
         with pytest.raises(TypeError):
-            backend.sign_x509_certificate(object(), private_key, DummyHash())
+            backend.create_x509_certificate(object(), private_key, DummyHash())
 
     def test_checks_for_unsupported_extensions(self):
         private_key = RSA_KEY_2048.private_key(backend)
         builder = 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'),
         ])).public_key(
             private_key.public_key()
         ).serial_number(
diff --git a/tests/test_x509.py b/tests/test_x509.py
index e31b57f..4e763e3 100644
--- a/tests/test_x509.py
+++ b/tests/test_x509.py
@@ -828,6 +828,114 @@
 
 
 class TestCertificateBuilder(object):
+    @pytest.mark.requires_backend_interface(interface=RSABackend)
+    @pytest.mark.requires_backend_interface(interface=X509Backend)
+    def test_no_subject_name(self, backend):
+        subject_private_key = RSA_KEY_2048.private_key(backend)
+        builder = x509.CertificateBuilder().serial_number(
+            777
+        ).issuer_name(x509.Name([
+            x509.NameAttribute(x509.OID_COUNTRY_NAME, u'US'),
+        ])).public_key(
+            subject_private_key.public_key()
+        ).not_valid_before(
+            datetime.datetime(2002, 1, 1, 12, 1)
+        ).not_valid_after(
+            datetime.datetime(2030, 12, 31, 8, 30)
+        )
+        with pytest.raises(ValueError):
+            builder.sign(subject_private_key, hashes.SHA256(), backend)
+
+    @pytest.mark.requires_backend_interface(interface=RSABackend)
+    @pytest.mark.requires_backend_interface(interface=X509Backend)
+    def test_no_issuer_name(self, backend):
+        subject_private_key = RSA_KEY_2048.private_key(backend)
+        builder = x509.CertificateBuilder().serial_number(
+            777
+        ).subject_name(x509.Name([
+            x509.NameAttribute(x509.OID_COUNTRY_NAME, u'US'),
+        ])).public_key(
+            subject_private_key.public_key()
+        ).not_valid_before(
+            datetime.datetime(2002, 1, 1, 12, 1)
+        ).not_valid_after(
+            datetime.datetime(2030, 12, 31, 8, 30)
+        )
+        with pytest.raises(ValueError):
+            builder.sign(subject_private_key, hashes.SHA256(), backend)
+
+    @pytest.mark.requires_backend_interface(interface=RSABackend)
+    @pytest.mark.requires_backend_interface(interface=X509Backend)
+    def test_no_public_key(self, backend):
+        subject_private_key = RSA_KEY_2048.private_key(backend)
+        builder = x509.CertificateBuilder().serial_number(
+            777
+        ).issuer_name(x509.Name([
+            x509.NameAttribute(x509.OID_COUNTRY_NAME, u'US'),
+        ])).subject_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)
+        )
+        with pytest.raises(ValueError):
+            builder.sign(subject_private_key, hashes.SHA256(), backend)
+
+    @pytest.mark.requires_backend_interface(interface=RSABackend)
+    @pytest.mark.requires_backend_interface(interface=X509Backend)
+    def test_no_not_valid_before(self, backend):
+        subject_private_key = RSA_KEY_2048.private_key(backend)
+        builder = x509.CertificateBuilder().serial_number(
+            777
+        ).issuer_name(x509.Name([
+            x509.NameAttribute(x509.OID_COUNTRY_NAME, u'US'),
+        ])).subject_name(x509.Name([
+            x509.NameAttribute(x509.OID_COUNTRY_NAME, u'US'),
+        ])).public_key(
+            subject_private_key.public_key()
+        ).not_valid_after(
+            datetime.datetime(2030, 12, 31, 8, 30)
+        )
+        with pytest.raises(ValueError):
+            builder.sign(subject_private_key, hashes.SHA256(), backend)
+
+    @pytest.mark.requires_backend_interface(interface=RSABackend)
+    @pytest.mark.requires_backend_interface(interface=X509Backend)
+    def test_no_not_valid_after(self, backend):
+        subject_private_key = RSA_KEY_2048.private_key(backend)
+        builder = x509.CertificateBuilder().serial_number(
+            777
+        ).issuer_name(x509.Name([
+            x509.NameAttribute(x509.OID_COUNTRY_NAME, u'US'),
+        ])).subject_name(x509.Name([
+            x509.NameAttribute(x509.OID_COUNTRY_NAME, u'US'),
+        ])).public_key(
+            subject_private_key.public_key()
+        ).not_valid_before(
+            datetime.datetime(2002, 1, 1, 12, 1)
+        )
+        with pytest.raises(ValueError):
+            builder.sign(subject_private_key, hashes.SHA256(), backend)
+
+    @pytest.mark.requires_backend_interface(interface=RSABackend)
+    @pytest.mark.requires_backend_interface(interface=X509Backend)
+    def test_no_serial_number(self, backend):
+        subject_private_key = RSA_KEY_2048.private_key(backend)
+        builder = x509.CertificateBuilder().issuer_name(x509.Name([
+            x509.NameAttribute(x509.OID_COUNTRY_NAME, u'US'),
+        ])).subject_name(x509.Name([
+            x509.NameAttribute(x509.OID_COUNTRY_NAME, u'US'),
+        ])).public_key(
+            subject_private_key.public_key()
+        ).not_valid_before(
+            datetime.datetime(2002, 1, 1, 12, 1)
+        ).not_valid_after(
+            datetime.datetime(2030, 12, 31, 8, 30)
+        )
+        with pytest.raises(ValueError):
+            builder.sign(subject_private_key, hashes.SHA256(), backend)
+
     def test_issuer_name_must_be_a_name_type(self):
         builder = x509.CertificateBuilder()
 
@@ -967,6 +1075,19 @@
     def test_sign_with_unsupported_hash(self, backend):
         private_key = RSA_KEY_2048.private_key(backend)
         builder = x509.CertificateBuilder()
+        builder = builder.subject_name(
+            x509.Name([x509.NameAttribute(x509.OID_COUNTRY_NAME, u'US')])
+        ).issuer_name(
+            x509.Name([x509.NameAttribute(x509.OID_COUNTRY_NAME, u'US')])
+        ).serial_number(
+            1
+        ).public_key(
+            private_key.public_key()
+        ).not_valid_before(
+            datetime.datetime(2002, 1, 1, 12, 1)
+        ).not_valid_after(
+            datetime.datetime(2032, 1, 1, 12, 1)
+        )
 
         with pytest.raises(TypeError):
             builder.sign(private_key, object(), backend)
@@ -979,6 +1100,19 @@
 
         private_key = DSA_KEY_2048.private_key(backend)
         builder = x509.CertificateBuilder()
+        builder = builder.subject_name(
+            x509.Name([x509.NameAttribute(x509.OID_COUNTRY_NAME, u'US')])
+        ).issuer_name(
+            x509.Name([x509.NameAttribute(x509.OID_COUNTRY_NAME, u'US')])
+        ).serial_number(
+            1
+        ).public_key(
+            private_key.public_key()
+        ).not_valid_before(
+            datetime.datetime(2002, 1, 1, 12, 1)
+        ).not_valid_after(
+            datetime.datetime(2032, 1, 1, 12, 1)
+        )
 
         with pytest.raises(NotImplementedError):
             builder.sign(private_key, hashes.SHA512(), backend)
@@ -992,10 +1126,171 @@
         _skip_curve_unsupported(backend, ec.SECP256R1())
         private_key = ec.generate_private_key(ec.SECP256R1(), backend)
         builder = x509.CertificateBuilder()
+        builder = builder.subject_name(
+            x509.Name([x509.NameAttribute(x509.OID_COUNTRY_NAME, u'US')])
+        ).issuer_name(
+            x509.Name([x509.NameAttribute(x509.OID_COUNTRY_NAME, u'US')])
+        ).serial_number(
+            1
+        ).public_key(
+            private_key.public_key()
+        ).not_valid_before(
+            datetime.datetime(2002, 1, 1, 12, 1)
+        ).not_valid_after(
+            datetime.datetime(2032, 1, 1, 12, 1)
+        )
 
         with pytest.raises(NotImplementedError):
             builder.sign(private_key, hashes.SHA512(), backend)
 
+    @pytest.mark.parametrize(
+        "cdp",
+        [
+            x509.CRLDistributionPoints([
+                x509.DistributionPoint(
+                    full_name=None,
+                    relative_name=x509.Name([
+                        x509.NameAttribute(
+                            x509.OID_COMMON_NAME,
+                            u"indirect CRL for indirectCRL CA3"
+                        ),
+                    ]),
+                    reasons=None,
+                    crl_issuer=[x509.DirectoryName(
+                        x509.Name([
+                            x509.NameAttribute(x509.OID_COUNTRY_NAME, u"US"),
+                            x509.NameAttribute(
+                                x509.OID_ORGANIZATION_NAME,
+                                u"Test Certificates 2011"
+                            ),
+                            x509.NameAttribute(
+                                x509.OID_ORGANIZATIONAL_UNIT_NAME,
+                                u"indirectCRL CA3 cRLIssuer"
+                            ),
+                        ])
+                    )],
+                )
+            ]),
+            x509.CRLDistributionPoints([
+                x509.DistributionPoint(
+                    full_name=[x509.DirectoryName(
+                        x509.Name([
+                            x509.NameAttribute(x509.OID_COUNTRY_NAME, u"US"),
+                        ])
+                    )],
+                    relative_name=None,
+                    reasons=None,
+                    crl_issuer=[x509.DirectoryName(
+                        x509.Name([
+                            x509.NameAttribute(
+                                x509.OID_ORGANIZATION_NAME,
+                                u"cryptography Testing"
+                            ),
+                        ])
+                    )],
+                )
+            ]),
+            x509.CRLDistributionPoints([
+                x509.DistributionPoint(
+                    full_name=[
+                        x509.UniformResourceIdentifier(
+                            u"http://myhost.com/myca.crl"
+                        ),
+                        x509.UniformResourceIdentifier(
+                            u"http://backup.myhost.com/myca.crl"
+                        )
+                    ],
+                    relative_name=None,
+                    reasons=frozenset([
+                        x509.ReasonFlags.key_compromise,
+                        x509.ReasonFlags.ca_compromise
+                    ]),
+                    crl_issuer=[x509.DirectoryName(
+                        x509.Name([
+                            x509.NameAttribute(x509.OID_COUNTRY_NAME, u"US"),
+                            x509.NameAttribute(
+                                x509.OID_COMMON_NAME, u"cryptography CA"
+                            ),
+                        ])
+                    )],
+                )
+            ]),
+            x509.CRLDistributionPoints([
+                x509.DistributionPoint(
+                    full_name=[x509.UniformResourceIdentifier(
+                        u"http://domain.com/some.crl"
+                    )],
+                    relative_name=None,
+                    reasons=frozenset([
+                        x509.ReasonFlags.key_compromise,
+                        x509.ReasonFlags.ca_compromise,
+                        x509.ReasonFlags.affiliation_changed,
+                        x509.ReasonFlags.superseded,
+                        x509.ReasonFlags.privilege_withdrawn,
+                        x509.ReasonFlags.cessation_of_operation,
+                        x509.ReasonFlags.aa_compromise,
+                        x509.ReasonFlags.certificate_hold,
+                    ]),
+                    crl_issuer=None
+                )
+            ]),
+            x509.CRLDistributionPoints([
+                x509.DistributionPoint(
+                    full_name=None,
+                    relative_name=None,
+                    reasons=None,
+                    crl_issuer=[x509.DirectoryName(
+                        x509.Name([
+                            x509.NameAttribute(
+                                x509.OID_COMMON_NAME, u"cryptography CA"
+                            ),
+                        ])
+                    )],
+                )
+            ]),
+            x509.CRLDistributionPoints([
+                x509.DistributionPoint(
+                    full_name=[x509.UniformResourceIdentifier(
+                        u"http://domain.com/some.crl"
+                    )],
+                    relative_name=None,
+                    reasons=frozenset([x509.ReasonFlags.aa_compromise]),
+                    crl_issuer=None
+                )
+            ])
+        ]
+    )
+    @pytest.mark.requires_backend_interface(interface=RSABackend)
+    @pytest.mark.requires_backend_interface(interface=X509Backend)
+    def test_crl_distribution_points(self, backend, cdp):
+        issuer_private_key = RSA_KEY_2048.private_key(backend)
+        subject_private_key = RSA_KEY_2048.private_key(backend)
+
+        builder = x509.CertificateBuilder().serial_number(
+            4444444
+        ).issuer_name(x509.Name([
+            x509.NameAttribute(x509.OID_LOCALITY_NAME, u'Austin'),
+        ])).subject_name(x509.Name([
+            x509.NameAttribute(x509.OID_LOCALITY_NAME, u'Austin'),
+        ])).public_key(
+            subject_private_key.public_key()
+        ).add_extension(
+            cdp,
+            critical=False,
+        ).not_valid_before(
+            datetime.datetime(2002, 1, 1, 12, 1)
+        ).not_valid_after(
+            datetime.datetime(2030, 12, 31, 8, 30)
+        )
+
+        cert = builder.sign(issuer_private_key, hashes.SHA1(), backend)
+
+        ext = cert.extensions.get_extension_for_oid(
+            x509.OID_CRL_DISTRIBUTION_POINTS
+        )
+        assert ext.critical is False
+        assert ext.value == cdp
+
     @pytest.mark.requires_backend_interface(interface=DSABackend)
     @pytest.mark.requires_backend_interface(interface=X509Backend)
     def test_build_cert_with_dsa_private_key(self, backend):
diff --git a/vectors/cryptography_vectors/x509/rapidssl_sha256_ca_g3.pem b/vectors/cryptography_vectors/x509/rapidssl_sha256_ca_g3.pem
new file mode 100644
index 0000000..f0d7012
--- /dev/null
+++ b/vectors/cryptography_vectors/x509/rapidssl_sha256_ca_g3.pem
@@ -0,0 +1,25 @@
+-----BEGIN CERTIFICATE-----
+MIIEJTCCAw2gAwIBAgIDAjp3MA0GCSqGSIb3DQEBCwUAMEIxCzAJBgNVBAYTAlVT
+MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i
+YWwgQ0EwHhcNMTQwODI5MjEzOTMyWhcNMjIwNTIwMjEzOTMyWjBHMQswCQYDVQQG
+EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEgMB4GA1UEAxMXUmFwaWRTU0wg
+U0hBMjU2IENBIC0gRzMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCv
+VJvZWF0eLFbG1eh/9H0WA//Qi1rkjqfdVC7UBMBdmJyNkA+8EGVf2prWRHzAn7Xp
+SowLBkMEu/SW4ib2YQGRZjEiwzQ0Xz8/kS9EX9zHFLYDn4ZLDqP/oIACg8PTH2lS
+1p1kD8mD5xvEcKyU58Okaiy9uJ5p2L4KjxZjWmhxgHsw3hUEv8zTvz5IBVV6s9cQ
+DAP8m/0Ip4yM26eO8R5j3LMBL3+vV8M8SKeDaCGnL+enP/C1DPz1hNFTvA5yT2AM
+QriYrRmIV9cE7Ie/fodOoyH5U/02mEiN1vi7SPIpyGTRzFRIU4uvt2UevykzKdkp
+YEj4/5G8V1jlNS67abZZAgMBAAGjggEdMIIBGTAfBgNVHSMEGDAWgBTAephojYn7
+qwVkDBF9qn1luMrMTjAdBgNVHQ4EFgQUw5zz/NNGCDS7zkZ/oHxb8+IIy1kwEgYD
+VR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAQYwNQYDVR0fBC4wLDAqoCig
+JoYkaHR0cDovL2cuc3ltY2IuY29tL2NybHMvZ3RnbG9iYWwuY3JsMC4GCCsGAQUF
+BwEBBCIwIDAeBggrBgEFBQcwAYYSaHR0cDovL2cuc3ltY2QuY29tMEwGA1UdIARF
+MEMwQQYKYIZIAYb4RQEHNjAzMDEGCCsGAQUFBwIBFiVodHRwOi8vd3d3Lmdlb3Ry
+dXN0LmNvbS9yZXNvdXJjZXMvY3BzMA0GCSqGSIb3DQEBCwUAA4IBAQCjWB7GQzKs
+rC+TeLfqrlRARy1+eI1Q9vhmrNZPc9ZE768LzFvB9E+aj0l+YK/CJ8cW8fuTgZCp
+fO9vfm5FlBaEvexJ8cQO9K8EWYOHDyw7l8NaEpt7BDV7o5UzCHuTcSJCs6nZb0+B
+kvwHtnm8hEqddwnxxYny8LScVKoSew26T++TGezvfU5ho452nFnPjJSxhJf3GrkH
+uLLGTxN5279PURt/aQ1RKsHWFf83UTRlUfQevjhq7A6rvz17OQV79PP7GqHQyH5O
+ZI3NjGFVkP46yl0lD/gdo0p0Vk8aVUBwdSWmMy66S6VdU5oNMOGNX2Esr8zvsJmh
+gP8L8mJMcCaY
+-----END CERTIFICATE-----