Microsoft

Blog Categories

Subscribe to RSS feed

Archives

RSA Encryption using an SSL Certificate

I was recently engaged on a project where we needed to create our own password vault for the execution of our API calls to external systems. The first cut of the encryption used Triple DES. Triple DES was great except we had to store the key in our code. The thought of storing the key in the code is fine if security is not a concern. Obfuscation was discussed, but I have encountered at least one de-obfuscator that worked well, so that was ruled out. Since our goal was to make the storage of the key maintainable by administrators we decided on RSA encryption using an SSL certificate. The certificate will have to be self signed so that we can use the private key for our encryption needs. I used Microsoft’s Certificate Creation Tool to generate my certificate. The certificate was then installed in the personal folder for the computer account.

Now that we got the certificate installed we can start using it. Below I will describe how to use the certificate for encryption. I have worked a bit with encryption in the past and found it easiest to create a class that encapsulates the functionality for a specific type of encryption. What I am going to show you for RSA is very similar to what I did with the Triple DES. The first thing I did was create a class called “Encryption” The class constructor took one parameter which was the certificate subject. The certificate subject is used in the retrieval of the certificate from the certificate store on the machine. The Constructor looks like the code shown below:

public Encryption(string certSubject)

{

X509Certificate2 cert = GetCert(certSubject);

publicXml = cert.PublicKey.Key.ToXmlString(false);

privateXml = cert.PrivateKey.ToXmlString(true);

}

A private method called GetCert (shown below) is used to get the X509 Certificate:

private X509Certificate2 GetCert(string certSubject)

{

X509Store store = new X509Store(StoreLocation.LocalMachine);

store.Open(OpenFlags.ReadOnly);

X509Certificate2 cert = null;

foreach (X509Certificate2 c in store.Certificates)

{

if (c.Subject == certSubject)

{

cert = c;

break;

}

}

store.Close();

return cert;

}

Now that we have a reference to the certificate and the key’s XML representation we can start using it. The method we use for encrypting is pretty straight forward and uses an RSACryptoProvider. The Encrypt method looks like the following:

public byte[] Encrypt(string data)

{

byte[] arInput = System.Text.Encoding.Unicode.GetBytes(data);

RSACryptoServiceProvider provider = new RSACryptoServiceProvider();

provider.Clear();

provider.FromXmlString(publicXml);

byte[] text = provider.Encrypt(arInput, false);

return text;

}

The first order of business is to convert the string to encrypt into a byte array. Once converted we can create our Crypto Provider. The clear method on the provider is called to clear out any default values. I figured this out the hard way; my provider did not like using the key I was feeding it when I did not call clear. Once the provider is cleared we can now load our key that we got from the SSL certificate. Once that is done we can encrypt our string/byte array.

The encrypt method returns a byte array. You can either store the byte array in its native format or you can do as we did and convert it to a string. This leads us to our next public method in the class. I created the method as static in the event that we wanted to use this functionality elsewhere…I did not want to force the creation of the encryption object to do something that is not related to encryption.

public static string ConvertByteArrayToString(byte[] data)

{

return BitConverter.ToString(data);

}

Now that we have the encrypted data converted to a string we can store it in a database or where ever the application calls for. Now let’s focus on the decryption. The Decrypt method has two signatures; one takes a string and the other takes a byte array. The methods for the decryption are shown below:

public string Decrypt(byte[] data)

{

CspParameters parms = new CspParameters();

parms.Flags = CspProviderFlags.UseMachineKeyStore;

RSACryptoServiceProvider provider = new RSACryptoServiceProvider(parms);

provider.FromXmlString(privateXml);

byte[] unencryptedData = provider.Decrypt(data, false);

return System.Text.Encoding.Unicode.GetString(unencryptedData);

}

public string Decrypt(string data)

{

if (data.Trim() == string.Empty)

return data;

byte[] arData = Encryption.ConvertStringToByteArray(data);

return Decrypt(arData);

}

The method that takes the string parameter should be self explanatory so I am not going to talk about it. The method that accepts the byte array is the one we will discuss. The first that I have to do is Create CspParameters to pass into the crypto provider. The flag we are specifying is for the provider to use the machine key store. Once we create our provider with the specified parameters we can give it our private key information that we retrieved as part of our constructor. Once that is done we can start decrypting.

The only other method I have not discussed is another static method that we will use for converting a string to a byte array. The method looks like the following:

public static byte[] ConvertStringToByteArray(string encryptedData)

{

string[] arData = encryptedData.Split("-".ToCharArray());

byte[] returnData = new byte[arData.Length];

for (int i = 0; i < arData.Length; i++)

returnData[i] = byte.Parse(arData[i], NumberStyles.HexNumber);

return returnData;

}

The code above just takes each byte and converts it one byte at a time to a character.

When coding this there were no issues until we started deploying this to production. One of the major gotchas was the app pool account for the web application needs to have read rights on the certificate. This is a gotcha that only occurs on Windows Server 2008. There are plenty of articles out there that will explain how to set the permissions for a certificate.

Leave a Reply