Question

I'm quite new to C# and encryption so please have patience with me. I want to save some binary data ("objects" - in fact mostly only parts of objects, thus I can't / don't use serialization, BinaryWriter and similar) and I want to encrypt it in memory and then write it using FileStream. At first I wanted to use some sort of Xor but I didn't know that it is so easy to break, now I changed code to use Aes.

The thing is that I will have some relatively large files and quite often I will only need to change or read like 32 bytes of data. Thus I must be capable of encrypting only one chunk of data and also capable of decrypting only desired chunks of data. For now I came up only with the following solution.

When saving data I loop through all the data and inside the loop encrypt a chunk of data and write it to a file. While reading, I have loop which reads chunks of data and inside the loop I have to declare the decryptor, which I find very inefficient.

Here's the code for encryption & saving:

        //setup file stream for saving data
        FileStream fStream = new FileStream(fileName, FileMode.OpenOrCreate, FileAccess.Write, FileShare.Read, 1024, false);

        //setup encryption (AES)
        SymmetricAlgorithm aes = Aes.Create();
        byte[] key = { 145, 12, 32, 245, 98, 132, 98, 214, 6, 77, 131, 44, 221, 3, 9, 50 };
        byte[] iv = { 15, 122, 132, 5, 93, 198, 44, 31, 9, 39, 241, 49, 250, 188, 80, 7 };
        aes.Padding = PaddingMode.None;
        ICryptoTransform encryptor = aes.CreateEncryptor(key, iv);

        foreach(....)
        {
           //data manipulation

           //encryption
           MemoryStream m = new MemoryStream();
           using (Stream c = new CryptoStream(m, encryptor, CryptoStreamMode.Write))
              c.Write(data, 0, data.Length);
           byte[] original = new byte[32];
           original = m.ToArray();
           fStream.Write(original, 0, original.Length);
        }

The key and iv is hardcoded just to enable easier debugging and solving problems, once this will work I'll change the way key and iv are generated.

Here's the code for reading & decryption: FileStream fStream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, false);

            //setup encryption (AES)
            SymmetricAlgorithm aes = Aes.Create();
            byte[] key = { 145, 12, 32, 245, 98, 132, 98, 214, 6, 77, 131, 44, 221, 3, 9, 50 };
            byte[] iv = { 15, 122, 132, 5, 93, 198, 44, 31, 9, 39, 241, 49, 250, 188, 80, 7 };
            aes.Padding = PaddingMode.None;

            //reading
            while (numBytesToRead > 0)
            {
                byte[] original = new byte[32];
                byte[] data = new byte[32];
                int len = fStream.Read(original, 0, 32);

                //error checking ...

               //decryption
                ICryptoTransform decryptor = aes.CreateDecryptor(key, iv);  //this is a slow operation
                MemoryStream m = new MemoryStream();
                using (Stream c = new CryptoStream(m, decryptor, CryptoStreamMode.Write))
                    c.Write(original, 0, original.Length);
                data = m.ToArray();

                //data manipulation ...
            }

Well the thing is that I find it very inefficient to create a decryptor in a loop. There will be quite a lot of data. If I create it before entering the loop then I can't decrypt properly and have to change encryption (declare encryption stream and memory stream before loop), but then I can't encrypt / decrypt only desired chunk of data. Also there aren't many files which would only require random reading / writing. For instance at some files I'll want to read from a certain position till the end of file, which can be quite a lot.

What's your view on this? Is there a better way to achieve this? Maybe different encryption algorithm (in the beginning I wanted to use some sort of xor but I found out it is very easy to "crack") ?

p.s. I want to encrypt in memory and I must use seekable streams.

Was it helpful?

Solution

If you want full random access, ECB is the way to go (as the earlier answer recommends). You would not need to recreate the encryption stream for each block, because it doesn't use an IV and encrypting a block doesn't permute the stream (unlike most other modes where the cryptotext depends on previous blocks or the position of the block in the stream). Wikipedia has a nice illustration (the ciphertux picture) of one of the problems with this mode.

If your file logically consists of larger chunks (such as database records or disk sectors in virtual disks), you should consider encrypting them as units. In CBC mode you would generate a new random IV for each chunk each time you write it and store it with the block (thus using up an additional 32 bytes of storage per chunk), and you would need to rewrite the entire chunk even if a single byte changes, but the security would be much better.

OTHER TIPS

You could use the ECB Encryption Mode (CipherMode.ECB). Other encryption modes feed back the cipher and/or plain text to the next block of text to encrypt/decrypt. This provides greater security, as repeted parts are encrypted in a different way. However, it requires that the complete stream is decrypted.

With the Electronic Code Book (ECB) mode, each block is ciphered individually, so you can implement random access at cipher block boundaries. However, ECB introduces vulnerabilities, especially when the plain text is repetitive. See here

I was just given a tip about using GCM instead of ECB. ECB is as you probably know not a very secure way to do it and I am currently implementing a prototype using Bouncy Castle API (check this thread: https://stackoverflow.com/a/10366194/637783).

If you want to decrypt block by block you have to keep the last 8 bytes and use it as IV for next block

while ((count = fileRead.Read(read, 0, 16)) > 0)
                {
                    if (transed > 0)
                        aes.IV = read;
                    transed += count;
                    ...
                }
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top