using System.Security.Cryptography; using System.Text; using GestorFacturas.API.Services.Interfaces; namespace GestorFacturas.API.Services; public class EncryptionService : IEncryptionService { private readonly string _key; public EncryptionService(IConfiguration config) { // La clave debe venir del .env / appsettings _key = config["EncryptionKey"] ?? throw new ArgumentNullException("EncryptionKey no configurada"); // Ajustar si la clave no tiene el tamaño correcto (AES-256 requiere 32 bytes) // Aquí hacemos un hash SHA256 de la clave para asegurar que siempre tenga 32 bytes válidos using var sha256 = SHA256.Create(); var keyBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(_key)); _key = Convert.ToBase64String(keyBytes); } public string Encrypt(string plainText) { if (string.IsNullOrEmpty(plainText)) return plainText; var key = Convert.FromBase64String(_key); using var aes = Aes.Create(); aes.Key = key; aes.GenerateIV(); // Generar vector de inicialización aleatorio using var encryptor = aes.CreateEncryptor(aes.Key, aes.IV); using var ms = new MemoryStream(); // Escribir el IV al principio del stream (necesario para desencriptar) ms.Write(aes.IV, 0, aes.IV.Length); using (var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write)) using (var sw = new StreamWriter(cs)) { sw.Write(plainText); } return Convert.ToBase64String(ms.ToArray()); } public string Decrypt(string cipherText) { if (string.IsNullOrEmpty(cipherText)) return cipherText; try { var fullCipher = Convert.FromBase64String(cipherText); var key = Convert.FromBase64String(_key); using var aes = Aes.Create(); aes.Key = key; // Extraer el IV (los primeros 16 bytes) var iv = new byte[16]; Array.Copy(fullCipher, 0, iv, 0, iv.Length); aes.IV = iv; using var decryptor = aes.CreateDecryptor(aes.Key, aes.IV); using var ms = new MemoryStream(fullCipher, 16, fullCipher.Length - 16); using var cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read); using var sr = new StreamReader(cs); return sr.ReadToEnd(); } catch { // Si falla al desencriptar (ej. porque el dato viejo no estaba encriptado), // devolvemos el texto original para no romper la app en la migración. return cipherText; } } }