91dbe9ae11
- Nuovo progetto MachineGuard: libreria che verifica se la macchina corrente è autorizzata all'esecuzione tramite DPAPI (Data Protection API di Windows) - Nuovo progetto MachineGuardSetup: tool di configurazione da eseguire come Amministratore per registrare la macchina autorizzata - Data_Coupler.sln: aggiunti entrambi i nuovi progetti alla soluzione - Data_Coupler.csproj: aggiunto riferimento al progetto MachineGuard - Program.cs: integrazione MachineGuard all'avvio dell'applicazione; se la macchina non è autorizzata l'app viene arrestata immediatamente con log critico e scrittura nel Windows Event Log
86 lines
3.0 KiB
C#
86 lines
3.0 KiB
C#
using Microsoft.Extensions.Logging;
|
|
using Microsoft.Extensions.Options;
|
|
using System.Runtime.Versioning;
|
|
using System.Security.Cryptography;
|
|
using System.Text;
|
|
|
|
namespace MachineGuard;
|
|
|
|
/// <summary>
|
|
/// Implementazione Windows-only della protezione machine-binding tramite DPAPI.
|
|
/// Utilizza <see cref="ProtectedData"/> con scope <see cref="DataProtectionScope.LocalMachine"/>:
|
|
/// il dato cifrato è legato fisicamente alla macchina e non può essere decifrato
|
|
/// su un'altra macchina, anche con gli stessi account o credenziali.
|
|
/// </summary>
|
|
[SupportedOSPlatform("windows")]
|
|
internal sealed class DpapiMachineGuard : IMachineGuard
|
|
{
|
|
private readonly MachineGuardOptions _options;
|
|
private readonly ILogger<DpapiMachineGuard> _logger;
|
|
|
|
public DpapiMachineGuard(IOptions<MachineGuardOptions> options, ILogger<DpapiMachineGuard> logger)
|
|
{
|
|
_options = options.Value;
|
|
_logger = logger;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public bool Verify()
|
|
{
|
|
var secretPath = ResolveSecretFilePath();
|
|
|
|
if (!File.Exists(secretPath))
|
|
{
|
|
_logger.LogError(
|
|
"MachineGuard: file secret non trovato in '{Path}'. " +
|
|
"Eseguire MachineGuardSetup.exe su questa macchina per inizializzare l'autorizzazione.",
|
|
secretPath);
|
|
return false;
|
|
}
|
|
|
|
try
|
|
{
|
|
var encryptedBytes = File.ReadAllBytes(secretPath);
|
|
var decryptedBytes = ProtectedData.Unprotect(encryptedBytes, null, DataProtectionScope.LocalMachine);
|
|
var decryptedToken = Encoding.UTF8.GetString(decryptedBytes);
|
|
|
|
if (!string.Equals(decryptedToken, MachineGuardToken.ExpectedToken, StringComparison.Ordinal))
|
|
{
|
|
_logger.LogError(
|
|
"MachineGuard: il token decifrato non corrisponde al token atteso. " +
|
|
"Questa macchina non è autorizzata a eseguire questa applicazione.");
|
|
return false;
|
|
}
|
|
|
|
_logger.LogInformation("MachineGuard: autorizzazione macchina verificata con successo.");
|
|
return true;
|
|
}
|
|
catch (CryptographicException ex)
|
|
{
|
|
_logger.LogError(ex,
|
|
"MachineGuard: decifrazione fallita. " +
|
|
"Il file secret potrebbe provenire da un'altra macchina o essere corrotto. " +
|
|
"Percorso: '{Path}'", secretPath);
|
|
return false;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex,
|
|
"MachineGuard: errore imprevisto durante la verifica. Percorso: '{Path}'", secretPath);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private string ResolveSecretFilePath()
|
|
{
|
|
if (!string.IsNullOrWhiteSpace(_options.SecretFilePath))
|
|
return _options.SecretFilePath;
|
|
|
|
var appData = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData);
|
|
if (string.IsNullOrEmpty(appData))
|
|
appData = @"C:\ProgramData";
|
|
|
|
return Path.Combine(appData, "DataCoupler", "machine.guard");
|
|
}
|
|
}
|