Last active
March 29, 2026 13:03
-
-
Save santisq/a68d958641f654b5e40549e931cdce8c to your computer and use it in GitHub Desktop.
AES encrypt / decrypt strings
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| using System; | |
| using System.IO; | |
| using System.Management.Automation; | |
| using System.Security.Cryptography; | |
| using System.Text; | |
| [Cmdlet(VerbsDiagnostic.Test, "Encrypt")] | |
| public sealed class EncryptAes : PSCmdlet | |
| { | |
| [Parameter(Mandatory = true, Position = 0)] | |
| public string Content { get; set; } = null!; | |
| [Parameter(Mandatory = true, Position = 1)] | |
| public byte[] Secret { get; set; } = null!; | |
| protected override void EndProcessing() | |
| { | |
| using SymmetricAlgorithm algo = Aes.Create(); | |
| using MemoryStream mem = new(); | |
| using (CryptoStream crypto = new( | |
| stream: mem, | |
| transform: algo.CreateEncryptor(Secret, algo.IV), | |
| mode: CryptoStreamMode.Write)) | |
| { | |
| crypto.Write(algo.IV); | |
| crypto.Write(Encoding.UTF8.GetBytes(Content)); | |
| } | |
| WriteObject(Convert.ToBase64String(mem.ToArray())); | |
| } | |
| } | |
| [Cmdlet(VerbsDiagnostic.Test, "Decrypt")] | |
| public sealed class DecryptAes : PSCmdlet | |
| { | |
| [Parameter(Mandatory = true, Position = 0)] | |
| public string Content { get; set; } = null!; | |
| [Parameter(Mandatory = true, Position = 1)] | |
| public byte[] Secret { get; set; } = null!; | |
| protected override void EndProcessing() | |
| { | |
| ReadOnlySpan<byte> span = Convert.FromBase64String(Content); | |
| using Aes aes = Aes.Create(); | |
| using MemoryStream mem = new(); | |
| using (CryptoStream cryptoStream = new( | |
| stream: mem, | |
| transform: aes.CreateDecryptor(Secret, [.. span[..16]]), | |
| mode: CryptoStreamMode.Write)) | |
| { | |
| cryptoStream.Write([.. span[16..]], 0, span.Length - 16); | |
| } | |
| WriteObject(Encoding.UTF8.GetString(mem.ToArray())); | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| using System; | |
| using System.Buffers; | |
| using System.Security.Cryptography; | |
| using System.Text; | |
| const int NonceSize = 12; // AesGcm.NonceByteSizes.MaxSize; | |
| const int TagSize = 16; // AesGcm.TagByteSizes.MaxSize; | |
| const int NonceTagOffset = NonceSize + TagSize; | |
| string? key = args[2]; | |
| ArgumentException.ThrowIfNullOrWhiteSpace(key, nameof(key)); | |
| ReadOnlySpan<byte> keySpan = Convert.FromBase64String(key); | |
| switch (args[0]) | |
| { | |
| case "-e": | |
| Console.WriteLine(Encrypt(args[1], keySpan)); | |
| break; | |
| case "-d": | |
| Console.WriteLine(Decrypt(args[1], keySpan)); | |
| break; | |
| default: | |
| Console.Error.WriteLine($""" | |
| Unknown parameter: {args[0]} | |
| Usage: | |
| {AppDomain.CurrentDomain.FriendlyName} -e <plainText> <b64key> | |
| {AppDomain.CurrentDomain.FriendlyName} -d <cipherText> <b64key> | |
| Options: | |
| -e Encrypt the given plain text using the base64-encoded key | |
| -d Decrypt the given cipher text using the base64-encoded key | |
| Examples: | |
| {AppDomain.CurrentDomain.FriendlyName} -e "Hello World" "your-base64-key-here" | |
| {AppDomain.CurrentDomain.FriendlyName} -d "encrypted-text-here" "your-base64-key-here" | |
| """); | |
| break; | |
| } | |
| static string Encrypt(string? plainText, ReadOnlySpan<byte> key) | |
| { | |
| ArgumentException.ThrowIfNullOrWhiteSpace(plainText, nameof(plainText)); | |
| byte[] cipherPool = ArrayPool<byte>.Shared.Rent(512); | |
| byte[] resultPool = ArrayPool<byte>.Shared.Rent(512); | |
| try | |
| { | |
| ReadOnlySpan<byte> plain = Encoding.UTF8.GetBytes(plainText); | |
| using AesGcm aes = new(key, TagSize); | |
| Span<byte> nonce = stackalloc byte[NonceSize]; | |
| Span<byte> tag = stackalloc byte[TagSize]; | |
| RandomNumberGenerator.Fill(nonce); | |
| Span<byte> cipher = cipherPool.AsSpan(0, plain.Length); | |
| aes.Encrypt(nonce, plain, cipher, tag); | |
| return Convert.ToBase64String( | |
| resultPool | |
| .AsSpan(0, NonceTagOffset + cipher.Length) | |
| .Concat(nonce, cipher, tag)); | |
| } | |
| finally | |
| { | |
| ArrayPool<byte>.Shared.Return(cipherPool); | |
| ArrayPool<byte>.Shared.Return(resultPool); | |
| } | |
| } | |
| static string Decrypt(string? encryptedText, ReadOnlySpan<byte> key) | |
| { | |
| ArgumentException.ThrowIfNullOrWhiteSpace(encryptedText, nameof(encryptedText)); | |
| byte[] resultPool = ArrayPool<byte>.Shared.Rent(512); | |
| try | |
| { | |
| using AesGcm aes = new(key, TagSize); | |
| ReadOnlySpan<byte> encrypted = Convert.FromBase64String(encryptedText); | |
| ReadOnlySpan<byte> cipher = encrypted[NonceSize..^TagSize]; | |
| Span<byte> result = resultPool.AsSpan(0, cipher.Length); | |
| aes.Decrypt(encrypted[..NonceSize], cipher, encrypted[^TagSize..], result); | |
| return Encoding.UTF8.GetString(result); | |
| } | |
| finally | |
| { | |
| ArrayPool<byte>.Shared.Return(resultPool); | |
| } | |
| } | |
| internal static class Extensions | |
| { | |
| extension<T>(Span<T> source) | |
| { | |
| internal ReadOnlySpan<T> Concat( | |
| ReadOnlySpan<T> s1, | |
| ReadOnlySpan<T> s2, | |
| ReadOnlySpan<T> s3) | |
| { | |
| s1.CopyTo(source); | |
| int offset = s1.Length; | |
| s2.CopyTo(source[offset..]); | |
| offset += s2.Length; | |
| s3.CopyTo(source[offset..]); | |
| return source; | |
| } | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| using System; | |
| using System.Buffers; | |
| using System.Management.Automation; | |
| using System.Security.Cryptography; | |
| using System.Text; | |
| [Cmdlet(VerbsDiagnostic.Test, "Encrypt")] | |
| public sealed class EncryptAes : PSCmdlet | |
| { | |
| [Parameter(Mandatory = true, Position = 0)] | |
| public string Content { get; set; } = null!; | |
| [Parameter(Mandatory = true, Position = 1)] | |
| public byte[] Key { get; set; } = null!; | |
| protected override void EndProcessing() | |
| { | |
| ReadOnlySpan<byte> nonce = RandomNumberGenerator.GetBytes(AesGcm.NonceByteSizes.MaxSize); | |
| Span<byte> tag = stackalloc byte[AesCcm.TagByteSizes.MaxSize]; | |
| ReadOnlySpan<byte> plain = Encoding.UTF8.GetBytes(Content); | |
| Span<byte> cipher = stackalloc byte[plain.Length]; | |
| using AesGcm aes = new(Key, AesCcm.TagByteSizes.MaxSize); | |
| aes.Encrypt(nonce, plain, cipher, tag); | |
| ReadOnlySpan<byte> result = [..nonce, ..cipher, ..tag]; | |
| WriteObject(Convert.ToBase64String(result)); | |
| } | |
| } | |
| [Cmdlet(VerbsDiagnostic.Test, "Decrypt")] | |
| public sealed class DecryptAes : PSCmdlet | |
| { | |
| [Parameter(Mandatory = true, Position = 0)] | |
| public string Content { get; set; } = null!; | |
| [Parameter(Mandatory = true, Position = 1)] | |
| public byte[] Key { get; set; } = null!; | |
| protected override void EndProcessing() | |
| { | |
| int nonceSize = AesGcm.NonceByteSizes.MaxSize; | |
| int tagSize = AesGcm.TagByteSizes.MaxSize; | |
| ReadOnlySpan<byte> encrypted = Convert.FromBase64String(Content); | |
| ReadOnlySpan<byte> cipher = encrypted[nonceSize..^tagSize]; | |
| Span<byte> result = stackalloc byte[cipher.Length]; | |
| using AesGcm aes = new(Key, tagSize); | |
| aes.Decrypt( | |
| nonce: encrypted[..nonceSize], | |
| ciphertext: cipher, | |
| tag: encrypted[^tagSize..], | |
| plaintext: result); | |
| WriteObject(Encoding.UTF8.GetString(result)); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment