175 lines
5.8 KiB
C#
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;
|
|
}
|
|
}
|
|
}
|