Link to home
Start Free TrialLog in
Avatar of Brian
BrianFlag for United States of America

asked on

Encrypt / Decrypt using Rigndael Algorithm

Hello Experts,

I have been having a very had time trying to find a correct way to Encrypt and Decrypt data to my Database.

I know that I would like to use Symmetric Algorithm and use the Rigndael Algorithm for ASP.NET 4.0 using C# as the language. I will need to Encrypt and Decrypt the following field types (int, string, datetime) data in my Database.

So to recap, I need help with the following below:

1.) Code to Encrypt / Decrypt Sensitive Data in DB using Symmetric Algoithm and using Rigndael for the Algorithm.

2.) Need to be able to Encrypt / Decrypt the following types of data (int, string, and datetime) values.
Avatar of chrisbray
chrisbray
Flag of United Kingdom of Great Britain and Northern Ireland image

Try this tutorial which appears to cover your requirements with example code in C#:

http://www.obviex.com/samples/Encryption.aspx

This one has code that you can use without writing anything, and some elegant code to look at if you want to roll your own:

http://www.codeproject.com/KB/security/DotNetCrypto.aspx
Avatar of Brian

ASKER

chrisbray:

I already tried http://www.obviex.com/samples/Encryption.aspx and did NOT have any luck.

I also already tried http://www.codeproject.com/KB/security/DotNetCrypto.aspx but it's not complete nor does it go into any detail on how to complete it.
Hi  asp_net2:

Not sure why you are having trouble.  See the code below, support class and demo winforms app based on the first link I sent you and fully working..

A couple of points:  

In the database you cannot store encrypted DateTime or Int values as they are no longer of that type.  You will need to convert them to and from strings
The supplied code uses PasswordDeriveBytes which is now officially obsolete.  It is fairly simple to replace it with Rfc2898DeriveBytes which is the preferred object now

I have also attached a working demo solution for you.

Hope that helps.

Chris Bray

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

namespace EncryptionDemo
{
    public class RijndaelSupport
    {
        /// <summary>
        /// Encrypts specified plaintext using Rijndael symmetric key algorithm
        /// and returns a base64-encoded result.
        /// </summary>
        /// <param name="plainText">
        /// Plaintext value to be encrypted.
        /// </param>
        /// <param name="passPhrase">
        /// Passphrase from which a pseudo-random password will be derived. The
        /// derived password will be used to generate the encryption key.
        /// Passphrase can be any string. In this example we assume that this
        /// passphrase is an ASCII string.
        /// </param>
        /// <param name="saltValue">
        /// Salt value used along with passphrase to generate password. Salt can
        /// be any string. In this example we assume that salt is an ASCII string.
        /// </param>
        /// <param name="hashAlgorithm">
        /// Hash algorithm used to generate password. Allowed values are: "MD5" and
        /// "SHA1". SHA1 hashes are a bit slower, but more secure than MD5 hashes.
        /// </param>
        /// <param name="passwordIterations">
        /// Number of iterations used to generate password. One or two iterations
        /// should be enough.
        /// </param>
        /// <param name="initVector">
        /// Initialization vector (or IV). This value is required to encrypt the
        /// first block of plaintext data. For RijndaelManaged class IV must be 
        /// exactly 16 ASCII characters long.
        /// </param>
        /// <param name="keySize">
        /// Size of encryption key in bits. Allowed values are: 128, 192, and 256. 
        /// Longer keys are more secure than shorter keys.
        /// </param>
        /// <returns>
        /// Encrypted value formatted as a base64-encoded string.
        /// </returns>
        public static string Encrypt(string plainText,
                                     string passPhrase,
                                     string saltValue,
                                     string hashAlgorithm,
                                     int passwordIterations,
                                     string initVector,
                                     int keySize)
        {
            // Convert strings into byte arrays.
            // Let us assume that strings only contain ASCII codes.
            // If strings include Unicode characters, use Unicode, UTF7, or UTF8 
            // encoding.
            byte[] initVectorBytes = Encoding.ASCII.GetBytes(initVector);
            byte[] saltValueBytes = Encoding.ASCII.GetBytes(saltValue);

            // Convert our plaintext into a byte array.
            // Let us assume that plaintext contains UTF8-encoded characters.
            byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText);

            // First, we must create a password, from which the key will be derived.
            // This password will be generated from the specified passphrase and 
            // salt value. The password will be created using the specified hash 
            // algorithm. Password creation can be done in several iterations.
            var password = new PasswordDeriveBytes(passPhrase, saltValueBytes, hashAlgorithm, 
                                                            passwordIterations);
            
            // Use the password to generate pseudo-random bytes for the encryption
            // key. Specify the size of the key in bytes (instead of bits).
            byte[] keyBytes = password.GetBytes(keySize / 8);

            // Create uninitialized Rijndael encryption object.
            var symmetricKey = new RijndaelManaged();

            // It is reasonable to set encryption mode to Cipher Block Chaining
            // (CBC). Use default options for other symmetric key parameters.
            symmetricKey.Mode = CipherMode.CBC;

            // Generate encryptor from the existing key bytes and initialization 
            // vector. Key size will be defined based on the number of the key 
            // bytes.
            ICryptoTransform encryptor = symmetricKey.CreateEncryptor(
                                                             keyBytes,
                                                             initVectorBytes);

            // Define memory stream which will be used to hold encrypted data.
            var memoryStream = new MemoryStream();

            // Define cryptographic stream (always use Write mode for encryption).
            var cryptoStream = new CryptoStream(memoryStream,
                                                         encryptor,
                                                         CryptoStreamMode.Write);
            // Start encrypting.
            cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);

            // Finish encrypting.
            cryptoStream.FlushFinalBlock();

            // Convert our encrypted data from a memory stream into a byte array.
            byte[] cipherTextBytes = memoryStream.ToArray();

            // Close both streams.
            memoryStream.Close();
            cryptoStream.Close();

            // Convert encrypted data into a base64-encoded string.
            string cipherText = Convert.ToBase64String(cipherTextBytes);

            // Return encrypted string.
            return cipherText;
        }

        /// <summary>
        /// Decrypts specified ciphertext using Rijndael symmetric key algorithm.
        /// </summary>
        /// <param name="cipherText">
        /// Base64-formatted ciphertext value.
        /// </param>
        /// <param name="passPhrase">
        /// Passphrase from which a pseudo-random password will be derived. The
        /// derived password will be used to generate the encryption key.
        /// Passphrase can be any string. In this example we assume that this
        /// passphrase is an ASCII string.
        /// </param>
        /// <param name="saltValue">
        /// Salt value used along with passphrase to generate password. Salt can
        /// be any string. In this example we assume that salt is an ASCII string.
        /// </param>
        /// <param name="hashAlgorithm">
        /// Hash algorithm used to generate password. Allowed values are: "MD5" and
        /// "SHA1". SHA1 hashes are a bit slower, but more secure than MD5 hashes.
        /// </param>
        /// <param name="passwordIterations">
        /// Number of iterations used to generate password. One or two iterations
        /// should be enough.
        /// </param>
        /// <param name="initVector">
        /// Initialization vector (or IV). This value is required to encrypt the
        /// first block of plaintext data. For RijndaelManaged class IV must be
        /// exactly 16 ASCII characters long.
        /// </param>
        /// <param name="keySize">
        /// Size of encryption key in bits. Allowed values are: 128, 192, and 256.
        /// Longer keys are more secure than shorter keys.
        /// </param>
        /// <returns>
        /// Decrypted string value.
        /// </returns>
        /// <remarks>
        /// Most of the logic in this function is similar to the Encrypt
        /// logic. In order for decryption to work, all parameters of this function
        /// - except cipherText value - must match the corresponding parameters of
        /// the Encrypt function which was called to generate the
        /// ciphertext.
        /// </remarks>
        public static string Decrypt(string cipherText,
                                     string passPhrase,
                                     string saltValue,
                                     string hashAlgorithm,
                                     int passwordIterations,
                                     string initVector,
                                     int keySize)
        {
            // Convert strings defining encryption key characteristics into byte
            // arrays. Let us assume that strings only contain ASCII codes.
            // If strings include Unicode characters, use Unicode, UTF7, or UTF8
            // encoding.
            byte[] initVectorBytes = Encoding.ASCII.GetBytes(initVector);
            byte[] saltValueBytes = Encoding.ASCII.GetBytes(saltValue);

            // Convert our ciphertext into a byte array.
            byte[] cipherTextBytes = Convert.FromBase64String(cipherText);

            // First, we must create a password, from which the key will be 
            // derived. This password will be generated from the specified 
            // passphrase and salt value. The password will be created using
            // the specified hash algorithm. Password creation can be done in
            // several iterations.
            var password = new PasswordDeriveBytes(
                                                            passPhrase,
                                                            saltValueBytes,
                                                            hashAlgorithm,
                                                            passwordIterations);

            // Use the password to generate pseudo-random bytes for the encryption
            // key. Specify the size of the key in bytes (instead of bits).
            byte[] keyBytes = password.GetBytes(keySize / 8);

            // Create uninitialized Rijndael encryption object.
            var symmetricKey = new RijndaelManaged();

            // It is reasonable to set encryption mode to Cipher Block Chaining
            // (CBC). Use default options for other symmetric key parameters.
            symmetricKey.Mode = CipherMode.CBC;

            // Generate decryptor from the existing key bytes and initialization 
            // vector. Key size will be defined based on the number of the key 
            // bytes.
            ICryptoTransform decryptor = symmetricKey.CreateDecryptor(
                                                             keyBytes,
                                                             initVectorBytes);

            // Define memory stream which will be used to hold encrypted data.
            var memoryStream = new MemoryStream(cipherTextBytes);

            // Define cryptographic stream (always use Read mode for encryption).
            var cryptoStream = new CryptoStream(memoryStream,
                                                          decryptor,
                                                          CryptoStreamMode.Read);

            // Since at this point we don't know what the size of decrypted data
            // will be, allocate the buffer long enough to hold ciphertext;
            // plaintext is never longer than ciphertext.
            var plainTextBytes = new byte[cipherTextBytes.Length];

            // Start decrypting.
            int decryptedByteCount = cryptoStream.Read(plainTextBytes,
                                                       0,
                                                       plainTextBytes.Length);

            // Close both streams.
            memoryStream.Close();
            cryptoStream.Close();

            // Convert decrypted data into a string. 
            // Let us assume that the original plaintext string was UTF8-encoded.
            string plainText = Encoding.UTF8.GetString(plainTextBytes,
                                                       0,
                                                       decryptedByteCount);

            // Return decrypted string.   
            return plainText;
        }
    }
}


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace EncryptionDemo
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void ResetButtonClick(object sender, EventArgs e)
        {
            Reset();
        }

        private void Reset()
        {
            textToEncryptTextBox.Text = @"My Test Text To Be Encrypted";
            passwordTextBox.Text = @"Pas5w0rd";
            encryptedTextDetailLabel.Text = string.Empty;
            decryptedTextDetailLabel.Text = string.Empty;

        }

        private void Form1Load(object sender, EventArgs e)
        {
            Reset();
        }

        private void EncryptButtonClick(object sender, EventArgs e)
        {
            encryptedTextDetailLabel.Text = RijndaelSupport.Encrypt(textToEncryptTextBox.Text, passwordTextBox.Text,
                                                                    "s@1tValue", "SHA1", 2,
                                                                    "@1B2c3D4e5F6g7H8", 256);
        }

        private void DecryptButtonClick(object sender, EventArgs e)
        {
            decryptedTextDetailLabel.Text = RijndaelSupport.Decrypt(encryptedTextDetailLabel.Text, passwordTextBox.Text,
                                                                    "s@1tValue", "SHA1", 2,
                                                                    "@1B2c3D4e5F6g7H8", 256);
        }
    }
}

Open in new window

EncryptionDemo.zip
Avatar of Brian

ASKER

Hi chrisbray,

Ok, so scrath what you just provided to me :) You beat me to the post :) I found a book with a very good tutorial and explanation on this stuff and I have been able to successfully Encrypt but I'm having a problem Decrypting the data now from my DB. I'm going to attach the Class that I created along with the code that I used to Encrypt and what I have now for Decrypting which is what I'm having trouble with.

If you have trouble understanding what I supplied then I can somehow send you the pages I was looking at. The book is called "Pro ASP.NET 4 in C# 2010" and it's by Apress.

Hopefully you can figure out how to Decrypt what I supplied to you. I was able to Encrypt fiine.

Also, It did create a key.config file within my application and I know that I need that file to Decrypt, just not sure how to Decrypt now :(
CLASS:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.IO;
using System.Text;
using System.Security.Cryptography;

/// <summary>
/// Summary description for Class1
/// </summary>
public class Class1
{
    public static class SymmetricEncryptionUtility
    {
        private static bool _ProtectKey;
        private static string _AlgorithmName;
        public static string AlgorithmName
        {
            get { return _AlgorithmName; }
            set { _AlgorithmName = value; }
        }

        public static bool ProtectKey
        {
            get { return _ProtectKey; }
            set { _ProtectKey = value; }
        }

        public static void GenerateKey(string targetFile)
        {
            // Create the algorithm
            SymmetricAlgorithm Algorithm = SymmetricAlgorithm.Create(AlgorithmName);
            Algorithm.GenerateKey();
            // Now get the key
            byte[] Key = Algorithm.Key;
            if (ProtectKey)
            {
                // Use DPAPI to encrypt key
                Key = ProtectedData.Protect(
                Key, null, DataProtectionScope.LocalMachine);
            }
            // Store the key in a file called key.config
            using (FileStream fs = new FileStream(targetFile, FileMode.Create))
            {
                fs.Write(Key, 0, Key.Length);
            }
        }

        public static void ReadKey(SymmetricAlgorithm algorithm, string keyFile)
        {
            byte[] Key;
            using (FileStream fs = new FileStream(keyFile, FileMode.Open))
            {
                Key = new byte[fs.Length];
                fs.Read(Key, 0, (int)fs.Length);
            }
            if (ProtectKey)
                algorithm.Key = ProtectedData.Unprotect(
                Key, null, DataProtectionScope.LocalMachine);
            else
                algorithm.Key = Key;
        }

        public static byte[] EncryptData(string data, string keyFile)
        {
            // Convert string data to byte array
            byte[] ClearData = System.Text.Encoding.UTF8.GetBytes(data);
            // Now create the algorithm
            SymmetricAlgorithm Algorithm = SymmetricAlgorithm.Create(AlgorithmName);

            // Encrypt information

            MemoryStream Target = new MemoryStream();
            // Generate a random initialization vector (IV) to use for the algorithm
            Algorithm.GenerateIV();
            Target.Write(Algorithm.IV, 0, Algorithm.IV.Length);
            // Encrypt actual data
            CryptoStream cs = new CryptoStream(Target,
            Algorithm.CreateEncryptor(), CryptoStreamMode.Write);
            cs.Write(ClearData, 0, ClearData.Length);
            cs.FlushFinalBlock();
            // Return the encrypted stream of data as a byte array
            return Target.ToArray();
        }

        public static string DecryptData(byte[] data, string keyFile)
        {
            // Create the algorithm
            SymmetricAlgorithm Algorithm = SymmetricAlgorithm.Create(AlgorithmName);

            // Decrypt information
            MemoryStream Target = new MemoryStream();
            // Read IV and initialize the algorithm with it
            int ReadPos = 0;
            byte[] IV = new byte[Algorithm.IV.Length];
            Array.Copy(data, IV, IV.Length);
            Algorithm.IV = IV;
            ReadPos += Algorithm.IV.Length;

            CryptoStream cs = new CryptoStream(Target,
            Algorithm.CreateDecryptor(), CryptoStreamMode.Write);
            cs.Write(data, ReadPos, data.Length - ReadPos);
            cs.FlushFinalBlock();
            // Get the bytes from the memory stream and convert them to text
            return Encoding.UTF8.GetString(Target.ToArray());
        }
    }
}

Open in new window

CODE TO ENCRYPT:

using System;
using System.Configuration;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.IO;
using System.Net.Mail;
using System.Net.NetworkInformation;
using System.Security.Cryptography;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.Security;

public partial class programinfo_ghap_newuser : System.Web.UI.Page
{
    private string KeyFileName;
    private string AlgorithmName = "Rijndael";
    protected void Page_Load(object sender, EventArgs e)
    {
        //Class1.SymmetricEncryptionUtility.AlgorithmName = AlgorithmName;
        //KeyFileName = Server.MapPath("./") + "\\symmetric_key.config";

        // Configure Encryption Utility 
        KeyFileName = Server.MapPath("key.config");
        Class1.SymmetricEncryptionUtility.AlgorithmName = "Rijndael";
        if (!System.IO.File.Exists(KeyFileName))
        {
            Class1.SymmetricEncryptionUtility.GenerateKey(KeyFileName);
        }



        if (!IsPostBack)
        {
            RetrieveClientValues();
            RetrieveBldgLocationValues();
            RetrieveClientsStatesValues();
        }

        // DISABLE PANELS
        pWork.Visible = false;
        pHome.Visible = false;
    }

    protected void btn_NewUser_Click(object sender, EventArgs e)
    {
        using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["HealthCourses"].ConnectionString))
        {
            SqlCommand cmd = new SqlCommand();
            cmd.CommandText = "HealthCourses_InsertUsers";
            cmd.CommandType = CommandType.StoredProcedure;
            cmd.Connection = conn;

            cmd.Parameters.Add("@c_id", SqlDbType.Int).Value = ddlClient.SelectedItem.Value;
            cmd.Parameters.Add("@bldg_id", SqlDbType.Int).Value = ddlBldgLocation.SelectedItem.Value;
            cmd.Parameters.Add("@s_id", SqlDbType.Int).Value = ddlState.SelectedItem.Value;
            cmd.Parameters.Add("@users_flname", SqlDbType.VarChar, 50).Value = txtName.Text;
            cmd.Parameters.Add("@users_street_address", SqlDbType.VarChar, 50).Value = txtHomeAddress.Text;
            cmd.Parameters.Add("@users_city", SqlDbType.VarChar, 50).Value = txtCity.Text;
            cmd.Parameters.Add("@users_zip", SqlDbType.VarChar, 5).Value = txtZipCode.Text;
            cmd.Parameters.Add("@users_phone", SqlDbType.VarChar, 12).Value = txtHomePhone.Text;
            cmd.Parameters.Add("@users_work_ext", SqlDbType.VarChar, 4).Value = txtWorkExtension.Text;
            cmd.Parameters.Add("@users_email", SqlDbType.VarChar, 100).Value = txtEmailAddress.Text;
            cmd.Parameters.Add("@users_username", SqlDbType.VarChar, 50).Value = txtUserName.Text;
            cmd.Parameters.Add("@users_password", SqlDbType.Binary, 96).Value = PasswordHash.HashPassword(txtPassword.Text);
            
            byte[] data = Class1.SymmetricEncryptionUtility.EncryptData(txtCreditCard.Text, KeyFileName);
            cmd.Parameters.AddWithValue("@users_cc", data);

            try
            {
                conn.Open();

                cmd.ExecuteNonQuery();

                btn_NewUser.Enabled = false;
        
                Response.Redirect("newuser_success.aspx");
            }

            catch (Exception ex)
            {
                btn_NewUser.Enabled = true;
                lblInsertError.Text = ex.Message.ToString();
            }
        }
    }
}

Open in new window

CODE TO DECRYPT:

using System;
using System.Configuration;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.IO;
using System.Net.Mail;
using System.Net.NetworkInformation;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

public partial class programinfo_ghap_resetpassword : System.Web.UI.Page
{
    public class MyClass
    {
        public string userscc { get; set; }
    }

    private string KeyFileName;
    protected void Page_Load(object sender, EventArgs e)
    {
        RetrieveUsersIDFullName();
    }

    protected void RetrieveUsersIDFullName()
    {
        KeyFileName = Server.MapPath("key.config");
        Class1.SymmetricEncryptionUtility.AlgorithmName = "Rijndael";

        int User = Convert.ToInt32(Request.QueryString["users_id"]);

        using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["HealthCourses"].ConnectionString))
        {
            SqlCommand cmd = new SqlCommand();
            cmd.CommandText = "HealthCourses_RetrieveUsersIDFullName";
            cmd.CommandType = CommandType.StoredProcedure;
            cmd.Connection = conn;

            cmd.Parameters.Add("@users_id", SqlDbType.Int).Value = User;

            try
            {
                conn.Open();

                List<MyClass> myClasses = new List<MyClass>();
                SqlDataReader rdr = cmd.ExecuteReader();

                if (rdr.Read())
                {
                    MyClass myClass = new MyClass();

                    hf_users_id.Value = rdr["users_id"].ToString();
                    lblUsersFullName.Text = rdr["users_flname"].ToString();

                    byte[] Email = (byte[])rdr["users_cc"];
                    string EmailData = Class1.SymmetricEncryptionUtility.DecryptData(Email, KeyFileName);
                    myClass.userscc = EmailData;
                }
            }

            catch (Exception ex)
            {
                ex.Message.ToString();
            }
        }
    }

Open in new window

In your samples you are encrypting credit card details but decrypting email... is that related?

I don't have all your data stuff to work with, but in any case the way to resolve the problem is to go back to basics - encrypt and decrypt a simple string, ignoring the database stuff completely.  If that works, all you have to do is use exactly the same procedure with the values passed to and retrieved from the database.

Plus, of course, if we are working with a simple string I may be able to help you figure it out if it doesn't work :-)
Avatar of Brian

ASKER

>> In your samples you are encrypting credit card details but decrypting email... is that related?
Sorry, yes it was the same. Please see the updated code be Decryption. I changed the names around to make more sense :)

>> Plus, of course, if we are working with a simple string I may be able to help you figure it out if it doesn't work :-)

I'm currently for testing purposes Encrypting / Decrypting string Data. The data that is getting Encrypted is stored in the DB as Varchar data. I know that CreditCard data is usually int values but for testing purposes I just tried it with simplet string values for now.

I tried to go from the beginning but I can't get that data to return to the following lines below. I debugged the following lines below but I keep getting "null" returned for each of the following below.

Also, I need the data that get Decrypted below to be displayed by a label control labeled Label1.

byte[] CreditCard = (byte[])rdr["users_cc"];
string CreditCardData = Class1.SymmetricEncryptionUtility.DecryptData(CreditCard, KeyFileName);
using System;
using System.Configuration;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.IO;
using System.Net.Mail;
using System.Net.NetworkInformation;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

public partial class programinfo_ghap_resetpassword : System.Web.UI.Page
{
    private string KeyFileName;
    protected void Page_Load(object sender, EventArgs e)
    {
        RetrieveUsersIDFullName();
    }

    protected void RetrieveUsersIDFullName()
    {
        KeyFileName = Server.MapPath("key.config");
        Class1.SymmetricEncryptionUtility.AlgorithmName = "Rijndael";

        int User = Convert.ToInt32(Request.QueryString["users_id"]);

        using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["HealthCourses"].ConnectionString))
        {
            SqlCommand cmd = new SqlCommand();
            cmd.CommandText = "HealthCourses_RetrieveUsersIDFullName";
            cmd.CommandType = CommandType.StoredProcedure;
            cmd.Connection = conn;

            cmd.Parameters.Add("@users_id", SqlDbType.Int).Value = User;

            try
            {
                conn.Open();

                SqlDataReader rdr = cmd.ExecuteReader();

                if (rdr.Read())
                {
                    hf_users_id.Value = rdr["users_id"].ToString();
                    lblUsersFullName.Text = rdr["users_flname"].ToString();

                    byte[] CreditCard = (byte[])rdr["users_cc"];
                    string CreditCardData = Class1.SymmetricEncryptionUtility.DecryptData(CreditCard, KeyFileName);
                }
            }

            catch (Exception ex)
            {
                ex.Message.ToString();
            }
        }
    }
}

Open in new window

Credit Card data is normally stored as a string, because of the need for encryption and the need to process sections of the data, plus the common requirement to be able to include spaces in the displayed number or replace part of the number with X characters for users who do not have sufficient security clearance to view the entire number.

I still say that you are trying to do too much too soon.  The first step is to use your encrypt and decrypt functions on a simple string for example in a test application as per the solution I sent you earlier.  Once you know that the encrypt and decrypt work, you can move on to the next stage.

I will try to look at your supplied code but I am running out of time tonight...  it is getting late here in the UK.  I will see what I can do, though!
Your supplied Class1 has errors - it is missing ProtectedData the DataProtectionScope enumeration for a start.

I guess if I am to try to sort this for you I will need your key file too?
Avatar of Brian

ASKER

I don't get any errors at all though when I encrypt. Why do you think I need the ProtededData stuff?
It is referred to as part of your code, but is not included in the class so I do not have access to it.  There is a ProtectedData object that has Protect and Unprotect methods, and those methods carry a parameter of type DataProtectionScope which is an enumeration.  Since neither of these is present in the code you have provided it must be somewhere else on your machine.  You may have it as part of your proejct, or the class may simply have a missing reference.

Check out lines 42 and 60 in your Class1 above (note that you have embedded your SymmetricEncryptionUtility class inside the Class1 class for no apparent reason and the namespace is missing from your sample code - could be that is why the other stuff is missing, dodgy copy and paste?).
OK, I have found DataProtectionScope which is part of System.Security.Cryptography but which is not recognised on my system.

Are you using VS2010?  What C# version are you using?
OK, sorted it - I have added the necessary references to my test framework, looking at it now for you.
Avatar of Brian

ASKER

Ok, thank very much!!!
Is your code by any chance swallowing errors?  DecryptData fails because the string is the wrong length in my tests, and that occurs in VS 2010...
Avatar of Brian

ASKER

No, no errors at all. The other thing that I had to do was add a reference to system.security.dll but don't hold me to that. I was able to encrypt no problems. When I tried to decrypt I did not receive any error just no data.
Hi asp_net2:

Your code definitely fails on the length of the returned value to be decrypted, at least on my machine.  

Following my policy of taking things back to basics to get them working and then building on them, here is a solution using the same methods in a somewhat simpler way.  First, a simple encryption class based on MSDN samples:

using System;
using System.Security.Cryptography;

namespace EncryptionDemo
{
    public static class DataProtection
    {

        // Create byte array for additional entropy when using Protect method.
        static readonly byte[] AdditionalEntropy = { 9, 8, 7, 6, 5 };

        public static byte[] Protect(byte[] data)
        {
            try
            {
                // Encrypt the data using DataProtectionScope.CurrentUser. The result can be decrypted
                //  only by the same current user.
                return ProtectedData.Protect(data, AdditionalEntropy, DataProtectionScope.CurrentUser);
            }
            catch (CryptographicException e)
            {
                Console.WriteLine(@"Data was not encrypted. An error occurred.");
                Console.WriteLine(e.ToString());
                return null;
            }
        }

        public static byte[] Unprotect(byte[] data)
        {
            try
            {
                //Decrypt the data using DataProtectionScope.CurrentUser.
                return ProtectedData.Unprotect(data, AdditionalEntropy, DataProtectionScope.CurrentUser);
            }
            catch (CryptographicException e)
            {
                Console.WriteLine(@"Data was not decrypted. An error occurred.");
                Console.WriteLine(e.ToString());
                return null;
            }
        }
    }
}

Open in new window


Now the test encryption:

        private void EncryptButtonClick(object sender, EventArgs e)
        {
            var encoding = new UTF8Encoding();
            var bytes = encoding.GetBytes(textToEncryptTextBox.Text);

            var data = DataProtection.Protect(bytes);
            encryptedTextDetailLabel.Text = Convert.ToBase64String(data);
        }

Open in new window


And finally the test decryption

        private void DecryptButtonClick(object sender, EventArgs e)
        {
            var encoding = new UTF8Encoding();
            var bytes = Convert.FromBase64String(encryptedTextDetailLabel.Text);

            var data = DataProtection.Unprotect(bytes);
            decryptedTextDetailLabel.Text = encoding.GetString(data);
        }

Open in new window


This all works perfectly for me, and if you test and make sure that it works for you that will give you a start.  You can then replace AddittionalEntropy with your key data and get that working.  After that change to local machine encryption and you should be done...
Avatar of Brian

ASKER

Hi chrisbray,

Thanks for the info in the last post. But I'm afraid that I'm not going to be able to use that method. The biggest reason why from what i have read is that Encrypting / Decrypting based on DataProtection using the local machine if poor method because the Encryption / Decryption lives on the machine and if the machine dies so does the encryption / decryption key. This is not a good practice. It's much better to create the actual key.config file and use that. By doing that I can performing a backup of that directory to make sure that the key.config file is backed up to another place while using the same key.config within my application.

Did you have a chance to see if you where able to decrypt my data using my method?
Hi asp_net2:

> The biggest reason why from what i have read is that Encrypting / Decrypting based on DataProtection using the local machine if poor method because the Encryption / Decryption lives on the machine and if the machine dies so does the encryption / decryption key.

I didn't suggest that, it came from your code!!  Look again at your code, the same lines I pointed out to you earlier where you are using LocalMachine as a parameter to the Protect and UnProtect calls - YOUR CODE not mine!   I have used the code basis you provided to produce a working system because yours does not work, although I have used CurrentUser in my example because that is safer.  I repeat - LocalMachine is from YOUR CODE..  

You have a major problem somewhere in your code and there is just too much of it that I don't have to be able to find the problem in a reasonable time.  You need to cut it back to the basics to get it working.  Once you have the basics working (and I have already given you that free and clear) you can then start to refine it to make it work the way that you want.  However, you MUST have a working starting point or you won't suceed.

So, to recap: I suggested LocalMachine ONLY because that is what you are already using in your code.  If that is wrong, then your code is even more broken than you thought!  I suggest again that you take the code I gave you and confirm that it works in your scenario.  Then, having a working solution you can start to modify and refine it to suit your requirements.
Here is the options that you can choose from using the method you have selected:

CurrentUser       The protected data is associated with the current user. Only threads running under the current user context can unprotect the data.
LocalMachine       The protected data is associated with the machine context. Any process running on the computer can unprotect data. This enumeration value is usually used in server-specific applications that run on a server where untrusted users are not allowed access.

The LocalMachine enumeration value allows multiple accounts to unprotect data. Use this value only when you trust every account on a computer. For most situations, you should use the CurrentUser value.

However, this can be a major problem with multi-user applications which a database normally is - you encrypt the data but your other users cannot then unencrypt it...  You may find that the solution you have chosen is unsuitable in this situation unless you can do the encryption and decryption server side and pass back the unencrypted data, which of course has its own security issues.
It is worth noting that the first solution I provided does not suffer from the limitations of the later method, although as a consequence it is less secure.  That said, in the scenario you outline it may be the better choice.
Avatar of Brian

ASKER

Hi chrisbay,

Yeah, I'm very sorry, i saw that after I posted the comment. My bad. To be honest with you chrisbay I have been trying to work from a books examples. So I think to make it easier I'm going to send you a PDF file that I created with what I'm trying to incorporate into my project. You will see that I created my .cs file to mimick theirs. I'm also trying to Encrypt / Decrypt data based on the Section in the PDF where it shows you how to Encrypt / Decrypt sensitive data in your database with examples they provide. Maybe you might be able to figure out how to decrypt what i'm stuck with by the PDF i upload.

Let me know, if it makes more sense.

Thanks again.
Scan-001.pdf
I am afraid that with the best will in the world I do not have time to debug fautly code from someone else's book for you...

I suggest you drop the one from the book that doesn't work and use one of the two options I have already provided that work perfectly well.  You can then add in the bits from your book one piece at a time until either it works or it breaks.  Once you know what breaks it, fixing it should be fairly easy.

I believe that a choice of two working solutions should be adequate for anyone!


Avatar of Brian

ASKER

Your solution did not work as I initially needed. I need to store the key.config file. I requested additional help with this post and hopefully someone else can assist with the code I provided in Decryption.

Thank you for your time.
ASKER CERTIFIED SOLUTION
Avatar of Carlos Villegas
Carlos Villegas
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of Brian

ASKER

Hi yv989c,

THANK YOU for your help with this. I was able to Encrypt data using you solution above but having difficulty Decrypting the data now :( Please see the logic that I'm using to perform the Encrypt and Decrypt. I also took your solution and made .cs out of it which I will attach as well.

If you look at my Decryption code I get a RED LINE with the message below.

string MyEncryptionUtil.DecryptObjectToString(byte[] encrypted ObjectBytes)

Error:
The best overloaded method match for 'MyEncryption.DecryptObjectToString(byte[])' has some invalid arguments.
using System;
using System.IO;
using System.Security.Cryptography;
using System.Configuration;
using System.Text;

/// <summary>
/// Summary description for MyEncryptionUtil
/// </summary>
public class MyEncryptionUtil
{
    // I'm storing the password into your web.config file:
    //<configuration>
    //  <appSettings>
    //    <add key="MyEncryptionUtil.Password" value="MyPassword"/>
    //  </appSettings>
    //</configuration>
    static readonly string Password = ConfigurationManager.AppSettings["MyEncryptionUtil.Password"];

    public static byte[] EncryptObject(object objectToEncrypt)
    {
        // We are using Salt to make it harder to guess our key
        // using a dictionary attack.
        byte[] salt = Encoding.ASCII.GetBytes(Password.Length.ToString());

        // The (Secret Key) will be generated from the specified
        // password and Salt.

        //PasswordDeriveBytes -- It Derives a key from a password
        PasswordDeriveBytes secretKey = new PasswordDeriveBytes(Password, salt);

        // We are now going to create an instance of the
        // Rihndael class. 
        using (RijndaelManaged rijndaelCipher = new RijndaelManaged())
        {
            // Create a encryptor from the existing SecretKey bytes.
            // We use 32 bytes for the secret key
            // (the default Rijndael key length is 256 bit = 32 bytes) and
            // then 16 bytes for the IV (initialization vector),
            // (the default Rijndael IV length is 128 bit = 16 bytes)
            ICryptoTransform encryptor = rijndaelCipher.CreateEncryptor(secretKey.GetBytes(16), secretKey.GetBytes(16));

            // Create a MemoryStream that is going to hold the encrypted bytes
            using (MemoryStream encryptedObject = new MemoryStream())
            {
                // Create a CryptoStream through which we are going to be processing our data.
                // CryptoStreamMode.Write means that we are going to be writing data
                // to the stream and the output will be written in the MemoryStream
                // we have provided. (always use write mode for encryption)
                using (CryptoStream cryptoStream = new CryptoStream(encryptedObject, encryptor, CryptoStreamMode.Write))
                {
                    // Serialize object to the encryption stream.
                    System.Runtime.Serialization.Formatters.Binary.BinaryFormatter bf = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
                    bf.Serialize(cryptoStream, objectToEncrypt);

                    // Finish encrypting.
                    cryptoStream.FlushFinalBlock();
                }

                return encryptedObject.ToArray();
            }
        }
    }

    public static object DecryptObject(byte[] encryptedObjectBytes)
    {
        byte[] salt = Encoding.ASCII.GetBytes(Password.Length.ToString());
        PasswordDeriveBytes secretKey = new PasswordDeriveBytes(Password, salt);

        using (MemoryStream memoryStream = new MemoryStream(encryptedObjectBytes))
        {
            using (RijndaelManaged rijndaelCipher = new RijndaelManaged())
            {
                // Create a decryptor from the existing SecretKey bytes.
                ICryptoTransform decryptor = rijndaelCipher.CreateDecryptor(secretKey.GetBytes(16), secretKey.GetBytes(16));

                // Create a CryptoStream. (always use Read mode for decryption).
                using (CryptoStream cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read))
                {
                    System.Runtime.Serialization.Formatters.Binary.BinaryFormatter bf = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
                    return bf.Deserialize(cryptoStream);
                }
            }
        }
    }

    public static string DecryptObjectToString(byte[] encryptedObjectBytes)
    {
        return (string)DecryptObject(encryptedObjectBytes);
    }

    public static int DecryptObjectToInt(byte[] encryptedObjectBytes)
    {
        return (int)DecryptObject(encryptedObjectBytes);
    }

    public static DateTime DecryptObjectToDateTime(byte[] encryptedObjectBytes)
    {
        return (DateTime)DecryptObject(encryptedObjectBytes);
    }

    public static bool DecryptObjectToBoolean(byte[] encryptedObjectBytes)
    {
        return (bool)DecryptObject(encryptedObjectBytes);
    }
}

Open in new window

ENCRYPTION CODE:

protected void btn_NewUser_Click(object sender, EventArgs e)
    {
        using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["HealthCourses"].ConnectionString))
        {
            SqlCommand cmd = new SqlCommand();
            cmd.CommandText = "HealthCourses_InsertUsers";
            cmd.CommandType = CommandType.StoredProcedure;
            cmd.Connection = conn;

            cmd.Parameters.Add("@c_id", SqlDbType.Int).Value = ddlClient.SelectedItem.Value;
            cmd.Parameters.Add("@bldg_id", SqlDbType.Int).Value = ddlBldgLocation.SelectedItem.Value;
            cmd.Parameters.Add("@s_id", SqlDbType.Int).Value = ddlState.SelectedItem.Value;
            cmd.Parameters.Add("@users_flname", SqlDbType.VarChar, 50).Value = txtName.Text;
            cmd.Parameters.Add("@users_street_address", SqlDbType.VarChar, 50).Value = txtHomeAddress.Text;
            cmd.Parameters.Add("@users_city", SqlDbType.VarChar, 50).Value = txtCity.Text;
            cmd.Parameters.Add("@users_zip", SqlDbType.VarChar, 5).Value = txtZipCode.Text;
            cmd.Parameters.Add("@users_phone", SqlDbType.VarChar, 12).Value = txtHomePhone.Text;
            cmd.Parameters.Add("@users_work_ext", SqlDbType.VarChar, 4).Value = txtWorkExtension.Text;
            cmd.Parameters.Add("@users_email", SqlDbType.VarChar, 100).Value = txtEmailAddress.Text;
            cmd.Parameters.Add("@users_username", SqlDbType.VarChar, 50).Value = txtUserName.Text;
            cmd.Parameters.Add("@users_password", SqlDbType.Binary, 96).Value = PasswordHash.HashPassword(txtPassword.Text);

            cmd.Parameters.Add("@users_cc", SqlDbType.VarBinary).Value = MyEncryptionUtil.EncryptObject(txtCreditCard.Text);

            try
            {
                conn.Open();

                cmd.ExecuteNonQuery();

                btn_NewUser.Enabled = false;
        
                Response.Redirect("newuser_success.aspx");
            }

            catch (Exception ex)
            {
                btn_NewUser.Enabled = true;
                lblInsertError.Text = ex.Message.ToString();
            }
        }
    }

Open in new window

DECRYPTION CODE:

protected void RetrieveUsersIDFullName()
    {
        int User = Convert.ToInt32(Request.QueryString["users_id"]);

        using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["HealthCourses"].ConnectionString))
        {
            SqlCommand cmd = new SqlCommand();
            cmd.CommandText = "HealthCourses_RetrieveUsersIDFullName";
            cmd.CommandType = CommandType.StoredProcedure;
            cmd.Connection = conn;

            cmd.Parameters.Add("@users_id", SqlDbType.Int).Value = User;

            try
            {
                conn.Open();

                SqlDataReader rdr = cmd.ExecuteReader();

                if (rdr.Read())
                {
                    hf_users_id.Value = rdr["users_id"].ToString();
                    lblUsersFullName.Text = rdr["users_flname"].ToString();

                    CreditCardText.Text = MyEncryptionUtil.DecryptObjectToString(rdr["users_cc"].ToString());

                }
            }

            catch (Exception ex)
            {
                ex.Message.ToString();
            }
        }
    }

Open in new window

SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of Brian

ASKER

Damn your good.... Are you one of the inventors of .NET :) That worked :)

Do you have any idea why my method did not work for Decypting? Also, is there a way to better secure the following below in my web.config file? Can the value be changed to anything?

<appSettings>
  <add key="MyEncryptionUtil.Password" value="MyPassword"/>
</appSettings>
Hello:

<appSettings>
  <add key="MyEncryptionUtil.Password" value="ThisIsYourPasswordChangeItToSomethingComplexAndStoreItInASafePlaceIfYouLostItYouWillLostAllYourEncryptedData"/>
</appSettings>

Clear enough? ;)

To protect sections of your web.config file, you can follow the instructions explained here:
http://msdn.microsoft.com/en-us/library/dtkwfdky.aspx
Hi asp_net2:

> Your solution did not work as I initially needed. I need to store the key.config file.

Indeed.  However, as I pointed out at the time, the code I provided was a starting point and you needed to prove the point that it worked before adding the key file and whatever other improvements were needed.  I note that in the excellent code provided by yv989c you are not even using a key.config file any more, so you were clearly allowing yourself to be blinkered by an unreasonable assumption - you should always try to keep an open mind and never be afraid to go back to basics.

Do you have any idea why my method did not work for Decypting?

If you mean the code corrected by yv989c, then it is because a string is not the same thing as a byte array, even though you can convert from one to the other.

I am glad you found a solution that works for you, and recommend that you accept the solution provided by yv989c as your answer.
Avatar of Brian

ASKER

Hi chrisbay,

1. Your code that you supplied did not seem that it was encrypting anything.
2. True, not using a key.config file but it's now stored in the web.config file and I can encrypt using the link provided by yv989c.
3. The code that I supplied did work, that is for encryption but I only had trouble decrypting.

The code supplied by yv989c is very good and very easy to learn from by the comments.
Hi asp_net2:

> 1. Your code that you supplied did not seem that it was encrypting anything.

Sorry, but that is just nonsense.  If you ran the code that I provided as it stands it both encrypts and decrypts perfectly.  I even provided you with a sample application with the first solution that can be modified to work with the second solution using the later code I provided and you can physically see the encrypted and decrypted text on the form... all you had to do was click the Encrypt and Decrypt buttons consecutively.

I am glad that you now have a solution that suits you, but BOTH the solutions I provided were fully working and that can easily be proved by simply downloading and running the code as-is.

Since you seem to agree that the solution provided by yv989c works and is suited to your requirements, is there a reason that you have not accepted it as the answer?  Do you need further help?
Avatar of Brian

ASKER

Thank you VERY MUCH for all your help with the code for Encrypting / Decrypting. I also appreciate you making the comments with it so that I can also learn from it. I can't thank you enough. Have yourself a wonderful Holiday Season. Thank you once again for your knowledge and will helping.

Thank you!!!
Glad to have been of help buddy, happy holidays :)