Encrypting Strings in C# using Authenticated Encryption

Encryption is fundamental and ubiquitous. Whether it’s to prevent sensitive settings (such as passwords and API tokens) from falling into the wrong hands, or making sure no one listens in on confidential communications, encryption is extremely important. Many people do not even realise that they use it every day.

Encrypting data using the .NET Framework or .NET Core libraries, however, is not trivial. There are different ways to encrypt and decrypt data, and sometimes this requires some knowledge about the underlying algorithm.

To keep things really simple, we’ll use a third party library that provides a simple interface for encryption and decryption. Because this library uses strings and byte arrays, it is not suitable for encryption of large amounts of data, such as huge files, which would bloat the application’s memory. However, it is perfectly fine for small strings.

Later in the article, I also share a simple tool that I built to help generate keys and test encryption and decryption. You can find this tool under the AuthenticatedEncryptionTester folder in the Gigi Labs BitBucket repository.

Using AuthenticatedEncryption

AuthenticatedEncryption is a library that provides simple methods for encryption and decryption:

“The library consists of a single static class. This makes it very easy to use. It uses Authenticated Encryption with Associated Data (AEAD), using the approach called “Encrypt then MAC” (EtM). It uses one key for the encryption part (cryptkey) and another key for the MAC part (authkey).”

All we need to start using this is to install the corresponding NuGet package, either using the Package Manager Console:

Install-Package AuthenticatedEncryption

…or using the .NET Core command line tools:

dotnet add package AuthenticatedEncryption

The project’s readme file (which is the first thing you see in the GitHub repo) explains how it’s used, and it is really simple. First, you generate two keys, called the cryptkey and authkey respectively:

var cryptKey = AuthenticatedEncryption.AuthenticatedEncryption.NewKey();
var authKey = AuthenticatedEncryption.AuthenticatedEncryption.NewKey();

This is something you will typically do once, since you have to encrypt and decrypt using the same pair of keys.

Next, we need something to encrypt. We can get this from user input:

Console.Write("Enter something to encrypt: ");
string plainText = Console.ReadLine();

We can now encrypt the plain text by using the keys we generated earlier:

string encrypted = AuthenticatedEncryption.AuthenticatedEncryption
    .Encrypt(plainText, cryptKey, authKey);
Console.WriteLine($"Encrypted: {encrypted}");

And we can also decrypt the cipher text using a similar mechanism:

string decrypted = AuthenticatedEncryption.AuthenticatedEncryption
    .Decrypt(encrypted, cryptKey, authKey);
Console.WriteLine($"Decrypted: {decrypted}");

You will by now have noted the double AuthenticatedEncryption that is constantly repeated throughout the code. This is a result of the unfortunate choice of the library author to use the same for the class and namespace. There is already an open issue for this.

Let’s run this code and see what happens:

Simple encryption and decryption using the AuthenticatedEncryption library. Running on Kubuntu 19.10 using .NET Core.

As you can see, the input string was encrypted and the result was encoded in base64. This was later decrypted to produce the original input string once again.

Authenticated Encryption Tester

To facilitate key generation as well as experimentation, I wrote this small tool:

Authenticated Encryption Tester. A simple tool to quickly use the functions of the AuthenticatedEncryption library.

This lets you use the AuthenticatedEncryption library functionality that we have just seen in the previous section. It’s useful to initially generate your keys, and also to test that you are actually able to encrypt and decrypt your secrets successfully.

It is a WPF application running on .NET Core 3, so unlike the AuthenticatedEncryption library, unfortunately it only works on Windows. However, for those of you who, like me, have the misfortune of already using Windows, it can turn out to be a handy utility.

You can get the code from the AuthenticatedEncryptionTester folder in the Gigi Labs BitBucket repository. While I won’t go through all the code in the interest of brevity, I’d like to go through some parts and show that it’s doing pretty much what we’ve seen in the previous section.

        private void GenerateCryptKeyButton_Click(object sender, RoutedEventArgs e)
            => GenerateKeyInTextBox(this.CryptKeyField);

        private void GenerateAuthKeyButton_Click(object sender, RoutedEventArgs e)
            => GenerateKeyInTextBox(this.AuthKeyField);

// ...

        private void GenerateKeyInTextBox(TextBox textBox)
        {
            string key = AuthenticatedEncryption
                .AuthenticatedEncryption.NewKeyBase64Encoded();
            textBox.Text = key;
        }

The first two fields in the window expect to have the two keys in base64 format. You can either use keys you had generated earlier and stored, or you can hit the Generate buttons to create new ones. These buttons create new keys using the NewKeyBase64Encoded() method, which is just like NewKey() except that it returns a base64-encoded string instead of a byte array. This is handy in situations where you want a string representation, such as in a GUI like this.

Encryption and decryption also work just like in the previous section, and the implementation merely adds some extra code for validation and I/O. This is the method that runs when you click the Encrypt button:

        private void EncryptButton_Click(object sender, RoutedEventArgs e)
        {
            const string operation = "Encrypt";

            string cryptKeyBase64 = this.CryptKeyField.Text;
            string authKeyBase64 = this.AuthKeyField.Text;
            string plainText = this.PlainTextField.Text;

            try
            {
                if (string.IsNullOrWhiteSpace(cryptKeyBase64)
                    || string.IsNullOrWhiteSpace(authKeyBase64)
                    || string.IsNullOrWhiteSpace(plainText))
                {
                    ShowWarning("Both keys and the plain text must have a value.",
                        operation);
                }
                else
                {
                    byte[] cryptKey = Convert.FromBase64String(cryptKeyBase64);
                    byte[] authKey = Convert.FromBase64String(authKeyBase64);

                    string cipherText = AuthenticatedEncryption
                        .AuthenticatedEncryption.Encrypt(plainText, cryptKey, authKey);
                    this.CipherTextField.Text = cipherText;
                }
            }
            catch (Exception ex)
            {
                ShowError(ex, operation);
            }
        }

The Encrypt button takes what’s in the Plain Text field and puts an encrypted version in the Cipher Text field. The Decrypt button does the opposite, taking the Cipher Text and putting the decrypted version in the Pain Text field. The code for the Decrypt button is very similar to that of the Encrypt button so I won’t include it here.

One thing you’ll note as you experiment with this is that the encrypted output string changes every time. This is an expected behaviour that provides better security. By clearing the value in the Plain Text field before hitting Decrypt, you can verify that it is always decrypted correctly to the original input string, even with different encrypted values.

Summary

The AuthenticatedEncryption library is great for encryption and decryption of simple strings. For large amounts of data, you should instead use streams together with the cryptographic APIs available in the .NET Framework or .NET Core.

You can use my Authenticated Encryption Tester to generate keys or experiment with encryption and decryption using the AuthenticatedEncryption library. It is built on WPF so it only works on Windows.

Leave a Reply

Your email address will not be published. Required fields are marked *