Geeks With Blogs
Freestyle Coding Programming in the Real World

Every now and then, the day job causes me to want to rip my hair out. I know, this is shocking news, especially for those of you that have seen me. However, it is true1.

This weeks Tour de Pain comes to us courtesy of cryptography. Before we begin, let me say this. By the nature of the beast, you can't know what you're doing while you're doing it2. As such, most adventures into the subject will lead to heartache. The sad part is that the .NET Cryptographic providers were not the true source of heartache.

I began my expedition with a socket. My client wanted me to create a service that would track changes in a folder, encrypt the data, and ship it to a server. When the server received the file, it would generate a matching response file and return it to the client3.

There are plenty of symmetric cryptographic algorithms out there. In fact, there are so many that the .NET System.Security.Cryptography namespace has an ICryptoTransform interface ease your pain. Just have your Symmetric Cryptographic algorithm generate one of these, wrap the stream, and you're set.

public static byte[] Encrypt( SymmetricAlgorithm p_Crypto, byte[] p_Source ) {
using( MemoryStream _OutStream = new MemoryStream() ) {
using( CryptoStream _Encryptor = new CryptoStream( _OutStream, p_Crypto.CreateEncryptor( Key, IV ), CryptoStreamMode.Write ) ) {
_Encryptor.Write( p_Source, 0, p_Source.Length );
_Encryptor.FlushFinalBlock();
return _OutStream.ToArray();
}
}
}

The deep readers among us may have noticed the MemoryStream. They may have wondered, "Chris, You said you were dealing with sockets. Why didn't you use the lovely NetworkStream that the socket can provide?" To this I remind you of the pain.

The socket can provide you with a NetworkStream. Even better, the NetworkStream is Bidirectional. Unfortunately, the CryptoStream is not bidirectional. It's not even bidirectional when you use a SymmetricAlgorithm. You have to create a decryption function.

public static byte[] Decrypt( SymmetricAlgorithm p_Crypto, byte[] p_Source ) {
using( CryptoStream _Decryptor = new CryptoStream( new MemoryStream( p_Source ), p_Crypto.CreateDecryptor( Key, IV ), CryptoStreamMode.Read ) ) {
byte[] _Buffer = new byte[p_Crypto.BlockSize];
List<byte> _Builder = new List<byte>();
int _Read = 0;
do {
_Read = _Decryptor.Read( _Buffer, 0, p_Crypto.BlockSize );4
for( int _lcv = 0; _lcv < _Read; ++_lcv ) {
_Builder.Add( _Buffer[_lcv] );5
}
} while( _Read != 0 );
return _Builder.ToArray();
}
}

So, why didn't I just pass in my NetworkStream? Even the MSDN sample code uses the raw NetworkStream. The problem is that Stream.Close() closes every stream up the chain. Thus, closing my CryptoStream will close me NetworkStream. Unfortunately, closing a NetworkStream also closes the underlying socket. Because of this, I end up encrypting my data in memory before passing it to the socket.

It is worth noting that you can just create two CryptoStreams, one for reading while it decrypts and one for writing while it encrypts. You can use the same NetworkStream attached to both CryptoStream. You can also wrap the CryptoStream in a StreamReader and StreamWriter. This can reduce the need to play around with byte[]. However, any error on one stream will kill just about all the streams and close your connection.

As always, yes, there is a lot more code there that I'm not allowed to show you. However, I can answer questions...

1 It actually was the original motivation for the blog.
2 Yes, the is a slight exaggeration..
3 Forgive me for being a little vague. However, you should have expected a little vagueness when I said I was dealing with cryptography.
4 If you want to know why I'm only pulling off BlockSize each time, look up how Symmetric Cryptographic Algorithms work.
5 This is for decoding reasons. Copying a block at a time is still going to be in O(n). However, by manually copying it, I know I'm only grabbing valid data. This eases the conversion from the byte[] to the actual data.

Posted on Wednesday, August 31, 2011 12:12 AM .NET , C# , Cryptography , Sockets | Back to top


Comments on this post: Bidirectional Encrypted Streams

# re: Bidirectional Encrypted Streams
Requesting Gravatar...
.BlockSize property contains bits count and .Read method reads bytes.
Shouldn't you have to use p_Crypto.BlockSize / 8 in .Read method instead of p_Crypto.BlockSize ?
Left by sgissinger on Aug 09, 2012 7:59 AM

# re: Bidirectional Encrypted Streams
Requesting Gravatar...
You know, I never noticed that. However, that just means I'm pulling 8 blocks at a time. Since EVERYTHING has to be in block increments, padded if necessacy, it ends up correctly.
Left by Chris Gardner on Aug 10, 2012 10:43 PM

Your comment:
 (will show your gravatar)


Copyright © Chris Gardner | Powered by: GeeksWithBlogs.net