Symmetric and Asymmetric Encryption in .NET Core

This post looks at symmetric and asymmetric encryption and how this could be implemented in .NET Core. Symmetric encryption is fast and can encrypt or decrypt large amounts of text, streams or files but requires a shared key. Asymmetric encryption can be used without shared a key, but can only encrypt or decrypt small texts depending of the key size.

Code: https://github.com/damienbod/SendingEncryptedData

Blogs in this series

Symmetric Encryption in .NET Core

System.Security.Cryptography implements and provides the APIs for encryption in .NET Core. In this example, a simple text will be encrypted. The key is created using a random string created using the RandomNumberGenerator class. Each session for encryption and decryption is created and returned as base 64 strings. Using the key and the IV properties, strings can be encrypted or decrypted.

public (string Key, string IVBase64) InitSymmetricEncryptionKeyIV()
{
	var key = GetEncodedRandomString(32); // 256
	Aes cipher = CreateCipher(key);
	var IVBase64 = Convert.ToBase64String(cipher.IV);
	return (key, IVBase64);
}

private string GetEncodedRandomString(int length)
{
	var base64 = Convert.ToBase64String(GenerateRandomBytes(length));
	return base64;
}

private Aes CreateCipher(string keyBase64)
{
	// Default values: Keysize 256, Padding PKC27
	Aes cipher = Aes.Create();
	cipher.Mode = CipherMode.CBC;  // Ensure the integrity of the ciphertext if using CBC

	cipher.Padding = PaddingMode.ISO10126;
	cipher.Key = Convert.FromBase64String(keyBase64);

	return cipher;
}

private byte[] GenerateRandomBytes(int length)
{
	var byteArray = new byte[length];
	RandomNumberGenerator.Fill(byteArray);
	return byteArray;
}

The Encrypt method takes the three parameters and produces an encrypted text which can only be decrypted using the same key and IV base 64 strings. If encrypting large amounts of text, then a CryptoStream should be used. See the example in the Microsoft docs.

public string Encrypt(string text, string IV, string key)
{
	Aes cipher = CreateCipher(key);
	cipher.IV = Convert.FromBase64String(IV);

	ICryptoTransform cryptTransform = cipher.CreateEncryptor();
	byte[] plaintext = Encoding.UTF8.GetBytes(text);
	byte[] cipherText = cryptTransform.TransformFinalBlock(plaintext, 0, plaintext.Length);

	return Convert.ToBase64String(cipherText);
}

The Decrypt method takes the same three parameters as the Encrypt method and produces a decrypted text.

public string Decrypt(string encryptedText, string IV, string key)
{
	Aes cipher = CreateCipher(key);
	cipher.IV = Convert.FromBase64String(IV);

	ICryptoTransform cryptTransform = cipher.CreateDecryptor();
	byte[] encryptedBytes = Convert.FromBase64String(encryptedText);
	byte[] plainBytes = cryptTransform.TransformFinalBlock(encryptedBytes, 0, encryptedBytes.Length);

	return Encoding.UTF8.GetString(plainBytes);
}

A simple console application can be used to demonstrate that the symmetric encryption in .NET Core works using AES. For this to work, the key and the IV needs to be shared to decrypt the encrypted text. This would also work with small changes for streams or files.

using EncryptDecryptLib;
using System;

namespace ConsoleCreateEncryptedText
{
    class Program
    {
        static void Main(string[] args)
        {
           
            var text = "I have a big dog. You've got a cat. We all love animals!";


            Console.WriteLine("-- Encrypt Decrypt symmetric --");
            Console.WriteLine("");

            var symmetricEncryptDecrypt = new SymmetricEncryptDecrypt();
            var (Key, IVBase64) = symmetricEncryptDecrypt.InitSymmetricEncryptionKeyIV();

            var encryptedText = symmetricEncryptDecrypt.Encrypt(text, IVBase64, Key);

            Console.WriteLine("-- Key --");
            Console.WriteLine(Key);
            Console.WriteLine("-- IVBase64 --");
            Console.WriteLine(IVBase64);

            Console.WriteLine("");
            Console.WriteLine("-- Encrypted Text --");
            Console.WriteLine(encryptedText);

            var decryptedText = symmetricEncryptDecrypt.Decrypt(encryptedText, IVBase64, Key);

            Console.WriteLine("-- Decrypted Text --");
            Console.WriteLine(decryptedText);
        }
    }
}

Asymmetric Encryption in .NET Core

Asymmetric encryption is great in that a shared secret is not required. The text is encrypted with a public key and can be decrypted with the private key from same RSA. The text is limited in size depending on the key size.

public string Encrypt(string text, RSA rsa)
{
	byte[] data = Encoding.UTF8.GetBytes(text);
	byte[] cipherText = rsa.Encrypt(data, RSAEncryptionPadding.Pkcs1);
	return Convert.ToBase64String(cipherText);
}

public string Decrypt(string text, RSA rsa)
{
	byte[] data = Convert.FromBase64String(text); 
	byte[] cipherText = rsa.Decrypt(data, RSAEncryptionPadding.Pkcs1);
	return Encoding.UTF8.GetString(cipherText);
}

The CreateRsaPublicKey and the CreateRsaPrivateKey static utility methods create an RSA from a X509Certificate2. The private key RSA is used for decryption, the public key RSA is used for Encryption.

using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;

namespace EncryptDecryptLib
{
    public static class Utils
    {
        public static RSA CreateRsaPublicKey(X509Certificate2 certificate)
        {
            RSA publicKeyProvider = certificate.GetRSAPublicKey();
            return publicKeyProvider;
        }

        public static RSA CreateRsaPrivateKey(X509Certificate2 certificate)
        {
            RSA privateKeyProvider = certificate.GetRSAPrivateKey();
            return privateKeyProvider;
        }
    }
}

A X509Certificate2 certificate is used to encrypt and decrypt the strings. CertificateManager was used to create the certificate.

using CertificateManager;
using CertificateManager.Models;
using System;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;

namespace EncryptDecryptLib
{
    public class CreateRsaCertificates
    {
        public static X509Certificate2 CreateRsaCertificate(
          CreateCertificates createCertificates, int keySize)
        {
            var basicConstraints = new BasicConstraints
            {
                CertificateAuthority = true,
                HasPathLengthConstraint = true,
                PathLengthConstraint = 2,
                Critical = false
            };

            var subjectAlternativeName = new SubjectAlternativeName
            {
                DnsName = new List<string>
                {
                    "SigningCertificateTest",
                }
            };

            var x509KeyUsageFlags = X509KeyUsageFlags.KeyCertSign
               | X509KeyUsageFlags.DigitalSignature
               | X509KeyUsageFlags.KeyEncipherment
               | X509KeyUsageFlags.CrlSign
               | X509KeyUsageFlags.DataEncipherment
               | X509KeyUsageFlags.NonRepudiation
               | X509KeyUsageFlags.KeyAgreement;

            var enhancedKeyUsages = new OidCollection
            {
                OidLookup.CodeSigning,
                OidLookup.SecureEmail,
                OidLookup.TimeStamping 
            };

            var certificate = createCertificates.NewRsaSelfSignedCertificate(
                new DistinguishedName { CommonName = "SigningCertificateTest" },
                basicConstraints,
                new ValidityPeriod
                {
                    ValidFrom = DateTimeOffset.UtcNow,
                    ValidTo = DateTimeOffset.UtcNow.AddYears(1)
                },
                subjectAlternativeName,
                enhancedKeyUsages,
                x509KeyUsageFlags,
                new RsaConfiguration
                {
                    KeySize = keySize, 
                    RSASignaturePadding = RSASignaturePadding.Pkcs1,
                    HashAlgorithmName = HashAlgorithmName.SHA256
                });

            return certificate;
        }
    }
}

A console application is used to create a RSA certificate with a key size of 2048. This certificate is then used to encrypt a text, and then decrypt the encrypted text again.

using CertificateManager;
using EncryptDecryptLib;
using Microsoft.Extensions.DependencyInjection;
using System;

namespace ConsoleAsymmetricEncryption
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello World!");
            var serviceProvider = new ServiceCollection()
                .AddCertificateManager()
                .BuildServiceProvider();

            var cc = serviceProvider.GetService<CreateCertificates>();

            var cert3072 = CreateRsaCertificates.CreateRsaCertificate(cc, 3072);

            var text = "I have a big dog. You've got a cat. We all love animals!";

            Console.WriteLine("-- Encrypt Decrypt asymmetric --");
            Console.WriteLine("");

            var asymmetricEncryptDecrypt = new AsymmetricEncryptDecrypt();

            var encryptedText = asymmetricEncryptDecrypt.Encrypt(text,
                Utils.CreateRsaPublicKey(cert3072));

            Console.WriteLine("");
            Console.WriteLine("-- Encrypted Text --");
            Console.WriteLine(encryptedText);

            var decryptedText = asymmetricEncryptDecrypt.Decrypt(encryptedText,
               Utils.CreateRsaPrivateKey(cert3072));

            Console.WriteLine("-- Decrypted Text --");
            Console.WriteLine(decryptedText);
        }
    }
}



Asymmetric encryption is slow compared to symmetric encryption and has a size limit. Symmetric encryption requires a shared key. In the next blog, we will use the asymmetric encryption and the symmetric encryption together and get the benefits of both to send encrypted texts to targeted identities.

Links:

https://docs.microsoft.com/en-us/dotnet/standard/security/encrypting-data

https://docs.microsoft.com/en-us/dotnet/standard/security/decrypting-data

https://docs.microsoft.com/en-us/aspnet/core/security/data-protection/configuration/overview

https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.protecteddata.unprotect

https://docs.microsoft.com/en-us/dotnet/standard/security/how-to-use-data-protection

https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.aes?view=netcore-3.1

https://docs.microsoft.com/en-us/dotnet/standard/security/cross-platform-cryptography

https://docs.microsoft.com/en-us/dotnet/standard/security/vulnerabilities-cbc-mode

https://edi.wang/post/2019/1/15/caveats-in-aspnet-core-data-protection

https://dev.to/stratiteq/cryptography-with-practical-examples-in-net-core-1mc4

https://www.tpeczek.com/2020/08/supporting-encrypted-content-encoding.html

https://cryptobook.nakov.com/

https://www.meziantou.net/cryptography-in-dotnet.htm

3 comments

  1. Great article, the Asymmetric introduction has the text backwards: ” The text is encrypted with a private key and can be decrypted with the public key from same RSA. “

    1. Thanks Steve, I’ll fix now
      – fixed 🙂

  2. […] Symmetric and Asymmetric Encryption in .NET Core (Damien Bowden) […]

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: