using System.Security.Cryptography; using System.Text; using System.Runtime.InteropServices; namespace CredentialManager.Services; /// /// Interfaccia per il servizio di crittografia /// public interface IEncryptionService { string Encrypt(string plainText); string Decrypt(string encryptedText); bool CanDecrypt(string encryptedText); string MigrateEncryptedText(string oldEncryptedText, string newPlainText); } /// /// Servizio per la crittografia delle password cross-platform con supporto per migrazione /// public class EncryptionService : IEncryptionService { private readonly byte[] _key; private readonly byte[] _iv; private const string ENTROPY_STRING = "CredentialManager2025"; private const string AES_PREFIX = "AES:"; private const string PROTECTED_PREFIX = "PROTECTED:"; public EncryptionService() { // Chiave e IV derivati da una stringa fissa var keySource = "CredentialManager2025KeyForEncryption!"; var ivSource = "CredMgr2025IV!"; using var sha256 = SHA256.Create(); _key = sha256.ComputeHash(Encoding.UTF8.GetBytes(keySource)); _iv = new byte[16]; Array.Copy(Encoding.UTF8.GetBytes(ivSource), _iv, Math.Min(16, ivSource.Length)); } public string Encrypt(string plainText) { if (string.IsNullOrEmpty(plainText)) return string.Empty; try { // Sempre usa AES per nuove crittografie per garantire portabilità return AES_PREFIX + EncryptWithAes(plainText); } catch (Exception ex) { throw new InvalidOperationException("Errore durante la crittografia", ex); } } public string Decrypt(string encryptedText) { if (string.IsNullOrEmpty(encryptedText)) return string.Empty; try { // Determina il metodo di crittografia utilizzato if (encryptedText.StartsWith(AES_PREFIX)) { // Rimuovi il prefisso e decrittografa con AES var aesEncryptedText = encryptedText.Substring(AES_PREFIX.Length); return DecryptWithAes(aesEncryptedText); } else if (encryptedText.StartsWith(PROTECTED_PREFIX)) { // Rimuovi il prefisso e decrittografa con ProtectedData var protectedEncryptedText = encryptedText.Substring(PROTECTED_PREFIX.Length); return DecryptWithProtectedData(protectedEncryptedText); } else { // Formato legacy - prova prima ProtectedData (se su Windows), poi AES return DecryptLegacyFormat(encryptedText); } } catch (Exception ex) { throw new InvalidOperationException("Errore durante la decrittografia", ex); } } public bool CanDecrypt(string encryptedText) { if (string.IsNullOrEmpty(encryptedText)) return true; try { Decrypt(encryptedText); return true; } catch { return false; } } public string MigrateEncryptedText(string oldEncryptedText, string newPlainText) { // Cripta il nuovo testo in chiaro con il metodo attuale (AES) return Encrypt(newPlainText); } private string DecryptLegacyFormat(string encryptedText) { // Su Windows, prova prima ProtectedData if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { try { return DecryptWithProtectedData(encryptedText); } catch (CryptographicException ex) when (ex.Message.Contains("Chiave non utilizzabile") || ex.Message.Contains("Key not valid for use in specified state") || ex.Message.Contains("The data is invalid")) { // ProtectedData non riesce (probabilmente diversa macchina/utente) // Prova con AES come fallback try { return DecryptWithAes(encryptedText); } catch { throw new InvalidOperationException( "Impossibile decrittografare le credenziali. " + "Le credenziali potrebbero essere state create su una macchina/utente diverso. " + "È necessario re-inserire le credenziali.", ex); } } } else { // Su altre piattaforme, usa AES return DecryptWithAes(encryptedText); } } private string EncryptWithProtectedData(string plainText) { byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText); byte[] entropy = Encoding.UTF8.GetBytes(ENTROPY_STRING); if (OperatingSystem.IsWindows()) { byte[] encryptedBytes = ProtectedData.Protect(plainTextBytes, entropy, DataProtectionScope.CurrentUser); return Convert.ToBase64String(encryptedBytes); } // Fallback ad AES se non su Windows return EncryptWithAes(plainText); } private string DecryptWithProtectedData(string encryptedText) { if (OperatingSystem.IsWindows()) { byte[] encryptedBytes = Convert.FromBase64String(encryptedText); byte[] entropy = Encoding.UTF8.GetBytes(ENTROPY_STRING); byte[] decryptedBytes = ProtectedData.Unprotect(encryptedBytes, entropy, DataProtectionScope.CurrentUser); return Encoding.UTF8.GetString(decryptedBytes); } // Fallback ad AES se non su Windows return DecryptWithAes(encryptedText); } private string EncryptWithAes(string plainText) { using var aes = Aes.Create(); aes.Key = _key; aes.IV = _iv; using var encryptor = aes.CreateEncryptor(); using var msEncrypt = new MemoryStream(); using var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write); using var swEncrypt = new StreamWriter(csEncrypt); swEncrypt.Write(plainText); swEncrypt.Close(); return Convert.ToBase64String(msEncrypt.ToArray()); } private string DecryptWithAes(string encryptedText) { byte[] cipherBytes = Convert.FromBase64String(encryptedText); using var aes = Aes.Create(); aes.Key = _key; aes.IV = _iv; using var decryptor = aes.CreateDecryptor(); using var msDecrypt = new MemoryStream(cipherBytes); using var csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read); using var srDecrypt = new StreamReader(csDecrypt); return srDecrypt.ReadToEnd(); } }