# Cryptography in C# — Asymmetric and Symmetric Encryption

Mar 8, 2023·

## Introduction

Cryptography has always been seen as a complex and intricate concept. While this is true in terms of the actual algorithms entailed, in most modern languages, C# inclusive we have a convenient and relatively stress-free method of implementing cryptographic concerns.

The code for this blog can be obtained here.

## What is cryptography?

Cryptography is simply the process of encrypting a message with a key, to be decrypted by a final consumer.

There are three major classes of cryptographic algorithms; symmetric encryption, asymmetric encryption, and hash functions.

In this article, we will be expounding on symmetric encryption and asymmetric encryption. Hash functions will be covered in part 2 of this series.

## Uses of cryptography

• confidentiality: making the data only readable by the intended consumers.

• Data integrity: ensuring the data wasn't mutated in an unidentified manner.

• Non-repudiation: assurance that a party to a contract cannot deny the authenticity of a message that originates with them.

• Authentication: ensuring only the right person acts.

## Symmetric Encryption

Symmetric encryption is a type of encryption in which both the sender and the receiver use the same key to encrypt and decrypt the message.

Example: Jane wants to share an encrypted message with Dave, Jane encrypts this message using a key. She then shares this same key with Dave, who uses it to decrypt and read the message.

There are various types of symmetric encryption. In this article, we will cover DES/Triple DES and AES algorithms.

### Examples of Symmetric Algorithms

DES and Triple DES Symmetric Algorithms

DES — Data Encryption Standard is a symmetric encryption algorithm that encrypts using 56 bits of data. The DES algorithm was broken, and as a result, came Triple DES; which encrypted and decrypted data with three keys.

Both DES and Triple DES are not recommended in modern applications, given the advancement of computational power made in the last few decades. We wouldn't be coding this because of this reason.

AES Symmetric Algorithms

AES (Advanced encryption standard) is a successor to the DES mentioned above after it was broken. Rather than the 56-bit key in DES, AES uses a 128, 192, or 256-bit key; making it less susceptible to brute force attacks.

AES is based on the Rigndaal cipher which is a series of encryption algorithms.

There are different modes of AES encryption with differing advantages and disadvantages. Some of the modes are:

• ECB mode: Electronic Code Book mode

• CBC mode: Cipher Block Chaining mode

• CFB mode: Cipher Feedback mode

• OFB mode: Output Feedback mode

• CTR mode: Counter mode

• GCM mode: Galois Counter Mode

We won't go into details about the different modes as that is beyond the scope of this article.

As part of this article, we will implement the AES algorithm with the CBC mode. (Note: The implementations of the other modes have a similar interface as the CBC modes)

### Implementing AES with the CBC mode:

Step 1: Create a class and import necessary namespaces:

``````using System.Security.Cryptography;
``````

Step 2: Create your encrypt method

Note the `EncrypteCbc` method.

``````public static byte[] Encrypt(byte[] data, byte[] key, byte[] initializationVector)
{
using var aes = Aes.Create();
aes.Key = key;
var encryptedData = aes.EncryptCbc(data, initializationVector);

return encryptedData;
}
``````

Step 3: Create your decrypt method

Note the `DecryptCbc` method.

``````public static byte[] Decrypt(byte[] data,  byte[] key, byte[] initializationVector)
{
using var aes = Aes.Create();
aes.Key = key;
var decryptedData = aes.DecryptCbc(data, initializationVector);

return decryptedData;
}
``````
• "data": serialized data to be encrypted

• "key": the key used for encryption

• "initilizationVector": used to create variability in the encrypted data such that the same data encrypted with the same key would not produce the same output.

In the main method of your code, call your encrypt class as follows:

``````
void TestSymmetricEncryption()
{
var message = "Hello world!";

// generate our initialization vector
var initializationVector = RandomNumberGenerator.GetBytes(16);

// generate our key
var key = RandomNumberGenerator.GetBytes(32);

// encrypt our data
var encryptedData = AesEncryption.Encrypt(message.ToBytes(), key, initializationVector);
Console.WriteLine(Convert.ToBase64String(encryptedData));

// decrypt our data
var decryptedData = AesEncryption.Decrypt(encryptedData, key, initializationVector);
Console.WriteLine(decryptedData.BytesToString());
}
``````

Please note that the `ToBytes()` and `BytesToString()` methods are extension methods:

``````public static byte[] ToBytes(this string input)
{
return Encoding.UTF8.GetBytes(input);
}

public static string BytesToString(this byte[] data)
{
return Encoding.UTF8.GetString(data);
}
``````

• it is extremely secure

• it is relatively fast, compared to asymmetric encryption.

• Key sharing - for the receiver to decrypt the message, they have to have the same key used by the sender in the encryption. This is problematic because we need to be concerned about how to securely share the key and the negative impact that might ensue if the key is compromised.

## Asymmetric Encryption

Asymmetric encryption attempts to solve the problem of key sharing in symmetric encryption by using two mathematically linked keys to encrypt and decrypt the data respectively. The public key encrypts the data, and the private key decrypts the data.

The private key can't be determined from the public key. Thus to be able to decrypt the data one must have the private key.

### Example of Asymmetric encryption

RSA Algorithm

The RSA algorithm (Rivest-Shamir-Adleman) is an asymmetric algorithm; it facilitates the encryption and decryption of data with both private and public keys.

### Implementing RSA Algorithm

Step 1: Create a new class and import the necessary namespaces

``````using System.Security.Cryptography;
``````

Step 2: Create our encryptor and initialize it with the key size in bits (in our case 2048 bits - 256 bytes)

``````private readonly RSA _rsa;

public RsaEncryption()
{
_rsa = RSA.Create(2048);
}
``````

Step 3: Create a method that is responsible for encrypting our data

``````public byte[] Encrypt(string dataToEncrypt)
{
}
``````

Step 4: Create a method that is responsible for decrypting our data

``````public byte[] Decrypt(byte[] dataToDecrypt)
{
}
``````

Step 5: Create methods that facilitate the export/import of our public keys

``````public byte[] ExportPublicKey()
{
return _rsa.ExportRSAPublicKey();
}

public void ImportPublicKey(byte[] publicKey)
{
_rsa.ImportRSAPublicKey(publicKey, out _);
}
``````

Step 6: Create a method that exports our private key

``````public byte[] ExportPrivateKey(int numberOfIterations, string password)
{
var keyParams = new PbeParameters(
PbeEncryptionAlgorithm.Aes256Cbc, HashAlgorithmName.SHA256, numberOfIterations);

var encryptedPrivateKey = _rsa.ExportEncryptedPkcs8PrivateKey(

return encryptedPrivateKey;
}
``````

Step 7: Testing our implementation

``````
void TestAsymmetricEncryption()
{
// initialize our encryptor
var rsa = new RsaEncryption();

// create our private key
byte[] encryptedPrivateKey = rsa.ExportPrivateKey(100000, "bjbh2j3hbjbjk33iui5br");

// export our public key
byte[] publicKey = rsa.ExportPublicKey();
// our data to encrypt
const string original = "Hello World!";

// encrypt our data
var encrypted = rsa.Encrypt(original);

// create new encryptor to decrypt our data
var rsa2 = new RsaEncryption();

// import our public key
rsa2.ImportPublicKey(publicKey);

// import the mathematically linked private key
rsa2.ImportEncryptedPrivateKey(encryptedPrivateKey, "bjbh2j3hbjbjk33iui5br");

// decrypt our data
var decrypted = rsa2.Decrypt(encrypted).BytesToString();
}
``````

• It mitigates the effect of key sharing by providing two keys for this purpose.

• The size of data to encrypt is limited.

## Hybrid Encryption

Due to the disadvantage of asymmetric encryption (limitation of data size), a common approach is to encrypt our data with symmetric encryption and in turn, encrypt our symmetric key with asymmetric encryption.

### Implementation of Hybrid Encryption

Step 1: Create a new class "HybridEncryption.cs" for hybrid encryption and import the useful namespaces.

Step 2: Create a new class "EncryptionPacket.cs" to package our encryption parameters and include the following properties.

``````public class EncryptionPacket
{
public byte[] EncryptedSessionKey;
public byte[] EncryptedData;
public byte[] InitializationVector;
}
``````

Step 3: Create a method "EncryptData" which will be responsible for encrypting our data.

``````public static EncryptionPacket EncryptData(byte[] original, AsymmetricEncryption.RsaEncryption rsaParams)
{
// Generate our session key.
var encryptionKey = RandomNumberGenerator.GetBytes(32);

// Create the encrypted packet and generate the IV.
var encryptedPacket = new EncryptionPacket { InitializationVector = RandomNumberGenerator.GetBytes(16) };

// Encrypt our data with AES.
encryptedPacket.EncryptedData = AesEncryption.Encrypt(original, encryptionKey, encryptedPacket.InitializationVector);

// Encrypt the session key with RSA
encryptedPacket.EncryptedSessionKey = rsaParams.Encrypt(encryptionKey);

return encryptedPacket;
}
``````

Step 4: Create a method "DecryptData" for the decryption of our data.

``````public static byte[] DecryptData(EncryptionPacket encryptedPacket, AsymmetricEncryption.RsaEncryption rsaEncryptor)
{
// Decrypt AES Key with RSA.
var decryptedSessionKey = rsaEncryptor.Decrypt(encryptedPacket.EncryptedSessionKey);

// Decrypt our data with  AES using the decrypted session key.
var decryptedData = AesEncryption.Decrypt(encryptedPacket.EncryptedData,
decryptedSessionKey, encryptedPacket.InitializationVector);

return decryptedData;
}
``````

Step 5: Testing our implementation

``````void TestHybridEncryption()
{
Console.WriteLine("********* Initializing Hybrid encryption test");
const string message = "Hello World!";
Console.WriteLine(\$"Original data: {message}");

// initialize our rsa encryptor
var rsaEncryptor = new RsaEncryption();

// encrypt our data
var encryptionPacket = HybridEncryption.EncryptData(message.ToBytes(), rsaEncryptor);
Console.WriteLine(\$"Encrypted data: {Convert.ToBase64String(encryptionPacket.EncryptedData)}");

// decrypt our data
var decrypted = HybridEncryption.DecryptData(encryptionPacket, rsaEncryptor).BytesToString();
Console.WriteLine(\$"Decrypted data: {decrypted}");
}
``````