Search code examples
pythonaespycrypto

How come I can't decrypted my AES encrypted message on someone elses AES decryptor?


from Crypto.Cipher import AES
import os

key = 'mysecretpassword'
iv = os.urandom(16)
plaintext1 = 'Secret Message A'
encobj = AES.new(key, AES.MODE_CBC, iv)
ciphertext1 = encobj.encrypt(plaintext1) 
encryptedText = ciphertext1.encode('base64')
print encryptedText
decobj = AES.new(key, AES.MODE_CBC, iv)
print decobj.decrypt(ciphertext1)

I copied the printed value of encryptedText and the key from my code and pasted to the websites below.

http://www.everpassword.com/aes-encryptor

http://www.nakov.com/blog/2011/12/26/online-aes-encryptor-decryptor-javascript/

I would expected it to be able to decrypt my cipher, but it doesn't. Thus I must be using pycrypto wrong. How do I fix this? The two sites can both encrypt and decrypt between each other, but mines can't. Both the websites do indeed use CBC mode.


Solution

  • If you look at the page source for the website in question, you will see that it uses gibberish-aes javascript library. To see whet you have to do to make it work, you have to study what it does.

    Looking through its source code, it seems to use a random salt for encryption. That, prepended by the string Salted__ forms the beginning of the cyphertext before it is base64 encoded.

    randArr = function(num) {
        var result = [], i;
        for (i = 0; i < num; i++) {
            result = result.concat(Math.floor(Math.random() * 256));
        }
        return result;
    },
    

    and

    enc = function(string, pass, binary) {
            // string, password in plaintext
            var salt = randArr(8),
            pbe = openSSLKey(s2a(pass, binary), salt),
            key = pbe.key,
            iv = pbe.iv,
            cipherBlocks,
            saltBlock = [[83, 97, 108, 116, 101, 100, 95, 95].concat(salt)];
            string = s2a(string, binary);
            cipherBlocks = rawEncrypt(string, key, iv);
            // Spells out 'Salted__'
            cipherBlocks = saltBlock.concat(cipherBlocks);
            return Base64.encode(cipherBlocks);
        },
    

    For decryption, it uses picks the random portion of the salt out of the beginning of the cyphertext after base64 decoding (the first slice operator):

    dec = function(string, pass, binary) {
        // string, password in plaintext
        var cryptArr = Base64.decode(string),
        salt = cryptArr.slice(8, 16),
        pbe = openSSLKey(s2a(pass, binary), salt),
        key = pbe.key,
        iv = pbe.iv;
        cryptArr = cryptArr.slice(16, cryptArr.length);
        // Take off the Salted__ffeeddcc
        string = rawDecrypt(cryptArr, key, iv, binary);
        return string;
    },
    

    The missing piece now is the openSSLkey function:

    openSSLKey = function(passwordArr, saltArr) {
        // Number of rounds depends on the size of the AES in use
        // 3 rounds for 256
        // 2 rounds for the key, 1 for the IV
        // 2 rounds for 128
        // 1 round for the key, 1 round for the IV
        // 3 rounds for 192 since it's not evenly divided by 128 bits
        var rounds = Nr >= 12 ? 3: 2,
        key = [],
        iv = [],
        md5_hash = [],
        result = [],
        data00 = passwordArr.concat(saltArr),
        i;
        md5_hash[0] = GibberishAES.Hash.MD5(data00);
        result = md5_hash[0];
        for (i = 1; i < rounds; i++) {
            md5_hash[i] = GibberishAES.Hash.MD5(md5_hash[i - 1].concat(data00));
            result = result.concat(md5_hash[i]);
        }
        key = result.slice(0, 4 * Nk);
        iv = result.slice(4 * Nk, 4 * Nk + 16);
        return {
            key: key,
            iv: iv
        };
    },
    

    So basically you have to translate the openSSLKey function to Python and feed it your password and salt. That creates a (key, iv) tuple. Use those to encrypt your data. Prepend the string Salted__ and the salt to the ciphertext before encoding it with base64. Then it should work, I think.