0

I find myself repeating myself with this code

using (var fileStream = new FileStream(path, FileMode.Open, FileAccess.Read)
{
  using (var aes = AesCryptoServiceProvider() { Key = ... }
  {

    // Read the IV at the beginning of the filestream

    using (var cryptoStream = new CryptoStream(fileStream, aes.CreateDecryptor(), CryptoStreamMode.Read)
    {

      // Actual code only using cryptoStream

    }
  }
}

and

using (var fileStream = new FileStream(path, FileMode.Create, FileAccess.Write)
{
  using (var aes = AesCryptoServiceProvider() { Key = ... }
  {

    // Write the IV at the beginning of the filestream

    using (var cryptoStream = new CryptoStream(fileStream, aes.CreateDecryptor(), CryptoStreamMode.Write)
    {

      // Actual code only using cryptoStream

    }
  }
}

And I ask myself, if it is possible to replace this with something like this

using (var cryptoStream = new MyDecryptionStream(path))
{
  // Actual code
}

The actual code can be very different. It can be an image that has to be saved or an xml serialization.

I tried to implement my own Stream class, that casts all methods to a private property CryptoStream. But this didn't work out. It always broke at the counterpart, where I tried to read the IV at the beginning.

4
  • Note that you don't always need all the {}. It is considered aesthethically correct to do using () /* new line*/ using () /* new line */ using () { without the middle {. Visual Studio will format all the using at the same level Commented Jun 8, 2018 at 16:09
  • Yes, it is possible by implementing IDisposable on the MyDecryptionStream. Why you didn't succeed is hard to tell without code and a different question. Commented Jun 8, 2018 at 16:10
  • So what was your implementation, and how, specifically, was it not working? We can't tell you how to fix it if you haven't shown it and haven't described what's wrong with it. Commented Jun 8, 2018 at 16:14
  • Don't forget that using is just syntactic sugar for IDisposables. This is what's generated from a using, to take out any magic: learn.microsoft.com/en-us/dotnet/csharp/language-reference/…. Thus, the answer is, yes, you can do this by creating a IDisposable MyDecryptionStream that correctly manages the usage & disposal of the underlying FileStream, AesCryptoServiceProvider, and CryptoStream. I'd recommend you try that and come back to SO with problems you encounter while implementing the IDisposable. Commented Jun 8, 2018 at 16:18

2 Answers 2

1

Here's an extremely rough example of what you're trying to do. There are a lot of places where improvement could be made, but it is a working sample that you can hopefully build on.

First, we create a class that implements IDisposable. This allows us to use this class in using statements. This class will instantiate the three other objects we need, and handle disposing them, all by itself.

class MyCryptoStream : IDisposable
{
    private FileStream fileStream = null;
    private AesCryptoServiceProvider aes = null;
    public CryptoStream cryptoStream = null;

    public enum Mode
    {
        Write,
        Read
    }

    public MyCryptoStream(string filepath, Mode mode, byte[] key, byte[] iv = null)
    {
        if(mode == Mode.Write)
        {
            fileStream = new FileStream(filepath, FileMode.Open, FileAccess.Write);
            fileStream.Write(iv, 0, 16);
            aes = new AesCryptoServiceProvider() { Key = key, IV = iv };

            cryptoStream = new CryptoStream(fileStream, aes.CreateEncryptor(), CryptoStreamMode.Write);
        }
        else
        {
            iv = new byte[16];
            fileStream = new FileStream(filepath, FileMode.Open, FileAccess.Read);
            fileStream.Read(iv, 0, 16);
            aes = new AesCryptoServiceProvider() { Key = key, IV = iv };

            cryptoStream = new CryptoStream(fileStream, aes.CreateDecryptor(), CryptoStreamMode.Read);
        }
    }

    #region IDisposable Support
    private bool disposedValue = false; // To detect redundant calls

    protected virtual void Dispose(bool disposing)
    {
        if (!disposedValue)
        {
            if (disposing)
            {
                if (cryptoStream != null)
                {
                    cryptoStream.Dispose();
                }
                if (aes != null)
                {
                    aes.Dispose();
                }
                if (fileStream != null)
                {
                    fileStream.Dispose();
                }
            }

            // TODO: free unmanaged resources (unmanaged objects) and override a finalizer below.
            // TODO: set large fields to null.

            disposedValue = true;
        }
    }

    // TODO: override a finalizer only if Dispose(bool disposing) above has code to free unmanaged resources.
    // ~UsingReduction() {
    //   // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
    //   Dispose(false);
    // }

    // This code added to correctly implement the disposable pattern.
    public void Dispose()
    {
        // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
        Dispose(true);
        // TODO: uncomment the following line if the finalizer is overridden above.
        // GC.SuppressFinalize(this);
    }
    #endregion

}

Now, we can use this class like so:

        string path = "..\\..\\test.txt";
        byte[] key = null;
        byte[] iv = null;
        using (AesCryptoServiceProvider myAes = new AesCryptoServiceProvider())
        {
            key = myAes.Key;
            iv = myAes.IV;
        }
        using (MyCryptoStream ur = new MyCryptoStream(path, MyCryptoStream.Mode.Write, key, iv))
        {
            using (StreamWriter sw = new StreamWriter(ur.cryptoStream))
            {
                sw.Write("Test string");
            }
        }
        string text = string.Empty;
        using (MyCryptoStream ur = new MyCryptoStream(path, MyCryptoStream.Mode.Read, key))
        {
            using (StreamReader sr = new StreamReader(ur.cryptoStream))
            {
                text = sr.ReadToEnd();
            }
        }

If you run this example, you can see that it writes "Test string" to a file using the cryptostream, and then reads the same text back from that file. Checking the value of text, we can see that it is still "Test string", indicating that the procedure was successful.

Sign up to request clarification or add additional context in comments.

Comments

1

What about a helper function?

public static TResult ReadFileUsingCrypto<TResult>(string path, KeyThing key, Func<CryptoStream, TResult> use)
{
    using (var fileStream = new FileStream(path, FileMode.Open, FileAccesa.Read))
    using (var aes = new AesCryptoServiceProvider(){...}))
    using (var cryptoStream = new CryptoStream(fileStream, aes.CreateDecryptor(), CryptoStreamMode.Read))
    {
        return use(cryptoStream);
    }
}

And then

var result = ReadFileUsingCrypto(“myFile”, key, crypto => <use crypto here and return result>);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.