I'd been tasked with implementing AES encryption on a project. The reference code had been written in Java - it needed to be converted to Python. While organizing my notes to write a SO question, I accidentally stumbled across the answer! In the hopes that someone else finds this useful, I'm going to mention my notes here as a 'share your knowledge' kind of question.
The requirement was to encrypt a message using AES with a given key. Here is a simplified look at the reference code (in Java),
import javax.crypto.Cipher; import javax.crypto.spec.SecretKeySpec; import org.apache.commons.codec.binary.Base64; import sun.misc.BASE64Encoder; public class EncryptAES { private static String toHexString(byte[] data) { StringBuffer buf = new StringBuffer(); for (int i = 0; i < data.length; ++i) { String s = Integer.toHexString(data[i] & 0XFF); buf.append((s.length() == 1) ? ("0" + s) : s); } return buf.toString(); } public static String encrypt(String input, String key) { byte[] crypted = null; try { SecretKeySpec skey = new SecretKeySpec(key.getBytes(), "AES"); Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); cipher.init(Cipher.ENCRYPT_MODE, skey); crypted = cipher.doFinal(input.getBytes()); final String encryptedString = toHexString(Base64.encodeBase64(crypted)); return encryptedString; } catch (Exception e) { System.out.println(e.toString()); } return new String(new BASE64Encoder().encode(crypted)); } public static void main(String[] args) { String key = args[0]; String plaintext = args[1]; System.out.println("KEY = " + key); System.out.println("PLAINTEXT = " + plaintext); System.out.println("CIPHER = " + EncryptAES.encrypt(plaintext, key)); } } If you save the above as 'EncryptAES.java' and keep the library file commons-codec-1.7.jar in the same directory, you can compile it with the following command,
$ javac EncryptAES.java -cp commons-codec-1.7.jar Here is the output when running the program a few times,
$ java -cp "commons-codec-1.7.jar:." EncryptAES ddddffffeeeerrrr message KEY = ddddffffeeeerrrr MESSAGE = message CRYPTO = 397a59594d35524e6b6a463253706f41467668646b773d3d $ $ java -cp "commons-codec-1.7.jar:." EncryptAES qqqqwwwweeeerrrr ThisIsAVeryImportantMessage KEY = qqqqwwwweeeerrrr PLAINTEXT = ThisIsAVeryImportantMessage CIPHER = 56536a384d667736756b595a394e396b6d504d736231444673375250736d5639596f637072792f6e4b424d3d $ Looking around, I found the Python Crypto library. Here is one of the early attempts I had to replicate the above output,
#!/usr/bin/python import sys from Crypto.Cipher import AES if __name__ == '__main__': key = sys.argv[1] plaintext = sys.argv[2] print 'KEY = ' + key print 'PLAINTEXT = ' + plaintext encobj = AES.new(key, AES.MODE_ECB) ciphertext = encobj.encrypt(plaintext) print 'CIPHER = ' + ciphertext.encode('hex') This doesn't quite get what I need. Instead, I get an error message about the input string needing to be a multiple of 16 in length. Which brings me to my next attempt,
#!/usr/bin/python import sys from Crypto.Cipher import AES # ref: https://gist.github.com/crmccreary/5610068 BS = 16 pad = lambda s: s + (BS - len(s) % BS) * chr(BS - len(s) % BS) unpad = lambda s : s[0:-ord(s[-1])] class AESCipher: def __init__( self, key ): """ Requires hex encoded param as a key """ self.key = key.decode("hex") def encrypt( self, raw ): """ Returns hex encoded encrypted value! """ raw = pad(raw) cipher = AES.new(self.key, AES.MODE_ECB) return cipher.encrypt(raw).encode("hex") if __name__ == '__main__': key = sys.argv[1] plaintext = sys.argv[2] print 'KEY = ' + key print 'PLAINTEXT = ' + plaintext # ref: http://stackoverflow.com/a/16882092 hex_key = "".join("{:02x}".format(ord(c)) for c in key) encryptor = AESCipher(hex_key) ciphertext = encryptor.encrypt(plaintext) print 'CIPHER = ' + ciphertext I'm not really sure what to make of the output, to be honest,
$ python EncryptAES2.py ddddffffeeeerrrr message KEY = ddddffffeeeerrrr PLAINTEXT = message CIPHER = f7361833944d9231764a9a0016f85d93 $ I tried a lot of things - different encryption modes, blogs, SO questions, and had given up on finding a solution on my own. It was at this point that I decided to collect my notes and ask a question here. Now, there wouldn't be much of a point if I didn't list my attempts, so I started organizing them in a folder and labelled them EncryptAES.py, EncryptAES2.py .. etc.