In this article, Richard takes us through some of the cryptography tools available in the .NET Framework that provide hashing and encryption of different types of data.
Cryptographic Objects in C#: Part 1 - Using ICryptoTransform Interfaces and Cryptographic Objects (Page 4 of 5 )
In the following code we will see how the actual encryption is done. Formerly (in beta 1) we would have defined a class which implemented ICryptoStream to allow us to write to any stream - this could then be custom defined. The authors of the .NET Crypto libraries have simplified this even further in Beta 2 by allowing ICryptoTransform interfaces to be created that represent either encryption or decryption. This contains all the key and IV information.
try { //Here we create the decryption objects but if the key file doesn't //exist, automatically throw an exception again
CryptoStream csDecrypt = new CryptoStream(fOut, streamDecrypt, CryptoStreamMode.Write);
len = 0; lenCount = 0; byte[] bOutputDecrypt = new byte[4096]; while (lenCount < fileLength) { len = fIn.Read(bContents,0,4096); csDecrypt.Write(bContents,0,len); lenCount = lenCount + len; } csDecrypt.FlushFinalBlock(); csDecrypt.Close(); } catch(Exception e) { throw e; }
Some of the above file handling code might be familiar to .NET aficionados. The CryptoStream object is created and takes as its arguments the ICryptoTransform interface (which is our Decryptor and contains the key and IV that we need). It also needs a valid stream object. In the above code it is a FileStream although this is because we are going to write to a file.
In a later piece of code we use a MemoryStream in the same place to write to memory and retrieve our encrypted output as a byte array.
However, it just needs to be a stream which means that it can be written to any networking stream object too. In the while loop we read from one file and then write to the CryptoStream (effectively in this case another file). The Stream can be written to any number of times, then FlushFinalBlock() is called followed by Close(), which will flush the stream buffer. Close releases all handles (in this case to files).
MemoryStream mStream = new MemoryStream(); mStream.Position = 0;
As can be seen from the above implementation a MemoryStream can be used in place of a FileStream. This exemplifies the idea that any stream can be used. A MemoryStream is useful in this effect as an area of memory can be written to as a stream and later discarded when the contents are retrieved into an array or string.
After the first encryption is done then the key file is written in the same directory as the plaintext and the cipher text output. The above method is used to write a Base64 encoded version of the key and the IV to a file. This can again be extended to use a key file which contains multiple keys. I've written this article into a .dll project which can be used generically within a project. The following sample code (ConsoleClient.cs) explains how to use the objects contained therein:
try { //First file encryption or decryption CryptoWrapper.EncryptStringOrFile e = new EncryptStringOrFile(EncryptStringOrFile.KeyEncryptAlgorithm.DES, "C:\\temp.enc"); string temp = e.Encrypt(); //Then hash passwords ans usernames CryptoWrapper.HashLoginCredentials h = new HashLoginCredentials(HashLoginCredentials.HashTypeDef.SHA1, "username", "password"); string hash = h.Hash(true); //Then encrypt a stream CryptoWrapper.EncryptStringOrFile e1 = new EncryptStringOrFile(EncryptStringOrFile.KeyEncryptAlgorithm.DES, "EncryptOrDecryptThisText"); string temp1 = e1.Encrypt();
Console.WriteLine("filename is: "+temp); Console.WriteLine("hash is: "+hash); Console.WriteLine("Encrypted text is: "+temp1); Console.WriteLine("\r\nPress any key to exit..."); Console.Read(); return 0; } catch(Exception e) { Console.WriteLine(e.StackTrace); return 0; }
Here is an example of the encrypted output from the above main() method:
In the first line "temp.enc" is passed to the EncryptFileOrString object and then the encrypt method is called which looks for the key file in the respective directory and decrypts the file into "temp.tmp ". It returns the name of the output file is successful.
In the second operation a HashLoginCredentials object is created and it is instructed to use a SHA1 hash, username and password is passed into this constructor and the resultant hash is returned.
In the final operation a DES algorithm is used to encrypt some plaintext. The cipher text is returned to the console in the form of a string. The only prerequisite is for a file called C:\temp.key to exist so that key information can used to encrypt and decrypt plain and cipher text respectively.
A test harness for the .dll has been provided in ASP.NET. After extracting the .zip file, typing http://localhost/CryptoWrapper/login.aspx (assuming you have installed a web server) should present the following screen:
There is a readme.txt file to accompany the test harness which should be read prior to using the page. However, code from the test harness can be easily adapted into an application to allow, amongst other things, logins and password hashing from which can be stored and retrieved from an XML file and also applications that allow files to be uploaded but encrypt them on the server to allow a greater level of security.