summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Kehrer <paul.l.kehrer@gmail.com>2016-07-29 15:31:04 +0800
committerCory Benfield <lukasaoz@gmail.com>2016-07-29 08:31:04 +0100
commit72d968b2dea6937c2e2b1fe62bf404b667a98a80 (patch)
treec6b5209a5036d8eb55d6c7db9996147ab1cbef0e
parentdb8ec13b1ebec5ec3d5f2ff185adf0c961841163 (diff)
downloadpyopenssl-72d968b2dea6937c2e2b1fe62bf404b667a98a80.tar.gz
Convert between pyOpenSSL and cryptography objects (#439)
* convert pkey to cryptography keys and vice versa * pep8 and such * Add documentation and changelog * add a type check and verify that it rejects ECDSA keys from cryptography
-rw-r--r--CHANGELOG.rst2
-rw-r--r--src/OpenSSL/crypto.py42
-rw-r--r--tests/test_crypto.py75
3 files changed, 119 insertions, 0 deletions
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index fc81dec..9f867c4 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -27,6 +27,8 @@ Changes:
`#496 <https://github.com/pyca/pyopenssl/pull/496>`_
- Enable use of CRL (and more) in verify context.
`#483 <https://github.com/pyca/pyopenssl/pull/483>`_
+- ``OpenSSL.crypto.PKey`` can now be constructed from ``cryptography`` objects and also exported as such.
+ `#439 <https://github.com/pyca/pyopenssl/pull/439>`_
----
diff --git a/src/OpenSSL/crypto.py b/src/OpenSSL/crypto.py
index 4a379e8..c37f20b 100644
--- a/src/OpenSSL/crypto.py
+++ b/src/OpenSSL/crypto.py
@@ -10,6 +10,9 @@ from six import (
text_type as _text_type,
PY3 as _PY3)
+from cryptography.hazmat.backends.openssl.backend import backend
+from cryptography.hazmat.primitives.asymmetric import dsa, rsa
+
from OpenSSL._util import (
ffi as _ffi,
lib as _lib,
@@ -167,6 +170,45 @@ class PKey(object):
self._pkey = _ffi.gc(pkey, _lib.EVP_PKEY_free)
self._initialized = False
+ def to_cryptography_key(self):
+ """
+ Export as a ``cryptography`` key.
+
+ :rtype: One of ``cryptography``'s `key interfaces`_.
+
+ .. _key interfaces: https://cryptography.io/en/latest/hazmat/\
+ primitives/asymmetric/rsa/#key-interfaces
+
+ .. versionadded:: 16.1.0
+ """
+ if self._only_public:
+ return backend._evp_pkey_to_public_key(self._pkey)
+ else:
+ return backend._evp_pkey_to_private_key(self._pkey)
+
+ @classmethod
+ def from_cryptography_key(cls, crypto_key):
+ """
+ Construct based on a ``cryptography`` *crypto_key*.
+
+ :param crypto_key: A ``cryptography`` key.
+ :type crypto_key: One of ``cryptography``'s `key interfaces`_.
+
+ :rtype: PKey
+
+ .. versionadded:: 16.1.0
+ """
+ pkey = cls()
+ if not isinstance(crypto_key, (rsa.RSAPublicKey, rsa.RSAPrivateKey,
+ dsa.DSAPublicKey, dsa.DSAPrivateKey)):
+ raise TypeError("Unsupported key type")
+
+ pkey._pkey = crypto_key._evp_pkey
+ if isinstance(crypto_key, (rsa.RSAPublicKey, dsa.DSAPublicKey)):
+ pkey._only_public = True
+ pkey._initialized = True
+ return pkey
+
def generate_key(self, type, bits):
"""
Generate a key pair of the given type, with the given number of bits.
diff --git a/tests/test_crypto.py b/tests/test_crypto.py
index 81e0ae3..0f1c867 100644
--- a/tests/test_crypto.py
+++ b/tests/test_crypto.py
@@ -18,6 +18,10 @@ import pytest
from six import u, b, binary_type
+from cryptography.hazmat.backends.openssl.backend import backend
+from cryptography.hazmat.primitives import serialization
+from cryptography.hazmat.primitives.asymmetric import rsa
+
from OpenSSL.crypto import TYPE_RSA, TYPE_DSA, Error, PKey, PKeyType
from OpenSSL.crypto import X509, X509Type, X509Name, X509NameType
from OpenSSL.crypto import (
@@ -517,6 +521,13 @@ AYU/QVM4wGt8XGT2KwDFJaxYGKsGDMWmXY04dS+WPuetCbouWUusyFwRb9SzFave
vYeU7Ab/
-----END RSA PRIVATE KEY-----""")
+ec_private_key_pem = b"""-----BEGIN PRIVATE KEY-----
+MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgYirTZSx+5O8Y6tlG
+cka6W6btJiocdrdolfcukSoTEk+hRANCAAQkvPNu7Pa1GcsWU4v7ptNfqCJVq8Cx
+zo0MUVPQgwJ3aJtNM1QMOQUayCrRwfklg+D/rFSUwEUqtZh7fJDiFqz3
+-----END PRIVATE KEY-----
+"""
+
class X509ExtTests(TestCase):
"""
@@ -749,6 +760,70 @@ class X509ExtTests(TestCase):
issuer=badObj)
+class TestPKey(object):
+ """
+ py.test-based tests for :class:`OpenSSL.crypto.PKey`.
+
+ If possible, add new tests here.
+ """
+
+ def test_convert_from_cryptography_private_key(self):
+ """
+ PKey.from_cryptography_key creates a proper private PKey.
+ """
+ key = serialization.load_pem_private_key(
+ intermediate_key_pem, None, backend
+ )
+ pkey = PKey.from_cryptography_key(key)
+
+ assert isinstance(pkey, PKey)
+ assert pkey.bits() == key.key_size
+ assert pkey._only_public is False
+ assert pkey._initialized is True
+
+ def test_convert_from_cryptography_public_key(self):
+ """
+ PKey.from_cryptography_key creates a proper public PKey.
+ """
+ key = serialization.load_pem_public_key(cleartextPublicKeyPEM, backend)
+ pkey = PKey.from_cryptography_key(key)
+
+ assert isinstance(pkey, PKey)
+ assert pkey.bits() == key.key_size
+ assert pkey._only_public is True
+ assert pkey._initialized is True
+
+ def test_convert_from_cryptography_unsupported_type(self):
+ """
+ PKey.from_cryptography_key raises TypeError with an unsupported type.
+ """
+ key = serialization.load_pem_private_key(
+ ec_private_key_pem, None, backend
+ )
+ with pytest.raises(TypeError):
+ PKey.from_cryptography_key(key)
+
+ def test_convert_public_pkey_to_cryptography_key(self):
+ """
+ PKey.to_cryptography_key creates a proper cryptography public key.
+ """
+ pkey = load_publickey(FILETYPE_PEM, cleartextPublicKeyPEM)
+ key = pkey.to_cryptography_key()
+
+ assert isinstance(key, rsa.RSAPublicKey)
+ assert pkey.bits() == key.key_size
+
+ def test_convert_private_pkey_to_cryptography_key(self):
+ """
+ PKey.to_cryptography_key creates a proper cryptography private key.
+ """
+ pkey = load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM)
+ key = pkey.to_cryptography_key()
+
+ assert isinstance(key, rsa.RSAPrivateKey)
+ assert pkey.bits() == key.key_size
+
+
class PKeyTests(TestCase):
"""
Unit tests for :py:class:`OpenSSL.crypto.PKey`.