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; } } } }