diff options
-rw-r--r-- | cryptography/hazmat/backends/interfaces.py | 6 | ||||
-rw-r--r-- | cryptography/hazmat/backends/multibackend.py | 11 | ||||
-rw-r--r-- | cryptography/hazmat/backends/openssl/backend.py | 29 | ||||
-rw-r--r-- | cryptography/hazmat/primitives/serialization.py | 4 | ||||
-rw-r--r-- | docs/hazmat/primitives/asymmetric/serialization.rst | 21 | ||||
-rw-r--r-- | tests/hazmat/primitives/test_serialization.py | 36 | ||||
-rw-r--r-- | vectors/cryptography_vectors/asymmetric/PEM_Serialization/README.txt | 4 |
7 files changed, 106 insertions, 5 deletions
diff --git a/cryptography/hazmat/backends/interfaces.py b/cryptography/hazmat/backends/interfaces.py index 3761e254..187d7fc5 100644 --- a/cryptography/hazmat/backends/interfaces.py +++ b/cryptography/hazmat/backends/interfaces.py @@ -281,6 +281,12 @@ class PEMSerializationBackend(object): if the data is encrypted. """ + @abc.abstractmethod + def load_pem_public_key(self, data, password): + """ + Loads a public key from PEM encoded data. + """ + @six.add_metaclass(abc.ABCMeta) class TraditionalOpenSSLSerializationBackend(object): diff --git a/cryptography/hazmat/backends/multibackend.py b/cryptography/hazmat/backends/multibackend.py index 221f1a1e..925e762d 100644 --- a/cryptography/hazmat/backends/multibackend.py +++ b/cryptography/hazmat/backends/multibackend.py @@ -187,14 +187,12 @@ class MultiBackend(object): def load_rsa_private_numbers(self, numbers): for b in self._filtered_backends(RSABackend): return b.load_rsa_private_numbers(numbers) - raise UnsupportedAlgorithm("RSA is not supported by the backend", _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM) def load_rsa_public_numbers(self, numbers): for b in self._filtered_backends(RSABackend): return b.load_rsa_public_numbers(numbers) - raise UnsupportedAlgorithm("RSA is not supported by the backend", _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM) @@ -329,6 +327,15 @@ class MultiBackend(object): _Reasons.UNSUPPORTED_SERIALIZATION ) + def load_pem_public_key(self, data): + for b in self._filtered_backends(PEMSerializationBackend): + return b.load_pem_public_key(data) + + raise UnsupportedAlgorithm( + "This backend does not support this key serialization.", + _Reasons.UNSUPPORTED_SERIALIZATION + ) + def load_pkcs8_pem_private_key(self, data, password): for b in self._filtered_backends(PKCS8SerializationBackend): return b.load_pkcs8_pem_private_key(data, password) diff --git a/cryptography/hazmat/backends/openssl/backend.py b/cryptography/hazmat/backends/openssl/backend.py index cb988ac9..dd50fd3b 100644 --- a/cryptography/hazmat/backends/openssl/backend.py +++ b/cryptography/hazmat/backends/openssl/backend.py @@ -483,6 +483,27 @@ class Backend(object): else: raise UnsupportedAlgorithm("Unsupported key type.") + def _evp_pkey_to_public_key(self, evp_pkey): + """ + Return the appropriate type of PublicKey given an evp_pkey cdata + pointer. + """ + + type = evp_pkey.type + + if type == self._lib.EVP_PKEY_RSA: + rsa_cdata = self._lib.EVP_PKEY_get1_RSA(evp_pkey) + assert rsa_cdata != self._ffi.NULL + rsa_cdata = self._ffi.gc(rsa_cdata, self._lib.RSA_free) + return _RSAPublicKey(self, rsa_cdata) + elif type == self._lib.EVP_PKEY_DSA: + dsa_cdata = self._lib.EVP_PKEY_get1_DSA(evp_pkey) + assert dsa_cdata != self._ffi.NULL + dsa_cdata = self._ffi.gc(dsa_cdata, self._lib.DSA_free) + return _DSAPublicKey(self, dsa_cdata) + else: + raise UnsupportedAlgorithm("Unsupported key type.") + def _pem_password_cb(self, password): """ Generate a pem_password_cb function pointer that copied the password to @@ -787,6 +808,14 @@ class Backend(object): password, ) + 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, + ) + def load_traditional_openssl_pem_private_key(self, data, password): warnings.warn( "load_traditional_openssl_pem_private_key is deprecated and will " diff --git a/cryptography/hazmat/primitives/serialization.py b/cryptography/hazmat/primitives/serialization.py index cf1ca8ec..0fb560e0 100644 --- a/cryptography/hazmat/primitives/serialization.py +++ b/cryptography/hazmat/primitives/serialization.py @@ -44,3 +44,7 @@ def load_pem_pkcs8_private_key(data, password, backend): def load_pem_private_key(data, password, backend): return backend.load_pem_private_key(data, password) + + +def load_pem_public_key(data, backend): + return backend.load_pem_public_key(data) diff --git a/docs/hazmat/primitives/asymmetric/serialization.rst b/docs/hazmat/primitives/asymmetric/serialization.rst index 7a953d9b..18b89c44 100644 --- a/docs/hazmat/primitives/asymmetric/serialization.rst +++ b/docs/hazmat/primitives/asymmetric/serialization.rst @@ -92,6 +92,27 @@ all begin with ``-----BEGIN {format}-----`` and end with ``-----END is not supported by the backend or if the key is encrypted with a symmetric cipher that is not supported by the backend. +.. function:: load_pem_public_key(data, backend): + + .. versionadded:: 0.6 + + Deserialize a public key from PEM encoded data to one of the supported + asymmetric public key types. + + :param bytes data: The PEM encoded key data. + + :param backend: A + :class:`~cryptography.hazmat.backends.interfaces.PEMSerializationBackend` + provider. + + :returns: A new instance of a public key. + + :raises ValueError: If the PEM data could not be decrypted or if its + structure could not be decoded successfully. + + :raises UnsupportedAlgorithm: If the serialized key is of a type that + is not supported by the backend. + PKCS #8 Format ~~~~~~~~~~~~~~ diff --git a/tests/hazmat/primitives/test_serialization.py b/tests/hazmat/primitives/test_serialization.py index d369e8f4..4bc7e811 100644 --- a/tests/hazmat/primitives/test_serialization.py +++ b/tests/hazmat/primitives/test_serialization.py @@ -24,6 +24,7 @@ from cryptography.hazmat.primitives import interfaces from cryptography.hazmat.primitives.asymmetric import ec from cryptography.hazmat.primitives.serialization import ( load_pem_pkcs8_private_key, load_pem_private_key, + load_pem_public_key, load_pem_traditional_openssl_private_key ) @@ -38,7 +39,7 @@ class TestPEMSerialization(object): def test_load_pem_rsa_private_key(self, backend): key = load_vectors_from_file( os.path.join( - "asymmetric", "Traditional_OpenSSL_Serialization", "key1.pem"), + "asymmetric", "PEM_Serialization", "rsa_private_key.pem"), lambda pemfile: load_pem_private_key( pemfile.read().encode(), b"123456", backend ) @@ -49,6 +50,17 @@ class TestPEMSerialization(object): if isinstance(key, interfaces.RSAPrivateKeyWithNumbers): _check_rsa_private_numbers(key.private_numbers()) + def test_load_dsa_private_key(self, backend): + key = load_vectors_from_file( + os.path.join( + "asymmetric", "PEM_Serialization", "dsa_private_key.pem"), + lambda pemfile: load_pem_private_key( + pemfile.read().encode(), b"123456", backend + ) + ) + assert key + assert isinstance(key, interfaces.DSAPrivateKey) + @pytest.mark.parametrize( ("key_file", "password"), [ @@ -70,6 +82,28 @@ class TestPEMSerialization(object): assert key assert isinstance(key, interfaces.EllipticCurvePrivateKey) + def test_load_pem_rsa_public_key(self, backend): + key = load_vectors_from_file( + os.path.join( + "asymmetric", "PEM_Serialization", "rsa_public_key.pem"), + lambda pemfile: load_pem_public_key( + pemfile.read().encode(), backend + ) + ) + assert key + assert isinstance(key, interfaces.RSAPublicKey) + + def test_load_pem_dsa_public_key(self, backend): + key = load_vectors_from_file( + os.path.join( + "asymmetric", "PEM_Serialization", "dsa_public_key.pem"), + lambda pemfile: load_pem_public_key( + pemfile.read().encode(), backend + ) + ) + assert key + assert isinstance(key, interfaces.DSAPublicKey) + @pytest.mark.traditional_openssl_serialization class TestTraditionalOpenSSLSerialization(object): diff --git a/vectors/cryptography_vectors/asymmetric/PEM_Serialization/README.txt b/vectors/cryptography_vectors/asymmetric/PEM_Serialization/README.txt index 6963d2b5..865fbc9e 100644 --- a/vectors/cryptography_vectors/asymmetric/PEM_Serialization/README.txt +++ b/vectors/cryptography_vectors/asymmetric/PEM_Serialization/README.txt @@ -3,13 +3,13 @@ Example test files for PEM Serialization Backend tests Contains 1. ec_private_key.pem - Contains an Elliptic Curve key generated using OpenSSL, from the curve secp256r1. -2. ec_private_key_encrypted.pem - Contains the same Elliptic Curve key as ec_private_key.pem, except that +2. ec_private_key_encrypted.pem - Contains the same Elliptic Curve key as ec_private_key.pem, except that it is encrypted with AES-256 with the password "123456". 3. ec_public_key.pem - Contains the public key corresponding to ec_private_key.pem, generated using OpenSSL. 4. rsa_private_key.pem - Contains an RSA 2048 bit key generated using OpenSSL, protected by the secret "123456" with DES3 encryption. 5. rsa_public_key.pem - Contains an RSA 2048 bit public generated using OpenSSL from rsa_private_key.pem. 6. dsaparam.pem - Contains 2048-bit DSA parameters generated using OpenSSL; contains no keys. -7. dsa_private_key.pem - Contains a DSA 2048 bit key generated using OpenSSL from the parameters in +7. dsa_private_key.pem - Contains a DSA 2048 bit key generated using OpenSSL from the parameters in dsaparam.pem, protected by the secret "123456" with DES3 encryption. 8. dsa_public_key.pem - Contains a DSA 2048 bit key generated using OpenSSL from dsa_private_key.pem.
\ No newline at end of file |