Wandering Thoughts archives

2007-01-26

First impressions of pyOpenSSL

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.

python/PyOpenSSLComments written at 23:13:16; Add Comment


Page tools: See As Normal.
Search:
Login: Password:
Atom Syndication: Recent Pages, Recent Comments.

This dinky wiki is brought to you by the Insane Hackers Guild, Python sub-branch.