aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorAlex Stapleton <alexs@prol.etari.at>2015-03-01 16:02:23 +0000
committerAlex Stapleton <alexs@prol.etari.at>2015-03-01 16:02:23 +0000
commit6bb9624186f3546dd203e3a20e7785dad71c6256 (patch)
treece9d0ea0d2e821557aac3abf0f01966a125a801e /src
parente8f12f833f14c55b18919a55db52d24cc8880ae9 (diff)
parent223a8f02a37a87b3c7366441647013cf9a18b061 (diff)
downloadcryptography-6bb9624186f3546dd203e3a20e7785dad71c6256.tar.gz
cryptography-6bb9624186f3546dd203e3a20e7785dad71c6256.tar.bz2
cryptography-6bb9624186f3546dd203e3a20e7785dad71c6256.zip
Merge pull request #1503 from reaperhulk/serialize-some-keys
Support for traditional OpenSSL and PKCS8 RSA private key serialization
Diffstat (limited to 'src')
-rw-r--r--src/cryptography/hazmat/backends/openssl/rsa.py66
-rw-r--r--src/cryptography/hazmat/primitives/asymmetric/rsa.py19
-rw-r--r--src/cryptography/hazmat/primitives/interfaces/__init__.py5
-rw-r--r--src/cryptography/hazmat/primitives/serialization.py32
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