mirror of
https://github.com/OPCFoundation/UA-.NETStandard.git
synced 2026-10-26 04:19:53 +08:00
Merge pull request #376 from mregen/v103_csr_fix
GDS: Verify app uri in signing request. Use domain name from signing request if available. Should go as bugfix to 1.03 to prevent IOP issues with future GDS server releases. Fix for #375
This commit is contained in:
@@ -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()
|
||||
{
|
||||
|
||||
@@ -31,6 +31,9 @@
|
||||
<None Update="Opc.Ua.ServerConfigurationPushTestClient.Config.xml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="test.csr">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
Binary file not shown.
@@ -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<string>();
|
||||
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<X509Certificate2> 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
|
||||
|
||||
@@ -642,22 +642,30 @@ public class CertificateFactory
|
||||
}
|
||||
}
|
||||
|
||||
// build CSR extensions
|
||||
List<GeneralName> generalNames = new List<GeneralName>();
|
||||
|
||||
string applicationUri = Utils.GetApplicationUriFromCertificate(certificate);
|
||||
if (applicationUri != null)
|
||||
{
|
||||
generalNames.Add(new GeneralName(GeneralName.UniformResourceIdentifier, applicationUri));
|
||||
}
|
||||
|
||||
if (domainNames.Count > 0)
|
||||
{
|
||||
List<GeneralName> 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(
|
||||
|
||||
Reference in New Issue
Block a user