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 (9)

  1. siva says:

    Hi,

    i am having a very tough time figuring out how to encrypt something in

    c# 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.

  2. alejacma says:

    .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!

  3. PuZo says:

    Thank you for this article. Cleared out some things for me.

  4. AnVi says:

    For the encryption and decryption if we want to use RsaParameters instead of CspParameters what do we have to do?

  5. laolu says:

    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.aspx

    what else can i be doing wrong.

    Many thanks

  6. Tiago says:

    Very good example. Saved me a lot of time/work.

    Thank alejacma. 🙂

  7. Thanks for the sample. How would this be done with CNG Keys? I am having a tough time creating CNG Keys…

  8. Raj says:

    Thanks for posting such a great article

  9. Vincent says:

    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