using System.Security.Cryptography; using ZA.CoreService.ESBCertificateManager.Models; namespace ZA.CoreService.ESBCertificateManager.Services; public sealed class CertificateDeployer { public Task<(bool Success, string Status, string? Detail, string? BackupPath)> DeployAsync( string sourceCertificatePath, DeploymentTarget target, CancellationToken cancellationToken = default) { return Task.Run(() => Deploy(sourceCertificatePath, target, cancellationToken), cancellationToken); } private static (bool Success, string Status, string? Detail, string? BackupPath) Deploy( string sourceCertificatePath, DeploymentTarget target, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); if (!File.Exists(sourceCertificatePath)) { return (false, "Quelle fehlt", $"Quelldatei nicht gefunden: {sourceCertificatePath}", null); } string targetDirectory = PathResolver.ResolvePath(target.TargetDirectory); Directory.CreateDirectory(targetDirectory); string destinationPath = Path.Combine(targetDirectory, target.CertificateFileName); string? backupPath = null; try { if (File.Exists(destinationPath)) { backupPath = $"{destinationPath}.bak-{DateTime.Now:yyyyMMddHHmmss}"; File.Copy(destinationPath, backupPath, overwrite: false); } cancellationToken.ThrowIfCancellationRequested(); File.Copy(sourceCertificatePath, destinationPath, overwrite: true); string sourceHash = ComputeSha256(sourceCertificatePath); string destHash = ComputeSha256(destinationPath); if (!string.Equals(sourceHash, destHash, StringComparison.OrdinalIgnoreCase)) { RestoreFromBackupOrDelete(destinationPath, backupPath); return (false, "Hash-Fehler", "SHA-256 von Quelle und Ziel stimmen nicht überein. Rollback ausgeführt.", backupPath); } return (true, "Kopiert", $"Ziel: {destinationPath}", backupPath); } catch (OperationCanceledException) { throw; } catch (Exception ex) { try { if (backupPath is not null && File.Exists(backupPath) && File.Exists(destinationPath)) { File.Copy(backupPath, destinationPath, overwrite: true); } } catch { // Rollback best-effort } return (false, "Kopierfehler", ex.Message, backupPath); } } private static void RestoreFromBackupOrDelete(string destinationPath, string? backupPath) { if (backupPath is not null && File.Exists(backupPath)) { File.Copy(backupPath, destinationPath, overwrite: true); return; } if (File.Exists(destinationPath)) { File.Delete(destinationPath); } } public static string ComputeSha256(string filePath) { using FileStream stream = File.OpenRead(filePath); byte[] hash = SHA256.HashData(stream); return Convert.ToHexString(hash); } }