Building Secure ASP.NET Applications: Authentication, Authorization, and Secure Communication

Retired Content

This content is outdated and is no longer being maintained. It is provided as a courtesy for individuals who are still using these technologies. This page may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist.

 

patterns & practices Developer Center

How To: Create an Encryption Library in .NET 1.1

J.D. Meier, Alex Mackman, Michael Dunner, and Srinath Vasireddy
Microsoft Corporation

Published: November 2002

Last Revised: January 2006

Applies to:

  • .NET Framework 1.1

See the "patterns & practices Security Guidance for Applications Index" for links to additional security resources.

See the Landing Page for a starting point and complete overview of Building Secure ASP.NET Applications.

Summary: This How To shows you how to create a managed class library to provide encryption functionality for applications. It allows an application to choose the encryption algorithm. Supported algorithms include DES, Triple DES, RC2, and Rijndael. (15 printed pages)

Contents

Summary of Steps Step 1. Create a C# Class Library Step 2. Create a Console Test Application
Additional Resources

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 How To: Store an Encrypted Connection String in the Registry in ASP.NET 1.1 in the Reference section of this guide.

Summary of Steps

This How To includes the following steps:

  • Step 1. Create a C# Class Library
  • Step 2. Create a Console Test Application

Step 1. Create a C# Class Library

This procedure creates a C# class library, which will provide encryption and decryption functionality.

To create a C# class library

  1. Start Visual Studio .NET and create a new C# Class Library project called Encryption.

  2. Use Solution Explorer to rename class1.cs as EncryptTransformer.cs.

  3. In EncryptTransformer.cs, rename Class1 as EncryptTransformer.

  4. Change the scope of the class from public to internal.

    internal class EncryptTransformer
    
  5. Add the following using statement at the top of the file.

    using System.Security.Cryptography;
    
  6. Add the following enumerated type within the Encryption namespace.

    public enum EncryptionAlgorithm {Des = 1, Rc2, Rijndael, TripleDes};
    
  7. Add the following private member variables to the EncryptTransformer class.

    private EncryptionAlgorithm algorithmID;
    private byte[] initVec;
    private byte[] encKey;
    
  8. Replace the default constructor with the following constructor.

    internal EncryptTransformer(EncryptionAlgorithm algId)
    {
      //Save the algorithm being used.
      algorithmID = algId;
    }
    
  9. Add the following method to the class.

    internal ICryptoTransform GetCryptoServiceProvider(byte[] bytesKey,
    byte[] initVec)
    {
      // 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.");
        }
      }
    }
    
  10. Add the following properties to the class.

    internal byte[] IV
    {
      get{return initVec;}
      set{initVec = value;}
    }
    internal byte[] Key
    {
      get{return encKey;}
    }
    
  11. Add a new class called DecryptTransformer to the project.

  12. Add the following using statement at the top of the DecryptTransformer.cs file.

    using System.Security.Cryptography;
    
  13. Change the class scope from public to internal.

  14. Replace the default constructor with the following constructor.

    internal DecryptTransformer(EncryptionAlgorithm deCryptId)
    {
      algorithmID = deCryptId;
    }
    
    
  15. Add the following private variables to the class.

    private EncryptionAlgorithm algorithmID;
    private byte[] initVec;
    
  16. Add the following method to the class.

    internal ICryptoTransform GetCryptoServiceProvider(byte[] bytesKey,
    byte[] initVec)
    {
      // 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
    
  17. Add the following property to the class.

    internal byte[] IV
    {
      set{initVec = value;}
    }
    
  18. Add a new class called Encryptor to the project.

  19. Add the following using statements at the top of Encryptor.cs.

    using System.Security.Cryptography;
    using System.IO;
    
  20. Replace the default constructor with the following constructor.

    public Encryptor(EncryptionAlgorithm algId)
    {
      transformer = new EncryptTransformer(algId);
    }
    
  21. Add the following private member variables to the class.

    private EncryptTransformer transformer;
    private byte[] initVec;
    private byte[] encKey;
    
  22. Add the following Encrypt method to the class.

    public byte[] Encrypt(byte[] bytesData, byte[] bytesKey,
     byte[] initVec)
    {
      //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: \n" 
                            + ex.Message);
      }
      //Set the IV and key for the client to retrieve
      encKey = transformer.Key;
      encStream.FlushFinalBlock();
      encStream.Close();
    
      //Send the data back.
      return memStreamEncryptedData.ToArray();
    }//end Encrypt
    
  23. Add the following properties to the class.

    public byte[] IV
    {
      get{return initVec;}
      set{initVec = value;}
    }
    
    public byte[] Key
    {
      get{return encKey;}
    }
    
  24. Add a new class called Decryptor to the project.

  25. Add the following using statements at the top of Decryptor.cs

    using System.Security.Cryptography;
    using System.IO;
    
  26. Replace the default constructor with the following constructor.

    public Decryptor(EncryptionAlgorithm algId)
    {
      transformer = new DecryptTransformer(algId);
    }
    
  27. Add the following private member variables to the class.

    private DecryptTransformer transformer;
    private byte[] initVec;
    
  28. Add the following Decrypt method to the class.

    public byte[] Decrypt(byte[] bytesData, byte[] bytesKey,
     byte[] initVec)
    {
      //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: \n" 
                            + ex.Message);
      }
      decStream.FlushFinalBlock();
      decStream.Close();
      // Send the data back.
      return memStreamDecryptedData.ToArray();
    } //end Decrypt
    
  29. Add the following property to the class.

    public byte[] IV
    {
      set{initVec = value;}
    }
    
  30. On the Build menu, click BuildSolution.

Step 2. Create a Console Test Application

This procedure creates a simple console test application to test the encryption and decryption functionality.

To create a console test application

  1. Add a new C# Console application called EncryptionTester to the current solution.

  2. In Solution Explorer, right-click the EncryptionTester project, and then click Set as StartUp Project.

  3. Use Solution Explorer to rename class1.cs as EncryptionTest.cs.

  4. In EncryptionTest.cs, rename Class1 as EncryptionTest.

  5. Add a project reference to the Encryption project.

  6. Add the following using statements at the top of EncryptionTest.cs.

    using System.Text;
    using Encryption;
    
  7. 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;
    }
    
  8. On the Build menu, click BuildSolution.

  9. Run the test application to verify the operation of the Encryptor and Decryptor classes.

Additional Resources

For more information, see "How To: Store an Encrypted Connection String in the Registry" in the Reference section of this guide.

patterns & practices Developer Center

Retired Content

This content is outdated and is no longer being maintained. It is provided as a courtesy for individuals who are still using these technologies. This page may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist.

© Microsoft Corporation. All rights reserved.