Skip to content

Commit 7708a11

Browse files
authored
[GDS Client] Fix Certificate Request when private Key of existing Certificate 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
1 parent 3d57b47 commit 7708a11

File tree

1 file changed

+31
-3
lines changed

1 file changed

+31
-3
lines changed

Samples/GDS/Client/Controls/ApplicationCertificateControl.cs

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@
2727
* http://opcfoundation.org/License/MIT/1.00/
2828
* ======================================================================*/
2929

30-
using Opc.Ua.Gds;
3130
using Opc.Ua.Security.Certificates;
3231
using System;
3332
using System.Drawing;
@@ -50,6 +49,7 @@ public ApplicationCertificateControl()
5049
private ServerPushConfigurationClient m_server;
5150
private RegisteredApplication m_application;
5251
private X509Certificate2 m_certificate;
52+
private bool m_temporaryCertificateCreated;
5353
private string m_certificatePassword;
5454

5555
public async Task Initialize(
@@ -64,6 +64,7 @@ public async Task Initialize(
6464
m_server = server;
6565
m_application = application;
6666
m_certificate = null;
67+
m_temporaryCertificateCreated = false;
6768
m_certificatePassword = null;
6869

6970
CertificateRequestTimer.Enabled = false;
@@ -236,10 +237,31 @@ private async Task RequestNewCertificatePullMode(object sender, EventArgs e)
236237
SubjectName = Utils.ReplaceDCLocalhost(m_application.CertificateSubjectName)
237238
};
238239
m_certificate = await id.Find(true);
240+
//test if private key is available & exportable, else create new temporary certificate for csr
239241
if (m_certificate != null &&
240242
m_certificate.HasPrivateKey)
241243
{
242-
m_certificate = await id.LoadPrivateKey(m_certificatePassword);
244+
try
245+
{
246+
//this line fails with a CryptographicException if export of private key is not allowed
247+
_ = m_certificate.GetRSAPrivateKey().ExportParameters(true);
248+
//proceed with a CSR using the exportable private key
249+
m_certificate = await id.LoadPrivateKey(m_certificatePassword);
250+
}
251+
catch
252+
{
253+
//create temporary cert to generate csr from
254+
m_certificate = CertificateFactory.CreateCertificate(
255+
X509Utils.GetApplicationUriFromCertificate(m_certificate),
256+
m_application.ApplicationName,
257+
Utils.ReplaceDCLocalhost(m_application.CertificateSubjectName),
258+
m_application.GetDomainNames(m_certificate))
259+
.SetNotBefore(DateTime.Today.AddDays(-1))
260+
.SetNotAfter(DateTime.Today.AddDays(14))
261+
.SetRSAKeySize((ushort)(m_certificate.GetRSAPublicKey()?.KeySize ?? 0))
262+
.CreateForRSA();
263+
m_temporaryCertificateCreated = true;
264+
}
243265
}
244266
}
245267

@@ -347,7 +369,7 @@ private async void CertificateRequestTimer_Tick(object sender, EventArgs e)
347369
if (oldCertificate != null && oldCertificate.HasPrivateKey)
348370
{
349371
oldCertificate = await cid.LoadPrivateKey(string.Empty);
350-
newCert = CertificateFactory.CreateCertificateWithPrivateKey(newCert, oldCertificate);
372+
newCert = CertificateFactory.CreateCertificateWithPrivateKey(newCert, m_temporaryCertificateCreated ? m_certificate : oldCertificate);
351373
await store.Delete(oldCertificate.Thumbprint);
352374
}
353375
else
@@ -361,6 +383,12 @@ private async void CertificateRequestTimer_Tick(object sender, EventArgs e)
361383
newCert = CertificateFactory.Load(newCert, true);
362384
}
363385
await store.Add(newCert);
386+
if (m_temporaryCertificateCreated)
387+
{
388+
m_certificate.Dispose();
389+
m_certificate = null;
390+
m_temporaryCertificateCreated = false;
391+
}
364392
}
365393
}
366394
else

0 commit comments

Comments
 (0)