Merge lp:~rick-fdd/pyopenssl/pkcs12_mod_and_export2 into lp:~exarkun/pyopenssl/trunk
- pkcs12_mod_and_export2
- Merge into trunk
Status: | Merged | ||||
---|---|---|---|---|---|
Merged at revision: | not available | ||||
Proposed branch: | lp:~rick-fdd/pyopenssl/pkcs12_mod_and_export2 | ||||
Merge into: | lp:~exarkun/pyopenssl/trunk | ||||
Diff against target: | None lines | ||||
To merge this branch: | bzr merge lp:~rick-fdd/pyopenssl/pkcs12_mod_and_export2 | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Jean-Paul Calderone | Approve | ||
Review via email: mp+8962@code.launchpad.net |
Commit message
Description of the change
rick_dean (rick-fdd) wrote : | # |
Jean-Paul Calderone (exarkun) wrote : | # |
I should have mentioned earlier that there is someone who recently showed up on the mailing list (over on sourceforge) who is also a bit interested in this area (although he says he's more keen on CRLs than PKCS12). He's pushed a branch, <lp:~phil-mayers/pyopenssl/crl+morepkcs12>, which has some overlap with this one. I mostly prefer the PKCS12 code in this branch. I particularly like the documentation additions and many of the test suite additions. Here are some areas I think could be improved, though:
1. crypto_
2. There's a left over C99-style comment near the definition of crypto_
3. crypto_
4. crypto_
5. Hm. The way PKCS12 handles its CA certificates list has a hole. In trunk, crypto_PKCS12_New always creates cacerts, and makes it a tuple. This is good, since get_ca_certificates will return it directly, and if it were a list, callers could modify it in ways which would probably result in a crash later. With this branch, they can pass a list to set_ca_
6. All the code handling the key, cert, and cacerts fields of crypto_PKCS12Obj structures would probably be simpler if only one of NULL or Py_None were possible values. I think that may really already be the case anyway. crypto_PKCS12_New starts of setting key and cert to NULL, but then immediately either sets them to a PyObject* or goes to the error case. What happens when the NULL checks for key, cert, and cacerts are removed, and just the Py_None checks are left in place (where necessary)?
7. Regarding int vs Py_ssize_t, we should probably put a version-protected typedef into util.h or someplace, so we can spell this in a way that works with all supported versions and actually does the right thing on newer Pythons.
8. Still in crypto_
9. crypto_
10. Test method docstrings. I would prefer docstrings which describe some desired behavior which the test is verifying pyOpenSSL actually provides.
11. test_export_
I kinda skimmed the export code, I'm too sleepy at this point to do it justice. I'll have another look soon. Thanks.
rick_dean (rick-fdd) wrote : | # |
On Sat, Jul 18, 2009 at 03:04:46AM -0000, Jean-Paul Calderone wrote:
> Review: Needs Fixing
> I should have mentioned earlier that there is someone who recently showed up on the mailing list (over on sourceforge) who is also a bit interested in this area (although he says he's more keen on CRLs than PKCS12). He's pushed a branch, <lp:~phil-mayers/pyopenssl/crl+morepkcs12>, which has some overlap with this one. I mostly prefer the PKCS12 code in this branch. I particularly like the documentation additions and many of the test suite additions. Here are some areas I think could be improved, though:
Thanks. I just subscribed.
It's great to be able to compare code. His crypto_
leaks objects when trying to replace an existing one. Likewise
for crypto_
CA certificates isn't implemented. Except for export, our API
is identical.
>
> 1. crypto_
Agreed. fixed.
FWIW, returning the object is a very ruby thing to do, so
you can chain together multple method calls on one line.
> 2. There's a left over C99-style comment near the definition of crypto_
Ooops. Fixed.
> 3. crypto_
Agreed. fixed.
> 4. crypto_
Agreed. fixed.
> 5. Hm. The way PKCS12 handles its CA certificates list has a hole. In trunk, crypto_PKCS12_New always creates cacerts, and makes it a tuple. This is good, since get_ca_certificates will return it directly, and if it were a list, callers could modify it in ways which would probably result in a crash later. With this branch, they can pass a list to set_ca_
> 6. All the code handling the key, cert, and cacerts fields of crypto_PKCS12Obj structures would probably be simpler if only one of NULL or Py_None were possible values. I think that may really already be the case anyway. crypto_PKCS12_New starts of setting key and cert to NULL, but then immediately either sets them to a PyObject* or goes to the error case. What happens when the NULL checks for key, cert, and cacerts are removed, and just the Py_None checks are left in place (where necessary)?
> 7. Regarding int vs Py_ssize_t, we should probably put a version-protected typedef into util.h or someplace, so we can spell this in a way that works with all supported versions and actually does the right thing on newer Pythons.
Agreed, but I'm too lazy to test it right now.
#if !defined(
typedef int Py_ssize_t
#endif
> 8. Still in crypto_
- 124. By rick_dean
-
broke the monster PKCS12 test into little ones, and other PKCS12 test additions.
- 125. By rick_dean
-
Move all the PEM to test_crypto.py, so both it and test_ssl.py can use them without circular includes.
- 126. By rick_dean
-
The PyObjets of struct crypto_PKCS12Obj are never NULL so stop checking.
rick_dean (rick-fdd) wrote : | # |
On Sat, Jul 18, 2009 at 03:04:46AM -0000, Jean-Paul Calderone wrote:
> 5. Hm. The way PKCS12 handles its CA certificates list has a hole. In trunk, crypto_PKCS12_New always creates cacerts, and makes it a tuple. This is good, since get_ca_certificates will return it directly, and if it were a list, callers could modify it in ways which would probably result in a crash later. With this branch, they can pass a list to set_ca_
Yeah, good point. crypto_
that could fix this, but I like your tuple conversion idea better.
I'll give it a try.
> 6. All the code handling the key, cert, and cacerts fields of crypto_PKCS12Obj structures would probably be simpler if only one of NULL or Py_None were possible values. I think that may really already be the case anyway. crypto_PKCS12_New starts of setting key and cert to NULL, but then immediately either sets them to a PyObject* or goes to the error case. What happens when the NULL checks for key, cert, and cacerts are removed, and just the Py_None checks are left in place (where necessary)?
Agreed. The PyObjects of crypto_PKCS12Obj are never NULL.
Fixed. The checks are gone.
Can we ever traverse an object after a clear? I left in that NULL check.
In theory could the PyDECREF() of self->key in crypto_
call a desctructor which could access the PKCS12 object and cause
trouble with the now NULL self->cert? I suppose we should try
for a test case. Agressively eliminating NULL checks makes
me nervious, and if you let me, I'd leave them in.
> 8. Still in crypto_
Can PySequence_Length() fail after PySequence_Check() retuned
success? There is little point to PySequence_Check() then, as we
should just call PySequence_Length() and compare to zero. Likewise,
for PySequence_
I'd love to see a failing test case for this, if you can make one.
> 11. test_export_
Fixed. The monster has been slain. Where is my knighthood? :-)
>
> --
> https:/
> You are the owner of lp:~rick-fdd/pyopenssl/pkcs12_mod_and_export2.
We should create set and get functions for friendly_name
instead of a parameter of export, so our API can better support
reading them from loaded certificates.
--
Rick
rick_dean (rick-fdd) wrote : | # |
All of JP's comments have been implemented except
for the Py_ssize_t which I don't have the energy
to test. This branch is ready for merge.
The commit list shows the more recent commits
but the diff does not.
Jean-Paul Calderone (exarkun) wrote : | # |
I wrote a nicer review, but firefox crashed and ate it. This one's a bit terser, sorry.
1. crypto_
2. The return type of crypto_
3. crypto_
4. It's hard to tell for sure, but I *think* PKCS12_create doesn't take over the cacerts stack passed to it. So I think crypto_
5. In crypto_PKCS12_New, cacerts is leaked in all the error cases.
- 131. By rick_dean
-
Convert cacerts to tuple() before type checking so iterators can't play games with us. Jean-Paul's find.
- 132. By rick_dean
-
Fix return type of crypto_
PKCS12_ set_friendlynam e(). Jean-Paul's find. - 133. By rick_dean
-
Use PyString_
CheckExact( ) in crypto_ PKCS12_ set_friendlynam e(). Jean-Paul's find. - 134. By rick_dean
-
Stop leaking a STACK_OF(X509) on error cases of crypto_
PKCS12_ New(). Add a test case of that. Jean-Paul's find. - 135. By rick_dean
-
Stop leaking a STACK_OF(X509) in crypto_
PKCS12_ export. Jean-Paul's find. - 136. By rick_dean
-
More fixes of STACK_OF(X509) in crypto_
PKCS12_ New(). - 137. By rick_dean
-
Only allocate a STACK_OF(X509) in crypto_
PKCS12_ export( ) when needed.
rick_dean (rick-fdd) wrote : | # |
Excellent points! Thanks for the lesson. I pushed all five
fixes to lp:~rick-fdd/pyopenssl/pkcs12_mod_and_export2
--
Rick
On Fri, Jul 24, 2009 at 04:16:38AM -0000, Jean-Paul Calderone wrote:
> I wrote a nicer review, but firefox crashed and ate it. This one's a bit terser, sorry.
>
> 1. crypto_
> 2. The return type of crypto_
> 3. crypto_
> 4. It's hard to tell for sure, but I *think* PKCS12_create doesn't take over the cacerts stack passed to it. So I think crypto_
> 5. In crypto_PKCS12_New, cacerts is leaked in all the error cases.
>
> --
> https:/
> You are the owner of lp:~rick-fdd/pyopenssl/pkcs12_mod_and_export2.
Jean-Paul Calderone (exarkun) wrote : | # |
>
> Excellent points! Thanks for the lesson. I pushed all five
> fixes to lp:~rick-fdd/pyopenssl/pkcs12_mod_and_export2
>
Awesome, thanks for all your work on this. :) I made a bunch of cosmetic changes (I should put
together a style guide for pyOpenSSL, but I'm still not sure what it would say), but notices a
couple more actual potential issues, which I also tried to tackle. My changes are in a branch
based on yours, <lp:~exarkun/pyopenssl/pkcs12_mod_and_export2>. The revisions which actually
contain interesting changes are:
http://
http://
http://
I think I'm happy with the code now, so if you think these changes are sensible, I think I'll go ahead and merge them into trunk.
Jean-Paul
rick_dean (rick-fdd) wrote : | # |
I didn't know whose curly bracket style that was, yours
or openssl's. As long as we don't adopt the thousand
line function style of openssl, I'll be happy. :-)
#149 looks good. If Py_BuildValue returned NULL, it
was like the alias didn't exist. I contemplated an
error message, but chose not to. With the new code
maybe we should.
On Sun, Jul 26, 2009 at 01:57:57AM -0000, Jean-Paul Calderone wrote:
> >
> > Excellent points! Thanks for the lesson. I pushed all five
> > fixes to lp:~rick-fdd/pyopenssl/pkcs12_mod_and_export2
> >
>
> Awesome, thanks for all your work on this. :) I made a bunch of cosmetic changes (I should put
> together a style guide for pyOpenSSL, but I'm still not sure what it would say), but notices a
> couple more actual potential issues, which I also tried to tackle. My changes are in a branch
> based on yours, <lp:~exarkun/pyopenssl/pkcs12_mod_and_export2>. The revisions which actually
> contain interesting changes are:
>
> http://
> http://
> http://
>
> I think I'm happy with the code now, so if you think these changes are sensible, I think I'll go ahead and merge them into trunk.
>
> Jean-Paul
>
> --
> https:/
> You are the owner of lp:~rick-fdd/pyopenssl/pkcs12_mod_and_export2.
--
Rick
rick_dean (rick-fdd) wrote : | # |
My previous email was sent too early.
#149 I'm fine without a warning.
#156 Great catch. I hadn't noticed that PKCS12_parse()
would free the CA list, but not NULL it. For shame!
#157 Okay. Error paths are so hard to check.
A friend in a large company (that incidentally
uses a *lot* of python) says the culture there
is to always call exit() on errors. You can
log a message first if you like, but don't bother
wasting code on an error path which is futile
to get right.
--
Rick
On Sun, Jul 26, 2009 at 01:57:57AM -0000, Jean-Paul Calderone wrote:
> >
> > Excellent points! Thanks for the lesson. I pushed all five
> > fixes to lp:~rick-fdd/pyopenssl/pkcs12_mod_and_export2
> >
>
> Awesome, thanks for all your work on this. :) I made a bunch of cosmetic changes (I should put
> together a style guide for pyOpenSSL, but I'm still not sure what it would say), but notices a
> couple more actual potential issues, which I also tried to tackle. My changes are in a branch
> based on yours, <lp:~exarkun/pyopenssl/pkcs12_mod_and_export2>. The revisions which actually
> contain interesting changes are:
>
> http://
> http://
> http://
>
> I think I'm happy with the code now, so if you think these changes are sensible, I think I'll go ahead and merge them into trunk.
>
> Jean-Paul
>
> --
> https:/
> You are the owner of lp:~rick-fdd/pyopenssl/pkcs12_mod_and_export2.
--
Rick
Jean-Paul Calderone (exarkun) wrote : | # |
>
> I didn't know whose curly bracket style that was, yours
> or openssl's. As long as we don't adopt the thousand
> line function style of openssl, I'll be happy. :-)
>
> #149 looks good. If Py_BuildValue returned NULL, it
> was like the alias didn't exist. I contemplated an
> error message, but chose not to. With the new code
> maybe we should.
>
I wasn't sure how that Py_BuildValue might be induced to fail. I thought it would have to be
an internal CPython malloc failure or something like that. If the alias doesn't exist, doesn't
that mean the whole block with Py_BuildValue will be skipped? Or am I misunderstanding something?
Jean-Paul
Jean-Paul Calderone (exarkun) wrote : | # |
>
> My previous email was sent too early.
>
Oops, didn't see this message before posting my last message. :)
> #149 I'm fine without a warning.
>
> #156 Great catch. I hadn't noticed that PKCS12_parse()
> would free the CA list, but not NULL it. For shame!
>
> #157 Okay. Error paths are so hard to check.
> A friend in a large company (that incidentally
> uses a *lot* of python) says the culture there
> is to always call exit() on errors. You can
> log a message first if you like, but don't bother
> wasting code on an error path which is futile
> to get right.
>
Luckily for me, you basically provided the test case for this error path. :) I probably wouldn't have noticed these problems if not for the commented-out attempt to load that MAC-less PKCS12 blob in one of the tests you wrote. From there, it wasn't too hard to figure out what the fix was. Of course, since the OpenSSL API is so bleh, there may be some other case that's still not tested and doesn't work right... If I were going to pursue a rigorous testing policy for this code, I'd probably start by collecting a lot of PKCS12 data from a variety of sources and run them all through these pyOpenSSL APIs as part of the automated test suite. Off the top of my head, I'm not sure where to go to find such data, though (I guess web browsers can spit it out somehow? Or do they only load it?).
I think the current level of test coverage is good, even if it's not perfect. Problems discovered later can be fixed later. :)
I'm sort of a fan of exiting immediately on errors, at least in certain circumstances. I'd feel really bad putting that into a Python library, since I don't think most Python programmers expect libraries to do that. Maybe it's actually worth doing for error paths that we're not able to construct tests for. If people run into them in practice, that behavior would be a good motivation for them to construct a test case and contribute it back to the project. :) For the time being though, I'll probably leave things as-is.
> --
> Rick
>
> On Sun, Jul 26, 2009 at 01:57:57AM -0000, Jean-Paul Calderone wrote:
> > >
> > > Excellent points! Thanks for the lesson. I pushed all five
> > > fixes to lp:~rick-fdd/pyopenssl/pkcs12_mod_and_export2
> > >
> >
> > Awesome, thanks for all your work on this. :) I made a bunch of cosmetic
> changes (I should put
> > together a style guide for pyOpenSSL, but I'm still not sure what it would
> say), but notices a
> > couple more actual potential issues, which I also tried to tackle. My
> changes are in a branch
> > based on yours, <lp:~exarkun/pyopenssl/pkcs12_mod_and_export2>. The
> revisions which actually
> > contain interesting changes are:
> >
> > http://
> sion/149
> > http://
> sion/156
> > http://
> sion/157
> >
> > I think I'm happy with the code now, so if you think these changes are
> sensible, I think I'll go ahead and merge them into trunk.
> >
> > Jean-Paul
> >
> > --
> > https:/
>...
rick_dean (rick-fdd) wrote : | # |
Agreed that pyOpenSSL should avoid exit().
Agreed the code is ready to be committed.
It does not break any old functionality, and the
API is stable.
There is a difference between testing the
bindings and testing openssl. The unit tests
are responsible for the former, yet they
do some of the later.
--
Rick
On Sun, Jul 26, 2009 at 02:14:18PM -0000, Jean-Paul Calderone wrote:
> >
> > My previous email was sent too early.
> >
>
> Oops, didn't see this message before posting my last message. :)
>
> > #149 I'm fine without a warning.
> >
> > #156 Great catch. I hadn't noticed that PKCS12_parse()
> > would free the CA list, but not NULL it. For shame!
> >
> > #157 Okay. Error paths are so hard to check.
> > A friend in a large company (that incidentally
> > uses a *lot* of python) says the culture there
> > is to always call exit() on errors. You can
> > log a message first if you like, but don't bother
> > wasting code on an error path which is futile
> > to get right.
> >
>
> Luckily for me, you basically provided the test case for this error path. :) I probably wouldn't have noticed these problems if not for the commented-out attempt to load that MAC-less PKCS12 blob in one of the tests you wrote. From there, it wasn't too hard to figure out what the fix was. Of course, since the OpenSSL API is so bleh, there may be some other case that's still not tested and doesn't work right... If I were going to pursue a rigorous testing policy for this code, I'd probably start by collecting a lot of PKCS12 data from a variety of sources and run them all through these pyOpenSSL APIs as part of the automated test suite. Off the top of my head, I'm not sure where to go to find such data, though (I guess web browsers can spit it out somehow? Or do they only load it?).
>
> I think the current level of test coverage is good, even if it's not perfect. Problems discovered later can be fixed later. :)
>
> I'm sort of a fan of exiting immediately on errors, at least in certain circumstances. I'd feel really bad putting that into a Python library, since I don't think most Python programmers expect libraries to do that. Maybe it's actually worth doing for error paths that we're not able to construct tests for. If people run into them in practice, that behavior would be a good motivation for them to construct a test case and contribute it back to the project. :) For the time being though, I'll probably leave things as-is.
>
>
> > --
> > Rick
> >
> > On Sun, Jul 26, 2009 at 01:57:57AM -0000, Jean-Paul Calderone wrote:
> > > >
> > > > Excellent points! Thanks for the lesson. I pushed all five
> > > > fixes to lp:~rick-fdd/pyopenssl/pkcs12_mod_and_export2
> > > >
> > >
> > > Awesome, thanks for all your work on this. :) I made a bunch of cosmetic
> > changes (I should put
> > > together a style guide for pyOpenSSL, but I'm still not sure what it would
> > say), but notices a
> > > couple more actual potential issues, which I also tried to tackle. My
> > changes are in a branch
> > > based on yours, <lp:~exarkun/pyopenssl/pkcs12_mod_and_export2>. The
> > revisions which actually
> > > contain interesting changes are:
> > >
> > > ht...
rick_dean (rick-fdd) wrote : | # |
Yes, I was thinking of malloc as the likely Py_BuildValue()
failure. Either we drop the friendly name, or abort the
whole method. I don't care much, as long as we haven't
leaked or corrupted memory, which keeps me happy.
If Py_BuildValue() fails, then an exception is likely
futile anyway.
--
Rick
On Sun, Jul 26, 2009 at 02:04:30PM -0000, Jean-Paul Calderone wrote:
> >
> > I didn't know whose curly bracket style that was, yours
> > or openssl's. As long as we don't adopt the thousand
> > line function style of openssl, I'll be happy. :-)
> >
> > #149 looks good. If Py_BuildValue returned NULL, it
> > was like the alias didn't exist. I contemplated an
> > error message, but chose not to. With the new code
> > maybe we should.
> >
>
> I wasn't sure how that Py_BuildValue might be induced to fail. I thought it would have to be
> an internal CPython malloc failure or something like that. If the alias doesn't exist, doesn't
> that mean the whole block with Py_BuildValue will be skipped? Or am I misunderstanding something?
>
> Jean-Paul
>
> --
> https:/
> You are the owner of lp:~rick-fdd/pyopenssl/pkcs12_mod_and_export2.
Jean-Paul Calderone (exarkun) wrote : | # |
Accepted this a while ago.
Preview Diff
1 | === modified file 'doc/pyOpenSSL.tex' |
2 | --- doc/pyOpenSSL.tex 2009-07-16 18:09:53 +0000 |
3 | +++ doc/pyOpenSSL.tex 2009-07-17 19:49:48 +0000 |
4 | @@ -524,6 +524,19 @@ |
5 | |
6 | PKCS12 objects have the following methods: |
7 | |
8 | +\begin{methoddesc}[PKCS12]{export}{\optional{passphrase=None}\optional{, friendly_name=None}\optional{, iter=2000}\optional{, maciter=0}} |
9 | +Returns a PKCS12 object as a string. |
10 | + |
11 | +The optional \var{passphrase} must be a string not a callback. |
12 | + |
13 | +See also the man page for the C function \function{PKCS12_create}. |
14 | +\end{methoddesc} |
15 | + |
16 | +\begin{methoddesc}[PKCS12]{get_ca_certificates}{} |
17 | +Return CA certificates within the PKCS12 object as a tuple. Returns |
18 | +\constant{None} if no CA certificates are present. |
19 | +\end{methoddesc} |
20 | + |
21 | \begin{methoddesc}[PKCS12]{get_certificate}{} |
22 | Return certificate portion of the PKCS12 structure. |
23 | \end{methoddesc} |
24 | @@ -532,9 +545,18 @@ |
25 | Return private key portion of the PKCS12 structure |
26 | \end{methoddesc} |
27 | |
28 | -\begin{methoddesc}[PKCS12]{get_ca_certificates}{} |
29 | -Return CA certificates within the PKCS12 object as a tuple. Returns |
30 | -None if no CA certificates are present. |
31 | +\begin{methoddesc}[PKCS12]{set_ca_certificates}{cacerts} |
32 | +Replace or set the CA certificates within the PKCS12 object with the sequence \var{cacerts}. |
33 | + |
34 | +Set \var{cacerts} to \constant{None} to remove all CA certificates. |
35 | +\end{methoddesc} |
36 | + |
37 | +\begin{methoddesc}[PKCS12]{set_certificate}{cert} |
38 | +Replace or set the certificate portion of the PKCS12 structure. |
39 | +\end{methoddesc} |
40 | + |
41 | +\begin{methoddesc}[PKCS12]{set_privatekey}{pkey} |
42 | +Replace or set private key portion of the PKCS12 structure |
43 | \end{methoddesc} |
44 | |
45 | \subsubsection{X509Extension objects \label{openssl-509ext}} |
46 | |
47 | === modified file 'src/crypto/crypto.c' |
48 | --- src/crypto/crypto.c 2009-07-16 20:25:19 +0000 |
49 | +++ src/crypto/crypto.c 2009-07-17 17:50:12 +0000 |
50 | @@ -12,6 +12,7 @@ |
51 | #include <Python.h> |
52 | #define crypto_MODULE |
53 | #include "crypto.h" |
54 | +#include "pkcs12.h" |
55 | |
56 | static char crypto_doc[] = "\n\ |
57 | Main file of crypto sub module.\n\ |
58 | @@ -493,7 +494,6 @@ |
59 | static PyObject * |
60 | crypto_load_pkcs12(PyObject *spam, PyObject *args) |
61 | { |
62 | - crypto_PKCS12Obj *crypto_PKCS12_New(PKCS12 *, char *); |
63 | int len; |
64 | char *buffer, *passphrase = NULL; |
65 | BIO *bio; |
66 | |
67 | === modified file 'src/crypto/pkcs12.c' |
68 | --- src/crypto/pkcs12.c 2009-07-08 16:48:33 +0000 |
69 | +++ src/crypto/pkcs12.c 2009-07-17 20:21:23 +0000 |
70 | @@ -36,19 +36,84 @@ |
71 | return self->cert; |
72 | } |
73 | |
74 | +static char crypto_PKCS12_set_certificate_doc[] = "\n\ |
75 | +Replace the certificate portion of the PKCS12 structure\n\ |
76 | +\n\ |
77 | +@param cert: The new certificate.\n\ |
78 | +@type cert: L{X509}\n\ |
79 | +@return: X509 object containing the certificate\n\ |
80 | +"; |
81 | +static crypto_PKCS12Obj * |
82 | +crypto_PKCS12_set_certificate(crypto_PKCS12Obj *self, PyObject *args, PyObject *keywds) |
83 | +{ |
84 | + PyObject *cert = NULL; |
85 | + static char *kwlist[] = {"cert", NULL}; |
86 | + |
87 | + if (!PyArg_ParseTupleAndKeywords(args, keywds, "O:set_certificate", |
88 | + kwlist, &cert)) |
89 | + return NULL; |
90 | + |
91 | + if (cert != Py_None && ! PyObject_IsInstance(cert, (PyObject *) &crypto_X509_Type)) { |
92 | + PyErr_SetString(PyExc_TypeError, "cert must be type X509 or None"); |
93 | + return NULL; |
94 | + } |
95 | + |
96 | + Py_INCREF(cert); /* Make consistent before calling Py_DECREF() */ |
97 | + if(self->cert) { |
98 | + Py_DECREF(self->cert); |
99 | + } |
100 | + self->cert = cert; |
101 | + |
102 | + Py_INCREF(self); |
103 | + return self; |
104 | +} |
105 | + |
106 | static char crypto_PKCS12_get_privatekey_doc[] = "\n\ |
107 | Return private key portion of the PKCS12 structure\n\ |
108 | \n\ |
109 | @returns: PKey object containing the private key\n\ |
110 | "; |
111 | -static PyObject * |
112 | +//static PyObject * |
113 | +static crypto_PKeyObj * |
114 | crypto_PKCS12_get_privatekey(crypto_PKCS12Obj *self, PyObject *args) |
115 | { |
116 | if (!PyArg_ParseTuple(args, ":get_privatekey")) |
117 | return NULL; |
118 | |
119 | Py_INCREF(self->key); |
120 | - return self->key; |
121 | + return (crypto_PKeyObj *) self->key; |
122 | +} |
123 | + |
124 | +static char crypto_PKCS12_set_privatekey_doc[] = "\n\ |
125 | +Replace or set the certificate portion of the PKCS12 structure\n\ |
126 | +\n\ |
127 | +@param pkey: The new private key.\n\ |
128 | +@type pkey: L{PKey}\n\ |
129 | +@return: None\n\ |
130 | +"; |
131 | +static crypto_PKCS12Obj * |
132 | +crypto_PKCS12_set_privatekey(crypto_PKCS12Obj *self, PyObject *args, PyObject *keywds) |
133 | +{ |
134 | + PyObject *pkey = NULL; |
135 | + static char *kwlist[] = {"pkey", NULL}; |
136 | + |
137 | + if (!PyArg_ParseTupleAndKeywords(args, keywds, "O:set_privatekey", |
138 | + kwlist, &pkey)) |
139 | + return NULL; |
140 | + |
141 | + if (pkey != Py_None && ! PyObject_IsInstance(pkey, (PyObject *) &crypto_PKey_Type)) { |
142 | + PyErr_SetString(PyExc_TypeError, "pkey must be type X509 or None"); |
143 | + return NULL; |
144 | + } |
145 | + |
146 | + Py_INCREF(pkey); /* Make consistent before calling Py_DECREF() */ |
147 | + if(self->key) { |
148 | + Py_DECREF(self->key); |
149 | + } |
150 | + self->key = pkey; |
151 | + |
152 | + Py_INCREF(self); |
153 | + return self; |
154 | } |
155 | |
156 | static char crypto_PKCS12_get_ca_certificates_doc[] = "\n\ |
157 | @@ -67,6 +132,118 @@ |
158 | return self->cacerts; |
159 | } |
160 | |
161 | +static char crypto_PKCS12_set_ca_certificates_doc[] = "\n\ |
162 | +Replace or set the CA certificates withing the PKCS12 object.\n\ |
163 | +\n\ |
164 | +@param cacerts: The new CA certificates.\n\ |
165 | +@type cacerts: Sequence of L{X509}\n\ |
166 | +@return: None\n\ |
167 | +"; |
168 | +static crypto_PKCS12Obj * |
169 | +crypto_PKCS12_set_ca_certificates(crypto_PKCS12Obj *self, PyObject *args, PyObject *keywds) |
170 | +{ |
171 | + PyObject *cacerts; |
172 | + static char *kwlist[] = {"cacerts", NULL}; |
173 | + int i; /* Py_ssize_t for Python 2.5+ */ |
174 | + |
175 | + if (!PyArg_ParseTupleAndKeywords(args, keywds, "O:set_ca_certificates", |
176 | + kwlist, &cacerts)) |
177 | + return NULL; |
178 | + if (cacerts == Py_None) { |
179 | + /* We are good. */ |
180 | + } else if (PySequence_Check(cacerts)) { /* is iterable */ |
181 | + for(i = 0;i < PySequence_Length(cacerts);i++) { /* For each CA cert */ |
182 | + PyObject *obj; |
183 | + obj = PySequence_GetItem(cacerts, i); |
184 | + if (PyObject_Type(obj) != (PyObject *) &crypto_X509_Type) { |
185 | + Py_DECREF(obj); |
186 | + PyErr_SetString(PyExc_TypeError, "cacerts iterable must only contain X509Type"); |
187 | + return NULL; |
188 | + } |
189 | + Py_DECREF(obj); |
190 | + } |
191 | + } else { |
192 | + PyErr_SetString(PyExc_TypeError, "cacerts must be an iterable or None"); |
193 | + return NULL; |
194 | + } |
195 | + |
196 | + Py_INCREF(cacerts); /* Make consistent before calling Py_DECREF() */ |
197 | + if(self->cacerts) { |
198 | + Py_DECREF(self->cacerts); |
199 | + } |
200 | + self->cacerts = cacerts; |
201 | + |
202 | + Py_INCREF(self); |
203 | + return self; |
204 | +} |
205 | + |
206 | +static char crypto_PKCS12_export_doc[] = "\n\ |
207 | +export([passphrase=None][, friendly_name=None][, iter=2048][, maciter=1]\n\ |
208 | +Dump a PKCS12 object as a string. See also \"man PKCS12_create\".\n\ |
209 | +\n\ |
210 | +@param passphrase: used to encrypt the PKCS12\n\ |
211 | +@type passphrase: L{str}\n\ |
212 | +@param friendly_name: A descriptive comment\n\ |
213 | +@type friendly_name: L{str}\n\ |
214 | +@param iter: How many times to repeat the encryption\n\ |
215 | +@type iter: L{int}\n\ |
216 | +@param maciter: How many times to repeat the MAC\n\ |
217 | +@type maciter: L{int}\n\ |
218 | +@return: The string containing the PKCS12\n\ |
219 | +"; |
220 | +static PyObject * |
221 | +crypto_PKCS12_export(crypto_PKCS12Obj *self, PyObject *args, PyObject *keywds) |
222 | +{ |
223 | + int buf_len; |
224 | + PyObject *buffer; |
225 | + char *temp, *passphrase = NULL, *friendly_name = NULL; |
226 | + BIO *bio; |
227 | + PKCS12 *p12; |
228 | + EVP_PKEY *pkey = NULL; |
229 | + STACK_OF(X509) *cacerts = NULL; |
230 | + X509 *x509 = NULL; |
231 | + int iter = 0; /* defaults to PKCS12_DEFAULT_ITER */ |
232 | + int maciter = 0; |
233 | + static char *kwlist[] = {"passphrase", "friendly_name", "iter", "maciter", NULL}; |
234 | + |
235 | + if (!PyArg_ParseTupleAndKeywords(args, keywds, "|zzii:export", |
236 | + kwlist, &passphrase, &friendly_name, &iter, &maciter)) |
237 | + return NULL; |
238 | + |
239 | + if (self->key && self->key != Py_None) { |
240 | + pkey = ((crypto_PKeyObj*) self->key)->pkey; |
241 | + } |
242 | + if (self->cert && self->cert != Py_None) { |
243 | + x509 = ((crypto_X509Obj*) self->cert)->x509; |
244 | + } |
245 | + cacerts = sk_X509_new_null(); |
246 | + if (self->cacerts && self->cacerts != Py_None) { |
247 | + int i; /* Py_ssize_t for Python 2.5+ */ |
248 | + PyObject *obj; |
249 | + for(i = 0;i < PySequence_Length(self->cacerts);i++) { /* For each CA cert */ |
250 | + obj = PySequence_GetItem(self->cacerts, i); |
251 | + /* assert(PyObject_IsInstance(obj, (PyObject *) &crypto_X509_Type )); */ |
252 | + sk_X509_push(cacerts, (( crypto_X509Obj* ) obj)->x509); |
253 | + Py_DECREF(obj); |
254 | + } |
255 | + } |
256 | + |
257 | + p12 = PKCS12_create(passphrase, friendly_name, pkey, x509, cacerts, |
258 | + NID_pbe_WithSHA1And3_Key_TripleDES_CBC, |
259 | + NID_pbe_WithSHA1And3_Key_TripleDES_CBC, |
260 | + iter, maciter, 0); |
261 | + if( p12 == NULL ) { |
262 | + exception_from_error_queue(crypto_Error); |
263 | + return NULL; |
264 | + } |
265 | + bio = BIO_new(BIO_s_mem()); |
266 | + i2d_PKCS12_bio(bio, p12); |
267 | + buf_len = BIO_get_mem_data(bio, &temp); |
268 | + buffer = PyString_FromStringAndSize(temp, buf_len); |
269 | + BIO_free(bio); |
270 | + return buffer; |
271 | +} |
272 | + |
273 | /* |
274 | * ADD_METHOD(name) expands to a correct PyMethodDef declaration |
275 | * { 'name', (PyCFunction)crypto_PKCS12_name, METH_VARARGS, crypto_PKCS12_name_doc } |
276 | @@ -74,11 +251,17 @@ |
277 | */ |
278 | #define ADD_METHOD(name) \ |
279 | { #name, (PyCFunction)crypto_PKCS12_##name, METH_VARARGS, crypto_PKCS12_##name##_doc } |
280 | +#define ADD_KW_METHOD(name) \ |
281 | + { #name, (PyCFunction)crypto_PKCS12_##name, METH_VARARGS | METH_KEYWORDS, crypto_PKCS12_##name##_doc } |
282 | static PyMethodDef crypto_PKCS12_methods[] = |
283 | { |
284 | ADD_METHOD(get_certificate), |
285 | + ADD_KW_METHOD(set_certificate), |
286 | ADD_METHOD(get_privatekey), |
287 | + ADD_KW_METHOD(set_privatekey), |
288 | ADD_METHOD(get_ca_certificates), |
289 | + ADD_KW_METHOD(set_ca_certificates), |
290 | + ADD_KW_METHOD(export), |
291 | { NULL, NULL } |
292 | }; |
293 | #undef ADD_METHOD |
294 | @@ -108,7 +291,7 @@ |
295 | cacerts = sk_X509_new_null(); |
296 | |
297 | /* parse the PKCS12 lump */ |
298 | - if (!(cacerts && PKCS12_parse(p12, passphrase, &pkey, &cert, &cacerts))) |
299 | + if (p12 && !(cacerts && PKCS12_parse(p12, passphrase, &pkey, &cert, &cacerts))) |
300 | { |
301 | exception_from_error_queue(crypto_Error); |
302 | return NULL; |
303 | @@ -122,11 +305,20 @@ |
304 | Py_INCREF(Py_None); |
305 | self->cacerts = Py_None; |
306 | |
307 | - if ((self->cert = (PyObject *)crypto_X509_New(cert, 1)) == NULL) |
308 | - goto error; |
309 | - |
310 | - if ((self->key = (PyObject *)crypto_PKey_New(pkey, 1)) == NULL) |
311 | - goto error; |
312 | + if (cert == NULL) { |
313 | + Py_INCREF(Py_None); |
314 | + self->cert = Py_None; |
315 | + } else { |
316 | + if ((self->cert = (PyObject *)crypto_X509_New(cert, 1)) == NULL) |
317 | + goto error; |
318 | + } |
319 | + if (pkey == NULL) { |
320 | + Py_INCREF(Py_None); |
321 | + self->key = Py_None; |
322 | + } else { |
323 | + if ((self->key = (PyObject *)crypto_PKey_New(pkey, 1)) == NULL) |
324 | + goto error; |
325 | + } |
326 | |
327 | /* Make a tuple for the CA certs */ |
328 | cacert_count = sk_X509_num(cacerts); |
329 | @@ -154,6 +346,23 @@ |
330 | return NULL; |
331 | } |
332 | |
333 | +static char crypto_PKCS12_doc[] = "\n\ |
334 | +PKCS12() -> PKCS12 instance\n\ |
335 | +\n\ |
336 | +Create a new empty PKCS12 object.\n\ |
337 | +\n\ |
338 | +@returns: The PKCS12 object\n\ |
339 | +"; |
340 | +static PyObject * |
341 | +crypto_PKCS12_new(PyTypeObject *subtype, PyObject *args, PyObject *kwargs) |
342 | +{ |
343 | + if (!PyArg_ParseTuple(args, ":PKCS12")) { |
344 | + return NULL; |
345 | + } |
346 | + |
347 | + return (PyObject *)crypto_PKCS12_New(NULL, NULL); |
348 | +} |
349 | + |
350 | /* |
351 | * Find attribute |
352 | * |
353 | @@ -245,9 +454,24 @@ |
354 | NULL, /* setattro */ |
355 | NULL, /* as_buffer */ |
356 | Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, |
357 | - NULL, /* doc */ |
358 | + crypto_PKCS12_doc, |
359 | (traverseproc)crypto_PKCS12_traverse, |
360 | (inquiry)crypto_PKCS12_clear, |
361 | + NULL, /* tp_richcompare */ |
362 | + 0, /* tp_weaklistoffset */ |
363 | + NULL, /* tp_iter */ |
364 | + NULL, /* tp_iternext */ |
365 | + crypto_PKCS12_methods, /* tp_methods */ |
366 | + NULL, /* tp_members */ |
367 | + NULL, /* tp_getset */ |
368 | + NULL, /* tp_base */ |
369 | + NULL, /* tp_dict */ |
370 | + NULL, /* tp_descr_get */ |
371 | + NULL, /* tp_descr_set */ |
372 | + 0, /* tp_dictoffset */ |
373 | + NULL, /* tp_init */ |
374 | + NULL, /* tp_alloc */ |
375 | + crypto_PKCS12_new, /* tp_new */ |
376 | }; |
377 | |
378 | /* |
379 | @@ -262,6 +486,10 @@ |
380 | return 0; |
381 | } |
382 | |
383 | + if (PyModule_AddObject(module, "PKCS12", (PyObject *)&crypto_PKCS12_Type) != 0) { |
384 | + return 0; |
385 | + } |
386 | + |
387 | if (PyModule_AddObject(module, "PKCS12Type", (PyObject *)&crypto_PKCS12_Type) != 0) { |
388 | return 0; |
389 | } |
390 | |
391 | === modified file 'src/crypto/pkcs12.h' |
392 | --- src/crypto/pkcs12.h 2008-02-19 01:50:23 +0000 |
393 | +++ src/crypto/pkcs12.h 2009-07-17 17:22:16 +0000 |
394 | @@ -27,4 +27,7 @@ |
395 | PyObject *cacerts; |
396 | } crypto_PKCS12Obj; |
397 | |
398 | +crypto_PKCS12Obj * |
399 | +crypto_PKCS12_New(PKCS12 *p12, char *passphrase); |
400 | + |
401 | #endif |
402 | |
403 | === modified file 'test/test_crypto.py' |
404 | --- test/test_crypto.py 2009-07-05 16:54:05 +0000 |
405 | +++ test/test_crypto.py 2009-07-17 20:05:22 +0000 |
406 | @@ -17,11 +17,10 @@ |
407 | from OpenSSL.crypto import dump_certificate, load_certificate_request |
408 | from OpenSSL.crypto import dump_certificate_request, dump_privatekey |
409 | from OpenSSL.crypto import PKCS7Type, load_pkcs7_data |
410 | -from OpenSSL.crypto import PKCS12Type, load_pkcs12 |
411 | +from OpenSSL.crypto import PKCS12Type, load_pkcs12, PKCS12 |
412 | from OpenSSL.crypto import NetscapeSPKI, NetscapeSPKIType |
413 | from OpenSSL.test.util import TestCase |
414 | |
415 | - |
416 | cleartextCertificatePEM = """-----BEGIN CERTIFICATE----- |
417 | MIIC7TCCAlagAwIBAgIIPQzE4MbeufQwDQYJKoZIhvcNAQEFBQAwWDELMAkGA1UE |
418 | BhMCVVMxCzAJBgNVBAgTAklMMRAwDgYDVQQHEwdDaGljYWdvMRAwDgYDVQQKEwdU |
419 | @@ -709,6 +708,123 @@ |
420 | cert = load_certificate(FILETYPE_PEM, self.pemData) |
421 | self.assertEqual(cert.get_notBefore(), "20090325123658Z") |
422 | |
423 | +class PKCS12Tests(TestCase): |
424 | + """ |
425 | + Tests functions in the L{OpenSSL.crypto.PKCS12} module. |
426 | + """ |
427 | + pemData = cleartextCertificatePEM + cleartextPrivateKeyPEM |
428 | + |
429 | + def test_construction(self): |
430 | + p12 = PKCS12() |
431 | + self.assertEqual(None, p12.get_certificate()) |
432 | + self.assertEqual(None, p12.get_privatekey()) |
433 | + self.assertEqual(None, p12.get_ca_certificates()) |
434 | + |
435 | + def test_type_errors(self): |
436 | + p12 = PKCS12() |
437 | + self.assertRaises(TypeError, p12.set_certificate, 3) |
438 | + self.assertRaises(TypeError, p12.set_privatekey, 3) |
439 | + self.assertRaises(TypeError, p12.set_ca_certificates, 3) |
440 | + self.assertRaises(TypeError, p12.set_ca_certificates, X509()) |
441 | + self.assertRaises(TypeError, p12.set_ca_certificates, (3, 4)) |
442 | + |
443 | + def test_key_only(self): |
444 | + """ |
445 | + L{OpenSSL.crypto.PKCS12.export} and load a PKCS without a key |
446 | + """ |
447 | + passwd = 'blah' |
448 | + p12 = PKCS12() |
449 | + pkey = load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM) |
450 | + p12.set_privatekey( pkey ) |
451 | + self.assertEqual(None, p12.get_certificate()) |
452 | + self.assertEqual(pkey, p12.get_privatekey()) |
453 | + dumped_p12 = p12.export(passphrase=passwd, iter=2, maciter=3) |
454 | + p12 = load_pkcs12(dumped_p12, passwd) |
455 | + self.assertEqual(None, p12.get_ca_certificates()) |
456 | + self.assertEqual(None, p12.get_certificate()) |
457 | + # It's actually in the pkcs12, but we silently don't find it (a key without a cert) |
458 | + #self.assertEqual(cleartextPrivateKeyPEM, dump_privatekey(FILETYPE_PEM, p12.get_privatekey())) |
459 | + |
460 | + def test_cert_only(self): |
461 | + """ |
462 | + L{OpenSSL.crypto.PKCS12.export} and load a PKCS without a key. |
463 | + Strangely, OpenSSL converts it to a CA cert. |
464 | + """ |
465 | + passwd = 'blah' |
466 | + p12 = PKCS12() |
467 | + cert = load_certificate(FILETYPE_PEM, cleartextCertificatePEM) |
468 | + p12.set_certificate( cert ) |
469 | + self.assertEqual(cert, p12.get_certificate()) |
470 | + self.assertEqual(None, p12.get_privatekey()) |
471 | + dumped_p12 = p12.export(passphrase=passwd, iter=2, maciter=3) |
472 | + p12 = load_pkcs12(dumped_p12, passwd) |
473 | + self.assertEqual(None, p12.get_privatekey()) |
474 | + self.assertEqual(None, p12.get_certificate()) |
475 | + self.assertEqual(cleartextCertificatePEM, dump_certificate(FILETYPE_PEM, p12.get_ca_certificates()[0])) |
476 | + |
477 | + |
478 | + def test_export_and_load(self): |
479 | + """ |
480 | + L{OpenSSL.crypto.PKCS12.export} and others |
481 | + """ |
482 | + # use openssl program to create a p12 then load it |
483 | + from OpenSSL.test.test_ssl import client_cert_pem, client_key_pem, server_cert_pem, server_key_pem, root_cert_pem |
484 | + passwd = 'whatever' |
485 | + pem = client_key_pem + client_cert_pem |
486 | + p12_str = _runopenssl(pem, "pkcs12", '-export', '-clcerts', '-passout', 'pass:'+passwd) |
487 | + p12 = load_pkcs12(p12_str, passwd) |
488 | + # verify p12 using pkcs12 get_* functions |
489 | + cert_pem = dump_certificate(FILETYPE_PEM, p12.get_certificate()) |
490 | + self.assertEqual(cert_pem, client_cert_pem) |
491 | + key_pem = dump_privatekey(FILETYPE_PEM, p12.get_privatekey()) |
492 | + self.assertEqual(key_pem, client_key_pem) |
493 | + self.assertEqual(None, p12.get_ca_certificates()) |
494 | + # dump cert and verify it using the openssl program |
495 | + dumped_p12 = p12.export(passphrase=passwd, iter=2, maciter=0, friendly_name='blueberry') |
496 | + recovered_key = _runopenssl(dumped_p12, "pkcs12", '-nocerts', '-nodes', '-passin', 'pass:'+passwd) |
497 | + self.assertEqual(recovered_key[-len(client_key_pem):], client_key_pem) |
498 | + recovered_cert = _runopenssl(dumped_p12, "pkcs12", '-clcerts', '-nodes', '-passin', 'pass:'+passwd, '-nokeys') |
499 | + self.assertEqual(recovered_cert[-len(client_cert_pem):], client_cert_pem) |
500 | + # change the cert and key |
501 | + p12.set_certificate(load_certificate(FILETYPE_PEM, server_cert_pem)) |
502 | + p12.set_privatekey(load_privatekey(FILETYPE_PEM, server_key_pem)) |
503 | + root_cert = load_certificate(FILETYPE_PEM, root_cert_pem) |
504 | + p12.set_ca_certificates( [ root_cert ] ) |
505 | + p12.set_ca_certificates( ( root_cert, ) ) |
506 | + self.assertEqual(1, len(p12.get_ca_certificates())) |
507 | + self.assertEqual(root_cert, p12.get_ca_certificates()[0]) |
508 | + # recover changed cert and key using the openssl program |
509 | + dumped_p12 = p12.export(passphrase=passwd, iter=2, maciter=0, friendly_name='Serverlicious') |
510 | + recovered_key = _runopenssl(dumped_p12, "pkcs12", '-nocerts', '-nodes', '-passin', 'pass:'+passwd) |
511 | + self.assertEqual(recovered_key[-len(server_key_pem):], server_key_pem) |
512 | + recovered_cert = _runopenssl(dumped_p12, "pkcs12", '-clcerts', '-nodes', '-passin', 'pass:'+passwd, '-nokeys') |
513 | + self.assertEqual(recovered_cert[-len(server_cert_pem):], server_cert_pem) |
514 | + recovered_cert = _runopenssl(dumped_p12, "pkcs12", '-cacerts', '-nodes', '-passin', 'pass:'+passwd, '-nokeys') |
515 | + self.assertEqual(recovered_cert[-len(root_cert_pem):], root_cert_pem) |
516 | + # Test other forms of no password |
517 | + passwd = '' |
518 | + dumped_p12_empty = p12.export(passphrase=passwd, iter=2, maciter=0, friendly_name='Sewer') |
519 | + dumped_p12_none = p12.export(passphrase=None, iter=2, maciter=0, friendly_name='Sewer') |
520 | + dumped_p12_nopw = p12.export( iter=2, maciter=0, friendly_name='Sewer') |
521 | + recovered_empty = _runopenssl(dumped_p12_empty, "pkcs12", '-nodes', '-passin', 'pass:'+passwd ) |
522 | + recovered_none = _runopenssl(dumped_p12_none, "pkcs12", '-nodes', '-passin', 'pass:'+passwd ) |
523 | + recovered_nopw = _runopenssl(dumped_p12_nopw, "pkcs12", '-nodes', '-passin', 'pass:'+passwd ) |
524 | + self.assertEqual(recovered_none, recovered_nopw) |
525 | + self.assertEqual(recovered_none, recovered_empty) |
526 | + # Test removing CA certs |
527 | + p12.set_ca_certificates( None ) |
528 | + self.assertEqual(None, p12.get_ca_certificates()) |
529 | + # Test without MAC |
530 | + dumped_p12 = p12.export(maciter=-1, passphrase=passwd, iter=2) |
531 | + recovered_key = _runopenssl(dumped_p12, "pkcs12", '-nocerts', '-nodes', '-passin', 'pass:'+passwd, '-nomacver' ) |
532 | + self.assertEqual(recovered_key[-len(server_key_pem):], server_key_pem) |
533 | + # We can't load PKCS12 without MAC, because we use PCKS_parse() |
534 | + #p12 = load_pkcs12(dumped_p12, passwd) |
535 | + # Test export without any args |
536 | + dumped_p12 = p12.export() |
537 | + recovered_key = _runopenssl(dumped_p12, "pkcs12", '-nodes', '-passin', 'pass:' ) |
538 | + self.assertEqual(recovered_key[-len(server_key_pem):], server_key_pem) |
539 | + |
540 | |
541 | def test_get_notAfter(self): |
542 | """ |
543 | @@ -732,6 +848,18 @@ |
544 | |
545 | |
546 | |
547 | +def _runopenssl(pem, *args): |
548 | + """ |
549 | + Run the command line openssl tool with the given arguments and write |
550 | + the given PEM to its stdin. |
551 | + """ |
552 | + write, read = popen2(" ".join(("openssl",) + args), "b") |
553 | + write.write(pem) |
554 | + write.close() |
555 | + return read.read() |
556 | + |
557 | + |
558 | + |
559 | class FunctionTests(TestCase): |
560 | """ |
561 | Tests for free-functions in the L{OpenSSL.crypto} module. |
562 | @@ -801,17 +929,6 @@ |
563 | self.assertEqual(loadedKey.bits(), key.bits()) |
564 | |
565 | |
566 | - def _runopenssl(self, pem, *args): |
567 | - """ |
568 | - Run the command line openssl tool with the given arguments and write |
569 | - the given PEM to its stdin. |
570 | - """ |
571 | - write, read = popen2(" ".join(("openssl",) + args), "b") |
572 | - write.write(pem) |
573 | - write.close() |
574 | - return read.read() |
575 | - |
576 | - |
577 | def test_dump_certificate(self): |
578 | """ |
579 | L{dump_certificate} writes PEM, DER, and text. |
580 | @@ -821,13 +938,13 @@ |
581 | dumped_pem = dump_certificate(FILETYPE_PEM, cert) |
582 | self.assertEqual(dumped_pem, cleartextCertificatePEM) |
583 | dumped_der = dump_certificate(FILETYPE_ASN1, cert) |
584 | - good_der = self._runopenssl(dumped_pem, "x509", "-outform", "DER") |
585 | + good_der = _runopenssl(dumped_pem, "x509", "-outform", "DER") |
586 | self.assertEqual(dumped_der, good_der) |
587 | cert2 = load_certificate(FILETYPE_ASN1, dumped_der) |
588 | dumped_pem2 = dump_certificate(FILETYPE_PEM, cert2) |
589 | self.assertEqual(dumped_pem2, cleartextCertificatePEM) |
590 | dumped_text = dump_certificate(FILETYPE_TEXT, cert) |
591 | - good_text = self._runopenssl(dumped_pem, "x509", "-noout", "-text") |
592 | + good_text = _runopenssl(dumped_pem, "x509", "-noout", "-text") |
593 | self.assertEqual(dumped_text, good_text) |
594 | |
595 | |
596 | @@ -840,13 +957,13 @@ |
597 | self.assertEqual(dumped_pem, cleartextPrivateKeyPEM) |
598 | dumped_der = dump_privatekey(FILETYPE_ASN1, key) |
599 | # XXX This OpenSSL call writes "writing RSA key" to standard out. Sad. |
600 | - good_der = self._runopenssl(dumped_pem, "rsa", "-outform", "DER") |
601 | + good_der = _runopenssl(dumped_pem, "rsa", "-outform", "DER") |
602 | self.assertEqual(dumped_der, good_der) |
603 | key2 = load_privatekey(FILETYPE_ASN1, dumped_der) |
604 | dumped_pem2 = dump_privatekey(FILETYPE_PEM, key2) |
605 | self.assertEqual(dumped_pem2, cleartextPrivateKeyPEM) |
606 | dumped_text = dump_privatekey(FILETYPE_TEXT, key) |
607 | - good_text = self._runopenssl(dumped_pem, "rsa", "-noout", "-text") |
608 | + good_text = _runopenssl(dumped_pem, "rsa", "-noout", "-text") |
609 | self.assertEqual(dumped_text, good_text) |
610 | |
611 | |
612 | @@ -858,13 +975,13 @@ |
613 | dumped_pem = dump_certificate_request(FILETYPE_PEM, req) |
614 | self.assertEqual(dumped_pem, cleartextCertificateRequestPEM) |
615 | dumped_der = dump_certificate_request(FILETYPE_ASN1, req) |
616 | - good_der = self._runopenssl(dumped_pem, "req", "-outform", "DER") |
617 | + good_der = _runopenssl(dumped_pem, "req", "-outform", "DER") |
618 | self.assertEqual(dumped_der, good_der) |
619 | req2 = load_certificate_request(FILETYPE_ASN1, dumped_der) |
620 | dumped_pem2 = dump_certificate_request(FILETYPE_PEM, req2) |
621 | self.assertEqual(dumped_pem2, cleartextCertificateRequestPEM) |
622 | dumped_text = dump_certificate_request(FILETYPE_TEXT, req) |
623 | - good_text = self._runopenssl(dumped_pem, "req", "-noout", "-text") |
624 | + good_text = _runopenssl(dumped_pem, "req", "-noout", "-text") |
625 | self.assertEqual(dumped_text, good_text) |
626 | |
627 |
It's got everything but the changelog.