Categories: General
Date: Nov 12, 2004
Title: Python Crypto madness
As I previously have mentioned, I am currently taking a real close look at Yahoo!'s Domain Keys draft. One of my goals is, to setup Postfix to use the Domain Keys system.
Before I go on, I should probably explain the quick details behind Domain Keys. It's a system where a sending email server signs the email (body and headers) with a private key. The public key is stored in a TXT DNS record, so that a receving email server can fetch it, and verify the signature. If the signature is correct, then the email hasn't been modified in transit.
It's clear that there are two participating parties in this system; the signing side and the verifying side. Right now I'm trying to implement the verifying party in my Postfix setup.
The way I do this is through an filtering smtp proxy (Postfix details).
All mails get redirected through it, it checks the signature and appends a header stating that the signature is either good or bad.
I started writing this smtp proxy in Perl, but as I couldn't get any of the RSA modules to compile I gave up and rewrote it in Python. The reason I chose Python was that I had seen multiple RSA moduled for it on the net. However, it turned out to be much more complicated than I thought.
First off, Python comes with a nice set of digest algorithms like MD5, SHA1, etc.. For more advanced crypto stuff they suggest that you use The Python Cryptography Toolkit (sometimes known as pycrypto).
Well, no problem, I installed the beast, found a class called Crypto.PublicKey.RSA, which sounded exactly what I needed. Even better, it has a method called verify(), yay!
But this is where things start to get a whee-bit complicated. The public key (stored in a TXT DNS record, as you recall), is in a format specified in the PKCS#1 standard. However, pycrypto (also known as The Python Cryptography Toolkit), can only construct Crypto.PublicKey.RSA objects if you happen to have the two integers that a public key consists of (n and e for those RSA geeks). As I am not in the mood to write a reader that can extract those two integers from the public key, I go looking for alternatives.
On the website of The Python Cryptography Toolkit (dare I mention that it's also known as pycrypto), two packages that should ease the use of pycrypto (you know by now) are mentioned; ezPyCrypto and yawPyCrypto. Both can import public keys, but only if they were exported by themselves (I don't even think they can import each others keys).
So, 3 crypto packages, two of them wrapping the third, and still no support for reading the most standard representation of public keys, geez.
As I don't give up easily I track down another package, pyOpenSSL. This time the documentation actually mentions ASN.1, which is related to the public key format (the details are boring and I ignore them, as this is getting long enough without them). Oh the joy... didn't last long. PyOpenSSL seems to focus on SSL (Secure Socket Layer) and certificates, and it doesn't seem to do simple thing I want it to do; verifying a signature.
To be fair, I should add that PyOpenSSL is only in version 0.6, and the website states that alot is missing from it. It still counts as a Crypto package, so that's 4 of them, and none can do what I want.
When I was about to give up, the most amazing thing happened. By pure luck I stumbled across POW (aka. Python OpenSSL Wrappers). After fidling with it for half an hour or so (mostly fighting to make it compile), I had written a little script that could verify a signature. POW isn't perfect, it has a strange bug, that only allows it to read one (PEM) of the two formats (the other is ASN.1) public keys are found in. But as PEM is basically ASN.1 base64 encoded, I can easily do the conversion to PEM.
Luckily this ended good and I can now finally return to the problem at hand; implementing domain keys. I'm just puzzled by the fact that there are at least 5 packages providing crypto stuff for Python coders. It's a jungle out there, don't get lost!