Wednesday, April 27, 2016

Transport RSA Key Containers

We have a Message Bus application where messages are passed all around, from one application instance to another. This message traffic can be intranet, inter-domain or internet. We keep the messages safe from prying eyes by using RSA encryption (public keys are stored in databases).

Now, the whole RSA encryption thing is a topic for another day (or just Google for more info, in case I never get around to writing that blog post). Today, I’m going to talk about what to do if you need to move one of your existing Message Bus deployments to another computer for some reason (like the computer is old or dying and you need to replace it with new hardware).

This new computer will not have your RSA KeyContainer. Creating a new KeyContainer with the same name doesn’t get you the same keys, obviously. All your messaging traffic comes to a screeching halt, because now the messages can’t be decrypted with these different keys.

The solution is to copy your RSA KeyContainer to the new computer. I have a little utility application that I wrote that will read the RSA KeyContainer and write it to an XML file. The utility will also read from an XML file and create an RSA KeyContainer from it. In the UI of the utility, simply click one button to save the KeyContainer to the XML file. Click another button to create a KeyContainer from the XML file.

Here’s the basic code you need to do that (you can write the UI part of it yourself):

private string KeyContainerName = "Your.KeyContainer.Name";
private string DefaultFileName = "YourKeyContainer.xml";
private string SavedFileName = "";
private RSACryptoServiceProvider KeyContainer;

// call these from one of the two button clicks:
private void GetKeyContainerSaveToFile()
{
if (this.GetRSAContainer())
{
// save key container to a file
this.WriteDataToFile(this.KeyContainer.ToXmlString(true), this.DefaultFileName);
if (this.SavedFileName != "")
// Display to UI: "RSA KeyContainer saved to " + this.SavedFileName;
else
// Display to UI: "CANCELLED! RSA KeyContainer NOT saved to file";
}
}
private void GetXmlCreateKeyContainer()
{
if (this.GetRSAContainer())
{
// read key container XML from file and save it to the container
string key = this.ReadDataFromFile(this.DefaultFileName);
if (key != "")
{
this.KeyContainer.FromXmlString(key);
// Display to UI: "RSA KeyContainer info retrieved from file " + this.SavedFileName + " and saved to machine KeyContainer";
}
else
// Display to UI: "CANCELLED! RSA NOT retreived from file and NOT saved to machine";
}
}

// this will get an existing Container, or create a new Container if none exists
private bool GetRSAContainer()
{
try
{
// retrieve the key container
CspParameters cp = new CspParameters();
cp.KeyContainerName = this.KeyContainerName;
cp.Flags |= CspProviderFlags.UseMachineKeyStore;
this.KeyContainer = new RSACryptoServiceProvider(cp);
return true;
}
catch (Exception ex)
{
// Display the Exception to UI
return false;
}
}

// Write to an XML file:
private void WriteDataToFile(string Data, string FileName)
{
// Configure file dialog box
Microsoft.Win32.SaveFileDialog dlg = new Microsoft.Win32.SaveFileDialog();
dlg.FileName = FileName;
dlg.DefaultExt = ".xml"; // Default file extension
dlg.Filter = "XML documents (.xml)|*.xml"; // Filter files by extension

// Show save file dialog box
Nullable<bool> result = dlg.ShowDialog();

if (result == true)
{
this.SavedFileName = dlg.FileName;
using (StringReader sr = new StringReader(Data))
{
using (TextWriter tw = new StreamWriter(dlg.FileName))
{
string s = null;
while (true)
{
s = sr.ReadLine();
if (s != null)
tw.WriteLine(s);
else
break;
}
}
}
}
else
this.SavedFileName = "";
}

// Retrieve from the XML file
private string ReadDataFromFile(string FileName)
{
// Configure file dialog box
Microsoft.Win32.OpenFileDialog dlg = new Microsoft.Win32.OpenFileDialog();
dlg.FileName = FileName;
dlg.DefaultExt = ".xml"; // Default file extension
dlg.Filter = "XML documents (.xml)|*.xml"; // Filter files by extension

// Show open file dialog box
Nullable<bool> result = dlg.ShowDialog();

string xml = "";
if (result == true)
{
this.SavedFileName = dlg.FileName;
using (StreamReader file = new StreamReader(dlg.FileName))
{
xml = file.ReadToEnd();
}
}
else
this.SavedFileName = "";

return xml;
}