Files
EFC-DESK-V2/Classes/SecureDataHandler.cs
2026-02-09 10:55:45 -07:00

175 lines
5.8 KiB
C#

using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Cryptography;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace EFCDesk.Classes
{
public static class SecureDataHandler
{
private const string SettingKey = "IdUsuario";
// Genera entropía dinámica a partir de datos de hardware y usuario
private static byte[] GetEntropy()
{
string machineName = Environment.MachineName;
string userName = Environment.UserName;
string cpu = Environment.GetEnvironmentVariable("PROCESSOR_IDENTIFIER") ?? "";
string sysDrive = Environment.GetEnvironmentVariable("SystemDrive") ?? "";
//string exeHash = GetExecutableHash();
string mac = GetMacAddress() ?? "";
string combo = $"MiAppSalt|{machineName}|{userName}|{cpu}|{sysDrive}|{mac}";
return Encoding.UTF8.GetBytes(combo);
}
// Calcula hash SHA256 del ejecutable para validar que no ha sido copiado o modificado
private static string GetExecutableHash()
{
string exePath = Application.ExecutablePath;
using (var sha = SHA256.Create())
using (var stream = File.OpenRead(exePath))
{
byte[] hash = sha.ComputeHash(stream);
return Convert.ToBase64String(hash);
}
}
private static string? GetMacAddress()
{
var nic = System.Net.NetworkInformation.NetworkInterface
.GetAllNetworkInterfaces()
.FirstOrDefault(n =>
n.OperationalStatus == System.Net.NetworkInformation.OperationalStatus.Up &&
n.NetworkInterfaceType != System.Net.NetworkInformation.NetworkInterfaceType.Loopback &&
n.GetPhysicalAddress().GetAddressBytes().Length > 0);
if (nic == null) return null;
// Sin ":"
return string.Concat(nic.GetPhysicalAddress()
.GetAddressBytes()
.Select(b => b.ToString("X2")));
}
public static void SaveData(string data)
{
if (data == null) throw new ArgumentNullException(nameof(data));
byte[] bytes = Encoding.UTF8.GetBytes(data);
byte[] encrypted = ProtectedData.Protect(bytes, GetEntropy(), DataProtectionScope.CurrentUser);
string encryptedBase64 = Convert.ToBase64String(encrypted);
Properties.Settings.Default[SettingKey] = encryptedBase64;
Properties.Settings.Default.Save();
ClearBytes(bytes);
ClearBytes(encrypted);
}
public static SecureString? ReadDataAsSecureString()
{
string? encryptedBase64 = Properties.Settings.Default[SettingKey] as string;
if (string.IsNullOrEmpty(encryptedBase64)) return null;
byte[] encrypted = Convert.FromBase64String(encryptedBase64);
byte[]? decrypted = null;
try
{
decrypted = ProtectedData.Unprotect(encrypted, GetEntropy(), DataProtectionScope.CurrentUser);
string? data = Encoding.UTF8.GetString(decrypted);
var secure = new SecureString();
if (data != null)
{
foreach (char c in data) secure.AppendChar(c);
ClearString(ref data);
}
secure.MakeReadOnly();
return secure;
}
catch (CryptographicException)
{
// Los datos no se pueden desencriptar → devolver null en vez de corromper
return null;
}
finally
{
if (decrypted != null) ClearBytes(decrypted);
ClearBytes(encrypted);
}
}
public static string? ReadDataAsPlainText()
{
SecureString? ss = ReadDataAsSecureString();
if (ss == null) return null;
try { return SecureStringToString(ss); }
finally { ss.Dispose(); }
}
public static void DeleteData()
{
Properties.Settings.Default[SettingKey] = null;
Properties.Settings.Default.Save();
}
public static bool IsDataValid()
{
string? encryptedBase64 = Properties.Settings.Default[SettingKey] as string;
if (string.IsNullOrEmpty(encryptedBase64)) return false;
byte[] encrypted = Convert.FromBase64String(encryptedBase64);
try
{
byte[] decrypted = ProtectedData.Unprotect(encrypted, GetEntropy(), DataProtectionScope.CurrentUser);
ClearBytes(decrypted);
return true;
}
catch
{
return false;
}
}
private static string? SecureStringToString(SecureString secure)
{
if (secure == null) return null;
IntPtr ptr = IntPtr.Zero;
try
{
ptr = Marshal.SecureStringToGlobalAllocUnicode(secure);
return Marshal.PtrToStringUni(ptr);
}
finally
{
if (ptr != IntPtr.Zero) Marshal.ZeroFreeGlobalAllocUnicode(ptr);
}
}
private static void ClearBytes(byte[] buf)
{
if (buf == null) return;
for (int i = 0; i < buf.Length; i++) buf[i] = 0;
}
private static unsafe void ClearString(ref string? s)
{
if (s == null) return;
fixed (char* p = s)
{
for (int i = 0; i < s.Length; i++) p[i] = '\0';
}
s = null;
}
}
}