Text Encryptor — C# source
Encrypt or decrypt text with a password using AES-256-GCM. Share the ciphertext safely - only someone with the password can read it.
This is the C# implementation — the same logic the interactive tool runs, in a shareable, citable form.
// Text Encryptor — password-based text encryption (AES-256-GCM + PBKDF2).
//
// Language: C# (.NET 8+, standard library only)
// Source: CosmoDev polyglot showcase port of the Text Encryptor tool, ported
// from src/lib/text-encryptor.ts (the canonical TypeScript
// implementation).
// License: display source — part of CosmoDev's polyglot tool pages.
//
// Format: PBKDF2 (SHA-256, 100k iterations, 16-byte random salt) derives an
// AES-256 key from the password; AES-GCM encrypts with a 12-byte random IV.
// The Base64 output packs salt + IV + ciphertext (+ GCM tag), so every
// encryption is unique and self-contained - decrypt needs only the string and
// the password.
using System;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
public static partial class TextEncryptor
{
private const int Pbkdf2Iterations = 100_000;
private const int SaltBytes = 16;
private const int IvBytes = 12;
/// <summary>Smallest valid packed payload: salt + IV + one AES-GCM block (tag).</summary>
private const int MinBytes = SaltBytes + IvBytes + 16;
private static readonly Regex Base64Shape =
new(@"^[A-Za-z0-9+/]+={0,2}$", RegexOptions.Compiled);
/// <summary>
/// Base64 → bytes. Accepts surrounding whitespace; throws on any other
/// non-alphabet character or a wrong length.
/// </summary>
private static byte[] Base64ToBytes(string base64)
{
var clean = Regex.Replace(base64, @"\s+", "");
if (clean.Length == 0)
{
throw new FormatException("Invalid Base64: input is empty");
}
if (clean.Length % 4 != 0)
{
throw new FormatException("Invalid Base64: length must be a multiple of 4");
}
if (!Base64Shape.IsMatch(clean))
{
throw new FormatException("Invalid Base64: unexpected character");
}
return Convert.FromBase64String(clean);
}
/// <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, Pbkdf2Iterations, HashAlgorithmName.SHA256, 32);
/// <summary>
/// Encrypt UTF-8 <paramref name="plaintext"/> under
/// <paramref name="password"/> and return the Base64 string packing
/// salt + IV + AES-256-GCM ciphertext. Throws if either input is empty.
/// </summary>
public static string EncryptText(string plaintext, string password)
{
if (plaintext == string.Empty) throw new ArgumentException("Nothing to encrypt: input is empty");
if (password == string.Empty) throw new ArgumentException("Password must not be empty");
var salt = new byte[SaltBytes];
var iv = new byte[IvBytes];
RandomNumberGenerator.Fill(salt);
RandomNumberGenerator.Fill(iv);
var plain = Encoding.UTF8.GetBytes(plaintext);
var key = DeriveKey(password, salt);
var ciphertext = new byte[plain.Length + 16];
try
{
using var aes = new AesGcm(key, 16);
aes.Encrypt(iv, plain, ciphertext[..plain.Length], ciphertext[plain.Length..]);
}
finally
{
CryptographicOperations.ZeroMemory(key);
}
var packed = new byte[SaltBytes + IvBytes + ciphertext.Length];
Buffer.BlockCopy(salt, 0, packed, 0, SaltBytes);
Buffer.BlockCopy(iv, 0, packed, SaltBytes, IvBytes);
Buffer.BlockCopy(ciphertext, 0, packed, SaltBytes + IvBytes, ciphertext.Length);
return Convert.ToBase64String(packed);
}
/// <summary>
/// Decrypt a Base64 string produced by <see cref="EncryptText"/> back to
/// plaintext. Throws on empty input/password, invalid Base64, a payload
/// too short to be one of ours, or a GCM authentication failure (wrong
/// password / tampered data).
/// </summary>
public static string DecryptText(string ciphertext, string password)
{
if (string.IsNullOrWhiteSpace(ciphertext)) throw new ArgumentException("Nothing to decrypt: input is empty");
if (password == string.Empty) throw new ArgumentException("Password must not be empty");
var packed = Base64ToBytes(ciphertext.Trim());
if (packed.Length < MinBytes)
{
throw new FormatException("Ciphertext too short - not a valid salt + IV + AES-GCM payload");
}
var salt = packed[..SaltBytes];
var iv = packed[SaltBytes..(SaltBytes + IvBytes)];
var data = packed[(SaltBytes + IvBytes)..];
var plaintext = new byte[data.Length - 16];
var key = DeriveKey(password, salt);
try
{
using var aes = new AesGcm(key, 16);
aes.Decrypt(iv, data[..plaintext.Length], data[plaintext.Length..], plaintext);
}
catch (CryptographicException)
{
throw new CryptographicException("Decryption failed - wrong password or corrupted ciphertext");
}
finally
{
CryptographicOperations.ZeroMemory(key);
}
return Encoding.UTF8.GetString(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 →