0

I have an interface defined like so:

public interface IEncryptionService
{
    Stream Encrypt(Stream cleartext);
    Stream Decrypt(Stream encrypted);
}

I am implementing this interface with an AesCryptoServiceProvider, but there's clearly a problem here. The IV (Initialization Vector) is not returned on the interface... so encrypting something would work fine, as long as I have no desire to decrypt it ever again. The Decrypt() method has no chance at all of working.

What I want to do is include the IV in cleartext at the beginning of the stream, then add the CryptoStream to it, so it is essentially encrypted data with a "header" that I could strip off and use for decrypting the stream.

So... how would I do that? I can create the CryptoStream easy enough, but it looks like this would encrypt the IV, which kinda defeats the purpose. I could load the CryptoStream into memory, prepend the IV, and then stream it out as a MemoryStream, but this would be really inefficient, and would die on large streams.

What is a good, secure, scalable practice for this?

Jeremy Holovacs
  • 22,480
  • 33
  • 117
  • 254
  • Your CryptoStream has a MemoryStream as a backing store already probably. Make an array of the IV and write it to that MemoryStream directly, and then write plain text to your CryptoStream after that. I believe this is how I have done it in the past. – WDS Aug 30 '15 at 23:27
  • @WDS, can you give an example? – Jeremy Holovacs Aug 30 '15 at 23:27
  • 1
    Yeah, will take a couple minutes but I will. – WDS Aug 30 '15 at 23:28

1 Answers1

1

Here is what I had in mind. See how you write the IV to the MemoryStream and then follow it with the crypto? Then when you want to decrypt, pull the IV off first in the same way.

Sorry, been a long time. This one is working. It should scale well if you don't cast ms toArray(); at the end. For example write to FileStream as you go and you should not need much memory at all. This is just to demo prepending the IV.

    private byte[] encrypt(byte[] originalPlaintextBytes)
    {
        using (SymmetricAlgorithm algorithm = AesCryptoServiceProvider.Create())
        {
            algorithm.GenerateKey();
            algorithm.GenerateIV();
            byte[] iv = algorithm.IV;
            byte[] key = algorithm.Key;
            using (ICryptoTransform encryptor = algorithm.CreateEncryptor(key, iv))
            {
                using (MemoryStream ms = new MemoryStream())
                using (CryptoStream cs = new CryptoStream(ms, encryptor,CryptoStreamMode.Write))
                {
                    BinaryWriter bw = new BinaryWriter(ms);
                    bw.Write(iv);
                    cs.Write(originalPlaintextBytes, 0, originalPlaintextBytes.Length);
                    return ms.ToArray();
                }
            }
        }
    }

OK rather than edit the above code, here is a whole program that randomly creates a plaintext file of 1 megabyte. Then it encrypts it into ciphertext. Note that this program does not ever need 1 megabyte of memory in which to operate. It is completely scalable. Again, as before, this program is to demonstrate the concept, and you would do better with a readBuffer larger than 1 byte. But I did not want to create that and obscure the core answer. I hope this helps. I think it is exactly the kind of approach you need.

using System;
using System.IO;
using System.Security.Cryptography;
using System.Windows.Forms;

namespace SO_AES
{
    public partial class Form1 : Form
    {
        Random ran = new Random();
        public Form1()
        {
            InitializeComponent();
            using (var file = File.Open("Plaintext.txt", FileMode.OpenOrCreate))
            {
                byte[] junkBytes = new byte[1000];
                for (int i = 0; i < 1000; i++)
                {
                    for (int j = 0; j < 1000; j++)
                    {
                        junkBytes[j] = (byte)ran.Next(0, 255);
                    }
                    file.Write(junkBytes, 0, junkBytes.Length);
                }
            }

            using (var plainTextFile = File.Open("Plaintext.txt", FileMode.Open))
            {
                using (var cryptoTextFile = File.Open("Crypto.txt", FileMode.OpenOrCreate))
                {
                    encrypt(plainTextFile, cryptoTextFile);
                }
            }
        }

        void encrypt(Stream inStream, Stream outStream)
        {
            using (SymmetricAlgorithm algorithm = AesCryptoServiceProvider.Create())
            {
                algorithm.GenerateKey();
                algorithm.GenerateIV();
                byte[] iv = algorithm.IV;
                byte[] key = algorithm.Key;
                using (ICryptoTransform encryptor = algorithm.CreateEncryptor(key, iv))
                {
                    using (CryptoStream cs = new CryptoStream(outStream, encryptor, CryptoStreamMode.Write))
                    {
                        BinaryWriter bw = new BinaryWriter(outStream);
                        bw.Write(iv);
                        byte[] readBuffer = new byte[1];
                        BinaryReader br = new BinaryReader(inStream);
                        while (br.Read(readBuffer, 0, readBuffer.Length) != 0)
                        {
                            cs.Write(readBuffer, 0, 1);
                        }
                    }
                }
            }
            inStream.Close();
            outStream.Close();
        }
    }
}
WDS
  • 966
  • 1
  • 9
  • 17
  • Hmm. How well will this scale? It looks like you're loading everything into memory first. – Jeremy Holovacs Aug 30 '15 at 23:44
  • Ugh, I have made a mistake here. Let me check the issue and edit. As it is posted, MemoryStream is not keeping the IV. – WDS Aug 30 '15 at 23:47
  • You should be able to substitute a FileStream for the MemoryStream and use virtually no memory to run this, no matter how large your plain text. – WDS Aug 30 '15 at 23:57
  • Oh, and yeah if you want it to scale like that, you also need to modify this method to accept a Stream as argument instead of an array. Easy enough to do. My main goal was to demo prepending the IV. – WDS Aug 31 '15 at 00:03
  • I don't think this will scale well returning a stream... looks like it would have to build the data in memory and return en masse. – Jeremy Holovacs Aug 31 '15 at 00:07
  • The only thing that would create a memory issue as I posted it is the originalPlainTextBytes array. Let me modify it to show with a Stream input and output. Will post in a few. – WDS Aug 31 '15 at 00:09
  • Just added a larger version that will scale. Before anyone comments that Random is not cryptographically secure, please note I am only using it to fill a demo file with bytes, not for the crypto itself. – WDS Aug 31 '15 at 00:41
  • 1
    This... looks like it might work. I'll try it out and get back to you. – Jeremy Holovacs Aug 31 '15 at 00:43