using System; using System.Collections.Generic; using System.Linq; using System.Security.Cryptography; using System.Text; using System.Threading.Tasks; namespace GB5Shared.SSO { public static class SsoHelpers { public static string Base64UrlEncode(string input) { var bytes = Encoding.UTF8.GetBytes(input); var base64 = Convert.ToBase64String(bytes); return base64.Replace("+", "-").Replace("/", "_").TrimEnd('='); } public static string Base64UrlDecode(string input) { string base64 = input.Replace("-", "+").Replace("_", "/"); switch (base64.Length % 4) { case 2: base64 += "=="; break; case 3: base64 += "="; break; } var bytes = Convert.FromBase64String(base64); return Encoding.UTF8.GetString(bytes); } // AES-GCM encrypt: nonce(12) | ciphertext | tag(16) returned as base64 public static string EncryptAesGcm(string plaintext, byte[] key) { if (key == null || (key.Length != 16 && key.Length != 32)) throw new ArgumentException("Key must be 16 or 32 bytes"); var plaintextBytes = Encoding.UTF8.GetBytes(plaintext); var nonce = RandomNumberGenerator.GetBytes(12); var ciphertext = new byte[plaintextBytes.Length]; var tag = new byte[16]; using var aes = new AesGcm(key); aes.Encrypt(nonce, plaintextBytes, ciphertext, tag, null); var outBytes = new byte[nonce.Length + ciphertext.Length + tag.Length]; Buffer.BlockCopy(nonce, 0, outBytes, 0, nonce.Length); Buffer.BlockCopy(ciphertext, 0, outBytes, nonce.Length, ciphertext.Length); Buffer.BlockCopy(tag, 0, outBytes, nonce.Length + ciphertext.Length, tag.Length); return Convert.ToBase64String(outBytes); } public static string DecryptAesGcm(string base64Combined, byte[] key) { if (key == null || (key.Length != 16 && key.Length != 32)) throw new ArgumentException("Key must be 16 or 32 bytes"); var combined = Convert.FromBase64String(base64Combined); var nonce = new byte[12]; Buffer.BlockCopy(combined, 0, nonce, 0, 12); var cipherLen = combined.Length - 12 - 16; if (cipherLen < 0) throw new CryptographicException("Invalid payload length"); var ciphertext = new byte[cipherLen]; Buffer.BlockCopy(combined, 12, ciphertext, 0, cipherLen); var tag = new byte[16]; Buffer.BlockCopy(combined, 12 + cipherLen, tag, 0, 16); var plaintextBytes = new byte[cipherLen]; using var aes = new AesGcm(key); aes.Decrypt(nonce, ciphertext, tag, plaintextBytes, null); return Encoding.UTF8.GetString(plaintextBytes); } } }