using System.Security.Cryptography; using System.Text; using CareFix.Api.Options; using Microsoft.Extensions.Options; namespace CareFix.Api.Security; /// AES-256-GCM encryption for hospital SQL passwords. Layout: nonce(12) | tag(16) | ciphertext. public sealed class CredentialVault { private readonly byte[] _key; public CredentialVault(IOptions options) { try { _key = Convert.FromBase64String(options.Value.MasterKey); } catch (FormatException) { throw new InvalidOperationException("CareFix:MasterKey must be base64 (32 random bytes)."); } if (_key.Length != 32) throw new InvalidOperationException("CareFix:MasterKey must decode to exactly 32 bytes."); } public byte[] Encrypt(string plain) { var nonce = RandomNumberGenerator.GetBytes(12); var pt = Encoding.UTF8.GetBytes(plain); var ct = new byte[pt.Length]; var tag = new byte[16]; using var aes = new AesGcm(_key, 16); aes.Encrypt(nonce, pt, ct, tag); return [.. nonce, .. tag, .. ct]; } public string Decrypt(byte[] blob) { if (blob.Length < 28) throw new CryptographicException("Stored credential is corrupt."); var pt = new byte[blob.Length - 28]; using var aes = new AesGcm(_key, 16); aes.Decrypt(blob.AsSpan(0, 12), blob.AsSpan(28), blob.AsSpan(12, 16), pt); return Encoding.UTF8.GetString(pt); } }