From 0724bac0be4bd9b2ec0c3ae3483e946dc604fe5e Mon Sep 17 00:00:00 2001 From: Roman Ettlinger Date: Thu, 13 Aug 2026 22:14:47 +0200 Subject: [PATCH] Fix GDS Client cert path placeholder expansion when saving When saving a new certificate, GetAbsoluteFilePath only resolves paths to files that already exist, so for a not-yet-created file it failed and the code fell back to the raw path with an unexpanded %CommonApplicationData% placeholder. Route the public and private key save paths through a new GetSaveFilePath helper that falls back to Utils.ReplaceSpecialFolderNames so placeholders are consistently expanded and certs are written to the intended location. Fixes #740 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../Controls/ApplicationCertificateControl.cs | 40 ++++++++++++++++++- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/Samples/GDS/Client/Controls/ApplicationCertificateControl.cs b/Samples/GDS/Client/Controls/ApplicationCertificateControl.cs index e13e90f13..39a2726bb 100644 --- a/Samples/GDS/Client/Controls/ApplicationCertificateControl.cs +++ b/Samples/GDS/Client/Controls/ApplicationCertificateControl.cs @@ -473,7 +473,7 @@ private async void CertificateRequestTimer_Tick(object sender, EventArgs e) else { DialogResult result = DialogResult.Yes; - string absoluteCertificatePublicKeyPath = Utils.GetAbsoluteFilePath(m_application.CertificatePublicKeyPath, true, false, false) ?? m_application.CertificatePublicKeyPath; + string absoluteCertificatePublicKeyPath = GetSaveFilePath(m_application.CertificatePublicKeyPath); FileInfo file = new FileInfo(absoluteCertificatePublicKeyPath); if (file.Exists) { @@ -504,7 +504,7 @@ private async void CertificateRequestTimer_Tick(object sender, EventArgs e) // if we provided a PFX or P12 with the private key, we need to merge the new cert with the private key if (m_application.GetPrivateKeyFormat((m_server != null ? await m_server.GetSupportedKeyFormatsAsync() : ArrayOf.Empty).ToArray()) == "PFX") { - string absoluteCertificatePrivateKeyPath = Utils.GetAbsoluteFilePath(m_application.CertificatePrivateKeyPath, true, false, false) ?? m_application.CertificatePrivateKeyPath; + string absoluteCertificatePrivateKeyPath = GetSaveFilePath(m_application.CertificatePrivateKeyPath); file = new FileInfo(absoluteCertificatePrivateKeyPath); if (file.Exists) { @@ -612,6 +612,42 @@ private async void CertificateRequestTimer_Tick(object sender, EventArgs e) } } + /// + /// Resolves a certificate file path for saving. + /// + /// + /// only resolves paths to files that + /// already exist. When saving a new certificate the target file does not exist yet, so that call fails + /// and the raw path (which may still contain an unexpanded placeholder such as + /// %CommonApplicationData%) would be used verbatim. This helper falls back to + /// so environment/special-folder placeholders are + /// consistently expanded and the certificate is written to the intended location. + /// + private static string GetSaveFilePath(string filePath) + { + if (String.IsNullOrEmpty(filePath)) + { + return filePath; + } + + try + { + // Prefer an already existing file (also handles current-directory lookup). + string resolved = Utils.GetAbsoluteFilePath(filePath, true, false, false); + if (!String.IsNullOrEmpty(resolved)) + { + return resolved; + } + } + catch (ServiceResultException) + { + // File does not exist yet (new certificate): fall back to placeholder expansion below. + } + + // Expand special-folder/environment placeholders so a new certificate is saved to the intended path. + return Utils.ReplaceSpecialFolderNames(filePath) ?? filePath; + } + private async void ApplyChangesButton_Click(object sender, EventArgs e) { ApplyChangesButton.Enabled = false;