The major difference between symmetric and asymmetric algorithms is key management. Symmetric algorithms have one key, and asymmetric algorithms have two keys: public key for encrypting data and private key for decrypting data. The public key can be available to everyone who wants to encrypt data, the private key should be available only to those decrypting information.
You don’t need to include a way to select the algorithm in asymmetric encryption utility class, because the .NET Framework ships with only one asymmetric algorithm for real data encryption – RSA:
public static class AsymmetricEncryptionUtility
{
public static string GenerateKey(string targetFile) { }
private static void ReadKey(RSACryptoServiceProvider algorithm, string keyFile) { }
public static byte[] EncryptData(string data, string publicKey) { }
public static string DecryptData(byte[] data, string keyFile) { }
}
The GenerateKey method creates an instance of the RSA algorithm for generating the key. It stores only the private key in the file secured through the DPAPI and returns the public key representation as an XML string using the ToXmlString() method of the algorithm. The private key is usually kept as a secret by the application, while the public key is shared with others to be able to encrypt information that then is decrypted by the application using its secret private key:
public static string GenerateKey(string targetFile)
{
RSACryptoServiceProvider Algorithm = new RSACryptoServiceProvider();
// Save the private key
string CompleteKey = Algorithm.ToXmlString(true);
byte[] KeyBytes = Encoding.UTF8.GetBytes(CompleteKey);
KeyBytes = ProtectedData.Protect(KeyBytes,null, DataProtectionScope.LocalMachine);
using (FileStream fs = new FileStream(targetFile, FileMode.Create))
{
fs.Write(KeyBytes, 0, KeyBytes.Length);
}
// Return the public key
return Algorithm.ToXmlString(false);
}
The caller of the function needs to store the public key somewhere, because this is necessary for encrypting information. You can retrieve the key as an XML representation through a method called ToXmlString(). The parameter specifies whether private key information is included (true) or not (false). As a result, the GenerateKey function first calls the ToXmlString() function with the true parameter to store the complete key information in the file and then calls it with the false parameter to include the public key only. Subsequently, the ReadKey() method just reads the key from the file and then initializes the passed algorithm instance through FromXml(), the opposite of the ToXmlString() method:
private static void ReadKey(RSACryptoServiceProvider algorithm, string keyFile)
{
byte[] KeyBytes;
using(FileStream fs = new FileStream(keyFile, FileMode.Open))
{
KeyBytes = new byte[fs.Length];
fs.Read(KeyBytes, 0, (int)fs.Length);
}
KeyBytes = ProtectedData.Unprotect(KeyBytes,
null, DataProtectionScope.LocalMachine);
algorithm.FromXmlString(Encoding.UTF8.GetString(KeyBytes));
}
The EncryptData() function requires the caller to pass in the XML string representation of the public key returned by the GenerateKey() method, because the private key is not required for encryption. Encryption and decryption with RSA takes place as follows:
public static byte[] EncryptData(string data, string publicKey)
{
// Create the algorithm based on the public key
RSACryptoServiceProvider Algorithm = new RSACryptoServiceProvider();
Algorithm.FromXmlString(publicKey);
// Now encrypt the data
return Algorithm.Encrypt(Encoding.UTF8.GetBytes(data), true);
}
public static string DecryptData(byte[] data, string keyFile)
{
RSACryptoServiceProvider Algorithm = new RSACryptoServiceProvider();
ReadKey(Algorithm, keyFile);
byte[] ClearData = Algorithm.Decrypt(data, true);
return Convert.ToString(Encoding.UTF8.GetString(ClearData));
}