summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJean-Paul Calderone <exarkun@twistedmatrix.com>2014-05-05 12:58:28 -0400
committerJean-Paul Calderone <exarkun@twistedmatrix.com>2014-05-05 12:58:28 -0400
commit238fb74dc95828a8e9fb0369460f1096663f3b78 (patch)
treecfd18d1f98d46d828720890e80c2d055c6f86085
parentd4ff4e460f30d228407198ad9d0a38b7651735f3 (diff)
parentf0ff13bd2d1909a466c52f0ae7986b0b96835a78 (diff)
downloadpyopenssl-238fb74dc95828a8e9fb0369460f1096663f3b78.tar.gz
Merge pull request #108 from pyca/pkcs12-without-passphrase
Re-enable support for loading PKCS12 containers that have no passphrase or an empty passphrase and let this be the default when no passphrase is passed to `load_pkcs12`.
-rw-r--r--ChangeLog5
-rw-r--r--OpenSSL/crypto.py9
-rw-r--r--OpenSSL/test/test_crypto.py87
3 files changed, 92 insertions, 9 deletions
diff --git a/ChangeLog b/ChangeLog
index 6416e18..b73a144 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -12,6 +12,11 @@
* OpenSSL/SSL.py: ``Connection.send`` and ``Connection.sendall``
now also accept the ``buffer`` type as data.
+2014-04-05 Stephen Holsapple <sholsapp@gmail.com>
+
+ * OpenSSL/crypto.py: Make ``load_pkcs12`` backwards compatible with
+ pyOpenSSL 0.13 by making passphrase optional.
+
2014-03-30 Fedor Brunner <fedor.brunner@azet.sk>
* OpenSSL/SSL.py: Add ``get_finished``, ``get_peer_finished``
diff --git a/OpenSSL/crypto.py b/OpenSSL/crypto.py
index 03fe853..54569ea 100644
--- a/OpenSSL/crypto.py
+++ b/OpenSSL/crypto.py
@@ -2366,7 +2366,7 @@ def load_pkcs7_data(type, buffer):
-def load_pkcs12(buffer, passphrase):
+def load_pkcs12(buffer, passphrase=None):
"""
Load a PKCS12 object from a buffer
@@ -2379,6 +2379,13 @@ def load_pkcs12(buffer, passphrase):
bio = _new_mem_buf(buffer)
+ # Use null passphrase if passphrase is None or empty string. With PKCS#12
+ # password based encryption no password and a zero length password are two
+ # different things, but OpenSSL implementation will try both to figure out
+ # which one works.
+ if not passphrase:
+ passphrase = _ffi.NULL
+
p12 = _lib.d2i_PKCS12_bio(bio, _ffi.NULL)
if p12 == _ffi.NULL:
_raise_current_error()
diff --git a/OpenSSL/test/test_crypto.py b/OpenSSL/test/test_crypto.py
index 34e60a3..bbe5d05 100644
--- a/OpenSSL/test/test_crypto.py
+++ b/OpenSSL/test/test_crypto.py
@@ -1941,6 +1941,21 @@ class PKCS12Tests(TestCase):
self.assertEqual(recovered_cert[-len(ca):], ca)
+ def verify_pkcs12_container(self, p12):
+ """
+ Verify that the PKCS#12 container contains the correct client
+ certificate and private key.
+
+ :param p12: The PKCS12 instance to verify.
+ :type p12: :py:class:`PKCS12`
+ """
+ cert_pem = dump_certificate(FILETYPE_PEM, p12.get_certificate())
+ key_pem = dump_privatekey(FILETYPE_PEM, p12.get_privatekey())
+ self.assertEqual(
+ (client_cert_pem, client_key_pem, None),
+ (cert_pem, key_pem, p12.get_ca_certificates()))
+
+
def test_load_pkcs12(self):
"""
A PKCS12 string generated using the openssl command line can be loaded
@@ -1950,14 +1965,70 @@ class PKCS12Tests(TestCase):
pem = client_key_pem + client_cert_pem
p12_str = _runopenssl(
pem, b"pkcs12", b"-export", b"-clcerts", b"-passout", b"pass:" + passwd)
- p12 = load_pkcs12(p12_str, passwd)
- # verify
- self.assertTrue(isinstance(p12, PKCS12))
- cert_pem = dump_certificate(FILETYPE_PEM, p12.get_certificate())
- self.assertEqual(cert_pem, client_cert_pem)
- key_pem = dump_privatekey(FILETYPE_PEM, p12.get_privatekey())
- self.assertEqual(key_pem, client_key_pem)
- self.assertEqual(None, p12.get_ca_certificates())
+ p12 = load_pkcs12(p12_str, passphrase=passwd)
+ self.verify_pkcs12_container(p12)
+
+
+ def test_load_pkcs12_no_passphrase(self):
+ """
+ A PKCS12 string generated using openssl command line can be loaded with
+ :py:obj:`load_pkcs12` without a passphrase and its components extracted
+ and examined.
+ """
+ pem = client_key_pem + client_cert_pem
+ p12_str = _runopenssl(
+ pem, b"pkcs12", b"-export", b"-clcerts", b"-passout", b"pass:")
+ p12 = load_pkcs12(p12_str)
+ self.verify_pkcs12_container(p12)
+
+
+ def _dump_and_load(self, dump_passphrase, load_passphrase):
+ """
+ A helper method to dump and load a PKCS12 object.
+ """
+ p12 = self.gen_pkcs12(client_cert_pem, client_key_pem)
+ dumped_p12 = p12.export(passphrase=dump_passphrase, iter=2, maciter=3)
+ return load_pkcs12(dumped_p12, passphrase=load_passphrase)
+
+
+ def test_load_pkcs12_null_passphrase_load_empty(self):
+ """
+ A PKCS12 string can be dumped with a null passphrase, loaded with an
+ empty passphrase with :py:obj:`load_pkcs12`, and its components
+ extracted and examined.
+ """
+ self.verify_pkcs12_container(
+ self._dump_and_load(dump_passphrase=None, load_passphrase=b''))
+
+
+ def test_load_pkcs12_null_passphrase_load_null(self):
+ """
+ A PKCS12 string can be dumped with a null passphrase, loaded with a
+ null passphrase with :py:obj:`load_pkcs12`, and its components
+ extracted and examined.
+ """
+ self.verify_pkcs12_container(
+ self._dump_and_load(dump_passphrase=None, load_passphrase=None))
+
+
+ def test_load_pkcs12_empty_passphrase_load_empty(self):
+ """
+ A PKCS12 string can be dumped with an empty passphrase, loaded with an
+ empty passphrase with :py:obj:`load_pkcs12`, and its components
+ extracted and examined.
+ """
+ self.verify_pkcs12_container(
+ self._dump_and_load(dump_passphrase=b'', load_passphrase=b''))
+
+
+ def test_load_pkcs12_empty_passphrase_load_null(self):
+ """
+ A PKCS12 string can be dumped with an empty passphrase, loaded with a
+ null passphrase with :py:obj:`load_pkcs12`, and its components
+ extracted and examined.
+ """
+ self.verify_pkcs12_container(
+ self._dump_and_load(dump_passphrase=b'', load_passphrase=None))
def test_load_pkcs12_garbage(self):