using System; using System.IO; using System.Security.Cryptography; using System.Text; namespace DesktopBot.Services { /// /// Gestisce il salvataggio e il caricamento sicuro delle credenziali Alpaca. /// Cifra con AES-256 usando una chiave derivata da MachineName + UserName. /// public static class CredentialService { private static readonly string SettingsFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "TradingBot"); private static readonly string CredentialsFile = Path.Combine(SettingsFolder, "credentials.dat"); private static byte[] DeriveKey() { var seed = Environment.MachineName + Environment.UserName + "TradingBotSalt_v1"; using (var sha = SHA256.Create()) return sha.ComputeHash(Encoding.UTF8.GetBytes(seed)); } public static void SaveCredentials(string apiKey, string apiSecret, bool isPaper) { if (!Directory.Exists(SettingsFolder)) Directory.CreateDirectory(SettingsFolder); var plainText = apiKey + "|" + apiSecret + "|" + isPaper; var plainBytes = Encoding.UTF8.GetBytes(plainText); var key = DeriveKey(); using (var aes = Aes.Create()) { aes.Key = key; aes.GenerateIV(); using (var ms = new MemoryStream()) { ms.Write(aes.IV, 0, aes.IV.Length); using (var cs = new CryptoStream(ms, aes.CreateEncryptor(), CryptoStreamMode.Write)) cs.Write(plainBytes, 0, plainBytes.Length); File.WriteAllBytes(CredentialsFile, ms.ToArray()); } } } public static AlpacaCredentials LoadCredentials() { if (!File.Exists(CredentialsFile)) return null; try { var data = File.ReadAllBytes(CredentialsFile); var key = DeriveKey(); using (var aes = Aes.Create()) { aes.Key = key; var iv = new byte[16]; Array.Copy(data, 0, iv, 0, 16); aes.IV = iv; using (var ms = new MemoryStream(data, 16, data.Length - 16)) using (var cs = new CryptoStream(ms, aes.CreateDecryptor(), CryptoStreamMode.Read)) using (var reader = new StreamReader(cs, Encoding.UTF8)) { var plain = reader.ReadToEnd(); var parts = plain.Split('|'); if (parts.Length != 3) return null; return new AlpacaCredentials { ApiKey = parts[0], ApiSecret = parts[1], IsPaper = bool.Parse(parts[2]) }; } } } catch { return null; } } public static bool HasCredentials() => File.Exists(CredentialsFile); public static void DeleteCredentials() { if (File.Exists(CredentialsFile)) File.Delete(CredentialsFile); } } /// Modello credenziali Alpaca public class AlpacaCredentials { public string ApiKey { get; set; } public string ApiSecret { get; set; } public bool IsPaper { get; set; } } }