Merge pull request #1727 from public/dh-key-iface-2015
DH key interfaces
diff --git a/.travis/install.sh b/.travis/install.sh
index 18deb8e..aacfc5a 100755
--- a/.travis/install.sh
+++ b/.travis/install.sh
@@ -68,7 +68,7 @@
fi
# Retry `update` on failure, some of the servers aren't super reliable.
- sudo apt-get -y update || sudo apt-get -y update
+ sudo apt-get -y update || sudo apt-get -y update || sudo apt-get -y update
if [[ "${OPENSSL}" == "0.9.8" ]]; then
sudo apt-get install -y --force-yes libssl-dev/lucid
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index b35dc14..15e8541 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -6,6 +6,12 @@
.. note:: This version is not yet released and is under active development.
+* Deprecated support for Python 2.6. At the time there is no time table for
+ actually dropping support, however we strongly encourage all users to upgrade
+ their Python, as Python 2.6 no longer receives support from the Python core
+ team.
+* Fixed compilation when using an OpenSSL which was compiled with the
+ ``no-comp`` (``OPENSSL_NO_COMP``) option.
* Support :attr:`~cryptography.hazmat.primitives.serialization.Encoding.DER`
serialization of public keys using the ``public_bytes`` method of
:class:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPublicKeyWithSerialization`,
@@ -18,6 +24,9 @@
:class:`~cryptography.hazmat.primitives.asymmetric.dsa.DSAPrivateKeyWithSerialization`,
and
:class:`~cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePrivateKeyWithSerialization`.
+* Add support for parsing X.509 certificate signing requests (CSRs) with
+ :func:`~cryptography.x509.load_pem_x509_csr` and
+ :func:`~cryptography.x509.load_der_x509_csr`.
0.8.1 - 2015-03-20
~~~~~~~~~~~~~~~~~~
diff --git a/docs/development/test-vectors.rst b/docs/development/test-vectors.rst
index ea6c95e..5353b1d 100644
--- a/docs/development/test-vectors.rst
+++ b/docs/development/test-vectors.rst
@@ -132,20 +132,25 @@
a subject alternative name extension with the ``otherName`` general name.
* ``san_registered_id.pem`` - An RSA 1024 bit certificate containing a
subject alternative name extension with the ``registeredID`` general name.
+* ``all_key_usages.pem`` - An RSA 2048 bit self-signed certificate containing
+ a key usage extension with all nine purposes set to true.
Custom X.509 Request Vectors
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-* ``dsa_sha1.pem`` - Contains a certificate request using 1024-bit DSA
- parameters and SHA1 generated using OpenSSL.
-* ``rsa_md4.pem`` - Contains a certificate request using 2048 bit RSA and MD4
+* ``dsa_sha1.pem`` and ``dsa_sha1.der`` - Contain a certificate request using
+ 1024-bit DSA parameters and SHA1 generated using OpenSSL.
+* ``rsa_md4.pem`` and ``rsa_md4.der`` - Contain a certificate request using
+ 2048 bit RSA and MD4 generated using OpenSSL.
+* ``rsa_sha1.pem`` and ``rsa_sha1.der`` - Contain a certificate request using
+ 2048 bit RSA and SHA1 generated using OpenSSL.
+* ``rsa_sha256.pem`` and ``rsa_sha256.der`` - Contain a certificate request
+ using 2048 bit RSA and SHA256 generated using OpenSSL.
+* ``ec_sha256.pem`` and ``ec_sha256.der`` - Contain a certificate request
+ using EC (``secp384r1``) and SHA256 generated using OpenSSL.
+* ``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.
-* ``rsa_sha1.pem`` - Contains a certificate request using 2048 bit RSA and
- SHA1 generated using OpenSSL.
-* ``rsa_sha256.pem`` - Contains a certificate request using 2048 bit RSA and
- SHA256 generated using OpenSSL.
-* ``ec_sha256.pem`` - Contains a certificate request using EC (``secp384r1``)
- and SHA256 generated using OpenSSL.
Hashes
~~~~~~
diff --git a/docs/fernet.rst b/docs/fernet.rst
index f1a4c74..eacbc2a 100644
--- a/docs/fernet.rst
+++ b/docs/fernet.rst
@@ -92,8 +92,10 @@
>>> f.decrypt(token)
'Secret message!'
- Fernet performs all encryption options using the *first* key in the
- ``list`` provided. Decryption supports using *any* of constituent keys.
+ MultiFernet performs all encryption options using the *first* key in the
+ ``list`` provided. MultiFernet attempts to decrypt tokens with each key in
+ turn. A :class:`cryptography.fernet.InvalidToken` exception is raised if
+ the correct key is not found in the ``list`` provided.
Key rotation makes it easy to replace old keys. You can add your new key at
the front of the list to start encrypting new messages, and remove old keys
diff --git a/docs/hazmat/backends/interfaces.rst b/docs/hazmat/backends/interfaces.rst
index 1af8d8f..8866cf7 100644
--- a/docs/hazmat/backends/interfaces.rst
+++ b/docs/hazmat/backends/interfaces.rst
@@ -509,3 +509,12 @@
:param bytes data: DER formatted certificate data.
:returns: An instance of :class:`~cryptography.x509.Certificate`.
+
+ .. method:: load_pem_x509_csr(data)
+
+ .. versionadded:: 0.9
+
+ :param bytes data: PEM formatted certificate signing request data.
+
+ :returns: An instance of
+ :class:`~cryptography.x509.CertificateSigningRequest`.
diff --git a/docs/hazmat/primitives/asymmetric/dsa.rst b/docs/hazmat/primitives/asymmetric/dsa.rst
index 3c3567b..179bb8d 100644
--- a/docs/hazmat/primitives/asymmetric/dsa.rst
+++ b/docs/hazmat/primitives/asymmetric/dsa.rst
@@ -438,4 +438,4 @@
.. _`DSA`: https://en.wikipedia.org/wiki/Digital_Signature_Algorithm
.. _`public-key`: https://en.wikipedia.org/wiki/Public-key_cryptography
.. _`FIPS 186-4`: http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf
-.. _`at least 2048`: http://www.ecrypt.eu.org/documents/D.SPA.20.pdf
+.. _`at least 2048`: http://www.ecrypt.eu.org/ecrypt2/documents/D.SPA.20.pdf
diff --git a/docs/hazmat/primitives/asymmetric/ec.rst b/docs/hazmat/primitives/asymmetric/ec.rst
index 6c03d77..6f4afe7 100644
--- a/docs/hazmat/primitives/asymmetric/ec.rst
+++ b/docs/hazmat/primitives/asymmetric/ec.rst
@@ -438,7 +438,7 @@
.. _`FIPS 186-3`: http://csrc.nist.gov/publications/fips/fips186-3/fips_186-3.pdf
.. _`FIPS 186-4`: http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf
.. _`some concern`: https://crypto.stackexchange.com/questions/10263/should-we-trust-the-nist-recommended-ecc-parameters
-.. _`less than 224 bits`: http://www.ecrypt.eu.org/documents/D.SPA.20.pdf
+.. _`less than 224 bits`: http://www.ecrypt.eu.org/ecrypt2/documents/D.SPA.20.pdf
.. _`64x lower computational cost than DH`: https://www.nsa.gov/business/programs/elliptic_curve.shtml
.. _`minimize the number of security concerns for elliptic-curve cryptography`: http://cr.yp.to/ecdh/curve25519-20060209.pdf
.. _`SafeCurves`: http://safecurves.cr.yp.to/
diff --git a/docs/hazmat/primitives/asymmetric/rsa.rst b/docs/hazmat/primitives/asymmetric/rsa.rst
index e703310..a518774 100644
--- a/docs/hazmat/primitives/asymmetric/rsa.rst
+++ b/docs/hazmat/primitives/asymmetric/rsa.rst
@@ -683,7 +683,7 @@
.. _`public-key`: https://en.wikipedia.org/wiki/Public-key_cryptography
.. _`specific mathematical properties`: https://en.wikipedia.org/wiki/RSA_(cryptosystem)#Key_generation
.. _`use 65537`: http://www.daemonology.net/blog/2009-06-11-cryptographic-right-answers.html
-.. _`at least 2048`: http://www.ecrypt.eu.org/documents/D.SPA.20.pdf
+.. _`at least 2048`: http://www.ecrypt.eu.org/ecrypt2/documents/D.SPA.20.pdf
.. _`OpenPGP`: https://en.wikipedia.org/wiki/Pretty_Good_Privacy
.. _`Chinese Remainder Theorem`: https://en.wikipedia.org/wiki/RSA_%28cryptosystem%29#Using_the_Chinese_remainder_algorithm
.. _`security proof`: http://eprint.iacr.org/2001/062.pdf
diff --git a/docs/spelling_wordlist.txt b/docs/spelling_wordlist.txt
index 41728d2..f7b73b3 100644
--- a/docs/spelling_wordlist.txt
+++ b/docs/spelling_wordlist.txt
@@ -22,6 +22,7 @@
deserialized
Diffie
Docstrings
+Encodings
fernet
Fernet
hazmat
@@ -38,12 +39,14 @@
naïve
namespace
namespaces
+paddings
pickleable
plaintext
preprocessor
preprocessors
pseudorandom
pyOpenSSL
+relicensed
Schneier
scrypt
Serializers
diff --git a/docs/x509.rst b/docs/x509.rst
index 44d53a4..afc9620 100644
--- a/docs/x509.rst
+++ b/docs/x509.rst
@@ -3,11 +3,57 @@
.. currentmodule:: cryptography.x509
+.. testsetup::
+
+ pem_req_data = b"""
+ -----BEGIN CERTIFICATE REQUEST-----
+ MIIC0zCCAbsCAQAwWTELMAkGA1UEBhMCVVMxETAPBgNVBAgMCElsbGlub2lzMRAw
+ DgYDVQQHDAdDaGljYWdvMREwDwYDVQQKDAhyNTA5IExMQzESMBAGA1UEAwwJaGVs
+ bG8uY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqhZx+Mo9VRd9
+ vsnWWa6NBCws21rZ0+1B/JGgB4hDsZS7iDE4Bj5z4idheFRtl8bBbdjPknq7BfoF
+ 8v15Zq/Zv7i2xMSDL+LUrTBZezRd4bRTGqCm6YJ5EYkhqdcqeZleHCFImguHoq1J
+ Fh0+kObQrTHXw3ZP57a3o1IvyIUA3nNoCBL0QQhwBXaDXOojMKNR+bqB5ve8GS1y
+ Elr0AM/+cJsfaIahNQUgFKx3Eu3GeEOMKYOAG1lycgdQdmTUybLrT3U7vkClTseM
+ xHg1r5En7ALjONIhqRuq3rddYahrP8HXozb3zUy3cJ7P6IeaosuvNzvMXOX9P6HD
+ Ha9urDAJ1wIDAQABoDUwMwYJKoZIhvcNAQkOMSYwJDAiBgNVHREEGzAZggl3b3Js
+ ZC5jb22CDHdoYXRldmVyLmNvbTANBgkqhkiG9w0BAQUFAAOCAQEAS4Ro6h+z52SK
+ YSLCYARpnEu/rmh4jdqndt8naqcNb6uLx9mlKZ2W9on9XDjnSdQD9q+ZP5aZfESw
+ R0+rJhW9ZrNa/g1pt6M24ihclHYDAxYMWxT1z/TXXGM3TmZZ6gfYlNE1kkBuODHa
+ UYsR/1Ht1E1EsmmUimt2n+zQR2K8T9Coa+boaUW/GsTEuz1aaJAkj5ZvTDiIhRG4
+ AOCqFZOLAQmCCNgJnnspD9hDz/Ons085LF5wnYjN4/Nsk5tS6AGs3xjZ3jPoOGGn
+ 82WQ9m4dBGoVDZXsobVTaN592JEYwN5iu72zRn7Einb4V4H5y3yD2dD4yWPlt4pk
+ 5wFkeYsZEA==
+ -----END CERTIFICATE REQUEST-----
+ """.strip()
+
+ pem_data = b"""
+ -----BEGIN CERTIFICATE-----
+ MIIDfDCCAmSgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBFMQswCQYDVQQGEwJVUzEf
+ MB0GA1UEChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEVMBMGA1UEAxMMVHJ1c3Qg
+ QW5jaG9yMB4XDTEwMDEwMTA4MzAwMFoXDTMwMTIzMTA4MzAwMFowQDELMAkGA1UE
+ BhMCVVMxHzAdBgNVBAoTFlRlc3QgQ2VydGlmaWNhdGVzIDIwMTExEDAOBgNVBAMT
+ B0dvb2QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCQWJpHYo37
+ Xfb7oJSPe+WvfTlzIG21WQ7MyMbGtK/m8mejCzR6c+f/pJhEH/OcDSMsXq8h5kXa
+ BGqWK+vSwD/Pzp5OYGptXmGPcthDtAwlrafkGOS4GqIJ8+k9XGKs+vQUXJKsOk47
+ RuzD6PZupq4s16xaLVqYbUC26UcY08GpnoLNHJZS/EmXw1ZZ3d4YZjNlpIpWFNHn
+ UGmdiGKXUPX/9H0fVjIAaQwjnGAbpgyCumWgzIwPpX+ElFOUr3z7BoVnFKhIXze+
+ VmQGSWxZxvWDUN90Ul0tLEpLgk3OVxUB4VUGuf15OJOpgo1xibINPmWt14Vda2N9
+ yrNKloJGZNqLAgMBAAGjfDB6MB8GA1UdIwQYMBaAFOR9X9FclYYILAWuvnW2ZafZ
+ XahmMB0GA1UdDgQWBBRYAYQkG7wrUpRKPaUQchRR9a86yTAOBgNVHQ8BAf8EBAMC
+ AQYwFwYDVR0gBBAwDjAMBgpghkgBZQMCATABMA8GA1UdEwEB/wQFMAMBAf8wDQYJ
+ KoZIhvcNAQELBQADggEBADWHlxbmdTXNwBL/llwhQqwnazK7CC2WsXBBqgNPWj7m
+ tvQ+aLG8/50Qc2Sun7o2VnwF9D18UUe8Gj3uPUYH+oSI1vDdyKcjmMbKRU4rk0eo
+ 3UHNDXwqIVc9CQS9smyV+x1HCwL4TTrq+LXLKx/qVij0Yqk+UJfAtrg2jnYKXsCu
+ FMBQQnWCGrwa1g1TphRp/RmYHnMynYFmZrXtzFz+U9XEA7C+gPq4kqDI/iVfIT1s
+ 6lBtdB50lrDVwl2oYfAvW/6sC2se2QleZidUmrziVNP4oEeXINokU6T6p//HM1FG
+ QYw2jOvpKcKtWCSAnegEbgsGYzATKjmPJPJ0npHFqzM=
+ -----END CERTIFICATE-----
+ """.strip()
+
X.509 is an ITU-T standard for a `public key infrastructure`_. X.509v3 is
defined in :rfc:`5280` (which obsoletes :rfc:`2459` and :rfc:`3280`). X.509
certificates are commonly used in protocols like `TLS`_.
-
Loading Certificates
~~~~~~~~~~~~~~~~~~~~
@@ -43,32 +89,6 @@
:returns: An instance of :class:`~cryptography.x509.Certificate`.
-.. testsetup::
-
- pem_data = b"""
- -----BEGIN CERTIFICATE-----
- MIIDfDCCAmSgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBFMQswCQYDVQQGEwJVUzEf
- MB0GA1UEChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEVMBMGA1UEAxMMVHJ1c3Qg
- QW5jaG9yMB4XDTEwMDEwMTA4MzAwMFoXDTMwMTIzMTA4MzAwMFowQDELMAkGA1UE
- BhMCVVMxHzAdBgNVBAoTFlRlc3QgQ2VydGlmaWNhdGVzIDIwMTExEDAOBgNVBAMT
- B0dvb2QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCQWJpHYo37
- Xfb7oJSPe+WvfTlzIG21WQ7MyMbGtK/m8mejCzR6c+f/pJhEH/OcDSMsXq8h5kXa
- BGqWK+vSwD/Pzp5OYGptXmGPcthDtAwlrafkGOS4GqIJ8+k9XGKs+vQUXJKsOk47
- RuzD6PZupq4s16xaLVqYbUC26UcY08GpnoLNHJZS/EmXw1ZZ3d4YZjNlpIpWFNHn
- UGmdiGKXUPX/9H0fVjIAaQwjnGAbpgyCumWgzIwPpX+ElFOUr3z7BoVnFKhIXze+
- VmQGSWxZxvWDUN90Ul0tLEpLgk3OVxUB4VUGuf15OJOpgo1xibINPmWt14Vda2N9
- yrNKloJGZNqLAgMBAAGjfDB6MB8GA1UdIwQYMBaAFOR9X9FclYYILAWuvnW2ZafZ
- XahmMB0GA1UdDgQWBBRYAYQkG7wrUpRKPaUQchRR9a86yTAOBgNVHQ8BAf8EBAMC
- AQYwFwYDVR0gBBAwDjAMBgpghkgBZQMCATABMA8GA1UdEwEB/wQFMAMBAf8wDQYJ
- KoZIhvcNAQELBQADggEBADWHlxbmdTXNwBL/llwhQqwnazK7CC2WsXBBqgNPWj7m
- tvQ+aLG8/50Qc2Sun7o2VnwF9D18UUe8Gj3uPUYH+oSI1vDdyKcjmMbKRU4rk0eo
- 3UHNDXwqIVc9CQS9smyV+x1HCwL4TTrq+LXLKx/qVij0Yqk+UJfAtrg2jnYKXsCu
- FMBQQnWCGrwa1g1TphRp/RmYHnMynYFmZrXtzFz+U9XEA7C+gPq4kqDI/iVfIT1s
- 6lBtdB50lrDVwl2oYfAvW/6sC2se2QleZidUmrziVNP4oEeXINokU6T6p//HM1FG
- QYw2jOvpKcKtWCSAnegEbgsGYzATKjmPJPJ0npHFqzM=
- -----END CERTIFICATE-----
- """.strip()
-
.. doctest::
>>> from cryptography import x509
@@ -77,6 +97,52 @@
>>> cert.serial
2
+Loading Certificate Signing Requests
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. function:: load_pem_x509_csr(data, backend)
+
+ .. versionadded:: 0.9
+
+ Deserialize a certificate signing request (CSR) from PEM encoded data. PEM
+ requests are base64 decoded and have delimiters that look like
+ ``-----BEGIN CERTIFICATE REQUEST-----``. This format is also known as
+ PKCS#10.
+
+ :param bytes data: The PEM encoded request data.
+
+ :param backend: A backend supporting the
+ :class:`~cryptography.hazmat.backends.interfaces.X509Backend`
+ interface.
+
+ :returns: An instance of
+ :class:`~cryptography.x509.CertificateSigningRequest`.
+
+.. function:: load_der_x509_csr(data, backend)
+
+ .. versionadded:: 0.9
+
+ Deserialize a certificate signing request (CSR) from DER encoded data. DER
+ is a binary format and is not commonly used with CSRs.
+
+ :param bytes data: The DER encoded request data.
+
+ :param backend: A backend supporting the
+ :class:`~cryptography.hazmat.backends.interfaces.X509Backend`
+ interface.
+
+ :returns: An instance of
+ :class:`~cryptography.x509.CertificateSigningRequest`.
+
+.. doctest::
+
+ >>> from cryptography import x509
+ >>> from cryptography.hazmat.backends import default_backend
+ >>> from cryptography.hazmat.primitives import hashes
+ >>> csr = x509.load_pem_x509_csr(pem_req_data, default_backend())
+ >>> isinstance(csr.signature_hash_algorithm, hashes.SHA1)
+ True
+
X.509 Certificate Object
~~~~~~~~~~~~~~~~~~~~~~~~
@@ -205,6 +271,55 @@
:raises cryptography.x509.DuplicateExtension: If more than one
extension of the same type is found within the certificate.
+ .. doctest::
+
+ >>> for ext in cert.extensions:
+ ... print(ext)
+ <Extension(oid=<ObjectIdentifier(oid=2.5.29.19, name=basicConstraints)>, critical=True, value=<BasicConstraints(ca=True, path_length=None)>)>
+
+X.509 CSR (Certificate Signing Request) Object
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. class:: CertificateSigningRequest
+
+ .. versionadded:: 0.9
+
+ .. method:: public_key()
+
+ :type:
+ :class:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPublicKey` or
+ :class:`~cryptography.hazmat.primitives.asymmetric.dsa.DSAPublicKey` or
+ :class:`~cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePublicKey`
+
+ The public key associated with the request.
+
+ .. doctest::
+
+ >>> from cryptography.hazmat.primitives.asymmetric import rsa
+ >>> public_key = csr.public_key()
+ >>> isinstance(public_key, rsa.RSAPublicKey)
+ True
+
+ .. attribute:: subject
+
+ :type: :class:`Name`
+
+ The :class:`Name` of the subject.
+
+ .. attribute:: signature_hash_algorithm
+
+ :type: :class:`~cryptography.hazmat.primitives.hashes.HashAlgorithm`
+
+ Returns the
+ :class:`~cryptography.hazmat.primitives.hashes.HashAlgorithm` which
+ was used in signing this request.
+
+ .. doctest::
+
+ >>> from cryptography.hazmat.primitives import hashes
+ >>> isinstance(csr.signature_hash_algorithm, hashes.SHA1)
+ True
+
.. class:: Name
.. versionadded:: 0.8
@@ -292,6 +407,23 @@
An X.509 Extensions instance is an ordered list of extensions. The object
is iterable to get every extension.
+ .. method:: get_extension_for_oid(oid)
+
+ :param oid: An :class:`ObjectIdentifier` instance.
+
+ :returns: An instance of the extension class.
+
+ :raises cryptography.x509.ExtensionNotFound: If the certificate does
+ not have the extension requested.
+
+ :raises cryptography.x509.UnsupportedExtension: If the certificate
+ contains an extension that is not supported.
+
+ .. doctest::
+
+ >>> cert.extensions.get_extension_for_oid(x509.OID_BASIC_CONSTRAINTS)
+ <Extension(oid=<ObjectIdentifier(oid=2.5.29.19, name=basicConstraints)>, critical=True, value=<BasicConstraints(ca=True, path_length=None)>)>
+
.. class:: Extension
.. versionadded:: 0.9
@@ -315,6 +447,98 @@
Returns an instance of the extension type corresponding to the OID.
+.. class:: KeyUsage
+
+ .. versionadded:: 0.9
+
+ The key usage extension defines the purpose of the key contained in the
+ certificate. The usage restriction might be employed when a key that could
+ be used for more than one operation is to be restricted. It corresponds to
+ :data:`OID_KEY_USAGE`.
+
+ .. attribute:: digital_signature
+
+ :type: bool
+
+ This purpose is set to true when the subject public key is used for verifying
+ digital signatures, other than signatures on certificates
+ (``key_cert_sign``) and CRLs (``crl_sign``).
+
+ .. attribute:: content_commitment
+
+ :type: bool
+
+ This purpose is set to true when the subject public key is used for verifying
+ digital signatures, other than signatures on certificates
+ (``key_cert_sign``) and CRLs (``crl_sign``). It is used to provide a
+ non-repudiation service that protects against the signing entity
+ falsely denying some action. In the case of later conflict, a
+ reliable third party may determine the authenticity of the signed
+ data. This was called ``non_repudiation`` in older revisions of the
+ X.509 specification.
+
+ .. attribute:: key_encipherment
+
+ :type: bool
+
+ This purpose is set to true when the subject public key is used for
+ enciphering private or secret keys.
+
+ .. attribute:: data_encipherment
+
+ :type: bool
+
+ This purpose is set to true when the subject public key is used for
+ directly enciphering raw user data without the use of an intermediate
+ symmetric cipher.
+
+ .. attribute:: key_agreement
+
+ :type: bool
+
+ This purpose is set to true when the subject public key is used for key
+ agreement. For example, when a Diffie-Hellman key is to be used for
+ key management, then this purpose is set to true.
+
+ .. attribute:: key_cert_sign
+
+ :type: bool
+
+ This purpose is set to true when the subject public key is used for
+ verifying signatures on public key certificates. If this purpose is set
+ to true then ``ca`` must be true in the :class:`BasicConstraints`
+ extension.
+
+ .. attribute:: crl_sign
+
+ :type: bool
+
+ This purpose is set to true when the subject public key is used for
+ verifying signatures on certificate revocation lists.
+
+ .. attribute:: encipher_only
+
+ :type: bool
+
+ When this purposes is set to true and the ``key_agreement`` purpose is
+ also set, the subject public key may be used only for enciphering data
+ while performing key agreement.
+
+ :raises ValueError: This is raised if accessed when ``key_agreement``
+ is false.
+
+ .. attribute:: decipher_only
+
+ :type: bool
+
+ When this purposes is set to true and the ``key_agreement`` purpose is
+ also set, the subject public key may be used only for deciphering data
+ while performing key agreement.
+
+ :raises ValueError: This is raised if accessed when ``key_agreement``
+ is false.
+
+
.. class:: BasicConstraints
.. versionadded:: 0.9
@@ -343,6 +567,15 @@
subordinate CA, but the subordinate CA is not allowed to create
subordinates with ``ca`` set to true.
+.. class:: ExtendedKeyUsage
+
+ .. versionadded:: 0.9
+
+ This extension indicates one or more purposes for which the certified
+ public key may be used, in addition to or in place of the basic
+ purposes indicated in the key usage extension. The object is
+ iterable to obtain the list of :ref:`extended key usage OIDs <eku_oids>`.
+
Object Identifiers
~~~~~~~~~~~~~~~~~~
@@ -501,6 +734,41 @@
Corresponds to the dotted string ``"2.16.840.1.101.3.4.3.2"``. This is
a SHA256 digest signed by a DSA key.
+.. _eku_oids:
+
+Extended Key Usage OIDs
+~~~~~~~~~~~~~~~~~~~~~~~
+
+.. data:: OID_SERVER_AUTH
+
+ Corresponds to the dotted string ``"1.3.6.1.5.5.7.3.1"``. This is used to
+ denote that a certificate may be used for TLS web server authentication.
+
+.. data:: OID_CLIENT_AUTH
+
+ Corresponds to the dotted string ``"1.3.6.1.5.5.7.3.2"``. This is used to
+ denote that a certificate may be used for TLS web client authentication.
+
+.. data:: OID_CODE_SIGNING
+
+ Corresponds to the dotted string ``"1.3.6.1.5.5.7.3.3"``. This is used to
+ denote that a certificate may be used for code signing.
+
+.. data:: OID_EMAIL_PROTECTION
+
+ Corresponds to the dotted string ``"1.3.6.1.5.5.7.3.4"``. This is used to
+ denote that a certificate may be used for email protection.
+
+.. data:: OID_TIME_STAMPING
+
+ Corresponds to the dotted string ``"1.3.6.1.5.5.7.3.8"``. This is used to
+ denote that a certificate may be used for time stamping.
+
+.. data:: OID_OCSP_SIGNING
+
+ Corresponds to the dotted string ``"1.3.6.1.5.5.7.3.9"``. This is used to
+ denote that a certificate may be used for signing OCSP responses.
+
.. _extension_oids:
Extension OIDs
@@ -511,6 +779,11 @@
Corresponds to the dotted string ``"2.5.29.19"``. The identifier for the
:class:`BasicConstraints` extension type.
+.. data:: OID_KEY_USAGE
+
+ Corresponds to the dotted string ``"2.5.29.15"``. The identifier for the
+ :class:`KeyUsage` extension type.
+
Exceptions
~~~~~~~~~~
@@ -546,6 +819,17 @@
Returns the OID.
+.. class:: ExtensionNotFound
+
+ This is raised when calling :meth:`Extensions.get_extension_for_oid` with
+ an extension OID that is not present in the certificate.
+
+ .. attribute:: oid
+
+ :type: :class:`ObjectIdentifier`
+
+ Returns the OID.
+
.. _`public key infrastructure`: https://en.wikipedia.org/wiki/Public_key_infrastructure
.. _`TLS`: https://en.wikipedia.org/wiki/Transport_Layer_Security
diff --git a/src/cryptography/__init__.py b/src/cryptography/__init__.py
index 6da0b38..985ebd6 100644
--- a/src/cryptography/__init__.py
+++ b/src/cryptography/__init__.py
@@ -4,6 +4,9 @@
from __future__ import absolute_import, division, print_function
+import sys
+import warnings
+
from cryptography.__about__ import (
__author__, __copyright__, __email__, __license__, __summary__, __title__,
__uri__, __version__
@@ -14,3 +17,10 @@
"__title__", "__summary__", "__uri__", "__version__", "__author__",
"__email__", "__license__", "__copyright__",
]
+
+if sys.version_info[:2] == (2, 6):
+ warnings.warn(
+ "Python 2.6 is no longer supported by the Python core team, please "
+ "upgrade your Python.",
+ DeprecationWarning
+ )
diff --git a/src/cryptography/hazmat/backends/interfaces.py b/src/cryptography/hazmat/backends/interfaces.py
index 7980890..5224f5c 100644
--- a/src/cryptography/hazmat/backends/interfaces.py
+++ b/src/cryptography/hazmat/backends/interfaces.py
@@ -261,3 +261,15 @@
"""
Load an X.509 certificate from DER encoded data.
"""
+
+ @abc.abstractmethod
+ def load_der_x509_csr(self, data):
+ """
+ Load an X.509 CSR from DER encoded data.
+ """
+
+ @abc.abstractmethod
+ def load_pem_x509_csr(self, data):
+ """
+ Load an X.509 CSR from PEM encoded data.
+ """
diff --git a/src/cryptography/hazmat/backends/multibackend.py b/src/cryptography/hazmat/backends/multibackend.py
index 6e378c1..784ab84 100644
--- a/src/cryptography/hazmat/backends/multibackend.py
+++ b/src/cryptography/hazmat/backends/multibackend.py
@@ -324,3 +324,21 @@
"This backend does not support X.509.",
_Reasons.UNSUPPORTED_X509
)
+
+ def load_der_x509_csr(self, data):
+ for b in self._filtered_backends(X509Backend):
+ return b.load_der_x509_csr(data)
+
+ raise UnsupportedAlgorithm(
+ "This backend does not support X.509.",
+ _Reasons.UNSUPPORTED_X509
+ )
+
+ def load_pem_x509_csr(self, data):
+ for b in self._filtered_backends(X509Backend):
+ return b.load_pem_x509_csr(data)
+
+ 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 60aa453..665771a 100644
--- a/src/cryptography/hazmat/backends/openssl/backend.py
+++ b/src/cryptography/hazmat/backends/openssl/backend.py
@@ -34,7 +34,9 @@
from cryptography.hazmat.backends.openssl.rsa import (
_RSAPrivateKey, _RSAPublicKey
)
-from cryptography.hazmat.backends.openssl.x509 import _Certificate
+from cryptography.hazmat.backends.openssl.x509 import (
+ _Certificate, _CertificateSigningRequest
+)
from cryptography.hazmat.bindings.openssl.binding import Binding
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import dsa, ec, rsa
@@ -475,20 +477,20 @@
pointer.
"""
- type = evp_pkey.type
+ key_type = evp_pkey.type
- if type == self._lib.EVP_PKEY_RSA:
+ if key_type == self._lib.EVP_PKEY_RSA:
rsa_cdata = self._lib.EVP_PKEY_get1_RSA(evp_pkey)
assert rsa_cdata != self._ffi.NULL
rsa_cdata = self._ffi.gc(rsa_cdata, self._lib.RSA_free)
return _RSAPrivateKey(self, rsa_cdata)
- elif type == self._lib.EVP_PKEY_DSA:
+ elif key_type == self._lib.EVP_PKEY_DSA:
dsa_cdata = self._lib.EVP_PKEY_get1_DSA(evp_pkey)
assert dsa_cdata != self._ffi.NULL
dsa_cdata = self._ffi.gc(dsa_cdata, self._lib.DSA_free)
return _DSAPrivateKey(self, dsa_cdata)
elif (self._lib.Cryptography_HAS_EC == 1 and
- type == self._lib.EVP_PKEY_EC):
+ key_type == self._lib.EVP_PKEY_EC):
ec_cdata = self._lib.EVP_PKEY_get1_EC_KEY(evp_pkey)
assert ec_cdata != self._ffi.NULL
ec_cdata = self._ffi.gc(ec_cdata, self._lib.EC_KEY_free)
@@ -502,20 +504,20 @@
pointer.
"""
- type = evp_pkey.type
+ key_type = evp_pkey.type
- if type == self._lib.EVP_PKEY_RSA:
+ if key_type == self._lib.EVP_PKEY_RSA:
rsa_cdata = self._lib.EVP_PKEY_get1_RSA(evp_pkey)
assert rsa_cdata != self._ffi.NULL
rsa_cdata = self._ffi.gc(rsa_cdata, self._lib.RSA_free)
return _RSAPublicKey(self, rsa_cdata)
- elif type == self._lib.EVP_PKEY_DSA:
+ elif key_type == self._lib.EVP_PKEY_DSA:
dsa_cdata = self._lib.EVP_PKEY_get1_DSA(evp_pkey)
assert dsa_cdata != self._ffi.NULL
dsa_cdata = self._ffi.gc(dsa_cdata, self._lib.DSA_free)
return _DSAPublicKey(self, dsa_cdata)
elif (self._lib.Cryptography_HAS_EC == 1 and
- type == self._lib.EVP_PKEY_EC):
+ key_type == self._lib.EVP_PKEY_EC):
ec_cdata = self._lib.EVP_PKEY_get1_EC_KEY(evp_pkey)
assert ec_cdata != self._ffi.NULL
ec_cdata = self._ffi.gc(ec_cdata, self._lib.EC_KEY_free)
@@ -669,7 +671,7 @@
def dsa_parameters_supported(self, p, q, g):
if self._lib.OPENSSL_VERSION_NUMBER < 0x1000000f:
- return (utils.bit_length(p) <= 1024 and utils.bit_length(q) <= 160)
+ return utils.bit_length(p) <= 1024 and utils.bit_length(q) <= 160
else:
return True
@@ -820,6 +822,28 @@
x509 = self._ffi.gc(x509, self._lib.X509_free)
return _Certificate(self, x509)
+ def load_pem_x509_csr(self, data):
+ mem_bio = self._bytes_to_bio(data)
+ x509_req = self._lib.PEM_read_bio_X509_REQ(
+ mem_bio.bio, self._ffi.NULL, self._ffi.NULL, self._ffi.NULL
+ )
+ if x509_req == self._ffi.NULL:
+ self._consume_errors()
+ raise ValueError("Unable to load request")
+
+ x509_req = self._ffi.gc(x509_req, self._lib.X509_REQ_free)
+ return _CertificateSigningRequest(self, x509_req)
+
+ def load_der_x509_csr(self, data):
+ mem_bio = self._bytes_to_bio(data)
+ x509_req = self._lib.d2i_X509_REQ_bio(mem_bio.bio, self._ffi.NULL)
+ if x509_req == self._ffi.NULL:
+ self._consume_errors()
+ raise ValueError("Unable to load request")
+
+ x509_req = self._ffi.gc(x509_req, self._lib.X509_REQ_free)
+ return _CertificateSigningRequest(self, x509_req)
+
def _load_key(self, openssl_read_func, convert_func, data, password):
mem_bio = self._bytes_to_bio(data)
@@ -1205,13 +1229,13 @@
assert res == 1
return self._read_mem_bio(bio)
- def _private_key_bytes_traditional_der(self, type, cdata):
- if type == self._lib.EVP_PKEY_RSA:
+ def _private_key_bytes_traditional_der(self, key_type, cdata):
+ if key_type == self._lib.EVP_PKEY_RSA:
write_bio = self._lib.i2d_RSAPrivateKey_bio
elif (self._lib.Cryptography_HAS_EC == 1 and
- type == self._lib.EVP_PKEY_EC):
+ key_type == self._lib.EVP_PKEY_EC):
write_bio = self._lib.i2d_ECPrivateKey_bio
- elif type == self._lib.EVP_PKEY_DSA:
+ elif key_type == self._lib.EVP_PKEY_DSA:
write_bio = self._lib.i2d_DSAPrivateKey_bio
bio = self._create_mem_bio()
diff --git a/src/cryptography/hazmat/backends/openssl/utils.py b/src/cryptography/hazmat/backends/openssl/utils.py
index 498c100..001121f 100644
--- a/src/cryptography/hazmat/backends/openssl/utils.py
+++ b/src/cryptography/hazmat/backends/openssl/utils.py
@@ -16,7 +16,7 @@
if 8 * digest_len > order_bits:
rshift = 8 - (order_bits & 0x7)
- assert rshift > 0 and rshift < 8
+ assert 0 < rshift < 8
mask = 0xFF >> rshift << rshift
diff --git a/src/cryptography/hazmat/backends/openssl/x509.py b/src/cryptography/hazmat/backends/openssl/x509.py
index 3502d12..6a7032b 100644
--- a/src/cryptography/hazmat/backends/openssl/x509.py
+++ b/src/cryptography/hazmat/backends/openssl/x509.py
@@ -45,7 +45,7 @@
assert res >= 0
assert buf[0] != backend._ffi.NULL
buf = backend._ffi.gc(
- buf, lambda buf: backend._lib.OPENSSL_free(buf[0])
+ buf, lambda buffer: backend._lib.OPENSSL_free(buffer[0])
)
value = backend._ffi.buffer(buf[0], res)[:].decode('utf8')
oid = _obj2txt(backend, obj)
@@ -168,13 +168,16 @@
raise x509.DuplicateExtension(
"Duplicate {0} extension found".format(oid), oid
)
- elif oid == x509.OID_BASIC_CONSTRAINTS and critical:
+ elif oid == x509.OID_BASIC_CONSTRAINTS:
+ value = self._build_basic_constraints(ext)
+ elif oid == x509.OID_KEY_USAGE and critical:
# TODO: remove this obviously.
warnings.warn(
- "Extension support is not fully implemented. A basic "
- "constraints extension with the critical flag was seen and"
- " IGNORED."
+ "Extension support is not fully implemented. A key usage "
+ "extension with the critical flag was seen and IGNORED."
)
+ seen_oids.add(oid)
+ continue
elif critical:
raise x509.UnsupportedExtension(
"{0} is not currently supported".format(oid), oid
@@ -185,5 +188,60 @@
continue
seen_oids.add(oid)
+ extensions.append(x509.Extension(oid, critical, value))
return x509.Extensions(extensions)
+
+ 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:
+ bn = self._backend._lib.ASN1_INTEGER_to_BN(
+ basic_constraints.pathlen, self._backend._ffi.NULL
+ )
+ assert bn != self._backend._ffi.NULL
+ bn = self._backend._ffi.gc(bn, self._backend._lib.BN_free)
+ path_length = self._backend._bn_to_int(bn)
+
+ return x509.BasicConstraints(ca, path_length)
+
+
+@utils.register_interface(x509.CertificateSigningRequest)
+class _CertificateSigningRequest(object):
+ def __init__(self, backend, x509_req):
+ self._backend = backend
+ self._x509_req = x509_req
+
+ def public_key(self):
+ pkey = self._backend._lib.X509_REQ_get_pubkey(self._x509_req)
+ assert pkey != self._backend._ffi.NULL
+ pkey = self._backend._ffi.gc(pkey, self._backend._lib.EVP_PKEY_free)
+ return self._backend._evp_pkey_to_public_key(pkey)
+
+ @property
+ 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)
+
+ @property
+ def signature_hash_algorithm(self):
+ oid = _obj2txt(self._backend, self._x509_req.sig_alg.algorithm)
+ try:
+ return x509._SIG_OIDS_TO_HASH[oid]
+ except KeyError:
+ raise UnsupportedAlgorithm(
+ "Signature algorithm OID:{0} not recognized".format(oid)
+ )
diff --git a/src/cryptography/hazmat/bindings/openssl/asn1.py b/src/cryptography/hazmat/bindings/openssl/asn1.py
index d8b8331..45dfe75 100644
--- a/src/cryptography/hazmat/bindings/openssl/asn1.py
+++ b/src/cryptography/hazmat/bindings/openssl/asn1.py
@@ -40,6 +40,7 @@
typedef struct asn1_string_st ASN1_OCTET_STRING;
typedef struct asn1_string_st ASN1_IA5STRING;
+typedef ... ASN1_BIT_STRING;
typedef ... ASN1_OBJECT;
typedef ... ASN1_STRING;
typedef ... ASN1_TYPE;
@@ -115,9 +116,12 @@
ASN1_VALUE *ASN1_item_d2i(ASN1_VALUE **, const unsigned char **, long,
const ASN1_ITEM *);
+int ASN1_BIT_STRING_set_bit(ASN1_BIT_STRING *, int, int);
"""
MACROS = """
+/* This is not a macro, but is const on some versions of OpenSSL */
+int ASN1_BIT_STRING_get_bit(ASN1_BIT_STRING *, int);
ASN1_TIME *M_ASN1_TIME_dup(void *);
const ASN1_ITEM *ASN1_ITEM_ptr(ASN1_ITEM_EXP *);
diff --git a/src/cryptography/hazmat/bindings/openssl/pem.py b/src/cryptography/hazmat/bindings/openssl/pem.py
index 98c7648..8ec3fef 100644
--- a/src/cryptography/hazmat/bindings/openssl/pem.py
+++ b/src/cryptography/hazmat/bindings/openssl/pem.py
@@ -32,7 +32,9 @@
int i2d_PKCS8PrivateKey_nid_bio(BIO *, EVP_PKEY *, int,
char *, int, pem_password_cb *, void *);
+int i2d_PKCS7_bio(BIO *, PKCS7 *);
PKCS7 *d2i_PKCS7_bio(BIO *, PKCS7 **);
+
EVP_PKEY *d2i_PKCS8PrivateKey_bio(BIO *, EVP_PKEY **, pem_password_cb *,
void *);
@@ -45,6 +47,8 @@
int PEM_write_bio_X509_CRL(BIO *, X509_CRL *);
PKCS7 *PEM_read_bio_PKCS7(BIO *, PKCS7 **, pem_password_cb *, void *);
+int PEM_write_bio_PKCS7(BIO *, PKCS7 *);
+
DH *PEM_read_bio_DHparams(BIO *, DH **, pem_password_cb *, void *);
DSA *PEM_read_bio_DSAPrivateKey(BIO *, DSA **, pem_password_cb *, void *);
diff --git a/src/cryptography/hazmat/bindings/openssl/pkcs7.py b/src/cryptography/hazmat/bindings/openssl/pkcs7.py
index 3196fa1..df82afe 100644
--- a/src/cryptography/hazmat/bindings/openssl/pkcs7.py
+++ b/src/cryptography/hazmat/bindings/openssl/pkcs7.py
@@ -13,10 +13,37 @@
ASN1_OBJECT *type;
...;
} PKCS7;
+
+static const int PKCS7_BINARY;
+static const int PKCS7_DETACHED;
+static const int PKCS7_NOATTR;
+static const int PKCS7_NOCERTS;
+static const int PKCS7_NOCHAIN;
+static const int PKCS7_NOINTERN;
+static const int PKCS7_NOSIGS;
+static const int PKCS7_NOSMIMECAP;
+static const int PKCS7_NOVERIFY;
+static const int PKCS7_STREAM;
+static const int PKCS7_TEXT;
"""
FUNCTIONS = """
+PKCS7 *SMIME_read_PKCS7(BIO *, BIO **);
+int SMIME_write_PKCS7(BIO *, PKCS7 *, BIO *, int);
+
void PKCS7_free(PKCS7 *);
+
+PKCS7 *PKCS7_sign(X509 *, EVP_PKEY *, Cryptography_STACK_OF_X509 *,
+ BIO *, int);
+int PKCS7_verify(PKCS7 *, Cryptography_STACK_OF_X509 *, X509_STORE *, BIO *,
+ BIO *, int);
+Cryptography_STACK_OF_X509 *PKCS7_get0_signers(PKCS7 *,
+ Cryptography_STACK_OF_X509 *,
+ int);
+
+PKCS7 *PKCS7_encrypt(Cryptography_STACK_OF_X509 *, BIO *,
+ const EVP_CIPHER *, int);
+int PKCS7_decrypt(PKCS7 *, EVP_PKEY *, X509 *, BIO *, int);
"""
MACROS = """
@@ -26,7 +53,6 @@
int PKCS7_type_is_data(PKCS7 *);
"""
-CUSTOMIZATIONS = """
-"""
+CUSTOMIZATIONS = ""
CONDITIONAL_NAMES = {}
diff --git a/src/cryptography/hazmat/bindings/openssl/ssl.py b/src/cryptography/hazmat/bindings/openssl/ssl.py
index bc4b2e7..d680c3a 100644
--- a/src/cryptography/hazmat/bindings/openssl/ssl.py
+++ b/src/cryptography/hazmat/bindings/openssl/ssl.py
@@ -546,11 +546,7 @@
#else
static const long Cryptography_HAS_ALPN = 1;
#endif
-/* LibreSSL has removed support for compression, and with it the
- * COMP_METHOD use in ssl.h. This is a hack to make the function types
- * in this code match those in ssl.h.
- */
-#ifdef LIBRESSL_VERSION_NUMBER
+#if defined(OPENSSL_NO_COMP) || defined(LIBRESSL_VERSION_NUMBER)
static const long Cryptography_HAS_COMPRESSION = 0;
typedef void COMP_METHOD;
#else
diff --git a/src/cryptography/hazmat/bindings/openssl/x509.py b/src/cryptography/hazmat/bindings/openssl/x509.py
index 949a936..b5c9ee1 100644
--- a/src/cryptography/hazmat/bindings/openssl/x509.py
+++ b/src/cryptography/hazmat/bindings/openssl/x509.py
@@ -44,7 +44,10 @@
typedef ... X509_EXTENSIONS;
-typedef ... X509_REQ;
+typedef struct {
+ X509_ALGOR *sig_alg;
+ ...;
+} X509_REQ;
typedef struct {
ASN1_INTEGER *serialNumber;
@@ -165,6 +168,9 @@
int X509_REVOKED_set_serialNumber(X509_REVOKED *, ASN1_INTEGER *);
+int X509_REVOKED_get_ext_count(X509_REVOKED *);
+X509_EXTENSION *X509_REVOKED_get_ext(X509_REVOKED *, int);
+int X509_REVOKED_add_ext(X509_REVOKED *, X509_EXTENSION*, int);
int X509_REVOKED_add1_ext_i2d(X509_REVOKED *, int, void *, int, unsigned long);
X509_CRL *d2i_X509_CRL_bio(BIO *, X509_CRL **);
@@ -175,6 +181,9 @@
int X509_CRL_print(BIO *, X509_CRL *);
int X509_CRL_set_issuer_name(X509_CRL *, X509_NAME *);
int X509_CRL_sign(X509_CRL *, EVP_PKEY *, const EVP_MD *);
+int X509_CRL_get_ext_count(X509_CRL *);
+X509_EXTENSION *X509_CRL_get_ext(X509_CRL *, int);
+int X509_CRL_add_ext(X509_CRL *, X509_EXTENSION *, int);
int NETSCAPE_SPKI_verify(NETSCAPE_SPKI *, EVP_PKEY *);
int NETSCAPE_SPKI_sign(NETSCAPE_SPKI *, EVP_PKEY *, const EVP_MD *);
@@ -259,11 +268,22 @@
int sk_X509_REVOKED_num(Cryptography_STACK_OF_X509_REVOKED *);
X509_REVOKED *sk_X509_REVOKED_value(Cryptography_STACK_OF_X509_REVOKED *, int);
+Cryptography_STACK_OF_X509_CRL *sk_X509_CRL_new_null(void);
+void sk_X509_CRL_free(Cryptography_STACK_OF_X509_CRL *);
+int sk_X509_CRL_num(Cryptography_STACK_OF_X509_CRL *);
+int sk_X509_CRL_push(Cryptography_STACK_OF_X509_CRL *, X509_CRL *);
+X509_CRL *sk_X509_CRL_value(Cryptography_STACK_OF_X509_CRL *, int);
+
int i2d_RSAPublicKey(RSA *, unsigned char **);
int i2d_RSAPrivateKey(RSA *, unsigned char **);
int i2d_DSAPublicKey(DSA *, unsigned char **);
int i2d_DSAPrivateKey(DSA *, unsigned char **);
+int X509_CRL_get_version(X509_CRL *);
+ASN1_TIME *X509_CRL_get_lastUpdate(X509_CRL *);
+ASN1_TIME *X509_CRL_get_nextUpdate(X509_CRL *);
+X509_NAME *X509_CRL_get_issuer(X509_CRL *);
+
/* These aren't macros these arguments are all const X on openssl > 1.0.x */
int X509_CRL_set_lastUpdate(X509_CRL *, ASN1_TIME *);
int X509_CRL_set_nextUpdate(X509_CRL *, ASN1_TIME *);
diff --git a/src/cryptography/x509.py b/src/cryptography/x509.py
index 29602b3..697d7d6 100644
--- a/src/cryptography/x509.py
+++ b/src/cryptography/x509.py
@@ -42,8 +42,30 @@
"1.2.840.10040.4.3": "dsa-with-sha1",
"2.16.840.1.101.3.4.3.1": "dsa-with-sha224",
"2.16.840.1.101.3.4.3.2": "dsa-with-sha256",
- "2.5.29.19": "basicConstraints",
+ "1.3.6.1.5.5.7.3.1": "serverAuth",
+ "1.3.6.1.5.5.7.3.2": "clientAuth",
+ "1.3.6.1.5.5.7.3.3": "codeSigning",
+ "1.3.6.1.5.5.7.3.4": "emailProtection",
+ "1.3.6.1.5.5.7.3.8": "timeStamping",
+ "1.3.6.1.5.5.7.3.9": "OCSPSigning",
+ "2.5.29.9": "subjectDirectoryAttributes",
+ "2.5.29.14": "subjectKeyIdentifier",
"2.5.29.15": "keyUsage",
+ "2.5.29.17": "subjectAltName",
+ "2.5.29.18": "issuerAltName",
+ "2.5.29.19": "basicConstraints",
+ "2.5.29.30": "nameConstraints",
+ "2.5.29.31": "cRLDistributionPoints",
+ "2.5.29.32": "certificatePolicies",
+ "2.5.29.33": "policyMappings",
+ "2.5.29.35": "authorityKeyIdentifier",
+ "2.5.29.36": "policyConstraints",
+ "2.5.29.37": "extendedKeyUsage",
+ "2.5.29.46": "freshestCRL",
+ "2.5.29.54": "inhibitAnyPolicy",
+ "1.3.6.1.5.5.7.1.1": "authorityInfoAccess",
+ "1.3.6.1.5.5.7.1.11": "subjectInfoAccess",
+ "1.3.6.1.5.5.7.48.1.5": "OCSPNoCheck",
}
@@ -60,6 +82,14 @@
return backend.load_der_x509_certificate(data)
+def load_pem_x509_csr(data, backend):
+ return backend.load_pem_x509_csr(data)
+
+
+def load_der_x509_csr(data, backend):
+ return backend.load_der_x509_csr(data)
+
+
class InvalidVersion(Exception):
def __init__(self, msg, parsed_version):
super(InvalidVersion, self).__init__(msg)
@@ -78,6 +108,12 @@
self.oid = oid
+class ExtensionNotFound(Exception):
+ def __init__(self, msg, oid):
+ super(ExtensionNotFound, self).__init__(msg)
+ self.oid = oid
+
+
class NameAttribute(object):
def __init__(self, oid, value):
if not isinstance(oid, ObjectIdentifier):
@@ -155,14 +191,37 @@
return len(self._attributes)
+OID_SUBJECT_DIRECTORY_ATTRIBUTES = ObjectIdentifier("2.5.29.9")
+OID_SUBJECT_KEY_IDENTIFIER = ObjectIdentifier("2.5.29.14")
OID_KEY_USAGE = ObjectIdentifier("2.5.29.15")
+OID_SUBJECT_ALTERNATIVE_NAME = ObjectIdentifier("2.5.29.17")
+OID_ISSUER_ALTERNATIVE_NAME = ObjectIdentifier("2.5.29.18")
OID_BASIC_CONSTRAINTS = ObjectIdentifier("2.5.29.19")
+OID_NAME_CONSTRAINTS = ObjectIdentifier("2.5.29.30")
+OID_CRL_DISTRIBUTION_POINTS = ObjectIdentifier("2.5.29.31")
+OID_CERTIFICATE_POLICIES = ObjectIdentifier("2.5.29.32")
+OID_POLICY_MAPPINGS = ObjectIdentifier("2.5.29.33")
+OID_AUTHORITY_KEY_IDENTIFIER = ObjectIdentifier("2.5.29.35")
+OID_POLICY_CONSTRAINTS = ObjectIdentifier("2.5.29.36")
+OID_EXTENDED_KEY_USAGE = ObjectIdentifier("2.5.29.37")
+OID_FRESHEST_CRL = ObjectIdentifier("2.5.29.46")
+OID_INHIBIT_ANY_POLICY = ObjectIdentifier("2.5.29.54")
+OID_AUTHORITY_INFORMATION_ACCESS = ObjectIdentifier("1.3.6.1.5.5.7.1.1")
+OID_SUBJECT_INFORMATION_ACCESS = ObjectIdentifier("1.3.6.1.5.5.7.1.11")
+OID_OCSP_NO_CHECK = ObjectIdentifier("1.3.6.1.5.5.7.48.1.5")
class Extensions(object):
def __init__(self, extensions):
self._extensions = extensions
+ def get_extension_for_oid(self, oid):
+ for ext in self:
+ if ext.oid == oid:
+ return ext
+
+ raise ExtensionNotFound("No {0} extension was found".format(oid), oid)
+
def __iter__(self):
return iter(self._extensions)
@@ -193,6 +252,26 @@
"value={0.value})>").format(self)
+class ExtendedKeyUsage(object):
+ def __init__(self, usages):
+ for oid in usages:
+ if not isinstance(oid, ObjectIdentifier):
+ raise TypeError(
+ "Every item in the usages list must be an ObjectIdentifier"
+ )
+
+ self._usages = usages
+
+ def __iter__(self):
+ return iter(self._usages)
+
+ def __len__(self):
+ return len(self._usages)
+
+ def __repr__(self):
+ return "<ExtendedKeyUsage({0})>".format(self._usages)
+
+
class BasicConstraints(object):
def __init__(self, ca, path_length):
if not isinstance(ca, bool):
@@ -220,6 +299,53 @@
"path_length={0.path_length})>").format(self)
+class KeyUsage(object):
+ def __init__(self, digital_signature, content_commitment, key_encipherment,
+ data_encipherment, key_agreement, key_cert_sign, crl_sign,
+ encipher_only, decipher_only):
+ if not key_agreement and (encipher_only or decipher_only):
+ raise ValueError(
+ "encipher_only and decipher_only can only be true when "
+ "key_agreement is true"
+ )
+
+ self._digital_signature = digital_signature
+ self._content_commitment = content_commitment
+ self._key_encipherment = key_encipherment
+ self._data_encipherment = data_encipherment
+ self._key_agreement = key_agreement
+ self._key_cert_sign = key_cert_sign
+ self._crl_sign = crl_sign
+ self._encipher_only = encipher_only
+ self._decipher_only = decipher_only
+
+ digital_signature = utils.read_only_property("_digital_signature")
+ content_commitment = utils.read_only_property("_content_commitment")
+ key_encipherment = utils.read_only_property("_key_encipherment")
+ data_encipherment = utils.read_only_property("_data_encipherment")
+ key_agreement = utils.read_only_property("_key_agreement")
+ key_cert_sign = utils.read_only_property("_key_cert_sign")
+ crl_sign = utils.read_only_property("_crl_sign")
+
+ @property
+ def encipher_only(self):
+ if not self.key_agreement:
+ raise ValueError(
+ "encipher_only is undefined unless key_agreement is true"
+ )
+ else:
+ return self._encipher_only
+
+ @property
+ def decipher_only(self):
+ if not self.key_agreement:
+ raise ValueError(
+ "decipher_only is undefined unless key_agreement is true"
+ )
+ else:
+ return self._decipher_only
+
+
OID_COMMON_NAME = ObjectIdentifier("2.5.4.3")
OID_COUNTRY_NAME = ObjectIdentifier("2.5.4.6")
OID_LOCALITY_NAME = ObjectIdentifier("2.5.4.7")
@@ -266,6 +392,13 @@
OID_DSA_WITH_SHA256.dotted_string: hashes.SHA256()
}
+OID_SERVER_AUTH = ObjectIdentifier("1.3.6.1.5.5.7.3.1")
+OID_CLIENT_AUTH = ObjectIdentifier("1.3.6.1.5.5.7.3.2")
+OID_CODE_SIGNING = ObjectIdentifier("1.3.6.1.5.5.7.3.3")
+OID_EMAIL_PROTECTION = ObjectIdentifier("1.3.6.1.5.5.7.3.4")
+OID_TIME_STAMPING = ObjectIdentifier("1.3.6.1.5.5.7.3.8")
+OID_OCSP_SIGNING = ObjectIdentifier("1.3.6.1.5.5.7.3.9")
+
@six.add_metaclass(abc.ABCMeta)
class Certificate(object):
@@ -323,3 +456,25 @@
Returns a HashAlgorithm corresponding to the type of the digest signed
in the certificate.
"""
+
+
+@six.add_metaclass(abc.ABCMeta)
+class CertificateSigningRequest(object):
+ @abc.abstractmethod
+ def public_key(self):
+ """
+ Returns the public key
+ """
+
+ @abc.abstractproperty
+ def subject(self):
+ """
+ Returns the subject name object.
+ """
+
+ @abc.abstractproperty
+ def signature_hash_algorithm(self):
+ """
+ Returns a HashAlgorithm corresponding to the type of the digest signed
+ in the certificate.
+ """
diff --git a/tests/hazmat/backends/test_multibackend.py b/tests/hazmat/backends/test_multibackend.py
index 5a8891c..5871e6c 100644
--- a/tests/hazmat/backends/test_multibackend.py
+++ b/tests/hazmat/backends/test_multibackend.py
@@ -197,6 +197,12 @@
def load_der_x509_certificate(self, data):
pass
+ def load_pem_x509_csr(self, data):
+ pass
+
+ def load_der_x509_csr(self, data):
+ pass
+
class TestMultiBackend(object):
def test_ciphers(self):
@@ -472,9 +478,15 @@
backend.load_pem_x509_certificate(b"certdata")
backend.load_der_x509_certificate(b"certdata")
+ backend.load_pem_x509_csr(b"reqdata")
+ backend.load_der_x509_csr(b"reqdata")
backend = MultiBackend([])
with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_X509):
backend.load_pem_x509_certificate(b"certdata")
with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_X509):
backend.load_der_x509_certificate(b"certdata")
+ with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_X509):
+ backend.load_pem_x509_csr(b"reqdata")
+ with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_X509):
+ backend.load_der_x509_csr(b"reqdata")
diff --git a/tests/test_x509.py b/tests/test_x509.py
index 2a47268..dc148d9 100644
--- a/tests/test_x509.py
+++ b/tests/test_x509.py
@@ -340,6 +340,51 @@
with pytest.raises(UnsupportedAlgorithm):
cert.signature_hash_algorithm
+ @pytest.mark.parametrize(
+ ("path", "loader_func"),
+ [
+ [
+ os.path.join("x509", "requests", "rsa_sha1.pem"),
+ x509.load_pem_x509_csr
+ ],
+ [
+ os.path.join("x509", "requests", "rsa_sha1.der"),
+ x509.load_der_x509_csr
+ ],
+ ]
+ )
+ def test_load_rsa_certificate_request(self, path, loader_func, backend):
+ request = _load_cert(path, loader_func, backend)
+ 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, 'US'),
+ x509.NameAttribute(x509.OID_STATE_OR_PROVINCE_NAME, 'Texas'),
+ x509.NameAttribute(x509.OID_LOCALITY_NAME, 'Austin'),
+ x509.NameAttribute(x509.OID_ORGANIZATION_NAME, 'PyCA'),
+ x509.NameAttribute(x509.OID_COMMON_NAME, 'cryptography.io'),
+ ]
+
+ @pytest.mark.parametrize(
+ "loader_func",
+ [x509.load_pem_x509_csr, x509.load_der_x509_csr]
+ )
+ def test_invalid_certificate_request(self, loader_func, backend):
+ with pytest.raises(ValueError):
+ loader_func(b"notacsr", backend)
+
+ def test_unsupported_signature_hash_algorithm_request(self, backend):
+ request = _load_cert(
+ os.path.join("x509", "requests", "rsa_md4.pem"),
+ x509.load_pem_x509_csr,
+ backend
+ )
+ with pytest.raises(UnsupportedAlgorithm):
+ request.signature_hash_algorithm
+
@pytest.mark.requires_backend_interface(interface=DSABackend)
@pytest.mark.requires_backend_interface(interface=X509Backend)
@@ -392,6 +437,34 @@
"822ff5d234e073b901cf5941f58e1f538e71d40d", 16
)
+ @pytest.mark.parametrize(
+ ("path", "loader_func"),
+ [
+ [
+ os.path.join("x509", "requests", "dsa_sha1.pem"),
+ x509.load_pem_x509_csr
+ ],
+ [
+ os.path.join("x509", "requests", "dsa_sha1.der"),
+ x509.load_der_x509_csr
+ ],
+ ]
+ )
+ def test_load_dsa_request(self, path, loader_func, backend):
+ request = _load_cert(path, loader_func, backend)
+ 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_COMMON_NAME, 'cryptography.io'),
+ x509.NameAttribute(x509.OID_ORGANIZATION_NAME, 'PyCA'),
+ x509.NameAttribute(x509.OID_COUNTRY_NAME, 'US'),
+ x509.NameAttribute(x509.OID_STATE_OR_PROVINCE_NAME, 'Texas'),
+ x509.NameAttribute(x509.OID_LOCALITY_NAME, 'Austin'),
+ ]
+
@pytest.mark.requires_backend_interface(interface=EllipticCurveBackend)
@pytest.mark.requires_backend_interface(interface=X509Backend)
@@ -428,6 +501,35 @@
with pytest.raises(NotImplementedError):
cert.public_key()
+ @pytest.mark.parametrize(
+ ("path", "loader_func"),
+ [
+ [
+ os.path.join("x509", "requests", "ec_sha256.pem"),
+ x509.load_pem_x509_csr
+ ],
+ [
+ os.path.join("x509", "requests", "ec_sha256.der"),
+ x509.load_der_x509_csr
+ ],
+ ]
+ )
+ def test_load_ecdsa_certificate_request(self, path, loader_func, backend):
+ _skip_curve_unsupported(backend, ec.SECP384R1())
+ request = _load_cert(path, loader_func, backend)
+ assert isinstance(request.signature_hash_algorithm, hashes.SHA256)
+ 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_COMMON_NAME, 'cryptography.io'),
+ x509.NameAttribute(x509.OID_ORGANIZATION_NAME, 'PyCA'),
+ x509.NameAttribute(x509.OID_COUNTRY_NAME, 'US'),
+ x509.NameAttribute(x509.OID_STATE_OR_PROVINCE_NAME, 'Texas'),
+ x509.NameAttribute(x509.OID_LOCALITY_NAME, 'Austin'),
+ ]
+
class TestNameAttribute(object):
def test_init_bad_oid(self):
diff --git a/tests/test_x509_ext.py b/tests/test_x509_ext.py
index d828152..c1512d5 100644
--- a/tests/test_x509_ext.py
+++ b/tests/test_x509_ext.py
@@ -35,6 +35,103 @@
)
+class TestKeyUsage(object):
+ def test_key_agreement_false_encipher_decipher_true(self):
+ with pytest.raises(ValueError):
+ x509.KeyUsage(
+ digital_signature=False,
+ content_commitment=False,
+ key_encipherment=False,
+ data_encipherment=False,
+ key_agreement=False,
+ key_cert_sign=False,
+ crl_sign=False,
+ encipher_only=True,
+ decipher_only=False
+ )
+
+ with pytest.raises(ValueError):
+ x509.KeyUsage(
+ digital_signature=False,
+ content_commitment=False,
+ key_encipherment=False,
+ data_encipherment=False,
+ key_agreement=False,
+ key_cert_sign=False,
+ crl_sign=False,
+ encipher_only=True,
+ decipher_only=True
+ )
+
+ with pytest.raises(ValueError):
+ x509.KeyUsage(
+ digital_signature=False,
+ content_commitment=False,
+ key_encipherment=False,
+ data_encipherment=False,
+ key_agreement=False,
+ key_cert_sign=False,
+ crl_sign=False,
+ encipher_only=False,
+ decipher_only=True
+ )
+
+ def test_properties_key_agreement_true(self):
+ ku = 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
+ )
+ assert ku.digital_signature is True
+ assert ku.content_commitment is True
+ assert ku.key_encipherment is False
+ assert ku.data_encipherment is False
+ assert ku.key_agreement is False
+ assert ku.key_cert_sign is True
+ assert ku.crl_sign is False
+
+ def test_key_agreement_true_properties(self):
+ ku = x509.KeyUsage(
+ digital_signature=False,
+ content_commitment=False,
+ key_encipherment=False,
+ data_encipherment=False,
+ key_agreement=True,
+ key_cert_sign=False,
+ crl_sign=False,
+ encipher_only=False,
+ decipher_only=True
+ )
+ assert ku.key_agreement is True
+ assert ku.encipher_only is False
+ assert ku.decipher_only is True
+
+ def test_key_agreement_false_properties(self):
+ ku = x509.KeyUsage(
+ digital_signature=False,
+ content_commitment=False,
+ key_encipherment=False,
+ data_encipherment=False,
+ key_agreement=False,
+ key_cert_sign=False,
+ crl_sign=False,
+ encipher_only=False,
+ decipher_only=False
+ )
+ assert ku.key_agreement is False
+ with pytest.raises(ValueError):
+ ku.encipher_only
+
+ with pytest.raises(ValueError):
+ ku.decipher_only
+
+
class TestBasicConstraints(object):
def test_ca_not_boolean(self):
with pytest.raises(TypeError):
@@ -62,6 +159,34 @@
)
+class TestExtendedKeyUsage(object):
+ def test_not_all_oids(self):
+ with pytest.raises(TypeError):
+ x509.ExtendedKeyUsage(["notoid"])
+
+ def test_iter_len(self):
+ eku = x509.ExtendedKeyUsage([
+ x509.ObjectIdentifier("1.3.6.1.5.5.7.3.1"),
+ x509.ObjectIdentifier("1.3.6.1.5.5.7.3.2"),
+ ])
+ assert len(eku) == 2
+ assert list(eku) == [
+ x509.OID_SERVER_AUTH,
+ x509.OID_CLIENT_AUTH
+ ]
+
+ def test_repr(self):
+ eku = x509.ExtendedKeyUsage([
+ x509.ObjectIdentifier("1.3.6.1.5.5.7.3.1"),
+ x509.ObjectIdentifier("1.3.6.1.5.5.7.3.2"),
+ ])
+ assert repr(eku) == (
+ "<ExtendedKeyUsage([<ObjectIdentifier(oid=1.3.6.1.5.5.7.3.1, name="
+ "serverAuth)>, <ObjectIdentifier(oid=1.3.6.1.5.5.7.3.2, name=clien"
+ "tAuth)>])>"
+ )
+
+
@pytest.mark.requires_backend_interface(interface=RSABackend)
@pytest.mark.requires_backend_interface(interface=X509Backend)
class TestExtensions(object):
@@ -74,6 +199,23 @@
ext = cert.extensions
assert len(ext) == 0
assert list(ext) == []
+ with pytest.raises(x509.ExtensionNotFound) as exc:
+ ext.get_extension_for_oid(x509.OID_BASIC_CONSTRAINTS)
+
+ assert exc.value.oid == x509.OID_BASIC_CONSTRAINTS
+
+ def test_one_extension(self, backend):
+ cert = _load_cert(
+ os.path.join(
+ "x509", "custom", "basic_constraints_not_critical.pem"
+ ),
+ x509.load_pem_x509_certificate,
+ backend
+ )
+ extensions = cert.extensions
+ ext = extensions.get_extension_for_oid(x509.OID_BASIC_CONSTRAINTS)
+ assert ext is not None
+ assert ext.value.ca is False
def test_duplicate_extension(self, backend):
cert = _load_cert(
@@ -112,3 +254,94 @@
)
extensions = cert.extensions
assert len(extensions) == 0
+
+
+@pytest.mark.requires_backend_interface(interface=RSABackend)
+@pytest.mark.requires_backend_interface(interface=X509Backend)
+class TestBasicConstraintsExtension(object):
+ def test_ca_true_pathlen_6(self, backend):
+ cert = _load_cert(
+ os.path.join(
+ "x509", "PKITS_data", "certs", "pathLenConstraint6CACert.crt"
+ ),
+ x509.load_der_x509_certificate,
+ backend
+ )
+ ext = cert.extensions.get_extension_for_oid(
+ x509.OID_BASIC_CONSTRAINTS
+ )
+ assert ext is not None
+ assert ext.critical is True
+ assert ext.value.ca is True
+ assert ext.value.path_length == 6
+
+ def test_path_length_zero(self, backend):
+ cert = _load_cert(
+ os.path.join("x509", "custom", "bc_path_length_zero.pem"),
+ x509.load_pem_x509_certificate,
+ backend
+ )
+ ext = cert.extensions.get_extension_for_oid(
+ x509.OID_BASIC_CONSTRAINTS
+ )
+ assert ext is not None
+ assert ext.critical is True
+ assert ext.value.ca is True
+ assert ext.value.path_length == 0
+
+ def test_ca_true_no_pathlen(self, backend):
+ cert = _load_cert(
+ os.path.join("x509", "PKITS_data", "certs", "GoodCACert.crt"),
+ x509.load_der_x509_certificate,
+ backend
+ )
+ ext = cert.extensions.get_extension_for_oid(
+ x509.OID_BASIC_CONSTRAINTS
+ )
+ assert ext is not None
+ assert ext.critical is True
+ assert ext.value.ca is True
+ assert ext.value.path_length is None
+
+ def test_ca_false(self, backend):
+ cert = _load_cert(
+ os.path.join("x509", "cryptography.io.pem"),
+ x509.load_pem_x509_certificate,
+ backend
+ )
+ ext = cert.extensions.get_extension_for_oid(
+ x509.OID_BASIC_CONSTRAINTS
+ )
+ assert ext is not None
+ assert ext.critical is True
+ assert ext.value.ca is False
+ assert ext.value.path_length is None
+
+ def test_no_basic_constraints(self, backend):
+ cert = _load_cert(
+ os.path.join(
+ "x509",
+ "PKITS_data",
+ "certs",
+ "ValidCertificatePathTest1EE.crt"
+ ),
+ x509.load_der_x509_certificate,
+ backend
+ )
+ with pytest.raises(x509.ExtensionNotFound):
+ cert.extensions.get_extension_for_oid(x509.OID_BASIC_CONSTRAINTS)
+
+ def test_basic_constraint_not_critical(self, backend):
+ cert = _load_cert(
+ os.path.join(
+ "x509", "custom", "basic_constraints_not_critical.pem"
+ ),
+ x509.load_pem_x509_certificate,
+ backend
+ )
+ ext = cert.extensions.get_extension_for_oid(
+ x509.OID_BASIC_CONSTRAINTS
+ )
+ assert ext is not None
+ assert ext.critical is False
+ assert ext.value.ca is False
diff --git a/vectors/cryptography_vectors/x509/custom/all_key_usages.pem b/vectors/cryptography_vectors/x509/custom/all_key_usages.pem
new file mode 100644
index 0000000..e24caf2
--- /dev/null
+++ b/vectors/cryptography_vectors/x509/custom/all_key_usages.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC6TCCAdGgAwIBAgITBmdJiX54UcRwde48tbHzferkszANBgkqhkiG9w0BAQUF
+ADAmMRUwEwYDVQQDDAxjcnlwdG9ncmFwaHkxDTALBgNVBAoMBFB5Q0EwHhcNMTUw
+NDAyMTgyMzIwWhcNMTYwNDAxMTgyMzIwWjAmMRUwEwYDVQQDDAxjcnlwdG9ncmFw
+aHkxDTALBgNVBAoMBFB5Q0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
+AQCtKAh5oo9zN6ATc8c7+kJNpi9uOOE1650X6H6F4XpDQGmg7uoOGpvZqi4BwkiJ
+6tE6IR9qmfuksrSmVraBFGfXLKVknLBpB9aAAeBv7Kh7nrPfd39fbliwbW7NJIHH
++nG02FLPPMOdUKNyfTY2+kthkrjGCjYqOTEIUkFLgxk3/V2bmTSEiNGmi/1qQl9Y
+pRJlaMYhDkS01Ox7/20uYI/S8EAXWQefV23szoesiiS6QvVncBRUeYCj+rQsr2+p
+wXz1TWypLEukjo+C6SyjVEYUMUwYKg/0hBMQIpnAWuiNiiJArWf+l9a4O1SgsPG7
+nwWClir6jr33LBD5DF1cJCS3AgMBAAGjEDAOMAwGA1UdDwQFAwMH/4AwDQYJKoZI
+hvcNAQEFBQADggEBAEJUuCiqMQYZowPi9OmyHGk7vAxh2MCKDQJDI1DhNdCPNoOl
+nGSrNiFVRh6PAh3i+QSoh3pvbFvP0pCgasoaukqxKPK9pCKzBrwwsA7U7hvtJlIp
+gOb5RG55mPDl5SxSJyHlOPHotG9ACeQOvbfqn3KM9Jn5aBir/laRKsSrM/daeeZ8
+4LQOb5pSNK41NKxeidm1AdNEMt33duYkhWZ63gviYvr6ri+3OOHhlFZeCI297TW9
+dHZpYMwi3hN7jYJLh5NFBNlnngG92lMcYfSBntjeCN2uPwO72utKMYb3kF/JxglX
+dxeA+zWQjvhx3s2Zt8/N10JnnbKImPJdCA59X3M=
+-----END CERTIFICATE-----
diff --git a/vectors/cryptography_vectors/x509/requests/dsa_sha1.der b/vectors/cryptography_vectors/x509/requests/dsa_sha1.der
new file mode 100644
index 0000000..35276d5
--- /dev/null
+++ b/vectors/cryptography_vectors/x509/requests/dsa_sha1.der
Binary files differ
diff --git a/vectors/cryptography_vectors/x509/requests/ec_sha256.der b/vectors/cryptography_vectors/x509/requests/ec_sha256.der
new file mode 100644
index 0000000..3a0cde2
--- /dev/null
+++ b/vectors/cryptography_vectors/x509/requests/ec_sha256.der
Binary files differ
diff --git a/vectors/cryptography_vectors/x509/requests/rsa_md4.der b/vectors/cryptography_vectors/x509/requests/rsa_md4.der
new file mode 100644
index 0000000..e9a3e95
--- /dev/null
+++ b/vectors/cryptography_vectors/x509/requests/rsa_md4.der
Binary files differ
diff --git a/vectors/cryptography_vectors/x509/requests/rsa_sha1.der b/vectors/cryptography_vectors/x509/requests/rsa_sha1.der
new file mode 100644
index 0000000..5bd6314
--- /dev/null
+++ b/vectors/cryptography_vectors/x509/requests/rsa_sha1.der
Binary files differ
diff --git a/vectors/cryptography_vectors/x509/requests/rsa_sha256.der b/vectors/cryptography_vectors/x509/requests/rsa_sha256.der
new file mode 100644
index 0000000..5c19775
--- /dev/null
+++ b/vectors/cryptography_vectors/x509/requests/rsa_sha256.der
Binary files differ
diff --git a/vectors/cryptography_vectors/x509/requests/san_rsa_sha1.der b/vectors/cryptography_vectors/x509/requests/san_rsa_sha1.der
new file mode 100644
index 0000000..c950e2e
--- /dev/null
+++ b/vectors/cryptography_vectors/x509/requests/san_rsa_sha1.der
Binary files differ
diff --git a/vectors/cryptography_vectors/x509/requests/san_rsa_sha1.pem b/vectors/cryptography_vectors/x509/requests/san_rsa_sha1.pem
new file mode 100644
index 0000000..1db2bc1
--- /dev/null
+++ b/vectors/cryptography_vectors/x509/requests/san_rsa_sha1.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE REQUEST-----
+MIIC4jCCAcoCAQAwWzELMAkGA1UEBhMCVVMxETAPBgNVBAgMCElsbGlub2lzMRAw
+DgYDVQQHDAdDaGljYWdvMQ0wCwYDVQQKDARQeUNBMRgwFgYDVQQDDA9jcnlwdG9n
+cmFwaHkuaW8wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCqNTQULxLj
+k/RirTqUfg/5PSUUQeBiNJd7aHIUfe/uXm+h+JV/wx+7pKV4VfLsog3Vc5NKwnt1
+5v2HUkrT4IfcU7bBKKBYENc8R+HqqcLDUtmW+XntuwJmLr/HX1kAT1XuaGorzTkf
+SMVYlHlA7QagB8/jpxZATbmcHNPK4dUnlY+W70jO12hKtDYaIviHpCtIMNHLXso6
+xZS1tDr9a8DK2rFJ3OnPnoqPyhNkrUKt2tUC5aej2hZlewdZbKHax0xk+q4wTxT1
+qNQ9YiysHdnUdgzqXUJBIoqg/WKMxT2lUdF18nl5/DRQfJ2Ci49/T0AMnzt4PvJN
+bxDf2uViMjAhAgMBAAGgQjBABgkqhkiG9w0BCQ4xMzAxMC8GA1UdEQQoMCaCD2Ny
+eXB0b2dyYXBoeS5pb4ITc3ViLmNyeXB0b2dyYXBoeS5pbzANBgkqhkiG9w0BAQUF
+AAOCAQEAavgmRwSFd8ASj5mQl03WdI+j+U2HIUhqiNU7taqM00FPcGdGs2nLApu3
+jH1MzZJ47pZD+Aqcb/8HD6MRYBgK8tH2pPdn7Z6cQZ1+cBBvOvXn771RPk+bQufs
+J3lYe2eK6NZKY7a3nqCNCDK6p4sOnIF49GoXLABDO5/2iwCs7Zb1SOxw9YVoUQT8
+qzuO7DWO0ojKB7zUl9GEeKGiLvUZCi9cP7eA0Jf4ojrFeT6cwUr9g61h+bLLAOX0
+knAQVheFwSx2jeQk+J12ga8HCscst9Mx7IeZ+blADEXFALDXoVk8M+ZftvbaXABw
+VUrJdCoVGtWDIk58Pvw71M+HqbQ3dg==
+-----END CERTIFICATE REQUEST-----