Skip to content

File Encryptor — C# source

Encrypt or decrypt any file with AES-256-GCM in your browser. Password-based, zero server contact. Drag, drop, done.

This is the C# implementation — the same logic the interactive tool runs, in a shareable, citable form.

// File Encryptor — password-based AES-256-GCM file encryption.
//
// Language: C# (.NET 8+, standard library only)
// Source:   CosmoDev polyglot showcase port of the File Encryptor tool, ported
//           from src/lib/file-encryptor.ts (the canonical TypeScript
//           implementation).
// License:  display source — part of CosmoDev's polyglot tool pages.
//
// Wire format:  salt (16 B) || IV (12 B) || AES-256-GCM ciphertext + tag.
// The 256-bit key is derived from the password with PBKDF2-SHA256 and a fresh
// random salt per encryption, so the same file + password never encrypts to
// the same bytes, and the password itself is never stored or derivable from
// the output.

using System;
using System.Security.Cryptography;
using System.Text;

public static class FileEncryptor
{
    public const int SaltLength = 16;
    public const int IvLength = 12;
    public const int Iterations = 100_000;

    // GCM appends a 16-byte auth tag to the ciphertext; the smallest possible
    // encrypted payload is therefore salt + IV + tag = 44 bytes.
    private const int TagLength = 16;
    private const int MinLength = SaltLength + IvLength + TagLength;

    /// <summary>PBKDF2-SHA256 (100k iterations) → a 256-bit AES key.</summary>
    private static byte[] DeriveKey(string password, byte[] salt) =>
        Rfc2898DeriveBytes.Pbkdf2(
            Encoding.UTF8.GetBytes(password), salt, Iterations, HashAlgorithmName.SHA256, 32);

    /// <summary>Encrypt <paramref name="data"/> under <paramref name="password"/>.
    /// Returns salt || IV || ciphertext+tag.</summary>
    public static byte[] EncryptFile(byte[] data, string password)
    {
        if (string.IsNullOrEmpty(password))
        {
            throw new ArgumentException("Password must not be empty.");
        }
        if (data.Length == 0)
        {
            throw new ArgumentException("Input data is empty - nothing to encrypt.");
        }

        var salt = new byte[SaltLength];
        var iv = new byte[IvLength];
        RandomNumberGenerator.Fill(salt);
        RandomNumberGenerator.Fill(iv);

        var key = DeriveKey(password, salt);
        var ciphertext = new byte[data.Length + TagLength];
        try
        {
            using var aes = new AesGcm(key, TagLength);
            aes.Encrypt(iv, data, ciphertext[..data.Length], ciphertext[data.Length..]);
        }
        finally
        {
            CryptographicOperations.ZeroMemory(key);
        }

        var output = new byte[SaltLength + IvLength + ciphertext.Length];
        Buffer.BlockCopy(salt, 0, output, 0, SaltLength);
        Buffer.BlockCopy(iv, 0, output, SaltLength, IvLength);
        Buffer.BlockCopy(ciphertext, 0, output, SaltLength + IvLength, ciphertext.Length);
        return output;
    }

    /// <summary>
    /// Decrypt a payload produced by <see cref="EncryptFile"/>. Throws when the
    /// password is wrong or the payload was corrupted/tampered (GCM auth-tag
    /// failure).
    /// </summary>
    public static byte[] DecryptFile(byte[] data, string password)
    {
        if (string.IsNullOrEmpty(password))
        {
            throw new ArgumentException("Password must not be empty.");
        }
        if (data.Length < MinLength)
        {
            throw new ArgumentException(
                $"Input is too short to be an encrypted file (needs at least {MinLength} bytes: salt + IV + auth tag).");
        }

        var salt = data[..SaltLength];
        var iv = data[SaltLength..(SaltLength + IvLength)];
        var ciphertext = data[(SaltLength + IvLength)..];
        var plaintext = new byte[ciphertext.Length - TagLength];

        var key = DeriveKey(password, salt);
        try
        {
            using var aes = new AesGcm(key, TagLength);
            // A GCM auth-tag failure means the key did not match (wrong
            // password) or the payload was modified after encryption.
            aes.Decrypt(iv, ciphertext[..plaintext.Length], ciphertext[plaintext.Length..], plaintext);
        }
        catch (CryptographicException)
        {
            throw new CryptographicException("Decryption failed: wrong password or corrupted file.");
        }
        finally
        {
            CryptographicOperations.ZeroMemory(key);
        }
        return plaintext;
    }
}

Also available in 8 other languages

Every CosmoDev tool ships its pure logic in TypeScript (web) and Go (CLI), with authored implementations in a dozen-plus languages — the same contract, ported. Compare all languages side by side →