Working with public-key encryption isn't quite as hard as it seems. The task of encrypting and decrypting data in a PKCS environment all boils down to one thing: key management. You need to know where your keys are, and you need to make sure that they are protected before you can reliably use public-key encryption.
One of the most common tasks in PKCS encryption is the generation and storage of new keys. Fortunately, the RSA cryptographic service provider will create the public/private key pair automatically for you if you don't supply one. RSA is named for the three people who invented the cryptographic algorithm: Ron Rivest, Adi Shamir, and Leonard Adleman.
You definitely don't want your private key lying around in a simple file on your hard drive, because then all data encrypted with that key becomes just as vulnerable as your hard drive (which is pretty vulnerable).
To solve this problem, Windows allows you to make use of key containers. A key container is a secure place to store your key, and you are the only one who has access to that container. Even an administrator with full access to the hard drive will be unable to obtain the key from your key container (unless they have your login and password, of course).
To store your key pair in a container, you need to make use of the CspParameters class. This class maintains a list of parameters that are passed to a cryptographic service provider upon instantiation. One of these parameters is a property called KeyContainerName. This name can be anything you choose, but make sure it's something that your application can reproduce, because if you forget the container name, you will lose the key pair.
The following few lines of code will create an instance of the CspParameters class, indicate the name of the key container, and instantiate the RSA crypto provider. If this is the first time these lines of code have been executed, a new key pair will be generated and placed in the container:
CspParameters csp = new CspParameters(); csp.KeyContainerName = "SAMSContainer"; RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(csp); // perform encryption/decryption operations
Each time the code instantiates an RSA crypto provider with the same container name, the code will reuse the existing key pair. Note that another application, logged on under another user, will not be able to read the key information, even if the container name matches. For more information about Windows key containers, you can look up the Cryptography topic in the MSDN documentation at http://msdn.microsoft.com.
To demonstrate how to send an encrypted message from one person (or application) to another, I'll start with Jane and John Doe. Jane creates an instance of the RSA provider, and saves her key in an XML file using the ToXmlString() method on the RSACryptoServiceProvider class. The contents of this XML file aren't all that pretty to look at, but if you're interested you can see the JanesKey.xml file with the code accompanying this book.
As mentioned before, to send a message to Jane, you need to encrypt it with Jane's public key. So, to create an application that simulates this process, the application will read Jane's public key from the JanesKey.xml file, and use that information to encrypt a message to her, as shown in the following example:
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(); StreamReader sr = File.OpenText(@"..\..\..\..\JanesKey.xml"); string rsaXml = sr.ReadToEnd(); sr.Close(); rsa.FromXmlString(rsaXml); string messageToJane = "Hey Jane, this is John. How are things?"; byte encrypted = rsa.Encrypt( System.Text.ASCIIEncoding.ASCII.GetBytes(messageToJane), false); FileStream fs = new FileStream(@"..\..\..\..\MessageToJane.dat", FileMode.Create); fs.Write(encrypted, 0, encrypted.Length); fs.Close();
The code in this sample creates an RSA provider, which loads Jane's public key using the FromXmlString method. Then the message is encrypted into an array of bytes, which is then stored in a file called MessageToJane.dat. If you run this sample on your own, you'll see that the encrypted file takes up 128 bytes, far more than the plain text version of the message.
To continue the sample, we need to switch to Jane's perspective. She will be attempting to decrypt John's message with her private key. So she'll need to load her key either from a container or from an XML file with the FromXmlString method. For simplicity in this sample, she'll load it from XML, as shown in the following example:
StreamReader sr = File.OpenText(@"..\..\..\..\JanesKey.xml"); string janesKey = sr.ReadToEnd(); sr.Close(); RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(); rsa.FromXmlString(janesKey); FileStream fs = new FileStream(@"..\..\..\..\MessageToJane.dat", FileMode.Open); byte encrypted = new byte[fs.Length]; fs.Read(encrypted, 0, (int)fs.Length); byte decrypted = rsa.Decrypt(encrypted, false); fs.Close(); Console.WriteLine(System.Text.ASCIIEncoding.ASCII.GetString(decrypted)); Console.ReadLine();
There are only a few steps in the preceding code. First, Jane's key pair is loaded from the XML file (this is ordinarily a bad practice...use key containers for safe storage of key pairs) and then John's encrypted message is loaded into a byte array. That byte array is then decrypted using Jane's private key, and the decrypted message is displayed on the console.