First impressions of pyOpenSSL

January 26, 2007

pyOpenSSL is a high-level Python wrapper around a subset of the OpenSSL library (to quote it). It was pulled onto my machines recently as part of a Fedora update, and since I'm currently interested in OpenSSL-related things I decided to play around with it to try it out.

After writing some basic stuff, I have to say that I like it. It's not entirely documented (and for some things you really want to read the OpenSSL manpages too), but it works fine. It's also nicely simple to use; my test program to connect to a server and extract its SSL certificate is only 40 lines.

Here's the gotchas and stuff I know about so far:

  • despite what the Connection object documentation says about the .connect() method, it just connects the underlying socket. You must then call .do_handshake() to start SSL on the new connection.

  • end-of-connection raises ZeroReturnError instead of having the connection's .recv() method return a zero-sized result. I imagine this makes sense if you understand the OpenSSL library.

  • I'm not entirely clear what exactly .shutdown() does; it seems to only tell the other side that you're not going to send more stuff. (I probably need to write a server in pyOpenSSL to really understand it.)

  • there seems to be no way to find out the expiry date of an X509 certificate, although X509 objects have a function to tell you if they've expired.

  • The X509 .digest() function's single argument is a string that is the name of the hash you want to use. The available hashes on my Fedora Core 6 machine seem to be be md2, md5, sha, sha1, dss1, and ripemd aka ripemd160. However, the exact list is in the depths of the OpenSSL library, so yours may differ; see the EVP_DigestInit manpage.

  • the string values of X509Name objects are almost but not quite the usual '/C=...' form; they have Python repr-style type bits glued on the ends.

  • some of the in-Python help text is misleading or wrong; trust the PostScript documentation instead, which is pretty good (if you like minimalism).

These are pretty much minor concerns, though; the only one that required me to do some serious delving was the .digest() issue. (And I'm coming into this completely ignorant of the underlying OpenSSL library routines, which doesn't help. Advanced usage of pyOpenSSL, like callbacks, clearly requires familiarity with OpenSSL itself.)

Sidebar: a simple example client

Here's an example of how you'd use the client side of pyOpenSSL (without error checking and the like):

import socket
from OpenSSL import SSL

def ding(host, port, msg):
  s = socket.socket()
  # TLSv1 chosen more or less arbitrarily
  cx = SSL.Context(SSL.TLSv1_METHOD)
  cn = SSL.Connection(cx, s)

  cn.connect((host, port))
  cn.do_handshake()

  cn.sendall(msg)
  tl = []
  try:
    while 1:
      r = cn.recv(8192)
      tl.append(r)
  except SSL.ZeroReturnError:
    pass

  cn.shutdown()
  cn.close()
  return "".join(tl)

As far as I can tell from strace, the .recv() calls are blocking and this won't spin waiting for network IO.


Comments on this page:

From 193.22.87.200 at 2007-02-06 12:39:04:

I've actually got a patch that adds accessors for the notBefore and notAfter dates if you're interested. Drop me a mail at rich (a) kde.org if you want it.

By cks at 2007-02-06 17:20:51:

I currently don't need notBefore/notAfter enough to build a custom privately patched version of pyOpenSSL, although I'll remember this if that ever changes. And thanks for the offer!

(I'll also hope that in the mean time, the primary author is interested enough to accept the patches into the mainline version, since they strike me as generally useful.)

Written on 26 January 2007.
« A clever trick to deal with students powering off workstations
Why I think that DNS whitelists are going to fail »

Page tools: View Source, View Normal.
Search:
Login: Password:

Last modified: Fri Jan 26 23:13:16 2007
This dinky wiki is brought to you by the Insane Hackers Guild, Python sub-branch.