Merge pull request #2180 from reaperhulk/easy-conditional-fix

create a ConditionalLibrary and remove unsupported items
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 85f8447..f07a908 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -24,8 +24,33 @@
   and :class:`~cryptography.hazmat.primitives.kdf.concatkdf.ConcatKDFHMAC`.
 * Raise a ``TypeError`` when passing objects that are not text as the value to
   :class:`~cryptography.x509.NameAttribute`.
+* Add support for :class:`~cryptography.x509.OtherName` as a general name
+  type.
+* Added new X.509 extension support in :class:`~cryptography.x509.Certificate`
+  The following new extensions are now supported:
+
+  * :class:`~cryptography.x509.OCSPNoCheck`
+  * :class:`~cryptography.x509.InhibitAnyPolicy`
+  * :class:`~cryptography.x509.IssuerAlternativeName`
+  * :class:`~cryptography.x509.NameConstraints`
+
+* Extension support was added to
+  :class:`~cryptography.x509.CertificateSigningRequest`.
 * Add support for creating certificate signing requests with
-  :class:`~cryptography.x509.CertificateSigningRequestBuilder`.
+  :class:`~cryptography.x509.CertificateSigningRequestBuilder`. This includes
+  support for the following extensions:
+
+  * :class:`~cryptography.x509.BasicConstraints`
+  * :class:`~cryptography.x509.ExtendedKeyUsage`
+  * :class:`~cryptography.x509.KeyUsage`
+  * :class:`~cryptography.x509.SubjectAlternativeName`
+
+* Add support for creating signed certificates with
+  :class:`~cryptography.x509.CertificateBuilder`. This includes support for
+  the following extensions
+
+  * :class:`~cryptography.x509.BasicConstraints`
+  * :class:`~cryptography.x509.SubjectAlternativeName`
 
 0.9.3 - 2015-07-09
 ~~~~~~~~~~~~~~~~~~
diff --git a/docs/development/test-vectors.rst b/docs/development/test-vectors.rst
index ea44a46..d400e66 100644
--- a/docs/development/test-vectors.rst
+++ b/docs/development/test-vectors.rst
@@ -233,6 +233,8 @@
 * ``cp_user_notice_no_explicit_text.pem`` - An RSA 2048 bit self-signed
   certificate containing a certificate policies extension with a user notice
   with no explicit text.
+* ``cp_invalid.pem`` - An RSA 2048 bit self-signed certificate containing a
+  certificate policies extension with invalid data.
 * ``ian_uri.pem`` - An RSA 2048 bit certificate containing an issuer
   alternative name extension with a ``URI`` general name.
 * ``ocsp_nocheck.pem`` - An RSA 2048 bit self-signed certificate containing
diff --git a/docs/installation.rst b/docs/installation.rst
index becab6b..f7a88b9 100644
--- a/docs/installation.rst
+++ b/docs/installation.rst
@@ -142,7 +142,7 @@
 .. code-block:: console
 
     $ brew install openssl
-    $ env ARCHFLAGS="-arch x86_64" LDFLAGS="-L/usr/local/opt/openssl/lib" CFLAGS="-I/usr/local/opt/openssl/include" pip install cryptography
+    $ env ARCHFLAGS="-arch x86_64" LDFLAGS="-L$(brew --prefix openssl)/lib" CFLAGS="-I$(brew --prefix openssl)/include" pip install cryptography
 
 or `MacPorts`_:
 
diff --git a/docs/x509/reference.rst b/docs/x509/reference.rst
index 9179468..61971fe 100644
--- a/docs/x509/reference.rst
+++ b/docs/x509/reference.rst
@@ -388,6 +388,140 @@
 
         The extensions encoded in the CRL.
 
+X.509 Certificate Builder
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. class:: CertificateBuilder
+
+    .. versionadded:: 1.0
+
+    .. doctest::
+
+        >>> from cryptography import x509
+        >>> from cryptography.hazmat.backends import default_backend
+        >>> from cryptography.hazmat.primitives import hashes
+        >>> from cryptography.hazmat.primitives.asymmetric import rsa
+        >>> import datetime
+        >>> import uuid
+        >>> one_day = datetime.timedelta(1, 0, 0)
+        >>> private_key = rsa.generate_private_key(
+        ...     public_exponent=65537,
+        ...     key_size=2048,
+        ...     backend=default_backend()
+        ... )
+        >>> public_key = rsa.generate_private_key(
+        ...     public_exponent=65537,
+        ...     key_size=2048,
+        ...     backend=default_backend()
+        ... ).public_key()
+        >>> builder = x509.CertificateBuilder()
+        >>> builder = builder.subject_name(x509.Name([
+        ...     x509.NameAttribute(x509.OID_COMMON_NAME, u'cryptography.io'),
+        ... ]))
+        >>> builder = builder.issuer_name(x509.Name([
+        ...     x509.NameAttribute(x509.OID_COMMON_NAME, u'cryptography.io'),
+        ... ]))
+        >>> builder = builder.not_valid_before(datetime.datetime.today() - one_day)
+        >>> builder = builder.not_valid_after(datetime.datetime(2018, 8, 2))
+        >>> builder = builder.serial_number(int(uuid.uuid4()))
+        >>> builder = builder.public_key(public_key)
+        >>> builder = builder.add_extension(
+        ...     x509.BasicConstraints(ca=False, path_length=None), critical=True,
+        ... )
+        >>> certificate = builder.sign(
+        ...     private_key=private_key, algorithm=hashes.SHA256(),
+        ...     backend=default_backend()
+        ... )
+        >>> isinstance(certificate, x509.Certificate)
+        True
+
+    .. method:: issuer_name(name)
+
+        Sets the issuer's distinguished name.
+
+        :param name: The :class:`~cryptography.x509.Name` that describes the
+            issuer (CA).
+
+    .. method:: subject_name(name)
+
+        Sets the subject's distinguished name.
+
+        :param name: The :class:`~cryptography.x509.Name` that describes the
+            subject.
+
+    .. method:: public_key(public_key)
+
+        Sets the subject's public key.
+
+        :param public_key: The subject's public key. This can be one of
+            :class:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPublicKey`,
+            :class:`~cryptography.hazmat.primitives.asymmetric.dsa.DSAPublicKey` or
+            :class:`~cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePublicKey`
+
+    .. method:: serial_number(serial_number)
+
+        Sets the certificate's serial number (an integer).  The CA's policy
+        determines how it attributes serial numbers to certificates.  The only
+        requirement is that this number uniquely identify the certificate given
+        the issuer.
+
+        :param serial_number: Integer number that will be used by the CA to
+            identify this certificate (most notably during certificate
+            revocation checking). Users are encouraged to use a method of
+            generating 20 bytes of entropy, e.g., UUID4. For more information
+            on secure random number generation, see :doc:`/random-numbers`.
+
+    .. method:: not_valid_before(time)
+
+        Sets the certificate's activation time.  This is the time from which
+        clients can start trusting the certificate.  It may be different from
+        the time at which the certificate was created.
+
+        :param time: The :class:`datetime.datetime` object (in UTC) that marks the
+            activation time for the certificate.  The certificate may not be
+            trusted clients if it is used before this time.
+
+    .. method:: not_valid_after(time)
+
+        Sets the certificate's expiration time.  This is the time from which
+        clients should no longer trust the certificate.  The CA's policy will
+        determine how long the certificate should remain in use.
+
+        :param time: The :class:`datetime.datetime` object (in UTC) that marks the
+            expiration time for the certificate.  The certificate may not be
+            trusted clients if it is used after this time.
+
+    .. method:: add_extension(extension, critical)
+
+        Adds an X.509 extension to the certificate.
+
+        :param extension: The extension to add to the certificate. Can be one
+            of :class:`~cryptography.x509.BasicConstraints` or
+            :class:`~cryptography.x509.SubjectAlternativeName`.
+
+        :param critical: Set to ``True`` if the extension must be understood and
+             handled by whoever reads the certificate.
+
+    .. method:: sign(private_key, algorithm, backend)
+
+        Sign the certificate using the CA's private key.
+
+        :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 signature.
+
+        :param backend: Backend that will be used to build the certificate.
+            Must support the
+            :class:`~cryptography.hazmat.backends.interfaces.X509Backend`
+            interface.
+
+
 X.509 CSR (Certificate Signing Request) Object
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
@@ -431,6 +565,25 @@
             >>> isinstance(csr.signature_hash_algorithm, hashes.SHA1)
             True
 
+    .. attribute:: extensions
+
+        :type: :class:`Extensions`
+
+        The extensions encoded in the certificate signing request.
+
+        :raises cryptography.x509.DuplicateExtension: If more than one
+            extension of the same type is found within the certificate signing request.
+
+        :raises cryptography.x509.UnsupportedExtension: If the certificate signing request
+            contains an extension that is not supported.
+
+        :raises cryptography.x509.UnsupportedGeneralNameType: If an extension
+            contains a general name that is not supported.
+
+        :raises UnicodeError: If an extension contains IDNA encoding that is
+            invalid or not compliant with IDNA 2008.
+
+
     .. method:: public_bytes(encoding)
 
         .. versionadded:: 1.0
diff --git a/src/_cffi_src/openssl/x509v3.py b/src/_cffi_src/openssl/x509v3.py
index 89822b8..f6a1890 100644
--- a/src/_cffi_src/openssl/x509v3.py
+++ b/src/_cffi_src/openssl/x509v3.py
@@ -203,6 +203,9 @@
 
 int i2d_EXTENDED_KEY_USAGE(EXTENDED_KEY_USAGE *, unsigned char **);
 
+int i2d_AUTHORITY_INFO_ACCESS(Cryptography_STACK_OF_ACCESS_DESCRIPTION *,
+                              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);
@@ -216,6 +219,9 @@
 int sk_ACCESS_DESCRIPTION_push(Cryptography_STACK_OF_ACCESS_DESCRIPTION *,
                                ACCESS_DESCRIPTION *);
 
+ACCESS_DESCRIPTION *ACCESS_DESCRIPTION_new(void);
+void ACCESS_DESCRIPTION_free(ACCESS_DESCRIPTION *);
+
 X509_EXTENSION *X509V3_EXT_conf_nid(Cryptography_LHASH_OF_CONF_VALUE *,
                                     X509V3_CTX *, int, char *);
 
diff --git a/src/cryptography/hazmat/backends/interfaces.py b/src/cryptography/hazmat/backends/interfaces.py
index 4d378e6..49ccda1 100644
--- a/src/cryptography/hazmat/backends/interfaces.py
+++ b/src/cryptography/hazmat/backends/interfaces.py
@@ -280,6 +280,12 @@
         Create and sign an X.509 CSR from a CSR builder object.
         """
 
+    @abc.abstractmethod
+    def sign_x509_certificate(self, builder, private_key, algorithm):
+        """
+        Sign an X.509 Certificate from a CertificateBuilder object.
+        """
+
 
 @six.add_metaclass(abc.ABCMeta)
 class DHBackend(object):
diff --git a/src/cryptography/hazmat/backends/multibackend.py b/src/cryptography/hazmat/backends/multibackend.py
index 6e911fd..8008989 100644
--- a/src/cryptography/hazmat/backends/multibackend.py
+++ b/src/cryptography/hazmat/backends/multibackend.py
@@ -351,3 +351,12 @@
             "This backend does not support X.509.",
             _Reasons.UNSUPPORTED_X509
         )
+
+    def sign_x509_certificate(self, builder, private_key, algorithm):
+        for b in self._filtered_backends(X509Backend):
+            return b.sign_x509_certificate(builder, private_key, algorithm)
+
+        raise UnsupportedAlgorithm(
+            "This backend does not support X.509.",
+            _Reasons.UNSUPPORTED_X509
+        )
diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py
index db4f963..f9da9ea 100644
--- a/src/cryptography/hazmat/backends/openssl/backend.py
+++ b/src/cryptography/hazmat/backends/openssl/backend.py
@@ -4,6 +4,7 @@
 
 from __future__ import absolute_import, division, print_function
 
+import calendar
 import collections
 import itertools
 from contextlib import contextmanager
@@ -78,6 +79,12 @@
     return i
 
 
+def _encode_asn1_int_gc(backend, x):
+    i = _encode_asn1_int(backend, x)
+    i = backend._ffi.gc(i, backend._lib.ASN1_INTEGER_free)
+    return i
+
+
 def _encode_asn1_str(backend, data, length):
     """
     Create an ASN1_OCTET_STRING from a Python byte string.
@@ -195,6 +202,32 @@
     return pp, r
 
 
+def _encode_authority_information_access(backend, authority_info_access):
+    aia = backend._lib.sk_ACCESS_DESCRIPTION_new_null()
+    assert aia != backend._ffi.NULL
+    aia = backend._ffi.gc(
+        aia, backend._lib.sk_ACCESS_DESCRIPTION_free
+    )
+    for access_description in authority_info_access:
+        ad = backend._lib.ACCESS_DESCRIPTION_new()
+        method = _txt2obj(
+            backend, access_description.access_method.dotted_string
+        )
+        gn = _encode_general_name(backend, access_description.access_location)
+        ad.method = method
+        ad.location = gn
+        res = backend._lib.sk_ACCESS_DESCRIPTION_push(aia, ad)
+        assert res >= 1
+
+    pp = backend._ffi.new('unsigned char **')
+    r = backend._lib.i2d_AUTHORITY_INFO_ACCESS(aia, pp)
+    assert r > 0
+    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
@@ -1063,6 +1096,114 @@
 
         return _CertificateSigningRequest(self, x509_req)
 
+    def sign_x509_certificate(self, builder, private_key, algorithm):
+        if not isinstance(builder, x509.CertificateBuilder):
+            raise TypeError('Builder type mismatch.')
+        if not isinstance(algorithm, hashes.HashAlgorithm):
+            raise TypeError('Algorithm must be a registered hash algorithm.')
+
+        if self._lib.OPENSSL_VERSION_NUMBER <= 0x10001000:
+            if isinstance(private_key, _DSAPrivateKey):
+                raise NotImplementedError(
+                    "Certificate signatures aren't implemented for DSA"
+                    " keys on OpenSSL versions less than 1.0.1."
+                )
+            if isinstance(private_key, _EllipticCurvePrivateKey):
+                raise NotImplementedError(
+                    "Certificate signatures aren't implemented for EC"
+                    " keys on OpenSSL versions less than 1.0.1."
+                )
+
+        # Resolve the signature algorithm.
+        evp_md = self._lib.EVP_get_digestbyname(
+            algorithm.name.encode('ascii')
+        )
+        assert evp_md != self._ffi.NULL
+
+        # Create an empty certificate.
+        x509_cert = self._lib.X509_new()
+        x509_cert = self._ffi.gc(x509_cert, backend._lib.X509_free)
+
+        # Set the x509 version.
+        res = self._lib.X509_set_version(x509_cert, builder._version.value)
+        assert res == 1
+
+        # Set the subject's name.
+        res = self._lib.X509_set_subject_name(
+            x509_cert, _encode_name(self, list(builder._subject_name))
+        )
+        assert res == 1
+
+        # Set the subject's public key.
+        res = self._lib.X509_set_pubkey(
+            x509_cert, builder._public_key._evp_pkey
+        )
+        assert res == 1
+
+        # Set the certificate serial number.
+        serial_number = _encode_asn1_int_gc(self, builder._serial_number)
+        res = self._lib.X509_set_serialNumber(x509_cert, serial_number)
+        assert res == 1
+
+        # Set the "not before" time.
+        res = self._lib.ASN1_TIME_set(
+            self._lib.X509_get_notBefore(x509_cert),
+            calendar.timegm(builder._not_valid_before.timetuple())
+        )
+        assert res != self._ffi.NULL
+
+        # Set the "not after" time.
+        res = self._lib.ASN1_TIME_set(
+            self._lib.X509_get_notAfter(x509_cert),
+            calendar.timegm(builder._not_valid_after.timetuple())
+        )
+        assert res != self._ffi.NULL
+
+        # Add extensions.
+        for i, extension in enumerate(builder._extensions):
+            if isinstance(extension.value, x509.BasicConstraints):
+                pp, r = _encode_basic_constraints(self, extension.value)
+            elif isinstance(extension.value, x509.KeyUsage):
+                pp, r = _encode_key_usage(self, extension.value)
+            elif isinstance(extension.value, x509.ExtendedKeyUsage):
+                pp, r = _encode_extended_key_usage(self, extension.value)
+            elif isinstance(extension.value, x509.SubjectAlternativeName):
+                pp, r = _encode_subject_alt_name(self, extension.value)
+            elif isinstance(extension.value, x509.AuthorityInformationAccess):
+                pp, r = _encode_authority_information_access(
+                    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,
+                1 if extension.critical else 0,
+                _encode_asn1_str_gc(self, pp[0], r)
+            )
+            res = self._lib.X509_add_ext(x509_cert, extension, i)
+            assert res == 1
+
+        # Set the issuer name.
+        res = self._lib.X509_set_issuer_name(
+            x509_cert, _encode_name(self, list(builder._issuer_name))
+        )
+        assert res == 1
+
+        # Sign the certificate with the issuer's private key.
+        res = self._lib.X509_sign(
+            x509_cert, private_key._evp_pkey, evp_md
+        )
+        if res == 0:
+            errors = self._consume_errors()
+            assert errors[0][1] == self._lib.ERR_LIB_RSA
+            assert errors[0][3] == self._lib.RSA_R_DIGEST_TOO_BIG_FOR_RSA_KEY
+            raise ValueError("Digest too big for RSA key")
+
+        return _Certificate(self, x509_cert)
+
     def load_pem_private_key(self, data, password):
         return self._load_key(
             self._lib.PEM_read_bio_PrivateKey,
@@ -1577,13 +1718,15 @@
             if format is serialization.PrivateFormat.PKCS8:
                 write_bio = self._lib.PEM_write_bio_PKCS8PrivateKey
                 key = evp_pkey
-            elif format is serialization.PrivateFormat.TraditionalOpenSSL:
+            else:
+                assert format is serialization.PrivateFormat.TraditionalOpenSSL
                 if evp_pkey.type == self._lib.EVP_PKEY_RSA:
                     write_bio = self._lib.PEM_write_bio_RSAPrivateKey
                 elif evp_pkey.type == self._lib.EVP_PKEY_DSA:
                     write_bio = self._lib.PEM_write_bio_DSAPrivateKey
-                elif (self._lib.Cryptography_HAS_EC == 1 and
-                      evp_pkey.type == self._lib.EVP_PKEY_EC):
+                else:
+                    assert self._lib.Cryptography_HAS_EC == 1
+                    assert evp_pkey.type == self._lib.EVP_PKEY_EC
                     write_bio = self._lib.PEM_write_bio_ECPrivateKey
 
                 key = cdata
@@ -1600,7 +1743,8 @@
                 return self._private_key_bytes_traditional_der(
                     evp_pkey.type, cdata
                 )
-            elif format is serialization.PrivateFormat.PKCS8:
+            else:
+                assert format is serialization.PrivateFormat.PKCS8
                 write_bio = self._lib.i2d_PKCS8PrivateKey_bio
                 key = evp_pkey
         else:
@@ -1625,7 +1769,8 @@
         elif (self._lib.Cryptography_HAS_EC == 1 and
               key_type == self._lib.EVP_PKEY_EC):
             write_bio = self._lib.i2d_ECPrivateKey_bio
-        elif key_type == self._lib.EVP_PKEY_DSA:
+        else:
+            assert key_type == self._lib.EVP_PKEY_DSA
             write_bio = self._lib.i2d_DSAPrivateKey_bio
 
         bio = self._create_mem_bio()
@@ -1640,7 +1785,8 @@
         if format is serialization.PublicFormat.SubjectPublicKeyInfo:
             if encoding is serialization.Encoding.PEM:
                 write_bio = self._lib.PEM_write_bio_PUBKEY
-            elif encoding is serialization.Encoding.DER:
+            else:
+                assert encoding is serialization.Encoding.DER
                 write_bio = self._lib.i2d_PUBKEY_bio
 
             key = evp_pkey
@@ -1649,7 +1795,8 @@
             assert evp_pkey.type == self._lib.EVP_PKEY_RSA
             if encoding is serialization.Encoding.PEM:
                 write_bio = self._lib.PEM_write_bio_RSAPublicKey
-            elif encoding is serialization.Encoding.DER:
+            else:
+                assert encoding is serialization.Encoding.DER
                 write_bio = self._lib.i2d_RSAPublicKey_bio
 
             key = cdata
diff --git a/src/cryptography/hazmat/backends/openssl/rsa.py b/src/cryptography/hazmat/backends/openssl/rsa.py
index 21414c0..822c730 100644
--- a/src/cryptography/hazmat/backends/openssl/rsa.py
+++ b/src/cryptography/hazmat/backends/openssl/rsa.py
@@ -268,8 +268,9 @@
                     self._backend._lib.RSA_R_DATA_TOO_LARGE_FOR_KEY_SIZE):
                 reason = ("Salt length too long for key size. Try using "
                           "MAX_LENGTH instead.")
-            elif (errors[0].reason ==
-                    self._backend._lib.RSA_R_DIGEST_TOO_BIG_FOR_RSA_KEY):
+            else:
+                assert (errors[0].reason ==
+                        self._backend._lib.RSA_R_DIGEST_TOO_BIG_FOR_RSA_KEY)
                 reason = "Digest too large for key size. Use a larger key."
             assert reason is not None
             raise ValueError(reason)
diff --git a/src/cryptography/hazmat/backends/openssl/x509.py b/src/cryptography/hazmat/backends/openssl/x509.py
index 493abc8..ee9a3bb 100644
--- a/src/cryptography/hazmat/backends/openssl/x509.py
+++ b/src/cryptography/hazmat/backends/openssl/x509.py
@@ -234,7 +234,15 @@
                         "{0} is not currently supported".format(oid), oid
                     )
             else:
-                value = handler(backend, ext)
+                d2i = backend._lib.X509V3_EXT_d2i(ext)
+                if d2i == backend._ffi.NULL:
+                    backend._consume_errors()
+                    raise ValueError(
+                        "The {0} extension is invalid and can't be "
+                        "parsed".format(oid)
+                    )
+
+                value = handler(backend, d2i)
                 extensions.append(x509.Extension(oid, critical, value))
 
             seen_oids.add(oid)
@@ -358,12 +366,8 @@
         return self._backend._read_mem_bio(bio)
 
 
-def _decode_certificate_policies(backend, ext):
-    cp = backend._ffi.cast(
-        "Cryptography_STACK_OF_POLICYINFO *",
-        backend._lib.X509V3_EXT_d2i(ext)
-    )
-    assert cp != backend._ffi.NULL
+def _decode_certificate_policies(backend, cp):
+    cp = backend._ffi.cast("Cryptography_STACK_OF_POLICYINFO *", cp)
     cp = backend._ffi.gc(cp, backend._lib.sk_POLICYINFO_free)
     num = backend._lib.sk_POLICYINFO_num(cp)
     certificate_policies = []
@@ -386,7 +390,8 @@
                         pqi.d.cpsuri.data, pqi.d.cpsuri.length
                     )[:].decode('ascii')
                     qualifiers.append(cpsuri)
-                elif pqualid == x509.OID_CPS_USER_NOTICE:
+                else:
+                    assert pqualid == x509.OID_CPS_USER_NOTICE
                     user_notice = _decode_user_notice(
                         backend, pqi.d.usernotice
                     )
@@ -431,12 +436,8 @@
     return x509.UserNotice(notice_reference, explicit_text)
 
 
-def _decode_basic_constraints(backend, ext):
-    bc_st = backend._lib.X509V3_EXT_d2i(ext)
-    assert bc_st != backend._ffi.NULL
-    basic_constraints = backend._ffi.cast(
-        "BASIC_CONSTRAINTS *", bc_st
-    )
+def _decode_basic_constraints(backend, bc_st):
+    basic_constraints = backend._ffi.cast("BASIC_CONSTRAINTS *", bc_st)
     basic_constraints = backend._ffi.gc(
         basic_constraints, backend._lib.BASIC_CONSTRAINTS_free
     )
@@ -447,19 +448,13 @@
     if basic_constraints.pathlen == backend._ffi.NULL:
         path_length = None
     else:
-        path_length = _asn1_integer_to_int(
-            backend, basic_constraints.pathlen
-        )
+        path_length = _asn1_integer_to_int(backend, basic_constraints.pathlen)
 
     return x509.BasicConstraints(ca, path_length)
 
 
-def _decode_subject_key_identifier(backend, ext):
-    asn1_string = backend._lib.X509V3_EXT_d2i(ext)
-    assert asn1_string != backend._ffi.NULL
-    asn1_string = backend._ffi.cast(
-        "ASN1_OCTET_STRING *", asn1_string
-    )
+def _decode_subject_key_identifier(backend, asn1_string):
+    asn1_string = backend._ffi.cast("ASN1_OCTET_STRING *", asn1_string)
     asn1_string = backend._ffi.gc(
         asn1_string, backend._lib.ASN1_OCTET_STRING_free
     )
@@ -468,13 +463,9 @@
     )
 
 
-def _decode_authority_key_identifier(backend, ext):
-    akid = backend._lib.X509V3_EXT_d2i(ext)
-    assert akid != backend._ffi.NULL
+def _decode_authority_key_identifier(backend, akid):
     akid = backend._ffi.cast("AUTHORITY_KEYID *", akid)
-    akid = backend._ffi.gc(
-        akid, backend._lib.AUTHORITY_KEYID_free
-    )
+    akid = backend._ffi.gc(akid, backend._lib.AUTHORITY_KEYID_free)
     key_identifier = None
     authority_cert_issuer = None
     authority_cert_serial_number = None
@@ -499,15 +490,9 @@
     )
 
 
-def _decode_authority_information_access(backend, ext):
-    aia = backend._lib.X509V3_EXT_d2i(ext)
-    assert aia != backend._ffi.NULL
-    aia = backend._ffi.cast(
-        "Cryptography_STACK_OF_ACCESS_DESCRIPTION *", aia
-    )
-    aia = backend._ffi.gc(
-        aia, backend._lib.sk_ACCESS_DESCRIPTION_free
-    )
+def _decode_authority_information_access(backend, aia):
+    aia = backend._ffi.cast("Cryptography_STACK_OF_ACCESS_DESCRIPTION *", aia)
+    aia = backend._ffi.gc(aia, backend._lib.sk_ACCESS_DESCRIPTION_free)
     num = backend._lib.sk_ACCESS_DESCRIPTION_num(aia)
     access_descriptions = []
     for i in range(num):
@@ -521,13 +506,9 @@
     return x509.AuthorityInformationAccess(access_descriptions)
 
 
-def _decode_key_usage(backend, ext):
-    bit_string = backend._lib.X509V3_EXT_d2i(ext)
-    assert bit_string != backend._ffi.NULL
+def _decode_key_usage(backend, bit_string):
     bit_string = backend._ffi.cast("ASN1_BIT_STRING *", bit_string)
-    bit_string = backend._ffi.gc(
-        bit_string, backend._lib.ASN1_BIT_STRING_free
-    )
+    bit_string = backend._ffi.gc(bit_string, backend._lib.ASN1_BIT_STRING_free)
     get_bit = backend._lib.ASN1_BIT_STRING_get_bit
     digital_signature = get_bit(bit_string, 0) == 1
     content_commitment = get_bit(bit_string, 1) == 1
@@ -551,11 +532,8 @@
     )
 
 
-def _decode_general_names_extension(backend, ext):
-    gns = backend._ffi.cast(
-        "GENERAL_NAMES *", backend._lib.X509V3_EXT_d2i(ext)
-    )
-    assert gns != backend._ffi.NULL
+def _decode_general_names_extension(backend, gns):
+    gns = backend._ffi.cast("GENERAL_NAMES *", gns)
     gns = backend._ffi.gc(gns, backend._lib.GENERAL_NAMES_free)
     general_names = _decode_general_names(backend, gns)
     return general_names
@@ -573,11 +551,8 @@
     )
 
 
-def _decode_name_constraints(backend, ext):
-    nc = backend._ffi.cast(
-        "NAME_CONSTRAINTS *", backend._lib.X509V3_EXT_d2i(ext)
-    )
-    assert nc != backend._ffi.NULL
+def _decode_name_constraints(backend, nc):
+    nc = backend._ffi.cast("NAME_CONSTRAINTS *", nc)
     nc = backend._ffi.gc(nc, backend._lib.NAME_CONSTRAINTS_free)
     permitted = _decode_general_subtrees(backend, nc.permittedSubtrees)
     excluded = _decode_general_subtrees(backend, nc.excludedSubtrees)
@@ -602,12 +577,8 @@
     return subtrees
 
 
-def _decode_extended_key_usage(backend, ext):
-    sk = backend._ffi.cast(
-        "Cryptography_STACK_OF_ASN1_OBJECT *",
-        backend._lib.X509V3_EXT_d2i(ext)
-    )
-    assert sk != backend._ffi.NULL
+def _decode_extended_key_usage(backend, sk):
+    sk = backend._ffi.cast("Cryptography_STACK_OF_ASN1_OBJECT *", sk)
     sk = backend._ffi.gc(sk, backend._lib.sk_ASN1_OBJECT_free)
     num = backend._lib.sk_ASN1_OBJECT_num(sk)
     ekus = []
@@ -621,14 +592,9 @@
     return x509.ExtendedKeyUsage(ekus)
 
 
-def _decode_crl_distribution_points(backend, ext):
-    cdps = backend._ffi.cast(
-        "Cryptography_STACK_OF_DIST_POINT *",
-        backend._lib.X509V3_EXT_d2i(ext)
-    )
-    assert cdps != backend._ffi.NULL
-    cdps = backend._ffi.gc(
-        cdps, backend._lib.sk_DIST_POINT_free)
+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)
     num = backend._lib.sk_DIST_POINT_num(cdps)
 
     dist_points = []
@@ -716,12 +682,8 @@
     return x509.CRLDistributionPoints(dist_points)
 
 
-def _decode_inhibit_any_policy(backend, ext):
-    asn1_int = backend._ffi.cast(
-        "ASN1_INTEGER *",
-        backend._lib.X509V3_EXT_d2i(ext)
-    )
-    assert asn1_int != backend._ffi.NULL
+def _decode_inhibit_any_policy(backend, asn1_int):
+    asn1_int = backend._ffi.cast("ASN1_INTEGER *", asn1_int)
     asn1_int = backend._ffi.gc(asn1_int, backend._lib.ASN1_INTEGER_free)
     skip_certs = _asn1_integer_to_int(backend, asn1_int)
     return x509.InhibitAnyPolicy(skip_certs)
@@ -789,35 +751,33 @@
         return self._backend._read_mem_bio(bio)
 
 
+_EXTENSION_HANDLERS = {
+    x509.OID_BASIC_CONSTRAINTS: _decode_basic_constraints,
+    x509.OID_SUBJECT_KEY_IDENTIFIER: _decode_subject_key_identifier,
+    x509.OID_KEY_USAGE: _decode_key_usage,
+    x509.OID_SUBJECT_ALTERNATIVE_NAME: _decode_subject_alt_name,
+    x509.OID_EXTENDED_KEY_USAGE: _decode_extended_key_usage,
+    x509.OID_AUTHORITY_KEY_IDENTIFIER: _decode_authority_key_identifier,
+    x509.OID_AUTHORITY_INFORMATION_ACCESS: (
+        _decode_authority_information_access
+    ),
+    x509.OID_CERTIFICATE_POLICIES: _decode_certificate_policies,
+    x509.OID_CRL_DISTRIBUTION_POINTS: _decode_crl_distribution_points,
+    x509.OID_OCSP_NO_CHECK: _decode_ocsp_no_check,
+    x509.OID_INHIBIT_ANY_POLICY: _decode_inhibit_any_policy,
+    x509.OID_ISSUER_ALTERNATIVE_NAME: _decode_issuer_alt_name,
+    x509.OID_NAME_CONSTRAINTS: _decode_name_constraints,
+}
+
+
 _CERTIFICATE_EXTENSION_PARSER = _X509ExtensionParser(
     ext_count=lambda backend, x: backend._lib.X509_get_ext_count(x),
     get_ext=lambda backend, x, i: backend._lib.X509_get_ext(x, i),
-    handlers={
-        x509.OID_BASIC_CONSTRAINTS: _decode_basic_constraints,
-        x509.OID_SUBJECT_KEY_IDENTIFIER: _decode_subject_key_identifier,
-        x509.OID_KEY_USAGE: _decode_key_usage,
-        x509.OID_SUBJECT_ALTERNATIVE_NAME: _decode_subject_alt_name,
-        x509.OID_EXTENDED_KEY_USAGE: _decode_extended_key_usage,
-        x509.OID_AUTHORITY_KEY_IDENTIFIER: _decode_authority_key_identifier,
-        x509.OID_AUTHORITY_INFORMATION_ACCESS: (
-            _decode_authority_information_access
-        ),
-        x509.OID_CERTIFICATE_POLICIES: _decode_certificate_policies,
-        x509.OID_CRL_DISTRIBUTION_POINTS: _decode_crl_distribution_points,
-        x509.OID_OCSP_NO_CHECK: _decode_ocsp_no_check,
-        x509.OID_INHIBIT_ANY_POLICY: _decode_inhibit_any_policy,
-        x509.OID_ISSUER_ALTERNATIVE_NAME: _decode_issuer_alt_name,
-        x509.OID_NAME_CONSTRAINTS: _decode_name_constraints,
-    }
+    handlers=_EXTENSION_HANDLERS
 )
 
 _CSR_EXTENSION_PARSER = _X509ExtensionParser(
     ext_count=lambda backend, x: backend._lib.sk_X509_EXTENSION_num(x),
     get_ext=lambda backend, x, i: backend._lib.sk_X509_EXTENSION_value(x, i),
-    handlers={
-        x509.OID_BASIC_CONSTRAINTS: _decode_basic_constraints,
-        x509.OID_KEY_USAGE: _decode_key_usage,
-        x509.OID_SUBJECT_ALTERNATIVE_NAME: _decode_subject_alt_name,
-        x509.OID_EXTENDED_KEY_USAGE: _decode_extended_key_usage,
-    }
+    handlers=_EXTENSION_HANDLERS
 )
diff --git a/src/cryptography/hazmat/primitives/serialization.py b/src/cryptography/hazmat/primitives/serialization.py
index 098b31d..fc50456 100644
--- a/src/cryptography/hazmat/primitives/serialization.py
+++ b/src/cryptography/hazmat/primitives/serialization.py
@@ -33,9 +33,9 @@
 
 
 def load_ssh_public_key(data, backend):
-    key_parts = data.split(b' ')
+    key_parts = data.split(b' ', 2)
 
-    if len(key_parts) != 2 and len(key_parts) != 3:
+    if len(key_parts) < 2:
         raise ValueError(
             'Key is not in the proper format or contains extra data.')
 
diff --git a/src/cryptography/x509.py b/src/cryptography/x509.py
index 75552fc..978eb56 100644
--- a/src/cryptography/x509.py
+++ b/src/cryptography/x509.py
@@ -5,6 +5,7 @@
 from __future__ import absolute_import, division, print_function
 
 import abc
+import datetime
 import ipaddress
 from email.utils import parseaddr
 from enum import Enum
@@ -17,6 +18,7 @@
 
 from cryptography import utils
 from cryptography.hazmat.primitives import hashes
+from cryptography.hazmat.primitives.asymmetric import dsa, ec, rsa
 
 
 _OID_NAMES = {
@@ -95,6 +97,8 @@
     8: "registeredID",
 }
 
+_UNIX_EPOCH = datetime.datetime(1970, 1, 1)
+
 
 class Version(Enum):
     v1 = 0
@@ -1598,3 +1602,154 @@
         if self._subject_name is None:
             raise ValueError("A CertificateSigningRequest must have a subject")
         return backend.create_x509_csr(self, private_key, algorithm)
+
+
+class CertificateBuilder(object):
+    def __init__(self, issuer_name=None, subject_name=None,
+                 public_key=None, serial_number=None, not_valid_before=None,
+                 not_valid_after=None, extensions=[]):
+        self._version = Version.v3
+        self._issuer_name = issuer_name
+        self._subject_name = subject_name
+        self._public_key = public_key
+        self._serial_number = serial_number
+        self._not_valid_before = not_valid_before
+        self._not_valid_after = not_valid_after
+        self._extensions = extensions
+
+    def issuer_name(self, name):
+        """
+        Sets the CA's distinguished name.
+        """
+        if not isinstance(name, Name):
+            raise TypeError('Expecting x509.Name object.')
+        if self._issuer_name is not None:
+            raise ValueError('The issuer name may only be set once.')
+        return CertificateBuilder(
+            name, self._subject_name, self._public_key,
+            self._serial_number, self._not_valid_before,
+            self._not_valid_after, self._extensions
+        )
+
+    def subject_name(self, name):
+        """
+        Sets the requestor's distinguished name.
+        """
+        if not isinstance(name, Name):
+            raise TypeError('Expecting x509.Name object.')
+        if self._subject_name is not None:
+            raise ValueError('The subject name may only be set once.')
+        return CertificateBuilder(
+            self._issuer_name, name, self._public_key,
+            self._serial_number, self._not_valid_before,
+            self._not_valid_after, self._extensions
+        )
+
+    def public_key(self, key):
+        """
+        Sets the requestor's public key (as found in the signing request).
+        """
+        if not isinstance(key, (dsa.DSAPublicKey, rsa.RSAPublicKey,
+                                ec.EllipticCurvePublicKey)):
+            raise TypeError('Expecting one of DSAPublicKey, RSAPublicKey,'
+                            ' or EllipticCurvePublicKey.')
+        if self._public_key is not None:
+            raise ValueError('The public key may only be set once.')
+        return CertificateBuilder(
+            self._issuer_name, self._subject_name, key,
+            self._serial_number, self._not_valid_before,
+            self._not_valid_after, self._extensions
+        )
+
+    def serial_number(self, number):
+        """
+        Sets the certificate serial number.
+        """
+        if not isinstance(number, six.integer_types):
+            raise TypeError('Serial number must be of integral type.')
+        if self._serial_number is not None:
+            raise ValueError('The serial number may only be set once.')
+        if number < 0:
+            raise ValueError('The serial number should be non-negative.')
+        if utils.bit_length(number) > 160:  # As defined in RFC 5280
+            raise ValueError('The serial number should not be more than 160 '
+                             'bits.')
+        return CertificateBuilder(
+            self._issuer_name, self._subject_name,
+            self._public_key, number, self._not_valid_before,
+            self._not_valid_after, self._extensions
+        )
+
+    def not_valid_before(self, time):
+        """
+        Sets the certificate activation time.
+        """
+        if not isinstance(time, datetime.datetime):
+            raise TypeError('Expecting datetime object.')
+        if self._not_valid_before is not None:
+            raise ValueError('The not valid before may only be set once.')
+        if time <= _UNIX_EPOCH:
+            raise ValueError('The not valid before date must be after the unix'
+                             ' epoch (1970 January 1).')
+        return CertificateBuilder(
+            self._issuer_name, self._subject_name,
+            self._public_key, self._serial_number, time,
+            self._not_valid_after, self._extensions
+        )
+
+    def not_valid_after(self, time):
+        """
+        Sets the certificate expiration time.
+        """
+        if not isinstance(time, datetime.datetime):
+            raise TypeError('Expecting datetime object.')
+        if self._not_valid_after is not None:
+            raise ValueError('The not valid after may only be set once.')
+        if time <= _UNIX_EPOCH:
+            raise ValueError('The not valid after date must be after the unix'
+                             ' epoch (1970 January 1).')
+        return CertificateBuilder(
+            self._issuer_name, self._subject_name,
+            self._public_key, self._serial_number, self._not_valid_before,
+            time, self._extensions
+        )
+
+    def add_extension(self, extension, critical):
+        """
+        Adds an X.509 extension to the certificate.
+        """
+        if isinstance(extension, BasicConstraints):
+            extension = Extension(OID_BASIC_CONSTRAINTS, critical, extension)
+        elif isinstance(extension, KeyUsage):
+            extension = Extension(OID_KEY_USAGE, critical, extension)
+        elif isinstance(extension, ExtendedKeyUsage):
+            extension = Extension(OID_EXTENDED_KEY_USAGE, critical, extension)
+        elif isinstance(extension, SubjectAlternativeName):
+            extension = Extension(
+                OID_SUBJECT_ALTERNATIVE_NAME, critical, extension
+            )
+        elif isinstance(extension, AuthorityInformationAccess):
+            extension = Extension(
+                OID_AUTHORITY_INFORMATION_ACCESS, critical, extension
+            )
+        elif isinstance(extension, InhibitAnyPolicy):
+            extension = Extension(OID_INHIBIT_ANY_POLICY, critical, extension)
+        else:
+            raise NotImplementedError('Unsupported X.509 extension.')
+
+        # TODO: This is quadratic in the number of extensions
+        for e in self._extensions:
+            if e.oid == extension.oid:
+                raise ValueError('This extension has already been set.')
+
+        return CertificateBuilder(
+            self._issuer_name, self._subject_name,
+            self._public_key, self._serial_number, self._not_valid_before,
+            self._not_valid_after, self._extensions + [extension]
+        )
+
+    def sign(self, private_key, algorithm, backend):
+        """
+        Signs the certificate using the CA's private key.
+        """
+        return backend.sign_x509_certificate(self, private_key, algorithm)
diff --git a/tests/hazmat/backends/test_multibackend.py b/tests/hazmat/backends/test_multibackend.py
index 3c05cdf..d516af1 100644
--- a/tests/hazmat/backends/test_multibackend.py
+++ b/tests/hazmat/backends/test_multibackend.py
@@ -206,6 +206,9 @@
     def create_x509_csr(self, builder, private_key, algorithm):
         pass
 
+    def sign_x509_certificate(self, builder, private_key, algorithm):
+        pass
+
 
 class TestMultiBackend(object):
     def test_ciphers(self):
@@ -484,6 +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 = MultiBackend([])
         with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_X509):
@@ -496,3 +500,7 @@
             backend.load_der_x509_csr(b"reqdata")
         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(
+                object(), b"privatekey", hashes.SHA1()
+            )
diff --git a/tests/hazmat/backends/test_openssl.py b/tests/hazmat/backends/test_openssl.py
index 6a2e8a7..0f2c80a 100644
--- a/tests/hazmat/backends/test_openssl.py
+++ b/tests/hazmat/backends/test_openssl.py
@@ -4,6 +4,7 @@
 
 from __future__ import absolute_import, division, print_function
 
+import datetime
 import os
 import subprocess
 import sys
@@ -14,6 +15,7 @@
 import pytest
 
 from cryptography import utils
+from cryptography import x509
 from cryptography.exceptions import InternalError, _Reasons
 from cryptography.hazmat.backends.interfaces import RSABackend
 from cryptography.hazmat.backends.openssl.backend import (
@@ -34,6 +36,20 @@
 from ...utils import load_vectors_from_file, raises_unsupported_algorithm
 
 
+def skip_if_libre_ssl(openssl_version):
+    if u'LibreSSL' in openssl_version:
+        pytest.skip("LibreSSL hard-codes RAND_bytes to use arc4random.")
+
+
+class TestLibreSkip(object):
+    def test_skip_no(self):
+        assert skip_if_libre_ssl(u"OpenSSL 0.9.8zf 19 Mar 2015") is None
+
+    def test_skip_yes(self):
+        with pytest.raises(pytest.skip.Exception):
+            skip_if_libre_ssl(u"LibreSSL 2.1.6")
+
+
 @utils.register_interface(Mode)
 class DummyMode(object):
     name = "dummy-mode"
@@ -216,6 +232,19 @@
         bn = backend._int_to_bn(0)
         assert backend._bn_to_int(bn) == 0
 
+    def test_actual_osrandom_bytes(self, monkeypatch):
+        skip_if_libre_ssl(backend.openssl_version_text())
+        sample_data = (b"\x01\x02\x03\x04" * 4)
+        length = len(sample_data)
+
+        def notrandom(size):
+            assert size == length
+            return sample_data
+        monkeypatch.setattr(os, "urandom", notrandom)
+        buf = backend._ffi.new("char[]", length)
+        backend._lib.RAND_bytes(buf, length)
+        assert backend._ffi.buffer(buf)[0:length] == sample_data
+
 
 class TestOpenSSLRandomEngine(object):
     def teardown_method(self, method):
@@ -478,6 +507,33 @@
             backend.create_x509_csr(object(), private_key, hashes.SHA1())
 
 
+class TestOpenSSLSignX509Certificate(object):
+    def test_requires_certificate_builder(self):
+        private_key = RSA_KEY_2048.private_key(backend)
+
+        with pytest.raises(TypeError):
+            backend.sign_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'),
+        ])).public_key(
+            private_key.public_key()
+        ).serial_number(
+            777
+        ).not_valid_before(
+            datetime.datetime(1999, 1, 1)
+        ).not_valid_after(
+            datetime.datetime(2020, 1, 1)
+        ).add_extension(
+            x509.InhibitAnyPolicy(0), False
+        )
+
+        with pytest.raises(NotImplementedError):
+            builder.sign(private_key, hashes.SHA1(), backend)
+
+
 class TestOpenSSLSerialisationWithOpenSSL(object):
     def test_pem_password_cb_buffer_too_small(self):
         ffi_cb, cb = backend._pem_password_cb(b"aa")
diff --git a/tests/hazmat/bindings/test_openssl.py b/tests/hazmat/bindings/test_openssl.py
index c5f0a7d..20171fa 100644
--- a/tests/hazmat/bindings/test_openssl.py
+++ b/tests/hazmat/bindings/test_openssl.py
@@ -4,27 +4,11 @@
 
 from __future__ import absolute_import, division, print_function
 
-import os
-
 import pytest
 
 from cryptography.hazmat.bindings.openssl.binding import Binding
 
 
-def skip_if_libre_ssl(openssl_version):
-    if b'LibreSSL' in openssl_version:
-        pytest.skip("LibreSSL hard-codes RAND_bytes to use arc4random.")
-
-
-class TestLibreSkip(object):
-    def test_skip_no(self):
-        assert skip_if_libre_ssl(b"OpenSSL 0.9.8zf 19 Mar 2015") is None
-
-    def test_skip_yes(self):
-        with pytest.raises(pytest.skip.Exception):
-            skip_if_libre_ssl(b"LibreSSL 2.1.6")
-
-
 class TestOpenSSL(object):
     def test_binding_loads(self):
         binding = Binding()
@@ -108,20 +92,6 @@
         with pytest.raises(RuntimeError):
             b._register_osrandom_engine()
 
-    def test_actual_osrandom_bytes(self, monkeypatch):
-        b = Binding()
-        skip_if_libre_ssl(b.ffi.string(b.lib.OPENSSL_VERSION_TEXT))
-        sample_data = (b"\x01\x02\x03\x04" * 4)
-        length = len(sample_data)
-
-        def notrandom(size):
-            assert size == length
-            return sample_data
-        monkeypatch.setattr(os, "urandom", notrandom)
-        buf = b.ffi.new("char[]", length)
-        b.lib.RAND_bytes(buf, length)
-        assert b.ffi.buffer(buf)[0:length] == sample_data
-
     def test_ssl_ctx_options(self):
         # Test that we're properly handling 32-bit unsigned on all platforms.
         b = Binding()
diff --git a/tests/hazmat/primitives/test_serialization.py b/tests/hazmat/primitives/test_serialization.py
index af60583..f82e735 100644
--- a/tests/hazmat/primitives/test_serialization.py
+++ b/tests/hazmat/primitives/test_serialization.py
@@ -854,7 +854,7 @@
         with pytest.raises(ValueError):
             load_ssh_public_key(ssh_key, backend)
 
-    def test_load_ssh_public_key_rsa_extra_string_after_comment(self, backend):
+    def test_load_ssh_public_key_rsa_comment_with_spaces(self, backend):
         ssh_key = (
             b"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDDu/XRP1kyK6Cgt36gts9XAk"
             b"FiiuJLW6RU0j3KKVZSs1I7Z3UmU9/9aVh/rZV43WQG8jaR6kkcP4stOR0DEtll"
@@ -866,8 +866,7 @@
             b"2MzHvnbv testkey@localhost extra"
         )
 
-        with pytest.raises(ValueError):
-            load_ssh_public_key(ssh_key, backend)
+        load_ssh_public_key(ssh_key, backend)
 
     def test_load_ssh_public_key_rsa_extra_data_after_modulo(self, backend):
         ssh_key = (
@@ -943,7 +942,7 @@
         with pytest.raises(ValueError):
             load_ssh_public_key(ssh_key, backend)
 
-    def test_load_ssh_public_key_dss_extra_string_after_comment(self, backend):
+    def test_load_ssh_public_key_dss_comment_with_spaces(self, backend):
         ssh_key = (
             b"ssh-dss AAAAB3NzaC1kc3MAAACBALmwUtfwdjAUjU2Dixd5DvT0NDcjjr69UD"
             b"LqSD/Xt5Al7D3GXr1WOrWGpjO0NE9qzRCvMTU7zykRH6XjuNXB6Hvv48Zfm4vm"
@@ -957,8 +956,7 @@
             b"z53N7tPF/IhHTjBHb1Ol7IFu9p9A== testkey@localhost extra"
         )
 
-        with pytest.raises(ValueError):
-            load_ssh_public_key(ssh_key, backend)
+        load_ssh_public_key(ssh_key, backend)
 
     def test_load_ssh_public_key_dss_extra_data_after_modulo(self, backend):
         ssh_key = (
diff --git a/tests/test_utils.py b/tests/test_utils.py
index f71264e..210e929 100644
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -3045,8 +3045,13 @@
     assert expected == load_kasvs_dh_vectors(vector_data)
 
 
+def test_load_kasvs_ecdh_vectors_empty_vector_data():
+    assert [] == load_kasvs_ecdh_vectors([])
+
+
 def test_load_kasvs_ecdh_vectors():
     vector_data = textwrap.dedent("""
+    #  CAVS 11.0
     #  Parameter set(s) supported: EA EB EC ED EE
     #  CAVSid: CAVSid (in hex: 434156536964)
     #  IUTid: In hex: a1b2c3d4e5
diff --git a/tests/test_x509.py b/tests/test_x509.py
index 98cf49b..e31b57f 100644
--- a/tests/test_x509.py
+++ b/tests/test_x509.py
@@ -22,7 +22,7 @@
 from cryptography.hazmat.primitives.asymmetric import dsa, ec, rsa
 
 from .hazmat.primitives.fixtures_dsa import DSA_KEY_2048
-from .hazmat.primitives.fixtures_rsa import RSA_KEY_2048
+from .hazmat.primitives.fixtures_rsa import RSA_KEY_2048, RSA_KEY_512
 from .hazmat.primitives.test_ec import _skip_curve_unsupported
 from .utils import load_vectors_from_file
 
@@ -775,6 +775,439 @@
         assert hash(request1) == hash(request2)
         assert hash(request1) != hash(request3)
 
+    def test_build_cert(self, backend):
+        issuer_private_key = RSA_KEY_2048.private_key(backend)
+        subject_private_key = RSA_KEY_2048.private_key(backend)
+
+        not_valid_before = datetime.datetime(2002, 1, 1, 12, 1)
+        not_valid_after = datetime.datetime(2030, 12, 31, 8, 30)
+
+        builder = x509.CertificateBuilder().serial_number(
+            777
+        ).issuer_name(x509.Name([
+            x509.NameAttribute(x509.OID_COUNTRY_NAME, u'US'),
+            x509.NameAttribute(x509.OID_STATE_OR_PROVINCE_NAME, u'Texas'),
+            x509.NameAttribute(x509.OID_LOCALITY_NAME, u'Austin'),
+            x509.NameAttribute(x509.OID_ORGANIZATION_NAME, u'PyCA'),
+            x509.NameAttribute(x509.OID_COMMON_NAME, u'cryptography.io'),
+        ])).subject_name(x509.Name([
+            x509.NameAttribute(x509.OID_COUNTRY_NAME, u'US'),
+            x509.NameAttribute(x509.OID_STATE_OR_PROVINCE_NAME, u'Texas'),
+            x509.NameAttribute(x509.OID_LOCALITY_NAME, u'Austin'),
+            x509.NameAttribute(x509.OID_ORGANIZATION_NAME, u'PyCA'),
+            x509.NameAttribute(x509.OID_COMMON_NAME, u'cryptography.io'),
+        ])).public_key(
+            subject_private_key.public_key()
+        ).add_extension(
+            x509.BasicConstraints(ca=False, path_length=None), True,
+        ).add_extension(
+            x509.SubjectAlternativeName([x509.DNSName(u"cryptography.io")]),
+            critical=False,
+        ).not_valid_before(
+            not_valid_before
+        ).not_valid_after(
+            not_valid_after
+        )
+
+        cert = builder.sign(issuer_private_key, hashes.SHA1(), backend)
+
+        assert cert.version is x509.Version.v3
+        assert cert.not_valid_before == not_valid_before
+        assert cert.not_valid_after == not_valid_after
+        basic_constraints = cert.extensions.get_extension_for_oid(
+            x509.OID_BASIC_CONSTRAINTS
+        )
+        assert basic_constraints.value.ca is False
+        assert basic_constraints.value.path_length is None
+        subject_alternative_name = cert.extensions.get_extension_for_oid(
+            x509.OID_SUBJECT_ALTERNATIVE_NAME
+        )
+        assert list(subject_alternative_name.value) == [
+            x509.DNSName(u"cryptography.io"),
+        ]
+
+
+class TestCertificateBuilder(object):
+    def test_issuer_name_must_be_a_name_type(self):
+        builder = x509.CertificateBuilder()
+
+        with pytest.raises(TypeError):
+            builder.issuer_name("subject")
+
+        with pytest.raises(TypeError):
+            builder.issuer_name(object)
+
+    def test_issuer_name_may_only_be_set_once(self):
+        name = x509.Name([
+            x509.NameAttribute(x509.OID_COUNTRY_NAME, u'US'),
+        ])
+        builder = x509.CertificateBuilder().issuer_name(name)
+
+        with pytest.raises(ValueError):
+            builder.issuer_name(name)
+
+    def test_subject_name_must_be_a_name_type(self):
+        builder = x509.CertificateBuilder()
+
+        with pytest.raises(TypeError):
+            builder.subject_name("subject")
+
+        with pytest.raises(TypeError):
+            builder.subject_name(object)
+
+    def test_subject_name_may_only_be_set_once(self):
+        name = x509.Name([
+            x509.NameAttribute(x509.OID_COUNTRY_NAME, u'US'),
+        ])
+        builder = x509.CertificateBuilder().subject_name(name)
+
+        with pytest.raises(ValueError):
+            builder.subject_name(name)
+
+    @pytest.mark.requires_backend_interface(interface=RSABackend)
+    @pytest.mark.requires_backend_interface(interface=X509Backend)
+    def test_public_key_must_be_public_key(self, backend):
+        private_key = RSA_KEY_2048.private_key(backend)
+        builder = x509.CertificateBuilder()
+
+        with pytest.raises(TypeError):
+            builder.public_key(private_key)
+
+    @pytest.mark.requires_backend_interface(interface=RSABackend)
+    @pytest.mark.requires_backend_interface(interface=X509Backend)
+    def test_public_key_may_only_be_set_once(self, backend):
+        private_key = RSA_KEY_2048.private_key(backend)
+        public_key = private_key.public_key()
+        builder = x509.CertificateBuilder().public_key(public_key)
+
+        with pytest.raises(ValueError):
+            builder.public_key(public_key)
+
+    def test_serial_number_must_be_an_integer_type(self):
+        with pytest.raises(TypeError):
+            x509.CertificateBuilder().serial_number(10.0)
+
+    def test_serial_number_must_be_non_negative(self):
+        with pytest.raises(ValueError):
+            x509.CertificateBuilder().serial_number(-10)
+
+    def test_serial_number_must_be_less_than_160_bits_long(self):
+        with pytest.raises(ValueError):
+            # 2 raised to the 160th power is actually 161 bits
+            x509.CertificateBuilder().serial_number(2 ** 160)
+
+    def test_serial_number_may_only_be_set_once(self):
+        builder = x509.CertificateBuilder().serial_number(10)
+
+        with pytest.raises(ValueError):
+            builder.serial_number(20)
+
+    def test_invalid_not_valid_after(self):
+        with pytest.raises(TypeError):
+            x509.CertificateBuilder().not_valid_after(104204304504)
+
+        with pytest.raises(TypeError):
+            x509.CertificateBuilder().not_valid_after(datetime.time())
+
+        with pytest.raises(ValueError):
+            x509.CertificateBuilder().not_valid_after(
+                datetime.datetime(1960, 8, 10)
+            )
+
+    def test_not_valid_after_may_only_be_set_once(self):
+        builder = x509.CertificateBuilder().not_valid_after(
+            datetime.datetime.now()
+        )
+
+        with pytest.raises(ValueError):
+            builder.not_valid_after(
+                datetime.datetime.now()
+            )
+
+    def test_invalid_not_valid_before(self):
+        with pytest.raises(TypeError):
+            x509.CertificateBuilder().not_valid_before(104204304504)
+
+        with pytest.raises(TypeError):
+            x509.CertificateBuilder().not_valid_before(datetime.time())
+
+        with pytest.raises(ValueError):
+            x509.CertificateBuilder().not_valid_before(
+                datetime.datetime(1960, 8, 10)
+            )
+
+    def test_not_valid_before_may_only_be_set_once(self):
+        builder = x509.CertificateBuilder().not_valid_before(
+            datetime.datetime.now()
+        )
+
+        with pytest.raises(ValueError):
+            builder.not_valid_before(
+                datetime.datetime.now()
+            )
+
+    def test_add_extension_checks_for_duplicates(self):
+        builder = x509.CertificateBuilder().add_extension(
+            x509.BasicConstraints(ca=False, path_length=None), True,
+        )
+
+        with pytest.raises(ValueError):
+            builder.add_extension(
+                x509.BasicConstraints(ca=False, path_length=None), True,
+            )
+
+    def test_add_unsupported_extension(self):
+        builder = x509.CertificateBuilder()
+
+        with pytest.raises(NotImplementedError):
+            builder.add_extension(object(), False)
+
+    @pytest.mark.requires_backend_interface(interface=RSABackend)
+    @pytest.mark.requires_backend_interface(interface=X509Backend)
+    def test_sign_with_unsupported_hash(self, backend):
+        private_key = RSA_KEY_2048.private_key(backend)
+        builder = x509.CertificateBuilder()
+
+        with pytest.raises(TypeError):
+            builder.sign(private_key, object(), backend)
+
+    @pytest.mark.requires_backend_interface(interface=DSABackend)
+    @pytest.mark.requires_backend_interface(interface=X509Backend)
+    def test_sign_with_dsa_private_key_is_unsupported(self, backend):
+        if backend._lib.OPENSSL_VERSION_NUMBER >= 0x10001000:
+            pytest.skip("Requires an older OpenSSL. Must be < 1.0.1")
+
+        private_key = DSA_KEY_2048.private_key(backend)
+        builder = x509.CertificateBuilder()
+
+        with pytest.raises(NotImplementedError):
+            builder.sign(private_key, hashes.SHA512(), backend)
+
+    @pytest.mark.requires_backend_interface(interface=EllipticCurveBackend)
+    @pytest.mark.requires_backend_interface(interface=X509Backend)
+    def test_sign_with_ec_private_key_is_unsupported(self, backend):
+        if backend._lib.OPENSSL_VERSION_NUMBER >= 0x10001000:
+            pytest.skip("Requires an older OpenSSL. Must be < 1.0.1")
+
+        _skip_curve_unsupported(backend, ec.SECP256R1())
+        private_key = ec.generate_private_key(ec.SECP256R1(), backend)
+        builder = x509.CertificateBuilder()
+
+        with pytest.raises(NotImplementedError):
+            builder.sign(private_key, hashes.SHA512(), backend)
+
+    @pytest.mark.requires_backend_interface(interface=DSABackend)
+    @pytest.mark.requires_backend_interface(interface=X509Backend)
+    def test_build_cert_with_dsa_private_key(self, backend):
+        if backend._lib.OPENSSL_VERSION_NUMBER < 0x10001000:
+            pytest.skip("Requires a newer OpenSSL. Must be >= 1.0.1")
+
+        issuer_private_key = DSA_KEY_2048.private_key(backend)
+        subject_private_key = DSA_KEY_2048.private_key(backend)
+
+        not_valid_before = datetime.datetime(2002, 1, 1, 12, 1)
+        not_valid_after = datetime.datetime(2030, 12, 31, 8, 30)
+
+        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()
+        ).add_extension(
+            x509.BasicConstraints(ca=False, path_length=None), True,
+        ).add_extension(
+            x509.SubjectAlternativeName([x509.DNSName(u"cryptography.io")]),
+            critical=False,
+        ).not_valid_before(
+            not_valid_before
+        ).not_valid_after(
+            not_valid_after
+        )
+
+        cert = builder.sign(issuer_private_key, hashes.SHA1(), backend)
+
+        assert cert.version is x509.Version.v3
+        assert cert.not_valid_before == not_valid_before
+        assert cert.not_valid_after == not_valid_after
+        basic_constraints = cert.extensions.get_extension_for_oid(
+            x509.OID_BASIC_CONSTRAINTS
+        )
+        assert basic_constraints.value.ca is False
+        assert basic_constraints.value.path_length is None
+        subject_alternative_name = cert.extensions.get_extension_for_oid(
+            x509.OID_SUBJECT_ALTERNATIVE_NAME
+        )
+        assert list(subject_alternative_name.value) == [
+            x509.DNSName(u"cryptography.io"),
+        ]
+
+    @pytest.mark.requires_backend_interface(interface=EllipticCurveBackend)
+    @pytest.mark.requires_backend_interface(interface=X509Backend)
+    def test_build_cert_with_ec_private_key(self, backend):
+        if backend._lib.OPENSSL_VERSION_NUMBER < 0x10001000:
+            pytest.skip("Requires a newer OpenSSL. Must be >= 1.0.1")
+
+        _skip_curve_unsupported(backend, ec.SECP256R1())
+        issuer_private_key = ec.generate_private_key(ec.SECP256R1(), backend)
+        subject_private_key = ec.generate_private_key(ec.SECP256R1(), backend)
+
+        not_valid_before = datetime.datetime(2002, 1, 1, 12, 1)
+        not_valid_after = datetime.datetime(2030, 12, 31, 8, 30)
+
+        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()
+        ).add_extension(
+            x509.BasicConstraints(ca=False, path_length=None), True,
+        ).add_extension(
+            x509.SubjectAlternativeName([x509.DNSName(u"cryptography.io")]),
+            critical=False,
+        ).not_valid_before(
+            not_valid_before
+        ).not_valid_after(
+            not_valid_after
+        )
+
+        cert = builder.sign(issuer_private_key, hashes.SHA1(), backend)
+
+        assert cert.version is x509.Version.v3
+        assert cert.not_valid_before == not_valid_before
+        assert cert.not_valid_after == not_valid_after
+        basic_constraints = cert.extensions.get_extension_for_oid(
+            x509.OID_BASIC_CONSTRAINTS
+        )
+        assert basic_constraints.value.ca is False
+        assert basic_constraints.value.path_length is None
+        subject_alternative_name = cert.extensions.get_extension_for_oid(
+            x509.OID_SUBJECT_ALTERNATIVE_NAME
+        )
+        assert list(subject_alternative_name.value) == [
+            x509.DNSName(u"cryptography.io"),
+        ]
+
+    @pytest.mark.requires_backend_interface(interface=RSABackend)
+    @pytest.mark.requires_backend_interface(interface=X509Backend)
+    def test_build_cert_with_rsa_key_too_small(self, backend):
+        issuer_private_key = RSA_KEY_512.private_key(backend)
+        subject_private_key = RSA_KEY_512.private_key(backend)
+
+        not_valid_before = datetime.datetime(2002, 1, 1, 12, 1)
+        not_valid_after = datetime.datetime(2030, 12, 31, 8, 30)
+
+        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(
+            not_valid_before
+        ).not_valid_after(
+            not_valid_after
+        )
+
+        with pytest.raises(ValueError):
+            builder.sign(issuer_private_key, hashes.SHA512(), backend)
+
+    @pytest.mark.requires_backend_interface(interface=RSABackend)
+    @pytest.mark.requires_backend_interface(interface=X509Backend)
+    def test_extended_key_usage(self, backend):
+        issuer_private_key = RSA_KEY_2048.private_key(backend)
+        subject_private_key = RSA_KEY_2048.private_key(backend)
+
+        not_valid_before = datetime.datetime(2002, 1, 1, 12, 1)
+        not_valid_after = datetime.datetime(2030, 12, 31, 8, 30)
+
+        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(
+            not_valid_before
+        ).not_valid_after(
+            not_valid_after
+        ).public_key(
+            subject_private_key.public_key()
+        ).serial_number(
+            123
+        ).add_extension(
+            x509.ExtendedKeyUsage([
+                x509.OID_CLIENT_AUTH,
+                x509.OID_SERVER_AUTH,
+                x509.OID_CODE_SIGNING,
+            ]), critical=False
+        ).sign(issuer_private_key, hashes.SHA256(), backend)
+
+        eku = cert.extensions.get_extension_for_oid(
+            x509.OID_EXTENDED_KEY_USAGE
+        )
+        assert eku.critical is False
+        assert eku.value == x509.ExtendedKeyUsage([
+            x509.OID_CLIENT_AUTH,
+            x509.OID_SERVER_AUTH,
+            x509.OID_CODE_SIGNING,
+        ])
+
+    @pytest.mark.requires_backend_interface(interface=RSABackend)
+    @pytest.mark.requires_backend_interface(interface=X509Backend)
+    def test_key_usage(self, backend):
+        issuer_private_key = RSA_KEY_2048.private_key(backend)
+        subject_private_key = RSA_KEY_2048.private_key(backend)
+
+        not_valid_before = datetime.datetime(2002, 1, 1, 12, 1)
+        not_valid_after = datetime.datetime(2030, 12, 31, 8, 30)
+
+        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(
+            not_valid_before
+        ).not_valid_after(
+            not_valid_after
+        ).public_key(
+            subject_private_key.public_key()
+        ).serial_number(
+            123
+        ).add_extension(
+            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
+        ).sign(issuer_private_key, hashes.SHA256(), backend)
+
+        ext = cert.extensions.get_extension_for_oid(x509.OID_KEY_USAGE)
+        assert ext.critical is False
+        assert ext.value == 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
+        )
+
 
 @pytest.mark.requires_backend_interface(interface=X509Backend)
 class TestCertificateSigningRequestBuilder(object):
@@ -1219,6 +1652,49 @@
 
         assert str(exc.value) == "Digest too big for RSA key"
 
+    @pytest.mark.requires_backend_interface(interface=RSABackend)
+    @pytest.mark.requires_backend_interface(interface=X509Backend)
+    def test_build_cert_with_aia(self, backend):
+        issuer_private_key = RSA_KEY_2048.private_key(backend)
+        subject_private_key = RSA_KEY_2048.private_key(backend)
+
+        not_valid_before = datetime.datetime(2002, 1, 1, 12, 1)
+        not_valid_after = datetime.datetime(2030, 12, 31, 8, 30)
+
+        aia = x509.AuthorityInformationAccess([
+            x509.AccessDescription(
+                x509.OID_OCSP,
+                x509.UniformResourceIdentifier(u"http://ocsp.domain.com")
+            ),
+            x509.AccessDescription(
+                x509.OID_CA_ISSUERS,
+                x509.UniformResourceIdentifier(u"http://domain.com/ca.crt")
+            )
+        ])
+
+        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()
+        ).add_extension(
+            aia, critical=False
+        ).not_valid_before(
+            not_valid_before
+        ).not_valid_after(
+            not_valid_after
+        )
+
+        cert = builder.sign(issuer_private_key, hashes.SHA1(), backend)
+
+        ext = cert.extensions.get_extension_for_oid(
+            x509.OID_AUTHORITY_INFORMATION_ACCESS
+        )
+        assert ext.value == aia
+
 
 @pytest.mark.requires_backend_interface(interface=DSABackend)
 @pytest.mark.requires_backend_interface(interface=X509Backend)
diff --git a/tests/test_x509_ext.py b/tests/test_x509_ext.py
index 7b13582..890709a 100644
--- a/tests/test_x509_ext.py
+++ b/tests/test_x509_ext.py
@@ -2853,3 +2853,18 @@
             x509.OID_INHIBIT_ANY_POLICY
         ).value
         assert iap.skip_certs == 5
+
+
+@pytest.mark.requires_backend_interface(interface=RSABackend)
+@pytest.mark.requires_backend_interface(interface=X509Backend)
+class TestInvalidExtension(object):
+    def test_invalid_certificate_policies_data(self, backend):
+        cert = _load_cert(
+            os.path.join(
+                "x509", "custom", "cp_invalid.pem"
+            ),
+            x509.load_pem_x509_certificate,
+            backend
+        )
+        with pytest.raises(ValueError):
+            cert.extensions
diff --git a/tests/utils.py b/tests/utils.py
index 5083d48..7e7abdf 100644
--- a/tests/utils.py
+++ b/tests/utils.py
@@ -539,8 +539,8 @@
             elif line.startswith("Qy = "):
                 key_data["y"] = int(line.split("=")[1], 16)
 
-    if key_data is not None:
-        vectors.append(key_data)
+    assert key_data is not None
+    vectors.append(key_data)
 
     return vectors
 
@@ -559,9 +559,6 @@
     for line in vector_data:
         line = line.strip()
 
-        if not line or line.startswith("#"):
-            continue
-
         curve_match = curve_rx.match(line)
         if curve_match:
             curve_name = _ECDSA_CURVE_NAMES[curve_match.group("curve")]
@@ -593,8 +590,8 @@
             elif line.startswith("Result = "):
                 data["fail"] = line.split("=")[1].strip()[0] == "F"
 
-    if data is not None:
-        vectors.append(data)
+    assert data is not None
+    vectors.append(data)
     return vectors
 
 
diff --git a/vectors/cryptography_vectors/x509/custom/cp_invalid.pem b/vectors/cryptography_vectors/x509/custom/cp_invalid.pem
new file mode 100644
index 0000000..b7bcc07
--- /dev/null
+++ b/vectors/cryptography_vectors/x509/custom/cp_invalid.pem
@@ -0,0 +1,16 @@
+-----BEGIN CERTIFICATE-----
+MIIC8TCCAdmgAwIBAgITBmsoYWX1PCELRmm8qB2WJ2QdDjANBgkqhkiG9w0BAQUFADASMRAwDgYD
+VQQDDAdQeUNBIENBMB4XDTE1MDUxMTE4NTc0NVoXDTE2MDUxMDE4NTc0NVowEjEQMA4GA1UEAwwH
+UHlDQSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK3FTRITEY4b/Y1Uv4CtH61Y
+19TPxK2+H/XuqHwtYlPRyD35LLFES0wykf0V2m1DUmf9jQa9R63jBZxzCgJ/oIJzV28PgSg9P/Nn
+417fNASDduY2GPvYuwwKXcLY2fBBFjBrz7z/5tyXCADjLDkzoUTzQlYPbhOrFU5QwaqlckXBgt/4
+8GRDujoHy4RSMEDNjLUDgwx7Z/JK2ujbGJDguLRuBsHirk2h6xXEmSWxquKDXw4NnakwBqp8kKhQ
+2xTSWXxabNps8FCBM4sC78gKgONy3lbYdHFt/2BU4yAMyowJwtDEYHCqe1g4sVsB839Ol0SXb6vl
+eXQ6dx+zbi8UzTsCAwEAAaNAMD4wPAYDVR0gBDUwMzAxBgtghkgB4DkBAgMEATAiMCAGCCsGAQUF
+BwICFhRodHRwOi8vb3RoZXIuY29tL2NwczANBgkqhkiG9w0BAQUFAAOCAQEADpZIjHvu02euPNI8
+nzzDufRXEnjrF09xc9pudxTjWU2mSVApXPmTDyWzOD+2HmsNKHRE6sWjca5qPDeDbGq4JOw+TzYq
+9eoqwK2Sh0QHUpg5ZaAmIJ1qe5/sNETH5RFlXrlzW9S0rwViLgUaJp6MreTdGZbxdpNsfdkuNd+S
+Tz0MA/3ScbdUcj6uwQQ4JxQiTuPwD35pKwxfUzHjeTmqIEHDuCk17KqIRORdbeD3vFx0R5IQ3mQ6
+9zSGY2AGB0A9oS0qQ2/Mh59A6xyjbPH3Rr7g5MW58PPTWp2FSXkloy7Ze+doQ7wXE6PVmaeKz5qA
+9OGaCHIiC2iG9UcqWxfeWw==
+-----END CERTIFICATE-----