diff --git a/SampleApplications/Samples/GDS/ClientTest/ClientTest.cs b/SampleApplications/Samples/GDS/ClientTest/ClientTest.cs
index 7aa2e5cd69..ca26b9b985 100644
--- a/SampleApplications/Samples/GDS/ClientTest/ClientTest.cs
+++ b/SampleApplications/Samples/GDS/ClientTest/ClientTest.cs
@@ -38,6 +38,7 @@ using Opc.Ua.Gds;
using Opc.Ua.Gds.Client;
using Opc.Ua.Gds.Test;
using Opc.Ua.Test;
+using System.IO;
namespace NUnit.Opc.Ua.Gds.Test
{
@@ -523,7 +524,7 @@ namespace NUnit.Opc.Ua.Gds.Test
}
}
- [Test, Order(521)]
+ [Test, Order(530)]
public void FinishGoodSigningRequests()
{
AssertIgnoreTestWithoutGoodRegistration();
@@ -568,6 +569,26 @@ namespace NUnit.Opc.Ua.Gds.Test
}
+ [Test, Order(550)]
+ public void StartGoodSigningRequestWithInvalidAppURI()
+ {
+ AssertIgnoreTestWithoutGoodRegistration();
+ ConnectGDS(true);
+ var application = _goodApplicationTestSet[0];
+ Assert.Null(application.CertificateRequestId);
+ // load csr with invalid app URI
+ byte[] certificateRequest = File.ReadAllBytes("test.csr");
+ Assert.That(() =>
+ {
+ NodeId requestId = _gdsClient.GDSClient.StartSigningRequest(
+ application.ApplicationRecord.ApplicationId,
+ application.CertificateGroupId,
+ application.CertificateTypeId,
+ certificateRequest);
+ }, Throws.Exception);
+ }
+
+
[Test, Order(600)]
public void GetGoodCertificateGroupsNullTests()
{
diff --git a/SampleApplications/Samples/GDS/ClientTest/GlobalDiscoveryClientTest.csproj b/SampleApplications/Samples/GDS/ClientTest/GlobalDiscoveryClientTest.csproj
index a16068808a..9ce6473816 100644
--- a/SampleApplications/Samples/GDS/ClientTest/GlobalDiscoveryClientTest.csproj
+++ b/SampleApplications/Samples/GDS/ClientTest/GlobalDiscoveryClientTest.csproj
@@ -31,6 +31,9 @@
PreserveNewest
+
+ PreserveNewest
+
diff --git a/SampleApplications/Samples/GDS/ClientTest/test.csr b/SampleApplications/Samples/GDS/ClientTest/test.csr
new file mode 100644
index 0000000000..7d98d96ffd
Binary files /dev/null and b/SampleApplications/Samples/GDS/ClientTest/test.csr differ
diff --git a/SampleApplications/Samples/GDS/ServerCommon/CertificateGroup.cs b/SampleApplications/Samples/GDS/ServerCommon/CertificateGroup.cs
index 30acd06871..b57bb89c3a 100644
--- a/SampleApplications/Samples/GDS/ServerCommon/CertificateGroup.cs
+++ b/SampleApplications/Samples/GDS/ServerCommon/CertificateGroup.cs
@@ -27,9 +27,8 @@
* http://opcfoundation.org/License/MIT/1.00/
* ======================================================================*/
-using Org.BouncyCastle.Asn1.Pkcs;
-using Org.BouncyCastle.Pkcs;
using System;
+using System.Collections.Generic;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;
@@ -145,24 +144,63 @@ namespace Opc.Ua.Gds.Server
string[] domainNames,
byte[] certificateRequest)
{
- Pkcs10CertificationRequest pkcs10CertificationRequest = new Pkcs10CertificationRequest(certificateRequest);
- CertificationRequestInfo info = pkcs10CertificationRequest.GetCertificationRequestInfo();
- DateTime yesterday = DateTime.UtcNow.AddDays(-1);
- return CertificateFactory.CreateCertificate(
- null,
- null,
- null,
- application.ApplicationUri ?? "urn:ApplicationURI",
- application.ApplicationNames.Count > 0 ? application.ApplicationNames[0].Text : "ApplicationName",
- info.Subject.ToString(),
- domainNames,
- Configuration.DefaultCertificateKeySize,
- yesterday,
- Configuration.DefaultCertificateLifetime,
- Configuration.DefaultCertificateHashSize,
- false,
- await LoadSigningKeyAsync(Certificate, string.Empty),
- info.SubjectPublicKeyInfo.GetEncoded());
+ try
+ {
+ var pkcs10CertificationRequest = new Org.BouncyCastle.Pkcs.Pkcs10CertificationRequest(certificateRequest);
+
+ if (!pkcs10CertificationRequest.Verify())
+ {
+ throw new ServiceResultException(StatusCodes.BadInvalidArgument, "CSR signature invalid.");
+ }
+
+ var info = pkcs10CertificationRequest.GetCertificationRequestInfo();
+ var altNameExtension = GetAltNameExtensionFromCSRInfo(info);
+ if (altNameExtension != null)
+ {
+ if (altNameExtension.Uris.Count > 0)
+ {
+ if (!altNameExtension.Uris.Contains(application.ApplicationUri))
+ {
+ throw new ServiceResultException(StatusCodes.BadCertificateUriInvalid,
+ "CSR AltNameExtension does not match "+ application.ApplicationUri);
+ }
+ }
+
+ if (altNameExtension.IPAddresses.Count > 0 || altNameExtension.DomainNames.Count > 0)
+ {
+ var domainNameList = new List();
+ domainNameList.AddRange(altNameExtension.DomainNames);
+ domainNameList.AddRange(altNameExtension.IPAddresses);
+ domainNames = domainNameList.ToArray();
+ }
+ }
+
+ DateTime yesterday = DateTime.UtcNow.AddDays(-1);
+ return CertificateFactory.CreateCertificate(
+ null,
+ null,
+ null,
+ application.ApplicationUri ?? "urn:ApplicationURI",
+ application.ApplicationNames.Count > 0 ? application.ApplicationNames[0].Text : "ApplicationName",
+ info.Subject.ToString(),
+ domainNames,
+ Configuration.DefaultCertificateKeySize,
+ yesterday,
+ Configuration.DefaultCertificateLifetime,
+ Configuration.DefaultCertificateHashSize,
+ false,
+ await LoadSigningKeyAsync(Certificate, string.Empty),
+ info.SubjectPublicKeyInfo.GetEncoded());
+ }
+ catch (Exception ex)
+ {
+ if (ex is ServiceResultException)
+ {
+ throw ex as ServiceResultException;
+ }
+ throw new ServiceResultException(StatusCodes.BadInvalidArgument, ex.Message);
+ }
+
}
public virtual async Task CreateCACertificateAsync(
@@ -248,6 +286,33 @@ namespace Opc.Ua.Gds.Server
}
}
}
+
+ private X509SubjectAltNameExtension GetAltNameExtensionFromCSRInfo(Org.BouncyCastle.Asn1.Pkcs.CertificationRequestInfo info)
+ {
+ try
+ {
+ for (int i = 0; i < info.Attributes.Count; i++)
+ {
+ var sequence = Org.BouncyCastle.Asn1.Asn1Sequence.GetInstance(info.Attributes[i].ToAsn1Object());
+ var oid = Org.BouncyCastle.Asn1.DerObjectIdentifier.GetInstance(sequence[0].ToAsn1Object());
+ if (oid.Equals(Org.BouncyCastle.Asn1.Pkcs.PkcsObjectIdentifiers.Pkcs9AtExtensionRequest))
+ {
+ var extensionInstance = Org.BouncyCastle.Asn1.DerSet.GetInstance(sequence[1]);
+ var extensionSequence = Org.BouncyCastle.Asn1.Asn1Sequence.GetInstance(extensionInstance[0]);
+ var extensions = Org.BouncyCastle.Asn1.X509.X509Extensions.GetInstance(extensionSequence);
+ Org.BouncyCastle.Asn1.X509.X509Extension extension = extensions.GetExtension(Org.BouncyCastle.Asn1.X509.X509Extensions.SubjectAlternativeName);
+ var asnEncodedAltNameExtension = new System.Security.Cryptography.AsnEncodedData(Org.BouncyCastle.Asn1.X509.X509Extensions.SubjectAlternativeName.ToString(), extension.Value.GetOctets());
+ var altNameExtension = new X509SubjectAltNameExtension(asnEncodedAltNameExtension, extension.IsCritical);
+ return altNameExtension;
+ }
+ }
+ }
+ catch
+ {
+ throw new ServiceResultException(StatusCodes.BadInvalidArgument, "CSR altNameExtension invalid.");
+ }
+ return null;
+ }
#endregion
#region Protected Fields
diff --git a/Stack/Opc.Ua.Core/Security/Certificates/CertificateFactory.cs b/Stack/Opc.Ua.Core/Security/Certificates/CertificateFactory.cs
index 8b0193deb3..a36e1b1118 100644
--- a/Stack/Opc.Ua.Core/Security/Certificates/CertificateFactory.cs
+++ b/Stack/Opc.Ua.Core/Security/Certificates/CertificateFactory.cs
@@ -642,22 +642,30 @@ public class CertificateFactory
}
}
+ // build CSR extensions
+ List generalNames = new List();
+
+ string applicationUri = Utils.GetApplicationUriFromCertificate(certificate);
+ if (applicationUri != null)
+ {
+ generalNames.Add(new GeneralName(GeneralName.UniformResourceIdentifier, applicationUri));
+ }
+
if (domainNames.Count > 0)
{
- List generalNames = CreateSubjectAlternateNameDomains(domainNames);
- if (generalNames.Count > 0)
- {
- IList oids = new ArrayList();
- IList values = new ArrayList();
- oids.Add(X509Extensions.SubjectAlternativeName);
- values.Add(new Org.BouncyCastle.Asn1.X509.X509Extension(false,
- new DerOctetString(new GeneralNames(generalNames.ToArray()).GetDerEncoded())));
+ generalNames.AddRange(CreateSubjectAlternateNameDomains(domainNames));
+ }
- AttributePkcs attribute = new AttributePkcs(PkcsObjectIdentifiers.Pkcs9AtExtensionRequest,
- new DerSet(new X509Extensions(oids, values)));
-
- attributes = new DerSet(attribute);
- }
+ if (generalNames.Count > 0)
+ {
+ IList oids = new ArrayList();
+ IList values = new ArrayList();
+ oids.Add(X509Extensions.SubjectAlternativeName);
+ values.Add(new Org.BouncyCastle.Asn1.X509.X509Extension(false,
+ new DerOctetString(new GeneralNames(generalNames.ToArray()).GetDerEncoded())));
+ AttributePkcs attribute = new AttributePkcs(PkcsObjectIdentifiers.Pkcs9AtExtensionRequest,
+ new DerSet(new X509Extensions(oids, values)));
+ attributes = new DerSet(attribute);
}
Pkcs10CertificationRequest pkcs10CertificationRequest = new Pkcs10CertificationRequest(