Show target certificate validity after PFX exchange.

Reuse the source PFX password for path probes and fall back to the loaded certificate expiry for successfully exchanged targets.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-31 08:12:45 +02:00
co-authored by Cursor
parent 89a694ba21
commit f98daec656
+58 -13
View File
@@ -47,6 +47,7 @@ namespace ZA.CoreService.ESBCertificateManager
private LocalSetupSelection? _localSetupSelection;
private CertificateInfo? _loadedCertificateInfo;
private string? _loadedCertificatePassword;
private IReadOnlyList<DeploymentTarget> _loadedTargets = [];
private bool _isOperationRunning;
private CancellationTokenSource? _runCts;
@@ -953,7 +954,9 @@ namespace ZA.CoreService.ESBCertificateManager
return;
}
CertificateInfo? certificateInfo = TryReadSelectedCertificate(selectedPath);
CertificateInfo? certificateInfo = TryReadSelectedCertificate(
selectedPath,
out string? certificatePassword);
if (certificateInfo == null)
{
return;
@@ -961,15 +964,26 @@ namespace ZA.CoreService.ESBCertificateManager
txtCertificatePath.Text = selectedPath;
_loadedCertificateInfo = certificateInfo;
_loadedCertificatePassword = certificatePassword;
SetStatus($"Zertifikat ausgewählt: {Path.GetFileName(selectedPath)}", isError: false);
DisplayCertificateInfo(certificateInfo);
UpdateToNextStep(2);
SetFileButtonState(fileLoaded: true);
RefreshActionButtonStates();
// Zielpfade mit Quell-Kennwort erneut prüfen, damit Ablauf/Gültigkeit sichtbar wird
if (_loadedTargets.Count > 0)
{
_ = ProbeTargetCertificatesAsync();
}
}
private CertificateInfo? TryReadSelectedCertificate(string selectedPath)
private CertificateInfo? TryReadSelectedCertificate(
string selectedPath,
out string? certificatePassword)
{
certificatePassword = null;
string extension = Path.GetExtension(selectedPath);
bool needsPassword =
extension.Equals(".pfx", StringComparison.OrdinalIgnoreCase)
@@ -991,7 +1005,9 @@ namespace ZA.CoreService.ESBCertificateManager
try
{
return ReadCertificate(selectedPath, pfxPassword);
CertificateInfo info = ReadCertificate(selectedPath, pfxPassword);
certificatePassword = pfxPassword;
return info;
}
catch (CryptographicException)
{
@@ -1019,6 +1035,7 @@ namespace ZA.CoreService.ESBCertificateManager
{
txtCertificatePath.Text = string.Empty;
_loadedCertificateInfo = null;
_loadedCertificatePassword = null;
SetStatus("Keine Datei ausgewählt", isError: false);
lblCertificateSubject.Text = "-";
@@ -1671,9 +1688,14 @@ namespace ZA.CoreService.ESBCertificateManager
return merged;
}
private async Task ProbeTargetCertificatesAsync()
private async Task ProbeTargetCertificatesAsync(
IReadOnlySet<int>? preferLoadedCertificateForTargetIds = null)
{
CertificateProbeService probe = new();
string? password = _loadedCertificatePassword;
Func<string?>? passwordPrompt = password is null
? null
: () => password;
foreach (DataGridViewRow row in dgvTargets.Rows)
{
@@ -1683,7 +1705,7 @@ namespace ZA.CoreService.ESBCertificateManager
}
CertificateProbeResult result = await Task.Run(
() => probe.Probe(target.FullTargetPath));
() => probe.Probe(target.FullTargetPath, passwordPrompt));
if (IsDisposed || !dgvTargets.Columns.Contains("Expiry"))
{
@@ -1692,13 +1714,20 @@ namespace ZA.CoreService.ESBCertificateManager
if (result.Success && result.Certificate is not null)
{
string expiry = result.Certificate.ValidUntil
.ToLocalTime()
.ToString("dd.MM.yyyy");
row.Cells["Expiry"].Value = FormatCertificateExpiryCell(
result.Certificate);
continue;
}
row.Cells["Expiry"].Value = result.Certificate.IsCurrentlyValid
? $"gültig bis {expiry}"
: $"abgelaufen {expiry}";
// Nach erfolgreichem Tausch: Quellzertifikat liegt am Ziel
// Gültigkeit auch zeigen, falls Probe am Kennwort scheitert.
if (preferLoadedCertificateForTargetIds is not null
&& preferLoadedCertificateForTargetIds.Contains(target.Id)
&& _loadedCertificateInfo is not null
&& result.FileExists)
{
row.Cells["Expiry"].Value = FormatCertificateExpiryCell(
_loadedCertificateInfo);
continue;
}
@@ -1714,6 +1743,17 @@ namespace ZA.CoreService.ESBCertificateManager
}
}
private static string FormatCertificateExpiryCell(CertificateInfo certificate)
{
string expiry = certificate.ValidUntil
.ToLocalTime()
.ToString("dd.MM.yyyy");
return certificate.IsCurrentlyValid
? $"gültig bis {expiry}"
: $"abgelaufen {expiry}";
}
private List<DeploymentTarget> GetSelectedTargets()
{
List<DeploymentTarget> selected = [];
@@ -2071,6 +2111,11 @@ namespace ZA.CoreService.ESBCertificateManager
targetResult.StatusText);
}
HashSet<int> exchangedTargetIds = runResult.TargetResults
.Where(result => result.Success)
.Select(result => result.TargetId)
.ToHashSet();
if (runResult.OverallSuccess)
{
SetStatus(
@@ -2078,7 +2123,7 @@ namespace ZA.CoreService.ESBCertificateManager
isError: false);
UpdateToNextStep(4);
await ProbeTargetCertificatesAsync();
await ProbeTargetCertificatesAsync(exchangedTargetIds);
}
else
{
@@ -2100,7 +2145,7 @@ namespace ZA.CoreService.ESBCertificateManager
"Austausch fehlgeschlagen",
details);
await ProbeTargetCertificatesAsync();
await ProbeTargetCertificatesAsync(exchangedTargetIds);
}
}
catch (OperationCanceledException)