May 13, 2024
Secure File Encryption and Decryption in C#
Introduction: Encrypting sensitive files is crucial for protecting data privacy and confidentiality. In this blog post, we'll explore how to implement secure file encryption and decryption in C# using the AES encryption algorithm with a password-derived key and a fixed salt.
Background: AES (Advanced Encryption Standard) is a symmetric encryption algorithm widely used for securing data. It operates on fixed-size blocks and supports different key sizes (128, 192, or 256 bits). AES is secure and efficient, making it a popular choice for encryption tasks.
Key Derivation: To derive a secure encryption key from a user-provided password, we'll use the PBKDF2 (Password-Based Key Derivation Function 2) algorithm. PBKDF2 applies a pseudorandom function iteratively to derive a key from the password and a salt. Using a salt helps mitigate common attacks like rainbow table attacks by adding randomness to the key derivation process.
Encryption Process: During encryption, we'll generate a random initialization vector (IV) and prepend it to the encrypted file. The IV ensures that each encryption produces unique ciphertext, even if the same plaintext is encrypted multiple times with the same key. We'll also prepend the salt used for key derivation to the encrypted file to facilitate decryption.
Decryption Process: During decryption, we'll read the IV and salt from the encrypted file and use them along with the user-provided password to derive the decryption key. By using the same IV and salt as during encryption, we ensure that decryption produces the correct plaintext.
Code Example: Let's take a look at the final C# code for secure file encryption and decryption:
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
Console.WriteLine("File Encryption Application");
Console.WriteLine("---------------------------");
// Prompt user for mode (encryption or decryption)
Console.Write("Choose mode (E for encryption, D for decryption): ");
char mode = Console.ReadKey().KeyChar;
Console.WriteLine();
// Fixed salt value
byte[] fixedSalt = new byte[16] { 0x53, 0x61, 0x6C, 0x74, 0x56, 0x61, 0x6C, 0x74, 0x46, 0x6F, 0x72, 0x46, 0x69, 0x78, 0x65, 0x64 };
switch (char.ToUpper(mode))
{
case 'E':
// Encryption mode
Console.Write("Enter the input file path: ");
string inputEncryptFile = Console.ReadLine();
Console.Write("Enter the output file path: ");
string outputEncryptFile = Console.ReadLine();
Console.Write("Enter the password: ");
string passwordEncrypt = Console.ReadLine();
try
{
EncryptFile(inputEncryptFile, outputEncryptFile, passwordEncrypt, fixedSalt);
Console.WriteLine("File encrypted successfully.");
}
catch (Exception ex)
{
Console.WriteLine($"An error occurred: {ex.Message}");
}
break;
case 'D':
// Decryption mode
Console.Write("Enter the input file path: ");
string inputDecryptFile = Console.ReadLine();
Console.Write("Enter the output file path: ");
string outputDecryptFile = Console.ReadLine();
Console.Write("Enter the password: ");
string passwordDecrypt = Console.ReadLine();
try
{
DecryptFile(inputDecryptFile, outputDecryptFile, passwordDecrypt, fixedSalt);
Console.WriteLine("File decrypted successfully.");
}
catch (Exception ex)
{
Console.WriteLine($"An error occurred: {ex.Message}");
}
break;
default:
Console.WriteLine("Invalid mode selection.");
break;
}
static void EncryptFile(string inputFile, string outputFile, string password, byte[] salt)
{
byte[] key;
using (var pbkdf2 = new Rfc2898DeriveBytes(password, salt, 10000)) // Use 10000 iterations
{
key = pbkdf2.GetBytes(32); // Generate a 256-bit key (32 bytes)
}
using (var aes = new AesManaged())
{
aes.Key = key;
aes.GenerateIV();
using (var inputStream = new FileStream(inputFile, FileMode.Open))
using (var outputStream = new FileStream(outputFile, FileMode.Create))
using (var cryptoStream = new CryptoStream(outputStream, aes.CreateEncryptor(), CryptoStreamMode.Write))
{
outputStream.Write(salt, 0, salt.Length); // Write salt to the output file
outputStream.Write(aes.IV, 0, aes.IV.Length); // Write IV to the output file
inputStream.CopyTo(cryptoStream);
}
}
}
static void DecryptFile(string inputFile, string outputFile, string password, byte[] salt)
{
byte[] iv = new byte[16];
using (var inputStream = new FileStream(inputFile, FileMode.Open))
{
// Skip reading the salt (as it's already passed as a parameter)
inputStream.Seek(salt.Length, SeekOrigin.Begin);
// Read the IV from the input file
inputStream.Read(iv, 0, iv.Length);
}
byte[] key;
using (var pbkdf2 = new Rfc2898DeriveBytes(password, salt, 10000)) // Use 10000 iterations
{
key = pbkdf2.GetBytes(32); // Generate a 256-bit key (32 bytes)
}
using (var aes = new AesManaged())
{
aes.Key = key;
aes.IV = iv;
using (var inputStream = new FileStream(inputFile, FileMode.Open))
using (var outputStream = new FileStream(outputFile, FileMode.Create))
using (var cryptoStream = new CryptoStream(inputStream, aes.CreateDecryptor(), CryptoStreamMode.Read))
{
// Skip the salt and IV bytes
inputStream.Seek(salt.Length + iv.Length, SeekOrigin.Begin);
// Decrypt the remaining data
cryptoStream.CopyTo(outputStream);
}
}
}
Conclusion: Implementing secure file encryption and decryption is essential for safeguarding sensitive data. By using strong encryption algorithms like AES and employing key derivation functions like PBKDF2, we can ensure that our encrypted files remain secure even if they fall into the wrong hands.
References:
278 views