Merge pull request #2045 from sigmavirus24/csr-builder

Adds CSR Builder (Redux of #1927)
This commit is contained in:
Paul Kehrer 2015-06-26 09:43:39 -05:00
commit 8f768dc5b9
9 changed files with 543 additions and 3 deletions

View file

@ -24,6 +24,8 @@ 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 creating certificate signing requests with
:class:`~cryptography.x509.CertificateSigningRequestBuilder`.
0.9.1 - 2015-06-06
~~~~~~~~~~~~~~~~~~

View file

@ -468,6 +468,76 @@ X.509 Revoked Certificate Object
The extensions encoded in the revoked certificate.
X.509 CSR (Certificate Signing Request) Builder Object
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. class:: CertificateSigningRequestBuilder
.. 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
>>> private_key = rsa.generate_private_key(
... public_exponent=65537,
... key_size=2048,
... backend=default_backend()
... )
>>> builder = x509.CertificateSigningRequestBuilder()
>>> builder = builder.subject_name(x509.Name([
... x509.NameAttribute(x509.OID_COMMON_NAME, u'cryptography.io'),
... ]))
>>> builder = builder.add_extension(
... x509.BasicConstraints(ca=False, path_length=None), critical=True,
... )
>>> request = builder.sign(
... default_backend(), private_key, hashes.SHA256()
... )
>>> isinstance(request, x509.CertificateSigningRequest)
True
.. method:: subject_name(name)
:param name: The :class:`~cryptography.x509.Name` of the certificate
subject.
:returns: A new
:class:`~cryptography.x509.CertificateSigningRequestBuilder`.
.. method:: add_extension(extension, critical)
:param extension: The :class:`~cryptography.x509.Extension` to add to
the request.
:param critical: Set to `True` if the extension must be understood and
handled by whoever reads the certificate.
:returns: A new
:class:`~cryptography.x509.CertificateSigningRequestBuilder`.
.. method:: sign(backend, private_key, algorithm)
:param backend: Backend that will be used to sign the request.
Must support the
:class:`~cryptography.hazmat.backends.interfaces.X509Backend`
interface.
: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 request. When the request is
signed by a certificate authority, the private key's associated
public key will be stored in the resulting certificate.
:param algorithm: The
:class:`~cryptography.hazmat.primitives.hashes.HashAlgorithm`
that will be used to generate the request signature.
:returns: A new
:class:`~cryptography.x509.CertificateSigningRequest`.
.. class:: Name
.. versionadded:: 0.8

View file

@ -274,6 +274,12 @@ class X509Backend(object):
Load an X.509 CSR from PEM encoded data.
"""
@abc.abstractmethod
def create_x509_csr(self, builder, private_key, algorithm):
"""
Create and sign an X.509 CSR from a CSR builder object.
"""
@six.add_metaclass(abc.ABCMeta)
class DHBackend(object):

View file

@ -342,3 +342,12 @@ class MultiBackend(object):
"This backend does not support X.509.",
_Reasons.UNSUPPORTED_X509
)
def create_x509_csr(self, builder, private_key, algorithm):
for b in self._filtered_backends(X509Backend):
return b.create_x509_csr(builder, private_key, algorithm)
raise UnsupportedAlgorithm(
"This backend does not support X.509.",
_Reasons.UNSUPPORTED_X509
)

View file

@ -10,7 +10,7 @@ from contextlib import contextmanager
import six
from cryptography import utils
from cryptography import utils, x509
from cryptography.exceptions import (
InternalError, UnsupportedAlgorithm, _Reasons
)
@ -56,6 +56,96 @@ _OpenSSLError = collections.namedtuple("_OpenSSLError",
["code", "lib", "func", "reason"])
def _encode_asn1_int(backend, x):
"""
Converts a python integer to a ASN1_INTEGER. The returned ASN1_INTEGER will
not be garbage collected (to support adding them to structs that take
ownership of the object). Be sure to register it for GC if it will be
discarded after use.
"""
# Convert Python integer to OpenSSL "bignum" in case value exceeds
# machine's native integer limits (note: `int_to_bn` doesn't automatically
# GC).
i = backend._int_to_bn(x)
i = backend._ffi.gc(i, backend._lib.BN_free)
# Wrap in a ASN.1 integer. Don't GC -- as documented.
i = backend._lib.BN_to_ASN1_INTEGER(i, backend._ffi.NULL)
assert i != backend._ffi.NULL
return i
def _encode_asn1_str(backend, data, length):
"""
Create an ASN1_OCTET_STRING from a Python byte string.
"""
s = backend._lib.ASN1_OCTET_STRING_new()
s = backend._ffi.gc(s, backend._lib.ASN1_OCTET_STRING_free)
backend._lib.ASN1_OCTET_STRING_set(s, data, length)
return s
def _encode_name(backend, attributes):
subject = backend._lib.X509_NAME_new()
subject = backend._ffi.gc(subject, backend._lib.X509_NAME_free)
for attribute in attributes:
value = attribute.value.encode('utf8')
obj = _txt2obj(backend, attribute.oid.dotted_string)
res = backend._lib.X509_NAME_add_entry_by_OBJ(
subject,
obj,
backend._lib.MBSTRING_UTF8,
value,
-1, -1, 0,
)
assert res == 1
return subject
def _txt2obj(backend, name):
"""
Converts a Python string with an ASN.1 object ID in dotted form to a
ASN1_OBJECT.
"""
name = name.encode('ascii')
obj = backend._lib.OBJ_txt2obj(name, 1)
assert obj != backend._ffi.NULL
obj = backend._ffi.gc(obj, backend._lib.ASN1_OBJECT_free)
return obj
def _encode_basic_constraints(backend, basic_constraints, critical):
obj = _txt2obj(backend, x509.OID_BASIC_CONSTRAINTS.dotted_string)
assert obj is not None
constraints = backend._lib.BASIC_CONSTRAINTS_new()
constraints.ca = 255 if basic_constraints.ca else 0
if basic_constraints.ca:
constraints.pathlen = _encode_asn1_int(
backend, basic_constraints.path_length
)
# Fetch the encoded payload.
pp = backend._ffi.new('unsigned char **')
r = backend._lib.i2d_BASIC_CONSTRAINTS(constraints, pp)
assert r > 0
pp = backend._ffi.gc(
pp, lambda pointer: backend._lib.OPENSSL_free(pointer[0])
)
# Wrap that in an X509 extension object.
extension = backend._lib.X509_EXTENSION_create_by_OBJ(
backend._ffi.NULL,
obj,
1 if critical else 0,
_encode_asn1_str(backend, pp[0], r),
)
assert extension != backend._ffi.NULL
# Return the wrapped extension.
return extension
@utils.register_interface(CipherBackend)
@utils.register_interface(CMACBackend)
@utils.register_interface(DERSerializationBackend)
@ -710,6 +800,79 @@ class Backend(object):
def create_cmac_ctx(self, algorithm):
return _CMACContext(self, algorithm)
def create_x509_csr(self, builder, private_key, algorithm):
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 signing requests aren't implemented for DSA"
" keys on OpenSSL versions less than 1.0.1."
)
if isinstance(private_key, _EllipticCurvePrivateKey):
raise NotImplementedError(
"Certificate signing requests 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 request.
x509_req = self._lib.X509_REQ_new()
assert x509_req != self._ffi.NULL
x509_req = self._ffi.gc(x509_req, self._lib.X509_REQ_free)
# Set x509 version.
res = self._lib.X509_REQ_set_version(x509_req, x509.Version.v1.value)
assert res == 1
# Set subject name.
res = self._lib.X509_REQ_set_subject_name(
x509_req, _encode_name(self, list(builder._subject_name))
)
assert res == 1
# Set subject public key.
public_key = private_key.public_key()
res = self._lib.X509_REQ_set_pubkey(
x509_req, public_key._evp_pkey
)
assert res == 1
# Add extensions.
extensions = self._lib.sk_X509_EXTENSION_new_null()
assert extensions != self._ffi.NULL
extensions = self._ffi.gc(
extensions,
self._lib.sk_X509_EXTENSION_free,
)
for extension in builder._extensions:
if isinstance(extension.value, x509.BasicConstraints):
extension = _encode_basic_constraints(
self,
extension.value,
extension.critical
)
else:
raise NotImplementedError('Extension not yet supported.')
res = self._lib.sk_X509_EXTENSION_push(extensions, extension)
assert res == 1
res = self._lib.X509_REQ_add_extensions(x509_req, extensions)
assert res == 1
# Sign the request using the requester's private key.
res = self._lib.X509_REQ_sign(
x509_req, private_key._evp_pkey, evp_md
)
assert res > 0
return _CertificateSigningRequest(self, x509_req)
def load_pem_private_key(self, data, password):
return self._load_key(
self._lib.PEM_read_bio_PrivateKey,

View file

@ -1442,3 +1442,44 @@ class RevokedCertificate(object):
"""
Returns an Extensions object containing a list of Revoked extensions.
"""
class CertificateSigningRequestBuilder(object):
def __init__(self, subject_name=None, extensions=[]):
"""
Creates an empty X.509 certificate request (v1).
"""
self._subject_name = subject_name
self._extensions = extensions
def subject_name(self, name):
"""
Sets the certificate 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 CertificateSigningRequestBuilder(name, self._extensions)
def add_extension(self, extension, critical):
"""
Adds an X.509 extension to the certificate request.
"""
if isinstance(extension, BasicConstraints):
extension = Extension(OID_BASIC_CONSTRAINTS, 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 CertificateSigningRequestBuilder(
self._subject_name, self._extensions + [extension]
)
def sign(self, backend, private_key, algorithm):
"""
Signs the request using the requestor's private key.
"""
return backend.create_x509_csr(self, private_key, algorithm)

View file

@ -203,6 +203,9 @@ class DummyX509Backend(object):
def load_der_x509_csr(self, data):
pass
def create_x509_csr(self, builder, private_key, algorithm):
pass
class TestMultiBackend(object):
def test_ciphers(self):
@ -480,6 +483,7 @@ class TestMultiBackend(object):
backend.load_der_x509_certificate(b"certdata")
backend.load_pem_x509_csr(b"reqdata")
backend.load_der_x509_csr(b"reqdata")
backend.create_x509_csr(object(), b"privatekey", hashes.SHA1())
backend = MultiBackend([])
with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_X509):
@ -490,3 +494,5 @@ class TestMultiBackend(object):
backend.load_pem_x509_csr(b"reqdata")
with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_X509):
backend.load_der_x509_csr(b"reqdata")
with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_X509):
backend.create_x509_csr(object(), b"privatekey", hashes.SHA1())

View file

@ -21,14 +21,16 @@ from cryptography.hazmat.backends.openssl.backend import (
)
from cryptography.hazmat.backends.openssl.ec import _sn_to_elliptic_curve
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import dsa, padding
from cryptography.hazmat.primitives.asymmetric import dsa, ec, padding
from cryptography.hazmat.primitives.ciphers import (
BlockCipherAlgorithm, Cipher, CipherAlgorithm
)
from cryptography.hazmat.primitives.ciphers.algorithms import AES
from cryptography.hazmat.primitives.ciphers.modes import CBC, CTR, Mode
from ..primitives.fixtures_dsa import DSA_KEY_2048
from ..primitives.fixtures_rsa import RSA_KEY_2048, RSA_KEY_512
from ..primitives.test_ec import _skip_curve_unsupported
from ...utils import load_vectors_from_file, raises_unsupported_algorithm
@ -453,6 +455,29 @@ class TestOpenSSLCMAC(object):
backend.create_cmac_ctx(FakeAlgorithm())
class TestOpenSSLCreateX509CSR(object):
@pytest.mark.skipif(
backend._lib.OPENSSL_VERSION_NUMBER >= 0x10001000,
reason="Requires an older OpenSSL. Must be < 1.0.1"
)
def test_unsupported_dsa_keys(self):
private_key = DSA_KEY_2048.private_key(backend)
with pytest.raises(NotImplementedError):
backend.create_x509_csr(object(), private_key, hashes.SHA1())
@pytest.mark.skipif(
backend._lib.OPENSSL_VERSION_NUMBER >= 0x10001000,
reason="Requires an older OpenSSL. Must be < 1.0.1"
)
def test_unsupported_ec_keys(self):
_skip_curve_unsupported(backend, ec.SECP256R1())
private_key = ec.generate_private_key(ec.SECP256R1(), backend)
with pytest.raises(NotImplementedError):
backend.create_x509_csr(object(), private_key, hashes.SHA1())
class TestOpenSSLSerialisationWithOpenSSL(object):
def test_pem_password_cb_buffer_too_small(self):
ffi_cb, cb = backend._pem_password_cb(b"aa")

View file

@ -20,6 +20,8 @@ from cryptography.hazmat.backends.interfaces import (
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.test_ec import _skip_curve_unsupported
from .utils import load_vectors_from_file
@ -586,7 +588,7 @@ class TestRSACertificateRequest(object):
x509.Extension(
x509.OID_BASIC_CONSTRAINTS,
True,
x509.BasicConstraints(True, 1),
x509.BasicConstraints(ca=True, path_length=1),
),
]
@ -679,6 +681,222 @@ class TestRSACertificateRequest(object):
assert serialized == request_bytes
@pytest.mark.requires_backend_interface(interface=X509Backend)
class TestCertificateSigningRequestBuilder(object):
@pytest.mark.requires_backend_interface(interface=RSABackend)
def test_sign_invalid_hash_algorithm(self, backend):
private_key = RSA_KEY_2048.private_key(backend)
builder = x509.CertificateSigningRequestBuilder()
with pytest.raises(TypeError):
builder.sign(backend, private_key, 'NotAHash')
@pytest.mark.requires_backend_interface(interface=RSABackend)
def test_build_ca_request_with_rsa(self, backend):
private_key = RSA_KEY_2048.private_key(backend)
request = x509.CertificateSigningRequestBuilder().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'),
])
).add_extension(
x509.BasicConstraints(ca=True, path_length=2), critical=True
).sign(
backend, private_key, hashes.SHA1()
)
assert isinstance(request.signature_hash_algorithm, hashes.SHA1)
public_key = request.public_key()
assert isinstance(public_key, rsa.RSAPublicKey)
subject = request.subject
assert isinstance(subject, x509.Name)
assert list(subject) == [
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'),
]
basic_constraints = request.extensions.get_extension_for_oid(
x509.OID_BASIC_CONSTRAINTS
)
assert basic_constraints.value.ca is True
assert basic_constraints.value.path_length == 2
@pytest.mark.requires_backend_interface(interface=RSABackend)
def test_build_ca_request_with_unicode(self, backend):
private_key = RSA_KEY_2048.private_key(backend)
request = x509.CertificateSigningRequestBuilder().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\U0001f37a'),
x509.NameAttribute(x509.OID_COMMON_NAME, u'cryptography.io'),
])
).add_extension(
x509.BasicConstraints(ca=True, path_length=2), critical=True
).sign(
backend, private_key, hashes.SHA1()
)
loaded_request = x509.load_pem_x509_csr(
request.public_bytes(encoding=serialization.Encoding.PEM), backend
)
subject = loaded_request.subject
assert isinstance(subject, x509.Name)
assert list(subject) == [
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\U0001f37a'),
x509.NameAttribute(x509.OID_COMMON_NAME, u'cryptography.io'),
]
@pytest.mark.requires_backend_interface(interface=RSABackend)
def test_build_nonca_request_with_rsa(self, backend):
private_key = RSA_KEY_2048.private_key(backend)
request = x509.CertificateSigningRequestBuilder().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'),
])
).add_extension(
x509.BasicConstraints(ca=False, path_length=None), critical=True,
).sign(
backend, private_key, hashes.SHA1()
)
assert isinstance(request.signature_hash_algorithm, hashes.SHA1)
public_key = request.public_key()
assert isinstance(public_key, rsa.RSAPublicKey)
subject = request.subject
assert isinstance(subject, x509.Name)
assert list(subject) == [
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'),
]
basic_constraints = request.extensions.get_extension_for_oid(
x509.OID_BASIC_CONSTRAINTS
)
assert basic_constraints.value.ca is False
assert basic_constraints.value.path_length is None
@pytest.mark.requires_backend_interface(interface=EllipticCurveBackend)
def test_build_ca_request_with_ec(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())
private_key = ec.generate_private_key(ec.SECP256R1(), backend)
request = x509.CertificateSigningRequestBuilder().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'),
])
).add_extension(
x509.BasicConstraints(ca=True, path_length=2), critical=True
).sign(
backend, private_key, hashes.SHA1()
)
assert isinstance(request.signature_hash_algorithm, hashes.SHA1)
public_key = request.public_key()
assert isinstance(public_key, ec.EllipticCurvePublicKey)
subject = request.subject
assert isinstance(subject, x509.Name)
assert list(subject) == [
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'),
]
basic_constraints = request.extensions.get_extension_for_oid(
x509.OID_BASIC_CONSTRAINTS
)
assert basic_constraints.value.ca is True
assert basic_constraints.value.path_length == 2
@pytest.mark.requires_backend_interface(interface=DSABackend)
def test_build_ca_request_with_dsa(self, backend):
if backend._lib.OPENSSL_VERSION_NUMBER < 0x10001000:
pytest.skip("Requires a newer OpenSSL. Must be >= 1.0.1")
private_key = DSA_KEY_2048.private_key(backend)
request = x509.CertificateSigningRequestBuilder().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'),
])
).add_extension(
x509.BasicConstraints(ca=True, path_length=2), critical=True
).sign(
backend, private_key, hashes.SHA1()
)
assert isinstance(request.signature_hash_algorithm, hashes.SHA1)
public_key = request.public_key()
assert isinstance(public_key, dsa.DSAPublicKey)
subject = request.subject
assert isinstance(subject, x509.Name)
assert list(subject) == [
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'),
]
basic_constraints = request.extensions.get_extension_for_oid(
x509.OID_BASIC_CONSTRAINTS
)
assert basic_constraints.value.ca is True
assert basic_constraints.value.path_length == 2
def test_add_duplicate_extension(self, backend):
builder = x509.CertificateSigningRequestBuilder().add_extension(
x509.BasicConstraints(True, 2), critical=True,
)
with pytest.raises(ValueError):
builder.add_extension(
x509.BasicConstraints(True, 2), critical=True,
)
def test_set_invalid_subject(self, backend):
builder = x509.CertificateSigningRequestBuilder()
with pytest.raises(TypeError):
builder.subject_name('NotAName')
def test_add_unsupported_extension(self, backend):
builder = x509.CertificateSigningRequestBuilder()
with pytest.raises(NotImplementedError):
builder.add_extension(
x509.AuthorityKeyIdentifier('keyid', None, None),
critical=False,
)
@pytest.mark.requires_backend_interface(interface=DSABackend)
@pytest.mark.requires_backend_interface(interface=X509Backend)
class TestDSACertificate(object):