Change the API for setting and getting friendlyNames of PKCS12
diff --git a/doc/pyOpenSSL.tex b/doc/pyOpenSSL.tex
index 5d5ca9d..19271ac 100644
--- a/doc/pyOpenSSL.tex
+++ b/doc/pyOpenSSL.tex
@@ -263,7 +263,9 @@
\begin{funcdesc}{load_pkcs12}{buffer\optional{, passphrase}}
Load pkcs12 data from the string \var{buffer}. If the pkcs12 structure is
encrypted, a \var{passphrase} must be included. The MAC is always
-checked and thus required.
+checked and thus required.
+
+See also the man page for the C function \function{PKCS12_parse}.
\end{funcdesc}
\subsubsection{X509 objects \label{openssl-x509}}
@@ -525,7 +527,7 @@
PKCS12 objects have the following methods:
-\begin{methoddesc}[PKCS12]{export}{\optional{passphrase=None}\optional{, friendly_name=None}\optional{, iter=2000}\optional{, maciter=0}}
+\begin{methoddesc}[PKCS12]{export}{\optional{passphrase=None}\optional{, iter=2048}\optional{, maciter=1}}
Returns a PKCS12 object as a string.
The optional \var{passphrase} must be a string not a callback.
@@ -542,6 +544,10 @@
Return certificate portion of the PKCS12 structure.
\end{methoddesc}
+\begin{methoddesc}[PKCS12]{get_friendlyname}{}
+Return friendlyName portion of the PKCS12 structure.
+\end{methoddesc}
+
\begin{methoddesc}[PKCS12]{get_privatekey}{}
Return private key portion of the PKCS12 structure
\end{methoddesc}
@@ -556,6 +562,10 @@
Replace or set the certificate portion of the PKCS12 structure.
\end{methoddesc}
+\begin{methoddesc}[PKCS12]{set_friendlyname}{name}
+Replace or set the friendlyName portion of the PKCS12 structure.
+\end{methoddesc}
+
\begin{methoddesc}[PKCS12]{set_privatekey}{pkey}
Replace or set private key portion of the PKCS12 structure
\end{methoddesc}
diff --git a/src/crypto/pkcs12.c b/src/crypto/pkcs12.c
index 963d29c..7a6a95c 100644
--- a/src/crypto/pkcs12.c
+++ b/src/crypto/pkcs12.c
@@ -176,14 +176,57 @@
return Py_None;
}
+static char crypto_PKCS12_get_friendlyname_doc[] = "\n\
+Return friendly name portion of the PKCS12 structure\n\
+\n\
+@returns: String containing the friendlyname\n\
+";
+static crypto_PKeyObj *
+crypto_PKCS12_get_friendlyname(crypto_PKCS12Obj *self, PyObject *args)
+{
+ if (!PyArg_ParseTuple(args, ":get_friendlyname"))
+ return NULL;
+
+ Py_INCREF(self->friendlyname);
+ return (crypto_PKeyObj *) self->friendlyname;
+}
+
+static char crypto_PKCS12_set_friendlyname_doc[] = "\n\
+Replace or set the certificate portion of the PKCS12 structure\n\
+\n\
+@param name: The new friendly name.\n\
+@type name: L{str}\n\
+@return: None\n\
+";
+static PyObject *
+crypto_PKCS12_set_friendlyname(crypto_PKCS12Obj *self, PyObject *args, PyObject *keywds)
+{
+ PyObject *name = NULL;
+ static char *kwlist[] = {"name", NULL};
+
+ if (!PyArg_ParseTupleAndKeywords(args, keywds, "O:set_friendlyname",
+ kwlist, &name))
+ return NULL;
+
+ if (name != Py_None && ! PyString_Check(name)) {
+ PyErr_SetString(PyExc_TypeError, "name must be a string or None");
+ return NULL;
+ }
+
+ Py_INCREF(name); /* Make consistent before calling Py_DECREF() */
+ Py_DECREF(self->friendlyname);
+ self->friendlyname = name;
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
static char crypto_PKCS12_export_doc[] = "\n\
export([passphrase=None][, friendly_name=None][, iter=2048][, maciter=1]\n\
Dump a PKCS12 object as a string. See also \"man PKCS12_create\".\n\
\n\
@param passphrase: used to encrypt the PKCS12\n\
@type passphrase: L{str}\n\
-@param friendly_name: A descriptive comment\n\
-@type friendly_name: L{str}\n\
@param iter: How many times to repeat the encryption\n\
@type iter: L{int}\n\
@param maciter: How many times to repeat the MAC\n\
@@ -203,10 +246,10 @@
X509 *x509 = NULL;
int iter = 0; /* defaults to PKCS12_DEFAULT_ITER */
int maciter = 0;
- static char *kwlist[] = {"passphrase", "friendly_name", "iter", "maciter", NULL};
+ static char *kwlist[] = {"passphrase", "iter", "maciter", NULL};
- if (!PyArg_ParseTupleAndKeywords(args, keywds, "|zzii:export",
- kwlist, &passphrase, &friendly_name, &iter, &maciter))
+ if (!PyArg_ParseTupleAndKeywords(args, keywds, "|zii:export",
+ kwlist, &passphrase, &iter, &maciter))
return NULL;
if (self->key != Py_None) {
@@ -226,6 +269,9 @@
Py_DECREF(obj);
}
}
+ if (self->friendlyname != Py_None) {
+ friendly_name = PyString_AsString(self->friendlyname);
+ }
p12 = PKCS12_create(passphrase, friendly_name, pkey, x509, cacerts,
NID_pbe_WithSHA1And3_Key_TripleDES_CBC,
@@ -260,6 +306,8 @@
ADD_KW_METHOD(set_privatekey),
ADD_METHOD(get_ca_certificates),
ADD_KW_METHOD(set_ca_certificates),
+ ADD_METHOD(get_friendlyname),
+ ADD_KW_METHOD(set_friendlyname),
ADD_KW_METHOD(export),
{ NULL, NULL }
};
@@ -305,9 +353,24 @@
if (cert == NULL) {
Py_INCREF(Py_None);
self->cert = Py_None;
+ Py_INCREF(Py_None);
+ self->friendlyname = Py_None;
} else {
+ unsigned char *alstr;
+ int allen;
if ((self->cert = (PyObject *)crypto_X509_New(cert, 1)) == NULL)
goto error;
+
+ /* Now we need to extract the friendlyName of the PKCS12
+ * that was stored by PKCS_pasrse() in the alias of the
+ * certificate. */
+ alstr = X509_alias_get0(cert, &allen);
+ if (alstr && (self->friendlyname = Py_BuildValue("s#", alstr, allen))){
+ /* success */
+ } else {
+ Py_INCREF(Py_None);
+ self->friendlyname = Py_None;
+ }
}
if (pkey == NULL) {
Py_INCREF(Py_None);
@@ -394,6 +457,8 @@
ret = visit(self->key, arg);
if (ret == 0 && self->cacerts != NULL)
ret = visit(self->cacerts, arg);
+ if (ret == 0 && self->friendlyname != NULL)
+ ret = visit(self->friendlyname, arg);
return ret;
}
@@ -412,6 +477,8 @@
self->key = NULL;
Py_XDECREF(self->cacerts);
self->cacerts = NULL;
+ Py_XDECREF(self->friendlyname);
+ self->friendlyname = NULL;
return 0;
}
diff --git a/src/crypto/pkcs12.h b/src/crypto/pkcs12.h
index 373fc22..9be80bb 100644
--- a/src/crypto/pkcs12.h
+++ b/src/crypto/pkcs12.h
@@ -24,7 +24,8 @@
PyObject_HEAD
PyObject *cert; /* never NULL */
PyObject *key; /* never NULL */
- PyObject *cacerts;
+ PyObject *cacerts; /* never NULL */
+ PyObject *friendlyname; /* never NULL */
} crypto_PKCS12Obj;
crypto_PKCS12Obj *
diff --git a/test/test_crypto.py b/test/test_crypto.py
index bfac032..684ae06 100644
--- a/test/test_crypto.py
+++ b/test/test_crypto.py
@@ -801,6 +801,7 @@
self.assertEqual(None, p12.get_certificate())
self.assertEqual(None, p12.get_privatekey())
self.assertEqual(None, p12.get_ca_certificates())
+ self.assertEqual(None, p12.get_friendlyname())
def test_type_errors(self):
@@ -819,6 +820,8 @@
self.assertRaises(TypeError, p12.set_ca_certificates, X509())
self.assertRaises(TypeError, p12.set_ca_certificates, (3, 4))
self.assertRaises(TypeError, p12.set_ca_certificates, ( PKey(), ))
+ self.assertRaises(TypeError, p12.set_friendlyname, 6)
+ self.assertRaises(TypeError, p12.set_friendlyname, ('foo', 'bar'))
def test_key_only(self):
@@ -860,7 +863,7 @@
self.assertEqual(cleartextCertificatePEM, dump_certificate(FILETYPE_PEM, p12.get_ca_certificates()[0]))
- def gen_pkcs12( self, cert_pem, key_pem, ca_pem ):
+ def gen_pkcs12( self, cert_pem=None, key_pem=None, ca_pem=None, fn=None ):
"""
Generate a PKCS12 object with components from PEM.
Verify that the set functions return None.
@@ -875,6 +878,9 @@
if ca_pem:
ret = p12.set_ca_certificates( ( load_certificate(FILETYPE_PEM, ca_pem), ) )
self.assertEqual(ret, None)
+ if fn:
+ ret = p12.set_friendlyname( fn )
+ self.assertEqual(ret, None)
return p12
@@ -939,13 +945,24 @@
def test_friendly_name(self):
"""
- Test that we can export a L{PKCS12} with a friendly name.
+ Test that we can get and set a friendlyName on a PKCS12.
+ Test that we can export a L{PKCS12} with a friendly name,
+ and confirm we can load the PKCS12 and find the friendly name.
+ Use the openssl program to also verify the certs in the PKCS12.
"""
- p12 = self.gen_pkcs12( server_cert_pem, server_key_pem, root_cert_pem )
passwd = 'Dogmeat[]{}!@#$%^&*()~`?/.,<>-_+=";:'
- for friendly_name in ('Serverlicious', None, '', '###'):
- dumped_p12 = p12.export(passphrase=passwd, iter=2, maciter=3,
- friendly_name=friendly_name)
+ p12 = self.gen_pkcs12( server_cert_pem, server_key_pem, root_cert_pem )
+ for friendly_name in ('Serverlicious', None, '###'):
+ p12.set_friendlyname(friendly_name)
+ self.assertEqual(p12.get_friendlyname(), friendly_name)
+ dumped_p12 = p12.export(passphrase=passwd, iter=2, maciter=3)
+ reloaded_p12 = load_pkcs12(dumped_p12, passwd)
+ self.assertEqual(p12.get_friendlyname(),
+ reloaded_p12.get_friendlyname())
+ # We would use the openssl program to confirm the friendly
+ # name, but it is not possible. The pkcs12 command
+ # does not store the friendly name in the cert's
+ # alias, which we could then extract.
self.check_recovery(dumped_p12, key=server_key_pem,
cert=server_cert_pem, ca=root_cert_pem, passwd=passwd)