AES Encryption Decryption Tool in .NET 8 (Console App)

Here is the code! copy it and paste it inside a file (say AESFileEncryptDecrypt.csx
) and then run it with dotnet-script AESFileEncryptDecrypt.csx
.
I am assuming that you already have dotnet-script
tool installed globally in your computer as well as .NET 8
SDK.
In case you don't, you can install dotnet-script
tool globally by running dotnet tool install -g dotnet-script
.
using System;
using System.IO;
using System.Security.Cryptography;
using System.Linq;
using System.IO.Compression;
// Define our sober retro color scheme.
ConsoleColor bgColor = ConsoleColor.Black;
ConsoleColor fgColor = ConsoleColor.Gray;
ConsoleColor headerBorderColor = ConsoleColor.DarkGreen; // For header borders.
ConsoleColor headerTitleColor = ConsoleColor.DarkGreen; // For header title.
ConsoleColor menuSelectedBG = ConsoleColor.Yellow; // Selected menu background.
ConsoleColor menuSelectedFG = ConsoleColor.Black; // Selected menu text.
ConsoleColor menuUnselectedFG = fgColor; // Unselected menu text.
ConsoleColor instructionColor = ConsoleColor.DarkGray; // Instructions.
ConsoleColor successColor = ConsoleColor.Green; // Success messages.
ConsoleColor errorColor = ConsoleColor.Red; // Error messages.
ConsoleColor accentColorS = ConsoleColor.Yellow; // For "S" key.
ConsoleColor accentColorO = ConsoleColor.Red; // For "O" key.
// Set the console's background and clear.
Console.BackgroundColor = bgColor;
Console.Clear();
// Helper method to write centered text.
void WriteCentered(string text, ConsoleColor? color = null)
{
int windowWidth = Console.WindowWidth;
string[] lines = text.Split('\n');
foreach (var line in lines)
{
int padding = Math.Max((windowWidth - line.Length) / 2, 0);
Console.ForegroundColor = color ?? fgColor;
Console.WriteLine(new string(' ', padding) + line);
Console.ResetColor();
Console.BackgroundColor = bgColor;
}
}
/// <summary>
/// Renders a header box using box–drawing characters with fixed width.
/// The borders are white and the title is yellow.
/// </summary>
void RenderHeader()
{
int boxWidth = 40; // Interior width.
string boxTitle = "AES Encrypt/Decrypt Tool";
int paddingLeft = (boxWidth - boxTitle.Length) / 2;
int paddingRight = boxWidth - boxTitle.Length - paddingLeft;
string headerTop = "╔" + new string('═', boxWidth) + "╗";
string headerMiddle = "║" + new string(' ', paddingLeft) + boxTitle + new string(' ', paddingRight) + "║";
string headerBottom = "╚" + new string('═', boxWidth) + "╝";
WriteCentered(headerTop, headerBorderColor);
WriteCentered(headerMiddle, headerTitleColor);
WriteCentered(headerBottom, headerBorderColor);
Console.WriteLine();
}
/// <summary>
/// Displays a menu with the given title and options. The menu is centered on the console,
/// with each option left–aligned. The current selection is highlighted (yellow background, black text).
/// Navigation is via arrow keys (with scrolling), Enter selects, and ESC cancels.
/// Returns the index of the selected option or -1 if canceled.
/// </summary>
int DisplayMenu(string title, string[] options)
{
int selectedIndex = 0;
ConsoleKeyInfo keyInfo;
while (true)
{
Console.Clear();
Console.WriteLine();
RenderHeader();
WriteCentered(title, fgColor);
Console.WriteLine();
// Build option strings with prefix.
string[] optionLines = new string[options.Length];
for (int i = 0; i < options.Length; i++)
{
string prefix = i == selectedIndex ? " > " : " ";
optionLines[i] = prefix + options[i];
}
int containerWidth = optionLines.Max(line => line.Length);
int leftMargin = Math.Max((Console.WindowWidth - containerWidth) / 2, 0);
// Reserve lines for header/instructions.
int headerLines = 8;
int instructionLines = 2;
int maxVisible = Math.Max(Console.WindowHeight - headerLines - instructionLines, 1);
int startIndex = 0;
if (options.Length > maxVisible)
{
startIndex = selectedIndex - maxVisible / 2;
if (startIndex < 0)
startIndex = 0;
if (startIndex + maxVisible > options.Length)
startIndex = options.Length - maxVisible;
}
// Render only visible options.
for (int i = startIndex; i < startIndex + Math.Min(maxVisible, options.Length - startIndex); i++)
{
string line = optionLines[i].PadRight(containerWidth);
Console.SetCursorPosition(leftMargin, Console.CursorTop);
if (i == selectedIndex)
{
Console.BackgroundColor = menuSelectedBG;
Console.ForegroundColor = menuSelectedFG;
Console.WriteLine(line);
Console.ResetColor();
Console.BackgroundColor = bgColor;
}
else
{
Console.ForegroundColor = menuUnselectedFG;
Console.WriteLine(line);
Console.ResetColor();
Console.BackgroundColor = bgColor;
}
}
WriteCentered("\nUse ↑/↓ to navigate, Enter to select, ESC to cancel.", instructionColor);
keyInfo = Console.ReadKey(true);
if (keyInfo.Key == ConsoleKey.UpArrow)
selectedIndex = (selectedIndex == 0) ? options.Length - 1 : selectedIndex - 1;
else if (keyInfo.Key == ConsoleKey.DownArrow)
selectedIndex = (selectedIndex == options.Length - 1) ? 0 : selectedIndex + 1;
else if (keyInfo.Key == ConsoleKey.Enter)
return selectedIndex;
else if (keyInfo.Key == ConsoleKey.Escape)
return -1;
}
}
/// <summary>
/// Navigates the file system from a starting directory, optionally filtering files by extension.
/// Returns the full path of the selected file or null if canceled.
/// </summary>
string NavigateFileSystem(string startPath, string fileExtensionFilter = null)
{
string currentPath = startPath;
while (true)
{
string[] directories, files;
try
{
directories = Directory.GetDirectories(currentPath);
files = Directory.GetFiles(currentPath);
if (!string.IsNullOrEmpty(fileExtensionFilter))
files = files.Where(f => f.EndsWith(fileExtensionFilter, StringComparison.OrdinalIgnoreCase)).ToArray();
}
catch (Exception ex)
{
Console.WriteLine("Error accessing directory: " + ex.Message);
return null;
}
directories = directories.OrderBy(d => d).ToArray();
files = files.OrderBy(f => f).ToArray();
var optionsList = new System.Collections.Generic.List<string>();
DirectoryInfo parentDir = Directory.GetParent(currentPath);
if (parentDir != null)
optionsList.Add("[..] Go up one level");
foreach (var dir in directories)
optionsList.Add("[DIR] " + Path.GetFileName(dir));
foreach (var file in files)
optionsList.Add(Path.GetFileName(file));
int selected = DisplayMenu("Current Path: " + currentPath, optionsList.ToArray());
if (selected == -1)
return null;
int index = selected;
if (parentDir != null)
{
if (index == 0)
{
currentPath = parentDir.FullName;
continue;
}
else index--;
}
if (index < directories.Length)
{
currentPath = directories[index];
continue;
}
else
{
int fileIndex = index - directories.Length;
if (fileIndex < files.Length)
return files[fileIndex];
}
}
}
/// <summary>
/// Navigates directories for folder encryption (lists only directories).
/// When a folder is highlighted, prompts for confirmation to either select it for encryption or open it.
/// Returns the full path of the selected folder or null if canceled.
/// </summary>
string NavigateFolderForEncryption(string startPath)
{
string currentPath = startPath;
while (true)
{
string[] directories;
try
{
directories = Directory.GetDirectories(currentPath);
}
catch (Exception ex)
{
Console.WriteLine("Error accessing directory: " + ex.Message);
return null;
}
directories = directories.OrderBy(d => d).ToArray();
var optionsList = new System.Collections.Generic.List<string>();
DirectoryInfo parentDir = Directory.GetParent(currentPath);
if (parentDir != null)
optionsList.Add("[..] Go up one level");
foreach (var dir in directories)
optionsList.Add("[DIR] " + Path.GetFileName(dir));
int selected = DisplayMenu("Current Folder: " + currentPath, optionsList.ToArray());
if (selected == -1)
return null;
int index = selected;
if (parentDir != null)
{
if (index == 0)
{
currentPath = parentDir.FullName;
continue;
}
else index--;
}
string chosenFolder = directories[index];
// Confirmation prompt with highlighted keys.
Console.Clear();
WriteCentered($"Selected folder: {Path.GetFileName(chosenFolder)}", headerTitleColor);
Console.WriteLine();
Console.Write("Press ");
Console.ForegroundColor = accentColorS;
Console.Write("S");
Console.ResetColor();
Console.BackgroundColor = bgColor;
Console.Write(" to select this folder for encryption, or ");
Console.ForegroundColor = accentColorO;
Console.Write("O");
Console.ResetColor();
Console.BackgroundColor = bgColor;
Console.WriteLine(" to open it.");
var key = Console.ReadKey(true);
Console.ResetColor();
Console.BackgroundColor = bgColor;
if (key.Key == ConsoleKey.S)
return chosenFolder;
else if (key.Key == ConsoleKey.O)
{
currentPath = chosenFolder;
continue;
}
else if (key.Key == ConsoleKey.Escape)
return null;
}
}
/// <summary>
/// Encrypts the input file and writes the encrypted data (with the IV prepended) to the output file.
/// </summary>
void EncryptFile(string inputFile, string outputFile, byte[] key)
{
using Aes aes = Aes.Create();
aes.Key = key;
aes.GenerateIV();
byte[] iv = aes.IV;
using FileStream fsOutput = new FileStream(outputFile, FileMode.Create, FileAccess.Write);
fsOutput.Write(iv, 0, iv.Length);
using CryptoStream cryptoStream = new CryptoStream(fsOutput, aes.CreateEncryptor(), CryptoStreamMode.Write);
using FileStream fsInput = new FileStream(inputFile, FileMode.Open, FileAccess.Read);
fsInput.CopyTo(cryptoStream);
}
/// <summary>
/// Decrypts the input file (which must have the IV as its first bytes) and writes the decrypted data to the output file.
/// </summary>
void DecryptFile(string inputFile, string outputFile, byte[] key)
{
using FileStream fsInput = new FileStream(inputFile, FileMode.Open, FileAccess.Read);
using Aes aes = Aes.Create();
aes.Key = key;
byte[] iv = new byte[aes.BlockSize / 8];
int bytesRead = fsInput.Read(iv, 0, iv.Length);
if (bytesRead < iv.Length)
throw new Exception("File is corrupted or not encrypted properly.");
aes.IV = iv;
using CryptoStream cryptoStream = new CryptoStream(fsInput, aes.CreateDecryptor(), CryptoStreamMode.Read);
using FileStream fsOutput = new FileStream(outputFile, FileMode.Create, FileAccess.Write);
cryptoStream.CopyTo(fsOutput);
}
/// <summary>
/// Zips the specified folder into a zip file.
/// </summary>
void ZipFolder(string folderPath, string zipPath)
{
ZipFile.CreateFromDirectory(folderPath, zipPath);
}
// Main program loop.
while (true)
{
// Main menu options: 0: Encrypt a file, 1: Encrypt a folder, 2: Decrypt a file, 3: Exit.
string[] mainMenuOptions = { "Encrypt a file", "Encrypt a folder", "Decrypt a file", "Exit" };
int mainSelection = DisplayMenu("Select an operation:", mainMenuOptions);
if (mainSelection == -1 || mainSelection == 3)
{
Console.Clear();
WriteCentered("Exiting the program. Goodbye!", headerBorderColor);
break;
}
// Hard-coded 32-byte key for AES-256.
byte[] key = new byte[32]
{
1,2,3,4,5,6,7,8,
9,10,11,12,13,14,15,16,
17,18,19,20,21,22,23,24,
25,26,27,28,29,30,31,32
};
if (mainSelection == 0) // Encrypt a file.
{
string startDirectory = Directory.GetCurrentDirectory();
string selectedFile = NavigateFileSystem(startDirectory);
if (selectedFile == null)
continue;
// Default output: original filename + ".aes"
string outputFilePath = Path.Combine(Path.GetDirectoryName(selectedFile), Path.GetFileName(selectedFile) + ".aes");
try
{
EncryptFile(selectedFile, outputFilePath, key);
Console.Clear();
WriteCentered("Operation Completed", successColor);
Console.Write("\nFile '");
Console.ForegroundColor = accentColorS;
Console.Write(Path.GetFileName(selectedFile));
Console.ResetColor();
Console.Write("' encrypted successfully to '");
Console.ForegroundColor = accentColorS;
Console.Write(Path.GetFileName(outputFilePath));
Console.ResetColor();
Console.WriteLine("'.");
}
catch (Exception ex)
{
Console.Clear();
WriteCentered("An error occurred:", errorColor);
WriteCentered(ex.Message, errorColor);
}
}
else if (mainSelection == 1) // Encrypt a folder.
{
string startDirectory = Directory.GetCurrentDirectory();
string selectedFolder = NavigateFolderForEncryption(startDirectory);
if (selectedFolder == null)
continue;
// Default output: folder name + ".zip.aes"
string outputFilePath = Path.Combine(Path.GetDirectoryName(selectedFolder), Path.GetFileName(selectedFolder) + ".zip.aes");
// Create a temporary zip.
string tempZipPath = Path.Combine(Path.GetDirectoryName(selectedFolder),
Path.GetFileName(selectedFolder) + "_" + Guid.NewGuid().ToString("N") + ".zip");
try
{
ZipFolder(selectedFolder, tempZipPath);
EncryptFile(tempZipPath, outputFilePath, key);
File.Delete(tempZipPath);
Console.Clear();
WriteCentered("Operation Completed", successColor);
Console.Write("\nFolder '");
Console.ForegroundColor = accentColorS;
Console.Write(Path.GetFileName(selectedFolder));
Console.ResetColor();
Console.Write("' zipped and encrypted successfully to '");
Console.ForegroundColor = accentColorS;
Console.Write(Path.GetFileName(outputFilePath));
Console.ResetColor();
Console.WriteLine("'.");
}
catch (Exception ex)
{
Console.Clear();
WriteCentered("An error occurred:", errorColor);
WriteCentered(ex.Message, errorColor);
}
}
else if (mainSelection == 2) // Decrypt a file.
{
string startDirectory = Directory.GetCurrentDirectory();
// Only show .aes files.
string selectedFile = NavigateFileSystem(startDirectory, ".aes");
if (selectedFile == null)
continue;
// Default output: remove the ".aes" extension.
string outputFilePath = Path.Combine(Path.GetDirectoryName(selectedFile), Path.GetFileNameWithoutExtension(selectedFile));
try
{
DecryptFile(selectedFile, outputFilePath, key);
Console.Clear();
WriteCentered("Operation Completed", successColor);
Console.Write("\nFile '");
Console.ForegroundColor = accentColorS;
Console.Write(Path.GetFileName(selectedFile));
Console.ResetColor();
Console.Write("' decrypted successfully to '");
Console.ForegroundColor = accentColorS;
Console.Write(Path.GetFileName(outputFilePath));
Console.ResetColor();
Console.WriteLine("'.");
}
catch (Exception ex)
{
Console.Clear();
WriteCentered("An error occurred:", errorColor);
WriteCentered(ex.Message, errorColor);
}
}
WriteCentered("\nPress any key to return to the main menu...", instructionColor);
Console.ReadKey();
}