ASP.NET Core 3.1 - AES Cipher


Ken Haggerty
Created 05/23/2020 - Updated 05/23/2020 15:25

This article will describe the AES Cipher. I will assume you have created a new ASP.NET Core 3.1 Razor Pages project and have reviewed the previous articles of the series. I won't use Identity or Individual User Accounts. See Tutorial: Get started with Razor Pages in ASP.NET Core.

FIDO Utilties Project and Article Series

This project helps mitigate some of the issues implementing the challenge only. The Users Without Passwords Project implements the challenge with users and authenticators.


Access to the research project source code may be purchased on KenHaggerty.Com at Manage > Assets.

I will publish the FIDO Utilities Project at fido.kenhaggerty.com until I publish the Users Without Passwords Project.

I enjoy writing these articles. It often enhances and clarifies my coding. The research project is a result of a lot of refactoring and hopefully provides logical segues for the articles. Thank you for supporting my efforts.

I included an AES Cipher demo in the free FIDO Utilities Project. I also published the demo to Ken's Demo Site/ Demos/ AES Cipher.

AES Cipher

The Users Without Passwords Project implements FIDO2 authenticators. The user logs in with a device rather than a password. The Users Without Identity Project implements a password hasher. Both use encryption to verify the user. With this fresh experience I decided to implement a cipher for database connection strings in appsettings. json. My research found Encryption And Decryption Using A Symmetric Key In C# which I used to developed my AesCrypto class. Vivek Kumar's example uses a hardcoded value as the key. My early implementations used a 32 hex character string converted from a new Guid byte array for random keys. See How do you convert a byte array to a hexadecimal string, and vice versa?

SecretKey = BitConverter.ToString(Guid.NewGuid().ToByteArray()).Replace("-", "");

This met my requirements to encrypt the appsettings connection strings. I used code behind and the cipher to generate a key, encrypt the local connection string for appsettings. development. json and the production connection string for appsettings. json. The key was hardcoded and used in Startup.cs > ConfigureServices to decrypt either connection string.

appsettings.json
"ConnectionStrings": {
    "DefaultConnection": "AKa6SWb6Ed1OotFEm7UDMg5AiRXOXoN6HRavWlXeF8X7v/pp6rR10ATEhjbUSnrbOyl8LS45Dp4eifc9HyhKwI96iXZh/eDAROfjW8BIyNjCB77K7i9pekUTNl0xscjGbeZiA4dUe3t648OFUJgnmcqEcQpgwfg3XyNsCjf4t/rbyBJCx/WYBsBAs3pNdzzl"
}
Startup.cs > ConfigureServices
var secretKey = "FF00F0F0FF00F0F0FF00F0F0FF00F0F0";
var configConnection = Configuration.GetConnectionString("DefaultConnection");
var defaultConnection = AesCrypto.DecryptString(secretKey, configConnection);
services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(defaultConnection));

The research for this article found more.

The Advanced Encryption Standard (AES), also known by its original name Rijndael is a specification for the encryption of electronic data established by the U.S. National Institute of Standards and Technology (NIST) in 2001.
AES is a subset of the Rijndael block cipher developed by two Belgian cryptographers, Vincent Rijmen and Joan Daemen, who submitted a proposal to NIST during the AES selection process. Rijndael is a family of ciphers with different key and block sizes. Wikipedia - Advanced Encryption Standard

Which lead to more about key sizes and the initialization vector.

AES was chosen as a subset of the family of block ciphers known as Rijndael. That family includes no less than 15 variants, for three possible block sizes (128, 192 and 256 bits) and five possible key sizes (128, 160, 192, 224 and 256 bits). AES, as standardized by NIST, includes only three variants, all with 128-bit blocks, and with keys of 128, 192 or 256 bits. Encrypting using AES-256, can I use 256 bits IV?

I enhanced the AesCrypto class to generate new 128, 192, and 256 bit random keys as Base64 or Hexadecimal strings. I extended the Encrypt and Decrypt methods to include initialization vector, key size, and format parameters.

using System;
using System.IO;
using System.Security.Cryptography;
// Copyright © 2020 Ken Haggerty (https://kenhaggerty.com)
// Licensed under the MIT License.
public static class AesCrypto
{
    /// <summary>
    /// Generates a new random key.
    /// </summary>
    /// <param name="keyByteSize">Key size in bytes.</param>
    /// <param name="useHex">Use Hexadecimal format.</param>
    /// <returns>
    /// A Base64 or Hexadecimal string representing a byte array of random values.
    /// </returns>
    /// <remarks>
    /// The AES Key can be 128 bits = 16 bytes, 192 bits = 24 bytes or 256 bits = 32 bytes.
    /// </remarks>
    public static string GetNewRandom(int keyByteSize = 16, bool useHex = false)
    {
        byte[] randomArray = new byte[keyByteSize];
        using (var rng = RandomNumberGenerator.Create())
        {
            rng.GetBytes(randomArray);
        }
        if (useHex)
            return BitConverter.ToString(randomArray).Replace("-", "");
        else
            return Convert.ToBase64String(randomArray);
    }

    /// <summary>
    /// Encrypts plain text using the key and initialization vector (iv).
    /// </summary>
    /// <param name="key">The key in Base64 or Hexadecimal string format.</param>
    /// <param name="iv">The initialization vector (iv) in Base64 or Hexadecimal string format.</param>
    /// <param name="plainText">The text to be encrypted.</param>
    /// <param name="keyByteSize">Key size in bytes.</param>
    /// <param name="useHex">Use Hexadecimal format for key and iv.</param>
    /// <returns>
    /// A string AES encrypted using the key and initialization vector (iv).
    /// </returns>
    /// <remarks>
    /// The AES Key can be 128 bits = 16 bytes, 192 bits = 24 bytes or 256 bits = 32 bytes.
    /// </remarks>
    public static string EncryptString(string key, string iv, string plainText, int keyByteSize = 16, bool useHex = false)
    {
        byte[] keyBytes = new byte[keyByteSize];
        byte[] ivBytes = new byte[16];
        byte[] cipherBytes;

        if (useHex)
        {
            int len = keyByteSize * 2;
            for (int i = 0; i < len; i += 2)
            {
                keyBytes[i / 2] = Convert.ToByte(key.Substring(i, 2), 16);
                if (i < 32)
                    ivBytes[i / 2] = Convert.ToByte(iv.Substring(i, 2), 16);
            }
        }
        else
        {
            keyBytes = Convert.FromBase64String(key);
            ivBytes = Convert.FromBase64String(iv);
        }

        using (Aes aes = Aes.Create())
        {
            aes.Key = keyBytes;
            aes.IV = ivBytes;
            ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, aes.IV);
            using MemoryStream memoryStream = new MemoryStream();
            using CryptoStream cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write);
            using (StreamWriter streamWriter = new StreamWriter(cryptoStream))
            {
                streamWriter.Write(plainText);
            }
            cipherBytes = memoryStream.ToArray();
        }
        return Convert.ToBase64String(cipherBytes);
    }

    /// <summary>
    /// Decrypts cipher text using the key and initialization vector (iv).
    /// </summary>
    /// <param name="key">The key in Base64 or Hexadecimal string format.</param>
    /// <param name="iv">The initialization vector (iv) in Base64 or Hexadecimal string format.</param>
    /// <param name="cipherText">The encrypted text.</param>
    /// <param name="keyByteSize">Key size in bytes.</param>
    /// <param name="useHex">Use Hexadecimal format for key and iv.</param>
    /// <returns>
    /// A string AES decrypted using the key and initialization vector (iv).
    /// </returns>
    /// <remarks>
    /// The AES Key can be 128 bits = 16 bytes, 192 bits = 24 bytes or 256 bits = 32 bytes.
    /// </remarks>
    public static string DecryptString(string key, string iv, string cipherText, int keyByteSize = 16, bool useHex = false)
    {
        byte[] keyBytes = new byte[keyByteSize];
        byte[] ivBytes = new byte[16];
        byte[] cipherBytes = Convert.FromBase64String(cipherText);

        if (useHex)
        {
            int len = keyByteSize * 2;
            for (int i = 0; i < len; i += 2)
            {
                keyBytes[i / 2] = Convert.ToByte(key.Substring(i, 2), 16);
                if (i < 32)
                    ivBytes[i / 2] = Convert.ToByte(iv.Substring(i, 2), 16);
            }
        }
        else
        {
            keyBytes = Convert.FromBase64String(key);
            ivBytes = Convert.FromBase64String(iv);
        }

        using Aes aes = Aes.Create();
        aes.Key = keyBytes;
        aes.IV = ivBytes;
        ICryptoTransform decryptor = aes.CreateDecryptor(aes.Key, aes.IV);
        using MemoryStream memoryStream = new MemoryStream(cipherBytes);
        using CryptoStream cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read);
        using StreamReader streamReader = new StreamReader(cryptoStream);
        return streamReader.ReadToEnd();
    }
}

Article Tags:

FIDO Validation

Comment Count = 0

Please log in to comment or follow.

Login Register
Follow to get web notifications when new comments are posted to this article.
Logged in users receive web notifications for new articles, topics and assets.
Web Notifications