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

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();
}