Hugo Assanti
Hugo Assanti

Reputation: 421

Encrypting and Decrypting a file with AES generates a broken file

I'm trying to encrypt and decrypt a file using AES. The problem that I have is that when the file gets decrypted, it is broken and you can't open it. The original file has a length of 81.970 bytes and the decrypted file has a length of 81.984 bytes...so there are 14 bytes added for some reason. The problem could be in the way the file gets encrypted but I don't know what I'm doing wrong.

What am I missing here? Could it be the way I'm processing the password, the iv and the padding?

Thanks for your time!

This is the code I use to encrypt:

    private AesManaged aesManaged;
    private string filePathToEncrypt;

    public Encrypt(AesManaged aesManaged, string filePathToEncrypt)
    {
        this.aesManaged = aesManaged;
        this.filePathToEncrypt = filePathToEncrypt;
    }

    public void DoEncryption()
    {
        byte[] cipherTextBytes;
        byte[] textBytes = File.ReadAllBytes(this.filePathToEncrypt);

        using(ICryptoTransform encryptor = aesManaged.CreateEncryptor(aesManaged.Key, aesManaged.IV))
        using (MemoryStream ms = new MemoryStream())
        using (CryptoStream cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
        {
            cs.Write(textBytes, 0, textBytes.Length);
            cs.FlushFinalBlock();
            cipherTextBytes = ms.ToArray();
        }

        File.WriteAllBytes("EncryptedFile.aes", cipherTextBytes);
    }

This is the code I use to decrypt:

    private AesManaged aesManaged;
    private string filePathToDecrypt;

    public Decrypt(AesManaged aesManaged, string filePathToDecrypt)
    {
        this.aesManaged = aesManaged;
        this.filePathToDecrypt = filePathToDecrypt;
    }
    public void DoDecrypt()
    {
        byte[] cypherBytes = File.ReadAllBytes(this.filePathToDecrypt);
        byte[] clearBytes = new byte[cypherBytes.Length];


        ICryptoTransform encryptor = aesManaged.CreateDecryptor(aesManaged.Key, aesManaged.IV);
        using (MemoryStream ms = new MemoryStream(cypherBytes))
        using (CryptoStream cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Read))
        {
            cs.Read(clearBytes, 0, clearBytes.Length);
            clearBytes = ms.ToArray();
        }

        File.WriteAllBytes("DecryptedFile.gif", clearBytes);
    }

And here is how I call the functions:

        string filePathToEncrypt = "dilbert.gif";
        string filePathToDecrypt = "EncryptedFile.aes";

        string password = "Password";
        string passwordSalt = "PasswordSalt";

        Rfc2898DeriveBytes deriveBytes = new Rfc2898DeriveBytes(password, Encoding.ASCII.GetBytes(passwordSalt));
        var aesManaged = new AesManaged
        {

            Key = deriveBytes.GetBytes(128 / 8),
            IV = deriveBytes.GetBytes(16),
            Padding = PaddingMode.PKCS7
        };

        Console.WriteLine("Encrypting File...");
        var encryptor = new Encrypt(aesManaged, filePathToEncrypt);
        encryptor.DoEncryption();
        Thread.Sleep(300);

        Console.WriteLine("Decrypting File...");
        var decryptor = new Decrypt(aesManaged, filePathToDecrypt);
        decryptor.DoDecrypt();
        Thread.Sleep(300);

Upvotes: 0

Views: 240

Answers (2)

Kurokawa Masato
Kurokawa Masato

Reputation: 136

The answer may be very simple. I don't see where do u try to choose a cipher mode, so by default it probably takes CBC, as IV was inited. Then, 81.970 are padded by 14 bytes, to be divisible by 32. So when it happens, the memory you allocated was just 81.970, so the padding bytes doesn't write correctly, cause of some sort of memory leak, and when decrypt is started, unpadding doesn't work correctly.

Upvotes: 0

xanatos
xanatos

Reputation: 111860

Try with:

public void DoEncryption()
{
    byte[] cipherBytes;
    byte[] textBytes = File.ReadAllBytes(this.filePathToEncrypt);

    using (ICryptoTransform encryptor = aesManaged.CreateEncryptor(aesManaged.Key, aesManaged.IV))
    using (MemoryStream input = new MemoryStream(textBytes))
    using (MemoryStream output = new MemoryStream())
    using (CryptoStream cs = new CryptoStream(output, encryptor, CryptoStreamMode.Write))
    {
        input.CopyTo(cs);
        cs.FlushFinalBlock();
        cipherBytes = output.ToArray();
    }

    File.WriteAllBytes("EncryptedFile.aes", cipherBytes);
}

and

public void DoDecrypt()
{
    byte[] cypherBytes = File.ReadAllBytes(this.filePathToDecrypt);
    byte[] textBytes;

    using (ICryptoTransform decryptor = aesManaged.CreateDecryptor(aesManaged.Key, aesManaged.IV))
    using (MemoryStream input = new MemoryStream(cypherBytes))
    using (MemoryStream output = new MemoryStream())
    using (CryptoStream cs = new CryptoStream(input, decryptor, CryptoStreamMode.Read))
    {
        cs.CopyTo(output);
        textBytes = output.ToArray();
    }

    File.WriteAllBytes("DecryptedFile.gif", textBytes);
}

Note that the code could be modified to not use temporary byte[] and read/write directly to input/output streams.

In general you can't desume the length of the plaintext from the length of the cyphertext, so this line:

new byte[cypherBytes.Length]

was totally wrong.

And please, don't use Encoding.ASCII in 2016. It is so like previous century. Use Encoding.UTF8 to support non-english characters.

Upvotes: 1

Related Questions