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.