diff options
Diffstat (limited to 'src')
4 files changed, 117 insertions, 5 deletions
diff --git a/src/cryptography/hazmat/backends/openssl/rsa.py b/src/cryptography/hazmat/backends/openssl/rsa.py index 00ddcda3..e7365c11 100644 --- a/src/cryptography/hazmat/backends/openssl/rsa.py +++ b/src/cryptography/hazmat/backends/openssl/rsa.py @@ -17,8 +17,13 @@ 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, + RSAPublicKeyWithNumbers +) +from cryptography.hazmat.primitives.serialization import ( + BestAvailableEncryption, Encoding, Format, KeySerializationEncryption, + NoEncryption ) @@ -507,6 +512,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,6 +565,62 @@ class _RSAPrivateKey(object): ) ) + def private_bytes(self, encoding, format, encryption_algorithm): + if not isinstance(encoding, Encoding): + raise TypeError("encoding must be an item from the Encoding enum") + + if not isinstance(format, Format): + raise TypeError("format must be an item from the Format enum") + + # This is a temporary check until we land DER serialization. + if encoding is not Encoding.PEM: + raise ValueError("Only PEM encoding is supported by this backend") + + if format is Format.PKCS8: + write_bio = self._backend._lib.PEM_write_bio_PKCS8PrivateKey + key = self._evp_pkey + elif format is Format.TraditionalOpenSSL: + write_bio = self._backend._lib.PEM_write_bio_RSAPrivateKey + key = self._rsa_cdata + + if not isinstance(encryption_algorithm, KeySerializationEncryption): + raise TypeError( + "Encryption algorithm must be a KeySerializationEncryption " + "instance" + ) + + if isinstance(encryption_algorithm, NoEncryption): + password = b"" + passlen = 0 + evp_cipher = self._backend._ffi.NULL + elif isinstance(encryption_algorithm, BestAvailableEncryption): + # This is a curated value that we will update over time. + evp_cipher = self._backend._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._backend._create_mem_bio() + res = write_bio( + bio, + key, + evp_cipher, + password, + passlen, + self._backend._ffi.NULL, + self._backend._ffi.NULL + ) + assert res == 1 + return self._backend._read_mem_bio(bio) + @utils.register_interface(RSAPublicKeyWithNumbers) class _RSAPublicKey(object): diff --git a/src/cryptography/hazmat/primitives/asymmetric/rsa.py b/src/cryptography/hazmat/primitives/asymmetric/rsa.py index 332ad2c3..4963d85c 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): diff --git a/src/cryptography/hazmat/primitives/interfaces/__init__.py b/src/cryptography/hazmat/primitives/interfaces/__init__.py index 6b4241bd..f9ffae06 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 ) diff --git a/src/cryptography/hazmat/primitives/serialization.py b/src/cryptography/hazmat/primitives/serialization.py index 0f9506e1..0d564221 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,32 @@ else: data = data[4:] return result + + +class Encoding(Enum): + PEM = "PEM" + DER = "DER" + + +class Format(Enum): + PKCS8 = "PKCS8" + TraditionalOpenSSL = "TraditionalOpenSSL" + + +@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 |