June 1, 2020
Hot Topics:

Cryptographic Solutions for .NET Developers: Hashing and Encryption

  • By Jani Järvinen
  • Send Email »
  • More Articles »

Creating Transformation Objects

How do you create this transformation object, then? To do this, you need to first create an instance of the algorithm class (in this case, the RC2 class from the System.Security.Cryptography namespace), and then call its CreateEncryptor method. This method is declared as a virtual method in the abstract base class SymmetricAlgorithm, from which the RC2 class is derived. This method returns an ICryptoTransform interface, which you can then pass in to the CryptoStream constructor.

Some further discussion is in order. When the code creates an instance of the RC2 class just after validating the parameters, you might notice that I don't simply call constructor of the RC2 class, but instead call the static Create method. This is because, in fact, the RC2 class is an abstract class. But by calling the Create method, I'm able to get a concrete implementation of some descending class that inherits from RC2.

All this can sound complex to you, but the idea is that this way, third parties can plug in their own implementations into the .NET cryptography framework. By calling the Create method, you easily can specify at runtime which implementation of the RC2 algorithm you want. If you don't pass a string (name) to the Create method, .NET's default implementation will be selected, as I've done.

Because you now have an implementation instance of the RC2 algorithm, you can proceed to call the CreateEncryptor method. This method needs the key and initialization vector byte arrays, and returns the ICryptoTransform interface. Note that, for simplicity, the ASCII characters in the password string are simply being converted to bytes to create the key. For this to work, the password must be of certain length, in the case of this RC2 algorithm implementation, 5 to 16 bytes. This corresponds to 40- to 128-bit key lengths (these values can be queried through the LegalKeySizes property).

The initialization vector is created as a hard-coded byte array. When you create an instance of the RC2 implementation class by calling RC2.Create, both the key and initialization vector are initialized with random data. However, as you will recall, both must be exactly the same if you want to later decrypt the encrypted data. Because the simple example application won't save the initialization vector anywhere and they key is supplied by the user, one option is to have a hard-coded initialization vector (another one would be to derive the vector from the key).

Copying the Stream and Inverting the Process

Up to now, you've learned how the cryptography classes in .NET are used to create an encryptor object and the transformation object. Next, you'll see what this line of code does:

CopyStream(input, crypto);

As the name suggests, this custom method simply copies data from the source stream (input) to the destination stream (your crypto stream in the case). Remember that, because you are working with files (FileStream objects), the CryptoStream is chained to the output FileStream, so that whenever data is written to the crypto stream, it goes through the transformation process (encryption) and is then written to the output file. Here's the implementation of the CopyStream method:

private static void CopyStream(Stream input, Stream output)
   const int blockSize = 64 * 1024;    // 64k
   // copy data block by block
   byte[] buffer = new byte[blockSize];
   int bytesRead = input.Read(buffer, 0, blockSize);
   while (bytesRead > 0)
      output.Write(buffer, 0, bytesRead);
      bytesRead = input.Read(buffer, 0, blockSize);

This method is rather simple, and loops through the input stream in 64-kilobyte blocks until all data has been read. Note that it is very important to remember to close all the streams involved in the encryption process; otherwise, you might not flush all encrypted data to the output stream and thus to the file on disk. It is best to do all this inside a try-finally block.

Before going into hashing, you should understand the opposite operation to encryption: decryption. If you take at the look at the code shown previously from the RC2Encrypt sample application and compare it to the RC2Decrypt application, you will notice that there are only minor differences. In fact, only three lines of code need to be changed to decrypt data instead of encrypt it.

In RC2Encrypt, you needed to create the encryptor object by calling the CreateEncryptor method. But for decryption purposes, you would call its counterpart, the CreateDecryptor method. This returns a transformation object "the other way around." The second change is the creation of the CryptoStream instance. When encrypting, the line is as follows:

crypto = new CryptoStream(output, transform, CryptoStreamMode.Write);

But when decrypting, it needs to be:

crypto = new CryptoStream(input, transform, CryptoStreamMode.Read);

Finally, the CopyStream method needs to be called as follows:

CopyStream(crypto, output);

Other than these differences, the two sample applications are functionally identical.

Page 2 of 3

This article was originally published on July 27, 2007

Enterprise Development Update

Don't miss an article. Subscribe to our newsletter below.

Thanks for your registration, follow us on our social networks to keep up-to-date