Encryption and Decryption using RSA Algorithm in C#

Encryption and Decryption using RSA Algorithm in C#
1.    Introduction
 
In one of my project assignment there were the requirement for hiding the confidential information like email, phone number, password. The project assignment also had the requirement to encrypt database connection string as that was the multi-tenancy application.  After googling and binging I come across few encryption and decryption algorithms which used public and private key to encrypt and decrypt the message strings. There are various algorithms available on internet but in this article I am explaining The most popular and secured RSA algorithm.
RSA is an algorithm used by modern computers to encrypt and decrypt messages. It is an asymmetric cryptographic algorithm. Asymmetric means that there are two different keys (public and private). This is also called public key cryptography, because one of them can be given to everyone. The other key must be kept private. It is based on the fact that finding the factors of an integer is hard (the factoring problem).
RSA stands for Ron Rivest, Adi Shamir and Leonard Adleman, who first publicly described it in 1978. A user of RSA creates and then publishes the product of two large prime numbers, along with an auxiliary value, as their public key. The prime factors must be kept secret. Anyone can use the public key to encrypt a message, but with currently published methods, if the public key is large enough, only someone with knowledge of the prime factors can feasibly decode the message.

2.    Overview

 

Microsoft .NET framework provides all classes related to security under System.Security.Cryptography namespace. The System.Security.Cryptography namespace provides cryptographic services, including secure encoding and decoding of data, as well as many other operations, such as hashing, random number generation, and message authentication. Creating and managing keys is an important part of the cryptographic process. There are basically two types of cryptography algorithms i.e. Symmetric and another is Asymmetric algorithm. Symmetric algorithms require the creation of a key and an initialization vector (IV). The key must be kept secret from anyone who should not decrypt your data. Asymmetric algorithms require the creation of a public key and a private key. The public key can be made public to anyone, while the private key must know only by the party who will decrypt the data encrypted with the public key.

The .NET Framework provides the RSACryptoServiceProvider and DSACryptoServiceProvider classes for asymmetric encryption. These classes create a public/private key pair when you use the default constructor to create a new instance.

The below diagram shows how the RSA Asymmetric algorithm works. The public key is used to encrypt the plain text and private key is used to decrypt the encrypted text to produce original plain text.

A public/private key pair is generated whenever a new instance of an asymmetric algorithm class is created. After a new instance of the class is created, the key information can be extracted using one of two methods:
·         The ToXmlString method, which returns an XML representation of the key information.
·         The ExportParameters method, which returns an RSAParameters structure that holds the key information.
So let’s start with the practical implementation of RSA algorithm. Here we need to create public and private keys first which can be used to encrypt and decrypt the message.

3.    Practical Implementation


In this example I have created the key for 2048 bit message string as I wanted to encrypt and decrypt my database connection string. If you wanted to encrypt smaller message like username, password, email and phone number then you can use 512 or 1024 bits.

Step 1: Generate the keys (public and private) required for encryption and decryption.

using System;
namespaceRSACryptography
{
    class Program
    {
        public static void Main(string[] args)
        {
            varkeyGenrator = new RSACryptographyKeyGenerator();
            var keys = keyGenrator.GenerateKeys(RSAKeySize.Key2048);
            Console.WriteLine(“nnn Public Key Text: “ + keys.PublicKey);
            Console.WriteLine(“nnn—————————————-nnn”);
            Console.WriteLine(“nnn Private Key Text: “ + keys.PrivateKey);
            Console.WriteLine(“nnn—————————————-nnn”);
            Console.ReadKey();
        }
    }
}
using System;
usingSystem.Security.Cryptography;
usingSystem.Text;
namespaceRSACryptography
{
    public enum RSAKeySize
    {
        Key512 = 512,
        Key1024 = 1024,
        Key2048 = 2048,
        Key4096 = 4096
    }
    public class RSAKeysTypes
    {
        public string PublicKey { get; set; }
        public stringPrivateKey { get; set; }
    }
    public class RSACryptographyKeyGenerator
    {
        public RSAKeysTypesGenerateKeys(RSAKeySize rsaKeySize)
        {
            int keySize = (int)rsaKeySize;
            if (keySize % 2 != 0 || keySize < 512)
                throw new Exception(“Key should be multiple of two and greater than 512.”);
            varrsaKeysTypes = new RSAKeysTypes();
            using (var provider = new RSACryptoServiceProvider(keySize))
            {
                var publicKey = provider.ToXmlString(false);
                varprivateKey = provider.ToXmlString(true);
                varpublicKeyWithSize = IncludeKeyInEncryptionString(publicKey, keySize);
                varprivateKeyWithSize = IncludeKeyInEncryptionString(privateKey, keySize);
                rsaKeysTypes.PublicKey = publicKeyWithSize;
                rsaKeysTypes.PrivateKey = privateKeyWithSize;
            }
            returnrsaKeysTypes;
        }
        private stringIncludeKeyInEncryptionString(string publicKey, int keySize)
        {
            return Convert.ToBase64String(Encoding.UTF8.GetBytes(keySize.ToString() + “!” + publicKey));
        }
    }
}
       After execution of above program, we will get below output i.e. public and private keys required for encryption and decryption.
      


Step 2: Create a Helper class to Encrypt and Decrypt text.

using System;
usingSystem.Security.Cryptography;
usingSystem.Text;
namespaceRSACryptography
{
   public static class CryptographyHelper
    {
       private static bool_optimalAsymmetricEncryptionPadding = false;
       //These keys are of 2048byte
private readonly static string PublicKey = “MjA0OCE8UlNBS2V5VmFsdWU+PE1vZHVsdXM+djFTTVVyYk5SZW50VDEya0FhWXNRMEh3Y2hjWG9nbnFUWGpYd1NXaGR5Qi9aaTQ5VnF4L0lFdWxSaGFhVjdHOUtENWRmY0I4eE
ZaZGgyNGJ0MHpZbGFNTlFyRVBNNnQzUEdvZXZmMXVCby9wVnhlcWFocEFkWkIwelNJcjhwTk5UOW52czV5WEN1Q00xRFo0UUR3Q3A3b2U2aXc2ZHZ4VEZNWFZJdW9rSkcrdmlFM
WhORDhnbGg0dFVsMWVBdThKT3YyR0tyWmhvTmUxK2tnRzNNUmRueEFGTDQyRDl4eWF5NERvcmpGL2ZjYWNNc3dFYkM3MUo2bFNobnR2YnQ1RnY0elY1bkg0aDhqYzhnV1dQVDUv
WG16TElLMmlJRDJ6L3NyeGgvbzdMRkRhWVhXMnVwbUt5VUJQR2k0OGJLUVZKT3JjZU9rd3owWE1nTDFJUk4yWnhRPT08L01vZHVsdXM+PEV4cG9uZW50PkFRQUI8L0V4cG9uZW50
PjwvUlNBS2V5VmFsdWU+”;
      
private readonly static stringPrivateKey = “MjA0OCE8UlNBS2V5VmFsdWU+PE1vZHVsdXM+djFTTVVyYk5SZW50VDEya0FhWXNRMEh3Y2hjWG9nbnFUWGpYd1NXaGR5Qi9aaTQ5VnF4L0lFdWxSaGFhVjdHOUtENWRmY0I4eEZa
ZGgyNGJ0MHpZbGFNTlFyRVBNNnQzUEdvZXZmMXVCby9wVnhlcWFocEFkWkIwelNJcjhwTk5UOW52czV5WEN1Q00xRFo0UUR3Q3A3b2U2aXc2ZHZ4VEZNWFZJdW9rSkcrdmlFMWhOR
DhnbGg0dFVsMWVBdThKT3YyR0tyWmhvTmUxK2tnRzNNUmRueEFGTDQyRDl4eWF5NERvcmpGL2ZjYWNNc3dFYkM3MUo2bFNobnR2YnQ1RnY0elY1bkg0aDhqYzhnV1dQVDUvWG16TE
lLMmlJRDJ6L3NyeGgvbzdMRkRhWVhXMnVwbUt5VUJQR2k0OGJLUVZKT3JjZU9rd3owWE1nTDFJUk4yWnhRPT08L01vZHVsdXM+PEV4cG9uZW50PkFRQUI8L0V4cG9uZW50PjxQPi8y
Y1VJS2RlMFB1b2RVaDJQQ3krbFU0aWFvVWtOZ0dOOVhHNmhvcll3c1ovbzdwdTJYZjZmS2E5M09OZ1R0NUpqaW5QL3grZG9ibmFiU1hNNFNwRGJlb3JVRGZBKzhYeDIxTHBCT0FtYU
tUVWlkejNjMHlQRXBQZ3lOMlpVb3poUWhjejZlUk01cUdQSlgxU29WMjczM3ZUREFtTEVWS0N4eFRZOHVNSWI3OD08L1A+PFE+djhjYlBmcHh5aXZUelhsV2Q5L3hNK3pRUlJRSk4r
TDFIYURiNHYxKzU3dExEb3VlcG03ajI0MkJFZ2U4dTNENmJEanZneWhBWFIxV3IwR09KSjBBb1ZPV2FLLzdvZ3NHZjBnM1dzNzVicWtWSmdNTHZETnFxSVVVd0ZqZml3TllONkJnN0
dIdGl2S0VGdmJldTEzcGFxVERyTnFuV0ZQaWFQK1lkQ09xVjNzPTwvUT48RFA+eWVSVDF0UTNjWC9kMUlocFhud0lVOEltRm9vVTY5UWl3YWtiUjR1dWVabXNBR001aVJMOG9WaTFz
VXpVTHNRczVRSk1kMklvbTFWdFF1YWtwRUZpZUJxcURvbGtOaUp0WTNDUTN0Zkp4T0szV0J1aVNEUjJ6THEwOEZPc0JjTnp0V2plRXIvendrUm9BYnlsZXdXN281Z2dadDJNWHk4WVR
nTSsxQkYvODhVPTwvRFA+PERRPlRGOUxYd1JFbW9HWHFJVkF4UjVlblJJYTR0ZVcwRFhHN1pTbzNKMmRFMFhJSHpQRTYzelBxeGlRSlJFRnZSUEI5cVU1NU41N3UxazZzektGRzltV2
JhaXZCbVBHN3dJN0JTZEtQQlNleXMzMUNSMC9hQ1NGdmpTNVRkeFdzYktVU0JyTFhuZWxOS2RkcVJPSkljN0ZiTjNPdXlDY2NoVjkzZGlqNnVSbEtzOD08L0RRPjxJbnZlcnNlUT5rS
mYwVHZoNDZjTEQ4OElIVVZ0V3hYaDVsYlNUTWw2ZnB5cFhhUU9laUtpTy9XcnZic21waXdBVEhDQ0pERDhYdDFwbTc5K0hrc21sUjlrYktXR2U4WmNqZHJHdUZlZ3NDUGRpT3VGMVN0
a283NWtnblJVY0ZTb1hxSzF1YVgvTWsxTEtDbVpZY3djQ0t2VC9OQUZrWVpVdVNqT3pPckVrRk9VNDdML3VDVE09PC9JbnZlcnNlUT48RD5HbTMyZUZLU0pvODYzZFRFbkFtMVlaRVJ
RdUZYdldWN1BUcHRLMXdrWXMxVmErc0ZSQnpON3Nza1NIdEUxTXBUbytTQmk2WjBWYmJNY3JIT0dGTUFOQ055Nkh5RzZnOU1pRWJzZWpndzQ2MHJnWUZlWkF1K1RiOG5zMUorR2FNcG
NkZGNHa2FPUXMxa0JzaURjZlFZTmMwckNoUVQrMjI5bUVmL3VqUDN6Q1IzcUNzdkZjVTRuMkMwZzBYSWhLQ1dHYXRsbW5MOW9FMWN0MzY4aWZYK0JCUVljUExqSE05TTZaSU9pMWtmR
3M2bXhaT0V3cm1BWFB0T0ZweW1tNlZjMUM4WGtVUENCVERtWUZTSFpiaHNaT09IZHpaVVlUa2lmN1VzRk40MjdTSDVrMTNpQTVGRGJTb053bW9kQ0ZrWitENGJNQ2JUZWgwVTNvell6
M3FnM1E9PTwvRD48L1JTQUtleVZhbHVlPg==”;
       public static string Encrypt(stringplainText)
       {
           int keySize = 0;
           stringpublicKeyXml = “”;
           GetKeyFromEncryptionString(PublicKey, out keySize, outpublicKeyXml);
           var encrypted = Encrypt(Encoding.UTF8.GetBytes(plainText), keySize, publicKeyXml);
          
           return Convert.ToBase64String(encrypted);
       }
       private static byte[] Encrypt(byte[] data, int keySize, stringpublicKeyXml)
       {
           if (data == null || data.Length == 0) throw new ArgumentException(“Data are empty”, “data”);
           int maxLength = GetMaxDataLength(keySize);
           if(data.Length > maxLength) throw new ArgumentException(String.Format(“Maximum data length is {0}”, maxLength), “data”);
           if(!IsKeySizeValid(keySize)) throw new ArgumentException(“Key size is not valid”, “keySize”);
           if (String.IsNullOrEmpty(publicKeyXml)) throw new ArgumentException(“Key is null or empty”, “publicKeyXml”);
           using (var provider = new RSACryptoServiceProvider(keySize))
           {
               provider.FromXmlString(publicKeyXml);
               returnprovider.Encrypt(data, _optimalAsymmetricEncryptionPadding);
           }
       }
       public static string Decrypt(stringencryptedText)
       {
           int keySize = 0;
           stringpublicAndPrivateKeyXml = “”;
           GetKeyFromEncryptionString(PrivateKey, out keySize, outpublicAndPrivateKeyXml);
           var decrypted = Decrypt(Convert.FromBase64String(encryptedText), keySize, publicAndPrivateKeyXml);
      
           return Encoding.UTF8.GetString(decrypted);
       }
       private static byte[] Decrypt(byte[] data, int keySize, stringpublicAndPrivateKeyXml)
       {
           if (data == null || data.Length == 0) throw new ArgumentException(“Data are empty”, “data”);
           if(!IsKeySizeValid(keySize)) throw new ArgumentException(“Key size is not valid”, “keySize”);
           if (String.IsNullOrEmpty(publicAndPrivateKeyXml)) throw new ArgumentException(“Key is null or empty”, “publicAndPrivateKeyXml”);
           using (var provider = new RSACryptoServiceProvider(keySize))
           {
               provider.FromXmlString(publicAndPrivateKeyXml);
               returnprovider.Decrypt(data, _optimalAsymmetricEncryptionPadding);
           }
       }
       private static intGetMaxDataLength(int keySize)
       {
           if(_optimalAsymmetricEncryptionPadding)
           {
               return ((keySize – 384) / 8) + 7;
           }
           return ((keySize – 384) / 8) + 37;
       }
       private static boolIsKeySizeValid(int keySize)
       {
           return keySize >= 384 && keySize <= 16384 && keySize % 8 == 0;
       }
       private static voidGetKeyFromEncryptionString(string rawkey, out int keySize, out string xmlKey)
       {
           keySize = 0;
           xmlKey = “”;
           if (rawkey != null && rawkey.Length > 0)
           {
               byte[] keyBytes = Convert.FromBase64String(rawkey);
               var stringKey = Encoding.UTF8.GetString(keyBytes);
               if(stringKey.Contains(“!”))
               {
                   varsplittedValues = stringKey.Split(new char[] { ‘!’ }, 2);
                   try
                   {
                       keySize = int.Parse(splittedValues[0]);
                       xmlKey = splittedValues[1];
                   }
                   catch (Exception e) { }
               }
           }
       }
    }
}

Step 3: Create a Client program to test above code.

using System;
namespaceRSACryptography
{
    class Program
    {
        public static void Main(string[] args)
        {
            // Let’s Encrypt and Decrypt password
            var password = “P@sswrd123”;
            Console.WriteLine(“nnn Original Password Text: “ + password);
            Console.WriteLine(“nnn————————————nnn”);
            varencryptedText = CryptographyHelper.Encrypt(password);
            Console.WriteLine(“nnn Encrypted Password Text: “ + encryptedText);
            Console.WriteLine(“nnn————————————nnn”);
            vardecryptedText = CryptographyHelper.Decrypt(encryptedText);
            Console.WriteLine(“nnn Decrypted Password Text: “ + decryptedText);
            //Let’s Encrypt and Decrypt Database connection string in Web.config file
            varconnectionString = “Data Source=USER-VAIO\SQLEXPRESS;Initial Catalog=OrderProcessing;Integrated Security=True”;
            Console.WriteLine(“nnn Original Connection String Text: “ + connectionString);
            Console.WriteLine(“nnn————————————nnn”);
            encryptedText = CryptographyHelper.Encrypt(connectionString);
            Console.WriteLine(“nnn Encrypted Connection String Text: “ + encryptedText);
            Console.WriteLine(“nnn————————————nnn”);
            decryptedText = CryptographyHelper.Decrypt(encryptedText);
            Console.WriteLine(“nnn Decrypted Connection String Text: “ + decryptedText);
            Console.ReadKey();
        }
    }
}
                After execution of client program, it shows below successful outcome of encryption and decryption. You can see how  password and database connection string is encrypted and decrypted.
               


4.    Summary

The RSACryptoServiceProviderclass supports key lengths from 384 bits to 16384 bits in increments of 8 bits if you have the Microsoft Enhanced Cryptographic Provider installed. It supports key lengths from 384 bits to 512 bits in increments of 8 bits if you have the Microsoft Base Cryptographic Provider installed.
 
In this article I have explained how RSA algorithm useful for securing our sensitive data. I hope you enjoyed this article.

5.    Download Code

Click hereto download the source code of this assignment.

2 comments

  1. Dear csharpdocs Team,
    I am using your code for implementing RSA encrypt/decrypt.

    this sample code is working fine, but when I replace its private and public key from mine versions it throws error.

    I used keys generated from https://www.devglan.com/online-tools/rsa-encryption-decryption

    I tried both the versions 2048 and 4096, but none worked.

    Please help,

    Thanks in advance.

  2. Hello,
    Above code is working only for given set of Keys. Please provide alternative solution.

Leave a Reply