From 23f0e3abd7107856d40a39bf30345e0cfe3c2769 Mon Sep 17 00:00:00 2001 From: Martin Regen Date: Wed, 28 Feb 2018 11:37:57 +0100 Subject: [PATCH] add App Uri to csr / test app uri in signing request. Use domain names in signing request. --- .../Samples/GDS/ClientTest/ClientTest.cs | 23 +++- .../GlobalDiscoveryClientTest.csproj | 3 + .../Samples/GDS/ClientTest/test.csr | Bin 0 -> 1229 bytes .../GDS/ServerCommon/CertificateGroup.cs | 105 ++++++++++++++---- .../Certificates/CertificateFactory.cs | 34 +++--- 5 files changed, 131 insertions(+), 34 deletions(-) create mode 100644 SampleApplications/Samples/GDS/ClientTest/test.csr 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 0000000000000000000000000000000000000000..7d98d96ffdf003dc9912d0800dba19694e859083 GIT binary patch literal 1229 zcmXqLVmWEh#JrJ-k-?y`+mPFUlZ`o)g-w{r#nn*2fDgps;9+-4EK4lPOf(cS5CDm= z^Kkl=7NzDUr52?dDj3Lt#JG6GL-R7zGE-9&97{{`a}!H4^YawkbCWy_O$>}cDwuh+ zLlc8ji^@`qoE>tDQqxoOO!ZPri}DLn^^)_83iNU_lZ*0;^V3Rz++2euMkNDYHcqWJ zkGAi;jEsz|49rc8{0u;GE~X|%Musny$HT9S{OC0aayYhi#oG0kw*_BSsd`*`D&ynE z3rzF+cIKEkOXlk+_%?0nn$kR-rS(AE^@IZ+Cj@;Hcvo+jx%pQ2X_35#&dmQ@{in?T zx~qKgoW(ZZH)fy zs3S3EUv+sjLOl#h4I1w_N1jR%e>*XtZB{>jP2u#~)zL*BFK)PeH(van;Y2pWm7@BT z>c(yPxyJ-7bI;0K>SQQr_!$K$UgLVKV=QuFamV|BKlT@d*1fL@4es_8x_NE=s!yBq z*6a}M7uPxcc6sKuU1E(pZysD_7rO1zj#KyMmF-%yor#%|fpI|-<2PW4e1eA)C!b*x z<71%EeFGt2pvwv~GX7`bFkk~xpeXic<4kDtU~K#0#K_BHCuXDMo|>0hlvo0cwerl8 z3LlBvn}8B)0&izE6u-9JU_HV94j$e1JQxAyZv z|M%~>S@piI-)hdhaE;&kdZDiGPZv#WJI8ZlzW$+imOHoIJK>fapD*-AvNY%DGA+A< zW>H#fx)&-|XoQ_RpSoUg;?%!SS{0rwDgKilGU31($*-)_zp;x)JEmS}YuPB8)$Sjn zaoa)H+-J)q8v|RvvN>luJ9$3DalE#jwdX)-@dsPE-!qKgeK|7qPQ}B}TNhI%d2K7= ewkba{{nKl|8}k3z{Jz}UJ~!=bInSb)%5ngEHPo^I literal 0 HcmV?d00001 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(