This How To describes how to create a generic encryption library that can be used to encrypt and decrypt data using the following algorithms:
DES (Digital Encryption Standard)
Triple DES
Rijndael
RC2
For an example application that uses the class library created in this How To, see "Appendix " in the Reference section of this book.
The following items describe the recommended hardware, software, network infrastructure, skills and knowledge, and service packs you will need.
Microsoft® Windows® 2000 operating system
Microsoft Visual Studio® .NET development system
The procedures in this article also require that you have knowledge of the Microsoft Visual C#™ development tool.
This How To includes the following procedures:
This procedure creates a C# class library, which will provide encryption and decryption functionality.
▸ To create a C# class library
Start Visual Studio .NET and create a new C# Class Library project called Encryption.
Use Solution Explorer to rename class1.cs as EncryptTransformer.cs.
In EncryptTransformer.cs, rename Class1 as EncryptTransformer.
Change the scope of the class from public to internal.
internal class EncryptTransformer
Add the following using statement at the top of the file.
using System.Security.Cryptography;
Add the following enumerated type within the Encryption namespace.
public enum EncryptionAlgorithm {Des = 1, Rc2, Rijndael, TripleDes};
Add the following private member variables to the EncryptTransformer class.
private EncryptionAlgorithm algorithmID; private byte[] initVec; private byte[] encKey;
Replace the default constructor with the following constructor.
internal EncryptTransformer(EncryptionAlgorithm algId) { //Save the algorithm being used. algorithmID = algId; }
Add the following method to the class.
internal ICryptoTransform GetCryptoServiceProvider(byte[] bytesKey) { // Pick the provider. switch (algorithmID) { case EncryptionAlgorithm.Des: { DES des = new DESCryptoServiceProvider(); des.Mode = CipherMode.CBC; // See if a key was provided if (null == bytesKey) { encKey = des.Key; } else { des.Key = bytesKey; encKey = des.Key; } // See if the client provided an initialization vector if (null == initVec) { // Have the algorithm create one initVec = des.IV; } else { //No, give it to the algorithm des.IV = initVec; } return des.CreateEncryptor(); } case EncryptionAlgorithm.TripleDes: { TripleDES des3 = new TripleDESCryptoServiceProvider(); des3.Mode = CipherMode.CBC; // See if a key was provided if (null == bytesKey) { encKey = des3.Key; } else { des3.Key = bytesKey; encKey = des3.Key; } // See if the client provided an IV if (null == initVec) { //Yes, have the alg create one initVec = des3.IV; } else { //No, give it to the alg. des3.IV = initVec; } return des3.CreateEncryptor(); } case EncryptionAlgorithm.Rc2: { RC2 rc2 = new RC2CryptoServiceProvider(); rc2.Mode = CipherMode.CBC; // Test to see if a key was provided if (null == bytesKey) { encKey = rc2.Key; } else { rc2.Key = bytesKey; encKey = rc2.Key; } // See if the client provided an IV if (null == initVec) { //Yes, have the alg create one initVec = rc2.IV; } else { //No, give it to the alg. rc2.IV = initVec; } return rc2.CreateEncryptor(); } case EncryptionAlgorithm.Rijndael: { Rijndael rijndael = new RijndaelManaged(); rijndael.Mode = CipherMode.CBC; // Test to see if a key was provided if(null == bytesKey) { encKey = rijndael.Key; } else { rijndael.Key = bytesKey; encKey = rijndael.Key; } // See if the client provided an IV if(null == initVec) { //Yes, have the alg create one initVec = rijndael.IV; } else { //No, give it to the alg. rijndael.IV = initVec; } return rijndael.CreateEncryptor(); } default: { throw new CryptographicException("Algorithm ID '" + algorithmID + "' not supported."); } } }
Add the following properties to the class.
internal byte[] IV { get{return initVec;} set{initVec = value;} } internal byte[] Key { get{return encKey;} }
Add a new class called DecryptTransformer to the project.
Add the following using statement at the top of the DecryptTransformer.cs file.
using System.Security.Cryptography;
Change the class scope from public to internal.
Replace the default constructor with the following constructor.
internal DecryptTransformer(EncryptionAlgorithm deCryptId) { algorithmID = deCryptId; }
Add the following private variables to the class.
private EncryptionAlgorithm algorithmID; private byte[] initVec;
Add the following method to the class.
internal ICryptoTransform GetCryptoServiceProvider(byte[] bytesKey) { // Pick the provider. switch (algorithmID) { case EncryptionAlgorithm.Des: { DES des = new DESCryptoServiceProvider(); des.Mode = CipherMode.CBC; des.Key = bytesKey; des.IV = initVec; return des.CreateDecryptor(); } case EncryptionAlgorithm.TripleDes: { TripleDES des3 = new TripleDESCryptoServiceProvider(); des3.Mode = CipherMode.CBC; return des3.CreateDecryptor(bytesKey, initVec); } case EncryptionAlgorithm.Rc2: { RC2 rc2 = new RC2CryptoServiceProvider(); rc2.Mode = CipherMode.CBC; return rc2.CreateDecryptor(bytesKey, initVec); } case EncryptionAlgorithm.Rijndael: { Rijndael rijndael = new RijndaelManaged(); rijndael.Mode = CipherMode.CBC; return rijndael.CreateDecryptor(bytesKey, initVec); } default: { throw new CryptographicException("Algorithm ID '" + algorithmID + "' not supported."); } } } //end GetCryptoServiceProvider
Add the following property to the class.
internal byte[] IV { set{initVec = value;} }
Add a new class called Encryptor to the project.
Add the following using statements at the top of Encryptor.cs.
using System.Security.Cryptography; using System.IO;
Replace the default constructor with the following constructor.
public Encryptor(EncryptionAlgorithm algId) { transformer = new EncryptTransformer(algId); }
Add the following private member variables to the class.
private EncryptTransformer transformer; private byte[] initVec; private byte[] encKey;
Add the following Encrypt method to the class.
public byte[] Encrypt(byte[] bytesData, byte[] bytesKey) { //Set up the stream that will hold the encrypted data. MemoryStream memStreamEncryptedData = new MemoryStream(); transformer.IV = initVec; ICryptoTransform transform = transformer.GetCryptoServiceProvider(bytesKey); CryptoStream encStream = new CryptoStream(memStreamEncryptedData, transform, CryptoStreamMode.Write); try { //Encrypt the data, write it to the memory stream. encStream.Write(bytesData, 0, bytesData.Length); } catch(Exception ex) { throw new Exception("Error while writing encrypted data to the stream: " + ex.Message); } //Set the IV and key for the client to retrieve encKey = transformer.Key; initVec = transformer.IV; encStream.FlushFinalBlock(); encStream.Close(); //Send the data back. return memStreamEncryptedData.ToArray(); }//end Encrypt
Add the following properties to the class.
public byte[] IV { get{return initVec;} set{initVec = value;} } public byte[] Key { get{return encKey;} }
Add a new class called Decryptor to the project.
Add the following using statements at the top of Decryptor.cs
using System.Security.Cryptography; using System.IO;
Replace the default constructor with the following constructor.
public Decryptor(EncryptionAlgorithm algId) { transformer = new DecryptTransformer(algId); }
Add the following private member variables to the class.
private DecryptTransformer transformer; private byte[] initVec;
Add the following Decrypt method to the class.
public byte[] Decrypt(byte[] bytesData, byte[] bytesKey) { //Set up the memory stream for the decrypted data. MemoryStream memStreamDecryptedData = new MemoryStream(); //Pass in the initialization vector. transformer.IV = initVec; ICryptoTransform transform = transformer.GetCryptoServiceProvider(bytesKey); CryptoStream decStream = new CryptoStream(memStreamDecryptedData, transform, CryptoStreamMode.Write); try { decStream.Write(bytesData, 0, bytesData.Length); } catch(Exception ex) { throw new Exception("Error while writing encrypted data to the stream: " + ex.Message); } decStream.FlushFinalBlock(); decStream.Close(); // Send the data back. return memStreamDecryptedData.ToArray(); } //end Decrypt
Add the following property to the class.
public byte[] IV { set{initVec = value;} }
On the Build menu, click Build Solution.
This procedure creates a simple console test application to test the encryption and decryption functionality.
▸ To create a console test application
Add a new C# Console application called EncryptionTester to the current solution.
In Solution Explorer, right-click the EncryptionTester project, and then click Set as StartUp Project.
Use Solution Explorer to rename class1.cs as EncryptionTest.cs.
In EncryptionTest.cs, rename Class1 as EncryptionTest.
Add a project reference to the Encryption project.
Add the following using statements at the top of EncryptionTest.cs.
using System.Text; using Encryption;
Add the following code to the Main method.
// Set the required algorithm EncryptionAlgorithm algorithm = EncryptionAlgorithm.Des; // Init variables. byte[] IV = null; byte[] cipherText = null; byte[] key = null; try { //Try to encrypt. //Create the encryptor. Encryptor enc = new Encryptor(EncryptionAlgorithm.Des); byte[] plainText = Encoding.ASCII.GetBytes("Test String"); if ((EncryptionAlgorithm.TripleDes == algorithm) || (EncryptionAlgorithm.Rijndael == algorithm)) { //3Des only work with a 16 or 24 byte key. key = Encoding.ASCII.GetBytes("password12345678"); if (EncryptionAlgorithm.Rijndael == algorithm) { // Must be 16 bytes for Rijndael. IV = Encoding.ASCII.GetBytes("init vec is big."); } else { IV = Encoding.ASCII.GetBytes("init vec"); } } else { //Des only works with an 8 byte key. The others uses variable length keys. //Set the key to null to have a new one generated. key = Encoding.ASCII.GetBytes("password"); IV = Encoding.ASCII.GetBytes("init vec"); } // Uncomment the next lines to have the key or IV generated for you. // key = null; // IV = null; enc.IV = IV; // Perform the encryption. cipherText = enc.Encrypt(plainText, key); // Retrieve the intialization vector and key. You will need it // for decryption. IV = enc.IV; key = enc.Key; // Look at your cipher text and initialization vector. Console.WriteLine(" Cipher text: " + Convert.ToBase64String(cipherText)); Console.WriteLine("Initialization vector: " + Convert.ToBase64String(IV)); Console.WriteLine(" Key: " + Convert.ToBase64String(key)); } catch(Exception ex) { Console.WriteLine("Exception encrypting. " + ex.Message); return; } try { //Try to decrypt. //Set up your decryption, give it the algorithm and initialization vector. Decryptor dec = new Decryptor(algorithm); dec.IV = IV; // Go ahead and decrypt. byte[] plainText = dec.Decrypt(cipherText, key); // Look at your plain text. Console.WriteLine(" Plain text: " + Encoding.ASCII.GetString(plainText)); } catch(Exception ex) { Console.WriteLine("Exception decrypting. " + ex.Message); return; }
On the Build menu, click Build Solution.
Run the test application to verify the operation of the Encryptor and Decryptor classes.
For more information, see "Appendix " in the Reference section of this book.