Merge lp:~rick-fdd/pyopenssl/pkcs12_mod_and_export2 into lp:~exarkun/pyopenssl/trunk

Proposed by rick_dean
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
Reviewer Review Type Date Requested Status
Jean-Paul Calderone Approve
Review via email: mp+8962@code.launchpad.net
To post a comment you must log in.
Revision history for this message
rick_dean (rick-fdd) wrote :

It's got everything but the changelog.

Revision history for this message
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_PKCS12_set_certificate documents its return type as an X509 object. This looks like a copy/paste mistake, I guess. The function does return self, the PKCS12 instance. It should probably return None, though, I think.
  2. There's a left over C99-style comment near the definition of crypto_PKCS12_get_privatekey which should likely just be deleted.
  3. crypto_PKCS12_set_privatekey has one of the same problems as crypto_PKCS12_set_certificate with respect to its return value.
  4. crypto_PKCS12_set_ca_certificates does as well.
  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_certificates. It might be valid at the time they pass it in, but then it can be mutated later. Requiring a tuple in crypto_PKCS12_set_ca_certificates instead of any sequence is probably a good idea.
  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_PKCS12_set_ca_certificates, PySequence_Length and PySequence_GetItem can have errors that should be checked for. I'm not sure if PyTuple_GetSize and PyTuple_GetItem (cf point 5) also can, maybe not.
  9. crypto_PKCS12_set_ca_certificates also returns self, should be None.
  10. Test method docstrings. I would prefer docstrings which describe some desired behavior which the test is verifying pyOpenSSL actually provides.
  11. test_export_and_load is too long. :) Any chance this can be broken into a few shorter pieces?

I kinda skimmed the export code, I'm too sleepy at this point to do it justice. I'll have another look soon. Thanks.

review: Needs Fixing
123. By rick_dean

Implemented some of the PKCS12 fixups requested by JP.

Revision history for this message
rick_dean (rick-fdd) wrote :
Download full text (4.2 KiB)

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_PKCS12_set_certificate()
leaks objects when trying to replace an existing one. Likewise
for crypto_PKCS12_set_privatekey(). Setting of
CA certificates isn't implemented. Except for export, our API
is identical.

>
> 1. crypto_PKCS12_set_certificate documents its return type as an X509 object. This looks like a copy/paste mistake, I guess. The function does return self, the PKCS12 instance. It should probably return None, though, I think.

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_PKCS12_get_privatekey which should likely just be deleted.

Ooops. Fixed.

> 3. crypto_PKCS12_set_privatekey has one of the same problems as crypto_PKCS12_set_certificate with respect to its return value.

Agreed. fixed.

> 4. crypto_PKCS12_set_ca_certificates does as well.

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_certificates. It might be valid at the time they pass it in, but then it can be mutated later. Requiring a tuple in crypto_PKCS12_set_ca_certificates instead of any sequence is probably a good idea.
> 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(PY_MAJOR_VERSION) || PY_VERSION_HEX < 0x02050000
typedef int Py_ssize_t
#endif

> 8. Still in crypto_PKCS12_set_ca_certificates, PySequence_Length and PySequence_GetItem can have e...

Read more...

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.

Revision history for this message
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_certificates. It might be valid at the time they pass it in, but then it can be mutated later. Requiring a tuple in crypto_PKCS12_set_ca_certificates instead of any sequence is probably a good idea.

Yeah, good point. crypto_PKCS12_export() has a commented-out assert
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_PKCS12_clear()
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_PKCS12_set_ca_certificates, PySequence_Length and PySequence_GetItem can have errors that should be checked for. I'm not sure if PyTuple_GetSize and PyTuple_GetItem (cf point 5) also can, maybe not.

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_GetItem() if we within the range of PySequence_length().
I'd love to see a failing test case for this, if you can make one.

> 11. test_export_and_load is too long. :) Any chance this can be broken into a few shorter pieces?

Fixed. The monster has been slain. Where is my knighthood? :-)

>
> --
> https://code.launchpad.net/~rick-fdd/pyopenssl/pkcs12_mod_and_export2/+merge/8962
> 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

127. By rick_dean

Convert crypto_PKCS12Obj->cacerts to a tuple as Jean-Paul pointed out.

128. By rick_dean

Change the API for setting and getting friendlyNames of PKCS12

129. By rick_dean

Handle error cases of PySequence_Length() and PySequence_GetItem(). Add an test case for zero length CA.

Revision history for this message
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.

130. By rick_dean

doc string fix

Revision history for this message
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_PKCS12_set_ca_certificates should convert cacerts to a tuple first, then iterate over that tuple and check the types of the elements. A broken or weird __iter__ could sneak a non-x509 past the checks as the code is now.
  2. The return type of crypto_PKCS12_get_friendlyname shouldn't really be crypto_PKeyObj, should it?
  3. crypto_PKCS12_set_friendlyname should use PyString_CheckExact, to reject str subclasses.
  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_PKCS12_export is currently leaking a STACK_OF(X509).
  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_friendlyname(). Jean-Paul's find.

133. By rick_dean

Use PyString_CheckExact() in crypto_PKCS12_set_friendlyname(). 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.

Revision history for this message
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_PKCS12_set_ca_certificates should convert cacerts to a tuple first, then iterate over that tuple and check the types of the elements. A broken or weird __iter__ could sneak a non-x509 past the checks as the code is now.
> 2. The return type of crypto_PKCS12_get_friendlyname shouldn't really be crypto_PKeyObj, should it?
> 3. crypto_PKCS12_set_friendlyname should use PyString_CheckExact, to reject str subclasses.
> 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_PKCS12_export is currently leaking a STACK_OF(X509).
> 5. In crypto_PKCS12_New, cacerts is leaked in all the error cases.
>
> --
> https://code.launchpad.net/~rick-fdd/pyopenssl/pkcs12_mod_and_export2/+merge/8962
> You are the owner of lp:~rick-fdd/pyopenssl/pkcs12_mod_and_export2.

138. By rick_dean

Change arg name of _runopenssl() from ''pem'' to ''stdin''.

Revision history for this message
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://bazaar.launchpad.net/~exarkun/pyopenssl/pkcs12_mod_and_export2/revision/149
  http://bazaar.launchpad.net/~exarkun/pyopenssl/pkcs12_mod_and_export2/revision/156
  http://bazaar.launchpad.net/~exarkun/pyopenssl/pkcs12_mod_and_export2/revision/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

Revision history for this message
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://bazaar.launchpad.net/~exarkun/pyopenssl/pkcs12_mod_and_export2/revision/149
> http://bazaar.launchpad.net/~exarkun/pyopenssl/pkcs12_mod_and_export2/revision/156
> http://bazaar.launchpad.net/~exarkun/pyopenssl/pkcs12_mod_and_export2/revision/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://code.launchpad.net/~rick-fdd/pyopenssl/pkcs12_mod_and_export2/+merge/8962
> You are the owner of lp:~rick-fdd/pyopenssl/pkcs12_mod_and_export2.

--
Rick

Revision history for this message
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://bazaar.launchpad.net/~exarkun/pyopenssl/pkcs12_mod_and_export2/revision/149
> http://bazaar.launchpad.net/~exarkun/pyopenssl/pkcs12_mod_and_export2/revision/156
> http://bazaar.launchpad.net/~exarkun/pyopenssl/pkcs12_mod_and_export2/revision/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://code.launchpad.net/~rick-fdd/pyopenssl/pkcs12_mod_and_export2/+merge/8962
> You are the owner of lp:~rick-fdd/pyopenssl/pkcs12_mod_and_export2.

--
Rick

Revision history for this message
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

Revision history for this message
Jean-Paul Calderone (exarkun) wrote :
Download full text (3.3 KiB)

>
> 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://bazaar.launchpad.net/~exarkun/pyopenssl/pkcs12_mod_and_export2/revi
> sion/149
> > http://bazaar.launchpad.net/~exarkun/pyopenssl/pkcs12_mod_and_export2/revi
> sion/156
> > http://bazaar.launchpad.net/~exarkun/pyopenssl/pkcs12_mod_and_export2/revi
> 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://code.launchpad.net/~rick-
>...

Read more...

Revision history for this message
rick_dean (rick-fdd) wrote :
Download full text (3.9 KiB)

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...

Read more...

Revision history for this message
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://code.launchpad.net/~rick-fdd/pyopenssl/pkcs12_mod_and_export2/+merge/8962
> You are the owner of lp:~rick-fdd/pyopenssl/pkcs12_mod_and_export2.

Revision history for this message
Jean-Paul Calderone (exarkun) wrote :

Accepted this a while ago.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
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

Subscribers

People subscribed via source and target branches

to status/vote changes: