.NET 2.0 Stream Compression & Encryption

A while ago I created a little application to store my passwords compressed and encrypted using XML serialization. I created it in .NET 1.1 so I could use it a my customer’s site (poor me). It’s compressed and encrypted XML in an XML container so I can use XmlSerializer all the way.
I handcrafted the streaming of the encryption and compression which was fun but took some time.
In .NET 2.0 this should be easier with CryptoStream and DeflateStream. The only tricky bit is how to get the compressed & encrypted data into and out of a MemoryStream properly. If you don’t close the Crypto & Deflate stream they don’t finalize their output properly and if you do close them they close the underlying MemoryStream. There’s a way around this but it differs between the CryptoStream and DeflateStream. The sample code below shows how to do this (and these aren’t my real passwords  of course):
 
The Save method required some tweaking, in short it does the following:
  • create a MemoryStream and a CryptoStream,
  • create a DeflateStream with leaveOpen set to true in the constructor, serialize the secret information into it and close it,
  • call CryptoStream.FlushFinalBlock(),
  • reset the MemoryStream to the beginning,
  • create the containing object (Vault) and put in the contents of the MemoryStream,
  • serialize the containing object to a file.

So, the CryptoStream has an extra method FlushFinalBlock() to tell the stream to finish its work. On the other hand DeflateStream is instructed to leave the underlying stream open. If the DeflateStream is then closed it will also finish its work.

namespace Spikes
{
    public class Credential
    {
        [XmlAttribute]
        public string Site;
        [XmlAttribute]
        public string User;
        [XmlAttribute]
        public string Password;
    }

   
public class Secrets
    {
        public Credential[] Credentials;
    }

   
public class Vault
    {
        [XmlAttribute]
        public int Hashes;
        [XmlAttribute]
        public byte[] IV;
        [XmlText]
        public byte[] Data;
    }

   
class Program
    {
        static void Main(string[] args)
        {
            Credential credential1 = new Credential();
            credential1.Site = "Info Support";
            credential1.User = "frankg";
            credential1.Password = "bl@h’1";
            Credential credential2 = new Credential();
            credential2.Site = "Live Mail";
            credential2.User = "fjtdg(at)hotmail.com";
            credential2.Password = "Y0!";
            Secrets secrets = new Secrets();
            secrets.Credentials = new Credential[] { credential1, credential2, };
            RandomNumberGenerator rng = RNGCryptoServiceProvider.Create();
            byte[] iv = new byte[16];
            rng.GetBytes(iv);
            const string path = @"C:\Users\Frank\Documents\vault.xml";
            const string password = @"P@$$\/\/w0rd";
            const int hashes = 16; //1048576;
            Save(secrets, path, password, hashes, iv);
            secrets = Open(path, password);
            Console.WriteLine(secrets.Credentials.Length);
        }

        static void Save(Secrets secrets, string path, string password, int hashes, byte[] iv)
        {
            using (MemoryStream dataStream = new MemoryStream())
            {
                Rijndael rijndael = Rijndael.Create();
                byte[] key = Key(password, hashes);
                using (CryptoStream cryptoStream = new CryptoStream(dataStream, rijndael.CreateEncryptor(key, iv),
CryptoStreamMode
.Write))
                {
                    using (Stream zipStream = new DeflateStream(cryptoStream, CompressionMode.Compress, true))
                    {
                        XmlSerializer secretsSerializer = new XmlSerializer(typeof(Secrets));
                        secretsSerializer.Serialize(zipStream, secrets);
                    }
                    cryptoStream.FlushFinalBlock();
                    dataStream.Position = 0;
                    Vault vault = new Vault();
                    vault.Hashes = hashes;
                    vault.IV = iv;
                    byte[] data = new byte[dataStream.Length];
                    dataStream.Read(data, 0, data.Length);
                    vault.Data = data;
                    using (FileStream vaultStream = File.Open(path, FileMode.Create))
                    {
                        XmlSerializer vaultSerializer = new XmlSerializer(typeof(Vault));
                        vaultSerializer.Serialize(vaultStream, vault);
                    }
                }
            }
        }

       
static Secrets Open(string path, string password)
        {
            using (FileStream vaultStream = File.Open(path, FileMode.Open))
            {
                XmlSerializer vaultSerializer = new XmlSerializer(typeof(Vault));
                Vault vault = (Vault)vaultSerializer.Deserialize(vaultStream);
                byte[] key = Key(password, vault.Hashes);
                using (MemoryStream dataStream = new MemoryStream())
                {
                    dataStream.Write(vault.Data, 0, vault.Data.Length);
                    dataStream.Flush();
                    dataStream.Position = 0;
                    Rijndael rijndael = Rijndael.Create();
                    using (CryptoStream cryptoStream = new CryptoStream(dataStream, rijndael.CreateDecryptor(key,
vault.IV),
CryptoStreamMode.Read))
                    {
                        using (Stream zipStream = new DeflateStream(cryptoStream, CompressionMode.Decompress))
                        {
                            XmlSerializer secretsSerializer = new XmlSerializer(typeof(Secrets));
                            return (Secrets)secretsSerializer.Deserialize(zipStream);
                        }
                    }
                }
            }
        }

       
static byte[] Key(string password, int hashes)
        {
            byte[] bytes = Encoding.UTF8.GetBytes(password);
            HashAlgorithm hasher = new SHA256Managed();
            for (int i = 0; i < hashes; i++)
            {
                bytes = hasher.ComputeHash(bytes);
            }
            return bytes;
        }
    }
}

Advertisements

One thought on “.NET 2.0 Stream Compression & Encryption

  1.  Now that is very neat indeed! Though Eye am rusty as heck with pyhton, xml, xsml, java, c++, or any other hack…U R the man. Lots of time spent banging on the ole congas 4 me! That, and a bit of photog, and data mining. Then, there is always my wish list…or, my if onlys….if only Eye had done this, or that. Then maybe Eye could have………But, hey! Eye\’m in the mountains and livin\’ in the outdoors. Couldn\’t B better…….Stay well, play safe. And……keep banging on the drums, OK? O<K<

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s