1
votes

I just started with Cryptography in C# an tried to first encrypt and then decrypt a file. But during the decryption, in the function private static byte[] Decrypt(byte[] inputBuffer) , at byte[] outputBuffer = transform.TransformFinalBlock(inputBuffer, 0, inputBuffer.Length); I am getting the following Exception: System.Security.Cryptography.CryptographicException: "Invalid data". But why is that?

This is the function to read the file:

private static void DecryptFile()
{
    byte[] buffer = new byte[4096];
    byte[] decryptionBuffer = new byte[4096];
    int bytesRead = 0;
    using (FileStream inputStream = File.OpenRead(Environment.CurrentDirectory + "\\Encrypted.txt"))
    {
        using (FileStream outputStream = File.Create(Environment.CurrentDirectory + "\\Decrypt.mp3"))
        {
            while ((bytesRead = inputStream.Read(buffer, 0, buffer.Length)) > 0)
            {
                decryptionBuffer = Decrypt(buffer);
                outputStream .Write(decryptionBuffer, 0, decryptionBuffer.Length);
            }
        }
        Console.WriteLine("Done.");
    }
}

This is the function to decrypt the file, the key and the initialization vector:

private static byte[] key = new byte[8] { 1, 2, 3, 4, 5, 6, 7, 8 };
private static byte[] iv = new byte[8] { 1, 2, 3, 4, 5, 6, 7, 8 };

private static byte[] Decrypt(byte[] inputBuffer)
{
    SymmetricAlgorithm algorithm = DES.Create();
    ICryptoTransform transform = algorithm.CreateDecryptor(key, iv);
    //here the exception is triggered
    byte[] outputBuffer = transform.TransformFinalBlock(inputBuffer, 0, inputBuffer.Length);
    return outputBuffer;
}

this is how the file was encrypted:

private static void EncryptFile()
{
    byte[] buffer = new byte[4096];
    byte[] enryptionBuffer = new byte[4096];
    int bytesRead = 0;
    using (FileStream inputStream = File.OpenRead(Environment.CurrentDirectory + "\\Test.txt"))
    {
        using (FileStream outputStream = File.Create(Environment.CurrentDirectory + "\\Encrypted.txt"))
        {
            while ((bytesRead = inputStream.Read(buffer, 0, buffer.Length)) > 0)
            {
                encryptionBuffer = Encrypt(buffer);
                outputStream .Write(encryptionBuffer, 0, encryptionBuffer.Length);
            }
        }
        Console.WriteLine("Done.");
    }
}

//Key and initialization vector are the same
private static byte[] key = new byte[8] { 1, 2, 3, 4, 5, 6, 7, 8 };
private static byte[] iv = new byte[8] { 1, 2, 3, 4, 5, 6, 7, 8 };

private static byte[] Encrypt(byte[] inputBuffer)
{
    SymmetricAlgorithm algorithm = DES.Create();
    ICryptoTransform transform = algorithm.CreateEncryptor(key, iv);
    byte[] outputBuffer = transform.TransformFinalBlock(inputBuffer, 0, inputBuffer.Length);
    return outputBuffer;
}
2
How was the file encrypted? Are you certain that the same IV's and key were used in the encryption? - S.L. Barth
You are decrypting each block afresh. That is unlikely to be how it was encrypted. - bommelding
The properties that were used to Encrypt are not the same as the ones you are using to Decrypt. DES has different size blocks and options and you will get an exception if the options aren't the same as the ones used to Encrypt. - jdweng
@S.L.Barth yes, I've just added how I encrypted it. - Kravag
I would not call single DES encryption anymore, personally. Nowadays it is obfuscation at best. - Maarten Bodewes

2 Answers

2
votes

Your encryption code produces larger output buffers than your input buffers. If you independently encrypt 4096 bytes at a time, it produces 4104 bytes of output1. If you wish to continue to independently encrypt each block of your file, you need to change your decryption code to use 4104 byte buffers when it's working in chunks.

Ideally though, this "encrypt each block separately" isn't a requirement. If that's the case, create your transform object once, not once each time through your loop. And use Transform rather than TransformFinalBlock up until you know you've reached the end of the file (pay attention that they return very different things however).

You're also ignoring bytesRead which tells you how much of your buffer is filled with useful data. You need to use that as well and don't make your final encryption round be x many bytes which are the last bytes of the file and bufferSize - x bytes of the previous block of data from the file.

I would probably look to instead create a CryptoStream that wraps one of your FileStream objects and then use Stream.CopyTo or moral equivalents to do this work. Let libraries worry about managing buffers, loops, etc.

And finally, ideally, you recognize that it's 2019 and so very far from appropriate to be writing new code that uses DES for encryption2


1This program, if you set a breakpoint on the Console.ReadLine line, has c containing 4104 bytes:

using System;
using System.Security.Cryptography;
using System.Threading.Tasks;

class Program
{
    static async Task Main(string[] args)
    {
        var b = new byte[4096];
        var c = Encrypt(b);
        Console.ReadLine();
    }

    private static byte[] key = new byte[8] { 1, 2, 3, 4, 5, 6, 7, 8 };
    private static byte[] iv = new byte[8] { 1, 2, 3, 4, 5, 6, 7, 8 };

    private static byte[] Encrypt(byte[] inputBuffer)
    {
        SymmetricAlgorithm algorithm = DES.Create();
        ICryptoTransform transform = algorithm.CreateEncryptor(key, iv);
        byte[] outputBuffer = transform.TransformFinalBlock(
                                inputBuffer,
                                0,
                                inputBuffer.Length);
        return outputBuffer;
    }
}

2So my EnryptFile in its entirety would be:

private static void EncryptFile()
{
    using (var inputStream = File.OpenRead(Environment.CurrentDirectory + "\\Test.txt"))
    using (var outputStream = File.Create(Environment.CurrentDirectory + "\\Encrypted.txt"))
    using (var aes = Aes.Create())
    using (var cStream = new CryptoStream(
                               inputStream,
                               aes.CreateEncryptor(key, iv),
                               CryptoStreamMode.Read))
    {
        cStream.CopyTo(outputStream);
    }
}

Or an async variant which which uses await cStream.CopyToAsync(outputStream); as it's innermost statement. DecryptFile would be similarly simplified.

0
votes

I was facing the same problem then I created a customized function for encryption/decryption and using this function also supports large files because we are reading and writing a file chunk by chunk. there is an EncryptMode that is what do you want by this method like if you want to encryption then send _mode as _mode.ENCRYPT and if you want decryption then send _mode as _mode.DECRYPT.

private enum EncryptMode { ENCRYPT, DECRYPT };

public void encryptDecryptChunkByChunk(string _inputPath, string _outputPath, string _encryptionKey, EncryptMode _mode, string _initVector) {

        string _out = "";// output string
        //_encryptionKey = MD5Hash (_encryptionKey);
        _pwd = Encoding.UTF8.GetBytes(_encryptionKey);
        _ivBytes = Encoding.UTF8.GetBytes(_initVector);

        int len = _pwd.Length;
        if (len > _key.Length)
        {
            len = _key.Length;
        }
        int ivLenth = _ivBytes.Length;
        if (ivLenth > _iv.Length)
        {
            ivLenth = _iv.Length;
        }

        Array.Copy(_pwd, _key, len);
        Array.Copy(_ivBytes, _iv, ivLenth);
        _rcipher.Key = _key;
        _rcipher.IV = _iv;

        if (_mode.Equals(EncryptMode.ENCRYPT))
        {
            //encrypt
            using (FileStream fs = new FileStream(_inputPath, FileMode.Open, FileAccess.Read))
            {
                using (BinaryReader br = new BinaryReader(fs, new ASCIIEncoding()))
                {
                    System.IO.StreamWriter file = new System.IO.StreamWriter(_outputPath);
                    try
                    {
                        byte[] chunk;

                        chunk = br.ReadBytes(CHUNK_SIZE);
                        while (chunk.Length > 0)
                        {
                            var base641 = Convert.ToBase64String(chunk);
                            //DumpBytes(chunk, chunk.Length);
                            chunk = br.ReadBytes(CHUNK_SIZE);
                            var base64 = Convert.ToBase64String(chunk);

                            byte[] plainText = _rcipher.CreateEncryptor().TransformFinalBlock(_enc.GetBytes(base641), 0, base641.Length);
                            var bas64Encrypted = Convert.ToBase64String(plainText);
                            //fsCrypt.Write(bas64Encrypted);
                            file.WriteLine(bas64Encrypted);
                        }
                        file.Close();
                    }
                    catch (Exception ex)
                    {
                        file.Close();
                    }
                }
            }
        }
        if (_mode.Equals(EncryptMode.DECRYPT))
        {
            FileStream fsOut = new FileStream(_outputPath, FileMode.OpenOrCreate, FileAccess.Write);
            try
            {
                foreach (string line in File.ReadLines(_inputPath))
                {
                    // Process your line here....
                    var p = line;
                    var x2 = Convert.FromBase64String(p);
                    byte[] plainText = _rcipher.CreateDecryptor().TransformFinalBlock(x2, 0, x2.Length);
                    var y1 = _enc.GetString(plainText);
                    var y2 = Convert.FromBase64String(y1);
                    fsOut.Write(y2, 0, y2.Length);
                }
                fsOut.Close();
            }
            catch (Exception ex)
            {
                fsOut.Close();
            }
        }
        _rcipher.Dispose();
    }