Alright, another post for the same topic: How to generate AquaticPrime licenses in python with PyCrypto (also for the Google App Engine in my case). This took me quite a long time (8h), because debugging crypto is very complicated (the wrong result is returned, so what now?). I actually had to learn how to encrypt using RSA (quite simple actually).

Note that you should understand my article on this topic for Java, because I won’t got into that detail this time.

The imports are pretty similar to Java:

from Crypto.Hash import SHA
from Crypto.PublicKey import RSA
import base64

Note that you have to install PyCrypto for this to work. If you’re using Google App Engine, you might have a few issues with that, since the directory the library is installed to is ignored by the App Engine environment. For now I moved the library into the app directory, but I’ll have to fix that eventually.

The public and private keys can be stored as a regular integer in python:

privateKey = int("...", 16)
publicKey = int("...", 16)

This time around, python even recognizes the ’0x’ prefix, so you don’t have to remove it (but it works fine without).

The next line is the one that took me around 7h to come up with:

RSAkey = RSA.construct((self.publicKey, 3, self.privateKey))

The parameters are ‘n’, ‘e’, ‘d’, ‘p’, ‘q’, ‘u’ (I’m only supplying the first three here, the others are not needed). ‘n’ is the public key, ‘e’ is the exponent (constant 3 in AquaticPrime) and ‘d’ the private key. When I reverse-engineered the PyCrypto-code, I discovered that the right way to get my signature is actually to decrypt the message. Encrypting only uses the public key, decrypting the private key.

First, I have to hash the sorted values from the dictionary using SHA1:

hash = SHA.new("user@email.comUserCockpit 2 Single User".encode("utf-8")).digest()

I used some placeholders for the fields here, they have to be filled in for proper licenses.

Next up is decryption (signing). However, openssl uses PKCS#1 padding. Java does that automatically, PyCrypto does not, so we have to do that manually:

signature = RSAkey.decrypt('\x00\x01' + ('\xff' * 105) + '\x00' + hash)

That’s nearly the same padding code as the PHP implementation, except for the string multiplication, which was a for-loop (yay for that shortcut).

So, all that’s left here is the base64 encoding, which is quite easy again:

base64.b64encode(signature)

That’s it! All that’s left is creating the plist string again, but that’s the same as in the Java code and not that exciting.

This is actually quite easy once you have the code for it :)

« »