2

I'm having a bit of an issue encrypting / decrypting cross languages.

Here is my python code to encrypt some text:

class AESCipher:
    def __init__( self, key, iv ):
        self.key = base64.b64decode(key)
        self.iv = base64.b64decode(iv)
    def encrypt( self, raw ):

        BS = 16
        pad = lambda s: s + (BS - len(s) % BS) * chr(BS - len(s) % BS) 
        raw = pad(raw)


        cipher = AES.new( self.key, AES.MODE_CBC, self.iv)
        res = self.iv + cipher.encrypt( raw )
        return base64.b64encode(res) 

    def decrypt( self, enc ):
        enc = base64.b64decode(enc)
        unpad = lambda s : s[:-ord(s[len(s)-1:])]
        cipher = AES.new(self.key, AES.MODE_CBC, self.iv )
        return unpad(cipher.decrypt( enc[16:] ))

crypt = AESCipher("key", "iv")
print "{0}".format(crypt.encrypt("Hallow"))

And C# Decrypting:

public static string DecryptStringFromBase64(string base64String)
{
    byte[] bytes = Decrypt(Convert.FromBase64String(base64String));
    var utf8 = Encoding.UTF8.GetString(bytes);
    return utf8;
}

public static byte[] Decrypt(byte[] bytes)
{
    AesManaged algorithm = new AesManaged();
    algorithm.IV = Convert.FromBase64String("IV");
    algorithm.Key = Convert.FromBase64String("KEY");

    byte[] ret = null;
    using (var decryptor = algorithm.CreateDecryptor())
    {
        using (MemoryStream msDecrypted = new MemoryStream())
        {
            using (CryptoStream csEncrypt = new CryptoStream(msDecrypted, decryptor, CryptoStreamMode.Write))
            {
                csEncrypt.Write(bytes, 0, bytes.Length);
            }
            ret = msDecrypted.ToArray();
        }
    }
    return ret;
}

However the decrypted value always comes out incorrectly like this:

decrypted text with garbage at the start

I think this has something to do with the padding, can anybody suggest how to get around this?

1 Answer 1

4

The problem is not the padding. It's the IV that you haven't sliced off. The IV doesn't have to be secret, but it should be randomly generated for each encryption. Since you're already including the IV into the ciphertext in Python, you have to use it in C# during decryption (of course the decryption should be done on the actual ciphertext excluding the IV):

C#

public static string DecryptStringFromBase64(string base64String)
{
    byte[] bytes = Decrypt(Convert.FromBase64String(base64String));
    var utf8 = Encoding.UTF8.GetString(bytes);
    return utf8;
}

public static byte[] Decrypt(byte[] bytes)
{
    byte[] iv = new byte[16]; // change
    Array.copy(bytes, iv, 16); // change
    AesManaged algorithm = new AesManaged();
    algorithm.IV = iv; // change
    algorithm.Key = Convert.FromBase64String("KEY");

    byte[] ret = null;
    using (var decryptor = algorithm.CreateDecryptor())
    {
        using (MemoryStream msDecrypted = new MemoryStream())
        {
            using (CryptoStream csEncrypt = new CryptoStream(msDecrypted, decryptor, CryptoStreamMode.Write))
            {
                csEncrypt.Write(bytes, 16, bytes.Length - 16); // change
            }
            ret = msDecrypted.ToArray();
        }
    }
    return ret;
}

The Python code is also not without its issues. You don't need to pass in the IV, because you're putting it into the ciphertext. So you can just create a new random IV. Additionally, the unpad function is not correct (or at least needlessly complicated).

class AESCipher:
    def __init__( self, key):
        self.key = base64.b64decode(key)

    def encrypt( self, raw ):

        BS = 16
        pad = lambda s: s + (BS - len(s) % BS) * chr(BS - len(s) % BS) 
        raw = pad(raw)

        iv = Random.new().read(BS)

        cipher = AES.new(self.key, AES.MODE_CBC, iv)
        res = iv + cipher.encrypt( raw )
        return base64.b64encode(res) 

    def decrypt( self, enc ):
        enc = base64.b64decode(enc)
        BS = 16
        iv = enc[:BS]
        unpad = lambda s : s[:-ord(s[-1])]
        cipher = AES.new(self.key, AES.MODE_CBC, iv )
        return unpad(cipher.decrypt( enc[BS:] ))

crypt = AESCipher("key", "iv")
print "{0}".format(crypt.encrypt("Hallow"))

Don't forget to authenticate your ciphertexts. Otherwise, a padding oracle attack might be launched against your system which enables an attack to decrypt every ciphertext with multiple online queries.

You can use an authenticated modes like GCM or EAX, or employ an encrypt-then-MAC scheme with a strong MAC like HMAC-SHA256.

Sign up to request clarification or add additional context in comments.

3 Comments

I gave this a shot and the line csEncrypt.Write(bytes, 16, bytes.Length); // change throws the error: An unhandled exception of type 'System.ArgumentException' occurred in mscorlib.dll Additional information: Offset and length were out of bounds for the array or count is greater than the number of elements from index to the end of the source collection.
Sorry, I thought the third argument would be the end index, but it is a count. It should work now.
Worked great for me thanks, only had to change algorithm.Key = Convert.FromBase64String("KEY"); to algorithm.Key = Encoding.UTF8.GetBytes(key);

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.