aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlex Gaynor <alex.gaynor@gmail.com>2015-05-18 18:26:08 -0400
committerAlex Gaynor <alex.gaynor@gmail.com>2015-05-18 18:26:08 -0400
commitaf89bb64b59f8352c6f9f2d48fbd6f98e7f18213 (patch)
tree13a7cdbfad66503fd891b10b4eecf0d64ae4df0e
parent63d95d0973970c01dbfa8432055bc7e13acc1049 (diff)
parentf85201830f737f2591622624a03df52bcb1c0f7e (diff)
downloadcryptography-af89bb64b59f8352c6f9f2d48fbd6f98e7f18213.tar.gz
cryptography-af89bb64b59f8352c6f9f2d48fbd6f98e7f18213.tar.bz2
cryptography-af89bb64b59f8352c6f9f2d48fbd6f98e7f18213.zip
Merge branch 'master' into macstadium-travis
-rwxr-xr-x.travis/install.sh2
-rw-r--r--AUTHORS.rst1
-rw-r--r--docs/development/test-vectors.rst13
-rw-r--r--docs/x509.rst23
-rw-r--r--src/cryptography/hazmat/backends/openssl/x509.py636
-rw-r--r--src/cryptography/x509.py42
-rw-r--r--tests/test_x509.py58
-rw-r--r--tests/test_x509_ext.py33
-rw-r--r--vectors/cryptography_vectors/x509/requests/basic_constraints.pem64
-rw-r--r--vectors/cryptography_vectors/x509/requests/two_basic_constraints.pem66
-rw-r--r--vectors/cryptography_vectors/x509/requests/unsupported_extension.pem64
-rw-r--r--vectors/cryptography_vectors/x509/requests/unsupported_extension_critical.pem64
12 files changed, 765 insertions, 301 deletions
diff --git a/.travis/install.sh b/.travis/install.sh
index f5873288..1baae5ba 100755
--- a/.travis/install.sh
+++ b/.travis/install.sh
@@ -4,7 +4,7 @@ set -e
set -x
if [[ "$(uname -s)" == 'Darwin' ]]; then
- brew update
+ brew update || brew update
if [[ "${OPENSSL}" != "0.9.8" ]]; then
brew upgrade openssl
diff --git a/AUTHORS.rst b/AUTHORS.rst
index 6e92a84f..430e30ea 100644
--- a/AUTHORS.rst
+++ b/AUTHORS.rst
@@ -22,3 +22,4 @@ PGP key fingerprints are enclosed in parentheses.
* Gregory Haynes <greg@greghaynes.net> (6FB6 44BF 9FD0 EBA2 1CE9 471F B08F 42F9 0DC6 599F)
* Chelsea Winfree <chelsea.winfree@gmail.com>
* Steven Buss <steven.buss@gmail.com> (1FB9 2EC1 CF93 DFD6 B47F F583 B1A5 6C22 290D A4C3)
+* Andre Caron <andre.l.caron@gmail.com>
diff --git a/docs/development/test-vectors.rst b/docs/development/test-vectors.rst
index 6f61a7ee..4c0063b5 100644
--- a/docs/development/test-vectors.rst
+++ b/docs/development/test-vectors.rst
@@ -217,6 +217,19 @@ Custom X.509 Request Vectors
* ``san_rsa_sha1.pem`` and ``san_rsa_sha1.der`` - Contain a certificate
request using RSA and SHA1 with a subject alternative name extension
generated using OpenSSL.
+* ``two_basic_constraints.pem`` - A certificate signing request
+ for a RSA 2048 bit key containing two basic constraints extensions.
+* ``unsupported_extension.pem`` - A certificate signing request
+ for an RSA 2048 bit key containing containing an unsupported
+ extension type. The OID was encoded as "1.2.3.4" with an
+ ``extnValue`` of "value".
+* ``unsupported_extension_critical.pem`` - A certificate signing
+ request for an RSA 2048 bit key containing containing an unsupported
+ extension type marked critical. The OID was encoded as "1.2.3.4"
+ with an ``extnValue`` of "value".
+* ``basic_constraints.pem`` - A certificate signing request for a RSA
+ 2048 bit key containing a basic constraints extension marked as
+ critical.
Hashes
~~~~~~
diff --git a/docs/x509.rst b/docs/x509.rst
index ff43be01..3f1af86c 100644
--- a/docs/x509.rst
+++ b/docs/x509.rst
@@ -509,8 +509,9 @@ General Name Classes
.. attribute:: value
- :type: :class:`~ipaddress.IPv4Address` or
- :class:`~ipaddress.IPv6Address`.
+ :type: :class:`~ipaddress.IPv4Address`,
+ :class:`~ipaddress.IPv6Address`, :class:`~ipaddress.IPv4Network`,
+ or :class:`~ipaddress.IPv6Network`.
.. class:: RegisteredID
@@ -698,6 +699,19 @@ X.509 Extensions
purposes indicated in the key usage extension. The object is
iterable to obtain the list of :ref:`extended key usage OIDs <eku_oids>`.
+.. class:: OCSPNoCheck
+
+ .. versionadded:: 0.10
+
+ This presence of this extension indicates that an OCSP client can trust a
+ responder for the lifetime of the responder's certificate. CAs issuing
+ such a certificate should realize that a compromise of the responder's key
+ is as serious as the compromise of a CA key used to sign CRLs, at least for
+ the validity period of this certificate. CA's may choose to issue this type
+ of certificate with a very short lifetime and renew it frequently. This
+ extension is only relevant when the certificate is an authorized OCSP
+ responder.
+
.. class:: AuthorityKeyIdentifier
.. versionadded:: 0.9
@@ -1234,6 +1248,11 @@ Extension OIDs
Corresponds to the dotted string ``"1.3.6.1.5.5.7.1.1"``. The identifier
for the :class:`AuthorityInformationAccess` extension type.
+.. data:: OID_OCSP_NO_CHECK
+
+ Corresponds to the dotted string ``"1.3.6.1.5.5.7.48.1.5"``. The identifier
+ for the :class:`OCSPNoCheck` extension type.
+
Exceptions
~~~~~~~~~~
diff --git a/src/cryptography/hazmat/backends/openssl/x509.py b/src/cryptography/hazmat/backends/openssl/x509.py
index 07e54baa..6db6fc9c 100644
--- a/src/cryptography/hazmat/backends/openssl/x509.py
+++ b/src/cryptography/hazmat/backends/openssl/x509.py
@@ -56,7 +56,7 @@ def _asn1_string_to_utf8(backend, asn1_string):
return backend._ffi.buffer(buf[0], res)[:].decode('utf8')
-def _build_x509_name_entry(backend, x509_name_entry):
+def _decode_x509_name_entry(backend, x509_name_entry):
obj = backend._lib.X509_NAME_ENTRY_get_object(x509_name_entry)
assert obj != backend._ffi.NULL
data = backend._lib.X509_NAME_ENTRY_get_data(x509_name_entry)
@@ -67,28 +67,28 @@ def _build_x509_name_entry(backend, x509_name_entry):
return x509.NameAttribute(x509.ObjectIdentifier(oid), value)
-def _build_x509_name(backend, x509_name):
+def _decode_x509_name(backend, x509_name):
count = backend._lib.X509_NAME_entry_count(x509_name)
attributes = []
for x in range(count):
entry = backend._lib.X509_NAME_get_entry(x509_name, x)
- attributes.append(_build_x509_name_entry(backend, entry))
+ attributes.append(_decode_x509_name_entry(backend, entry))
return x509.Name(attributes)
-def _build_general_names(backend, gns):
+def _decode_general_names(backend, gns):
num = backend._lib.sk_GENERAL_NAME_num(gns)
names = []
for i in range(num):
gn = backend._lib.sk_GENERAL_NAME_value(gns, i)
assert gn != backend._ffi.NULL
- names.append(_build_general_name(backend, gn))
+ names.append(_decode_general_name(backend, gn))
return names
-def _build_general_name(backend, gn):
+def _decode_general_name(backend, gn):
if gn.type == backend._lib.GEN_DNS:
data = backend._ffi.buffer(gn.d.dNSName.data, gn.d.dNSName.length)[:]
return x509.DNSName(idna.decode(data))
@@ -129,7 +129,7 @@ def _build_general_name(backend, gn):
)
elif gn.type == backend._lib.GEN_DIRNAME:
return x509.DirectoryName(
- _build_x509_name(backend, gn.d.directoryName)
+ _decode_x509_name(backend, gn.d.directoryName)
)
elif gn.type == backend._lib.GEN_EMAIL:
data = backend._ffi.buffer(
@@ -244,13 +244,13 @@ class _Certificate(object):
def issuer(self):
issuer = self._backend._lib.X509_get_issuer_name(self._x509)
assert issuer != self._backend._ffi.NULL
- return _build_x509_name(self._backend, issuer)
+ return _decode_x509_name(self._backend, issuer)
@property
def subject(self):
subject = self._backend._lib.X509_get_subject_name(self._x509)
assert subject != self._backend._ffi.NULL
- return _build_x509_name(self._backend, subject)
+ return _decode_x509_name(self._backend, subject)
@property
def signature_hash_algorithm(self):
@@ -278,23 +278,25 @@ class _Certificate(object):
"Duplicate {0} extension found".format(oid), oid
)
elif oid == x509.OID_BASIC_CONSTRAINTS:
- value = self._build_basic_constraints(ext)
+ value = _decode_basic_constraints(self._backend, ext)
elif oid == x509.OID_SUBJECT_KEY_IDENTIFIER:
- value = self._build_subject_key_identifier(ext)
+ value = _decode_subject_key_identifier(self._backend, ext)
elif oid == x509.OID_KEY_USAGE:
- value = self._build_key_usage(ext)
+ value = _decode_key_usage(self._backend, ext)
elif oid == x509.OID_SUBJECT_ALTERNATIVE_NAME:
- value = self._build_subject_alt_name(ext)
+ value = _decode_subject_alt_name(self._backend, ext)
elif oid == x509.OID_EXTENDED_KEY_USAGE:
- value = self._build_extended_key_usage(ext)
+ value = _decode_extended_key_usage(self._backend, ext)
elif oid == x509.OID_AUTHORITY_KEY_IDENTIFIER:
- value = self._build_authority_key_identifier(ext)
+ value = _decode_authority_key_identifier(self._backend, ext)
elif oid == x509.OID_AUTHORITY_INFORMATION_ACCESS:
- value = self._build_authority_information_access(ext)
+ value = _decode_authority_information_access(
+ self._backend, ext
+ )
elif oid == x509.OID_CERTIFICATE_POLICIES:
- value = self._build_certificate_policies(ext)
+ value = _decode_certificate_policies(self._backend, ext)
elif oid == x509.OID_CRL_DISTRIBUTION_POINTS:
- value = self._build_crl_distribution_points(ext)
+ value = _decode_crl_distribution_points(self._backend, ext)
elif critical:
raise x509.UnsupportedExtension(
"{0} is not currently supported".format(oid), oid
@@ -309,311 +311,323 @@ class _Certificate(object):
return x509.Extensions(extensions)
- def _build_certificate_policies(self, ext):
- cp = self._backend._ffi.cast(
- "Cryptography_STACK_OF_POLICYINFO *",
- self._backend._lib.X509V3_EXT_d2i(ext)
- )
- assert cp != self._backend._ffi.NULL
- cp = self._backend._ffi.gc(cp, self._backend._lib.sk_POLICYINFO_free)
- num = self._backend._lib.sk_POLICYINFO_num(cp)
- certificate_policies = []
- for i in range(num):
- qualifiers = None
- pi = self._backend._lib.sk_POLICYINFO_value(cp, i)
- oid = x509.ObjectIdentifier(_obj2txt(self._backend, pi.policyid))
- if pi.qualifiers != self._backend._ffi.NULL:
- qnum = self._backend._lib.sk_POLICYQUALINFO_num(pi.qualifiers)
- qualifiers = []
- for j in range(qnum):
- pqi = self._backend._lib.sk_POLICYQUALINFO_value(
- pi.qualifiers, j
- )
- pqualid = x509.ObjectIdentifier(
- _obj2txt(self._backend, pqi.pqualid)
- )
- if pqualid == x509.OID_CPS_QUALIFIER:
- cpsuri = self._backend._ffi.buffer(
- pqi.d.cpsuri.data, pqi.d.cpsuri.length
- )[:].decode('ascii')
- qualifiers.append(cpsuri)
- elif pqualid == x509.OID_CPS_USER_NOTICE:
- user_notice = self._build_user_notice(pqi.d.usernotice)
- qualifiers.append(user_notice)
-
- certificate_policies.append(
- x509.PolicyInformation(oid, qualifiers)
- )
-
- return x509.CertificatePolicies(certificate_policies)
- def _build_user_notice(self, un):
- explicit_text = None
- notice_reference = None
-
- if un.exptext != self._backend._ffi.NULL:
- explicit_text = _asn1_string_to_utf8(self._backend, un.exptext)
-
- if un.noticeref != self._backend._ffi.NULL:
- organization = _asn1_string_to_utf8(
- self._backend, un.noticeref.organization
- )
-
- num = self._backend._lib.sk_ASN1_INTEGER_num(
- un.noticeref.noticenos
- )
- notice_numbers = []
- for i in range(num):
- asn1_int = self._backend._lib.sk_ASN1_INTEGER_value(
- un.noticeref.noticenos, i
+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
+ cp = backend._ffi.gc(cp, backend._lib.sk_POLICYINFO_free)
+ num = backend._lib.sk_POLICYINFO_num(cp)
+ certificate_policies = []
+ for i in range(num):
+ qualifiers = None
+ pi = backend._lib.sk_POLICYINFO_value(cp, i)
+ oid = x509.ObjectIdentifier(_obj2txt(backend, pi.policyid))
+ if pi.qualifiers != backend._ffi.NULL:
+ qnum = backend._lib.sk_POLICYQUALINFO_num(pi.qualifiers)
+ qualifiers = []
+ for j in range(qnum):
+ pqi = backend._lib.sk_POLICYQUALINFO_value(
+ pi.qualifiers, j
)
- notice_num = _asn1_integer_to_int(
- self._backend, asn1_int
+ pqualid = x509.ObjectIdentifier(
+ _obj2txt(backend, pqi.pqualid)
)
- notice_numbers.append(notice_num)
+ if pqualid == x509.OID_CPS_QUALIFIER:
+ cpsuri = backend._ffi.buffer(
+ pqi.d.cpsuri.data, pqi.d.cpsuri.length
+ )[:].decode('ascii')
+ qualifiers.append(cpsuri)
+ elif pqualid == x509.OID_CPS_USER_NOTICE:
+ user_notice = _decode_user_notice(
+ backend, pqi.d.usernotice
+ )
+ qualifiers.append(user_notice)
- notice_reference = x509.NoticeReference(
- organization, notice_numbers
- )
+ certificate_policies.append(
+ x509.PolicyInformation(oid, qualifiers)
+ )
- return x509.UserNotice(notice_reference, explicit_text)
+ return x509.CertificatePolicies(certificate_policies)
- def _build_basic_constraints(self, ext):
- bc_st = self._backend._lib.X509V3_EXT_d2i(ext)
- assert bc_st != self._backend._ffi.NULL
- basic_constraints = self._backend._ffi.cast(
- "BASIC_CONSTRAINTS *", bc_st
- )
- basic_constraints = self._backend._ffi.gc(
- basic_constraints, self._backend._lib.BASIC_CONSTRAINTS_free
- )
- # The byte representation of an ASN.1 boolean true is \xff. OpenSSL
- # chooses to just map this to its ordinal value, so true is 255 and
- # false is 0.
- ca = basic_constraints.ca == 255
- if basic_constraints.pathlen == self._backend._ffi.NULL:
- path_length = None
- else:
- path_length = _asn1_integer_to_int(
- self._backend, basic_constraints.pathlen
- )
- return x509.BasicConstraints(ca, path_length)
+def _decode_user_notice(backend, un):
+ explicit_text = None
+ notice_reference = None
- def _build_subject_key_identifier(self, ext):
- asn1_string = self._backend._lib.X509V3_EXT_d2i(ext)
- assert asn1_string != self._backend._ffi.NULL
- asn1_string = self._backend._ffi.cast(
- "ASN1_OCTET_STRING *", asn1_string
- )
- asn1_string = self._backend._ffi.gc(
- asn1_string, self._backend._lib.ASN1_OCTET_STRING_free
- )
- return x509.SubjectKeyIdentifier(
- self._backend._ffi.buffer(asn1_string.data, asn1_string.length)[:]
+ if un.exptext != backend._ffi.NULL:
+ explicit_text = _asn1_string_to_utf8(backend, un.exptext)
+
+ if un.noticeref != backend._ffi.NULL:
+ organization = _asn1_string_to_utf8(
+ backend, un.noticeref.organization
)
- def _build_authority_key_identifier(self, ext):
- akid = self._backend._lib.X509V3_EXT_d2i(ext)
- assert akid != self._backend._ffi.NULL
- akid = self._backend._ffi.cast("AUTHORITY_KEYID *", akid)
- akid = self._backend._ffi.gc(
- akid, self._backend._lib.AUTHORITY_KEYID_free
+ num = backend._lib.sk_ASN1_INTEGER_num(
+ un.noticeref.noticenos
)
- key_identifier = None
- authority_cert_issuer = None
- authority_cert_serial_number = None
-
- if akid.keyid != self._backend._ffi.NULL:
- key_identifier = self._backend._ffi.buffer(
- akid.keyid.data, akid.keyid.length
- )[:]
-
- if akid.issuer != self._backend._ffi.NULL:
- authority_cert_issuer = _build_general_names(
- self._backend, akid.issuer
+ notice_numbers = []
+ for i in range(num):
+ asn1_int = backend._lib.sk_ASN1_INTEGER_value(
+ un.noticeref.noticenos, i
)
-
- if akid.serial != self._backend._ffi.NULL:
- authority_cert_serial_number = _asn1_integer_to_int(
- self._backend, akid.serial
+ notice_num = _asn1_integer_to_int(
+ backend, asn1_int
)
+ notice_numbers.append(notice_num)
- return x509.AuthorityKeyIdentifier(
- key_identifier, authority_cert_issuer, authority_cert_serial_number
+ notice_reference = x509.NoticeReference(
+ organization, notice_numbers
)
- def _build_authority_information_access(self, ext):
- aia = self._backend._lib.X509V3_EXT_d2i(ext)
- assert aia != self._backend._ffi.NULL
- aia = self._backend._ffi.cast(
- "Cryptography_STACK_OF_ACCESS_DESCRIPTION *", aia
- )
- aia = self._backend._ffi.gc(
- aia, self._backend._lib.sk_ACCESS_DESCRIPTION_free
- )
- num = self._backend._lib.sk_ACCESS_DESCRIPTION_num(aia)
- access_descriptions = []
- for i in range(num):
- ad = self._backend._lib.sk_ACCESS_DESCRIPTION_value(aia, i)
- assert ad.method != self._backend._ffi.NULL
- oid = x509.ObjectIdentifier(_obj2txt(self._backend, ad.method))
- assert ad.location != self._backend._ffi.NULL
- gn = _build_general_name(self._backend, ad.location)
- access_descriptions.append(x509.AccessDescription(oid, gn))
-
- return x509.AuthorityInformationAccess(access_descriptions)
-
- def _build_key_usage(self, ext):
- bit_string = self._backend._lib.X509V3_EXT_d2i(ext)
- assert bit_string != self._backend._ffi.NULL
- bit_string = self._backend._ffi.cast("ASN1_BIT_STRING *", bit_string)
- bit_string = self._backend._ffi.gc(
- bit_string, self._backend._lib.ASN1_BIT_STRING_free
+ 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
+ )
+ basic_constraints = backend._ffi.gc(
+ basic_constraints, backend._lib.BASIC_CONSTRAINTS_free
+ )
+ # The byte representation of an ASN.1 boolean true is \xff. OpenSSL
+ # chooses to just map this to its ordinal value, so true is 255 and
+ # false is 0.
+ ca = basic_constraints.ca == 255
+ if basic_constraints.pathlen == backend._ffi.NULL:
+ path_length = None
+ else:
+ path_length = _asn1_integer_to_int(
+ backend, basic_constraints.pathlen
)
- get_bit = self._backend._lib.ASN1_BIT_STRING_get_bit
- digital_signature = get_bit(bit_string, 0) == 1
- content_commitment = get_bit(bit_string, 1) == 1
- key_encipherment = get_bit(bit_string, 2) == 1
- data_encipherment = get_bit(bit_string, 3) == 1
- key_agreement = get_bit(bit_string, 4) == 1
- key_cert_sign = get_bit(bit_string, 5) == 1
- crl_sign = get_bit(bit_string, 6) == 1
- encipher_only = get_bit(bit_string, 7) == 1
- decipher_only = get_bit(bit_string, 8) == 1
- return x509.KeyUsage(
- digital_signature,
- content_commitment,
- key_encipherment,
- data_encipherment,
- key_agreement,
- key_cert_sign,
- crl_sign,
- encipher_only,
- decipher_only
+
+ 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
+ )
+ asn1_string = backend._ffi.gc(
+ asn1_string, backend._lib.ASN1_OCTET_STRING_free
+ )
+ return x509.SubjectKeyIdentifier(
+ backend._ffi.buffer(asn1_string.data, asn1_string.length)[:]
+ )
+
+
+def _decode_authority_key_identifier(backend, ext):
+ akid = backend._lib.X509V3_EXT_d2i(ext)
+ assert akid != backend._ffi.NULL
+ akid = backend._ffi.cast("AUTHORITY_KEYID *", akid)
+ akid = backend._ffi.gc(
+ akid, backend._lib.AUTHORITY_KEYID_free
+ )
+ key_identifier = None
+ authority_cert_issuer = None
+ authority_cert_serial_number = None
+
+ if akid.keyid != backend._ffi.NULL:
+ key_identifier = backend._ffi.buffer(
+ akid.keyid.data, akid.keyid.length
+ )[:]
+
+ if akid.issuer != backend._ffi.NULL:
+ authority_cert_issuer = _decode_general_names(
+ backend, akid.issuer
)
- def _build_subject_alt_name(self, ext):
- gns = self._backend._ffi.cast(
- "GENERAL_NAMES *", self._backend._lib.X509V3_EXT_d2i(ext)
+ if akid.serial != backend._ffi.NULL:
+ authority_cert_serial_number = _asn1_integer_to_int(
+ backend, akid.serial
)
- assert gns != self._backend._ffi.NULL
- gns = self._backend._ffi.gc(gns, self._backend._lib.GENERAL_NAMES_free)
- general_names = _build_general_names(self._backend, gns)
- return x509.SubjectAlternativeName(general_names)
+ return x509.AuthorityKeyIdentifier(
+ key_identifier, authority_cert_issuer, authority_cert_serial_number
+ )
- def _build_extended_key_usage(self, ext):
- sk = self._backend._ffi.cast(
- "Cryptography_STACK_OF_ASN1_OBJECT *",
- self._backend._lib.X509V3_EXT_d2i(ext)
- )
- assert sk != self._backend._ffi.NULL
- sk = self._backend._ffi.gc(sk, self._backend._lib.sk_ASN1_OBJECT_free)
- num = self._backend._lib.sk_ASN1_OBJECT_num(sk)
- ekus = []
- for i in range(num):
- obj = self._backend._lib.sk_ASN1_OBJECT_value(sk, i)
- assert obj != self._backend._ffi.NULL
- oid = x509.ObjectIdentifier(_obj2txt(self._backend, obj))
- ekus.append(oid)
+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
+ )
+ num = backend._lib.sk_ACCESS_DESCRIPTION_num(aia)
+ access_descriptions = []
+ for i in range(num):
+ ad = backend._lib.sk_ACCESS_DESCRIPTION_value(aia, i)
+ assert ad.method != backend._ffi.NULL
+ oid = x509.ObjectIdentifier(_obj2txt(backend, ad.method))
+ assert ad.location != backend._ffi.NULL
+ gn = _decode_general_name(backend, ad.location)
+ access_descriptions.append(x509.AccessDescription(oid, gn))
+
+ 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
+ bit_string = backend._ffi.cast("ASN1_BIT_STRING *", bit_string)
+ 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
+ key_encipherment = get_bit(bit_string, 2) == 1
+ data_encipherment = get_bit(bit_string, 3) == 1
+ key_agreement = get_bit(bit_string, 4) == 1
+ key_cert_sign = get_bit(bit_string, 5) == 1
+ crl_sign = get_bit(bit_string, 6) == 1
+ encipher_only = get_bit(bit_string, 7) == 1
+ decipher_only = get_bit(bit_string, 8) == 1
+ return x509.KeyUsage(
+ digital_signature,
+ content_commitment,
+ key_encipherment,
+ data_encipherment,
+ key_agreement,
+ key_cert_sign,
+ crl_sign,
+ encipher_only,
+ decipher_only
+ )
- return x509.ExtendedKeyUsage(ekus)
- def _build_crl_distribution_points(self, ext):
- cdps = self._backend._ffi.cast(
- "Cryptography_STACK_OF_DIST_POINT *",
- self._backend._lib.X509V3_EXT_d2i(ext)
- )
- assert cdps != self._backend._ffi.NULL
- cdps = self._backend._ffi.gc(
- cdps, self._backend._lib.sk_DIST_POINT_free)
- num = self._backend._lib.sk_DIST_POINT_num(cdps)
+def _decode_subject_alt_name(backend, ext):
+ gns = backend._ffi.cast(
+ "GENERAL_NAMES *", backend._lib.X509V3_EXT_d2i(ext)
+ )
+ assert gns != backend._ffi.NULL
+ gns = backend._ffi.gc(gns, backend._lib.GENERAL_NAMES_free)
+ general_names = _decode_general_names(backend, gns)
- dist_points = []
- for i in range(num):
- full_name = None
- relative_name = None
- crl_issuer = None
- reasons = None
- cdp = self._backend._lib.sk_DIST_POINT_value(cdps, i)
- if cdp.reasons != self._backend._ffi.NULL:
- # We will check each bit from RFC 5280
- # ReasonFlags ::= BIT STRING {
- # unused (0),
- # keyCompromise (1),
- # cACompromise (2),
- # affiliationChanged (3),
- # superseded (4),
- # cessationOfOperation (5),
- # certificateHold (6),
- # privilegeWithdrawn (7),
- # aACompromise (8) }
- reasons = []
- get_bit = self._backend._lib.ASN1_BIT_STRING_get_bit
- if get_bit(cdp.reasons, 1):
- reasons.append(x509.ReasonFlags.key_compromise)
-
- if get_bit(cdp.reasons, 2):
- reasons.append(x509.ReasonFlags.ca_compromise)
-
- if get_bit(cdp.reasons, 3):
- reasons.append(x509.ReasonFlags.affiliation_changed)
-
- if get_bit(cdp.reasons, 4):
- reasons.append(x509.ReasonFlags.superseded)
-
- if get_bit(cdp.reasons, 5):
- reasons.append(x509.ReasonFlags.cessation_of_operation)
-
- if get_bit(cdp.reasons, 6):
- reasons.append(x509.ReasonFlags.certificate_hold)
-
- if get_bit(cdp.reasons, 7):
- reasons.append(x509.ReasonFlags.privilege_withdrawn)
-
- if get_bit(cdp.reasons, 8):
- reasons.append(x509.ReasonFlags.aa_compromise)
-
- reasons = frozenset(reasons)
-
- if cdp.CRLissuer != self._backend._ffi.NULL:
- crl_issuer = _build_general_names(self._backend, cdp.CRLissuer)
-
- # Certificates may have a crl_issuer/reasons and no distribution
- # point so make sure it's not null.
- if cdp.distpoint != self._backend._ffi.NULL:
- # Type 0 is fullName, there is no #define for it in the code.
- if cdp.distpoint.type == 0:
- full_name = _build_general_names(
- self._backend, cdp.distpoint.name.fullname
- )
- # OpenSSL code doesn't test for a specific type for
- # relativename, everything that isn't fullname is considered
- # relativename.
- else:
- rns = cdp.distpoint.name.relativename
- rnum = self._backend._lib.sk_X509_NAME_ENTRY_num(rns)
- attributes = []
- for i in range(rnum):
- rn = self._backend._lib.sk_X509_NAME_ENTRY_value(
- rns, i
- )
- assert rn != self._backend._ffi.NULL
- attributes.append(
- _build_x509_name_entry(self._backend, rn)
- )
-
- relative_name = x509.Name(attributes)
-
- dist_points.append(
- x509.DistributionPoint(
- full_name, relative_name, reasons, crl_issuer
+ return x509.SubjectAlternativeName(general_names)
+
+
+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
+ sk = backend._ffi.gc(sk, backend._lib.sk_ASN1_OBJECT_free)
+ num = backend._lib.sk_ASN1_OBJECT_num(sk)
+ ekus = []
+
+ for i in range(num):
+ obj = backend._lib.sk_ASN1_OBJECT_value(sk, i)
+ assert obj != backend._ffi.NULL
+ oid = x509.ObjectIdentifier(_obj2txt(backend, obj))
+ ekus.append(oid)
+
+ 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)
+ num = backend._lib.sk_DIST_POINT_num(cdps)
+
+ dist_points = []
+ for i in range(num):
+ full_name = None
+ relative_name = None
+ crl_issuer = None
+ reasons = None
+ cdp = backend._lib.sk_DIST_POINT_value(cdps, i)
+ if cdp.reasons != backend._ffi.NULL:
+ # We will check each bit from RFC 5280
+ # ReasonFlags ::= BIT STRING {
+ # unused (0),
+ # keyCompromise (1),
+ # cACompromise (2),
+ # affiliationChanged (3),
+ # superseded (4),
+ # cessationOfOperation (5),
+ # certificateHold (6),
+ # privilegeWithdrawn (7),
+ # aACompromise (8) }
+ reasons = []
+ get_bit = backend._lib.ASN1_BIT_STRING_get_bit
+ if get_bit(cdp.reasons, 1):
+ reasons.append(x509.ReasonFlags.key_compromise)
+
+ if get_bit(cdp.reasons, 2):
+ reasons.append(x509.ReasonFlags.ca_compromise)
+
+ if get_bit(cdp.reasons, 3):
+ reasons.append(x509.ReasonFlags.affiliation_changed)
+
+ if get_bit(cdp.reasons, 4):
+ reasons.append(x509.ReasonFlags.superseded)
+
+ if get_bit(cdp.reasons, 5):
+ reasons.append(x509.ReasonFlags.cessation_of_operation)
+
+ if get_bit(cdp.reasons, 6):
+ reasons.append(x509.ReasonFlags.certificate_hold)
+
+ if get_bit(cdp.reasons, 7):
+ reasons.append(x509.ReasonFlags.privilege_withdrawn)
+
+ if get_bit(cdp.reasons, 8):
+ reasons.append(x509.ReasonFlags.aa_compromise)
+
+ reasons = frozenset(reasons)
+
+ if cdp.CRLissuer != backend._ffi.NULL:
+ crl_issuer = _decode_general_names(backend, cdp.CRLissuer)
+
+ # Certificates may have a crl_issuer/reasons and no distribution
+ # point so make sure it's not null.
+ if cdp.distpoint != backend._ffi.NULL:
+ # Type 0 is fullName, there is no #define for it in the code.
+ if cdp.distpoint.type == 0:
+ full_name = _decode_general_names(
+ backend, cdp.distpoint.name.fullname
)
+ # OpenSSL code doesn't test for a specific type for
+ # relativename, everything that isn't fullname is considered
+ # relativename.
+ else:
+ rns = cdp.distpoint.name.relativename
+ rnum = backend._lib.sk_X509_NAME_ENTRY_num(rns)
+ attributes = []
+ for i in range(rnum):
+ rn = backend._lib.sk_X509_NAME_ENTRY_value(
+ rns, i
+ )
+ assert rn != backend._ffi.NULL
+ attributes.append(
+ _decode_x509_name_entry(backend, rn)
+ )
+
+ relative_name = x509.Name(attributes)
+
+ dist_points.append(
+ x509.DistributionPoint(
+ full_name, relative_name, reasons, crl_issuer
)
+ )
- return x509.CRLDistributionPoints(dist_points)
+ return x509.CRLDistributionPoints(dist_points)
@utils.register_interface(x509.CertificateSigningRequest)
@@ -632,7 +646,7 @@ class _CertificateSigningRequest(object):
def subject(self):
subject = self._backend._lib.X509_REQ_get_subject_name(self._x509_req)
assert subject != self._backend._ffi.NULL
- return _build_x509_name(self._backend, subject)
+ return _decode_x509_name(self._backend, subject)
@property
def signature_hash_algorithm(self):
@@ -643,3 +657,35 @@ class _CertificateSigningRequest(object):
raise UnsupportedAlgorithm(
"Signature algorithm OID:{0} not recognized".format(oid)
)
+
+ @property
+ def extensions(self):
+ extensions = []
+ seen_oids = set()
+ x509_exts = self._backend._lib.X509_REQ_get_extensions(self._x509_req)
+ extcount = self._backend._lib.sk_X509_EXTENSION_num(x509_exts)
+ for i in range(0, extcount):
+ ext = self._backend._lib.sk_X509_EXTENSION_value(x509_exts, i)
+ assert ext != self._backend._ffi.NULL
+ crit = self._backend._lib.X509_EXTENSION_get_critical(ext)
+ critical = crit == 1
+ oid = x509.ObjectIdentifier(_obj2txt(self._backend, ext.object))
+ if oid in seen_oids:
+ raise x509.DuplicateExtension(
+ "Duplicate {0} extension found".format(oid), oid
+ )
+ elif oid == x509.OID_BASIC_CONSTRAINTS:
+ value = _decode_basic_constraints(self._backend, ext)
+ elif critical:
+ raise x509.UnsupportedExtension(
+ "{0} is not currently supported".format(oid), oid
+ )
+ else:
+ # Unsupported non-critical extension, silently skipping for now
+ seen_oids.add(oid)
+ continue
+
+ seen_oids.add(oid)
+ extensions.append(x509.Extension(oid, critical, value))
+
+ return x509.Extensions(extensions)
diff --git a/src/cryptography/x509.py b/src/cryptography/x509.py
index 71ba9042..7ac06622 100644
--- a/src/cryptography/x509.py
+++ b/src/cryptography/x509.py
@@ -278,6 +278,19 @@ class Extension(object):
return ("<Extension(oid={0.oid}, critical={0.critical}, "
"value={0.value})>").format(self)
+ def __eq__(self, other):
+ if not isinstance(other, Extension):
+ return NotImplemented
+
+ return (
+ self.oid == other.oid and
+ self.critical == other.critical and
+ self.value == other.value
+ )
+
+ def __ne__(self, other):
+ return not self == other
+
class ExtendedKeyUsage(object):
def __init__(self, usages):
@@ -307,6 +320,10 @@ class ExtendedKeyUsage(object):
return not self == other
+class OCSPNoCheck(object):
+ pass
+
+
class BasicConstraints(object):
def __init__(self, ca, path_length):
if not isinstance(ca, bool):
@@ -896,11 +913,18 @@ class RegisteredID(object):
class IPAddress(object):
def __init__(self, value):
if not isinstance(
- value, (ipaddress.IPv4Address, ipaddress.IPv6Address)
+ value,
+ (
+ ipaddress.IPv4Address,
+ ipaddress.IPv6Address,
+ ipaddress.IPv4Network,
+ ipaddress.IPv6Network
+ )
):
raise TypeError(
- "value must be an instance of ipaddress.IPv4Address or "
- "ipaddress.IPv6Address"
+ "value must be an instance of ipaddress.IPv4Address, "
+ "ipaddress.IPv6Address, ipaddress.IPv4Network, or "
+ "ipaddress.IPv6Network"
)
self._value = value
@@ -1125,6 +1149,12 @@ class Certificate(object):
in the certificate.
"""
+ @abc.abstractproperty
+ def extensions(self):
+ """
+ Returns an Extensions object.
+ """
+
@abc.abstractmethod
def __eq__(self, other):
"""
@@ -1158,3 +1188,9 @@ class CertificateSigningRequest(object):
Returns a HashAlgorithm corresponding to the type of the digest signed
in the certificate.
"""
+
+ @abc.abstractproperty
+ def extensions(self):
+ """
+ Returns the extensions in the signing request.
+ """
diff --git a/tests/test_x509.py b/tests/test_x509.py
index 8561f1f4..47c1c647 100644
--- a/tests/test_x509.py
+++ b/tests/test_x509.py
@@ -395,6 +395,9 @@ class TestRSACertificate(object):
x509.NameAttribute(x509.OID_ORGANIZATION_NAME, 'PyCA'),
x509.NameAttribute(x509.OID_COMMON_NAME, 'cryptography.io'),
]
+ extensions = request.extensions
+ assert isinstance(extensions, x509.Extensions)
+ assert list(extensions) == []
@pytest.mark.parametrize(
"loader_func",
@@ -413,6 +416,61 @@ class TestRSACertificate(object):
with pytest.raises(UnsupportedAlgorithm):
request.signature_hash_algorithm
+ def test_duplicate_extension(self, backend):
+ request = _load_cert(
+ os.path.join(
+ "x509", "requests", "two_basic_constraints.pem"
+ ),
+ x509.load_pem_x509_csr,
+ backend
+ )
+ with pytest.raises(x509.DuplicateExtension) as exc:
+ request.extensions
+
+ assert exc.value.oid == x509.OID_BASIC_CONSTRAINTS
+
+ def test_unsupported_critical_extension(self, backend):
+ request = _load_cert(
+ os.path.join(
+ "x509", "requests", "unsupported_extension_critical.pem"
+ ),
+ x509.load_pem_x509_csr,
+ backend
+ )
+ with pytest.raises(x509.UnsupportedExtension) as exc:
+ request.extensions
+
+ assert exc.value.oid == x509.ObjectIdentifier('1.2.3.4')
+
+ def test_unsupported_extension(self, backend):
+ request = _load_cert(
+ os.path.join(
+ "x509", "requests", "unsupported_extension.pem"
+ ),
+ x509.load_pem_x509_csr,
+ backend
+ )
+ extensions = request.extensions
+ assert len(extensions) == 0
+
+ def test_request_basic_constraints(self, backend):
+ request = _load_cert(
+ os.path.join(
+ "x509", "requests", "basic_constraints.pem"
+ ),
+ x509.load_pem_x509_csr,
+ backend
+ )
+ extensions = request.extensions
+ assert isinstance(extensions, x509.Extensions)
+ assert list(extensions) == [
+ x509.Extension(
+ x509.OID_BASIC_CONSTRAINTS,
+ True,
+ x509.BasicConstraints(True, 1),
+ ),
+ ]
+
@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 72f2f9e4..d3488a9f 100644
--- a/tests/test_x509_ext.py
+++ b/tests/test_x509_ext.py
@@ -38,6 +38,33 @@ class TestExtension(object):
"_length=None)>)>"
)
+ def test_eq(self):
+ ext1 = x509.Extension(
+ x509.ObjectIdentifier('1.2.3.4'), False, 'value'
+ )
+ ext2 = x509.Extension(
+ x509.ObjectIdentifier('1.2.3.4'), False, 'value'
+ )
+ assert ext1 == ext2
+
+ def test_ne(self):
+ ext1 = x509.Extension(
+ x509.ObjectIdentifier('1.2.3.4'), False, 'value'
+ )
+ ext2 = x509.Extension(
+ x509.ObjectIdentifier('1.2.3.5'), False, 'value'
+ )
+ ext3 = x509.Extension(
+ x509.ObjectIdentifier('1.2.3.4'), True, 'value'
+ )
+ ext4 = x509.Extension(
+ x509.ObjectIdentifier('1.2.3.4'), False, 'value4'
+ )
+ assert ext1 != ext2
+ assert ext1 != ext3
+ assert ext1 != ext4
+ assert ext1 != object()
+
class TestNoticeReference(object):
def test_notice_numbers_not_all_int(self):
@@ -1094,6 +1121,12 @@ class TestIPAddress(object):
gn2 = x509.IPAddress(ipaddress.IPv6Address(u"ff::"))
assert repr(gn2) == "<IPAddress(value=ff::)>"
+ gn3 = x509.IPAddress(ipaddress.IPv4Network(u"192.168.0.0/24"))
+ assert repr(gn3) == "<IPAddress(value=192.168.0.0/24)>"
+
+ gn4 = x509.IPAddress(ipaddress.IPv6Network(u"ff::/96"))
+ assert repr(gn4) == "<IPAddress(value=ff::/96)>"
+
def test_eq(self):
gn = x509.IPAddress(ipaddress.IPv4Address(u"127.0.0.1"))
gn2 = x509.IPAddress(ipaddress.IPv4Address(u"127.0.0.1"))
diff --git a/vectors/cryptography_vectors/x509/requests/basic_constraints.pem b/vectors/cryptography_vectors/x509/requests/basic_constraints.pem
new file mode 100644
index 00000000..7169cda7
--- /dev/null
+++ b/vectors/cryptography_vectors/x509/requests/basic_constraints.pem
@@ -0,0 +1,64 @@
+Certificate Request:
+ Data:
+ Version: 2 (0x2)
+ Subject: C=US, ST=Texas, L=Austin, O=PyCA, CN=cryptography.io
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ RSA Public Key: (2048 bit)
+ Modulus (2048 bit):
+ 00:a4:1a:ae:63:a2:ad:23:0a:b7:a2:f0:0e:50:fd:
+ 96:e1:02:96:05:07:72:c8:96:a7:d6:a9:f6:19:fd:
+ 61:98:9a:ca:98:5c:41:69:0c:f2:f8:27:f2:c4:7d:
+ fe:6d:08:9e:f7:99:2c:f7:ad:45:97:97:c3:eb:4c:
+ d7:03:e1:32:27:77:be:73:74:3b:62:1f:66:c7:83:
+ 3c:44:45:76:a8:f1:34:00:84:fd:43:f8:2e:95:00:
+ 2f:34:f5:c9:d6:67:44:e1:65:42:42:c1:53:5f:7c:
+ 47:b2:65:a6:d5:ad:c8:9a:13:0b:58:c4:b9:42:c7:
+ 4d:76:c0:99:57:9d:b0:33:ec:f8:dd:a3:61:86:1f:
+ df:bd:d8:56:ae:0d:bf:41:c8:b1:69:8a:2a:df:cf:
+ 2a:05:25:8a:34:6c:32:7e:8c:73:fa:41:1a:57:90:
+ 3d:5f:89:e1:b4:c6:bf:96:50:b7:62:fc:f7:20:13:
+ 6d:25:cc:b0:4f:3d:4b:85:d5:31:4a:a2:33:f9:f5:
+ 1f:e9:c1:e8:03:29:b3:df:65:51:80:27:2b:3e:d3:
+ 83:b2:fa:80:1b:03:43:56:07:26:f0:e9:58:64:bb:
+ dc:7e:fd:c1:88:e1:10:54:4e:a3:f6:67:6c:c9:54:
+ 3d:9c:8d:62:43:be:31:77:d4:c3:ee:39:3c:b0:eb:
+ e5:ab
+ Exponent: 65537 (0x10001)
+ Attributes:
+ Requested Extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:1
+ Signature Algorithm: sha1WithRSAEncryption
+ 0a:8a:70:98:1c:68:65:04:bb:6b:0c:93:90:03:e8:94:21:08:
+ 1d:af:e6:59:2a:27:b1:f7:80:c3:aa:0a:dd:8b:07:67:7e:cf:
+ ac:99:c7:c9:70:d8:f2:13:32:25:b9:03:7d:b7:37:da:f4:d6:
+ 43:00:be:80:fd:7d:6d:05:f7:a0:e8:3e:69:8a:b7:44:46:3c:
+ 58:87:28:72:1e:eb:31:50:26:25:39:0e:57:85:b5:28:a2:6a:
+ 0d:f5:88:70:f4:bc:81:d6:87:4e:f2:ca:64:1b:86:d5:04:2d:
+ 1e:d6:32:00:23:04:8b:b0:9a:a9:8c:5b:60:2d:9e:ea:57:7a:
+ d2:e3:b4:f0:f4:0c:08:54:af:91:e3:b4:61:51:91:7e:60:4f:
+ 0a:6e:db:65:38:1a:4f:35:07:e7:08:0d:0a:39:3e:7b:4a:bf:
+ 03:f6:6e:5b:f4:47:95:53:22:21:b9:91:db:0e:76:f1:0f:6f:
+ 82:a5:0f:b7:65:cf:19:12:9e:67:4e:5f:c1:b2:a7:02:d7:e4:
+ 6a:55:de:35:52:32:4d:45:ab:b3:fc:82:3d:6d:65:9c:be:6c:
+ 81:9a:10:9a:22:f8:75:de:9c:f4:61:de:6c:82:3a:5f:51:f4:
+ 7b:b7:14:68:0b:ac:2b:16:76:46:5e:3c:bb:03:dd:dc:12:17:
+ 70:06:4b:3c
+-----BEGIN CERTIFICATE REQUEST-----
+MIICwTCCAakCAQIwVzELMAkGA1UEBhMCVVMxDjAMBgNVBAgMBVRleGFzMQ8wDQYD
+VQQHDAZBdXN0aW4xDTALBgNVBAoMBFB5Q0ExGDAWBgNVBAMMD2NyeXB0b2dyYXBo
+eS5pbzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKQarmOirSMKt6Lw
+DlD9luEClgUHcsiWp9ap9hn9YZiayphcQWkM8vgn8sR9/m0InveZLPetRZeXw+tM
+1wPhMid3vnN0O2IfZseDPERFdqjxNACE/UP4LpUALzT1ydZnROFlQkLBU198R7Jl
+ptWtyJoTC1jEuULHTXbAmVedsDPs+N2jYYYf373YVq4Nv0HIsWmKKt/PKgUlijRs
+Mn6Mc/pBGleQPV+J4bTGv5ZQt2L89yATbSXMsE89S4XVMUqiM/n1H+nB6AMps99l
+UYAnKz7Tg7L6gBsDQ1YHJvDpWGS73H79wYjhEFROo/ZnbMlUPZyNYkO+MXfUw+45
+PLDr5asCAwEAAaAlMCMGCSqGSIb3DQEJDjEWMBQwEgYDVR0TAQH/BAgwBgEB/wIB
+ATANBgkqhkiG9w0BAQUFAAOCAQEACopwmBxoZQS7awyTkAPolCEIHa/mWSonsfeA
+w6oK3YsHZ37PrJnHyXDY8hMyJbkDfbc32vTWQwC+gP19bQX3oOg+aYq3REY8WIco
+ch7rMVAmJTkOV4W1KKJqDfWIcPS8gdaHTvLKZBuG1QQtHtYyACMEi7CaqYxbYC2e
+6ld60uO08PQMCFSvkeO0YVGRfmBPCm7bZTgaTzUH5wgNCjk+e0q/A/ZuW/RHlVMi
+IbmR2w528Q9vgqUPt2XPGRKeZ05fwbKnAtfkalXeNVIyTUWrs/yCPW1lnL5sgZoQ
+miL4dd6c9GHebII6X1H0e7cUaAusKxZ2Rl48uwPd3BIXcAZLPA==
+-----END CERTIFICATE REQUEST-----
diff --git a/vectors/cryptography_vectors/x509/requests/two_basic_constraints.pem b/vectors/cryptography_vectors/x509/requests/two_basic_constraints.pem
new file mode 100644
index 00000000..da23c06e
--- /dev/null
+++ b/vectors/cryptography_vectors/x509/requests/two_basic_constraints.pem
@@ -0,0 +1,66 @@
+Certificate Request:
+ Data:
+ Version: 2 (0x2)
+ Subject: C=US, ST=Texas, L=Austin, O=PyCA, CN=cryptography.io
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ RSA Public Key: (2048 bit)
+ Modulus (2048 bit):
+ 00:cc:72:54:2d:83:be:73:f5:9a:60:3f:b8:bd:78:
+ 7d:f4:3d:6e:31:38:a9:26:72:86:19:14:87:0d:f4:
+ 68:97:19:2f:d7:7c:80:45:ad:38:27:59:db:57:76:
+ a5:f3:b1:5e:34:5f:43:63:e5:24:0a:0f:c6:ab:39:
+ 69:d1:1a:87:68:8c:98:02:3a:08:1c:25:d7:65:30:
+ c3:b6:a9:fa:df:ca:9d:4b:4c:b3:00:a7:fa:ea:de:
+ ad:99:d2:67:5f:c4:15:0d:b3:70:4d:b2:0a:a7:dd:
+ 35:67:35:b9:4f:a8:b2:f6:a6:22:26:b6:ae:e8:ed:
+ 2d:e7:82:7e:a5:f2:2c:05:d5:03:5a:2e:1e:d0:62:
+ 79:04:55:4b:b3:99:94:8b:9f:6a:5e:ee:6e:b3:d4:
+ f0:d4:9a:82:ce:e3:f4:81:5c:b2:db:24:85:b8:e4:
+ 1c:6f:a8:3d:4e:91:27:6a:fe:48:29:f2:09:40:9e:
+ a1:e2:34:41:18:29:ce:f5:24:11:d9:22:2f:6f:94:
+ cf:22:4e:ba:f2:38:90:00:70:d5:86:d4:6d:c2:ce:
+ 6c:32:74:75:df:f1:1f:c9:a0:be:43:2b:84:34:db:
+ 34:2f:d3:15:8f:ff:85:62:40:79:d3:3c:04:a2:b8:
+ 43:34:ae:c2:3e:1d:ad:c0:4b:28:34:c2:6d:7e:e2:
+ 9f:75
+ Exponent: 65537 (0x10001)
+ Attributes:
+ Requested Extensions:
+ X509v3 Basic Constraints: critical
+ CA:FALSE
+ X509v3 Basic Constraints: critical
+ CA:FALSE
+ Signature Algorithm: sha1WithRSAEncryption
+ b8:00:de:3c:28:bf:56:9a:a7:8f:50:a3:86:a3:02:91:8b:97:
+ 1c:b8:73:81:c2:fd:85:d7:6f:ba:b1:c3:18:8a:17:d9:66:cd:
+ b9:9a:9c:1f:c8:0b:88:33:b7:4e:97:b2:60:43:ea:13:57:13:
+ 17:7c:23:7d:22:6e:65:b0:0a:bc:dc:12:ec:b3:85:2f:1b:c9:
+ ef:9c:19:f3:15:fd:78:89:a6:d1:2d:b8:bf:b6:17:b8:dc:b5:
+ 7a:e6:2a:4d:2c:da:01:10:31:96:12:13:49:08:1b:d9:ba:97:
+ 54:e4:21:b8:50:92:9d:1f:30:f0:a2:de:99:8e:da:0e:1f:84:
+ d4:22:2a:f6:d4:3b:43:81:25:ca:2a:e2:17:f6:ef:2f:db:df:
+ 67:dc:0f:1b:36:ac:46:b4:39:3b:d6:17:1a:12:fb:5f:1d:28:
+ db:9f:66:38:64:b7:43:ab:84:49:11:3b:ae:f1:30:cf:79:7e:
+ a6:52:ff:91:cb:9c:53:09:44:89:83:cf:04:7b:3c:12:7b:8f:
+ 56:e7:48:9a:e5:2a:f3:1f:93:ec:07:5f:1d:f1:6d:59:ed:5e:
+ f6:6a:be:63:60:02:f4:65:34:fb:dc:0a:1b:b3:99:b5:4b:4f:
+ 66:55:35:d3:79:85:48:7e:ca:0e:06:0f:92:00:27:93:79:ce:
+ f7:2f:ad:2b
+-----BEGIN CERTIFICATE REQUEST-----
+MIICyTCCAbECAQIwVzELMAkGA1UEBhMCVVMxDjAMBgNVBAgMBVRleGFzMQ8wDQYD
+VQQHDAZBdXN0aW4xDTALBgNVBAoMBFB5Q0ExGDAWBgNVBAMMD2NyeXB0b2dyYXBo
+eS5pbzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMxyVC2DvnP1mmA/
+uL14ffQ9bjE4qSZyhhkUhw30aJcZL9d8gEWtOCdZ21d2pfOxXjRfQ2PlJAoPxqs5
+adEah2iMmAI6CBwl12Uww7ap+t/KnUtMswCn+urerZnSZ1/EFQ2zcE2yCqfdNWc1
+uU+osvamIia2rujtLeeCfqXyLAXVA1ouHtBieQRVS7OZlIufal7ubrPU8NSags7j
+9IFcstskhbjkHG+oPU6RJ2r+SCnyCUCeoeI0QRgpzvUkEdkiL2+UzyJOuvI4kABw
+1YbUbcLObDJ0dd/xH8mgvkMrhDTbNC/TFY//hWJAedM8BKK4QzSuwj4drcBLKDTC
+bX7in3UCAwEAAaAtMCsGCSqGSIb3DQEJDjEeMBwwDAYDVR0TAQH/BAIwADAMBgNV
+HRMBAf8EAjAAMA0GCSqGSIb3DQEBBQUAA4IBAQC4AN48KL9WmqePUKOGowKRi5cc
+uHOBwv2F12+6scMYihfZZs25mpwfyAuIM7dOl7JgQ+oTVxMXfCN9Im5lsAq83BLs
+s4UvG8nvnBnzFf14iabRLbi/the43LV65ipNLNoBEDGWEhNJCBvZupdU5CG4UJKd
+HzDwot6ZjtoOH4TUIir21DtDgSXKKuIX9u8v299n3A8bNqxGtDk71hcaEvtfHSjb
+n2Y4ZLdDq4RJETuu8TDPeX6mUv+Ry5xTCUSJg88EezwSe49W50ia5SrzH5PsB18d
+8W1Z7V72ar5jYAL0ZTT73Aobs5m1S09mVTXTeYVIfsoOBg+SACeTec73L60r
+-----END CERTIFICATE REQUEST-----
diff --git a/vectors/cryptography_vectors/x509/requests/unsupported_extension.pem b/vectors/cryptography_vectors/x509/requests/unsupported_extension.pem
new file mode 100644
index 00000000..d96097c3
--- /dev/null
+++ b/vectors/cryptography_vectors/x509/requests/unsupported_extension.pem
@@ -0,0 +1,64 @@
+Certificate Request:
+ Data:
+ Version: 2 (0x2)
+ Subject: C=US, ST=Texas, L=Austin, O=PyCA, CN=cryptography.io
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ RSA Public Key: (2048 bit)
+ Modulus (2048 bit):
+ 00:b6:64:25:bd:fc:ba:bf:7b:ee:da:a6:25:79:75:
+ 59:59:cb:bc:da:eb:22:66:97:93:4d:f0:67:39:45:
+ 01:5c:58:0a:17:88:e4:05:14:c8:3f:33:39:5f:a0:
+ ca:fc:dd:28:3e:b1:d1:0d:87:c1:65:22:21:d3:4a:
+ 5c:f0:b6:4f:49:83:90:b1:88:47:b5:7f:7e:4f:34:
+ ae:51:a5:ee:fb:92:68:2d:0f:83:86:cb:8e:e7:1d:
+ ba:f4:6a:f2:ab:81:aa:fc:a4:05:fd:fd:07:88:59:
+ 6a:b7:c7:f5:df:92:e6:fd:ec:e7:a7:6f:ac:02:95:
+ b7:3f:01:39:82:8e:94:84:18:cf:07:1b:25:5e:94:
+ 56:ec:2e:b7:37:71:ba:09:d2:ef:a3:46:ec:38:34:
+ 1d:0f:f8:d1:1f:4f:11:bd:03:9e:c9:46:65:f7:6d:
+ 80:2d:32:5e:44:6f:e3:21:ec:c2:76:32:62:c2:9e:
+ 47:03:27:8b:ba:49:01:2b:61:1f:6d:7b:3c:05:11:
+ 84:bd:a8:bd:44:e2:30:1e:c2:19:fc:9c:60:79:29:
+ b9:62:92:97:94:d2:f7:30:84:b6:51:af:f1:9a:61:
+ 80:2e:f8:94:22:f7:73:32:20:79:b1:bf:ea:2d:20:
+ 20:48:51:3d:f1:51:03:b1:02:90:74:46:cf:3b:4e:
+ f8:4b
+ Exponent: 65537 (0x10001)
+ Attributes:
+ Requested Extensions:
+ 1.2.3.4:
+ value
+ Signature Algorithm: sha1WithRSAEncryption
+ 16:5f:86:90:13:fd:63:e6:c9:ca:74:68:b4:6e:e6:c5:c3:46:
+ c1:26:bc:64:2b:fc:ef:be:ab:eb:8b:a9:de:8d:4e:a8:f9:f0:
+ 3e:b0:0b:8c:e4:f8:0b:28:5b:13:0c:46:f8:3b:55:cb:cc:cb:
+ ed:6a:4f:16:3a:4b:e9:65:2d:3c:1a:a5:1f:a8:07:ab:22:ee:
+ 91:60:f1:06:76:0c:6e:8f:7b:25:36:4b:d6:60:04:77:e6:35:
+ 10:4f:eb:fc:2a:c3:71:e5:cb:9f:94:bd:6c:44:08:79:fb:b2:
+ a0:f5:f2:c0:79:b0:c4:22:ec:81:29:b3:97:e5:2f:1f:47:c5:
+ 1a:3f:be:50:c8:f4:29:9a:94:1d:19:a9:e2:d6:06:ca:07:43:
+ 6c:f1:e4:7e:fb:b8:70:0c:5b:41:c4:10:84:29:39:49:17:09:
+ d1:21:89:d7:c8:e5:6c:48:66:98:ac:8b:33:ab:da:1f:51:a9:
+ 2f:4c:39:6d:48:d9:7b:34:7f:b5:1e:9e:b8:87:8b:21:13:41:
+ d4:53:64:c1:16:e0:a8:c1:6f:dc:be:8f:67:ad:e6:30:79:af:
+ bf:7e:ff:64:99:50:d8:4c:58:66:9c:da:d1:53:06:2e:d3:82:
+ e3:2d:b3:65:71:6e:6a:67:cf:e1:96:4f:f7:ac:0b:2e:6e:28:
+ a4:df:f5:e6
+-----BEGIN CERTIFICATE REQUEST-----
+MIICuzCCAaMCAQIwVzELMAkGA1UEBhMCVVMxDjAMBgNVBAgMBVRleGFzMQ8wDQYD
+VQQHDAZBdXN0aW4xDTALBgNVBAoMBFB5Q0ExGDAWBgNVBAMMD2NyeXB0b2dyYXBo
+eS5pbzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALZkJb38ur977tqm
+JXl1WVnLvNrrImaXk03wZzlFAVxYCheI5AUUyD8zOV+gyvzdKD6x0Q2HwWUiIdNK
+XPC2T0mDkLGIR7V/fk80rlGl7vuSaC0Pg4bLjucduvRq8quBqvykBf39B4hZarfH
+9d+S5v3s56dvrAKVtz8BOYKOlIQYzwcbJV6UVuwutzdxugnS76NG7Dg0HQ/40R9P
+Eb0DnslGZfdtgC0yXkRv4yHswnYyYsKeRwMni7pJASthH217PAURhL2ovUTiMB7C
+GfycYHkpuWKSl5TS9zCEtlGv8ZphgC74lCL3czIgebG/6i0gIEhRPfFRA7ECkHRG
+zztO+EsCAwEAAaAfMB0GCSqGSIb3DQEJDjEQMA4wDAYDKgMEBAV2YWx1ZTANBgkq
+hkiG9w0BAQUFAAOCAQEAFl+GkBP9Y+bJynRotG7mxcNGwSa8ZCv8776r64up3o1O
+qPnwPrALjOT4CyhbEwxG+DtVy8zL7WpPFjpL6WUtPBqlH6gHqyLukWDxBnYMbo97
+JTZL1mAEd+Y1EE/r/CrDceXLn5S9bEQIefuyoPXywHmwxCLsgSmzl+UvH0fFGj++
+UMj0KZqUHRmp4tYGygdDbPHkfvu4cAxbQcQQhCk5SRcJ0SGJ18jlbEhmmKyLM6va
+H1GpL0w5bUjZezR/tR6euIeLIRNB1FNkwRbgqMFv3L6PZ63mMHmvv37/ZJlQ2ExY
+Zpza0VMGLtOC4y2zZXFuamfP4ZZP96wLLm4opN/15g==
+-----END CERTIFICATE REQUEST-----
diff --git a/vectors/cryptography_vectors/x509/requests/unsupported_extension_critical.pem b/vectors/cryptography_vectors/x509/requests/unsupported_extension_critical.pem
new file mode 100644
index 00000000..2ae17d8e
--- /dev/null
+++ b/vectors/cryptography_vectors/x509/requests/unsupported_extension_critical.pem
@@ -0,0 +1,64 @@
+Certificate Request:
+ Data:
+ Version: 2 (0x2)
+ Subject: C=US, ST=Texas, L=Austin, O=PyCA, CN=cryptography.io
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ RSA Public Key: (2048 bit)
+ Modulus (2048 bit):
+ 00:ce:ed:18:2c:b8:63:f6:65:50:1e:ec:7e:7b:86:
+ 56:25:3e:3d:5d:86:6e:9a:d1:56:b0:79:b7:85:c9:
+ 14:23:4d:ff:10:c7:68:f2:00:b6:57:39:a6:5d:3f:
+ ed:50:67:cd:bd:da:fb:68:a9:0f:79:a9:0e:79:23:
+ 62:d0:d1:27:96:f2:d6:36:b9:6d:4e:d6:4e:0f:32:
+ 88:9b:0b:90:98:31:00:fa:2e:ca:ac:82:e3:c3:60:
+ 1c:8e:f1:be:0d:1e:bc:b6:f7:a7:9b:60:aa:4b:9a:
+ a5:04:b5:a0:a8:29:0b:21:af:2c:40:26:87:75:85:
+ 3c:00:38:4d:a4:f9:25:8e:d5:d7:11:b4:dd:3c:27:
+ 11:0c:1c:1c:86:63:35:66:d6:47:73:6c:bd:00:03:
+ 2b:4a:d0:90:db:8c:e8:b4:8d:2a:ff:90:8f:0b:c8:
+ 32:ed:b3:23:7f:ce:01:74:26:f6:84:e6:53:54:44:
+ 45:e8:53:20:54:ec:00:6d:60:13:2b:bb:da:fe:19:
+ 82:db:a6:6c:72:fb:be:26:d2:9d:03:fd:7f:72:94:
+ 06:4e:25:d3:17:12:c5:2b:d6:a5:e1:29:3c:7b:d7:
+ da:da:89:bf:a9:09:41:ab:73:fb:8d:8b:3f:83:4b:
+ f1:bb:2b:5f:c6:a9:03:e6:c9:0a:fc:0a:f4:82:e1:
+ 4b:97
+ Exponent: 65537 (0x10001)
+ Attributes:
+ Requested Extensions:
+ 1.2.3.4: critical
+ value
+ Signature Algorithm: sha1WithRSAEncryption
+ 2c:70:0f:a6:d0:0d:70:24:a8:94:ad:4b:1d:50:46:19:7c:c0:
+ a8:fb:01:84:3b:3b:7e:b0:6f:6b:d9:86:81:a3:d4:03:e9:d7:
+ 0c:f6:ff:c6:43:00:88:59:7b:bc:8f:6d:3d:46:4d:a1:0b:40:
+ ba:7e:13:4e:4f:1d:02:35:e4:5b:30:a0:a8:fc:4d:49:a5:1b:
+ 11:19:57:25:58:57:03:09:55:56:cb:50:94:54:f9:15:a3:de:
+ ab:96:0d:b8:98:9d:0f:c7:16:e1:d6:0b:3b:7a:a2:53:07:d2:
+ 3c:f7:89:62:66:a4:34:39:c9:03:35:2b:a5:27:69:94:7d:56:
+ dc:72:8c:bc:3a:33:15:86:f8:c3:19:bb:c2:1d:51:3e:a9:1c:
+ 5c:8b:7a:63:18:1b:78:57:f4:14:be:39:90:38:d1:b6:8d:e1:
+ 45:63:1e:e1:32:54:3e:52:e9:5d:4d:d5:3c:65:b1:21:e3:00:
+ 88:f4:28:f7:34:f4:ac:08:54:59:4d:7b:b5:f4:84:d0:66:df:
+ 98:10:a3:38:bd:2c:e2:fa:87:7c:3f:c8:36:e6:a5:e1:b9:00:
+ 7d:c0:3a:40:69:b2:df:f9:c0:af:9f:e3:c6:48:a6:b6:69:0f:
+ e2:9e:36:dd:e8:ee:02:a1:10:1e:78:e6:c6:c3:b4:12:21:2d:
+ 70:4c:c0:b4
+-----BEGIN CERTIFICATE REQUEST-----
+MIICvjCCAaYCAQIwVzELMAkGA1UEBhMCVVMxDjAMBgNVBAgMBVRleGFzMQ8wDQYD
+VQQHDAZBdXN0aW4xDTALBgNVBAoMBFB5Q0ExGDAWBgNVBAMMD2NyeXB0b2dyYXBo
+eS5pbzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAM7tGCy4Y/ZlUB7s
+fnuGViU+PV2GbprRVrB5t4XJFCNN/xDHaPIAtlc5pl0/7VBnzb3a+2ipD3mpDnkj
+YtDRJ5by1ja5bU7WTg8yiJsLkJgxAPouyqyC48NgHI7xvg0evLb3p5tgqkuapQS1
+oKgpCyGvLEAmh3WFPAA4TaT5JY7V1xG03TwnEQwcHIZjNWbWR3NsvQADK0rQkNuM
+6LSNKv+QjwvIMu2zI3/OAXQm9oTmU1RERehTIFTsAG1gEyu72v4ZgtumbHL7vibS
+nQP9f3KUBk4l0xcSxSvWpeEpPHvX2tqJv6kJQatz+42LP4NL8bsrX8apA+bJCvwK
+9ILhS5cCAwEAAaAiMCAGCSqGSIb3DQEJDjETMBEwDwYDKgMEAQH/BAV2YWx1ZTAN
+BgkqhkiG9w0BAQUFAAOCAQEALHAPptANcCSolK1LHVBGGXzAqPsBhDs7frBva9mG
+gaPUA+nXDPb/xkMAiFl7vI9tPUZNoQtAun4TTk8dAjXkWzCgqPxNSaUbERlXJVhX
+AwlVVstQlFT5FaPeq5YNuJidD8cW4dYLO3qiUwfSPPeJYmakNDnJAzUrpSdplH1W
+3HKMvDozFYb4wxm7wh1RPqkcXIt6YxgbeFf0FL45kDjRto3hRWMe4TJUPlLpXU3V
+PGWxIeMAiPQo9zT0rAhUWU17tfSE0GbfmBCjOL0s4vqHfD/INual4bkAfcA6QGmy
+3/nAr5/jxkimtmkP4p423ejuAqEQHnjmxsO0EiEtcEzAtA==
+-----END CERTIFICATE REQUEST-----