Asymmetric encryption is usually considered preferable to Symmetric encryption for transferring messages to other parties.

 this is often mainly because it negates many of the risks associated with the exchange of a shared key and ensures that whilst anyone with the general public key can encrypt a message for the intended recipient, only that recipient can decrypt it.

Unfortunately the main down-side of asymmetric encryption algorithms is that they’re significantly slower than their symmetric cousins. intrinsically the asymmetric encryption of files, especially large ones, can often be a very computationally intensive process.

In order to supply both security AND performance, a hybrid approach are often taken.

This entails the cryptographically random generation of a key and initialization vector for Symmetric encryption.

These values are then encrypted using an Asymmetric algorithm and written to the computer file , before getting used to encrypt the source data Symmetrically and appending it to the output.

This approach provides a high degree of both performance and security, therein the info is encrypted employing a symmetric algorithm (fast) and therefore the key and iv, both randomly generated (secure) are encrypted by an asymmetric algorithm (secure).

It also has the added advantage that an equivalent payload encrypted on different occasions will have very different cyphertext, because the symmetric keys are randomly generated whenever.

The following class demonstrates asymmetric encryption of strings and byte arrays, also as hybrid file encryption.

 public static class AsymmetricProvider 
{  #region Key Generation  public class KeyPair 
{  public string PublicKey { get; set; } 
public string PrivateKey { get; set; }  }
public static KeyPair GenerateNewKeyPair(int keySize = 4096)
  {  // KeySize is measured in bits.
 1024 is the default, 2048 is better, 4096 is more robust but takes a fair bit longer to generate.
 using (var rsa = new RSACryptoServiceProvider(keySize)) 
 {  return new KeyPair {PublicKey = rsa.ToXmlString(false), PrivateKey = rsa.ToXmlString(true)}; 
 }  }  #endregion  #region Asymmetric Data Encryption and Decryption  public static byte[] 
EncryptData(byte[] data, string publicKey) 
 {  using (var asymmetricProvider = new RSACryptoServiceProvider())  {  asymmetricProvider.FromXmlString(publicKey);
  return asymmetricProvider.Encrypt(data, true); 
 }  }  public static byte[] DecryptData(byte[] data, string publicKey) 
 {  using (var asymmetricProvider = new RSACryptoServiceProvider()) 
 {  asymmetricProvider.FromXmlString(publicKey);
  if (asymmetricProvider.PublicOnly) 
 throw new Exception("The key provided is a public key and does not contain the private key elements required for decryption");
  return asymmetricProvider.Decrypt(data, true); 
 }  }  public static string EncryptString(string value, string publicKey)
  {  return Convert.ToBase64String(EncryptData 
 (Encoding.UTF8.GetBytes(value), publicKey)); 
 }  public static string DecryptString(string value, string privateKey)
  {  return Encoding.UTF8.GetString(EncryptData(Convert.FromBase64String(value), privateKey));
  }  #endregion  #region Hybrid File Encryption and Decription  public static void EncryptFile(string inputFilePath, string outputFilePath, string publicKey) 
 {  using (var symmetricCypher = new AesManaged())
  {  // Generate random key and IV for symmetric encryption  var key = new byte[symmetricCypher.KeySize / 8];
  var iv = new byte[symmetricCypher.BlockSize / 8];
  using (var rng = new RNGCryptoServiceProvider())
  {  rng.GetBytes(key);  rng.GetBytes(iv);  } 
 // Encrypt the symmetric key and IV  var buf = new byte[key.Length + iv.Length];
  Array.Copy(key, buf, key.Length); 
 Array.Copy(iv, 0, buf, key.Length, iv.Length);
  buf = EncryptData(buf, publicKey);
  var bufLen = BitConverter.GetBytes(buf.Length);
  // Symmetrically encrypt the data and write it to the file, along with the encrypted key and iv  using (var cypherKey = symmetricCypher.CreateEncryptor(key, iv)) 
 using (var fsIn = new FileStream(inputFilePath, FileMode.Open))
  using (var fsOut = new FileStream(outputFilePath, FileMode.Create))
  using (var cs = new CryptoStream(fsOut, cypherKey, CryptoStreamMode.Write))
  {  fsOut.Write(bufLen,0, bufLen.Length);
  fsOut.Write(buf, 0, buf.Length);  fsIn.CopyTo(cs); 
 }  }  }  public static void DecryptFile(string inputFilePath, string outputFilePath, string privateKey) 
 {  using (var symmetricCypher = new AesManaged()) 
 using (var fsIn = new FileStream(inputFilePath, FileMode.Open)) 
 {  // Determine the length of the encrypted key and IV  var buf = new byte[sizeof(int)];
  fsIn.Read(buf, 0, buf.Length);
  var bufLen = BitConverter.ToInt32(buf, 0);
  // Read the encrypted key and IV data from the file and decrypt using the asymmetric algorithm  buf = new byte[bufLen];
  fsIn.Read(buf, 0, buf.Length);
  buf = DecryptData(buf, privateKey);  
 var key = new byte[symmetricCypher.KeySize / 8];
  var iv = new byte[symmetricCypher.BlockSize / 8];
  Array.Copy(buf, key, key.Length);
  Array.Copy(buf, key.Length, iv, 0, iv.Length);
  // Decript the file data using the symmetric algorithm  using (var cypherKey = symmetricCypher.CreateDecryptor(key, iv))
  using (var fsOut = new FileStream(outputFilePath, FileMode.Create))
  using (var cs = new CryptoStream(fsOut, cypherKey, CryptoStreamMode.Write)) 
 {  fsIn.CopyTo(cs); 
 }  }  }  #endregion  #region Key Storage  public static void WritePublicKey(string publicKeyFilePath, string publicKey) 
 {  File.WriteAllText(publicKeyFilePath, publicKey);
  }  public static string ReadPublicKey(string publicKeyFilePath)
  {  return File.ReadAllText(publicKeyFilePath); 
 }  private const string SymmetricSalt = "Stack_Overflow!";
 // Change me!  public static string ReadPrivateKey(string privateKeyFilePath, string password)
  {  var salt = Encoding.UTF8.GetBytes(SymmetricSalt);  var cypherText = File.ReadAllBytes(privateKeyFilePath);
  using (var cypher = new AesManaged()) 
 {  var pdb = new Rfc2898DeriveBytes(password, salt);
  var key = pdb.GetBytes(cypher.KeySize / 8);
  var iv = pdb.GetBytes(cypher.BlockSize / 8);
  using (var decryptor = cypher.CreateDecryptor(key, iv)) 
 using (var msDecrypt = new MemoryStream(cypherText))
  using (var csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))  using (var srDecrypt = new StreamReader(csDecrypt))
  {  return srDecrypt.ReadToEnd();
  }  }  }  public static void WritePrivateKey(string privateKeyFilePath, string privateKey, string password) 
 {  var salt = Encoding.UTF8.GetBytes(SymmetricSalt);
  using (var cypher = new AesManaged()) 
 {  var pdb = new Rfc2898DeriveBytes(password, salt);
  var key = pdb.GetBytes(cypher.KeySize / 8);
  var iv = pdb.GetBytes(cypher.BlockSize / 8);
  using (var encryptor = cypher.CreateEncryptor(key, iv)) 
 using (var fsEncrypt = new FileStream(privateKeyFilePath, FileMode.Create)) 
 using (var csEncrypt = new CryptoStream(fsEncrypt, encryptor, CryptoStreamMode.Write))
  using (var swEncrypt = new StreamWriter(csEncrypt))
  {  swEncrypt.Write(privateKey);  }  }  }  #endregion 
}  

Example of use:

 private static void HybridCryptoTest(string privateKeyPath, string privateKeyPassword, string inputPath)
 {  // Setup the test  var publicKeyPath = Path.ChangeExtension(privateKeyPath, ".public"); 
 var outputPath = Path.Combine(Path.ChangeExtension(inputPath, ".enc"));
  var testPath = Path.Combine(Path.ChangeExtension(inputPath, ".test"));
  if (!File.Exists(privateKeyPath))
  {  var keys = AsymmetricProvider.GenerateNewKeyPair(2048);
  AsymmetricProvider.WritePublicKey(publicKeyPath, keys.PublicKey);
  AsymmetricProvider.WritePrivateKey(privateKeyPath, keys.PrivateKey, privateKeyPassword);
  }  // Encrypt the file  var publicKey = AsymmetricProvider.ReadPublicKey(publicKeyPath);
  AsymmetricProvider.EncryptFile(inputPath, outputPath, publicKey);
  // Decrypt it again to compare against the source file  var privateKey= AsymmetricProvider.ReadPrivateKey(privateKeyPath, privateKeyPassword);
  AsymmetricProvider.DecryptFile(outputPath, testPath, privateKey);
  // Check that the two files match  var source = File.ReadAllBytes(inputPath);
  var dest = File.ReadAllBytes(testPath);
  if (source.Length != dest.Length) 
 throw new Exception("Length does not match"); 
 if (source.Where((t, i) => t != dest[i]).Any())
  throw new Exception("Data mismatch"); } 

If you like this post, don’t forget to share 🙂

This article is written by our awesome writer
Comments to: Fast Asymmetric File Encryption In C#

Your email address will not be published. Required fields are marked *

Attach images - Only PNG, JPG, JPEG and GIF are supported.

New Dark Mode Is Here

Sign In to access the new Dark Mode reading option.

Join our Newsletter

Get our monthly recap with the latest news, articles and resources.

By subscribing you agree to our Privacy Policy.

Latest Articles

Explore Tutorials By Categories

About

Codeverb is simply an all in one interactive learning portal, we regularly add new topics and keep improving the existing ones, if you have any suggestions, questions, bugs issue or any other queries you can simply reach us via the contact page

Login

Welcome to Codeverb

Ready to learn something new?
Join Codeverb!

Read Smart, Save Time
  •  
    Strength indicator
  •  
  •  
    Log In | Lost Password