I am getting a C# AES Decryption "INPUT DATA IS NOT A COMPLETE BLOCK" error, but only on decryption?

Viewed 100

I am having a hard time decrypting files using the same mechanisms that I am encrypting them with. Whenever i try to decrypt i am getting a error about how the input data is not a complete doc. Below I am going to put two files, the service provider and what is consuming it. If anyone knows why it would be giving this behavior please let me know. I have not worked with this, that much, I am adopting another project that uses it pretty heavily. I am mainly trying to figure out if it is a configuration issue or not, I am not having a problem with the encryption side of things though, which is also below. I have spent several hours trying to figure this out.

This is the service that I am using to encrypt and decrypt.

using System;
using System.Security;
using System.Security.Cryptography;

namespace Shared
{
    // AES (Advanced Encryption Standard) 256-bit Cryptography
    public class CryptoProvider
    {
        RijndaelManaged aes;
        byte[] defaultKey = { 8, 190, 2, 33, 68, 88, 11, 200, 211, 35, 68, 23, 60, 24, 223, 67, 11, 190, 165, 33, 68, 88, 11, 200, 245, 35, 68, 23, 60, 24, 223, 67 };
        byte[] defaultIv = { 11, 190, 165, 33, 68, 88, 11, 200, 245, 35, 68, 23, 60, 24, 223, 67 };

        public CryptoProvider(byte[] key = null, CipherMode mode = CipherMode.CBC) {
            aes = new RijndaelManaged();
            aes.Mode = mode;
            aes.Padding = PaddingMode.PKCS7;
            aes.Key = key ?? defaultKey;
            aes.IV = defaultIv;
        }

        public static byte[] GenerateKey() {
            var e = new RijndaelManaged();
            e.GenerateKey();
            return e.Key;
        }

        public byte[] IV {
            get { return aes.IV; }
        }

        public ICryptoTransform CreateEncrypter() {
            return aes.CreateEncryptor(aes.Key, aes.IV);
        }

        public ICryptoTransform CreateDecrypter() {
            return aes.CreateDecryptor(aes.Key, aes.IV);
        }
    }
}

This is the file is using the service above.

public static void DecryptFile(string file, byte[] encryptionKey, bool clientEcbEnabled) {
        var newFileName = file + ".tmp";
        var ext = Path.GetExtension(file).ToLower();

        using (var fin = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read)) {
            using (var fout = new FileStream(newFileName, FileMode.OpenOrCreate, FileAccess.Write)) {
                fout.SetLength(0);

                var mode = Cryptographer.GetCipherMode(file, clientEcbEnabled);
                var cp = new CryptoProvider(encryptionKey, mode);
                var buffer = new byte[1048576];

                if (mode == CipherMode.CBC) {
                    // write the original length of the file
                    fout.Write(BitConverter.GetBytes((int)fin.Length), 0, 4);
                }

                using (var cs = new CryptoStream(fout, cp.CreateDecrypter(), CryptoStreamMode.Write)) {
                    int bytesRead = 0;

                    while ((bytesRead = fin.Read(buffer, 0, 1048576)) != 0) {
                        cs.Write(buffer, 0, bytesRead);

                        // report progress to event if wired up
                        if (EncryptProgress != null) EncryptProgress(bytesRead);
                    }
                    cs.FlushFinalBlock();
                }
            }
        }

        File.Delete(file);
        File.Move(newFileName, file);
    }

This is the working encryption method.

public static void EncryptFile(string file, byte[] encryptionKey, bool clientEcbEnabled) {
            var newFileName = file + ".tmp";
            var ext = Path.GetExtension(file).ToLower();

            using (var fin = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read)) {
                using (var fout = new FileStream(newFileName, FileMode.OpenOrCreate, FileAccess.Write)) {
                    fout.SetLength(0);

                    var mode = Cryptographer.GetCipherMode(file, clientEcbEnabled);
                    var cp = new CryptoProvider(encryptionKey, mode);
                    var buffer = new byte[4096];

                    if (mode == CipherMode.CBC) {
                        // write the original length of the file
                        fout.Write(BitConverter.GetBytes((int)fin.Length), 0, 4);
                    }

                    using (var cs = new CryptoStream(fout, cp.CreateEncrypter(), CryptoStreamMode.Write)) {
                        int bytesRead = 0;

                        while ((bytesRead = fin.Read(buffer, 0, 4096)) != 0) {
                            cs.Write(buffer, 0, bytesRead);

                            // report progress to event if wired up
                            if (EncryptProgress != null) EncryptProgress(bytesRead);
                        }

                        cs.FlushFinalBlock();
                    }
                }
            }

            File.Delete(file);
            File.Move(newFileName, file);
        }
1 Answers

You write (in case of the CBC mode) the size of the input file into the first 4 bytes of the created file, both when encrypting and decrypting. When decrypting, therefore, the first 4 bytes must be ignored, e.g. with

public static void DecryptFile(string file, byte[] encryptionKey, bool clientEcbEnabled)
{
    ...
    using (var fin = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read)
    {
        fin.seek(4, SeekOrigin.Begin);
        ...

Otherwise these 4 bytes are interpreted as ciphertext, which leads to the decryption error. But with this change decryption works on my machine.

Since you also write the file size at the beginning when decrypting, especially binary files can possibly no longer be read by their respective applications (e.g. doc files from Word).

Writing the file sizes is not necessary for the encryption/decryption process (also not for the CBC mode) and could be omitted (the data is not used in the code either). But maybe you have other reasons for this.

Also, with regard to your static IV, note that for security reasons it is important for AES/CBC to use a key/IV pair only once.

Related