aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.rst45
-rw-r--r--docs/development/test-vectors.rst34
-rw-r--r--docs/hazmat/primitives/asymmetric/dh.rst64
-rw-r--r--docs/hazmat/primitives/asymmetric/dsa.rst44
-rw-r--r--docs/hazmat/primitives/asymmetric/ec.rst69
-rw-r--r--docs/hazmat/primitives/asymmetric/index.rst1
-rw-r--r--docs/hazmat/primitives/asymmetric/rsa.rst130
-rw-r--r--docs/hazmat/primitives/asymmetric/serialization.rst105
-rw-r--r--docs/hazmat/primitives/symmetric-encryption.rst5
-rw-r--r--docs/hazmat/primitives/twofactor.rst4
-rw-r--r--docs/limitations.rst2
-rw-r--r--docs/spelling_wordlist.txt3
-rw-r--r--src/cryptography/hazmat/backends/openssl/backend.py139
-rw-r--r--src/cryptography/hazmat/backends/openssl/dsa.py26
-rw-r--r--src/cryptography/hazmat/backends/openssl/ec.py42
-rw-r--r--src/cryptography/hazmat/backends/openssl/rsa.py27
-rw-r--r--src/cryptography/hazmat/bindings/openssl/dh.py4
-rw-r--r--src/cryptography/hazmat/bindings/openssl/err.py5
-rw-r--r--src/cryptography/hazmat/bindings/openssl/pem.py16
-rw-r--r--src/cryptography/hazmat/primitives/asymmetric/dh.py101
-rw-r--r--src/cryptography/hazmat/primitives/asymmetric/dsa.py19
-rw-r--r--src/cryptography/hazmat/primitives/asymmetric/ec.py38
-rw-r--r--src/cryptography/hazmat/primitives/asymmetric/rsa.py38
-rw-r--r--src/cryptography/hazmat/primitives/interfaces/__init__.py13
-rw-r--r--src/cryptography/hazmat/primitives/padding.py48
-rw-r--r--src/cryptography/hazmat/primitives/serialization.py37
-rw-r--r--src/cryptography/hazmat/primitives/src/padding.c39
-rw-r--r--src/cryptography/hazmat/primitives/src/padding.h5
-rw-r--r--src/cryptography/utils.py4
-rw-r--r--tests/hazmat/backends/test_openssl.py35
-rw-r--r--tests/hazmat/primitives/test_dh.py113
-rw-r--r--tests/hazmat/primitives/test_dsa.py177
-rw-r--r--tests/hazmat/primitives/test_ec.py246
-rw-r--r--tests/hazmat/primitives/test_rsa.py183
-rw-r--r--tests/hazmat/primitives/test_serialization.py85
-rw-r--r--vectors/cryptography_vectors/asymmetric/public/PKCS1/dsa.pub.pem7
-rw-r--r--vectors/cryptography_vectors/asymmetric/public/PKCS1/rsa.pub.derbin0 -> 140 bytes
-rw-r--r--vectors/cryptography_vectors/asymmetric/public/PKCS1/rsa.pub.pem5
-rw-r--r--vectors/cryptography_vectors/x509/requests/dsa_sha1.pem15
-rw-r--r--vectors/cryptography_vectors/x509/requests/ec_sha256.pem10
-rw-r--r--vectors/cryptography_vectors/x509/requests/rsa_md4.pem16
-rw-r--r--vectors/cryptography_vectors/x509/requests/rsa_sha1.pem16
-rw-r--r--vectors/cryptography_vectors/x509/requests/rsa_sha256.pem16
43 files changed, 1893 insertions, 138 deletions
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index da529f68..e45a4ae6 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -78,6 +78,51 @@ Changelog
support loading DER encoded public keys.
* Fixed building against LibreSSL, a compile-time substitute for OpenSSL.
* FreeBSD 9.2 was removed from the continuous integration system.
+* Updated Windows wheels to be compiled against OpenSSL 1.0.2.
+* :func:`~cryptography.hazmat.primitives.serialization.load_pem_public_key`
+ and :func:`~cryptography.hazmat.primitives.serialization.load_der_public_key`
+ now support PKCS1 RSA public keys (in addition to the previous support for
+ SubjectPublicKeyInfo format for RSA, EC, and DSA).
+* Added
+ :class:`~cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePrivateKeyWithSerialization`
+ and deprecated
+ :class:`~cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePrivateKeyWithNumbers`.
+* Added
+ :meth:`~cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePrivateKeyWithSerialization.private_bytes`
+ to
+ :class:`~cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePrivateKeyWithSerialization`.
+* Added
+ :class:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateKeyWithSerialization`
+ and deprecated
+ :class:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateKeyWithNumbers`.
+* Added
+ :meth:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateKeyWithSerialization.private_bytes`
+ to
+ :class:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateKeyWithSerialization`.
+* Added
+ :class:`~cryptography.hazmat.primitives.asymmetric.dsa.DSAPrivateKeyWithSerialization`
+ and deprecated
+ :class:`~cryptography.hazmat.primitives.asymmetric.dsa.DSAPrivateKeyWithNumbers`.
+* Added
+ :meth:`~cryptography.hazmat.primitives.asymmetric.dsa.DSAPrivateKeyWithSerialization.private_bytes`
+ to
+ :class:`~cryptography.hazmat.primitives.asymmetric.dsa.DSAPrivateKeyWithSerialization`.
+* Added
+ :class:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPublicKeyWithSerialization`
+ and deprecated
+ :class:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPublicKeyWithNumbers`.
+* Added
+ :meth:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPublicKeyWithSerialization.public_bytes`
+ to
+ :class:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPublicKeyWithSerialization`.
+* Added
+ :class:`~cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePublicKeyWithSerialization`
+ and deprecated
+ :class:`~cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePublicKeyWithNumbers`.
+* Added
+ :meth:`~cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePublicKeyWithSerialization.public_bytes`
+ to
+ :class:`~cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePublicKeyWithSerialization`.
0.7.2 - 2015-01-16
~~~~~~~~~~~~~~~~~~
diff --git a/docs/development/test-vectors.rst b/docs/development/test-vectors.rst
index 2cd9faa6..68b85291 100644
--- a/docs/development/test-vectors.rst
+++ b/docs/development/test-vectors.rst
@@ -33,6 +33,9 @@ Asymmetric ciphers
`unenc-rsa-pkcs8.pem`_, `pkcs12_s2k_pem.c`_. The contents of
`enc2-rsa-pkcs8.pem`_ was re-encrypted using a stronger PKCS#8 cipher.
* `Botan's ECC private keys`_.
+* `asymmetric/public/PKCS1/dsa.pub.pem`_ is a PKCS1 DSA public key from the
+ Ruby test suite.
+
Custom Asymmetric Vectors
~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -71,7 +74,9 @@ Custom Asymmetric Vectors
`OpenSSL example key`_ for RSA.
* DER conversions of `enc-rsa-pkcs8.pem`_, `enc2-rsa-pkcs8.pem`_, and
`unenc-rsa-pkcs8.pem`_.
-
+* ``asymmetric/public/PKCS1/rsa.pub.pem`` and
+ ``asymmetric/public/PKCS1/rsa.pub.der`` are PKCS1 conversions of the public
+ key from ``asymmetric/PKCS8/unenc-rsa-pkcs8.pem`` using PEM and DER encoding.
X.509
~~~~~
@@ -104,6 +109,20 @@ Custom X.509 Vectors
generated using OpenSSL that contains a UTF8String common name with the value
"We heart UTF8!™".
+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
+ 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
~~~~~~
@@ -207,12 +226,12 @@ header format (substituting the correct information):
.. _`draft RFC`: https://tools.ietf.org/html/draft-josefsson-scrypt-kdf-01
.. _`Specification repository`: https://github.com/fernet/spec
.. _`errata`: http://www.rfc-editor.org/errata_search.php?rfc=6238
-.. _`OpenSSL example key`: http://git.openssl.org/gitweb/?p=openssl.git;a=blob;f=test/testrsa.pem;h=aad21067a8f7cb93a52a511eb9162fd83be39135;hb=66e8211c0b1347970096e04b18aa52567c325200
-.. _`GnuTLS key parsing tests`: https://gitorious.org/gnutls/gnutls/commit/f16ef39ef0303b02d7fa590a37820440c466ce8d
-.. _`enc-rsa-pkcs8.pem`: https://gitorious.org/gnutls/gnutls/source/f8d943b38bf74eaaa11d396112daf43cb8aa82ae:tests/pkcs8-decode/encpkcs8.pem
-.. _`enc2-rsa-pkcs8.pem`: https://gitorious.org/gnutls/gnutls/source/f8d943b38bf74eaaa11d396112daf43cb8aa82ae:tests/pkcs8-decode/enc2pkcs8.pem
-.. _`unenc-rsa-pkcs8.pem`: https://gitorious.org/gnutls/gnutls/source/f8d943b38bf74eaaa11d396112daf43cb8aa82ae:tests/pkcs8-decode/unencpkcs8.pem
-.. _`pkcs12_s2k_pem.c`: https://gitorious.org/gnutls/gnutls/source/f8d943b38bf74eaaa11d396112daf43cb8aa82ae:tests/pkcs12_s2k_pem.c
+.. _`OpenSSL example key`: https://github.com/openssl/openssl/blob/d02b48c63a58ea4367a0e905979f140b7d090f86/test/testrsa.pem
+.. _`GnuTLS key parsing tests`: https://gitlab.com/gnutls/gnutls/commit/f16ef39ef0303b02d7fa590a37820440c466ce8d
+.. _`enc-rsa-pkcs8.pem`: https://gitlab.com/gnutls/gnutls/blob/f8d943b38bf74eaaa11d396112daf43cb8aa82ae/tests/pkcs8-decode/encpkcs8.pem
+.. _`enc2-rsa-pkcs8.pem`: https://gitlab.com/gnutls/gnutls/blob/f8d943b38bf74eaaa11d396112daf43cb8aa82ae/tests/pkcs8-decode/enc2pkcs8.pem
+.. _`unenc-rsa-pkcs8.pem`: https://gitlab.com/gnutls/gnutls/blob/f8d943b38bf74eaaa11d396112daf43cb8aa82ae/tests/pkcs8-decode/unencpkcs8.pem
+.. _`pkcs12_s2k_pem.c`: https://gitlab.com/gnutls/gnutls/blob/f8d943b38bf74eaaa11d396112daf43cb8aa82ae/tests/pkcs12_s2k_pem.c
.. _`Botan's ECC private keys`: https://github.com/randombit/botan/tree/4917f26a2b154e841cd27c1bcecdd41d2bdeb6ce/src/tests/data/ecc
.. _`GnuTLS example keys`: https://gitorious.org/gnutls/gnutls/commit/ad2061deafdd7db78fd405f9d143b0a7c579da7b
.. _`NESSIE IDEA vectors`: https://www.cosic.esat.kuleuven.be/nessie/testvectors/bc/idea/Idea-128-64.verified.test-vectors
@@ -223,3 +242,4 @@ header format (substituting the correct information):
.. _`testx509.pem`: https://github.com/openssl/openssl/blob/master/test/testx509.pem
.. _`DigiCert Global Root G3`: http://cacerts.digicert.com/DigiCertGlobalRootG3.crt
.. _`root data`: https://hg.mozilla.org/projects/nss/file/25b2922cc564/security/nss/lib/ckfw/builtins/certdata.txt#l2053
+.. _`asymmetric/public/PKCS1/dsa.pub.pem`: https://github.com/ruby/ruby/blob/4ccb387f3bc436a08fc6d72c4931994f5de95110/test/openssl/test_pkey_dsa.rb#L53
diff --git a/docs/hazmat/primitives/asymmetric/dh.rst b/docs/hazmat/primitives/asymmetric/dh.rst
new file mode 100644
index 00000000..fdf113f7
--- /dev/null
+++ b/docs/hazmat/primitives/asymmetric/dh.rst
@@ -0,0 +1,64 @@
+.. hazmat::
+
+Diffie-Hellman key exchange
+===========================
+
+.. currentmodule:: cryptography.hazmat.primitives.asymmetric.dh
+
+
+.. class:: DHPrivateNumbers(x, public_numbers)
+
+ .. versionadded:: 0.8
+
+ The collection of integers that make up a Diffie-Hellman private key.
+
+ .. attribute:: public_numbers
+
+ :type: :class:`~cryptography.hazmat.primitives.asymmetric.dh.DHPublicNumbers`
+
+ The :class:`DHPublicNumbers` which makes up the DH public
+ key associated with this DH private key.
+
+ .. attribute:: x
+
+ :type: int
+
+ The private value.
+
+
+.. class:: DHPublicNumbers(parameters, y)
+
+ .. versionadded:: 0.8
+
+ The collection of integers that make up a Diffie-Hellman public key.
+
+ .. attribute:: parameter_numbers
+
+ :type: :class:`~cryptography.hazmat.primitives.asymmetric.dh.DHParameterNumbers`
+
+ The parameters for this DH group.
+
+ .. attribute:: y
+
+ :type: int
+
+ The public value.
+
+
+.. class:: DHParameterNumbers(p, g)
+
+ .. versionadded:: 0.8
+
+ The collection of integers that define a Diffie-Hellman group.
+
+ .. attribute:: p
+
+ :type: int
+
+ The prime modulus value.
+
+ .. attribute:: g
+
+ :type: int
+
+ The generator value.
diff --git a/docs/hazmat/primitives/asymmetric/dsa.rst b/docs/hazmat/primitives/asymmetric/dsa.rst
index 3a47da45..bd02423f 100644
--- a/docs/hazmat/primitives/asymmetric/dsa.rst
+++ b/docs/hazmat/primitives/asymmetric/dsa.rst
@@ -301,6 +301,50 @@ Key interfaces
instance.
+.. class:: DSAPrivateKeyWithSerialization
+
+ .. versionadded:: 0.8
+
+ Extends :class:`DSAPrivateKey`.
+
+ .. method:: private_numbers()
+
+ Create a
+ :class:`~cryptography.hazmat.primitives.asymmetric.dsa.DSAPrivateNumbers`
+ object.
+
+ :returns: A
+ :class:`~cryptography.hazmat.primitives.asymmetric.dsa.DSAPrivateNumbers`
+ instance.
+
+ .. method:: private_bytes(encoding, format, encryption_algorithm)
+
+ Allows serialization of the key to bytes. Encoding (
+ :attr:`~cryptography.hazmat.primitives.serialization.Encoding.PEM` or
+ :attr:`~cryptography.hazmat.primitives.serialization.Encoding.DER`),
+ format (
+ :attr:`~cryptography.hazmat.primitives.serialization.PrivateFormat.TraditionalOpenSSL`
+ or
+ :attr:`~cryptography.hazmat.primitives.serialization.PrivateFormat.PKCS8`)
+ and encryption algorithm (such as
+ :class:`~cryptography.hazmat.primitives.serialization.BestAvailableEncryption`
+ or :class:`~cryptography.hazmat.primitives.serialization.NoEncryption`)
+ are chosen to define the exact serialization.
+
+ :param encoding: A value from the
+ :class:`~cryptography.hazmat.primitives.serialization.Encoding` enum.
+
+ :param format: A value from the
+ :class:`~cryptography.hazmat.primitives.serialization.PrivateFormat`
+ enum.
+
+ :param encryption_algorithm: An instance of an object conforming to the
+ :class:`~cryptography.hazmat.primitives.serialization.KeySerializationEncryption`
+ interface.
+
+ :return bytes: Serialized key.
+
+
.. class:: DSAPublicKey
.. versionadded:: 0.3
diff --git a/docs/hazmat/primitives/asymmetric/ec.rst b/docs/hazmat/primitives/asymmetric/ec.rst
index 8b9a584b..6c03d773 100644
--- a/docs/hazmat/primitives/asymmetric/ec.rst
+++ b/docs/hazmat/primitives/asymmetric/ec.rst
@@ -326,6 +326,45 @@ Key Interfaces
:returns: An :class:`EllipticCurvePrivateNumbers` instance.
+.. class:: EllipticCurvePrivateKeyWithSerialization
+
+ .. versionadded:: 0.8
+
+ Extends :class:`EllipticCurvePrivateKey`.
+
+ .. method:: private_numbers()
+
+ Create a :class:`EllipticCurvePrivateNumbers` object.
+
+ :returns: An :class:`EllipticCurvePrivateNumbers` instance.
+
+ .. method:: private_bytes(encoding, format, encryption_algorithm)
+
+ Allows serialization of the key to bytes. Encoding (
+ :attr:`~cryptography.hazmat.primitives.serialization.Encoding.PEM` or
+ :attr:`~cryptography.hazmat.primitives.serialization.Encoding.DER`),
+ format (
+ :attr:`~cryptography.hazmat.primitives.serialization.PrivateFormat.TraditionalOpenSSL`
+ or
+ :attr:`~cryptography.hazmat.primitives.serialization.PrivateFormat.PKCS8`)
+ and encryption algorithm (such as
+ :class:`~cryptography.hazmat.primitives.serialization.BestAvailableEncryption`
+ or :class:`~cryptography.hazmat.primitives.serialization.NoEncryption`)
+ are chosen to define the exact serialization.
+
+ :param encoding: A value from the
+ :class:`~cryptography.hazmat.primitives.serialization.Encoding` enum.
+
+ :param format: A value from the
+ :class:`~cryptography.hazmat.primitives.serialization.PrivateFormat` enum.
+
+ :param encryption_algorithm: An instance of an object conforming to the
+ :class:`~cryptography.hazmat.primitives.serialization.KeySerializationEncryption`
+ interface.
+
+ :return bytes: Serialized key.
+
+
.. class:: EllipticCurvePublicKey
.. versionadded:: 0.5
@@ -366,6 +405,36 @@ Key Interfaces
:returns: An :class:`EllipticCurvePublicNumbers` instance.
+.. class:: EllipticCurvePublicKeyWithSerialization
+
+ .. versionadded:: 0.6
+
+ Extends :class:`EllipticCurvePublicKey`.
+
+ .. method:: public_numbers()
+
+ Create a :class:`EllipticCurvePublicNumbers` object.
+
+ :returns: An :class:`EllipticCurvePublicNumbers` instance.
+
+ .. method:: public_bytes(encoding, format)
+
+ Allows serialization of the key to bytes. Encoding (
+ :attr:`~cryptography.hazmat.primitives.serialization.Encoding.PEM` or
+ :attr:`~cryptography.hazmat.primitives.serialization.Encoding.DER`) and
+ format (
+ :attr:`~cryptography.hazmat.primitives.serialization.PublicFormat.SubjectPublicKeyInfo`)
+ are chosen to define the exact serialization.
+
+ :param encoding: A value from the
+ :class:`~cryptography.hazmat.primitives.serialization.Encoding` enum.
+
+ :param format: A value from the
+ :class:`~cryptography.hazmat.primitives.serialization.PublicFormat` enum.
+
+ :return bytes: Serialized key.
+
+
.. _`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
diff --git a/docs/hazmat/primitives/asymmetric/index.rst b/docs/hazmat/primitives/asymmetric/index.rst
index 59f00c5d..4242a0bd 100644
--- a/docs/hazmat/primitives/asymmetric/index.rst
+++ b/docs/hazmat/primitives/asymmetric/index.rst
@@ -29,6 +29,7 @@ and Elliptic Curve.
dsa
ec
rsa
+ dh
serialization
interfaces
utils
diff --git a/docs/hazmat/primitives/asymmetric/rsa.rst b/docs/hazmat/primitives/asymmetric/rsa.rst
index fd97d75b..e7033100 100644
--- a/docs/hazmat/primitives/asymmetric/rsa.rst
+++ b/docs/hazmat/primitives/asymmetric/rsa.rst
@@ -80,6 +80,56 @@ password. If the key is encrypted we can pass a ``bytes`` object as the
There is also support for :func:`loading public keys in the SSH format
<cryptography.hazmat.primitives.serialization.load_ssh_public_key>`.
+Key serialization
+~~~~~~~~~~~~~~~~~
+
+If you have a private key that you've loaded or generated which implements the
+:class:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateKeyWithSerialization`
+interface you can use
+:meth:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateKeyWithSerialization.private_bytes`
+to serialize the key.
+
+.. doctest::
+
+ >>> from cryptography.hazmat.primitives import serialization
+ >>> pem = private_key.private_bytes(
+ ... encoding=serialization.Encoding.PEM,
+ ... format=serialization.PrivateFormat.PKCS8,
+ ... encryption_algorithm=serialization.BestAvailableEncryption(b'mypassword')
+ ... )
+ >>> pem.splitlines()[0]
+ '-----BEGIN ENCRYPTED PRIVATE KEY-----'
+
+It is also possible to serialize without encryption using
+:class:`~cryptography.hazmat.primitives.serialization.NoEncryption`.
+
+.. doctest::
+
+ >>> pem = private_key.private_bytes(
+ ... encoding=serialization.Encoding.PEM,
+ ... format=serialization.PrivateFormat.TraditionalOpenSSL,
+ ... encryption_algorithm=serialization.NoEncryption()
+ ... )
+ >>> pem.splitlines()[0]
+ '-----BEGIN RSA PRIVATE KEY-----'
+
+Similarly, if your public key implements
+:class:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPublicKeyWithSerialization`
+interface you can use
+:meth:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPublicKeyWithSerialization.public_bytes`
+to serialize the key.
+
+.. doctest::
+
+ >>> from cryptography.hazmat.primitives import serialization
+ >>> public_key = private_key.public_key()
+ >>> pem = public_key.public_bytes(
+ ... encoding=serialization.Encoding.PEM,
+ ... format=serialization.PublicFormat.SubjectPublicKeyInfo
+ ... )
+ >>> pem.splitlines()[0]
+ '-----BEGIN PUBLIC KEY-----'
+
Signing
~~~~~~~
@@ -485,6 +535,50 @@ Key interfaces
instance.
+.. class:: RSAPrivateKeyWithSerialization
+
+ .. versionadded:: 0.8
+
+ Extends :class:`RSAPrivateKey`.
+
+ .. method:: private_numbers()
+
+ Create a
+ :class:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateNumbers`
+ object.
+
+ :returns: An
+ :class:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateNumbers`
+ instance.
+
+ .. method:: private_bytes(encoding, format, encryption_algorithm)
+
+ Allows serialization of the key to bytes. Encoding (
+ :attr:`~cryptography.hazmat.primitives.serialization.Encoding.PEM` or
+ :attr:`~cryptography.hazmat.primitives.serialization.Encoding.DER`),
+ format (
+ :attr:`~cryptography.hazmat.primitives.serialization.PrivateFormat.TraditionalOpenSSL`
+ or
+ :attr:`~cryptography.hazmat.primitives.serialization.PrivateFormat.PKCS8`)
+ and encryption algorithm (such as
+ :class:`~cryptography.hazmat.primitives.serialization.BestAvailableEncryption`
+ or :class:`~cryptography.hazmat.primitives.serialization.NoEncryption`)
+ are chosen to define the exact serialization.
+
+ :param encoding: A value from the
+ :class:`~cryptography.hazmat.primitives.serialization.Encoding` enum.
+
+ :param format: A value from the
+ :class:`~cryptography.hazmat.primitives.serialization.PrivateFormat`
+ enum.
+
+ :param encryption_algorithm: An instance of an object conforming to the
+ :class:`~cryptography.hazmat.primitives.serialization.KeySerializationEncryption`
+ interface.
+
+ :return bytes: Serialized key.
+
+
.. class:: RSAPublicKey
.. versionadded:: 0.2
@@ -549,6 +643,42 @@ Key interfaces
instance.
+.. class:: RSAPublicKeyWithSerialization
+
+ .. versionadded:: 0.8
+
+ Extends :class:`RSAPublicKey`.
+
+ .. method:: public_numbers()
+
+ Create a
+ :class:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPublicNumbers`
+ object.
+
+ :returns: An
+ :class:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPublicNumbers`
+ instance.
+
+ .. method:: public_bytes(encoding, format)
+
+ Allows serialization of the key to bytes. Encoding (
+ :attr:`~cryptography.hazmat.primitives.serialization.Encoding.PEM` or
+ :attr:`~cryptography.hazmat.primitives.serialization.Encoding.DER`) and
+ format (
+ :attr:`~cryptography.hazmat.primitives.serialization.PublicFormat.SubjectPublicKeyInfo`
+ or
+ :attr:`~cryptography.hazmat.primitives.serialization.PublicFormat.PKCS1`)
+ are chosen to define the exact serialization.
+
+ :param encoding: A value from the
+ :class:`~cryptography.hazmat.primitives.serialization.Encoding` enum.
+
+ :param format: A value from the
+ :class:`~cryptography.hazmat.primitives.serialization.PublicFormat` enum.
+
+ :return bytes: Serialized key.
+
+
.. _`RSA`: https://en.wikipedia.org/wiki/RSA_(cryptosystem)
.. _`public-key`: https://en.wikipedia.org/wiki/Public-key_cryptography
.. _`specific mathematical properties`: https://en.wikipedia.org/wiki/RSA_(cryptosystem)#Key_generation
diff --git a/docs/hazmat/primitives/asymmetric/serialization.rst b/docs/hazmat/primitives/asymmetric/serialization.rst
index 87f3c0b0..ff69973a 100644
--- a/docs/hazmat/primitives/asymmetric/serialization.rst
+++ b/docs/hazmat/primitives/asymmetric/serialization.rst
@@ -3,7 +3,7 @@
Key Serialization
=================
-.. currentmodule:: cryptography.hazmat.primitives.serialization
+.. module:: cryptography.hazmat.primitives.serialization
.. testsetup::
@@ -282,3 +282,106 @@ DSA keys look almost identical but begin with ``ssh-dss`` rather than
:raises cryptography.exceptions.UnsupportedAlgorithm: If the serialized
key is of a type that is not supported.
+
+Serialization Formats
+~~~~~~~~~~~~~~~~~~~~~
+
+.. class:: PrivateFormat
+
+ .. versionadded:: 0.8
+
+ An enumeration for private key formats. Used with the ``private_bytes``
+ method available on
+ :class:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateKeyWithSerialization`
+ ,
+ :class:`~cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePrivateKeyWithSerialization`
+ and
+ :class:`~cryptography.hazmat.primitives.asymmetric.dsa.DSAPrivateKeyWithSerialization`.
+
+ .. attribute:: TraditionalOpenSSL
+
+ Frequently known as PKCS#1 format. Still a widely used format, but
+ generally considered legacy.
+
+ .. attribute:: PKCS8
+
+ A more modern format for serializing keys which allows for better
+ encryption. Choose this unless you have explicit legacy compatibility
+ requirements.
+
+.. class:: PublicFormat
+
+ .. versionadded:: 0.8
+
+ An enumeration for public key formats. Used with the ``public_bytes``
+ method available on
+ :class:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPublicKeyWithSerialization`
+ and
+ :class:`~cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePublicKeyWithSerialization`.
+
+ .. attribute:: SubjectPublicKeyInfo
+
+ This is the typical public key format. It consists of an algorithm
+ identifier and the public key as a bit string. Choose this unless
+ you have specific needs.
+
+ .. attribute:: PKCS1
+
+ Just the public key elements (without the algorithm identifier). This
+ format is RSA only, but is used by some older systems.
+
+Serialization Encodings
+~~~~~~~~~~~~~~~~~~~~~~~
+
+.. class:: Encoding
+
+ .. versionadded:: 0.8
+
+ An enumeration for encoding types. Used with the ``private_bytes`` method
+ available on
+ :class:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateKeyWithSerialization`
+ ,
+ :class:`~cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePrivateKeyWithSerialization`
+ and
+ :class:`~cryptography.hazmat.primitives.asymmetric.dsa.DSAPrivateKeyWithSerialization`
+ as well as ``public_bytes`` on
+ :class:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPublicKeyWithSerialization`
+ and
+ :class:`~cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePublicKeyWithSerialization`.
+
+ .. attribute:: PEM
+
+ For PEM format. This is a base64 format with delimiters.
+
+ .. attribute:: DER
+
+ For DER format. This is a binary format.
+
+
+Serialization Encryption Types
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. class:: KeySerializationEncryption
+
+ Objects with this interface are usable as encryption types with methods
+ like ``private_bytes`` available on
+ :class:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateKeyWithSerialization`
+ ,
+ :class:`~cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePrivateKeyWithSerialization`
+ and
+ :class:`~cryptography.hazmat.primitives.asymmetric.dsa.DSAPrivateKeyWithSerialization`.
+ All other classes in this section represent the available choices for
+ encryption and have this interface. They are used with
+ :class:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateKeyWithSerialization.private_bytes`.
+
+.. class:: BestAvailableEncryption(password)
+
+ Encrypt using the best available encryption for a given key's backend.
+ This is a curated encryption choice and the algorithm may change over
+ time.
+
+ :param bytes password: The password to use for encryption.
+
+.. class:: NoEncryption
+
+ Do not encrypt.
diff --git a/docs/hazmat/primitives/symmetric-encryption.rst b/docs/hazmat/primitives/symmetric-encryption.rst
index 0f7e0377..47486895 100644
--- a/docs/hazmat/primitives/symmetric-encryption.rst
+++ b/docs/hazmat/primitives/symmetric-encryption.rst
@@ -6,7 +6,6 @@ Symmetric encryption
.. module:: cryptography.hazmat.primitives.ciphers
-
Symmetric encryption is a way to `encrypt`_ or hide the contents of material
where the sender and receiver both use the same secret key. Note that symmetric
encryption is **not** sufficient for most applications because it only
@@ -475,7 +474,7 @@ Interfaces
``AEADEncryptionContext`` provider. ``AEADCipherContext`` contains an
additional method :meth:`authenticate_additional_data` for adding
additional authenticated but unencrypted data (see note below). You should
- call this before calls to ``update``. When you are done call `finalize``
+ call this before calls to ``update``. When you are done call ``finalize``
to finish the operation.
.. note::
@@ -555,7 +554,7 @@ Interfaces used by the symmetric cipher modes described in
.. method:: validate_for_algorithm(algorithm)
- :param CipherAlgorithm algorithm:
+ :param cryptography.hazmat.primitives.ciphers.CipherAlgorithm algorithm:
Checks that the combination of this mode with the provided algorithm
meets any necessary invariants. This should raise an exception if they
diff --git a/docs/hazmat/primitives/twofactor.rst b/docs/hazmat/primitives/twofactor.rst
index 0d86f7cc..89d81222 100644
--- a/docs/hazmat/primitives/twofactor.rst
+++ b/docs/hazmat/primitives/twofactor.rst
@@ -39,7 +39,7 @@ codes (HMAC).
and be at least 128 bits. It is recommended that the
key be 160 bits.
:param int length: Length of generated one time password as ``int``.
- :param HashAlgorithm algorithm: A
+ :param cryptography.hazmat.primitives.hashes.HashAlgorithm algorithm: A
:class:`~cryptography.hazmat.primitives.hashes`
provider.
:param backend: A
@@ -137,7 +137,7 @@ similar to the following code.
and be at least 128 bits. It is recommended that the
key be 160 bits.
:param int length: Length of generated one time password as ``int``.
- :param HashAlgorithm algorithm: A
+ :param cryptography.hazmat.primitives.hashes.HashAlgorithm algorithm: A
:class:`~cryptography.hazmat.primitives.hashes`
provider.
:param int time_step: The time step size. The recommended size is 30.
diff --git a/docs/limitations.rst b/docs/limitations.rst
index ce61d893..0dfc49ca 100644
--- a/docs/limitations.rst
+++ b/docs/limitations.rst
@@ -16,4 +16,4 @@ Likelihood: unlikely, Remediation Cost: expensive to repair" and we do not
consider this a high risk for most users.
.. _`Memory wiping`: http://blogs.msdn.com/b/oldnewthing/archive/2013/05/29/10421912.aspx
-.. _`CERT secure coding guidelines`: https://www.securecoding.cert.org/confluence/display/seccode/MEM03-C.+Clear+sensitive+information+stored+in+reusable+resources
+.. _`CERT secure coding guidelines`: https://www.securecoding.cert.org/confluence/display/c/MEM03-C.+Clear+sensitive+information+stored+in+reusable+resources
diff --git a/docs/spelling_wordlist.txt b/docs/spelling_wordlist.txt
index ddd37897..81310d2d 100644
--- a/docs/spelling_wordlist.txt
+++ b/docs/spelling_wordlist.txt
@@ -14,6 +14,7 @@ crypto
cryptographic
cryptographically
Debian
+Diffie
decrypt
decrypted
decrypting
@@ -44,6 +45,8 @@ pseudorandom
pyOpenSSL
Schneier
scrypt
+Serializers
+serializer
Solaris
Tanja
testability
diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py
index eb23ef00..f33aba95 100644
--- a/src/cryptography/hazmat/backends/openssl/backend.py
+++ b/src/cryptography/hazmat/backends/openssl/backend.py
@@ -36,7 +36,7 @@ from cryptography.hazmat.backends.openssl.rsa import (
)
from cryptography.hazmat.backends.openssl.x509 import _Certificate
from cryptography.hazmat.bindings.openssl.binding import Binding
-from cryptography.hazmat.primitives import hashes
+from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import dsa, ec, rsa
from cryptography.hazmat.primitives.asymmetric.padding import (
MGF1, OAEP, PKCS1v15, PSS
@@ -320,6 +320,7 @@ class Backend(object):
)
def _bn_to_int(self, bn):
+ assert bn != self._ffi.NULL
if six.PY3:
# Python 3 has constant time from_bytes, so use that.
@@ -346,6 +347,7 @@ class Backend(object):
ownership of the object). Be sure to register it for GC if it will
be discarded after use.
"""
+ assert bn is None or bn != self._ffi.NULL
if bn is None:
bn = self._ffi.NULL
@@ -690,12 +692,28 @@ class Backend(object):
)
def load_pem_public_key(self, data):
- return self._load_key(
- self._lib.PEM_read_bio_PUBKEY,
- self._evp_pkey_to_public_key,
- data,
- None,
+ mem_bio = self._bytes_to_bio(data)
+ evp_pkey = self._lib.PEM_read_bio_PUBKEY(
+ mem_bio.bio, self._ffi.NULL, self._ffi.NULL, self._ffi.NULL
)
+ if evp_pkey != self._ffi.NULL:
+ evp_pkey = self._ffi.gc(evp_pkey, self._lib.EVP_PKEY_free)
+ return self._evp_pkey_to_public_key(evp_pkey)
+ else:
+ # It's not a (RSA/DSA/ECDSA) subjectPublicKeyInfo, but we still
+ # need to check to see if it is a pure PKCS1 RSA public key (not
+ # embedded in a subjectPublicKeyInfo)
+ self._consume_errors()
+ res = self._lib.BIO_reset(mem_bio.bio)
+ assert res == 1
+ rsa_cdata = self._lib.PEM_read_bio_RSAPublicKey(
+ mem_bio.bio, self._ffi.NULL, self._ffi.NULL, self._ffi.NULL
+ )
+ if rsa_cdata != self._ffi.NULL:
+ rsa_cdata = self._ffi.gc(rsa_cdata, self._lib.RSA_free)
+ return _RSAPublicKey(self, rsa_cdata)
+ else:
+ self._handle_key_loading_error()
def load_der_private_key(self, data, password):
# OpenSSL has a function called d2i_AutoPrivateKey that can simplify
@@ -760,12 +778,24 @@ class Backend(object):
def load_der_public_key(self, data):
mem_bio = self._bytes_to_bio(data)
evp_pkey = self._lib.d2i_PUBKEY_bio(mem_bio.bio, self._ffi.NULL)
- if evp_pkey == self._ffi.NULL:
+ if evp_pkey != self._ffi.NULL:
+ evp_pkey = self._ffi.gc(evp_pkey, self._lib.EVP_PKEY_free)
+ return self._evp_pkey_to_public_key(evp_pkey)
+ else:
+ # It's not a (RSA/DSA/ECDSA) subjectPublicKeyInfo, but we still
+ # need to check to see if it is a pure PKCS1 RSA public key (not
+ # embedded in a subjectPublicKeyInfo)
self._consume_errors()
- raise ValueError("Could not unserialize key data.")
-
- evp_pkey = self._ffi.gc(evp_pkey, self._lib.EVP_PKEY_free)
- return self._evp_pkey_to_public_key(evp_pkey)
+ res = self._lib.BIO_reset(mem_bio.bio)
+ assert res == 1
+ rsa_cdata = self._lib.d2i_RSAPublicKey_bio(
+ mem_bio.bio, self._ffi.NULL
+ )
+ if rsa_cdata != self._ffi.NULL:
+ rsa_cdata = self._ffi.gc(rsa_cdata, self._lib.RSA_free)
+ return _RSAPublicKey(self, rsa_cdata)
+ else:
+ self._handle_key_loading_error()
def load_pem_x509_certificate(self, data):
mem_bio = self._bytes_to_bio(data)
@@ -1093,6 +1123,93 @@ class Backend(object):
return ctx
+ def _private_key_bytes(self, encoding, format, encryption_algorithm,
+ traditional_write_func, evp_pkey, cdata):
+ if not isinstance(encoding, serialization.Encoding):
+ raise TypeError("encoding must be an item from the Encoding enum")
+
+ if not isinstance(format, serialization.PrivateFormat):
+ raise TypeError(
+ "format must be an item from the PrivateFormat enum"
+ )
+
+ # This is a temporary check until we land DER serialization.
+ if encoding is not serialization.Encoding.PEM:
+ raise ValueError("Only PEM encoding is supported by this backend")
+
+ if format is serialization.PrivateFormat.PKCS8:
+ write_bio = self._lib.PEM_write_bio_PKCS8PrivateKey
+ key = evp_pkey
+ elif format is serialization.PrivateFormat.TraditionalOpenSSL:
+ write_bio = traditional_write_func
+ key = cdata
+
+ if not isinstance(encryption_algorithm,
+ serialization.KeySerializationEncryption):
+ raise TypeError(
+ "Encryption algorithm must be a KeySerializationEncryption "
+ "instance"
+ )
+
+ if isinstance(encryption_algorithm, serialization.NoEncryption):
+ password = b""
+ passlen = 0
+ evp_cipher = self._ffi.NULL
+ elif isinstance(encryption_algorithm,
+ serialization.BestAvailableEncryption):
+ # This is a curated value that we will update over time.
+ evp_cipher = self._lib.EVP_get_cipherbyname(
+ b"aes-256-cbc"
+ )
+ password = encryption_algorithm.password
+ passlen = len(password)
+ if passlen > 1023:
+ raise ValueError(
+ "Passwords longer than 1023 bytes are not supported by "
+ "this backend"
+ )
+ else:
+ raise ValueError("Unsupported encryption type")
+
+ bio = self._create_mem_bio()
+ res = write_bio(
+ bio,
+ key,
+ evp_cipher,
+ password,
+ passlen,
+ self._ffi.NULL,
+ self._ffi.NULL
+ )
+ assert res == 1
+ return self._read_mem_bio(bio)
+
+ def _public_key_bytes(self, encoding, format, pkcs1_write_func, evp_pkey,
+ cdata):
+ if not isinstance(encoding, serialization.Encoding):
+ raise TypeError("encoding must be an item from the Encoding enum")
+
+ if not isinstance(format, serialization.PublicFormat):
+ raise TypeError(
+ "format must be an item from the PublicFormat enum"
+ )
+
+ # This is a temporary check until we land DER serialization.
+ if encoding is not serialization.Encoding.PEM:
+ raise ValueError("Only PEM encoding is supported by this backend")
+
+ if format is serialization.PublicFormat.SubjectPublicKeyInfo:
+ write_bio = self._lib.PEM_write_bio_PUBKEY
+ key = evp_pkey
+ elif format is serialization.PublicFormat.PKCS1:
+ write_bio = pkcs1_write_func
+ key = cdata
+
+ bio = self._create_mem_bio()
+ res = write_bio(bio, key)
+ assert res == 1
+ return self._read_mem_bio(bio)
+
class GetCipherByName(object):
def __init__(self, fmt):
diff --git a/src/cryptography/hazmat/backends/openssl/dsa.py b/src/cryptography/hazmat/backends/openssl/dsa.py
index d2972e4a..8d02e492 100644
--- a/src/cryptography/hazmat/backends/openssl/dsa.py
+++ b/src/cryptography/hazmat/backends/openssl/dsa.py
@@ -11,9 +11,6 @@ from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import (
AsymmetricSignatureContext, AsymmetricVerificationContext, dsa
)
-from cryptography.hazmat.primitives.interfaces import (
- DSAParametersWithNumbers, DSAPrivateKeyWithNumbers, DSAPublicKeyWithNumbers
-)
def _truncate_digest_for_dsa(dsa_cdata, digest, backend):
@@ -94,7 +91,7 @@ class _DSASignatureContext(object):
return self._backend._ffi.buffer(sig_buf)[:buflen[0]]
-@utils.register_interface(DSAParametersWithNumbers)
+@utils.register_interface(dsa.DSAParametersWithNumbers)
class _DSAParameters(object):
def __init__(self, backend, dsa_cdata):
self._backend = backend
@@ -111,7 +108,7 @@ class _DSAParameters(object):
return self._backend.generate_dsa_private_key(self)
-@utils.register_interface(DSAPrivateKeyWithNumbers)
+@utils.register_interface(dsa.DSAPrivateKeyWithSerialization)
class _DSAPrivateKey(object):
def __init__(self, backend, dsa_cdata):
self._backend = backend
@@ -159,8 +156,25 @@ class _DSAPrivateKey(object):
dsa_cdata.g = self._backend._lib.BN_dup(self._dsa_cdata.g)
return _DSAParameters(self._backend, dsa_cdata)
+ def private_bytes(self, encoding, format, encryption_algorithm):
+ evp_pkey = self._backend._lib.EVP_PKEY_new()
+ assert evp_pkey != self._backend._ffi.NULL
+ evp_pkey = self._backend._ffi.gc(
+ evp_pkey, self._backend._lib.EVP_PKEY_free
+ )
+ res = self._backend._lib.EVP_PKEY_set1_DSA(evp_pkey, self._dsa_cdata)
+ assert res == 1
+ return self._backend._private_key_bytes(
+ encoding,
+ format,
+ encryption_algorithm,
+ self._backend._lib.PEM_write_bio_DSAPrivateKey,
+ evp_pkey,
+ self._dsa_cdata
+ )
+
-@utils.register_interface(DSAPublicKeyWithNumbers)
+@utils.register_interface(dsa.DSAPublicKeyWithNumbers)
class _DSAPublicKey(object):
def __init__(self, backend, dsa_cdata):
self._backend = backend
diff --git a/src/cryptography/hazmat/backends/openssl/ec.py b/src/cryptography/hazmat/backends/openssl/ec.py
index 52c93da9..39b0a555 100644
--- a/src/cryptography/hazmat/backends/openssl/ec.py
+++ b/src/cryptography/hazmat/backends/openssl/ec.py
@@ -9,7 +9,7 @@ from cryptography.exceptions import (
InvalidSignature, UnsupportedAlgorithm, _Reasons
)
from cryptography.hazmat.backends.openssl.utils import _truncate_digest
-from cryptography.hazmat.primitives import hashes
+from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import (
AsymmetricSignatureContext, AsymmetricVerificationContext, ec
)
@@ -148,7 +148,7 @@ class _ECDSAVerificationContext(object):
return True
-@utils.register_interface(ec.EllipticCurvePrivateKeyWithNumbers)
+@utils.register_interface(ec.EllipticCurvePrivateKeyWithSerialization)
class _EllipticCurvePrivateKey(object):
def __init__(self, backend, ec_key_cdata):
self._backend = backend
@@ -200,6 +200,23 @@ class _EllipticCurvePrivateKey(object):
public_numbers=self.public_key().public_numbers()
)
+ def private_bytes(self, encoding, format, encryption_algorithm):
+ evp_pkey = self._backend._lib.EVP_PKEY_new()
+ assert evp_pkey != self._backend._ffi.NULL
+ evp_pkey = self._backend._ffi.gc(
+ evp_pkey, self._backend._lib.EVP_PKEY_free
+ )
+ res = self._backend._lib.EVP_PKEY_set1_EC_KEY(evp_pkey, self._ec_key)
+ assert res == 1
+ return self._backend._private_key_bytes(
+ encoding,
+ format,
+ encryption_algorithm,
+ self._backend._lib.PEM_write_bio_ECPrivateKey,
+ evp_pkey,
+ self._ec_key
+ )
+
@utils.register_interface(ec.EllipticCurvePublicKeyWithNumbers)
class _EllipticCurvePublicKey(object):
@@ -245,3 +262,24 @@ class _EllipticCurvePublicKey(object):
y=y,
curve=self._curve
)
+
+ def public_bytes(self, encoding, format):
+ if format is serialization.PublicFormat.PKCS1:
+ raise ValueError(
+ "EC public keys do not support PKCS1 serialization"
+ )
+
+ evp_pkey = self._backend._lib.EVP_PKEY_new()
+ assert evp_pkey != self._backend._ffi.NULL
+ evp_pkey = self._backend._ffi.gc(
+ evp_pkey, self._backend._lib.EVP_PKEY_free
+ )
+ res = self._backend._lib.EVP_PKEY_set1_EC_KEY(evp_pkey, self._ec_key)
+ assert res == 1
+ return self._backend._public_key_bytes(
+ encoding,
+ format,
+ None,
+ evp_pkey,
+ None
+ )
diff --git a/src/cryptography/hazmat/backends/openssl/rsa.py b/src/cryptography/hazmat/backends/openssl/rsa.py
index 00ddcda3..25168c2f 100644
--- a/src/cryptography/hazmat/backends/openssl/rsa.py
+++ b/src/cryptography/hazmat/backends/openssl/rsa.py
@@ -17,8 +17,9 @@ from cryptography.hazmat.primitives.asymmetric import (
from cryptography.hazmat.primitives.asymmetric.padding import (
AsymmetricPadding, MGF1, OAEP, PKCS1v15, PSS
)
-from cryptography.hazmat.primitives.interfaces import (
- RSAPrivateKeyWithNumbers, RSAPublicKeyWithNumbers
+from cryptography.hazmat.primitives.asymmetric.rsa import (
+ RSAPrivateKeyWithNumbers, RSAPrivateKeyWithSerialization,
+ RSAPublicKeyWithSerialization
)
@@ -507,6 +508,7 @@ class _RSAVerificationContext(object):
@utils.register_interface(RSAPrivateKeyWithNumbers)
+@utils.register_interface(RSAPrivateKeyWithSerialization)
class _RSAPrivateKey(object):
def __init__(self, backend, rsa_cdata):
self._backend = backend
@@ -559,8 +561,18 @@ class _RSAPrivateKey(object):
)
)
+ def private_bytes(self, encoding, format, encryption_algorithm):
+ return self._backend._private_key_bytes(
+ encoding,
+ format,
+ encryption_algorithm,
+ self._backend._lib.PEM_write_bio_RSAPrivateKey,
+ self._evp_pkey,
+ self._rsa_cdata
+ )
+
-@utils.register_interface(RSAPublicKeyWithNumbers)
+@utils.register_interface(RSAPublicKeyWithSerialization)
class _RSAPublicKey(object):
def __init__(self, backend, rsa_cdata):
self._backend = backend
@@ -592,3 +604,12 @@ class _RSAPublicKey(object):
e=self._backend._bn_to_int(self._rsa_cdata.e),
n=self._backend._bn_to_int(self._rsa_cdata.n),
)
+
+ def public_bytes(self, encoding, format):
+ return self._backend._public_key_bytes(
+ encoding,
+ format,
+ self._backend._lib.PEM_write_bio_RSAPublicKey,
+ self._evp_pkey,
+ self._rsa_cdata
+ )
diff --git a/src/cryptography/hazmat/bindings/openssl/dh.py b/src/cryptography/hazmat/bindings/openssl/dh.py
index 06ac6f41..b66e7196 100644
--- a/src/cryptography/hazmat/bindings/openssl/dh.py
+++ b/src/cryptography/hazmat/bindings/openssl/dh.py
@@ -18,6 +18,9 @@ typedef struct dh_st {
BIGNUM *priv_key;
/* Public DH value g^x */
BIGNUM *pub_key;
+ /* X9.42/RFC 2631 */
+ BIGNUM *q;
+ BIGNUM *j;
...;
} DH;
"""
@@ -28,6 +31,7 @@ void DH_free(DH *);
int DH_size(const DH *);
DH *DH_generate_parameters(int, int, void (*)(int, int, void *), void *);
int DH_check(const DH *, int *);
+int DH_check_pub_key(const DH *, const BIGNUM *, int *);
int DH_generate_key(DH *);
int DH_compute_key(unsigned char *, const BIGNUM *, DH *);
int DH_set_ex_data(DH *, int, void *);
diff --git a/src/cryptography/hazmat/bindings/openssl/err.py b/src/cryptography/hazmat/bindings/openssl/err.py
index ec393c1b..0ee19c9e 100644
--- a/src/cryptography/hazmat/bindings/openssl/err.py
+++ b/src/cryptography/hazmat/bindings/openssl/err.py
@@ -21,6 +21,7 @@ struct ERR_string_data_st {
};
typedef struct ERR_string_data_st ERR_STRING_DATA;
+static const int ERR_LIB_DH;
static const int ERR_LIB_EVP;
static const int ERR_LIB_EC;
static const int ERR_LIB_PEM;
@@ -95,6 +96,10 @@ static const int ASN1_R_UNSUPPORTED_TYPE;
static const int ASN1_R_WRONG_TAG;
static const int ASN1_R_WRONG_TYPE;
+static const int DH_F_COMPUTE_KEY;
+
+static const int DH_R_INVALID_PUBKEY;
+
static const int EVP_F_AES_INIT_KEY;
static const int EVP_F_D2I_PKEY;
static const int EVP_F_DSA_PKEY2PKCS8;
diff --git a/src/cryptography/hazmat/bindings/openssl/pem.py b/src/cryptography/hazmat/bindings/openssl/pem.py
index d0c70f5d..98c7648f 100644
--- a/src/cryptography/hazmat/bindings/openssl/pem.py
+++ b/src/cryptography/hazmat/bindings/openssl/pem.py
@@ -72,9 +72,23 @@ int PEM_write_bio_PUBKEY(BIO *, EVP_PKEY *);
"""
MACROS = """
+int PEM_write_bio_ECPrivateKey(BIO *, EC_KEY *, const EVP_CIPHER *,
+ unsigned char *, int, pem_password_cb *,
+ void *);
"""
CUSTOMIZATIONS = """
+// Cryptography_HAS_EC is provided by ec.py so we don't need to define it here
+#ifdef OPENSSL_NO_EC
+int (*PEM_write_bio_ECPrivateKey)(BIO *, EC_KEY *, const EVP_CIPHER *,
+ unsigned char *, int, pem_password_cb *,
+ void *) = NULL;
+#endif
+
"""
-CONDITIONAL_NAMES = {}
+CONDITIONAL_NAMES = {
+ "Cryptography_HAS_EC": [
+ "PEM_write_bio_ECPrivateKey"
+ ]
+}
diff --git a/src/cryptography/hazmat/primitives/asymmetric/dh.py b/src/cryptography/hazmat/primitives/asymmetric/dh.py
new file mode 100644
index 00000000..61556efb
--- /dev/null
+++ b/src/cryptography/hazmat/primitives/asymmetric/dh.py
@@ -0,0 +1,101 @@
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from __future__ import absolute_import, division, print_function
+
+import six
+
+from cryptography import utils
+
+
+class DHPrivateNumbers(object):
+ def __init__(self, x, public_numbers):
+ if not isinstance(x, six.integer_types):
+ raise TypeError("x must be an integer.")
+
+ if not isinstance(public_numbers, DHPublicNumbers):
+ raise TypeError("public_numbers must be an instance of "
+ "DHPublicNumbers.")
+
+ self._x = x
+ self._public_numbers = public_numbers
+
+ def __eq__(self, other):
+ if not isinstance(other, DHPrivateNumbers):
+ return NotImplemented
+
+ return (
+ self._x == other._x and
+ self._public_numbers == other._public_numbers
+ )
+
+ def __ne__(self, other):
+ return not self == other
+
+ public_numbers = utils.read_only_property("_public_numbers")
+ x = utils.read_only_property("_x")
+
+
+class DHPublicNumbers(object):
+ def __init__(self, y, parameter_numbers):
+ if not isinstance(y, six.integer_types):
+ raise TypeError("y must be an integer.")
+
+ if not isinstance(parameter_numbers, DHParameterNumbers):
+ raise TypeError(
+ "parameters must be an instance of DHParameterNumbers.")
+
+ self._y = y
+ self._parameter_numbers = parameter_numbers
+
+ def __eq__(self, other):
+ if not isinstance(other, DHPublicNumbers):
+ return NotImplemented
+
+ return (
+ self._y == other._y and
+ self._parameter_numbers == other._parameter_numbers
+ )
+
+ def __ne__(self, other):
+ return not self == other
+
+ y = utils.read_only_property("_y")
+ parameter_numbers = utils.read_only_property("_parameter_numbers")
+
+
+class DHParameterNumbers(object):
+ def __init__(self, p, g):
+ if (
+ not isinstance(p, six.integer_types) or
+ not isinstance(g, six.integer_types)
+ ):
+ raise TypeError("p and g must be integers")
+
+ self._p = p
+ self._g = g
+
+ def __eq__(self, other):
+ if not isinstance(other, DHParameterNumbers):
+ return NotImplemented
+
+ return (
+ self._p == other._p and
+ self._g == other._g
+ )
+
+ def __ne__(self, other):
+ return not self == other
+
+ p = utils.read_only_property("_p")
+ g = utils.read_only_property("_g")
diff --git a/src/cryptography/hazmat/primitives/asymmetric/dsa.py b/src/cryptography/hazmat/primitives/asymmetric/dsa.py
index 58058df9..084686e4 100644
--- a/src/cryptography/hazmat/primitives/asymmetric/dsa.py
+++ b/src/cryptography/hazmat/primitives/asymmetric/dsa.py
@@ -57,13 +57,30 @@ class DSAPrivateKey(object):
@six.add_metaclass(abc.ABCMeta)
-class DSAPrivateKeyWithNumbers(DSAPrivateKey):
+class DSAPrivateKeyWithSerialization(DSAPrivateKey):
@abc.abstractmethod
def private_numbers(self):
"""
Returns a DSAPrivateNumbers.
"""
+ @abc.abstractmethod
+ def private_bytes(self, encoding, format, encryption_algorithm):
+ """
+ Returns the key serialized as bytes.
+ """
+
+
+DSAPrivateKeyWithNumbers = utils.deprecated(
+ DSAPrivateKeyWithSerialization,
+ __name__,
+ (
+ "The DSAPrivateKeyWithNumbers interface has been renamed to "
+ "DSAPrivateKeyWithSerialization"
+ ),
+ utils.DeprecatedIn08
+)
+
@six.add_metaclass(abc.ABCMeta)
class DSAPublicKey(object):
diff --git a/src/cryptography/hazmat/primitives/asymmetric/ec.py b/src/cryptography/hazmat/primitives/asymmetric/ec.py
index c7749ca5..bf1705db 100644
--- a/src/cryptography/hazmat/primitives/asymmetric/ec.py
+++ b/src/cryptography/hazmat/primitives/asymmetric/ec.py
@@ -57,13 +57,30 @@ class EllipticCurvePrivateKey(object):
@six.add_metaclass(abc.ABCMeta)
-class EllipticCurvePrivateKeyWithNumbers(EllipticCurvePrivateKey):
+class EllipticCurvePrivateKeyWithSerialization(EllipticCurvePrivateKey):
@abc.abstractmethod
def private_numbers(self):
"""
Returns an EllipticCurvePrivateNumbers.
"""
+ @abc.abstractmethod
+ def private_bytes(self, encoding, format, encryption_algorithm):
+ """
+ Returns the key serialized as bytes.
+ """
+
+
+EllipticCurvePrivateKeyWithNumbers = utils.deprecated(
+ EllipticCurvePrivateKeyWithSerialization,
+ __name__,
+ (
+ "The EllipticCurvePrivateKeyWithNumbers interface has been renamed to "
+ "EllipticCurvePrivateKeyWithSerialization"
+ ),
+ utils.DeprecatedIn08
+)
+
@six.add_metaclass(abc.ABCMeta)
class EllipticCurvePublicKey(object):
@@ -81,13 +98,30 @@ class EllipticCurvePublicKey(object):
@six.add_metaclass(abc.ABCMeta)
-class EllipticCurvePublicKeyWithNumbers(EllipticCurvePublicKey):
+class EllipticCurvePublicKeyWithSerialization(EllipticCurvePublicKey):
@abc.abstractmethod
def public_numbers(self):
"""
Returns an EllipticCurvePublicNumbers.
"""
+ @abc.abstractmethod
+ def public_bytes(self, encoding, format):
+ """
+ Returns the key serialized as bytes.
+ """
+
+
+EllipticCurvePublicKeyWithNumbers = utils.deprecated(
+ EllipticCurvePublicKeyWithSerialization,
+ __name__,
+ (
+ "The EllipticCurvePublicKeyWithNumbers interface has been renamed to "
+ "EllipticCurvePublicKeyWithSerialization"
+ ),
+ utils.DeprecatedIn08
+)
+
@utils.register_interface(EllipticCurve)
class SECT571R1(object):
diff --git a/src/cryptography/hazmat/primitives/asymmetric/rsa.py b/src/cryptography/hazmat/primitives/asymmetric/rsa.py
index 332ad2c3..8adc7459 100644
--- a/src/cryptography/hazmat/primitives/asymmetric/rsa.py
+++ b/src/cryptography/hazmat/primitives/asymmetric/rsa.py
@@ -42,13 +42,30 @@ class RSAPrivateKey(object):
@six.add_metaclass(abc.ABCMeta)
-class RSAPrivateKeyWithNumbers(RSAPrivateKey):
+class RSAPrivateKeyWithSerialization(RSAPrivateKey):
@abc.abstractmethod
def private_numbers(self):
"""
Returns an RSAPrivateNumbers.
"""
+ @abc.abstractmethod
+ def private_bytes(self, encoding, format, encryption_algorithm):
+ """
+ Returns the key serialized as bytes.
+ """
+
+
+RSAPrivateKeyWithNumbers = utils.deprecated(
+ RSAPrivateKeyWithSerialization,
+ __name__,
+ (
+ "The RSAPrivateKeyWithNumbers interface has been renamed to "
+ "RSAPrivateKeyWithSerialization"
+ ),
+ utils.DeprecatedIn08
+)
+
@six.add_metaclass(abc.ABCMeta)
class RSAPublicKey(object):
@@ -72,13 +89,30 @@ class RSAPublicKey(object):
@six.add_metaclass(abc.ABCMeta)
-class RSAPublicKeyWithNumbers(RSAPublicKey):
+class RSAPublicKeyWithSerialization(RSAPublicKey):
@abc.abstractmethod
def public_numbers(self):
"""
Returns an RSAPublicNumbers
"""
+ @abc.abstractmethod
+ def public_bytes(self, encoding, format):
+ """
+ Returns the key serialized as bytes.
+ """
+
+
+RSAPublicKeyWithNumbers = utils.deprecated(
+ RSAPublicKeyWithSerialization,
+ __name__,
+ (
+ "The RSAPublicKeyWithNumbers interface has been renamed to "
+ "RSAPublicKeyWithSerialization"
+ ),
+ utils.DeprecatedIn08
+)
+
def generate_private_key(public_exponent, key_size, backend):
if not isinstance(backend, RSABackend):
diff --git a/src/cryptography/hazmat/primitives/interfaces/__init__.py b/src/cryptography/hazmat/primitives/interfaces/__init__.py
index 6b4241bd..6913ace9 100644
--- a/src/cryptography/hazmat/primitives/interfaces/__init__.py
+++ b/src/cryptography/hazmat/primitives/interfaces/__init__.py
@@ -289,11 +289,12 @@ RSAPrivateKey = utils.deprecated(
)
RSAPrivateKeyWithNumbers = utils.deprecated(
- rsa.RSAPrivateKeyWithNumbers,
+ rsa.RSAPrivateKeyWithSerialization,
__name__,
(
"The RSAPrivateKeyWithNumbers interface has moved to the "
- "cryptography.hazmat.primitives.asymmetric.rsa module"
+ "cryptography.hazmat.primitives.asymmetric.rsa module and has been "
+ "renamed RSAPrivateKeyWithSerialization"
),
utils.DeprecatedIn08
)
@@ -386,5 +387,9 @@ class MACContext(object):
signature.
"""
-# DeprecatedIn07
-CMACContext = MACContext
+CMACContext = utils.deprecated(
+ MACContext,
+ __name__,
+ "The CMACContext interface has been renamed to MACContext",
+ utils.DeprecatedIn07
+)
diff --git a/src/cryptography/hazmat/primitives/padding.py b/src/cryptography/hazmat/primitives/padding.py
index 8ad64dec..6247f7b5 100644
--- a/src/cryptography/hazmat/primitives/padding.py
+++ b/src/cryptography/hazmat/primitives/padding.py
@@ -6,6 +6,8 @@ from __future__ import absolute_import, division, print_function
import abc
+import os
+
import six
from cryptography import utils
@@ -13,47 +15,11 @@ from cryptography.exceptions import AlreadyFinalized
from cryptography.hazmat.bindings.utils import LazyLibrary, build_ffi
-TYPES = """
-uint8_t Cryptography_check_pkcs7_padding(const uint8_t *, uint8_t);
-"""
-
-FUNCTIONS = """
-/* Returns the value of the input with the most-significant-bit copied to all
- of the bits. */
-static uint8_t Cryptography_DUPLICATE_MSB_TO_ALL(uint8_t a) {
- return (1 - (a >> (sizeof(uint8_t) * 8 - 1))) - 1;
-}
-
-/* This returns 0xFF if a < b else 0x00, but does so in a constant time
- fashion */
-static uint8_t Cryptography_constant_time_lt(uint8_t a, uint8_t b) {
- a -= b;
- return Cryptography_DUPLICATE_MSB_TO_ALL(a);
-}
-
-uint8_t Cryptography_check_pkcs7_padding(const uint8_t *data,
- uint8_t block_len) {
- uint8_t i;
- uint8_t pad_size = data[block_len - 1];
- uint8_t mismatch = 0;
- for (i = 0; i < block_len; i++) {
- unsigned int mask = Cryptography_constant_time_lt(i, pad_size);
- uint8_t b = data[block_len - 1 - i];
- mismatch |= (mask & (pad_size ^ b));
- }
-
- /* Check to make sure the pad_size was within the valid range. */
- mismatch |= ~Cryptography_constant_time_lt(0, pad_size);
- mismatch |= Cryptography_constant_time_lt(block_len, pad_size);
-
- /* Make sure any bits set are copied to the lowest bit */
- mismatch |= mismatch >> 4;
- mismatch |= mismatch >> 2;
- mismatch |= mismatch >> 1;
- /* Now check the low bit to see if it's set */
- return (mismatch & 1) == 0;
-}
-"""
+with open(os.path.join(os.path.dirname(__file__), "src/padding.h")) as f:
+ TYPES = f.read()
+
+with open(os.path.join(os.path.dirname(__file__), "src/padding.c")) as f:
+ FUNCTIONS = f.read()
_ffi = build_ffi(cdef_source=TYPES, verify_source=FUNCTIONS)
diff --git a/src/cryptography/hazmat/primitives/serialization.py b/src/cryptography/hazmat/primitives/serialization.py
index 0f9506e1..8699fa91 100644
--- a/src/cryptography/hazmat/primitives/serialization.py
+++ b/src/cryptography/hazmat/primitives/serialization.py
@@ -4,11 +4,14 @@
from __future__ import absolute_import, division, print_function
+import abc
import base64
import struct
+from enum import Enum
import six
+from cryptography import utils
from cryptography.exceptions import UnsupportedAlgorithm
from cryptography.hazmat.primitives.asymmetric import dsa, ec, rsa
@@ -164,3 +167,37 @@ else:
data = data[4:]
return result
+
+
+class Encoding(Enum):
+ PEM = "PEM"
+ DER = "DER"
+
+
+class PrivateFormat(Enum):
+ PKCS8 = "PKCS8"
+ TraditionalOpenSSL = "TraditionalOpenSSL"
+
+
+class PublicFormat(Enum):
+ SubjectPublicKeyInfo = "X.509 subjectPublicKeyInfo with PKCS#1"
+ PKCS1 = "Raw PKCS#1"
+
+
+@six.add_metaclass(abc.ABCMeta)
+class KeySerializationEncryption(object):
+ pass
+
+
+@utils.register_interface(KeySerializationEncryption)
+class BestAvailableEncryption(object):
+ def __init__(self, password):
+ if not isinstance(password, bytes) or len(password) == 0:
+ raise ValueError("Password must be 1 or more bytes.")
+
+ self.password = password
+
+
+@utils.register_interface(KeySerializationEncryption)
+class NoEncryption(object):
+ pass
diff --git a/src/cryptography/hazmat/primitives/src/padding.c b/src/cryptography/hazmat/primitives/src/padding.c
new file mode 100644
index 00000000..570bad9f
--- /dev/null
+++ b/src/cryptography/hazmat/primitives/src/padding.c
@@ -0,0 +1,39 @@
+// This file is dual licensed under the terms of the Apache License, Version
+// 2.0, and the BSD License. See the LICENSE file in the root of this
+// repository for complete details.
+
+/* Returns the value of the input with the most-significant-bit copied to all
+ of the bits. */
+static uint8_t Cryptography_DUPLICATE_MSB_TO_ALL(uint8_t a) {
+ return (1 - (a >> (sizeof(uint8_t) * 8 - 1))) - 1;
+}
+
+/* This returns 0xFF if a < b else 0x00, but does so in a constant time
+ fashion */
+static uint8_t Cryptography_constant_time_lt(uint8_t a, uint8_t b) {
+ a -= b;
+ return Cryptography_DUPLICATE_MSB_TO_ALL(a);
+}
+
+uint8_t Cryptography_check_pkcs7_padding(const uint8_t *data,
+ uint8_t block_len) {
+ uint8_t i;
+ uint8_t pad_size = data[block_len - 1];
+ uint8_t mismatch = 0;
+ for (i = 0; i < block_len; i++) {
+ unsigned int mask = Cryptography_constant_time_lt(i, pad_size);
+ uint8_t b = data[block_len - 1 - i];
+ mismatch |= (mask & (pad_size ^ b));
+ }
+
+ /* Check to make sure the pad_size was within the valid range. */
+ mismatch |= ~Cryptography_constant_time_lt(0, pad_size);
+ mismatch |= Cryptography_constant_time_lt(block_len, pad_size);
+
+ /* Make sure any bits set are copied to the lowest bit */
+ mismatch |= mismatch >> 4;
+ mismatch |= mismatch >> 2;
+ mismatch |= mismatch >> 1;
+ /* Now check the low bit to see if it's set */
+ return (mismatch & 1) == 0;
+}
diff --git a/src/cryptography/hazmat/primitives/src/padding.h b/src/cryptography/hazmat/primitives/src/padding.h
new file mode 100644
index 00000000..4d218b1a
--- /dev/null
+++ b/src/cryptography/hazmat/primitives/src/padding.h
@@ -0,0 +1,5 @@
+// This file is dual licensed under the terms of the Apache License, Version
+// 2.0, and the BSD License. See the LICENSE file in the root of this
+// repository for complete details.
+
+uint8_t Cryptography_check_pkcs7_padding(const uint8_t *, uint8_t);
diff --git a/src/cryptography/utils.py b/src/cryptography/utils.py
index 78dcc1ca..253dea55 100644
--- a/src/cryptography/utils.py
+++ b/src/cryptography/utils.py
@@ -10,9 +10,7 @@ import sys
import warnings
-# DeprecatedIn07 objects exist. This comment exists to remind developers to
-# look for them when it's time for the ninth release cycle deprecation dance.
-
+DeprecatedIn07 = DeprecationWarning
DeprecatedIn08 = PendingDeprecationWarning
diff --git a/tests/hazmat/backends/test_openssl.py b/tests/hazmat/backends/test_openssl.py
index 0e4d75ed..ba0a2ba3 100644
--- a/tests/hazmat/backends/test_openssl.py
+++ b/tests/hazmat/backends/test_openssl.py
@@ -15,11 +15,12 @@ import pytest
from cryptography import utils
from cryptography.exceptions import InternalError, _Reasons
+from cryptography.hazmat.backends.interfaces import RSABackend
from cryptography.hazmat.backends.openssl.backend import (
Backend, backend
)
from cryptography.hazmat.backends.openssl.ec import _sn_to_elliptic_curve
-from cryptography.hazmat.primitives import hashes
+from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import dsa, padding
from cryptography.hazmat.primitives.ciphers import (
BlockCipherAlgorithm, Cipher, CipherAlgorithm
@@ -27,7 +28,7 @@ from cryptography.hazmat.primitives.ciphers import (
from cryptography.hazmat.primitives.ciphers.algorithms import AES
from cryptography.hazmat.primitives.ciphers.modes import CBC, CTR, Mode
-from ..primitives.fixtures_rsa import RSA_KEY_512
+from ..primitives.fixtures_rsa import RSA_KEY_2048, RSA_KEY_512
from ...utils import load_vectors_from_file, raises_unsupported_algorithm
@@ -493,3 +494,33 @@ class TestOpenSSLEllipticCurve(object):
def test_sn_to_elliptic_curve_not_supported(self):
with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_ELLIPTIC_CURVE):
_sn_to_elliptic_curve(backend, b"fake")
+
+
+@pytest.mark.requires_backend_interface(interface=RSABackend)
+class TestRSAPEMSerialization(object):
+ def test_password_length_limit(self):
+ password = b"x" * 1024
+ key = RSA_KEY_2048.private_key(backend)
+ with pytest.raises(ValueError):
+ key.private_bytes(
+ serialization.Encoding.PEM,
+ serialization.PrivateFormat.PKCS8,
+ serialization.BestAvailableEncryption(password)
+ )
+
+ def test_unsupported_private_key_encoding(self):
+ key = RSA_KEY_2048.private_key(backend)
+ with pytest.raises(ValueError):
+ key.private_bytes(
+ serialization.Encoding.DER,
+ serialization.PrivateFormat.PKCS8,
+ serialization.NoEncryption()
+ )
+
+ def test_unsupported_public_key_encoding(self):
+ key = RSA_KEY_2048.private_key(backend).public_key()
+ with pytest.raises(ValueError):
+ key.public_bytes(
+ serialization.Encoding.DER,
+ serialization.PublicFormat.SubjectPublicKeyInfo
+ )
diff --git a/tests/hazmat/primitives/test_dh.py b/tests/hazmat/primitives/test_dh.py
new file mode 100644
index 00000000..115f3d8c
--- /dev/null
+++ b/tests/hazmat/primitives/test_dh.py
@@ -0,0 +1,113 @@
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from __future__ import absolute_import, division, print_function
+
+import pytest
+
+from cryptography.hazmat.primitives.asymmetric import dh
+
+
+def test_dh_parameternumbers():
+ params = dh.DHParameterNumbers(
+ 65537, 3
+ )
+
+ assert params.p == 65537
+ assert params.g == 3
+
+ with pytest.raises(TypeError):
+ dh.DHParameterNumbers(
+ None, 3
+ )
+
+ with pytest.raises(TypeError):
+ dh.DHParameterNumbers(
+ 65537, None
+ )
+
+ with pytest.raises(TypeError):
+ dh.DHParameterNumbers(
+ None, None
+ )
+
+
+def test_dh_numbers():
+ params = dh.DHParameterNumbers(
+ 65537, 3
+ )
+
+ public = dh.DHPublicNumbers(
+ 1, params
+ )
+
+ assert public.parameter_numbers is params
+ assert public.y == 1
+
+ with pytest.raises(TypeError):
+ dh.DHPublicNumbers(
+ 1, None
+ )
+
+ with pytest.raises(TypeError):
+ dh.DHPublicNumbers(
+ None, params
+ )
+
+ private = dh.DHPrivateNumbers(
+ 1, public
+ )
+
+ assert private.public_numbers is public
+ assert private.x == 1
+
+ with pytest.raises(TypeError):
+ dh.DHPrivateNumbers(
+ 1, None
+ )
+
+ with pytest.raises(TypeError):
+ dh.DHPrivateNumbers(
+ None, public
+ )
+
+
+def test_dh_parameter_numbers_equality():
+ assert dh.DHParameterNumbers(65537, 3) == dh.DHParameterNumbers(65537, 3)
+ assert dh.DHParameterNumbers(6, 3) != dh.DHParameterNumbers(65537, 3)
+ assert dh.DHParameterNumbers(65537, 0) != dh.DHParameterNumbers(65537, 3)
+ assert dh.DHParameterNumbers(65537, 0) != object()
+
+
+def test_dh_private_numbers_equality():
+ params = dh.DHParameterNumbers(65537, 3)
+ public = dh.DHPublicNumbers(1, params)
+ private = dh.DHPrivateNumbers(2, public)
+
+ assert private == dh.DHPrivateNumbers(2, public)
+ assert private != dh.DHPrivateNumbers(0, public)
+ assert private != dh.DHPrivateNumbers(2, dh.DHPublicNumbers(0, params))
+ assert private != dh.DHPrivateNumbers(
+ 2, dh.DHPublicNumbers(1, dh.DHParameterNumbers(65537, 0))
+ )
+ assert private != object()
+
+
+def test_dh_public_numbers_equality():
+ params = dh.DHParameterNumbers(65537, 3)
+ public = dh.DHPublicNumbers(1, params)
+
+ assert public == dh.DHPublicNumbers(1, params)
+ assert public != dh.DHPublicNumbers(0, params)
+ assert public != dh.DHPublicNumbers(1, dh.DHParameterNumbers(65537, 0))
+ assert public != object()
diff --git a/tests/hazmat/primitives/test_dsa.py b/tests/hazmat/primitives/test_dsa.py
index 8c0fb80c..19ca0794 100644
--- a/tests/hazmat/primitives/test_dsa.py
+++ b/tests/hazmat/primitives/test_dsa.py
@@ -4,13 +4,17 @@
from __future__ import absolute_import, division, print_function
+import itertools
import os
import pytest
+from cryptography import utils
from cryptography.exceptions import AlreadyFinalized, InvalidSignature
-from cryptography.hazmat.backends.interfaces import DSABackend
-from cryptography.hazmat.primitives import hashes, interfaces
+from cryptography.hazmat.backends.interfaces import (
+ DSABackend, PEMSerializationBackend
+)
+from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import dsa
from cryptography.hazmat.primitives.asymmetric.utils import (
encode_rfc6979_signature
@@ -26,11 +30,28 @@ from ...utils import (
)
+def _skip_if_no_serialization(key, backend):
+ if not isinstance(key, dsa.DSAPrivateKeyWithSerialization):
+ pytest.skip(
+ "{0} does not support DSA key serialization".format(backend)
+ )
+
+
+def test_skip_if_no_serialization():
+ with pytest.raises(pytest.skip.Exception):
+ _skip_if_no_serialization("notakeywithserialization", "backend")
+
+
+@utils.register_interface(serialization.KeySerializationEncryption)
+class DummyKeyEncryption(object):
+ pass
+
+
@pytest.mark.requires_backend_interface(interface=DSABackend)
class TestDSA(object):
def test_generate_dsa_parameters(self, backend):
parameters = dsa.generate_parameters(1024, backend)
- assert isinstance(parameters, interfaces.DSAParameters)
+ assert isinstance(parameters, dsa.DSAParameters)
def test_generate_invalid_dsa_parameters(self, backend):
with pytest.raises(ValueError):
@@ -51,7 +72,7 @@ class TestDSA(object):
g=vector['g']
).parameters(backend)
skey = parameters.generate_private_key()
- if isinstance(skey, interfaces.DSAPrivateKeyWithNumbers):
+ if isinstance(skey, dsa.DSAPrivateKeyWithNumbers):
numbers = skey.private_numbers()
skey_parameters = numbers.public_numbers.parameter_numbers
pkey = skey.public_key()
@@ -74,7 +95,7 @@ class TestDSA(object):
def test_generate_dsa_private_key_and_parameters(self, backend):
skey = dsa.generate_private_key(1024, backend)
assert skey
- if isinstance(skey, interfaces.DSAPrivateKeyWithNumbers):
+ if isinstance(skey, dsa.DSAPrivateKeyWithNumbers):
numbers = skey.private_numbers()
skey_parameters = numbers.public_numbers.parameter_numbers
assert numbers.public_numbers.y == pow(
@@ -769,3 +790,149 @@ class TestDSANumberEquality(object):
)
)
assert priv != object()
+
+
+@pytest.mark.requires_backend_interface(interface=DSABackend)
+@pytest.mark.requires_backend_interface(interface=PEMSerializationBackend)
+class TestDSASerialization(object):
+ @pytest.mark.parametrize(
+ ("fmt", "password"),
+ itertools.product(
+ [
+ serialization.PrivateFormat.TraditionalOpenSSL,
+ serialization.PrivateFormat.PKCS8
+ ],
+ [
+ b"s",
+ b"longerpassword",
+ b"!*$&(@#$*&($T@%_somesymbols",
+ b"\x01" * 1000,
+ ]
+ )
+ )
+ def test_private_bytes_encrypted_pem(self, backend, fmt, password):
+ key_bytes = load_vectors_from_file(
+ os.path.join("asymmetric", "PKCS8", "unenc-dsa-pkcs8.pem"),
+ lambda pemfile: pemfile.read().encode()
+ )
+ key = serialization.load_pem_private_key(key_bytes, None, backend)
+ _skip_if_no_serialization(key, backend)
+ serialized = key.private_bytes(
+ serialization.Encoding.PEM,
+ fmt,
+ serialization.BestAvailableEncryption(password)
+ )
+ loaded_key = serialization.load_pem_private_key(
+ serialized, password, backend
+ )
+ loaded_priv_num = loaded_key.private_numbers()
+ priv_num = key.private_numbers()
+ assert loaded_priv_num == priv_num
+
+ @pytest.mark.parametrize(
+ "fmt",
+ [
+ serialization.PrivateFormat.TraditionalOpenSSL,
+ serialization.PrivateFormat.PKCS8
+ ],
+ )
+ def test_private_bytes_unencrypted_pem(self, backend, fmt):
+ key_bytes = load_vectors_from_file(
+ os.path.join(
+ "asymmetric",
+ "Traditional_OpenSSL_Serialization",
+ "dsa.1024.pem"
+ ),
+ lambda pemfile: pemfile.read().encode()
+ )
+ key = serialization.load_pem_private_key(key_bytes, None, backend)
+ _skip_if_no_serialization(key, backend)
+ serialized = key.private_bytes(
+ serialization.Encoding.PEM,
+ fmt,
+ serialization.NoEncryption()
+ )
+ loaded_key = serialization.load_pem_private_key(
+ serialized, None, backend
+ )
+ loaded_priv_num = loaded_key.private_numbers()
+ priv_num = key.private_numbers()
+ assert loaded_priv_num == priv_num
+
+ def test_private_bytes_traditional_openssl_unencrypted_pem(self, backend):
+ key_bytes = load_vectors_from_file(
+ os.path.join(
+ "asymmetric",
+ "Traditional_OpenSSL_Serialization",
+ "dsa.1024.pem"
+ ),
+ lambda pemfile: pemfile.read().encode()
+ )
+ key = serialization.load_pem_private_key(key_bytes, None, backend)
+ _skip_if_no_serialization(key, backend)
+ serialized = key.private_bytes(
+ serialization.Encoding.PEM,
+ serialization.PrivateFormat.TraditionalOpenSSL,
+ serialization.NoEncryption()
+ )
+ assert serialized == key_bytes
+
+ def test_private_bytes_invalid_encoding(self, backend):
+ key = load_vectors_from_file(
+ os.path.join("asymmetric", "PKCS8", "unenc-dsa-pkcs8.pem"),
+ lambda pemfile: serialization.load_pem_private_key(
+ pemfile.read().encode(), None, backend
+ )
+ )
+ _skip_if_no_serialization(key, backend)
+ with pytest.raises(TypeError):
+ key.private_bytes(
+ "notencoding",
+ serialization.PrivateFormat.PKCS8,
+ serialization.NoEncryption()
+ )
+
+ def test_private_bytes_invalid_format(self, backend):
+ key = load_vectors_from_file(
+ os.path.join("asymmetric", "PKCS8", "unenc-dsa-pkcs8.pem"),
+ lambda pemfile: serialization.load_pem_private_key(
+ pemfile.read().encode(), None, backend
+ )
+ )
+ _skip_if_no_serialization(key, backend)
+ with pytest.raises(TypeError):
+ key.private_bytes(
+ serialization.Encoding.PEM,
+ "invalidformat",
+ serialization.NoEncryption()
+ )
+
+ def test_private_bytes_invalid_encryption_algorithm(self, backend):
+ key = load_vectors_from_file(
+ os.path.join("asymmetric", "PKCS8", "unenc-dsa-pkcs8.pem"),
+ lambda pemfile: serialization.load_pem_private_key(
+ pemfile.read().encode(), None, backend
+ )
+ )
+ _skip_if_no_serialization(key, backend)
+ with pytest.raises(TypeError):
+ key.private_bytes(
+ serialization.Encoding.PEM,
+ serialization.PrivateFormat.TraditionalOpenSSL,
+ "notanencalg"
+ )
+
+ def test_private_bytes_unsupported_encryption_type(self, backend):
+ key = load_vectors_from_file(
+ os.path.join("asymmetric", "PKCS8", "unenc-dsa-pkcs8.pem"),
+ lambda pemfile: serialization.load_pem_private_key(
+ pemfile.read().encode(), None, backend
+ )
+ )
+ _skip_if_no_serialization(key, backend)
+ with pytest.raises(ValueError):
+ key.private_bytes(
+ serialization.Encoding.PEM,
+ serialization.PrivateFormat.TraditionalOpenSSL,
+ DummyKeyEncryption()
+ )
diff --git a/tests/hazmat/primitives/test_ec.py b/tests/hazmat/primitives/test_ec.py
index ea621ad6..40b1741c 100644
--- a/tests/hazmat/primitives/test_ec.py
+++ b/tests/hazmat/primitives/test_ec.py
@@ -10,8 +10,10 @@ import os
import pytest
from cryptography import exceptions, utils
-from cryptography.hazmat.backends.interfaces import EllipticCurveBackend
-from cryptography.hazmat.primitives import hashes
+from cryptography.hazmat.backends.interfaces import (
+ EllipticCurveBackend, PEMSerializationBackend
+)
+from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives.asymmetric.utils import (
encode_rfc6979_signature
@@ -31,6 +33,18 @@ _HASH_TYPES = {
}
+def _skip_if_no_serialization(key, backend):
+ if not isinstance(
+ key, (
+ ec.EllipticCurvePrivateKeyWithSerialization,
+ ec.EllipticCurvePublicKeyWithSerialization
+ )
+ ):
+ pytest.skip(
+ "{0} does not support EC key serialization".format(backend)
+ )
+
+
def _skip_ecdsa_vector(backend, curve_type, hash_type):
if not backend.elliptic_curve_signature_algorithm_supported(
ec.ECDSA(hash_type()),
@@ -63,12 +77,22 @@ class DummySignatureAlgorithm(object):
algorithm = None
+@utils.register_interface(serialization.KeySerializationEncryption)
+class DummyKeyEncryption(object):
+ pass
+
+
@pytest.mark.requires_backend_interface(interface=EllipticCurveBackend)
def test_skip_curve_unsupported(backend):
with pytest.raises(pytest.skip.Exception):
_skip_curve_unsupported(backend, DummyCurve())
+def test_skip_no_serialization():
+ with pytest.raises(pytest.skip.Exception):
+ _skip_if_no_serialization("fakebackend", "fakekey")
+
+
def test_ec_numbers():
numbers = ec.EllipticCurvePrivateNumbers(
1,
@@ -378,3 +402,221 @@ class TestECNumbersEquality(object):
1, ec.EllipticCurvePublicNumbers(1, 2, ec.SECP521R1())
)
assert priv != object()
+
+
+@pytest.mark.requires_backend_interface(interface=EllipticCurveBackend)
+@pytest.mark.requires_backend_interface(interface=PEMSerializationBackend)
+class TestECSerialization(object):
+ @pytest.mark.parametrize(
+ ("fmt", "password"),
+ itertools.product(
+ [
+ serialization.PrivateFormat.TraditionalOpenSSL,
+ serialization.PrivateFormat.PKCS8
+ ],
+ [
+ b"s",
+ b"longerpassword",
+ b"!*$&(@#$*&($T@%_somesymbols",
+ b"\x01" * 1000,
+ ]
+ )
+ )
+ def test_private_bytes_encrypted_pem(self, backend, fmt, password):
+ _skip_curve_unsupported(backend, ec.SECP256R1())
+ key_bytes = load_vectors_from_file(
+ os.path.join(
+ "asymmetric", "PKCS8", "ec_private_key.pem"),
+ lambda pemfile: pemfile.read().encode()
+ )
+ key = serialization.load_pem_private_key(key_bytes, None, backend)
+ _skip_if_no_serialization(key, backend)
+ serialized = key.private_bytes(
+ serialization.Encoding.PEM,
+ fmt,
+ serialization.BestAvailableEncryption(password)
+ )
+ loaded_key = serialization.load_pem_private_key(
+ serialized, password, backend
+ )
+ loaded_priv_num = loaded_key.private_numbers()
+ priv_num = key.private_numbers()
+ assert loaded_priv_num == priv_num
+
+ @pytest.mark.parametrize(
+ "fmt",
+ [
+ serialization.PrivateFormat.TraditionalOpenSSL,
+ serialization.PrivateFormat.PKCS8
+ ],
+ )
+ def test_private_bytes_unencrypted_pem(self, backend, fmt):
+ _skip_curve_unsupported(backend, ec.SECP256R1())
+ key_bytes = load_vectors_from_file(
+ os.path.join(
+ "asymmetric", "PKCS8", "ec_private_key.pem"),
+ lambda pemfile: pemfile.read().encode()
+ )
+ key = serialization.load_pem_private_key(key_bytes, None, backend)
+ _skip_if_no_serialization(key, backend)
+ serialized = key.private_bytes(
+ serialization.Encoding.PEM,
+ fmt,
+ serialization.NoEncryption()
+ )
+ loaded_key = serialization.load_pem_private_key(
+ serialized, None, backend
+ )
+ loaded_priv_num = loaded_key.private_numbers()
+ priv_num = key.private_numbers()
+ assert loaded_priv_num == priv_num
+
+ def test_private_bytes_traditional_openssl_unencrypted_pem(self, backend):
+ _skip_curve_unsupported(backend, ec.SECP256R1())
+ key_bytes = load_vectors_from_file(
+ os.path.join(
+ "asymmetric", "PEM_Serialization", "ec_private_key.pem"),
+ lambda pemfile: pemfile.read().encode()
+ )
+ key = serialization.load_pem_private_key(key_bytes, None, backend)
+ serialized = key.private_bytes(
+ serialization.Encoding.PEM,
+ serialization.PrivateFormat.TraditionalOpenSSL,
+ serialization.NoEncryption()
+ )
+ assert serialized == key_bytes
+
+ def test_private_bytes_invalid_encoding(self, backend):
+ _skip_curve_unsupported(backend, ec.SECP256R1())
+ key = load_vectors_from_file(
+ os.path.join(
+ "asymmetric", "PKCS8", "ec_private_key.pem"),
+ lambda pemfile: serialization.load_pem_private_key(
+ pemfile.read().encode(), None, backend
+ )
+ )
+ _skip_if_no_serialization(key, backend)
+ with pytest.raises(TypeError):
+ key.private_bytes(
+ "notencoding",
+ serialization.PrivateFormat.PKCS8,
+ serialization.NoEncryption()
+ )
+
+ def test_private_bytes_invalid_format(self, backend):
+ _skip_curve_unsupported(backend, ec.SECP256R1())
+ key = load_vectors_from_file(
+ os.path.join(
+ "asymmetric", "PKCS8", "ec_private_key.pem"),
+ lambda pemfile: serialization.load_pem_private_key(
+ pemfile.read().encode(), None, backend
+ )
+ )
+ _skip_if_no_serialization(key, backend)
+ with pytest.raises(TypeError):
+ key.private_bytes(
+ serialization.Encoding.PEM,
+ "invalidformat",
+ serialization.NoEncryption()
+ )
+
+ def test_private_bytes_invalid_encryption_algorithm(self, backend):
+ _skip_curve_unsupported(backend, ec.SECP256R1())
+ key = load_vectors_from_file(
+ os.path.join(
+ "asymmetric", "PKCS8", "ec_private_key.pem"),
+ lambda pemfile: serialization.load_pem_private_key(
+ pemfile.read().encode(), None, backend
+ )
+ )
+ _skip_if_no_serialization(key, backend)
+ with pytest.raises(TypeError):
+ key.private_bytes(
+ serialization.Encoding.PEM,
+ serialization.PrivateFormat.TraditionalOpenSSL,
+ "notanencalg"
+ )
+
+ def test_private_bytes_unsupported_encryption_type(self, backend):
+ _skip_curve_unsupported(backend, ec.SECP256R1())
+ key = load_vectors_from_file(
+ os.path.join(
+ "asymmetric", "PKCS8", "ec_private_key.pem"),
+ lambda pemfile: serialization.load_pem_private_key(
+ pemfile.read().encode(), None, backend
+ )
+ )
+ _skip_if_no_serialization(key, backend)
+ with pytest.raises(ValueError):
+ key.private_bytes(
+ serialization.Encoding.PEM,
+ serialization.PrivateFormat.TraditionalOpenSSL,
+ DummyKeyEncryption()
+ )
+
+
+@pytest.mark.requires_backend_interface(interface=EllipticCurveBackend)
+@pytest.mark.requires_backend_interface(interface=PEMSerializationBackend)
+class TestEllipticCurvePEMPublicKeySerialization(object):
+ def test_public_bytes_unencrypted_pem(self, backend):
+ _skip_curve_unsupported(backend, ec.SECP256R1())
+ key_bytes = load_vectors_from_file(
+ os.path.join(
+ "asymmetric", "PEM_Serialization", "ec_public_key.pem"
+ ),
+ lambda pemfile: pemfile.read().encode()
+ )
+ key = serialization.load_pem_public_key(key_bytes, backend)
+ _skip_if_no_serialization(key, backend)
+ serialized = key.public_bytes(
+ serialization.Encoding.PEM,
+ serialization.PublicFormat.SubjectPublicKeyInfo,
+ )
+ assert serialized == key_bytes
+
+ def test_public_bytes_invalid_encoding(self, backend):
+ _skip_curve_unsupported(backend, ec.SECP256R1())
+ key = load_vectors_from_file(
+ os.path.join(
+ "asymmetric", "PEM_Serialization", "ec_public_key.pem"
+ ),
+ lambda pemfile: serialization.load_pem_public_key(
+ pemfile.read().encode(), backend
+ )
+ )
+ _skip_if_no_serialization(key, backend)
+ with pytest.raises(TypeError):
+ key.public_bytes(
+ "notencoding",
+ serialization.PublicFormat.SubjectPublicKeyInfo
+ )
+
+ def test_public_bytes_invalid_format(self, backend):
+ _skip_curve_unsupported(backend, ec.SECP256R1())
+ key = load_vectors_from_file(
+ os.path.join(
+ "asymmetric", "PEM_Serialization", "ec_public_key.pem"
+ ),
+ lambda pemfile: serialization.load_pem_public_key(
+ pemfile.read().encode(), backend
+ )
+ )
+ _skip_if_no_serialization(key, backend)
+ with pytest.raises(TypeError):
+ key.public_bytes(serialization.Encoding.PEM, "invalidformat")
+
+ def test_public_bytes_pkcs1_unsupported(self, backend):
+ _skip_curve_unsupported(backend, ec.SECP256R1())
+ key = load_vectors_from_file(
+ os.path.join(
+ "asymmetric", "PEM_Serialization", "ec_public_key.pem"
+ ),
+ lambda pemfile: serialization.load_pem_public_key(
+ pemfile.read().encode(), backend
+ )
+ )
+ _skip_if_no_serialization(key, backend)
+ with pytest.raises(ValueError):
+ key.public_bytes(
+ serialization.Encoding.PEM, serialization.PublicFormat.PKCS1
+ )
diff --git a/tests/hazmat/primitives/test_rsa.py b/tests/hazmat/primitives/test_rsa.py
index 6d8e6874..e6d0ac28 100644
--- a/tests/hazmat/primitives/test_rsa.py
+++ b/tests/hazmat/primitives/test_rsa.py
@@ -15,8 +15,10 @@ from cryptography import utils
from cryptography.exceptions import (
AlreadyFinalized, InvalidSignature, _Reasons
)
-from cryptography.hazmat.backends.interfaces import RSABackend
-from cryptography.hazmat.primitives import hashes, interfaces
+from cryptography.hazmat.backends.interfaces import (
+ PEMSerializationBackend, RSABackend
+)
+from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import padding, rsa
from cryptography.hazmat.primitives.asymmetric.rsa import (
RSAPrivateNumbers, RSAPublicNumbers
@@ -46,6 +48,11 @@ class DummyMGF(object):
_salt_length = 0
+@utils.register_interface(serialization.KeySerializationEncryption)
+class DummyKeyEncryption(object):
+ pass
+
+
def _flatten_pkcs1_examples(vectors):
flattened_vectors = []
for vector in vectors:
@@ -78,6 +85,21 @@ def test_modular_inverse():
)
+def _skip_if_no_serialization(key, backend):
+ if not isinstance(
+ key,
+ (rsa.RSAPrivateKeyWithSerialization, rsa.RSAPublicKeyWithSerialization)
+ ):
+ pytest.skip(
+ "{0} does not support RSA key serialization".format(backend)
+ )
+
+
+def test_skip_if_no_serialization():
+ with pytest.raises(pytest.skip.Exception):
+ _skip_if_no_serialization("notakeywithserialization", "backend")
+
+
@pytest.mark.requires_backend_interface(interface=RSABackend)
class TestRSA(object):
@pytest.mark.parametrize(
@@ -91,7 +113,7 @@ class TestRSA(object):
skey = rsa.generate_private_key(public_exponent, key_size, backend)
assert skey.key_size == key_size
- if isinstance(skey, interfaces.RSAPrivateKeyWithNumbers):
+ if isinstance(skey, rsa.RSAPrivateKeyWithNumbers):
_check_rsa_private_numbers(skey.private_numbers())
pkey = skey.public_key()
assert isinstance(pkey.public_numbers(), rsa.RSAPublicNumbers)
@@ -1725,3 +1747,158 @@ class TestRSAPrimeFactorRecovery(object):
def test_invalid_recover_prime_factors(self):
with pytest.raises(ValueError):
rsa.rsa_recover_prime_factors(34, 3, 7)
+
+
+@pytest.mark.requires_backend_interface(interface=RSABackend)
+@pytest.mark.requires_backend_interface(interface=PEMSerializationBackend)
+class TestRSAPEMPrivateKeySerialization(object):
+ @pytest.mark.parametrize(
+ ("fmt", "password"),
+ itertools.product(
+ [
+ serialization.PrivateFormat.TraditionalOpenSSL,
+ serialization.PrivateFormat.PKCS8
+ ],
+ [
+ b"s",
+ b"longerpassword",
+ b"!*$&(@#$*&($T@%_somesymbols",
+ b"\x01" * 1000,
+ ]
+ )
+ )
+ def test_private_bytes_encrypted_pem(self, backend, fmt, password):
+ key = RSA_KEY_2048.private_key(backend)
+ _skip_if_no_serialization(key, backend)
+ serialized = key.private_bytes(
+ serialization.Encoding.PEM,
+ fmt,
+ serialization.BestAvailableEncryption(password)
+ )
+ loaded_key = serialization.load_pem_private_key(
+ serialized, password, backend
+ )
+ loaded_priv_num = loaded_key.private_numbers()
+ priv_num = key.private_numbers()
+ assert loaded_priv_num == priv_num
+
+ @pytest.mark.parametrize(
+ "fmt",
+ [
+ serialization.PrivateFormat.TraditionalOpenSSL,
+ serialization.PrivateFormat.PKCS8
+ ],
+ )
+ def test_private_bytes_unencrypted_pem(self, backend, fmt):
+ key = RSA_KEY_2048.private_key(backend)
+ _skip_if_no_serialization(key, backend)
+ serialized = key.private_bytes(
+ serialization.Encoding.PEM,
+ fmt,
+ serialization.NoEncryption()
+ )
+ loaded_key = serialization.load_pem_private_key(
+ serialized, None, backend
+ )
+ loaded_priv_num = loaded_key.private_numbers()
+ priv_num = key.private_numbers()
+ assert loaded_priv_num == priv_num
+
+ def test_private_bytes_traditional_openssl_unencrypted_pem(self, backend):
+ key_bytes = load_vectors_from_file(
+ os.path.join(
+ "asymmetric",
+ "Traditional_OpenSSL_Serialization",
+ "testrsa.pem"
+ ),
+ lambda pemfile: pemfile.read().encode()
+ )
+ key = serialization.load_pem_private_key(key_bytes, None, backend)
+ serialized = key.private_bytes(
+ serialization.Encoding.PEM,
+ serialization.PrivateFormat.TraditionalOpenSSL,
+ serialization.NoEncryption()
+ )
+ assert serialized == key_bytes
+
+ def test_private_bytes_invalid_encoding(self, backend):
+ key = RSA_KEY_2048.private_key(backend)
+ _skip_if_no_serialization(key, backend)
+ with pytest.raises(TypeError):
+ key.private_bytes(
+ "notencoding",
+ serialization.PrivateFormat.PKCS8,
+ serialization.NoEncryption()
+ )
+
+ def test_private_bytes_invalid_format(self, backend):
+ key = RSA_KEY_2048.private_key(backend)
+ _skip_if_no_serialization(key, backend)
+ with pytest.raises(TypeError):
+ key.private_bytes(
+ serialization.Encoding.PEM,
+ "invalidformat",
+ serialization.NoEncryption()
+ )
+
+ def test_private_bytes_invalid_encryption_algorithm(self, backend):
+ key = RSA_KEY_2048.private_key(backend)
+ _skip_if_no_serialization(key, backend)
+ with pytest.raises(TypeError):
+ key.private_bytes(
+ serialization.Encoding.PEM,
+ serialization.PrivateFormat.TraditionalOpenSSL,
+ "notanencalg"
+ )
+
+ def test_private_bytes_unsupported_encryption_type(self, backend):
+ key = RSA_KEY_2048.private_key(backend)
+ _skip_if_no_serialization(key, backend)
+ with pytest.raises(ValueError):
+ key.private_bytes(
+ serialization.Encoding.PEM,
+ serialization.PrivateFormat.TraditionalOpenSSL,
+ DummyKeyEncryption()
+ )
+
+
+@pytest.mark.requires_backend_interface(interface=RSABackend)
+@pytest.mark.requires_backend_interface(interface=PEMSerializationBackend)
+class TestRSAPEMPublicKeySerialization(object):
+ def test_public_bytes_unencrypted_pem(self, backend):
+ key_bytes = load_vectors_from_file(
+ os.path.join("asymmetric", "PKCS8", "unenc-rsa-pkcs8.pub.pem"),
+ lambda pemfile: pemfile.read().encode()
+ )
+ key = serialization.load_pem_public_key(key_bytes, backend)
+ _skip_if_no_serialization(key, backend)
+ serialized = key.public_bytes(
+ serialization.Encoding.PEM,
+ serialization.PublicFormat.SubjectPublicKeyInfo,
+ )
+ assert serialized == key_bytes
+
+ def test_public_bytes_pkcs1_unencrypted_pem(self, backend):
+ key_bytes = load_vectors_from_file(
+ os.path.join("asymmetric", "public", "PKCS1", "rsa.pub.pem"),
+ lambda pemfile: pemfile.read().encode()
+ )
+ key = serialization.load_pem_public_key(key_bytes, backend)
+ _skip_if_no_serialization(key, backend)
+ serialized = key.public_bytes(
+ serialization.Encoding.PEM,
+ serialization.PublicFormat.PKCS1,
+ )
+ assert serialized == key_bytes
+
+ def test_public_bytes_invalid_encoding(self, backend):
+ key = RSA_KEY_2048.private_key(backend).public_key()
+ _skip_if_no_serialization(key, backend)
+ with pytest.raises(TypeError):
+ key.public_bytes("notencoding", serialization.PublicFormat.PKCS1)
+
+ def test_public_bytes_invalid_format(self, backend):
+ key = RSA_KEY_2048.private_key(backend).public_key()
+ _skip_if_no_serialization(key, backend)
+ with pytest.raises(TypeError):
+ key.public_bytes(serialization.Encoding.PEM, "invalidformat")
diff --git a/tests/hazmat/primitives/test_serialization.py b/tests/hazmat/primitives/test_serialization.py
index a4a91430..07a8f02e 100644
--- a/tests/hazmat/primitives/test_serialization.py
+++ b/tests/hazmat/primitives/test_serialization.py
@@ -16,15 +16,10 @@ from cryptography.hazmat.backends.interfaces import (
DERSerializationBackend, DSABackend, EllipticCurveBackend,
PEMSerializationBackend, RSABackend
)
-from cryptography.hazmat.primitives import interfaces
-from cryptography.hazmat.primitives.asymmetric import ec
-from cryptography.hazmat.primitives.asymmetric.dsa import (
- DSAParameterNumbers, DSAPublicNumbers
-)
-from cryptography.hazmat.primitives.asymmetric.rsa import RSAPublicNumbers
+from cryptography.hazmat.primitives.asymmetric import dsa, ec, rsa
from cryptography.hazmat.primitives.serialization import (
- load_der_private_key, load_der_public_key, load_pem_private_key,
- load_pem_public_key, load_ssh_public_key
+ BestAvailableEncryption, load_der_private_key, load_der_public_key,
+ load_pem_private_key, load_pem_public_key, load_ssh_public_key
)
@@ -57,8 +52,8 @@ class TestDERSerialization(object):
mode="rb"
)
assert key
- assert isinstance(key, interfaces.RSAPrivateKey)
- if isinstance(key, interfaces.RSAPrivateKeyWithNumbers):
+ assert isinstance(key, rsa.RSAPrivateKey)
+ if isinstance(key, rsa.RSAPrivateKeyWithNumbers):
_check_rsa_private_numbers(key.private_numbers())
@pytest.mark.requires_backend_interface(interface=DSABackend)
@@ -80,8 +75,8 @@ class TestDERSerialization(object):
mode="rb"
)
assert key
- assert isinstance(key, interfaces.DSAPrivateKey)
- if isinstance(key, interfaces.DSAPrivateKeyWithNumbers):
+ assert isinstance(key, dsa.DSAPrivateKey)
+ if isinstance(key, dsa.DSAPrivateKeyWithNumbers):
_check_dsa_private_numbers(key.private_numbers())
@pytest.mark.parametrize(
@@ -103,7 +98,7 @@ class TestDERSerialization(object):
)
assert key
- assert isinstance(key, interfaces.EllipticCurvePrivateKey)
+ assert isinstance(key, ec.EllipticCurvePrivateKey)
assert key.curve.name == "secp256r1"
assert key.curve.key_size == 256
@@ -238,6 +233,7 @@ class TestDERSerialization(object):
"asymmetric", "DER_Serialization", "unenc-rsa-pkcs8.pub.der"),
os.path.join(
"asymmetric", "DER_Serialization", "rsa_public_key.der"),
+ os.path.join("asymmetric", "public", "PKCS1", "rsa.pub.der"),
]
)
@pytest.mark.requires_backend_interface(interface=RSABackend)
@@ -250,8 +246,8 @@ class TestDERSerialization(object):
mode="rb"
)
assert key
- assert isinstance(key, interfaces.RSAPublicKey)
- if isinstance(key, interfaces.RSAPublicKeyWithNumbers):
+ assert isinstance(key, rsa.RSAPublicKey)
+ if isinstance(key, rsa.RSAPublicKeyWithNumbers):
numbers = key.public_numbers()
assert numbers.e == 65537
@@ -278,7 +274,7 @@ class TestDERSerialization(object):
mode="rb"
)
assert key
- assert isinstance(key, interfaces.DSAPublicKey)
+ assert isinstance(key, dsa.DSAPublicKey)
@pytest.mark.requires_backend_interface(interface=EllipticCurveBackend)
def test_load_ec_public_key(self, backend):
@@ -293,7 +289,7 @@ class TestDERSerialization(object):
mode="rb"
)
assert key
- assert isinstance(key, interfaces.EllipticCurvePublicKey)
+ assert isinstance(key, ec.EllipticCurvePublicKey)
assert key.curve.name == "secp256r1"
assert key.curve.key_size == 256
@@ -333,8 +329,8 @@ class TestPEMSerialization(object):
)
assert key
- assert isinstance(key, interfaces.RSAPrivateKey)
- if isinstance(key, interfaces.RSAPrivateKeyWithNumbers):
+ assert isinstance(key, rsa.RSAPrivateKey)
+ if isinstance(key, rsa.RSAPrivateKeyWithNumbers):
_check_rsa_private_numbers(key.private_numbers())
@pytest.mark.parametrize(
@@ -355,8 +351,8 @@ class TestPEMSerialization(object):
)
)
assert key
- assert isinstance(key, interfaces.DSAPrivateKey)
- if isinstance(key, interfaces.DSAPrivateKeyWithNumbers):
+ assert isinstance(key, dsa.DSAPrivateKey)
+ if isinstance(key, dsa.DSAPrivateKeyWithNumbers):
_check_dsa_private_numbers(key.private_numbers())
@pytest.mark.parametrize(
@@ -379,7 +375,7 @@ class TestPEMSerialization(object):
)
assert key
- assert isinstance(key, interfaces.EllipticCurvePrivateKey)
+ assert isinstance(key, ec.EllipticCurvePrivateKey)
assert key.curve.name == "secp256r1"
assert key.curve.key_size == 256
@@ -389,6 +385,7 @@ class TestPEMSerialization(object):
os.path.join("asymmetric", "PKCS8", "unenc-rsa-pkcs8.pub.pem"),
os.path.join(
"asymmetric", "PEM_Serialization", "rsa_public_key.pem"),
+ os.path.join("asymmetric", "public", "PKCS1", "rsa.pub.pem"),
]
)
def test_load_pem_rsa_public_key(self, key_file, backend):
@@ -399,8 +396,8 @@ class TestPEMSerialization(object):
)
)
assert key
- assert isinstance(key, interfaces.RSAPublicKey)
- if isinstance(key, interfaces.RSAPublicKeyWithNumbers):
+ assert isinstance(key, rsa.RSAPublicKey)
+ if isinstance(key, rsa.RSAPublicKeyWithNumbers):
numbers = key.public_numbers()
assert numbers.e == 65537
@@ -421,7 +418,7 @@ class TestPEMSerialization(object):
)
)
assert key
- assert isinstance(key, interfaces.DSAPublicKey)
+ assert isinstance(key, dsa.DSAPublicKey)
@pytest.mark.requires_backend_interface(interface=EllipticCurveBackend)
def test_load_ec_public_key(self, backend):
@@ -435,7 +432,7 @@ class TestPEMSerialization(object):
)
)
assert key
- assert isinstance(key, interfaces.EllipticCurvePublicKey)
+ assert isinstance(key, ec.EllipticCurvePublicKey)
assert key.curve.name == "secp256r1"
assert key.curve.key_size == 256
@@ -542,7 +539,7 @@ class TestPEMSerialization(object):
)
)
- def test_wrong_format(self, backend):
+ def test_wrong_private_format(self, backend):
key_data = b"---- NOT A KEY ----\n"
with pytest.raises(ValueError):
@@ -555,6 +552,12 @@ class TestPEMSerialization(object):
key_data, b"this password will not be used", backend
)
+ def test_wrong_public_format(self, backend):
+ key_data = b"---- NOT A KEY ----\n"
+
+ with pytest.raises(ValueError):
+ load_pem_public_key(key_data, backend)
+
def test_corrupt_traditional_format(self, backend):
# privkey.pem with a bunch of data missing.
key_data = textwrap.dedent("""\
@@ -762,12 +765,12 @@ class TestPEMSerialization(object):
)
)
assert key
- assert isinstance(key, interfaces.DSAPrivateKey)
+ assert isinstance(key, dsa.DSAPrivateKey)
params = key.parameters()
- assert isinstance(params, interfaces.DSAParameters)
+ assert isinstance(params, dsa.DSAParameters)
- if isinstance(params, interfaces.DSAParametersWithNumbers):
+ if isinstance(params, dsa.DSAParametersWithNumbers):
num = key.private_numbers()
pub = num.public_numbers
parameter_numbers = pub.parameter_numbers
@@ -917,7 +920,7 @@ class TestRSASSHSerialization(object):
key = load_ssh_public_key(ssh_key, backend)
assert key is not None
- assert isinstance(key, interfaces.RSAPublicKey)
+ assert isinstance(key, rsa.RSAPublicKey)
numbers = key.public_numbers()
@@ -934,7 +937,7 @@ class TestRSASSHSerialization(object):
'46F8706AB88DDADBD9E8204D48B87789081E074024C8996783B31'
'7076A98ABF0A2D8550EAF2097D8CCC7BE76EF', 16)
- expected = RSAPublicNumbers(expected_e, expected_n)
+ expected = rsa.RSAPublicNumbers(expected_e, expected_n)
assert numbers == expected
@@ -1017,7 +1020,7 @@ class TestDSSSSHSerialization(object):
key = load_ssh_public_key(ssh_key, backend)
assert key is not None
- assert isinstance(key, interfaces.DSAPublicKey)
+ assert isinstance(key, dsa.DSAPublicKey)
numbers = key.public_numbers()
@@ -1043,9 +1046,9 @@ class TestDSSSSHSerialization(object):
"debb5982fc94d6a8c291f758feae63ad769a5621947221522a2dc31d18ede6f"
"b656", 16
)
- expected = DSAPublicNumbers(
+ expected = dsa.DSAPublicNumbers(
expected_y,
- DSAParameterNumbers(expected_p, expected_q, expected_g)
+ dsa.DSAParameterNumbers(expected_p, expected_q, expected_g)
)
assert numbers == expected
@@ -1062,7 +1065,7 @@ class TestECDSASSHSerialization(object):
b"teIg1TO03/FD9hbpBFgBeix3NrCFPls= root@cloud-server-01"
)
key = load_ssh_public_key(ssh_key, backend)
- assert isinstance(key, interfaces.EllipticCurvePublicKey)
+ assert isinstance(key, ec.EllipticCurvePublicKey)
expected_x = int(
"44196257377740326295529888716212621920056478823906609851236662550"
@@ -1164,3 +1167,13 @@ class TestECDSASSHSerialization(object):
)
with pytest.raises(ValueError):
load_ssh_public_key(ssh_key, backend)
+
+
+class TestKeySerializationEncryptionTypes(object):
+ def test_non_bytes_password(self):
+ with pytest.raises(ValueError):
+ BestAvailableEncryption(object())
+
+ def test_encryption_with_zero_length_password(self):
+ with pytest.raises(ValueError):
+ BestAvailableEncryption(b"")
diff --git a/vectors/cryptography_vectors/asymmetric/public/PKCS1/dsa.pub.pem b/vectors/cryptography_vectors/asymmetric/public/PKCS1/dsa.pub.pem
new file mode 100644
index 00000000..a2ce0bb0
--- /dev/null
+++ b/vectors/cryptography_vectors/asymmetric/public/PKCS1/dsa.pub.pem
@@ -0,0 +1,7 @@
+-----BEGIN DSA PUBLIC KEY-----
+MIHfAkEAyJSJ+g+P/knVcgDwwTzC7Pwg/pWs2EMd/r+lYlXhNfzg0biuXRul8VR4
+VUC/phySExY0PdcqItkR/xYAYNMbNwJBAOoV57X0FxKO/PrNa/MkoWzkCKV/hzhE
+p0zbFdsicw+hIjJ7S6Sd/FlDlo89HQZ2FuvWJ6wGLM1j00r39+F2qbMCFQCrkhIX
+SG+is37hz1IaBeEudjB2HQJAR0AloavBvtsng8obsjLb7EKnB+pSeHr/BdIQ3VH7
+fWLOqqkzFeRrYMDzUpl36XktY6Yq8EJYlW9pCMmBVNy/dQ==
+-----END DSA PUBLIC KEY-----
diff --git a/vectors/cryptography_vectors/asymmetric/public/PKCS1/rsa.pub.der b/vectors/cryptography_vectors/asymmetric/public/PKCS1/rsa.pub.der
new file mode 100644
index 00000000..4bccbb26
--- /dev/null
+++ b/vectors/cryptography_vectors/asymmetric/public/PKCS1/rsa.pub.der
Binary files differ
diff --git a/vectors/cryptography_vectors/asymmetric/public/PKCS1/rsa.pub.pem b/vectors/cryptography_vectors/asymmetric/public/PKCS1/rsa.pub.pem
new file mode 100644
index 00000000..6db528cf
--- /dev/null
+++ b/vectors/cryptography_vectors/asymmetric/public/PKCS1/rsa.pub.pem
@@ -0,0 +1,5 @@
+-----BEGIN RSA PUBLIC KEY-----
+MIGJAoGBALskegl+DrI3Msw5Z63xnj1rgoPR0KykwBi+jZgAwHv/B0TJyhy6NuEn
+af+x442L7lepOqoWQzlUGXyuaSQU9mT/vHTGZ2xM8QJJaccr4eGho0MU9HePyNCF
+WjWVrGKpwSEAd6CLlzC0Wiy4kC9IoAUoS/IPjeyLTQNCddatgcARAgMBAAE=
+-----END RSA PUBLIC KEY-----
diff --git a/vectors/cryptography_vectors/x509/requests/dsa_sha1.pem b/vectors/cryptography_vectors/x509/requests/dsa_sha1.pem
new file mode 100644
index 00000000..11688dbb
--- /dev/null
+++ b/vectors/cryptography_vectors/x509/requests/dsa_sha1.pem
@@ -0,0 +1,15 @@
+-----BEGIN CERTIFICATE REQUEST-----
+MIICWDCCAhgCAQAwVzEYMBYGA1UEAwwPY3J5cHRvZ3JhcGh5LmlvMQ0wCwYDVQQK
+DARQeUNBMQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxDzANBgNVBAcMBkF1
+c3RpbjCCAbYwggErBgcqhkjOOAQBMIIBHgKBgQCNf628CeKEqvppFUzqJBdwBJCe
+UZ+LNdaFzeW07NyVg+dNNwoPiK2pjwJvJ3Yvs9XaeDb5ht/Ns1ieW5Jb6hFN78A+
++B2uMMJLvG3z1YjpNCe7pkID1KWxaHsrXjtkPUxhSXb4n5WjjT5MiQZfupdRTCLF
+Ctu/KJFjp0tUhZs1twIVAINd5WvQfPf4LiAy/niUmu0ReqLvAoGAH3F7Wgd4L8Lk
+5o4xH+qRpU7dNrhqxjTRTwWmipfq6dLvMfse895Cw9EA35ymT1vcKux7/ftHTPgx
+/qBYU7XgWfLSSYCgrEY/HoGK81I+PLeaOdRfqScxiXdShCRpz4VAsBSRAk6q+85g
+GOih9GWMND9Lp8CyHlN2oh9L64SRlh4DgYQAAoGABxPwdkH2Npu1qVRSdKLUwBmY
+Nn+zcbueE0NjY2cu1o+CF0wt4FyOg5vG3laN1QuijY2dhxlCOq7FVX3xDXc6si1t
+Zcu4eASml7yP2WW5Uvn36FDt8TyKzbXXU7bRDlngtXMuPIK6+hQDQrxKO7oWvQaB
+yKai27t+/mziuEY7FwugADAJBgcqhkjOOAQDAy8AMCwCFGHVjcAo0BEIGKfYF9dC
+NXJ8Ss/fAhQJe1LhmOzpXeFyc/CpJN8jzp2BiA==
+-----END CERTIFICATE REQUEST-----
diff --git a/vectors/cryptography_vectors/x509/requests/ec_sha256.pem b/vectors/cryptography_vectors/x509/requests/ec_sha256.pem
new file mode 100644
index 00000000..9497ae3c
--- /dev/null
+++ b/vectors/cryptography_vectors/x509/requests/ec_sha256.pem
@@ -0,0 +1,10 @@
+-----BEGIN CERTIFICATE REQUEST-----
+MIIBTzCB1gIBADBXMRgwFgYDVQQDDA9jcnlwdG9ncmFwaHkuaW8xDTALBgNVBAoM
+BFB5Q0ExCzAJBgNVBAYTAlVTMQ4wDAYDVQQIDAVUZXhhczEPMA0GA1UEBwwGQXVz
+dGluMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE3hm1FMCzw66bOY6j4mtegWvc+RAs
+rY8S/gL55MkkhySzkpftdYLgTYsypVEDjQkIaAOm0/uRoaEWfsAhWLAO+tOck5ZG
+L6zP8P+vcVWBKQnTcmvVn94AHP9LubL1r4y6oAAwCgYIKoZIzj0EAwIDaAAwZQIw
+LBqffejBeHMy0jB6iGtHalnxcrmw4lAmLzI4sbRe4RK7brNbD7VqEjuSlushLf/D
+AjEAlM9EDJXFKCfVVq5tdlAOMAglXUfCn37ngu11WOUb/XaqRd9tmZ7VxGM0f+I4
+LRdR
+-----END CERTIFICATE REQUEST-----
diff --git a/vectors/cryptography_vectors/x509/requests/rsa_md4.pem b/vectors/cryptography_vectors/x509/requests/rsa_md4.pem
new file mode 100644
index 00000000..f62c8ef8
--- /dev/null
+++ b/vectors/cryptography_vectors/x509/requests/rsa_md4.pem
@@ -0,0 +1,16 @@
+-----BEGIN CERTIFICATE REQUEST-----
+MIICnDCCAYQCAQAwVzEYMBYGA1UEAwwPY3J5cHRvZ3JhcGh5LmlvMQ0wCwYDVQQK
+DARQeUNBMQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxDzANBgNVBAcMBkF1
+c3RpbjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMpZCKiob1IpjL+4
+S/J9Jgcm0ewZ/TbgJw9MB2sTQu7Ljmad9MttNeMyd9wa9fQIhenlpKjJWPSBekSX
+P0uvbAKkY00eCM/mi5Deb2lZaXkfpFC2DlsOKm4uyzmyd/ob2/rSeml7+u+xnGSM
+1MeYjuS5R7KrQUX+HJyw53cxkEHykXcsAvu3WxpnpZzLSFBNiE6lJZUl4vH86aUl
+qrB4C87c150uT01ouEqpon8KfwCnuH1Zqw1e+XVoW6sMqvkoqx2uSJxlMzlWpXhI
+rd3bMXHJ+mJyuDP5UVhQU0WuWXPO1JzEfVwVKigbpy8heHare/6tFHXp3V98fhEO
+qJGaArUCAwEAAaAAMA0GCSqGSIb3DQEBAwUAA4IBAQCp9f8iBtvibyoBB5W+h9RS
+IsbL/bY46MzfjkWaCr1GACC3FefO88qsQYZJNsnqrHKwl8BJUNxf5bLMyFmyLW9D
+jBah8wfo3ftjos1u1F1Bt31FcL7UlDoDLZ1LA3cP/JodSbUVZ8ByVI1bj0MRdAx5
+16YNJvCtzXQtoRZxr940u+S10G6E8sSwfGjqJIgOTiF3oYmS1hSBsIGJzrJ4HOp/
+FAN5pxYW3C0sAM4Aq7qSRHQdFoIXovgRxB4XFedIveEidralOe1K0ShV675jmjwa
+zoO/bTZGCIISD3KoIaCdkJjKoN0r2ckwhBTxsQsC6iO/FU5rBS4DG7369Y7FNIJ6
+-----END CERTIFICATE REQUEST-----
diff --git a/vectors/cryptography_vectors/x509/requests/rsa_sha1.pem b/vectors/cryptography_vectors/x509/requests/rsa_sha1.pem
new file mode 100644
index 00000000..0656c316
--- /dev/null
+++ b/vectors/cryptography_vectors/x509/requests/rsa_sha1.pem
@@ -0,0 +1,16 @@
+-----BEGIN CERTIFICATE REQUEST-----
+MIICnDCCAYQCAQAwVzELMAkGA1UEBhMCVVMxDjAMBgNVBAgTBVRleGFzMQ8wDQYD
+VQQHEwZBdXN0aW4xDTALBgNVBAoTBFB5Q0ExGDAWBgNVBAMTD2NyeXB0b2dyYXBo
+eS5pbzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKhAp4Rgy4YQZt+j
+BFqUumzxt6udJMdhz/3cwstePx3D5L4lPnA57xT+nW0jBPUNny4VhMUVMKt1CG81
+cTi/97hU0GfR1fOE8fLyw5zDsVQV4mOFVO+EAmSK4+8IM28it+zG1DMcKyHDCRp/
+epUYGAdUpkZkC2BBnkzG9ceYEQp/Awpjn+h+M7R3bfzZk5QOx3arV6GBrYWYhXl2
+3DA/mlc8phmrP+WWMo6SgGuChoPtwXzCVrQZSKK/qNBH0hWNPY4GmqBfqFsycqux
+xLRCK2Nm87cOZCN3sUXNYlnl0+fbBI1Rkh5Qdmo3sbEw7msR9QfSCoNAAejeFqks
+FPLpZKMCAwEAAaAAMA0GCSqGSIb3DQEBBQUAA4IBAQCDZMhv+7/gv8miH4MSVmWM
+qJiXQbgFdtNvCKk0YDpDsYNyRtABZ6UYq7Hee1Gh5bfr6hSUSACBixqSPIBPEgoN
+Yk9jEO956GEnVcKwHcx/Wd/bzg2z8mMPGF9QS4wXr4DL02T6X9poM3FTkwlIImzU
+Y4KHoK7WUk0wBohcGQKKHuL1qR1ud9uqC0mZbuCgxgtVthvQgKCLs0qn8+B+kfN/
+ahFkW+LYZUwVcNzaFF7XzJIBf31TIl1/KD80Wexb2kHPbdddQ2dsVDSDOFImt+T6
+KchznxsOrxmWE1k5kZeYYuNhgejEwnDDVLf1LBKNsbcGOYIzJMfqJHkbe8PXAF87
+-----END CERTIFICATE REQUEST-----
diff --git a/vectors/cryptography_vectors/x509/requests/rsa_sha256.pem b/vectors/cryptography_vectors/x509/requests/rsa_sha256.pem
new file mode 100644
index 00000000..06393d23
--- /dev/null
+++ b/vectors/cryptography_vectors/x509/requests/rsa_sha256.pem
@@ -0,0 +1,16 @@
+-----BEGIN CERTIFICATE REQUEST-----
+MIICnDCCAYQCAQAwVzELMAkGA1UEBhMCVVMxDjAMBgNVBAgMBVRleGFzMQ8wDQYD
+VQQHDAZBdXN0aW4xDTALBgNVBAoMBFB5Q0ExGDAWBgNVBAMMD2NyeXB0b2dyYXBo
+eS5pbzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALWsaWBzupTPnC9A
+xS2dbZtOCRgERIKL+SVOxaFdXko/TsmbUCY2GQ8xhLvhD0R1kEnpzI+wQ/3OQraq
+XysouWEVJp07L7AW14tkAwuo+Jy8/Or7NUrOCtOTN2fEO+hT81ihaocG5CwbEUUE
+QGSzX9XJjuyVUBRpou8muv0ZRcrJG08AP9pss4MMjkvTEfX6ldht9NWXlTz2AnOL
+MR4WSLS1H/Imv9OtO+nFkLUkedrV5eJasW5N2c6YkuHrEOK/Nv28WavULyBpUAJu
+k5iHQ1+/5A4spmarX3pvoqxMIeJ/Snju8Xn2T6MDSI4Y+9LbqplRDhl5LKo53RZi
+z4JKq8kCAwEAAaAAMA0GCSqGSIb3DQEBCwUAA4IBAQCgpYRnfdC9BjlohcdiUjqz
+jzzQgRs8+29YFRyQHfHHT0K0YYEeiblvdNExXVD2s3ppP/C/X2lhs5Nif8YaHI1d
+wU0VDIeY55qMv1uhvnK34GkISl3U4v4VLm7bSoPHYmzNgtFQTKs3bZVSz2EtNGAJ
+xRfSWcRYmcyJNvTmFStmsqa4NFvCU7PrPraxMJn5tRFJOEiN25qqgEehHwC+mEL4
+700bmos0oF3IiWa5iTByAA2rv+Z8vlpTjJe9bwVrXi7tBLQRV5Kb6EfXPn4jLd+q
+Qj3uFk6RyV3DyyM93L8bUSaX2gN/LC55sOnCaGR2t5MrvcrfRDe2EbrnqJsoOSu8
+-----END CERTIFICATE REQUEST-----