From a3386f1ec0078cf1e980625d68a131cc59c1c2e5 Mon Sep 17 00:00:00 2001 From: Martin Regen Date: Thu, 22 Aug 2024 10:36:49 +0200 Subject: [PATCH 1/6] check server application Uri with the create session response --- .../Opc.Ua.Client/ReverseConnectManager.cs | 2 +- Libraries/Opc.Ua.Client/Session/Session.cs | 40 +++++++++++++----- .../Opc.Ua.Client/Session/SessionAsync.cs | 3 +- .../ApplicationInstance.cs | 2 +- .../CertificateWrapper.cs | 3 +- .../Extensions/X509SubjectAltNameExtension.cs | 27 ++++++++++-- .../Opc.Ua.Server/Server/StandardServer.cs | 42 ++++++++++++------- .../Certificates/CertificateFactory.cs | 4 +- .../Security/Certificates/X509Utils.cs | 20 +++++++++ Stack/Opc.Ua.Core/Stack/Server/ServerBase.cs | 2 +- 10 files changed, 108 insertions(+), 37 deletions(-) diff --git a/Libraries/Opc.Ua.Client/ReverseConnectManager.cs b/Libraries/Opc.Ua.Client/ReverseConnectManager.cs index d8a998545..d58e33c5c 100644 --- a/Libraries/Opc.Ua.Client/ReverseConnectManager.cs +++ b/Libraries/Opc.Ua.Client/ReverseConnectManager.cs @@ -163,7 +163,7 @@ public Registration( EventHandler onConnectionWaiting) : this(endpointUrl, onConnectionWaiting) { - ServerUri = X509Utils.GetApplicationUriFromCertificate(serverCertificate); + ServerUri = X509Utils.GetApplicationUrisFromCertificate(serverCertificate).FirstOrDefault(); } private Registration( diff --git a/Libraries/Opc.Ua.Client/Session/Session.cs b/Libraries/Opc.Ua.Client/Session/Session.cs index 2bef15dfd..de6ba83c5 100644 --- a/Libraries/Opc.Ua.Client/Session/Session.cs +++ b/Libraries/Opc.Ua.Client/Session/Session.cs @@ -2338,7 +2338,6 @@ public void Open( if (requireEncryption) { - ValidateServerCertificateApplicationUri(serverCertificate); if (checkDomain) { m_configuration.CertificateValidator.Validate(serverCertificateChain, m_endpoint); @@ -2456,6 +2455,8 @@ public void Open( ValidateServerEndpoints(serverEndpoints); + ValidateServerCertificateApplicationUri(m_endpoint, serverCertificate); + ValidateServerSignature(serverCertificate, serverSignature, clientCertificateData, clientCertificateChainData, clientNonce); HandleSignedSoftwareCertificates(serverSoftwareCertificates); @@ -5352,28 +5353,47 @@ private void OpenValidateIdentity( !String.IsNullOrEmpty(identityPolicy.SecurityPolicyUri); } } + /// - /// Validates the ServerCertificate ApplicationUri to match the ApplicationUri of the Endpoint for an open call (Spec Part 4 5.4.1) + /// Validates the ServerCertificate ApplicationUri to match the ApplicationUri + /// of the Endpoint (Spec Part 4 5.4.1) returned by the CreateSessionResponse. + /// Ensure the endpoint was matched in + /// with the applicationUri of the server description before the validation. /// - private void ValidateServerCertificateApplicationUri( + private static void ValidateServerCertificateApplicationUri(ConfiguredEndpoint endpoint, X509Certificate2 serverCertificate) X509Certificate2 serverCertificate) { - var applicationUri = m_endpoint?.Description?.Server?.ApplicationUri; - //check is only neccessary if the ApplicatioUri is specified for the Endpoint + if (serverCertificate != null) + { + var applicationUri = endpoint?.Description?.Server?.ApplicationUri; + + // check that an ApplicatioUri is specified for the Endpoint if (string.IsNullOrEmpty(applicationUri)) { throw ServiceResultException.Create( StatusCodes.BadSecurityChecksFailed, - "No ApplicationUri is specified for the server in the EndpointDescription."); + "Server did not return an ApplicationUri in the EndpointDescription."); } - string certificateApplicationUri = X509Utils.GetApplicationUriFromCertificate(serverCertificate); - if (!string.Equals(certificateApplicationUri, applicationUri, StringComparison.Ordinal)) + + bool noMatch = true; + var certificateApplicationUris = X509Utils.GetApplicationUrisFromCertificate(serverCertificate); + foreach (var certificateApplicationUri in certificateApplicationUris) { + if (string.Equals(certificateApplicationUri, applicationUri, StringComparison.Ordinal)) + { + noMatch = false; + break; + } + } + + if (noMatch) + { throw ServiceResultException.Create( StatusCodes.BadSecurityChecksFailed, - "Server did not return a Certificate matching the ApplicationUri specified in the EndpointDescription."); + "Server did not return the ApplicationUri in the EndpointDescription that is used in the server certificate."); } } + } private void BuildCertificateData(out byte[] clientCertificateData, out byte[] clientCertificateChainData) { @@ -5538,7 +5558,7 @@ private void ValidateServerEndpoints(EndpointDescriptionCollection serverEndpoin } } - // find the matching description (TBD - check domains against certificate). + // find the matching description (TBD - check domains and application URI against certificate). bool found = false; var foundDescription = FindMatchingDescription(serverEndpoints, m_endpoint.Description, true); diff --git a/Libraries/Opc.Ua.Client/Session/SessionAsync.cs b/Libraries/Opc.Ua.Client/Session/SessionAsync.cs index 1b26df310..68bb5b988 100644 --- a/Libraries/Opc.Ua.Client/Session/SessionAsync.cs +++ b/Libraries/Opc.Ua.Client/Session/SessionAsync.cs @@ -93,7 +93,6 @@ public async Task OpenAsync( if (requireEncryption) { - ValidateServerCertificateApplicationUri(serverCertificate); if (checkDomain) { await m_configuration.CertificateValidator.ValidateAsync(serverCertificateChain, m_endpoint, ct).ConfigureAwait(false); @@ -201,6 +200,8 @@ public async Task OpenAsync( ValidateServerEndpoints(serverEndpoints); + ValidateServerCertificateApplicationUri(m_endpoint, serverCertificate); + ValidateServerSignature(serverCertificate, serverSignature, clientCertificateData, clientCertificateChainData, clientNonce); HandleSignedSoftwareCertificates(serverSoftwareCertificates); diff --git a/Libraries/Opc.Ua.Configuration/ApplicationInstance.cs b/Libraries/Opc.Ua.Configuration/ApplicationInstance.cs index 64dba21e1..d35aac3f9 100644 --- a/Libraries/Opc.Ua.Configuration/ApplicationInstance.cs +++ b/Libraries/Opc.Ua.Configuration/ApplicationInstance.cs @@ -672,7 +672,7 @@ private async Task CheckApplicationInstanceCertificateAsync( } // check uri. - string applicationUri = X509Utils.GetApplicationUriFromCertificate(certificate); + string applicationUri = X509Utils.GetApplicationUrisFromCertificate(certificate).FirstOrDefault(); if (String.IsNullOrEmpty(applicationUri)) { diff --git a/Libraries/Opc.Ua.Gds.Client.Common/CertificateWrapper.cs b/Libraries/Opc.Ua.Gds.Client.Common/CertificateWrapper.cs index 4d929cb0f..5cc3514b1 100644 --- a/Libraries/Opc.Ua.Gds.Client.Common/CertificateWrapper.cs +++ b/Libraries/Opc.Ua.Gds.Client.Common/CertificateWrapper.cs @@ -29,6 +29,7 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Runtime.Serialization; using System.Security.Cryptography.X509Certificates; using Opc.Ua.Security.Certificates; @@ -213,7 +214,7 @@ public string ApplicationUri { try { - return X509Utils.GetApplicationUriFromCertificate(Certificate); + return X509Utils.GetApplicationUrisFromCertificate(Certificate).FirstOrDefault(); } catch (Exception e) { diff --git a/Libraries/Opc.Ua.Security.Certificates/Extensions/X509SubjectAltNameExtension.cs b/Libraries/Opc.Ua.Security.Certificates/Extensions/X509SubjectAltNameExtension.cs index d799c1871..9ca7be21b 100644 --- a/Libraries/Opc.Ua.Security.Certificates/Extensions/X509SubjectAltNameExtension.cs +++ b/Libraries/Opc.Ua.Security.Certificates/Extensions/X509SubjectAltNameExtension.cs @@ -118,7 +118,23 @@ public X509SubjectAltNameExtension( { Oid = new Oid(SubjectAltName2Oid, kFriendlyName); Critical = false; - Initialize(applicationUri, domainNames); + Initialize(new string[] { applicationUri }, domainNames); + RawData = Encode(); + m_decoded = true; + } + + /// + /// Build the Subject Alternative name extension (for OPC UA application certs). + /// + /// The application Uri + /// The domain names. DNS Hostnames, IPv4 or IPv6 addresses + public X509SubjectAltNameExtension( + IEnumerable applicationUris, + IEnumerable domainNames) + { + Oid = new Oid(SubjectAltName2Oid, kFriendlyName); + Critical = false; + Initialize(applicationUris, domainNames); RawData = Encode(); m_decoded = true; } @@ -402,14 +418,17 @@ private void Decode(byte[] data) /// /// Initialize the Subject Alternative name extension. /// - /// The application Uri + /// The application Uris /// The general names. DNS Hostnames, IPv4 or IPv6 addresses - private void Initialize(string applicationUri, IEnumerable generalNames) + private void Initialize(IEnumerable applicationUris, IEnumerable generalNames) { var uris = new List(); var domainNames = new List(); var ipAddresses = new List(); - uris.Add(applicationUri); + foreach (string applicationUri in applicationUris) + { + uris.Add(applicationUri); + } foreach (string generalName in generalNames) { switch (Uri.CheckHostName(generalName)) diff --git a/Libraries/Opc.Ua.Server/Server/StandardServer.cs b/Libraries/Opc.Ua.Server/Server/StandardServer.cs index a5d19b836..5e4212902 100644 --- a/Libraries/Opc.Ua.Server/Server/StandardServer.cs +++ b/Libraries/Opc.Ua.Server/Server/StandardServer.cs @@ -380,23 +380,33 @@ public override ResponseHeader CreateSession( if (context.SecurityPolicyUri != SecurityPolicies.None) { - string certificateApplicationUri = X509Utils.GetApplicationUriFromCertificate(parsedClientCertificate); - - // verify if applicationUri from ApplicationDescription matches the applicationUri in the client certificate. - if (!String.IsNullOrEmpty(certificateApplicationUri) && - !String.IsNullOrEmpty(clientDescription.ApplicationUri) && - certificateApplicationUri != clientDescription.ApplicationUri) + // verify if applicationUri from ApplicationDescription matches the applicationUris in the client certificate. + if (!String.IsNullOrEmpty(clientDescription.ApplicationUri)) { - // report the AuditCertificateDataMismatch event for invalid uri - ServerInternal?.ReportAuditCertificateDataMismatchEvent(parsedClientCertificate, null, clientDescription.ApplicationUri, StatusCodes.BadCertificateUriInvalid); + bool noMatch = true; + var certificateApplicationUris = X509Utils.GetApplicationUrisFromCertificate(parsedClientCertificate); + foreach (var certificateApplicationUri in certificateApplicationUris) + { + if (!String.IsNullOrEmpty(certificateApplicationUri) && certificateApplicationUri == clientDescription.ApplicationUri) + { + noMatch = false; + break; + } + } - throw ServiceResultException.Create( - StatusCodes.BadCertificateUriInvalid, - "The URI specified in the ApplicationDescription {0} does not match the URI in the Certificate: {1}.", - clientDescription.ApplicationUri, certificateApplicationUri); - } + if (noMatch) + { + // report the AuditCertificateDataMismatch event for invalid uri + ServerInternal?.ReportAuditCertificateDataMismatchEvent(parsedClientCertificate, null, clientDescription.ApplicationUri, StatusCodes.BadCertificateUriInvalid); + + throw ServiceResultException.Create( + StatusCodes.BadCertificateUriInvalid, + "The URI specified in the ApplicationDescription {0} does not match the URIs in the Certificate.", + clientDescription.ApplicationUri); + } - CertificateValidator.Validate(clientCertificateChain); + CertificateValidator.Validate(clientCertificateChain); + } } } catch (Exception e) @@ -2327,7 +2337,7 @@ public bool RegisterWithDiscoveryServer() { client.RegisterServer(requestHeader, m_registrationInfo); } - + m_registeredWithDiscoveryServer = m_registrationInfo.IsOnline; return true; } @@ -3082,7 +3092,7 @@ protected override void OnServerStopping() // attempt graceful shutdown the server. try { - + if (m_maxRegistrationInterval > 0 && m_registeredWithDiscoveryServer) { // unregister from Discovery Server if registered before diff --git a/Stack/Opc.Ua.Core/Security/Certificates/CertificateFactory.cs b/Stack/Opc.Ua.Core/Security/Certificates/CertificateFactory.cs index 2a2fde212..1a145fc77 100644 --- a/Stack/Opc.Ua.Core/Security/Certificates/CertificateFactory.cs +++ b/Stack/Opc.Ua.Core/Security/Certificates/CertificateFactory.cs @@ -319,10 +319,10 @@ public static byte[] CreateSigningRequest( } } - string applicationUri = X509Utils.GetApplicationUriFromCertificate(certificate); + var applicationUris = X509Utils.GetApplicationUrisFromCertificate(certificate); // Subject Alternative Name - var subjectAltName = new X509SubjectAltNameExtension(applicationUri, domainNames); + var subjectAltName = new X509SubjectAltNameExtension(applicationUris, domainNames); request.CertificateExtensions.Add(new X509Extension(subjectAltName, false)); using (RSA rsa = certificate.GetRSAPrivateKey()) diff --git a/Stack/Opc.Ua.Core/Security/Certificates/X509Utils.cs b/Stack/Opc.Ua.Core/Security/Certificates/X509Utils.cs index e47f1a4c2..9c0a02372 100644 --- a/Stack/Opc.Ua.Core/Security/Certificates/X509Utils.cs +++ b/Stack/Opc.Ua.Core/Security/Certificates/X509Utils.cs @@ -126,6 +126,7 @@ public static int GetRSAPublicKeySize(X509Certificate2 certificate) /// /// The certificate. /// The application URI. + [Obsolete("Use GetApplicationUrisFromCertificate instead. The certificate may contain more than one Uri.")] public static string GetApplicationUriFromCertificate(X509Certificate2 certificate) { // extract the alternate domains from the subject alternate name extension. @@ -140,6 +141,25 @@ public static string GetApplicationUriFromCertificate(X509Certificate2 certifica return string.Empty; } + /// + /// Extracts the application URIs specified in the certificate. + /// + /// The certificate. + /// The application URIs. + public static IReadOnlyList GetApplicationUrisFromCertificate(X509Certificate2 certificate) + { + // extract the alternate domains from the subject alternate name extension. + X509SubjectAltNameExtension alternateName = X509Extensions.FindExtension(certificate); + + // get the application uris. + if (alternateName != null && alternateName.Uris != null) + { + return alternateName.Uris; + } + + return new List(); + } + /// /// Check if certificate has an application urn. /// diff --git a/Stack/Opc.Ua.Core/Stack/Server/ServerBase.cs b/Stack/Opc.Ua.Core/Stack/Server/ServerBase.cs index 9d75de2b0..15804de89 100644 --- a/Stack/Opc.Ua.Core/Stack/Server/ServerBase.cs +++ b/Stack/Opc.Ua.Core/Stack/Server/ServerBase.cs @@ -1405,7 +1405,7 @@ protected virtual void OnServerStarting(ApplicationConfiguration configuration) // assign a unique identifier if none specified. if (String.IsNullOrEmpty(configuration.ApplicationUri)) { - configuration.ApplicationUri = X509Utils.GetApplicationUriFromCertificate(InstanceCertificate); + configuration.ApplicationUri = X509Utils.GetApplicationUrisFromCertificate(InstanceCertificate).FirstOrDefault(); if (String.IsNullOrEmpty(configuration.ApplicationUri)) { From 0a65ea440b2b2e442d6b3b133cf5620feb7d1c26 Mon Sep 17 00:00:00 2001 From: Martin Regen Date: Thu, 22 Aug 2024 10:48:20 +0200 Subject: [PATCH 2/6] fix formatting --- Libraries/Opc.Ua.Client/Session/Session.cs | 55 +++++++++++----------- 1 file changed, 27 insertions(+), 28 deletions(-) diff --git a/Libraries/Opc.Ua.Client/Session/Session.cs b/Libraries/Opc.Ua.Client/Session/Session.cs index de6ba83c5..4700684ab 100644 --- a/Libraries/Opc.Ua.Client/Session/Session.cs +++ b/Libraries/Opc.Ua.Client/Session/Session.cs @@ -2416,24 +2416,24 @@ public void Open( if (!successCreateSession) { base.CreateSession( - null, - clientDescription, - m_endpoint.Description.Server.ApplicationUri, - m_endpoint.EndpointUrl.ToString(), - sessionName, - clientNonce, - clientCertificateChainData != null ? clientCertificateChainData : clientCertificateData, - sessionTimeout, - (uint)MessageContext.MaxMessageSize, - out sessionId, - out sessionCookie, - out m_sessionTimeout, - out serverNonce, - out serverCertificateData, - out serverEndpoints, - out serverSoftwareCertificates, - out serverSignature, - out m_maxRequestMessageSize); + null, + clientDescription, + m_endpoint.Description.Server.ApplicationUri, + m_endpoint.EndpointUrl.ToString(), + sessionName, + clientNonce, + clientCertificateChainData != null ? clientCertificateChainData : clientCertificateData, + sessionTimeout, + (uint)MessageContext.MaxMessageSize, + out sessionId, + out sessionCookie, + out m_sessionTimeout, + out serverNonce, + out serverCertificateData, + out serverEndpoints, + out serverSoftwareCertificates, + out serverSignature, + out m_maxRequestMessageSize); } // save session id. @@ -5361,24 +5361,23 @@ private void OpenValidateIdentity( /// with the applicationUri of the server description before the validation. /// private static void ValidateServerCertificateApplicationUri(ConfiguredEndpoint endpoint, X509Certificate2 serverCertificate) - X509Certificate2 serverCertificate) { if (serverCertificate != null) { var applicationUri = endpoint?.Description?.Server?.ApplicationUri; // check that an ApplicatioUri is specified for the Endpoint - if (string.IsNullOrEmpty(applicationUri)) - { - throw ServiceResultException.Create( - StatusCodes.BadSecurityChecksFailed, + if (string.IsNullOrEmpty(applicationUri)) + { + throw ServiceResultException.Create( + StatusCodes.BadSecurityChecksFailed, "Server did not return an ApplicationUri in the EndpointDescription."); - } + } bool noMatch = true; var certificateApplicationUris = X509Utils.GetApplicationUrisFromCertificate(serverCertificate); foreach (var certificateApplicationUri in certificateApplicationUris) - { + { if (string.Equals(certificateApplicationUri, applicationUri, StringComparison.Ordinal)) { noMatch = false; @@ -5388,12 +5387,12 @@ private static void ValidateServerCertificateApplicationUri(ConfiguredEndpoint e if (noMatch) { - throw ServiceResultException.Create( - StatusCodes.BadSecurityChecksFailed, + throw ServiceResultException.Create( + StatusCodes.BadSecurityChecksFailed, "Server did not return the ApplicationUri in the EndpointDescription that is used in the server certificate."); + } } } - } private void BuildCertificateData(out byte[] clientCertificateData, out byte[] clientCertificateChainData) { From c6e35ae8da1aacda3928a8c0c5e3398b5bcb65b1 Mon Sep 17 00:00:00 2001 From: Martin Regen Date: Thu, 22 Aug 2024 11:19:27 +0200 Subject: [PATCH 3/6] fix tests --- Tests/Opc.Ua.Gds.Tests/X509TestUtils.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/Opc.Ua.Gds.Tests/X509TestUtils.cs b/Tests/Opc.Ua.Gds.Tests/X509TestUtils.cs index e61ad47a1..1b0dc51e8 100644 --- a/Tests/Opc.Ua.Gds.Tests/X509TestUtils.cs +++ b/Tests/Opc.Ua.Gds.Tests/X509TestUtils.cs @@ -156,7 +156,7 @@ public static void VerifySignedApplicationCert(ApplicationTestData testApp, byte Assert.True(domainNames.Contains(domainName, StringComparer.OrdinalIgnoreCase)); } Assert.True(subjectAlternateName.Uris.Count == 1); - var applicationUri = X509Utils.GetApplicationUriFromCertificate(signedCert); + var applicationUri = X509Utils.GetApplicationUrisFromCertificate(signedCert).FirstOrDefault(); Assert.True(testApp.ApplicationRecord.ApplicationUri == applicationUri); } } From d4cb133f844d48d6ee8e4791c37356ac7f739b77 Mon Sep 17 00:00:00 2001 From: Martin Regen Date: Thu, 22 Aug 2024 14:48:21 +0200 Subject: [PATCH 4/6] fix a test --- .../Security/Certificates/CertificateFactoryTest.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Tests/Opc.Ua.Core.Tests/Security/Certificates/CertificateFactoryTest.cs b/Tests/Opc.Ua.Core.Tests/Security/Certificates/CertificateFactoryTest.cs index b6e28a798..16273c14e 100644 --- a/Tests/Opc.Ua.Core.Tests/Security/Certificates/CertificateFactoryTest.cs +++ b/Tests/Opc.Ua.Core.Tests/Security/Certificates/CertificateFactoryTest.cs @@ -435,8 +435,8 @@ public static void VerifyApplicationCert(ApplicationTestData testApp, X509Certif Assert.True(domainNames.Contains(domainName, StringComparer.OrdinalIgnoreCase)); } Assert.True(subjectAlternateName.Uris.Count == 1); - var applicationUri = X509Utils.GetApplicationUriFromCertificate(cert); - TestContext.Out.WriteLine("ApplicationUri: "); + var applicationUri = X509Utils.GetApplicationUrisFromCertificate(cert).FirstOrDefault(); + TestContext.Out.WriteLine("ApplicationUris: "); TestContext.Out.WriteLine(applicationUri); Assert.AreEqual(testApp.ApplicationUri, applicationUri); } From b7c787c62834d6df6881e3fd4571cefe0be3537e Mon Sep 17 00:00:00 2001 From: Martin Regen Date: Fri, 23 Aug 2024 08:20:40 +0200 Subject: [PATCH 5/6] improve error message --- Libraries/Opc.Ua.Client/Session/Session.cs | 17 +++++++++++++---- Stack/Opc.Ua.Core/Types/Utils/Utils.cs | 2 +- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/Libraries/Opc.Ua.Client/Session/Session.cs b/Libraries/Opc.Ua.Client/Session/Session.cs index 4700684ab..8d5798cab 100644 --- a/Libraries/Opc.Ua.Client/Session/Session.cs +++ b/Libraries/Opc.Ua.Client/Session/Session.cs @@ -5370,12 +5370,20 @@ private static void ValidateServerCertificateApplicationUri(ConfiguredEndpoint e if (string.IsNullOrEmpty(applicationUri)) { throw ServiceResultException.Create( - StatusCodes.BadSecurityChecksFailed, + StatusCodes.BadCertificateUriInvalid, "Server did not return an ApplicationUri in the EndpointDescription."); } - bool noMatch = true; var certificateApplicationUris = X509Utils.GetApplicationUrisFromCertificate(serverCertificate); + if (certificateApplicationUris.Count == 0) + { + throw ServiceResultException.Create( + StatusCodes.BadCertificateUriInvalid, + "The Server Certificate ({1}) does not contain an applicationUri.", + serverCertificate.Subject); + } + + bool noMatch = true; foreach (var certificateApplicationUri in certificateApplicationUris) { if (string.Equals(certificateApplicationUri, applicationUri, StringComparison.Ordinal)) @@ -5388,8 +5396,9 @@ private static void ValidateServerCertificateApplicationUri(ConfiguredEndpoint e if (noMatch) { throw ServiceResultException.Create( - StatusCodes.BadSecurityChecksFailed, - "Server did not return the ApplicationUri in the EndpointDescription that is used in the server certificate."); + StatusCodes.BadCertificateUriInvalid, + "The Application in the EndpointDescription ({0}) is not in the Server Certificate ({1}).", + applicationUri, serverCertificate.Subject); } } } diff --git a/Stack/Opc.Ua.Core/Types/Utils/Utils.cs b/Stack/Opc.Ua.Core/Types/Utils/Utils.cs index 6816af4e8..550a01e14 100644 --- a/Stack/Opc.Ua.Core/Types/Utils/Utils.cs +++ b/Stack/Opc.Ua.Core/Types/Utils/Utils.cs @@ -2529,7 +2529,7 @@ public static void UpdateExtension(ref XmlElementCollection extensions, XmlQu extensions.Add(document.DocumentElement); } } -#endregion + #endregion #region Reflection Helper Functions /// From 7d3ec375aaeec4423b763105763566087a8052cc Mon Sep 17 00:00:00 2001 From: Martin Regen Date: Fri, 23 Aug 2024 09:54:20 +0200 Subject: [PATCH 6/6] fix app uri match --- .../Opc.Ua.Client/ReverseConnectManager.cs | 11 ++++--- .../ApplicationInstance.cs | 31 ++++++++++++++----- .../Opc.Ua.Server/Server/StandardServer.cs | 3 +- 3 files changed, 32 insertions(+), 13 deletions(-) diff --git a/Libraries/Opc.Ua.Client/ReverseConnectManager.cs b/Libraries/Opc.Ua.Client/ReverseConnectManager.cs index d58e33c5c..bf5afe2c2 100644 --- a/Libraries/Opc.Ua.Client/ReverseConnectManager.cs +++ b/Libraries/Opc.Ua.Client/ReverseConnectManager.cs @@ -152,11 +152,14 @@ public Registration( } /// - /// Register with the server certificate. + /// Register with the server certificate to extract the application Uri. /// - /// - /// - /// + /// + /// The first Uri in the subject alternate name field is considered the application Uri. + /// + /// The server certificate with the application Uri. + /// The endpoint Url of the server. + /// The connection to use. public Registration( X509Certificate2 serverCertificate, Uri endpointUrl, diff --git a/Libraries/Opc.Ua.Configuration/ApplicationInstance.cs b/Libraries/Opc.Ua.Configuration/ApplicationInstance.cs index d35aac3f9..3c447a4b5 100644 --- a/Libraries/Opc.Ua.Configuration/ApplicationInstance.cs +++ b/Libraries/Opc.Ua.Configuration/ApplicationInstance.cs @@ -671,23 +671,38 @@ private async Task CheckApplicationInstanceCertificateAsync( } } - // check uri. - string applicationUri = X509Utils.GetApplicationUrisFromCertificate(certificate).FirstOrDefault(); + // default application uri + string applicationUri = configuration.ApplicationUri; - if (String.IsNullOrEmpty(applicationUri)) + // check certificate Uri and find a match + var applicationUris = X509Utils.GetApplicationUrisFromCertificate(certificate); + bool foundMatch = false; + foreach (var appUri in applicationUris) { - string message = "The Application URI could not be read from the certificate. Use certificate anyway?"; - if (!await ApplicationInstance.ApproveMessageAsync(message, silent).ConfigureAwait(false)) + if (configuration.ApplicationUri.Equals(appUri, StringComparison.Ordinal)) { - return false; + foundMatch = true; + break; } } - else if (!configuration.ApplicationUri.Equals(applicationUri, StringComparison.Ordinal)) + + if (!foundMatch && applicationUris.Count > 0) { - Utils.LogInfo("Updated the ApplicationUri: {0} --> {1}", configuration.ApplicationUri, applicationUri); + foundMatch = true; + applicationUri = applicationUris[0]; + Utils.LogInfo("Updating the ApplicationUri: {0} --> {1}", configuration.ApplicationUri, applicationUris[0]); configuration.ApplicationUri = applicationUri; } + if (!foundMatch) + { + string message = "The Application URI could not be found in the certificate. Use certificate anyway?"; + if (!await ApplicationInstance.ApproveMessageAsync(message, silent).ConfigureAwait(false)) + { + return false; + } + } + Utils.LogInfo("Using the ApplicationUri: {0}", applicationUri); // update configuration. diff --git a/Libraries/Opc.Ua.Server/Server/StandardServer.cs b/Libraries/Opc.Ua.Server/Server/StandardServer.cs index 5e4212902..575bcfb78 100644 --- a/Libraries/Opc.Ua.Server/Server/StandardServer.cs +++ b/Libraries/Opc.Ua.Server/Server/StandardServer.cs @@ -387,7 +387,8 @@ public override ResponseHeader CreateSession( var certificateApplicationUris = X509Utils.GetApplicationUrisFromCertificate(parsedClientCertificate); foreach (var certificateApplicationUri in certificateApplicationUris) { - if (!String.IsNullOrEmpty(certificateApplicationUri) && certificateApplicationUri == clientDescription.ApplicationUri) + if (!String.IsNullOrEmpty(certificateApplicationUri) && + certificateApplicationUri == clientDescription.ApplicationUri) { noMatch = false; break;