I had a requirement for a project that I am working on. The project consists of a Windows 2003 Web Server serving up custom C# Web Services, and a public-facing LAMP [Linux, Apache, MySQL, PHP] web server. I have certain data that I am storing in a MySQL database that must be encrypted. Given that this is a webserver symmetric encryption does not make sense because if someone gained access to the PHP files, they would have the password to decrypt the data!! That would be bad.
So I chose to do asymmetric encryption, thus only the Public Key would reside on the server. The PHP scripts would encrypt the data, store it in a database, and when needed would be transmitted to the web-services server and decrypted using C#. This proved to be a rather daunting task however.
PHP requires either an X509 certificate, or a PEM certificate. On the surface, .NET does neither! The RSACryptoProviderService was able to create keys, but they were in some XML format which was incompatible with what I needed to do with PHP.
It turns out that I needed to use X509 certificates to accomplish what I needed. Yikes, I had absolutely no idea what X509 was and how I was going to use it. Little did I know that the internet was going to conspire against me and make finding all of the information that I needed virtually impossible. After spending countless hours going over the MSDN, web pages, and other resources.. I finally managed to figure out what exactly needed to be done.
Following this blogpost on using Makecert I was able to create a certificate authority to sign my certificate, and create a certificate. I installed in it in the Certificate Store, exported BOTH the Private and Public key file. The public key I exported in BASE64 (second option) and the private key in PKCS#12 (PFX) format. The private key is readable by .NET for the decryption.
I sent the public key to my PHP webserver. Using the following code I was able to encrypt a string:
Now on the .NET side.. here is the code I used to decrypt the data using a private certificate file:
/// <summary>
///Decrypt data
/// </summary>
/// <param name="Base64EncryptedData"></param>
/// <param name="PathToPrivateKey"></param>
/// <returns></returns>
public static string DecryptEncryptedData(stringBase64EncryptedData, stringPathToPrivateKeyFile) {
X509Certificate2myCertificate;
try{
myCertificate = newX509Certificate2(PathToPrivateKeyFile);
} catch{
throw newCryptographicException("Unable to open key file.");
}
RSACryptoServiceProvider rsaObj;
if(myCertificate.HasPrivateKey) {
rsaObj = (RSACryptoServiceProvider)myCertificate.PrivateKey;
} else
throw newCryptographicException("Private key not contained within certificate.");
if(rsaObj == null)
returnString.Empty;
byte[] decryptedBytes;
try{
decryptedBytes = rsaObj.Decrypt(Convert.FromBase64String(Base64EncryptedData), false);
} catch {
throw newCryptographicException("Unable to decrypt data.");
}
// Check to make sure we decrpyted the string
if(decryptedBytes.Length == 0)
returnString.Empty;
else
returnSystem.Text.Encoding.UTF8.GetString(decryptedBytes);
}
There you have it! That's it in a nutshell. By using the X509Certificate2 class, I was able to read in the key [this reads in several key formats, by the way], then create the RSACryptoServiceProvider object and cast the .PrivateKey field into it, and perform the decryption.
- Matthew
Good article. Thanks for posting for us .NET people :) Easy and powerful.
Very useful article thanks for sharing :).
Thanks a lot for sharing. Simple, to the point, and very useful for me :)