aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorAlex Gaynor <alex.gaynor@gmail.com>2015-02-23 14:44:22 -0800
committerAlex Gaynor <alex.gaynor@gmail.com>2015-02-23 14:44:22 -0800
commit15c7f6fea2315210036265ad95513d42e10dface (patch)
treea161a0c9b27db6eec0042b83a3742422f8ce6c51 /src
parentca324456f050f3fa7346ca6032ec5b855d7fef8d (diff)
parentd8a1d0e624d44ef349cf51fda8559653a26fd460 (diff)
downloadcryptography-15c7f6fea2315210036265ad95513d42e10dface.tar.gz
cryptography-15c7f6fea2315210036265ad95513d42e10dface.tar.bz2
cryptography-15c7f6fea2315210036265ad95513d42e10dface.zip
Merge pull request #1610 from reaperhulk/load-der-keys
DER key loading support for the OpenSSL backend
Diffstat (limited to 'src')
-rw-r--r--src/cryptography/hazmat/backends/openssl/backend.py77
-rw-r--r--src/cryptography/hazmat/bindings/openssl/x509.py1
-rw-r--r--src/cryptography/hazmat/primitives/serialization.py8
3 files changed, 83 insertions, 3 deletions
diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py
index 8441e891..eb23ef00 100644
--- a/src/cryptography/hazmat/backends/openssl/backend.py
+++ b/src/cryptography/hazmat/backends/openssl/backend.py
@@ -15,9 +15,9 @@ from cryptography.exceptions import (
InternalError, UnsupportedAlgorithm, _Reasons
)
from cryptography.hazmat.backends.interfaces import (
- CMACBackend, CipherBackend, DSABackend, EllipticCurveBackend, HMACBackend,
- HashBackend, PBKDF2HMACBackend, PEMSerializationBackend, RSABackend,
- X509Backend
+ CMACBackend, CipherBackend, DERSerializationBackend, DSABackend,
+ EllipticCurveBackend, HMACBackend, HashBackend, PBKDF2HMACBackend,
+ PEMSerializationBackend, RSABackend, X509Backend
)
from cryptography.hazmat.backends.openssl.ciphers import (
_AESCTRCipherContext, _CipherContext
@@ -56,6 +56,7 @@ _OpenSSLError = collections.namedtuple("_OpenSSLError",
@utils.register_interface(CipherBackend)
@utils.register_interface(CMACBackend)
+@utils.register_interface(DERSerializationBackend)
@utils.register_interface(DSABackend)
@utils.register_interface(EllipticCurveBackend)
@utils.register_interface(HashBackend)
@@ -696,6 +697,76 @@ class Backend(object):
None,
)
+ def load_der_private_key(self, data, password):
+ # OpenSSL has a function called d2i_AutoPrivateKey that can simplify
+ # this. Unfortunately it doesn't properly support PKCS8 on OpenSSL
+ # 0.9.8 so we can't use it. Instead we sequentially try to load it 3
+ # different ways. First we'll try to load it as a traditional key
+ bio_data = self._bytes_to_bio(data)
+ key = self._evp_pkey_from_der_traditional_key(bio_data, password)
+ if not key:
+ # Okay so it's not a traditional key. Let's try
+ # PKCS8 unencrypted. OpenSSL 0.9.8 can't load unencrypted
+ # PKCS8 keys using d2i_PKCS8PrivateKey_bio so we do this instead.
+ # Reset the memory BIO so we can read the data again.
+ res = self._lib.BIO_reset(bio_data.bio)
+ assert res == 1
+ key = self._evp_pkey_from_der_unencrypted_pkcs8(bio_data, password)
+
+ if key:
+ return self._evp_pkey_to_private_key(key)
+ else:
+ # Finally we try to load it with the method that handles encrypted
+ # PKCS8 properly.
+ return self._load_key(
+ self._lib.d2i_PKCS8PrivateKey_bio,
+ self._evp_pkey_to_private_key,
+ data,
+ password,
+ )
+
+ def _evp_pkey_from_der_traditional_key(self, bio_data, password):
+ key = self._lib.d2i_PrivateKey_bio(bio_data.bio, self._ffi.NULL)
+ if key != self._ffi.NULL:
+ key = self._ffi.gc(key, self._lib.EVP_PKEY_free)
+ if password is not None:
+ raise TypeError(
+ "Password was given but private key is not encrypted."
+ )
+
+ return key
+ else:
+ self._consume_errors()
+ return None
+
+ def _evp_pkey_from_der_unencrypted_pkcs8(self, bio_data, password):
+ info = self._lib.d2i_PKCS8_PRIV_KEY_INFO_bio(
+ bio_data.bio, self._ffi.NULL
+ )
+ info = self._ffi.gc(info, self._lib.PKCS8_PRIV_KEY_INFO_free)
+ if info != self._ffi.NULL:
+ key = self._lib.EVP_PKCS82PKEY(info)
+ assert key != self._ffi.NULL
+ key = self._ffi.gc(key, self._lib.EVP_PKEY_free)
+ if password is not None:
+ raise TypeError(
+ "Password was given but private key is not encrypted."
+ )
+ return key
+ else:
+ self._consume_errors()
+ return None
+
+ def load_der_public_key(self, data):
+ mem_bio = self._bytes_to_bio(data)
+ evp_pkey = self._lib.d2i_PUBKEY_bio(mem_bio.bio, self._ffi.NULL)
+ if evp_pkey == self._ffi.NULL:
+ self._consume_errors()
+ raise ValueError("Could not unserialize key data.")
+
+ evp_pkey = self._ffi.gc(evp_pkey, self._lib.EVP_PKEY_free)
+ return self._evp_pkey_to_public_key(evp_pkey)
+
def load_pem_x509_certificate(self, data):
mem_bio = self._bytes_to_bio(data)
x509 = self._lib.PEM_read_bio_X509(
diff --git a/src/cryptography/hazmat/bindings/openssl/x509.py b/src/cryptography/hazmat/bindings/openssl/x509.py
index bf689e33..f5638da7 100644
--- a/src/cryptography/hazmat/bindings/openssl/x509.py
+++ b/src/cryptography/hazmat/bindings/openssl/x509.py
@@ -230,6 +230,7 @@ int i2d_DSAPrivateKey_bio(BIO *, DSA *);
PKCS8_PRIV_KEY_INFO *d2i_PKCS8_PRIV_KEY_INFO_bio(BIO *,
PKCS8_PRIV_KEY_INFO **);
+void PKCS8_PRIV_KEY_INFO_free(PKCS8_PRIV_KEY_INFO *);
"""
MACROS = """
diff --git a/src/cryptography/hazmat/primitives/serialization.py b/src/cryptography/hazmat/primitives/serialization.py
index dad419fe..0f9506e1 100644
--- a/src/cryptography/hazmat/primitives/serialization.py
+++ b/src/cryptography/hazmat/primitives/serialization.py
@@ -21,6 +21,14 @@ def load_pem_public_key(data, backend):
return backend.load_pem_public_key(data)
+def load_der_private_key(data, password, backend):
+ return backend.load_der_private_key(data, password)
+
+
+def load_der_public_key(data, backend):
+ return backend.load_der_public_key(data)
+
+
def load_ssh_public_key(data, backend):
key_parts = data.split(b' ')