Encryption 101: Symmetric Algorithms

Earlier this week, we covered one-way encryption using a hash algorithm and a salted hash. If you need to decrypt what you’ve encrypted, these solutions are out the window. The simplest solution for encrypting and decrypting is to use a symmetric algorithm. Data is encrypted using a secret key, and it’s decrypted the same way–using the secret key. (Symmetric!)

It requires a few more steps to accomplish, but overall, it’s still pretty simple. Let’s breakdown the steps:

  1. Select an algorithm
  2. Create a key
  3. Encrypt data
  4. Decrypt data

That doesn’t sound so bad, right!? So let’s do it!

Select an algorithm

The .NET Framework supports a number of different symmetric algorithms, including DES, RC2, Rijndael, and TripleDES. We’ll use TripleDES in this example, so let’s instantiate our crypto provider.

var algorithm = new TripleDESCryptoServiceProvider();

Create a key

Now that we have our crypto provider, creating our secret key is a breeze.

algorithm.GenerateKey();

But wait! That’s not quite it. We also need to generate an initialization vector (IV), which acts as a “randomizer” for the encryption. Don’t worry, though–it’s just as easy as generating the key.

algorithm.GenerateIV();

Encrypt data

We have our key and IV, so we’re ready to encrypt some data! This is a little more complicated, but still not too bad. All we’re doing is creating an ICryptoTransform “encryptor” from our symmetric crypto provider, then using it to write bytes to a CryptoStream. Sounds hard, but it’s really not so bad.

string Encrypt(SymmetricAlgorithm sa, string text)
{
    var encryptor = sa.CreateEncryptor(sa.Key, sa.IV);
    var bytes = Encoding.UTF8.GetBytes(text);
    using (var ms = new MemoryStream())
    {
        using (var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
        {
            cs.Write(bytes, 0, bytes.Length);
            cs.FlushFinalBlock();
        }
        return Convert.ToBase64String(ms.ToArray());
    }
}

Decrypt data

Now how do we go about decrypting it? Well, not surprisingly–this being a symmetric algorithm and all–it’s nearly identical to encrypting the data. The only difference is that we create a “decryptor” from our crypto provider.

string Decrypt(SymmetricAlgorithm sa, string encrypted)
{
    var decryptor = sa.CreateDecryptor(sa.Key, sa.IV);
    var bytes = Convert.FromBase64String(encrypted);
    using (var ms = new MemoryStream())
    {
        using (var cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Write))
        {
            cs.Write(bytes, 0, bytes.Length);
            cs.FlushFinalBlock();
        }
        return Encoding.UTF8.GetString(ms.ToArray());
    }
}

Putting it all together

Overall, symmetric encryption gives us a quick and easy way to do reasonably secure encryption. To properly decrypt data across applications, you’ll need to share the key and IV that you generated. Make sure to keep these values a secret, though, as they allow anybody to decrypt your sensitive data. Here’s the full example from the pieces above:

namespace adamprescott.net.EncryptionSymmetric
{
    using System;
    using System.IO;
    using System.Security.Cryptography;
    using System.Text;

    class Program
    {
        static void Main(string[] args)
        {
            var p = new Program();
            p.Run();
        }

        void Run()
        {
            Console.Write("Input: ");
            var input = Console.ReadLine();

            using (var algorithm = new TripleDESCryptoServiceProvider())
            {
                algorithm.GenerateKey();
                algorithm.GenerateIV();

                var encrypted = Encrypt(algorithm, input);
                Console.WriteLine("Encrypted: {0}", encrypted);

                var decrypted = Decrypt(algorithm, encrypted);
                Console.WriteLine("Decrypted: {0}", decrypted);
            }

            Console.ReadLine();
        }

        string Encrypt(SymmetricAlgorithm sa, string text)
        {
            var encryptor = sa.CreateEncryptor(sa.Key, sa.IV);
            var bytes = Encoding.UTF8.GetBytes(text);
            using (var ms = new MemoryStream())
            {
                using (var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
                {
                    cs.Write(bytes, 0, bytes.Length);
                    cs.FlushFinalBlock();
                }
                return Convert.ToBase64String(ms.ToArray());
            }
        }

        string Decrypt(SymmetricAlgorithm sa, string encrypted)
        {
            var decryptor = sa.CreateDecryptor(sa.Key, sa.IV);
            var bytes = Convert.FromBase64String(encrypted);
            using (var ms = new MemoryStream())
            {
                using (var cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Write))
                {
                    cs.Write(bytes, 0, bytes.Length);
                    cs.FlushFinalBlock();
                }
                return Encoding.UTF8.GetString(ms.ToArray());
            }
        }
    }
}
Advertisement

Encryption 101: This Hash Needs Salt

Yesterday, I wrote about how to do simple, one-way encryption using a hash algorithm. This is terrific for encrypting data that doesn’t need to be decrypted, like user passwords. The problem with hash algorithms is that the same values encrypt to the same encrypted values. This is bad because it is possible to see that two encrypted values are the same.

But do not worry. You can turn a hash into a salted hash with one very easy adjustment: add some random bytes to the value. Of course, in order for this to be effective, you’ll also need to store the salt value. When creating the hash for storage, you’ll use salt+value. When creating the hash for comparison, you’ll use salt+value. Get it?

Here’s a short example that uses the RNGCryptoServiceProvider class (RNG = Random Number Generator) to create a salt:

namespace adamprescott.net.EncryptionSaltedHash
{
    using System;
    using System.Security.Cryptography;
    using System.Text;

    class Program
    {
        static void Main(string[] args)
        {
            var p = new Program();
            p.Run();
        }

        private void Run()
        {
            Console.Write("Input: ");
            var input = Console.ReadLine();

            var salt = GetSalt();
            var hashed = HashText(salt + input);
            Console.WriteLine("Salt: {0}", salt);
            Console.WriteLine("Hashed: {0}", HashText(input));

            Console.WriteLine();
            Console.Write("What did you just enter?: ");
            input = Console.ReadLine();
            if (string.Equals(hashed, HashText(salt + input)))
            {
                Console.WriteLine("You are an honest person.");
            }
            else
            {
                Console.WriteLine("You are a liar!");
            }

            Console.ReadLine();
        }

        private string HashText(string text)
        {
            using (var md5 = new MD5CryptoServiceProvider())
            {
                var bytes = Encoding.UTF8.GetBytes(text);
                var hash = md5.ComputeHash(bytes);
                return Convert.ToBase64String(hash);
            }
        }

        private string GetSalt()
        {
            using (var rng = new RNGCryptoServiceProvider())
            {
                var bytes = new byte[8];
                rng.GetBytes(bytes);
                return Convert.ToBase64String(bytes);
            }
        }
    }
}

Encryption 101: Getting Your Hash On

Encryption can be a daunting topic. It’s mostly used only when needed, and it’s typically needed when sensitive data needs to be protected. There are a lot of different ways to encrypt data, and even “encryption made simple” articles can get very complicated. But, like many topics, it’s really not as difficult as it seems.

Part of simplifying the solution lies in identifying your needs. Let’s look at a very easy scenario: encrypting passwords. Obviously, you don’t want to store passwords in plain text. I’ve inherited systems with plain text passwords, and I think it’s flat-out embarrassing. The seemingly obvious solution would be to encrypt a password before saving it and then decrypt it for comparison during the authentication process. That’s over-complicating it, though; what you need is a hash!

Using a hash algorithm is a great way to create one-way encryption. This is ideal for a scenario like passwords. Encrypt the password, and store the encrypted value. When it’s time to authenticate, encrypt the user input and compare. If the encrypted strings match, so do the passwords.

Here’s an easy way to do MD5 encryption in C#:

namespace adamprescott.net.EncryptionHash
{
    using System;
    using System.Security.Cryptography;
    using System.Text;

    class Program
    {
        static void Main(string[] args)
        {
            var p = new Program();
            p.Run();
        }

        private void Run()
        {
            Console.Write("Input: ");
            var input = Console.ReadLine();
            Console.WriteLine("Hashed: {0}", HashText(input));
            Console.ReadLine();
        }

        private string HashText(string text)
        {
            using (var md5 = new MD5CryptoServiceProvider())
            {
                var bytes = Encoding.UTF8.GetBytes(text);
                var hash = md5.ComputeHash(bytes);
                return Convert.ToBase64String(hash);
            }
        }
    }
}

Want to use a different hash algorithm? No problem! Just change the CryptoServiceProvider. Here’s the same example using SHA1:

namespace adamprescott.net.EncryptionHash
{
    using System;
    using System.Security.Cryptography;
    using System.Text;

    class Program
    {
        static void Main(string[] args)
        {
            var p = new Program();
            p.Run();
        }

        private void Run()
        {
            Console.Write("Input: ");
            var input = Console.ReadLine();
            Console.WriteLine("Hashed: {0}", HashText(input));
            Console.ReadLine();
        }

        private string HashText(string text)
        {
            using (var sha1 = new SHA1CryptoServiceProvider())
            {
                var bytes = Encoding.UTF8.GetBytes(text);
                var hash = sha1.ComputeHash(bytes);
                return Convert.ToBase64String(hash);
            }
        }
    }
}
%d bloggers like this: