From f83e25c81bb186ed8a96d4a569d5068546a24349 Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Sat, 21 Feb 2015 18:34:00 -0600 Subject: Support for traditional OpenSSL and PKCS8 RSA private key serialization --- src/cryptography/hazmat/backends/openssl/rsa.py | 61 +++++++++++++++++++++- .../hazmat/primitives/asymmetric/rsa.py | 12 ++++- .../hazmat/primitives/serialization.py | 47 +++++++++++++++++ src/cryptography/utils.py | 1 + 4 files changed, 118 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/cryptography/hazmat/backends/openssl/rsa.py b/src/cryptography/hazmat/backends/openssl/rsa.py index 00ddcda3..1357889f 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 ( + BestAvailable, Encoding, KeySerializationEncryption, NoEncryption, PKCS8, + TraditionalOpenSSL ) @@ -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,57 @@ class _RSAPrivateKey(object): ) ) + def dump(self, serializer, encryption_algorithm): + if isinstance(serializer, PKCS8): + write_bio = self._backend._lib.PEM_write_bio_PKCS8PrivateKey + key = self._evp_pkey + elif isinstance(serializer, TraditionalOpenSSL): + write_bio = self._backend._lib.PEM_write_bio_RSAPrivateKey + key = self._rsa_cdata + else: + raise TypeError("serializer must be PKCS8 or TraditionalOpenSSL") + + if serializer.encoding != Encoding.PEM: + raise ValueError("Only PEM encoding is supported by this backend") + + 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, BestAvailable): + # 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..e994a9cc 100644 --- a/src/cryptography/hazmat/primitives/asymmetric/rsa.py +++ b/src/cryptography/hazmat/primitives/asymmetric/rsa.py @@ -42,13 +42,23 @@ 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 dump(self, serializer, encryption_algorithm): + """ + Returns the PEM encoded key. + """ + + +# DeprecatedIn08 +RSAPrivateKeyWithNumbers = RSAPrivateKeyWithSerialization + @six.add_metaclass(abc.ABCMeta) class RSAPublicKey(object): diff --git a/src/cryptography/hazmat/primitives/serialization.py b/src/cryptography/hazmat/primitives/serialization.py index 0f9506e1..9bfbc6b7 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,47 @@ else: data = data[4:] return result + + +class Encoding(Enum): + PEM = "PEM" + DER = "DER" + + +class PKCS8(object): + def __init__(self, encoding): + if not isinstance(encoding, Encoding): + raise TypeError( + "Encoding must be an element from the Encoding enum" + ) + + self.encoding = encoding + + +class TraditionalOpenSSL(object): + def __init__(self, encoding): + if not isinstance(encoding, Encoding): + raise TypeError( + "Encoding must be an element from the Encoding enum" + ) + + self.encoding = encoding + + +@six.add_metaclass(abc.ABCMeta) +class KeySerializationEncryption(object): + pass + + +@utils.register_interface(KeySerializationEncryption) +class BestAvailable(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/utils.py b/src/cryptography/utils.py index 78dcc1ca..77b6d253 100644 --- a/src/cryptography/utils.py +++ b/src/cryptography/utils.py @@ -12,6 +12,7 @@ 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. +# DeprecatedIn08 objects also exist. DeprecatedIn08 = PendingDeprecationWarning -- cgit v1.2.3 From 199dc276cd1b45a799b511090b37237df49d68a3 Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Mon, 23 Feb 2015 20:45:21 -0600 Subject: address review comments --- src/cryptography/hazmat/backends/openssl/rsa.py | 27 +++++++++++++--------- .../hazmat/primitives/asymmetric/rsa.py | 15 ++++++++---- .../hazmat/primitives/interfaces/__init__.py | 5 ++-- .../hazmat/primitives/serialization.py | 23 ++++-------------- src/cryptography/utils.py | 1 - 5 files changed, 34 insertions(+), 37 deletions(-) (limited to 'src') diff --git a/src/cryptography/hazmat/backends/openssl/rsa.py b/src/cryptography/hazmat/backends/openssl/rsa.py index 1357889f..efc1a577 100644 --- a/src/cryptography/hazmat/backends/openssl/rsa.py +++ b/src/cryptography/hazmat/backends/openssl/rsa.py @@ -22,8 +22,8 @@ from cryptography.hazmat.primitives.asymmetric.rsa import ( RSAPublicKeyWithNumbers ) from cryptography.hazmat.primitives.serialization import ( - BestAvailable, Encoding, KeySerializationEncryption, NoEncryption, PKCS8, - TraditionalOpenSSL + BestAvailableEncryption, Encoding, Format, KeySerializationEncryption, + NoEncryption ) @@ -565,18 +565,23 @@ class _RSAPrivateKey(object): ) ) - def dump(self, serializer, encryption_algorithm): - if isinstance(serializer, PKCS8): + def dump(self, encoding, fmt, encryption_algorithm): + if not isinstance(encoding, Encoding): + raise TypeError("encoding must be an item from the Encoding enum") + + if not isinstance(fmt, Format): + raise TypeError("format must be an item from the Format enum") + + # This is a temporary check until we land DER serialization. + if encoding != Encoding.PEM: + raise ValueError("Only PEM encoding is supported by this backend") + + if fmt == Format.PKCS8: write_bio = self._backend._lib.PEM_write_bio_PKCS8PrivateKey key = self._evp_pkey - elif isinstance(serializer, TraditionalOpenSSL): + elif fmt == Format.TraditionalOpenSSL: write_bio = self._backend._lib.PEM_write_bio_RSAPrivateKey key = self._rsa_cdata - else: - raise TypeError("serializer must be PKCS8 or TraditionalOpenSSL") - - if serializer.encoding != Encoding.PEM: - raise ValueError("Only PEM encoding is supported by this backend") if not isinstance(encryption_algorithm, KeySerializationEncryption): raise TypeError( @@ -588,7 +593,7 @@ class _RSAPrivateKey(object): password = b"" passlen = 0 evp_cipher = self._backend._ffi.NULL - elif isinstance(encryption_algorithm, BestAvailable): + 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" diff --git a/src/cryptography/hazmat/primitives/asymmetric/rsa.py b/src/cryptography/hazmat/primitives/asymmetric/rsa.py index e994a9cc..918717f3 100644 --- a/src/cryptography/hazmat/primitives/asymmetric/rsa.py +++ b/src/cryptography/hazmat/primitives/asymmetric/rsa.py @@ -50,14 +50,21 @@ class RSAPrivateKeyWithSerialization(RSAPrivateKey): """ @abc.abstractmethod - def dump(self, serializer, encryption_algorithm): + def dump(self, encoding, fmt, encryption_algorithm): """ - Returns the PEM encoded key. + Returns the dumped key. """ -# DeprecatedIn08 -RSAPrivateKeyWithNumbers = RSAPrivateKeyWithSerialization +RSAPrivateKeyWithNumbers = utils.deprecated( + RSAPrivateKeyWithSerialization, + __name__, + ( + "The RSAPrivateKeyWithNumbers interface has been renamed to " + "RSAPrivateKeyWithSerialization" + ), + utils.DeprecatedIn08 +) @six.add_metaclass(abc.ABCMeta) 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 9bfbc6b7..0d564221 100644 --- a/src/cryptography/hazmat/primitives/serialization.py +++ b/src/cryptography/hazmat/primitives/serialization.py @@ -174,24 +174,9 @@ class Encoding(Enum): DER = "DER" -class PKCS8(object): - def __init__(self, encoding): - if not isinstance(encoding, Encoding): - raise TypeError( - "Encoding must be an element from the Encoding enum" - ) - - self.encoding = encoding - - -class TraditionalOpenSSL(object): - def __init__(self, encoding): - if not isinstance(encoding, Encoding): - raise TypeError( - "Encoding must be an element from the Encoding enum" - ) - - self.encoding = encoding +class Format(Enum): + PKCS8 = "PKCS8" + TraditionalOpenSSL = "TraditionalOpenSSL" @six.add_metaclass(abc.ABCMeta) @@ -200,7 +185,7 @@ class KeySerializationEncryption(object): @utils.register_interface(KeySerializationEncryption) -class BestAvailable(object): +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.") diff --git a/src/cryptography/utils.py b/src/cryptography/utils.py index 77b6d253..78dcc1ca 100644 --- a/src/cryptography/utils.py +++ b/src/cryptography/utils.py @@ -12,7 +12,6 @@ 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. -# DeprecatedIn08 objects also exist. DeprecatedIn08 = PendingDeprecationWarning -- cgit v1.2.3 From 8aad028501ef434071d3969bce41c4e6375b4c61 Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Mon, 23 Feb 2015 22:03:09 -0600 Subject: rename dump to as_bytes --- src/cryptography/hazmat/backends/openssl/rsa.py | 2 +- src/cryptography/hazmat/primitives/asymmetric/rsa.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/cryptography/hazmat/backends/openssl/rsa.py b/src/cryptography/hazmat/backends/openssl/rsa.py index efc1a577..0a1f106f 100644 --- a/src/cryptography/hazmat/backends/openssl/rsa.py +++ b/src/cryptography/hazmat/backends/openssl/rsa.py @@ -565,7 +565,7 @@ class _RSAPrivateKey(object): ) ) - def dump(self, encoding, fmt, encryption_algorithm): + def as_bytes(self, encoding, fmt, encryption_algorithm): if not isinstance(encoding, Encoding): raise TypeError("encoding must be an item from the Encoding enum") diff --git a/src/cryptography/hazmat/primitives/asymmetric/rsa.py b/src/cryptography/hazmat/primitives/asymmetric/rsa.py index 918717f3..160ac6aa 100644 --- a/src/cryptography/hazmat/primitives/asymmetric/rsa.py +++ b/src/cryptography/hazmat/primitives/asymmetric/rsa.py @@ -50,9 +50,9 @@ class RSAPrivateKeyWithSerialization(RSAPrivateKey): """ @abc.abstractmethod - def dump(self, encoding, fmt, encryption_algorithm): + def as_bytes(self, encoding, fmt, encryption_algorithm): """ - Returns the dumped key. + Returns the key serialized as bytes. """ -- cgit v1.2.3 From 6177cbe2fad1422899a2c26cb53abbbf97886485 Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Fri, 27 Feb 2015 17:05:52 -0600 Subject: address review feedback --- src/cryptography/hazmat/backends/openssl/rsa.py | 10 +++++----- src/cryptography/hazmat/primitives/asymmetric/rsa.py | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/cryptography/hazmat/backends/openssl/rsa.py b/src/cryptography/hazmat/backends/openssl/rsa.py index 0a1f106f..ce6f646c 100644 --- a/src/cryptography/hazmat/backends/openssl/rsa.py +++ b/src/cryptography/hazmat/backends/openssl/rsa.py @@ -565,21 +565,21 @@ class _RSAPrivateKey(object): ) ) - def as_bytes(self, encoding, fmt, encryption_algorithm): + def as_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(fmt, Format): + 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 != Encoding.PEM: + if encoding is not Encoding.PEM: raise ValueError("Only PEM encoding is supported by this backend") - if fmt == Format.PKCS8: + if format is Format.PKCS8: write_bio = self._backend._lib.PEM_write_bio_PKCS8PrivateKey key = self._evp_pkey - elif fmt == Format.TraditionalOpenSSL: + elif format is Format.TraditionalOpenSSL: write_bio = self._backend._lib.PEM_write_bio_RSAPrivateKey key = self._rsa_cdata diff --git a/src/cryptography/hazmat/primitives/asymmetric/rsa.py b/src/cryptography/hazmat/primitives/asymmetric/rsa.py index 160ac6aa..932868e1 100644 --- a/src/cryptography/hazmat/primitives/asymmetric/rsa.py +++ b/src/cryptography/hazmat/primitives/asymmetric/rsa.py @@ -50,7 +50,7 @@ class RSAPrivateKeyWithSerialization(RSAPrivateKey): """ @abc.abstractmethod - def as_bytes(self, encoding, fmt, encryption_algorithm): + def as_bytes(self, encoding, format, encryption_algorithm): """ Returns the key serialized as bytes. """ -- cgit v1.2.3 From 223a8f02a37a87b3c7366441647013cf9a18b061 Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Sat, 28 Feb 2015 18:54:10 -0600 Subject: change as_bytes to private_bytes, link more things --- src/cryptography/hazmat/backends/openssl/rsa.py | 2 +- src/cryptography/hazmat/primitives/asymmetric/rsa.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/cryptography/hazmat/backends/openssl/rsa.py b/src/cryptography/hazmat/backends/openssl/rsa.py index ce6f646c..e7365c11 100644 --- a/src/cryptography/hazmat/backends/openssl/rsa.py +++ b/src/cryptography/hazmat/backends/openssl/rsa.py @@ -565,7 +565,7 @@ class _RSAPrivateKey(object): ) ) - def as_bytes(self, encoding, format, encryption_algorithm): + def private_bytes(self, encoding, format, encryption_algorithm): if not isinstance(encoding, Encoding): raise TypeError("encoding must be an item from the Encoding enum") diff --git a/src/cryptography/hazmat/primitives/asymmetric/rsa.py b/src/cryptography/hazmat/primitives/asymmetric/rsa.py index 932868e1..4963d85c 100644 --- a/src/cryptography/hazmat/primitives/asymmetric/rsa.py +++ b/src/cryptography/hazmat/primitives/asymmetric/rsa.py @@ -50,7 +50,7 @@ class RSAPrivateKeyWithSerialization(RSAPrivateKey): """ @abc.abstractmethod - def as_bytes(self, encoding, format, encryption_algorithm): + def private_bytes(self, encoding, format, encryption_algorithm): """ Returns the key serialized as bytes. """ -- cgit v1.2.3