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)
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.
.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!
Thank you for this article. Cleared out some things for me.
For the encryption and decryption if we want to use RsaParameters instead of CspParameters what do we have to do?
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
Very good example. Saved me a lot of time/work.
Thank alejacma. :)
Thanks for the sample. How would this be done with CNG Keys? I am having a tough time creating CNG Keys...