I recently needed to encrypt/decrypt strings sent as querystring parameters over the wire. The use case happens to be allowing people to unsubscribe from a newsletter by clicking on a hyperlink in their email. The server receives the email as a querystring. Obviously, I don’t want to expose a public service that takes an unencrypted email. So, I encrypt the email as part of the newsletter template. Then decrypt on the web server.
Not rocket science, but worth going over how I did it, as there were a few gotchas.
First, I generated an RSA crypto key as XML, using the code found here:
public class MyCrypto
{
RSACryptoServiceProvider rsa = null;
string publicPrivateKeyXML;
string publicOnlyKeyXML;
public void AssignNewKey()
{
const int PROVIDER_RSA_FULL = 1;
const string CONTAINER_NAME = "KeyContainer";
CspParameters cspParams;
cspParams = new CspParameters(PROVIDER_RSA_FULL);
cspParams.KeyContainerName = CONTAINER_NAME;
cspParams.Flags = CspProviderFlags.UseMachineKeyStore;
cspParams.ProviderName = "Microsoft Strong Cryptographic Provider";
rsa = new RSACryptoServiceProvider(cspParams);
//Pair of public and private key as XML string.
//Do not share this to other party
publicPrivateKeyXML = rsa.ToXmlString(true);
//Private key in xml file, this string should be share to other parties
publicOnlyKeyXML = rsa.ToXmlString(false);
}
}
Then, to encrypt the string in such a way that it could be passed as a querystring, I had to make some changes. First, I changed the encoding to UTF8 and not ASCII. Second, I then base64 encode the string, Third, I URL encode the string:
public string EncryptAndEncode(string text)
{
string encryptedText;
using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider())
{
rsa.FromXmlString(Resources.Resources.publicKeyXML);
var bytes = rsa.Encrypt(Encoding.UTF8.GetBytes(text), true);
encryptedText = Convert.ToBase64String(bytes);
}
return HttpUtility.UrlEncode(encryptedText);
}
On the decrypt side, because I’m getting the string as a method in my controller, the URL decoding is handled for me. So, the decrypt looks like this:
private string Decrypt(string text)
{
string decryptedText;
using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider())
{
rsa.FromXmlString(Resources.Resources.publicPrivateKeyXML);
var bytes = Convert.FromBase64String(text);
decryptedText = Encoding.UTF8.GetString(rsa.Decrypt(bytes, true));
}
return decryptedText;
}