From af4d18aa928e439b2cf1516b8c1fc9e5d44afe22 Mon Sep 17 00:00:00 2001 From: Palwinder Singh Date: Tue, 6 Jan 2026 23:59:36 -0800 Subject: [PATCH 1/2] TFS #650905: [BUG] [VKB) - Secrets Broker: Certificate Upload is Broken Added check for both encoding pcks12 and PEM encoding --- .../ConfigDb/LiteDbConfigurationRepository.cs | 5 ++- .../Extensions/CertificateExtensions.cs | 38 +++++++++++++++++++ .../Logic/CertificateData.cs | 3 +- .../Logic/SafeguardLogic.cs | 4 +- 4 files changed, 45 insertions(+), 5 deletions(-) create mode 100644 SafeguardDevOpsService/Extensions/CertificateExtensions.cs diff --git a/SafeguardDevOpsService/ConfigDb/LiteDbConfigurationRepository.cs b/SafeguardDevOpsService/ConfigDb/LiteDbConfigurationRepository.cs index 86affdcd..e1a467ae 100644 --- a/SafeguardDevOpsService/ConfigDb/LiteDbConfigurationRepository.cs +++ b/SafeguardDevOpsService/ConfigDb/LiteDbConfigurationRepository.cs @@ -12,6 +12,7 @@ using OneIdentity.DevOps.Data; using OneIdentity.DevOps.Data.Spp; using OneIdentity.DevOps.Exceptions; +using OneIdentity.DevOps.Extensions; using OneIdentity.DevOps.Logic; using CredentialType = CredentialManagement.CredentialType; @@ -653,7 +654,7 @@ public X509Certificate2 UserCertificate try { var bytes = Convert.FromBase64String(UserCertificateBase64Data); - var cert = X509CertificateLoader.LoadPkcs12(bytes, UserCertificatePassphrase); + var cert = CertificateExtensions.LoadFromBytes(bytes,UserCertificatePassphrase); return cert; } catch (Exception) @@ -699,7 +700,7 @@ public X509Certificate2 WebSslCertificate try { var bytes = Convert.FromBase64String(WebSslCertificateBase64Data); - var cert = X509CertificateLoader.LoadPkcs12(bytes, WebSslCertificatePassphrase); + var cert = CertificateExtensions.LoadFromBytes(bytes, WebSslCertificatePassphrase); return cert; } catch (Exception) diff --git a/SafeguardDevOpsService/Extensions/CertificateExtensions.cs b/SafeguardDevOpsService/Extensions/CertificateExtensions.cs new file mode 100644 index 00000000..c4a08fdf --- /dev/null +++ b/SafeguardDevOpsService/Extensions/CertificateExtensions.cs @@ -0,0 +1,38 @@ +using System.Security.Cryptography.X509Certificates; +using System.Text; + +namespace OneIdentity.DevOps.Extensions +{ + public static class CertificateExtensions + { + public static X509Certificate2 LoadFromBytes(byte[] rawData, string? password = null, X509KeyStorageFlags? keyStorageFlags = null) + { + // 1. Detect if the byte array is PKCS12 or PEM + X509ContentType contentType = X509Certificate2.GetCertContentType(rawData); + + if (contentType == X509ContentType.Pkcs12) + { + if (keyStorageFlags.HasValue) + { + // Use the new .NET 9 Loader for binary PKCS12/PFX + return X509CertificateLoader.LoadPkcs12(rawData, password, keyStorageFlags.Value); + } + else + { + // Use the new .NET 9 Loader for binary PKCS12/PFX + return X509CertificateLoader.LoadPkcs12(rawData, password); + } + + } + else + { + // It's likely PEM (text-based). + // We convert the bytes to a string to use the PEM parser. + string pemString = Encoding.UTF8.GetString(rawData); + + // This works if the PEM contains both the CERTIFICATE and PRIVATE KEY + return X509Certificate2.CreateFromPem(pemString); + } + } + } +} diff --git a/SafeguardDevOpsService/Logic/CertificateData.cs b/SafeguardDevOpsService/Logic/CertificateData.cs index 94a35ef9..7f9bae2d 100644 --- a/SafeguardDevOpsService/Logic/CertificateData.cs +++ b/SafeguardDevOpsService/Logic/CertificateData.cs @@ -4,6 +4,7 @@ using System.Security.Cryptography.X509Certificates; using System.Text; using OneIdentity.DevOps.Exceptions; +using OneIdentity.DevOps.Extensions; namespace OneIdentity.DevOps.Logic { @@ -16,7 +17,7 @@ internal class CertificateData : ICertificateData, IDisposable public CertificateData(string certB64, string password) { Password = password; - cert = X509CertificateLoader.LoadPkcs12(Convert.FromBase64String(certB64), password, X509KeyStorageFlags.EphemeralKeySet | X509KeyStorageFlags.Exportable); + cert = CertificateExtensions.LoadFromBytes(Convert.FromBase64String(certB64), password, X509KeyStorageFlags.EphemeralKeySet | X509KeyStorageFlags.Exportable); } public string Base64Certificate => Convert.ToBase64String(cert.Export(X509ContentType.Cert)); diff --git a/SafeguardDevOpsService/Logic/SafeguardLogic.cs b/SafeguardDevOpsService/Logic/SafeguardLogic.cs index fc128b10..db1b76b3 100644 --- a/SafeguardDevOpsService/Logic/SafeguardLogic.cs +++ b/SafeguardDevOpsService/Logic/SafeguardLogic.cs @@ -1640,7 +1640,7 @@ public void InstallCertificate(CertificateInfo certificate, CertificateType cert X509Certificate2 cert; try { - cert = X509CertificateLoader.LoadPkcs12(certificateBytes, certificate.Passphrase); + cert = CertificateExtensions.LoadFromBytes(certificateBytes, certificate.Passphrase); _logger.Debug( $"Parsed certificate for installation: subject={cert.SubjectName.Name}, thumbprint={cert.Thumbprint}"); } @@ -2304,7 +2304,7 @@ private CertificateInfo AddTrustedCertificate(string base64CertificateData, stri try { var certificateBytes = CertificateHelper.ConvertPemToData(base64CertificateData); - var cert = X509CertificateLoader.LoadPkcs12(certificateBytes, passPhrase); + var cert = CertificateExtensions.LoadFromBytes(certificateBytes, passPhrase); _logger.Debug( $"Parsed new trusted certificate: subject={cert.SubjectName}, thumbprint={cert.Thumbprint}."); From 00c75b29f24e34d2e469f49049dd98fd715eab43 Mon Sep 17 00:00:00 2001 From: Palwinder Singh Date: Thu, 8 Jan 2026 05:47:13 -0800 Subject: [PATCH 2/2] TFS #650905: [BUG] [VKB) - Secrets Broker: Certificate Upload is Broken Added check for both encoding pcks12 and PEM encoding --- .../Extensions/CertificateExtensions.cs | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/SafeguardDevOpsService/Extensions/CertificateExtensions.cs b/SafeguardDevOpsService/Extensions/CertificateExtensions.cs index c4a08fdf..8ee98d41 100644 --- a/SafeguardDevOpsService/Extensions/CertificateExtensions.cs +++ b/SafeguardDevOpsService/Extensions/CertificateExtensions.cs @@ -1,4 +1,5 @@ -using System.Security.Cryptography.X509Certificates; +using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; using System.Text; namespace OneIdentity.DevOps.Extensions @@ -30,8 +31,21 @@ public static X509Certificate2 LoadFromBytes(byte[] rawData, string? password = // We convert the bytes to a string to use the PEM parser. string pemString = Encoding.UTF8.GetString(rawData); - // This works if the PEM contains both the CERTIFICATE and PRIVATE KEY - return X509Certificate2.CreateFromPem(pemString); + // 1. Load the public certificate + var cert = X509CertificateLoader.LoadCertificate(rawData); + + // 2. Load the private key if it exists in the string + if (pemString.Contains("PRIVATE KEY")) + { + using var rsa = RSA.Create(); + // ImportFromPem automatically handles both 'Universal 2' and 'Universal 16' + rsa.ImportFromPem(pemString); + + // 3. Link them together + return cert.CopyWithPrivateKey(rsa); + } + + return cert; } } }