new branch is changes lp:~rick-fdd/pyopenssl/pkcs12_mod_and_export applied to trunk
diff --git a/test/test_crypto.py b/test/test_crypto.py
index aa22cd0..0a283e6 100644
--- a/test/test_crypto.py
+++ b/test/test_crypto.py
@@ -21,6 +21,8 @@
 from OpenSSL.crypto import NetscapeSPKI, NetscapeSPKIType
 from OpenSSL.test.util import TestCase
 
+from OpenSSL.crypto import load_pkcs12, PKCS12
+from subprocess import Popen, PIPE
 
 cleartextCertificatePEM = """-----BEGIN CERTIFICATE-----
 MIIC7TCCAlagAwIBAgIIPQzE4MbeufQwDQYJKoZIhvcNAQEFBQAwWDELMAkGA1UE
@@ -709,6 +711,129 @@
         cert = load_certificate(FILETYPE_PEM, self.pemData)
         self.assertEqual(cert.get_notBefore(), "20090325123658Z")
 
+class PKCS12Tests(TestCase):
+    """
+    Tests functions in the L{OpenSSL.crypto.PKCS12} module.
+    """
+    pemData = cleartextCertificatePEM + cleartextPrivateKeyPEM
+
+    def test_construction(self):
+        p12 = PKCS12() 
+        self.assertEqual(None, p12.get_certificate())
+        self.assertEqual(None, p12.get_privatekey())
+        self.assertEqual(None, p12.get_ca_certificates())
+
+    def test_type_errors(self):
+        p12 = PKCS12() 
+        self.assertRaises(TypeError, p12.set_certificate, 3)
+        self.assertRaises(TypeError, p12.set_privatekey, 3)
+        self.assertRaises(TypeError, p12.set_ca_certificates, 3)
+        self.assertRaises(TypeError, p12.set_ca_certificates, X509())
+        self.assertRaises(TypeError, p12.set_ca_certificates, (3, 4))
+
+    def test_key_only(self):
+        """
+        L{OpenSSL.crypto.PKCS12.export} and load a PKCS without a key
+        """
+        passwd = 'blah'
+        p12 = PKCS12()
+        pkey = load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM) 
+        p12.set_privatekey( pkey )
+        self.assertEqual(None, p12.get_certificate())
+        self.assertEqual(pkey, p12.get_privatekey())
+        dumped_p12 = p12.export(passphrase=passwd, iter=2, maciter=3)
+        p12 = load_pkcs12(dumped_p12, passwd)
+        self.assertEqual(None, p12.get_ca_certificates())
+        self.assertEqual(None, p12.get_certificate())
+        #  It's actually in the pkcs12, but we silently don't find it (a key without a cert)
+        #self.assertEqual(cleartextPrivateKeyPEM, dump_privatekey(FILETYPE_PEM, p12.get_privatekey()))
+
+    def test_cert_only(self):
+        """
+        L{OpenSSL.crypto.PKCS12.export} and load a PKCS without a key.
+        Strangely, OpenSSL converts it to a CA cert.
+        """
+        passwd = 'blah'
+        p12 = PKCS12()
+        cert = load_certificate(FILETYPE_PEM, cleartextCertificatePEM) 
+        p12.set_certificate( cert )
+        self.assertEqual(cert, p12.get_certificate())
+        self.assertEqual(None, p12.get_privatekey())
+        dumped_p12 = p12.export(passphrase=passwd, iter=2, maciter=3)
+        p12 = load_pkcs12(dumped_p12, passwd)
+        self.assertEqual(None, p12.get_privatekey())
+        self.assertEqual(None, p12.get_certificate())
+        self.assertEqual(cleartextCertificatePEM, dump_certificate(FILETYPE_PEM, p12.get_ca_certificates()[0]))
+
+
+    def test_export_and_load(self):
+        """
+        L{OpenSSL.crypto.PKCS12.export} and others
+        """
+        # use openssl program to create a p12 then load it
+        from OpenSSL.test.test_ssl import client_cert_pem, client_key_pem, server_cert_pem, server_key_pem, root_cert_pem
+        passwd = 'whatever'
+        pem = client_key_pem + client_cert_pem
+        p12_str = Popen(["openssl", "pkcs12", '-export', '-clcerts', '-passout', 'pass:'+passwd], \
+           stdin=PIPE, stdout=PIPE).communicate(input=str(pem))[0]
+        p12 = load_pkcs12(p12_str, passwd)
+        # verify p12 using pkcs12 get_* functions
+        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())
+        # dump cert and verify it using the openssl program
+        dumped_p12 = p12.export(passphrase=passwd, iter=2, maciter=0, friendly_name='blueberry')
+        recovered_key = Popen(["openssl", "pkcs12", '-nocerts', '-nodes', '-passin', 'pass:'+passwd ], \
+           stdin=PIPE, stdout=PIPE).communicate(input=str(dumped_p12))[0]
+        self.assertEqual(recovered_key[-len(client_key_pem):], client_key_pem)
+        recovered_cert = Popen(["openssl", "pkcs12", '-clcerts', '-nodes', '-passin', 'pass:'+passwd, '-nokeys' ], \
+           stdin=PIPE, stdout=PIPE).communicate(input=str(dumped_p12))[0]
+        self.assertEqual(recovered_cert[-len(client_cert_pem):], client_cert_pem)
+        # change the cert and key
+        p12.set_certificate(load_certificate(FILETYPE_PEM, server_cert_pem))
+        p12.set_privatekey(load_privatekey(FILETYPE_PEM, server_key_pem))
+        root_cert = load_certificate(FILETYPE_PEM, root_cert_pem) 
+        p12.set_ca_certificates( [ root_cert ] )
+        p12.set_ca_certificates( ( root_cert, ) )
+        self.assertEqual(1, len(p12.get_ca_certificates()))
+        self.assertEqual(root_cert, p12.get_ca_certificates()[0])
+        # recover changed cert and key using the openssl program
+        dumped_p12 = p12.export(passphrase=passwd, iter=2, maciter=0, friendly_name='Serverlicious')
+        recovered_key = Popen(["openssl", "pkcs12", '-nocerts', '-nodes', '-passin', 'pass:'+passwd ], \
+           stdin=PIPE, stdout=PIPE).communicate(input=str(dumped_p12))[0]
+        self.assertEqual(recovered_key[-len(server_key_pem):], server_key_pem)
+        recovered_cert = Popen(["openssl", "pkcs12", '-clcerts', '-nodes', '-passin', 'pass:'+passwd, '-nokeys' ], \
+           stdin=PIPE, stdout=PIPE).communicate(input=str(dumped_p12))[0]
+        self.assertEqual(recovered_cert[-len(server_cert_pem):], server_cert_pem)
+        recovered_cert = Popen(["openssl", "pkcs12", '-cacerts', '-nodes', '-passin', 'pass:'+passwd, '-nokeys' ], \
+           stdin=PIPE, stdout=PIPE).communicate(input=str(dumped_p12))[0]
+        self.assertEqual(recovered_cert[-len(root_cert_pem):], root_cert_pem)
+        #  Test other forms of no password
+        passwd = ''
+        dumped_p12_empty = p12.export(passphrase=passwd, iter=2, maciter=0, friendly_name='Sewer')
+        dumped_p12_none  = p12.export(passphrase=None,   iter=2, maciter=0, friendly_name='Sewer')
+        dumped_p12_nopw  = p12.export(                   iter=2, maciter=0, friendly_name='Sewer')
+        recovered_empty = Popen(["openssl", "pkcs12", '-nodes', '-passin', 'pass:'+passwd ], \
+           stdin=PIPE, stdout=PIPE).communicate(input=str(dumped_p12_empty))[0]
+        recovered_none  = Popen(["openssl", "pkcs12", '-nodes', '-passin', 'pass:'+passwd ], \
+           stdin=PIPE, stdout=PIPE).communicate(input=str(dumped_p12_none))[0]
+        recovered_nopw  = Popen(["openssl", "pkcs12", '-nodes', '-passin', 'pass:'+passwd ], \
+           stdin=PIPE, stdout=PIPE).communicate(input=str(dumped_p12_nopw))[0]
+        self.assertEqual(recovered_none, recovered_nopw)
+        self.assertEqual(recovered_none, recovered_empty)
+        #  Test removing CA certs
+        p12.set_ca_certificates( None )
+        self.assertEqual(None, p12.get_ca_certificates())
+        #  Test without MAC
+        dumped_p12 = p12.export(maciter=-1, passphrase=passwd, iter=2)
+        recovered_key = Popen(["openssl", "pkcs12", '-nocerts', '-nodes', '-passin', 'pass:'+passwd, '-nomacver' ], \
+           stdin=PIPE, stdout=PIPE).communicate(input=str(dumped_p12))[0]
+        self.assertEqual(recovered_key[-len(server_key_pem):], server_key_pem)
+        #  We can't load PKCS12 without MAC, because we use PCKS_parse()
+        #p12 = load_pkcs12(dumped_p12, passwd)
+
 
     def test_get_notAfter(self):
         """