Skip to content

Commit

Permalink
[GDS Client] Fix Certificate Request when private Key of existing Cer…
Browse files Browse the repository at this point in the history
…tificate is not exportable (#607)

* if the Private Key is not exportable, create a new key pair and issue the CSR from the temporary private key, once the cert is signed, replace the old private key with the new one
  • Loading branch information
romanett authored Nov 28, 2024
1 parent 3d57b47 commit 7708a11
Showing 1 changed file with 31 additions and 3 deletions.
34 changes: 31 additions & 3 deletions Samples/GDS/Client/Controls/ApplicationCertificateControl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
* http://opcfoundation.org/License/MIT/1.00/
* ======================================================================*/

using Opc.Ua.Gds;
using Opc.Ua.Security.Certificates;
using System;
using System.Drawing;
Expand All @@ -50,6 +49,7 @@ public ApplicationCertificateControl()
private ServerPushConfigurationClient m_server;
private RegisteredApplication m_application;
private X509Certificate2 m_certificate;
private bool m_temporaryCertificateCreated;
private string m_certificatePassword;

public async Task Initialize(
Expand All @@ -64,6 +64,7 @@ public async Task Initialize(
m_server = server;
m_application = application;
m_certificate = null;
m_temporaryCertificateCreated = false;
m_certificatePassword = null;

CertificateRequestTimer.Enabled = false;
Expand Down Expand Up @@ -236,10 +237,31 @@ private async Task RequestNewCertificatePullMode(object sender, EventArgs e)
SubjectName = Utils.ReplaceDCLocalhost(m_application.CertificateSubjectName)
};
m_certificate = await id.Find(true);
//test if private key is available & exportable, else create new temporary certificate for csr
if (m_certificate != null &&
m_certificate.HasPrivateKey)
{
m_certificate = await id.LoadPrivateKey(m_certificatePassword);
try
{
//this line fails with a CryptographicException if export of private key is not allowed
_ = m_certificate.GetRSAPrivateKey().ExportParameters(true);
//proceed with a CSR using the exportable private key
m_certificate = await id.LoadPrivateKey(m_certificatePassword);
}
catch
{
//create temporary cert to generate csr from
m_certificate = CertificateFactory.CreateCertificate(
X509Utils.GetApplicationUriFromCertificate(m_certificate),
m_application.ApplicationName,
Utils.ReplaceDCLocalhost(m_application.CertificateSubjectName),
m_application.GetDomainNames(m_certificate))
.SetNotBefore(DateTime.Today.AddDays(-1))
.SetNotAfter(DateTime.Today.AddDays(14))
.SetRSAKeySize((ushort)(m_certificate.GetRSAPublicKey()?.KeySize ?? 0))
.CreateForRSA();
m_temporaryCertificateCreated = true;
}
}
}

Expand Down Expand Up @@ -347,7 +369,7 @@ private async void CertificateRequestTimer_Tick(object sender, EventArgs e)
if (oldCertificate != null && oldCertificate.HasPrivateKey)
{
oldCertificate = await cid.LoadPrivateKey(string.Empty);
newCert = CertificateFactory.CreateCertificateWithPrivateKey(newCert, oldCertificate);
newCert = CertificateFactory.CreateCertificateWithPrivateKey(newCert, m_temporaryCertificateCreated ? m_certificate : oldCertificate);
await store.Delete(oldCertificate.Thumbprint);
}
else
Expand All @@ -361,6 +383,12 @@ private async void CertificateRequestTimer_Tick(object sender, EventArgs e)
newCert = CertificateFactory.Load(newCert, true);
}
await store.Add(newCert);
if (m_temporaryCertificateCreated)
{
m_certificate.Dispose();
m_certificate = null;
m_temporaryCertificateCreated = false;
}
}
}
else
Expand Down

0 comments on commit 7708a11

Please sign in to comment.