Share via


How to generate key pairs, encrypt and decrypt data with .NET (C#)

Hi all,

The other day a colleague of mine asked me if I had a .NET version of the C++ sample in How to generate key pairs, encrypt and decrypt data with CryptoAPI post. C++ sample calls CryptoAPI directly (and you know we can do the same thing in .NET through P/Invoke), but the idea was to use System.Security classes in order to get a pure .NET solution. The answer is yes, I have such sample, and here it is:

 using System;
using System.Collections.Generic;
using System.Text;
using System.Security.Cryptography;
using System.IO;

namespace EncryptDecrypt
{
    class Program
    {
        // Main
        static void Main(string[] args)
        {
            if ((args.Length == 3) && (args[0].Equals("k")))
            {
                // Generate a new key pair
                Keys(args[1], args[2]);
             
            }
            else if ((args.Length == 4) && (args[0].Equals("e")))
            {
                // Encrypt a file
                Encrypt(args[1], args[2], args[3]);

            }
            else if ((args.Length == 4) && (args[0].Equals("d")))
            {
                // Decrypt a file
                Decrypt(args[1], args[2], args[3]);
            }
            else {
                // Show usage
                Console.WriteLine("Usage:");
                Console.WriteLine("   - New key pair: EncryptDecrypt k public_key_file private_key_file");
                Console.WriteLine("   - Encrypt:      EncryptDecrypt e public_key_file plain_file encrypted_file");
                Console.WriteLine("   - Decrypt:      EncryptDecrypt d private_key_file encrypted_file plain_file");
            }

            // Exit
            Console.WriteLine("\n<< Press any key to continue >>");
            Console.ReadKey();
            return;

        } // Main

        // Generate a new key pair
        static void Keys(string publicKeyFileName, string privateKeyFileName)
        {
            // Variables
            CspParameters cspParams = null;
            RSACryptoServiceProvider rsaProvider = null;
            StreamWriter publicKeyFile = null;
            StreamWriter privateKeyFile = null;
            string publicKey = "";
            string privateKey = "";

            try 
            {
                // Create a new key pair on target CSP
                cspParams = new CspParameters();
                cspParams.ProviderType = 1; // PROV_RSA_FULL 
                //cspParams.ProviderName; // CSP name
                cspParams.Flags = CspProviderFlags.UseArchivableKey;
                cspParams.KeyNumber = (int)KeyNumber.Exchange;
                rsaProvider = new RSACryptoServiceProvider(cspParams);

                // Export public key
                publicKey = rsaProvider.ToXmlString(false);

                // Write public key to file
                publicKeyFile = File.CreateText(publicKeyFileName);
                publicKeyFile.Write(publicKey);                

                // Export private/public key pair 
                privateKey = rsaProvider.ToXmlString(true);

                // Write private/public key pair to file
                privateKeyFile = File.CreateText(privateKeyFileName);
                privateKeyFile.Write(privateKey);                
            }
            catch (Exception ex)
            {
                // Any errors? Show them
                Console.WriteLine("Exception generating a new key pair! More info:");
                Console.WriteLine(ex.Message);
            }
            finally
            {
                // Do some clean up if needed
                if (publicKeyFile != null)
                {
                    publicKeyFile.Close();
                }
                if (privateKeyFile != null)
                {
                    privateKeyFile.Close();
                }
            }

        } // Keys

        // Encrypt a file
        static void Encrypt(string publicKeyFileName, string plainFileName, string encryptedFileName)
        {
            // Variables
            CspParameters cspParams = null;
            RSACryptoServiceProvider rsaProvider = null;
            StreamReader publicKeyFile = null;
            StreamReader plainFile = null;
            FileStream encryptedFile = null;
            string publicKeyText = "";
            string plainText = "";
            byte[] plainBytes = null;
            byte[] encryptedBytes = null;

            try 
            {
                // Select target CSP
                cspParams = new CspParameters();
                cspParams.ProviderType = 1; // PROV_RSA_FULL 
                //cspParams.ProviderName; // CSP name
                rsaProvider = new RSACryptoServiceProvider(cspParams);

                // Read public key from file
                publicKeyFile = File.OpenText(publicKeyFileName);
                publicKeyText = publicKeyFile.ReadToEnd();

                // Import public key
                rsaProvider.FromXmlString(publicKeyText);

                // Read plain text from file
                plainFile = File.OpenText(plainFileName);
                plainText = plainFile.ReadToEnd();

                // Encrypt plain text
                plainBytes = Encoding.Unicode.GetBytes(plainText);
                encryptedBytes = rsaProvider.Encrypt(plainBytes, false);

                // Write encrypted text to file
                encryptedFile = File.Create(encryptedFileName);
                encryptedFile.Write(encryptedBytes, 0, encryptedBytes.Length);                
            }
            catch (Exception ex)
            {
                // Any errors? Show them
                Console.WriteLine("Exception encrypting file! More info:");
                Console.WriteLine(ex.Message);
            }    
            finally
            {
                // Do some clean up if needed
                if (publicKeyFile != null)
                {
                    publicKeyFile.Close();
                }
                if (plainFile != null)
                {
                    plainFile.Close();
                }
                if (encryptedFile != null)
                {
                    encryptedFile.Close();
                }
            }

        } // Encrypt

        // Decrypt a file
        static void Decrypt(string privateKeyFileName, string encryptedFileName, string plainFileName)
        {
            // Variables
            CspParameters cspParams = null;
            RSACryptoServiceProvider rsaProvider = null;
            StreamReader privateKeyFile = null;
            FileStream encryptedFile = null;
            StreamWriter plainFile = null;
            string privateKeyText = "";
            string plainText = "";
            byte[] encryptedBytes = null;
            byte[] plainBytes = null;

            try
            {
                // Select target CSP
                cspParams = new CspParameters();
                cspParams.ProviderType = 1; // PROV_RSA_FULL 
                //cspParams.ProviderName; // CSP name
                rsaProvider = new RSACryptoServiceProvider(cspParams);

                // Read private/public key pair from file
                privateKeyFile = File.OpenText(privateKeyFileName);
                privateKeyText = privateKeyFile.ReadToEnd();

                // Import private/public key pair
                rsaProvider.FromXmlString(privateKeyText);

                // Read encrypted text from file
                encryptedFile = File.OpenRead(encryptedFileName);
                encryptedBytes = new byte[encryptedFile.Length];
                encryptedFile.Read(encryptedBytes, 0, (int)encryptedFile.Length);

                // Decrypt text
                plainBytes = rsaProvider.Decrypt(encryptedBytes, false);

                // Write decrypted text to file
                plainFile = File.CreateText(plainFileName);
                plainText = Encoding.Unicode.GetString(plainBytes);
                plainFile.Write(plainText);
            }
            catch (Exception ex)
            {
                // Any errors? Show them
                Console.WriteLine("Exception decrypting file! More info:");
                Console.WriteLine(ex.Message);
            }
            finally
            {
                // Do some clean up if needed
                if (privateKeyFile != null)
                {
                    privateKeyFile.Close();
                }
                if (encryptedFile != null)
                {
                    encryptedFile.Close();
                }
                if (plainFile != null)
                {
                    plainFile.Close();
                }
            }

        } // Decrypt
    }
}

If you compare both samples you will see that .NET simplifies the task a lot. But sometimes we won't be able to do with System.Security classes exactly the same we can do with CryptoAPI. So don't forget about the API just yet!

I hope this helps.

Kind regards,

 

Alex (Alejandro Campos Magencio)

Comments

  • Anonymous
    December 04, 2008
    Hi,i am having a very tough time figuring out how to encrypt something inc# and then decrypt it in c++ using cryptography (and vice versa). There seems to be no corralation between the old api and the new .net implementation. any help will be appreciated. any example of even the simplist encryption that can implemented cross platform (c++ and c#) would be very helpful.
  • Anonymous
    December 04, 2008
    .NET is indeed calling CryptoAPI behind the scenes. You may use my CryptoAPI tracer (http://blogs.msdn.com/alejacma/archive/2007/10/31/cryptoapi-tracer.aspx) to check which CryptoAPI calls .NET is making to encrypt data, and use that info to decrypt data with C++ and CryptoAPI...I hope this helps. If not, I suggest you open a case with Microsoft Technical Support. Thanks!
  • Anonymous
    April 22, 2009
    Thank you for this article. Cleared out some things for me.
  • Anonymous
    November 02, 2009
    For the encryption and decryption if we want to use RsaParameters instead of CspParameters what do we have to do?
  • Anonymous
    February 08, 2010
    the above code compiles and runs but i get an error "Key not valid for use in specified state"when trying to call d encryt method.I run WinXp SP2 and .net framework 2.0,3.5,3.0 on same machine.i've checked the maximum length allowed by the different combinations of operating systems as suggested in http://blogs.msdn.com/alejacma/archive/2008/10/23/rsacryptoserviceprovider-encrypt-returns-key-not-valid-for-use-in-specified-state-error.aspxwhat else can i be doing wrong.Many thanks
  • Anonymous
    July 05, 2010
    Very good example. Saved me a lot of time/work.Thank alejacma. :)
  • Anonymous
    May 02, 2012
    Thanks for the sample. How would this be done with CNG Keys? I am having a tough time creating CNG Keys...
  • Anonymous
    September 04, 2013
    Thanks for posting such a great article
  • Anonymous
    May 13, 2015
    Hello alejacma, i am new to c# can I know how you compile this? I tried and compile but I cannot see any private key or public key file in my project