aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.rst27
-rw-r--r--docs/development/test-vectors.rst2
-rw-r--r--docs/installation.rst2
-rw-r--r--docs/x509/reference.rst153
-rw-r--r--src/_cffi_src/openssl/x509v3.py6
-rw-r--r--src/cryptography/hazmat/backends/interfaces.py6
-rw-r--r--src/cryptography/hazmat/backends/multibackend.py9
-rw-r--r--src/cryptography/hazmat/backends/openssl/backend.py161
-rw-r--r--src/cryptography/hazmat/backends/openssl/rsa.py5
-rw-r--r--src/cryptography/hazmat/backends/openssl/x509.py154
-rw-r--r--src/cryptography/hazmat/primitives/serialization.py4
-rw-r--r--src/cryptography/x509.py155
-rw-r--r--tests/hazmat/backends/test_multibackend.py8
-rw-r--r--tests/hazmat/backends/test_openssl.py56
-rw-r--r--tests/hazmat/bindings/test_openssl.py30
-rw-r--r--tests/hazmat/primitives/test_serialization.py10
-rw-r--r--tests/test_utils.py5
-rw-r--r--tests/test_x509.py478
-rw-r--r--tests/test_x509_ext.py15
-rw-r--r--tests/utils.py11
-rw-r--r--vectors/cryptography_vectors/x509/custom/cp_invalid.pem16
21 files changed, 1159 insertions, 154 deletions
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 85f84477..f07a9087 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -24,8 +24,33 @@ Changelog
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 ea44a46c..d400e662 100644
--- a/docs/development/test-vectors.rst
+++ b/docs/development/test-vectors.rst
@@ -233,6 +233,8 @@ Custom X.509 Vectors
* ``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 becab6b0..f7a88b98 100644
--- a/docs/installation.rst
+++ b/docs/installation.rst
@@ -142,7 +142,7 @@ To link cryptography against a custom version of OpenSSL you'll need to set
.. 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 9179468f..61971fed 100644
--- a/docs/x509/reference.rst
+++ b/docs/x509/reference.rst
@@ -388,6 +388,140 @@ X.509 CRL (Certificate Revocation List) Object
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 @@ X.509 CSR (Certificate Signing Request) Object
>>> 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 89822b85..f6a18903 100644
--- a/src/_cffi_src/openssl/x509v3.py
+++ b/src/_cffi_src/openssl/x509v3.py
@@ -203,6 +203,9 @@ int i2d_GENERAL_NAMES(GENERAL_NAMES *, unsigned char **);
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 @@ void sk_ACCESS_DESCRIPTION_free(Cryptography_STACK_OF_ACCESS_DESCRIPTION *);
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 4d378e6b..49ccda18 100644
--- a/src/cryptography/hazmat/backends/interfaces.py
+++ b/src/cryptography/hazmat/backends/interfaces.py
@@ -280,6 +280,12 @@ class X509Backend(object):
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 6e911fd5..8008989e 100644
--- a/src/cryptography/hazmat/backends/multibackend.py
+++ b/src/cryptography/hazmat/backends/multibackend.py
@@ -351,3 +351,12 @@ class MultiBackend(object):
"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 db4f963a..f9da9ea7 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 @@ def _encode_asn1_int(backend, x):
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 @@ def _encode_basic_constraints(backend, basic_constraints):
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 @@ class Backend(object):
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 @@ class Backend(object):
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 @@ class Backend(object):
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 @@ class Backend(object):
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 @@ class Backend(object):
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 @@ class Backend(object):
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 21414c05..822c7304 100644
--- a/src/cryptography/hazmat/backends/openssl/rsa.py
+++ b/src/cryptography/hazmat/backends/openssl/rsa.py
@@ -268,8 +268,9 @@ class _RSASignatureContext(object):
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 493abc83..ee9a3bbf 100644
--- a/src/cryptography/hazmat/backends/openssl/x509.py
+++ b/src/cryptography/hazmat/backends/openssl/x509.py
@@ -234,7 +234,15 @@ class _X509ExtensionParser(object):
"{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 @@ class _Certificate(object):
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 @@ def _decode_certificate_policies(backend, ext):
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 @@ def _decode_user_notice(backend, un):
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 @@ def _decode_basic_constraints(backend, ext):
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_subject_key_identifier(backend, ext):
)
-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_key_identifier(backend, ext):
)
-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 @@ def _decode_authority_information_access(backend, ext):
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_key_usage(backend, ext):
)
-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_issuer_alt_name(backend, ext):
)
-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 @@ def _decode_general_subtrees(backend, stack_subtrees):
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 @@ def _decode_extended_key_usage(backend, ext):
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 @@ def _decode_crl_distribution_points(backend, ext):
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 @@ class _CertificateSigningRequest(object):
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 098b31dc..fc50456e 100644
--- a/src/cryptography/hazmat/primitives/serialization.py
+++ b/src/cryptography/hazmat/primitives/serialization.py
@@ -33,9 +33,9 @@ def load_der_public_key(data, backend):
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 75552fc1..978eb560 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 six.moves import urllib_parse
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 @@ _GENERAL_NAMES = {
8: "registeredID",
}
+_UNIX_EPOCH = datetime.datetime(1970, 1, 1)
+
class Version(Enum):
v1 = 0
@@ -1598,3 +1602,154 @@ class CertificateSigningRequestBuilder(object):
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 3c05cdfa..d516af16 100644
--- a/tests/hazmat/backends/test_multibackend.py
+++ b/tests/hazmat/backends/test_multibackend.py
@@ -206,6 +206,9 @@ class DummyX509Backend(object):
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 @@ class TestMultiBackend(object):
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 @@ class TestMultiBackend(object):
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 6a2e8a77..0f2c80a6 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 pretend
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 ..primitives.test_ec import _skip_curve_unsupported
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 @@ class TestOpenSSL(object):
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 @@ class TestOpenSSLCreateX509CSR(object):
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 c5f0a7d7..20171fa7 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 @@ class TestOpenSSL(object):
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 af605830..f82e7354 100644
--- a/tests/hazmat/primitives/test_serialization.py
+++ b/tests/hazmat/primitives/test_serialization.py
@@ -854,7 +854,7 @@ class TestRSASSHSerialization(object):
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 @@ class TestRSASSHSerialization(object):
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 @@ class TestDSSSSHSerialization(object):
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 @@ class TestDSSSSHSerialization(object):
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 f71264ea..210e9292 100644
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -3045,8 +3045,13 @@ d518475576730ed528779366568e46b7dd4ed787cb72d0733c93
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 98cf49be..e31b57f4 100644
--- a/tests/test_x509.py
+++ b/tests/test_x509.py
@@ -22,7 +22,7 @@ from cryptography.hazmat.primitives import hashes, serialization
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 @@ class TestRSACertificateRequest(object):
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 @@ class TestCertificateSigningRequestBuilder(object):
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 7b135828..890709ae 100644
--- a/tests/test_x509_ext.py
+++ b/tests/test_x509_ext.py
@@ -2853,3 +2853,18 @@ class TestInhibitAnyPolicyExtension(object):
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 5083d48c..7e7abdf1 100644
--- a/tests/utils.py
+++ b/tests/utils.py
@@ -539,8 +539,8 @@ def load_fips_ecdsa_key_pair_vectors(vector_data):
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 @@ def load_fips_ecdsa_signing_vectors(vector_data):
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 @@ def load_fips_ecdsa_signing_vectors(vector_data):
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 00000000..b7bcc079
--- /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-----