Broken Thoughts

Techknowledge

.Net Dojo: Data Encryption

Encryption is a vital part to any web application. It is important that all variables, both in cookies and in the query string are encrypted for protection. By exposing your variables such as http://www.mysite.com/?x=10000 you leave it open for people to experiment with putting in different Id’s. While your code should make sure that anyone requesting content with the specified Id, it is a good practice to encrypt all your values to make sure no one can guess through your website.

Also, if you do not encrypt a cookie, you leave the contents open to anyone who would use an XSS attack to pull the cookie across domains. Leaving the cookie open just leaves hackers another back door into your site. To be fair, if a hacker was able to steal a cookie set on your domain, they would therefore be able to set the same cookie for them self. While encryption can’t stop an attacker from mimicking a user via their cookie, it could hinder them from learning what information you are storing.

In current cryptography, there are currently 3 major algorithms used. The first algorithm is Data Encryption Standard (DES). In .NET, DES uses 64 bit encryption. The next step up is Triple DES which uses 3 separate DES keys to provide 128 bit encryption. The most secure algorithm and the current standard used by the Department of Defense is AES or Rijndael. In .Net, Rijndael provides 256 bit encryption.

Each of the three algorithms require a key and an initialization vector. Ideally, you would want to set a string in the config file and use that string as the key or IV. The key and IV must be a byte array. To create a key from a string, you would need to implement the following code.


                char[] userKeyChars = new char[0];

                if (!String.IsNullOrEmpty(ConfigurationManager.AppSettings["EncryptionKey"]))
                    {
                        userKeyChars = System.Configuration.ConfigurationManager.AppSettings["EncryptionKey"].ToCharArray();
                    }

                    byte[] key = new byte[userKeyChars.Length];

                //only replace up to the number of bytes in the key override
                for (int x = 0; x < userKeyChars.Length; x++)
                {
                    key[x] = Convert.ToByte(userKeyChars[x]);
                }

                return key;

You would need to use the same code to build the key and the IV, and you would want different strings for the key and iv. The algorithm you would like to use will determine how long the string needs to be. Triple DES uses a 24 character string, 16 characters for AES, and 8 characters for DES.A best practice would be to use a different encryption key and IV for each application you build, and making it part of the config means it is easy to set, and easy to change. The only problem is that if you store encrypted data in the database, any time you change the key you will need to decrypt the values with the old key and encrypt them with the new key. If you don’t, your user’s will suddenly find them self unable to login if you are storing things like encrypted passwords.

The above example is part of a class called EncryptionKey, which has properties for the Key and IV for each algorithm. Next, I have an enumeration called Algorithm with the 3 choices. This allows the developer to switch between algorithms seamlessly. While you could make the encryption key 24 characters and use portions of it for all 3 algorithms, it would be possible to change the key and only change 1 or 2 of the algorithms.

Now we come to the meat and potatoes of the encryption library.


using System;
using System.Diagnostics;
using System.Security.Cryptography;
using System.Text;
using System.IO;

namespace Olympus
{
	///
	/// Encrypt text with multiple algorithms utilizing a default key or (full or partial) user defined key.
    /// Rijndael is the default algorithm because it is the most secure and the most efficient encryption algorithm
	///
	public static class Encryption
	{

        #region Encryption

        ///
        /// Encrypt the text to Rijndael
        ///
        /// the text to encrypt
        /// string of encrypted data
        public static string Encrypt(string text)
		{
            return Encrypt(text, Algorithm.Rijndael);
		}

        ///
        /// Encrypt the text using the selected algorithm
        ///
        /// The text to encrypt
        /// The algorithm to use
        /// System.String
        public static string Encrypt(string text, Algorithm algorithm)
        {
            if (!String.IsNullOrEmpty(text))
            {
                MemoryStream ms = new MemoryStream();
                CryptoStream cs;
                byte[] key = new byte[0];
                byte[] iv = new byte[0];

                //The algorithms used are considered the most secure.  Rijndael, or AES is by far the most efficient
                //  and most secure algorithm in use, and is endorsed by the US government as of 2007.
                #region Algorithm Selection
                if (algorithm == Algorithm.TripleDES)
                {
                    key = EncryptionKey.TripleDESKey;
                    iv = EncryptionKey.TripleDESIV;
                    TripleDESCryptoServiceProvider cryptoProvider = new TripleDESCryptoServiceProvider();
                    cs = new CryptoStream(ms, cryptoProvider.CreateEncryptor(key, iv), CryptoStreamMode.Write);
                }
                else if (algorithm == Algorithm.DES)
                {
                    key = EncryptionKey.DESKey;
                    iv = EncryptionKey.DESIV;
                    DESCryptoServiceProvider cryptoProvider = new DESCryptoServiceProvider();
                    cs = new CryptoStream(ms, cryptoProvider.CreateEncryptor(key, iv), CryptoStreamMode.Write);
                }
                else
                {
                    //  rijndael is the most secure and unbreakable encryption algorithm available
                    //  for this reason it should be the default
                    key = EncryptionKey.RijndaelKey;
                    iv = EncryptionKey.RijndaelIV;
                    RijndaelManaged cryptoProvider = new RijndaelManaged();
                    cs = new CryptoStream(ms, cryptoProvider.CreateEncryptor(key, iv), CryptoStreamMode.Write);
                }
                #endregion

                StreamWriter sw = new StreamWriter(cs);
                sw.Write(text);
                sw.Flush();
                cs.FlushFinalBlock();
                ms.Flush();
                return Convert.ToBase64String(ms.GetBuffer(), 0, Convert.ToInt32(ms.Length));
            }
            return "";
        }

        ///
        /// Encrypt a Querystring and encode for use in Querystrings using Rijndael
        ///
        /// The text to encrypt
        /// System.String
		public static string EncryptQueryString(string text)
		{
            return EncryptQueryString(text, Algorithm.Rijndael);
        }

        ///
        /// Encrypt a Querystring and encode for use in Querystrings
        ///
        /// The text to encrypt
        /// The algorithm to use for encrypting the data
        /// System.String
        public static string EncryptQueryString(string text, Algorithm algorithm)
        {
            if (String.IsNullOrEmpty(text))
            {
                return "";
            }

            return Encode(Encrypt(text, algorithm));
        }

        #endregion

        #region Decryption

        ///
        /// Decrypt a querystring value that is encrypted in Rijndael
        ///
        /// The text to decrypt
        /// System.String
        public static string DecryptQueryString(string text)
        {
            return DecryptQueryString(text, Algorithm.Rijndael);
        }

        ///
        /// Decrypt a querystring value
        ///
        /// The text to decrypt
        /// The algorithm to use for decryption
        /// System.String
        public static string DecryptQueryString(string text, Algorithm algorithm)
        {
            if (String.IsNullOrEmpty(text))
            {
                return "";
            }

            return Decrypt(Decode(text), algorithm);
        }

        ///
        /// Decrypt text that is encrypted in Rijndael
        ///
        /// the encrypted text to decrypt
        /// System.String
        public static string Decrypt(string text)
        {
            return Decrypt(text, Algorithm.Rijndael);
        }

        ///
        /// Decrypt text
        ///
        /// The encrypted text to decrypt
        /// The algorithm to use for decryption
        /// System.String
        public static string Decrypt(string text, Algorithm algorithm)
        {
            if (!String.IsNullOrEmpty(text))
            {
                byte[] buffer = Convert.FromBase64String(text);
                MemoryStream ms = new MemoryStream(buffer);
                CryptoStream cs;
                byte[] key = new byte[0];
                byte[] iv = new byte[0];

                #region Algorithm Selection
                if (algorithm == Algorithm.TripleDES)
                {
                    key = EncryptionKey.TripleDESKey;
                    iv = EncryptionKey.TripleDESIV;
                    TripleDESCryptoServiceProvider cryptoProvider = new TripleDESCryptoServiceProvider();
                    cs = new CryptoStream(ms, cryptoProvider.CreateDecryptor(key, iv), CryptoStreamMode.Read);
                }
                else if (algorithm == Algorithm.DES)
                {
                    key = EncryptionKey.DESKey;
                    iv = EncryptionKey.DESIV;
                    DESCryptoServiceProvider cryptoProvider = new DESCryptoServiceProvider();
                    cs = new CryptoStream(ms, cryptoProvider.CreateDecryptor(key, iv), CryptoStreamMode.Read);
                }
                else
                {
                    key = EncryptionKey.RijndaelKey;
                    iv = EncryptionKey.RijndaelIV;
                    RijndaelManaged cryptoProvider = new RijndaelManaged();
                    cs = new CryptoStream(ms, cryptoProvider.CreateDecryptor(key, iv), CryptoStreamMode.Read);
                }
                #endregion

                StreamReader sr = new StreamReader(cs);
                return sr.ReadToEnd();
            }
            return "";
        }
        #endregion

        #region Encoding
        ///
        /// Encode a string for http and make it safe for the querystring
        ///
        /// the text to encode
        /// System.String
        private static string Encode(string text)
        {
            //replace all + with the unused @ because querystring methods do not know how to handle a +
            return System.Web.HttpUtility.UrlEncode(text.Replace("+", "@"));
        }

        ///
        /// Decode a string that has been encoded
        ///
        /// the text to encode
        /// System.String
        private static string Decode(string text)
        {
            //replace all @ back to + and replace all spaces to a +
            //leaving this step out can cause errors!!
            return System.Web.HttpUtility.UrlDecode(text).Replace("@", "+").Replace(" ", "+");
        }
        #endregion
    }
}

Firstly, there are two methods of encryption. EncryptQuerystring and DecryptQueryString produce an encrypted string that can be stored in a cookie and in the query string and still be decrypted without producing errors. This is done by replacing a few special characters with characters not used in the query string.The root method of encryption and decryption uses the System.Security.Cryptography namespace. By using a memory stream, we are encrypting the data in memory, and then disposing of the object after the method is completed. This is a more secure route if encryption is done on the computer with the application, such as in a windows application. However, a better practice would be to have a server store the code and do the encryption and decryption. You should never have the decryption code on the client machine. .Net Reflector makes it too easy for anyone to pull your code apart (even dotfusicated code), extract your encryption key, and start playing with your server.

Encryption is a very important part of any web application. Used correctly it can be a very powerful tool to combat attacks.

January 14, 2008 - Posted by Broken Bokken | .Net | , , , , , , , , , , , , , , , , | 3 Comments

3 Comments »

  1. Nice work.

    could give me whole library code.

    Thanks
    ravi

    Comment by Ravi | April 17, 2008

  2. Could you e-mail us as we have some questions about this code.

    Comment by UCMoBOL | April 20, 2008

  3. I don’t typically do e-mail contact. If you have questions about the code you may ask them here. Thanks.

    Comment by Broken Bokken | May 15, 2008

Leave a comment