diff options
author | Paul Kehrer <paul.l.kehrer@gmail.com> | 2015-03-13 10:07:26 -0500 |
---|---|---|
committer | Paul Kehrer <paul.l.kehrer@gmail.com> | 2015-03-13 10:07:26 -0500 |
commit | 22d25d5674faba9170f64e6a4714dfa0c62cc5d7 (patch) | |
tree | 991f2f68d82d91ea066bebc3e60b636fc0bc1713 | |
parent | 710877611effc64cbdebe41c1cd91f52e9f2513c (diff) | |
download | cryptography-22d25d5674faba9170f64e6a4714dfa0c62cc5d7.tar.gz cryptography-22d25d5674faba9170f64e6a4714dfa0c62cc5d7.tar.bz2 cryptography-22d25d5674faba9170f64e6a4714dfa0c62cc5d7.zip |
support RSA DER private key serialization
-rw-r--r-- | src/cryptography/hazmat/backends/openssl/backend.py | 62 | ||||
-rw-r--r-- | tests/hazmat/backends/test_openssl.py | 15 | ||||
-rw-r--r-- | tests/hazmat/primitives/test_rsa.py | 103 |
3 files changed, 139 insertions, 41 deletions
diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py index 6b5c1a0c..150dbfc0 100644 --- a/src/cryptography/hazmat/backends/openssl/backend.py +++ b/src/cryptography/hazmat/backends/openssl/backend.py @@ -1133,24 +1133,6 @@ class Backend(object): "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: - if evp_pkey.type == self._lib.EVP_PKEY_RSA: - write_bio = self._lib.PEM_write_bio_RSAPrivateKey - elif evp_pkey.type == self._lib.EVP_PKEY_DSA: - write_bio = self._lib.PEM_write_bio_DSAPrivateKey - elif (self._lib.Cryptography_HAS_EC == 1 and - evp_pkey.type == self._lib.EVP_PKEY_EC): - write_bio = self._lib.PEM_write_bio_ECPrivateKey - - key = cdata - if not isinstance(encryption_algorithm, serialization.KeySerializationEncryption): raise TypeError( @@ -1178,6 +1160,37 @@ class Backend(object): else: raise ValueError("Unsupported encryption type") + if encoding is serialization.Encoding.PEM: + if format is serialization.PrivateFormat.PKCS8: + write_bio = self._lib.PEM_write_bio_PKCS8PrivateKey + key = evp_pkey + elif format is serialization.PrivateFormat.TraditionalOpenSSL: + if evp_pkey.type == self._lib.EVP_PKEY_RSA: + write_bio = self._lib.PEM_write_bio_RSAPrivateKey + elif evp_pkey.type == self._lib.EVP_PKEY_DSA: + write_bio = self._lib.PEM_write_bio_DSAPrivateKey + elif (self._lib.Cryptography_HAS_EC == 1 and + evp_pkey.type == self._lib.EVP_PKEY_EC): + write_bio = self._lib.PEM_write_bio_ECPrivateKey + + key = cdata + elif encoding is serialization.Encoding.DER: + if format is serialization.PrivateFormat.TraditionalOpenSSL: + if not isinstance( + encryption_algorithm, serialization.NoEncryption + ): + raise ValueError( + "Encryption is not supported for DER encoded " + "traditional OpenSSL keys" + ) + + return self._private_key_bytes_traditional_der( + evp_pkey.type, cdata + ) + elif format is serialization.PrivateFormat.PKCS8: + write_bio = self._lib.i2d_PKCS8PrivateKey_bio + key = evp_pkey + bio = self._create_mem_bio() res = write_bio( bio, @@ -1191,6 +1204,19 @@ class Backend(object): assert res == 1 return self._read_mem_bio(bio) + def _private_key_bytes_traditional_der(self, type, cdata): + if type == self._lib.EVP_PKEY_RSA: + write_bio = self._lib.i2d_RSAPrivateKey_bio + else: + raise TypeError( + "Only RSA keys are supported for DER serialization" + ) + + bio = self._create_mem_bio() + res = write_bio(bio, cdata) + assert res == 1 + return self._read_mem_bio(bio) + def _public_key_bytes(self, encoding, format, evp_pkey, cdata): if not isinstance(encoding, serialization.Encoding): raise TypeError("encoding must be an item from the Encoding enum") diff --git a/tests/hazmat/backends/test_openssl.py b/tests/hazmat/backends/test_openssl.py index cfdc06b4..410d3d2a 100644 --- a/tests/hazmat/backends/test_openssl.py +++ b/tests/hazmat/backends/test_openssl.py @@ -15,7 +15,7 @@ import pytest from cryptography import utils from cryptography.exceptions import InternalError, _Reasons -from cryptography.hazmat.backends.interfaces import RSABackend +from cryptography.hazmat.backends.interfaces import DSABackend, RSABackend from cryptography.hazmat.backends.openssl.backend import ( Backend, backend ) @@ -508,11 +508,18 @@ class TestRSAPEMSerialization(object): serialization.BestAvailableEncryption(password) ) + +@pytest.mark.requires_backend_interface(interface=DSABackend) +class TestDSADERSerialization(object): def test_unsupported_private_key_encoding(self): - key = RSA_KEY_2048.private_key(backend) - with pytest.raises(ValueError): + 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) + with pytest.raises(TypeError): key.private_bytes( serialization.Encoding.DER, - serialization.PrivateFormat.PKCS8, + serialization.PrivateFormat.TraditionalOpenSSL, serialization.NoEncryption() ) diff --git a/tests/hazmat/primitives/test_rsa.py b/tests/hazmat/primitives/test_rsa.py index ab7cc3ad..eb12df8d 100644 --- a/tests/hazmat/primitives/test_rsa.py +++ b/tests/hazmat/primitives/test_rsa.py @@ -1751,7 +1751,7 @@ class TestRSAPrimeFactorRecovery(object): @pytest.mark.requires_backend_interface(interface=RSABackend) @pytest.mark.requires_backend_interface(interface=PEMSerializationBackend) -class TestRSAPEMPrivateKeySerialization(object): +class TestRSAPrivateKeySerialization(object): @pytest.mark.parametrize( ("fmt", "password"), itertools.product( @@ -1783,44 +1783,109 @@ class TestRSAPEMPrivateKeySerialization(object): assert loaded_priv_num == priv_num @pytest.mark.parametrize( - "fmt", + ("fmt", "password"), [ - serialization.PrivateFormat.TraditionalOpenSSL, - serialization.PrivateFormat.PKCS8 - ], + [serialization.PrivateFormat.PKCS8, b"s"], + [serialization.PrivateFormat.PKCS8, b"longerpassword"], + [serialization.PrivateFormat.PKCS8, b"!*$&(@#$*&($T@%_somesymbol"], + [serialization.PrivateFormat.PKCS8, b"\x01" * 1000] + ] ) - def test_private_bytes_unencrypted_pem(self, backend, fmt): + def test_private_bytes_encrypted_der(self, backend, fmt, password): key = RSA_KEY_2048.private_key(backend) _skip_if_no_serialization(key, backend) serialized = key.private_bytes( - serialization.Encoding.PEM, + serialization.Encoding.DER, fmt, - serialization.NoEncryption() + serialization.BestAvailableEncryption(password) ) - loaded_key = serialization.load_pem_private_key( - serialized, None, backend + loaded_key = serialization.load_der_private_key( + serialized, password, 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): + @pytest.mark.parametrize( + ("encoding", "fmt", "loader_func"), + [ + [ + serialization.Encoding.PEM, + serialization.PrivateFormat.TraditionalOpenSSL, + serialization.load_pem_private_key + ], + [ + serialization.Encoding.DER, + serialization.PrivateFormat.TraditionalOpenSSL, + serialization.load_der_private_key + ], + [ + serialization.Encoding.PEM, + serialization.PrivateFormat.PKCS8, + serialization.load_pem_private_key + ], + [ + serialization.Encoding.DER, + serialization.PrivateFormat.PKCS8, + serialization.load_der_private_key + ], + ] + ) + def test_private_bytes_unencrypted(self, backend, encoding, fmt, + loader_func): + key = RSA_KEY_2048.private_key(backend) + _skip_if_no_serialization(key, backend) + serialized = key.private_bytes( + encoding, fmt, serialization.NoEncryption() + ) + loaded_key = loader_func(serialized, None, backend) + loaded_priv_num = loaded_key.private_numbers() + priv_num = key.private_numbers() + assert loaded_priv_num == priv_num + + @pytest.mark.parametrize( + ("key_path", "encoding", "loader_func"), + [ + [ + os.path.join( + "asymmetric", + "Traditional_OpenSSL_Serialization", + "testrsa.pem" + ), + serialization.Encoding.PEM, + serialization.load_pem_private_key + ], + [ + os.path.join("asymmetric", "DER_Serialization", "testrsa.der"), + serialization.Encoding.DER, + serialization.load_der_private_key + ], + ] + ) + def test_private_bytes_traditional_openssl_unencrypted( + self, backend, key_path, encoding, loader_func + ): key_bytes = load_vectors_from_file( - os.path.join( - "asymmetric", - "Traditional_OpenSSL_Serialization", - "testrsa.pem" - ), - lambda pemfile: pemfile.read().encode() + key_path, lambda pemfile: pemfile.read(), mode="rb" ) - key = serialization.load_pem_private_key(key_bytes, None, backend) + key = loader_func(key_bytes, None, backend) serialized = key.private_bytes( - serialization.Encoding.PEM, + encoding, serialization.PrivateFormat.TraditionalOpenSSL, serialization.NoEncryption() ) assert serialized == key_bytes + def test_private_bytes_traditional_der_encrypted_invalid(self, backend): + key = RSA_KEY_2048.private_key(backend) + _skip_if_no_serialization(key, backend) + with pytest.raises(ValueError): + key.private_bytes( + serialization.Encoding.DER, + serialization.PrivateFormat.TraditionalOpenSSL, + serialization.BestAvailableEncryption(b"password") + ) + def test_private_bytes_invalid_encoding(self, backend): key = RSA_KEY_2048.private_key(backend) _skip_if_no_serialization(key, backend) |