using System;
using System.IO;
using System.Security.Cryptography;
namespace PolyTrader.Core.Security
{
///
/// Löst den AES-Master-Key auf, mit dem Secrets at-rest
/// verschlüsselt (D-5/D-10 aus dem Deploymentcenter-Umsetzungsplan).
///
/// Reihenfolge: Umgebungsvariable, dann eine vorhandene Schlüsseldatei, sonst wird ein neuer
/// erzeugt und geschrieben — das ist der Regelfall direkt nach update-agent --action install
/// auf einem frischen System, wo niemand von Hand einen Schlüssel eintippt.
///
/// **Die eine Regel, die nie verletzt werden darf:** Erzeugt wird AUSSCHLIESSLICH, wenn weder
/// Variable noch Datei existieren. Ein versehentliches Überschreiben würde jedes bereits
/// verschlüsselte Secret in der Datenbank unwiederbringlich unlesbar machen — der einzige
/// wirklich unumkehrbare Fehler in diesem gesamten Bereich. Ein zweiter Aufruf (jeder weitere
/// Programmstart) liest deshalb ausnahmslos die vorhandene Datei, statt neu zu erzeugen.
///
public static class MasterKeyResolver
{
/// Pfad der Schlüsseldatei (z.B. neben der Anwendung).
/// Liest z.B. POLYTRADER_MASTER_KEY. Injizierbar für Tests.
/// Aufgerufen, wenn ein neuer Schlüssel erzeugt wurde (Pfad als Parameter).
/// Aufgerufen bei einem nicht fatalen Problem (z.B. Dateirechte, Erzeugung fehlgeschlagen).
public static string? Resolve(
string keyFilePath,
Func readEnvironmentVariable,
Action? onGenerated = null,
Action? onWarning = null)
{
string? masterKey = readEnvironmentVariable();
if (!string.IsNullOrWhiteSpace(masterKey))
{
return masterKey;
}
if (File.Exists(keyFilePath))
{
return File.ReadAllText(keyFilePath).Trim();
}
try
{
byte[] keyBytes = RandomNumberGenerator.GetBytes(32); // 256-Bit-Schlüssel
string generated = Convert.ToBase64String(keyBytes);
File.WriteAllText(keyFilePath, generated);
FilePermissions.RestrictToOwner(keyFilePath, onWarning);
onGenerated?.Invoke(keyFilePath);
return generated;
}
catch (Exception ex)
{
// Erzeugung darf den Start nie verhindern - ohne Master-Key laufen Secrets eben
// unverschlüsselt weiter (wie bisher ohne POLYTRADER_MASTER_KEY), statt abzustürzen.
onWarning?.Invoke($"Master-Key konnte nicht automatisch erzeugt werden: {ex.Message}.");
return null;
}
}
}
}