diff --git a/SampleApplications/SDK/Opc.Ua.Client/Session.cs b/SampleApplications/SDK/Opc.Ua.Client/Session.cs index 437bcb7777..54d597821f 100644 --- a/SampleApplications/SDK/Opc.Ua.Client/Session.cs +++ b/SampleApplications/SDK/Opc.Ua.Client/Session.cs @@ -199,7 +199,7 @@ namespace Opc.Ua.Client m_instanceCertificate.Thumbprint); } - // load certificate chain + // load certificate chain. m_instanceCertificateChain = new X509Certificate2Collection(m_instanceCertificate); List issuers = new List(); configuration.CertificateValidator.GetIssuers(m_instanceCertificate, issuers).Wait(); @@ -835,6 +835,7 @@ namespace Opc.Ua.Client } X509Certificate2 clientCertificate = null; + X509Certificate2Collection clientCertificateChain = null; if (endpointDescription.SecurityPolicyUri != SecurityPolicies.None) { @@ -849,6 +850,16 @@ namespace Opc.Ua.Client { throw ServiceResultException.Create(StatusCodes.BadConfigurationError, "ApplicationCertificate cannot be found."); } + + // load certificate chain. + clientCertificateChain = new X509Certificate2Collection(clientCertificate); + List issuers = new List(); + configuration.CertificateValidator.GetIssuers(clientCertificate, issuers).Wait(); + + for (int i = 0; i < issuers.Count; i++) + { + clientCertificateChain.Add(issuers[i].Certificate); + } } // initialize the channel which will be created with the server. @@ -857,6 +868,7 @@ namespace Opc.Ua.Client endpointDescription, endpointConfiguration, clientCertificate, + configuration.SecurityConfiguration.SendCertificateChain? clientCertificateChain : null, messageContext); // create the session object. @@ -890,6 +902,7 @@ namespace Opc.Ua.Client template.m_endpoint.Description, template.m_endpoint.Configuration, template.m_instanceCertificate, + template.m_configuration.SecurityConfiguration.SendCertificateChain ? template.m_instanceCertificateChain : null, template.m_configuration.CreateMessageContext()); // create the session object. @@ -968,10 +981,9 @@ namespace Opc.Ua.Client } } - EndpointDescription endpoint = m_endpoint.Description; - // create the client signature. - byte[] dataToSign = Utils.Append(endpoint.ServerCertificate, m_serverNonce); + byte[] dataToSign = Utils.Append(m_serverCertificate != null ? m_serverCertificate.RawData : null, m_serverNonce); + EndpointDescription endpoint = m_endpoint.Description; SignatureData clientSignature = SecurityPolicies.Sign(m_instanceCertificate, endpoint.SecurityPolicyUri, dataToSign); // check that the user identity is supported by the endpoint. @@ -1026,6 +1038,7 @@ namespace Opc.Ua.Client m_endpoint.Description, m_endpoint.Configuration, m_instanceCertificate, + m_configuration.SecurityConfiguration.SendCertificateChain ? m_instanceCertificateChain : null, MessageContext); // disposes the existing channel. @@ -1978,7 +1991,7 @@ namespace Opc.Ua.Client string securityPolicyUri = m_endpoint.Description.SecurityPolicyUri; - // get the identity token. + // get the identity token. if (identity == null) { identity = new UserIdentity(); @@ -2011,13 +2024,20 @@ namespace Opc.Ua.Client requireEncryption = identityPolicy.SecurityPolicyUri != SecurityPolicies.None; } - // validate the server certificate. + // validate the server certificate /certificate chain. X509Certificate2 serverCertificate = null; byte[] certificateData = m_endpoint.Description.ServerCertificate; + if (certificateData != null && certificateData.Length > 0 && requireEncryption) { - serverCertificate = Utils.ParseCertificateBlob(certificateData); - m_configuration.CertificateValidator.Validate(serverCertificate); + X509Certificate2Collection serverCertificateChain = Utils.ParseCertificateChainBlob(certificateData); + + if (serverCertificateChain.Count > 0) + { + serverCertificate = serverCertificateChain[0]; + } + + m_configuration.CertificateValidator.Validate(serverCertificateChain); if (checkDomain) { @@ -2025,11 +2045,6 @@ namespace Opc.Ua.Client } // save for reconnect m_checkDomain = checkDomain; - - //X509Certificate2Collection certificateChain = Utils.ParseCertificateChainBlob(certificateData); - //if (certificateChain.Count > 0) - // serverCertificate = certificateChain[0]; - //m_configuration.CertificateValidator.Validate(certificateChain); } // create a nonce. @@ -2045,6 +2060,19 @@ namespace Opc.Ua.Client // send the application instance certificate for the client. byte[] clientCertificateData = m_instanceCertificate != null ? m_instanceCertificate.RawData : null; + byte[] clientCertificateChainData = null; + + if (m_instanceCertificateChain != null && m_instanceCertificateChain.Count > 0 && m_configuration.SecurityConfiguration.SendCertificateChain) + { + List clientCertificateChain = new List(); + + for (int i = 0; i < m_instanceCertificateChain.Count; i++) + { + clientCertificateChain.AddRange(m_instanceCertificateChain[i].RawData); + } + + clientCertificateChainData = clientCertificateChain.ToArray(); + } ApplicationDescription clientDescription = new ApplicationDescription(); @@ -2103,7 +2131,7 @@ namespace Opc.Ua.Client m_endpoint.EndpointUrl.ToString(), sessionName, clientNonce, - clientCertificateData, + clientCertificateChainData != null ? clientCertificateChainData : clientCertificateData, sessionTimeout, (uint)MessageContext.MaxMessageSize, out sessionId, @@ -2129,13 +2157,27 @@ namespace Opc.Ua.Client //we need to call CloseSession if CreateSession was successful but some other exception is thrown try { - // verify that the server returned the same instance certificate. if (serverCertificateData != null && !Utils.IsEqual(serverCertificateData, m_endpoint.Description.ServerCertificate)) { - throw ServiceResultException.Create( - StatusCodes.BadCertificateInvalid, - "Server did not return the certificate used to create the secure channel."); + try + { + // verify for certificate chain in endpoint. + X509Certificate2Collection serverCertificateChain = Utils.ParseCertificateChainBlob(m_endpoint.Description.ServerCertificate); + + if (serverCertificateChain.Count > 0 && !Utils.IsEqual(serverCertificateData, serverCertificateChain[0].RawData)) + { + throw ServiceResultException.Create( + StatusCodes.BadCertificateInvalid, + "Server did not return the certificate used to create the secure channel."); + } + } + catch (Exception) + { + throw ServiceResultException.Create( + StatusCodes.BadCertificateInvalid, + "Server did not return the certificate used to create the secure channel."); + } } if (serverSignature == null || serverSignature.Signature == null) @@ -2191,7 +2233,6 @@ namespace Opc.Ua.Client } } - // find the matching description (TBD - check domains against certificate). bool found = false; Uri expectedUrl = Utils.ParseUri(m_endpoint.Description.EndpointUrl); @@ -2238,9 +2279,24 @@ namespace Opc.Ua.Client if (!SecurityPolicies.Verify(serverCertificate, m_endpoint.Description.SecurityPolicyUri, dataToSign, serverSignature)) { - throw ServiceResultException.Create( - StatusCodes.BadApplicationSignatureInvalid, - "Server did not provide a correct signature for the nonce data provided by the client."); + // validate the signature with complete chain if the check with leaf certificate failed. + if (clientCertificateChainData != null) + { + dataToSign = Utils.Append(clientCertificateChainData, clientNonce); + + if (!SecurityPolicies.Verify(serverCertificate, m_endpoint.Description.SecurityPolicyUri, dataToSign, serverSignature)) + { + throw ServiceResultException.Create( + StatusCodes.BadApplicationSignatureInvalid, + "Server did not provide a correct signature for the nonce data provided by the client."); + } + } + else + { + throw ServiceResultException.Create( + StatusCodes.BadApplicationSignatureInvalid, + "Server did not provide a correct signature for the nonce data provided by the client."); + } } // get a validator to check certificates provided by server. @@ -2270,7 +2326,7 @@ namespace Opc.Ua.Client ValidateSoftwareCertificates(softwareCertificates); // create the client signature. - dataToSign = Utils.Append(serverCertificateData, serverNonce); + dataToSign = Utils.Append(serverCertificate != null ? serverCertificate.RawData : null, serverNonce); SignatureData clientSignature = SecurityPolicies.Sign(m_instanceCertificate, securityPolicyUri, dataToSign); // select the security policy for the user token. @@ -2406,13 +2462,7 @@ namespace Opc.Ua.Client string securityPolicyUri = m_endpoint.Description.SecurityPolicyUri; // create the client signature. - byte[] serverCertificateData = null; - if (m_serverCertificate != null) - { - serverCertificateData = m_serverCertificate.RawData; - } - // create the client signature. - byte[] dataToSign = Utils.Append(serverCertificateData, serverNonce); + byte[] dataToSign = Utils.Append(m_serverCertificate != null ? m_serverCertificate.RawData : null, serverNonce); SignatureData clientSignature = SecurityPolicies.Sign(m_instanceCertificate, securityPolicyUri, dataToSign); // choose a default token. diff --git a/SampleApplications/SDK/Opc.Ua.Server/Server/StandardServer.cs b/SampleApplications/SDK/Opc.Ua.Server/Server/StandardServer.cs index ded11c7c06..d094bd40f3 100644 --- a/SampleApplications/SDK/Opc.Ua.Server/Server/StandardServer.cs +++ b/SampleApplications/SDK/Opc.Ua.Server/Server/StandardServer.cs @@ -341,7 +341,8 @@ namespace Opc.Ua.Server { try { - parsedClientCertificate = CertificateFactory.Create(clientCertificate, true); + X509Certificate2Collection clientCertificateChain = Utils.ParseCertificateChainBlob(clientCertificate); + parsedClientCertificate = clientCertificateChain[0]; if (context.SecurityPolicyUri != SecurityPolicies.None) { @@ -357,7 +358,7 @@ namespace Opc.Ua.Server "The URI specified in the ApplicationDescription does not match the URI in the Certificate."); } - CertificateValidator.Validate(parsedClientCertificate); + CertificateValidator.Validate(clientCertificateChain); } } catch (Exception e) @@ -395,29 +396,44 @@ namespace Opc.Ua.Server out sessionId, out authenticationToken, out serverNonce, - out revisedSessionTimeout); - + out revisedSessionTimeout); + lock (m_lock) - { + { // return the application instance certificate for the server. if (requireEncryption) { - serverCertificate = InstanceCertificate.RawData; + // check if complete chain should be sent. + if (Configuration.SecurityConfiguration.SendCertificateChain && InstanceCertificateChain != null && InstanceCertificateChain.Count >0) + { + List serverCertificateChain = new List(); + + for (int i = 0; i < InstanceCertificateChain.Count; i++) + { + serverCertificateChain.AddRange(InstanceCertificateChain[i].RawData); + } + + serverCertificate = serverCertificateChain.ToArray(); + } + else + { + serverCertificate = InstanceCertificate.RawData; + } } - + // return the endpoints supported by the server. serverEndpoints = GetEndpointDescriptions(endpointUrl, BaseAddresses, null); // return the software certificates assigned to the server. serverSoftwareCertificates = new SignedSoftwareCertificateCollection(ServerProperties.SoftwareCertificates); - + // sign the nonce provided by the client. serverSignature = null; - + // sign the client nonce (if provided). if (parsedClientCertificate != null && clientNonce != null) { - byte[] dataToSign = Utils.Append(clientCertificate, clientNonce); + byte[] dataToSign = Utils.Append(parsedClientCertificate.RawData, clientNonce); serverSignature = SecurityPolicies.Sign(InstanceCertificate, context.SecurityPolicyUri, dataToSign); } } @@ -2682,7 +2698,7 @@ namespace Opc.Ua.Server serverDescription.ApplicationType = configuration.ApplicationType; serverDescription.ProductUri = configuration.ProductUri; serverDescription.DiscoveryUrls = GetDiscoveryUrls(); - + endpoints = new EndpointDescriptionCollection(); IList endpointsForHost = null; diff --git a/SampleApplications/SDK/Opc.Ua.Server/Session/Session.cs b/SampleApplications/SDK/Opc.Ua.Server/Session/Session.cs index 2ac9d727ef..9b66641250 100644 --- a/SampleApplications/SDK/Opc.Ua.Server/Session/Session.cs +++ b/SampleApplications/SDK/Opc.Ua.Server/Session/Session.cs @@ -69,9 +69,9 @@ namespace Opc.Ua.Server byte[] clientNonce, byte[] serverNonce, string sessionName, - ApplicationDescription clientDescription, - string endpointUrl, - X509Certificate2 clientCertificate, + ApplicationDescription clientDescription, + string endpointUrl, + X509Certificate2 clientCertificate, double sessionTimeout, uint maxResponseMessageSize, double maxRequestAge, @@ -547,10 +547,34 @@ namespace Opc.Ua.Server if (!SecurityPolicies.Verify(m_clientCertificate, m_endpoint.SecurityPolicyUri, dataToSign, clientSignature)) { - throw new ServiceResultException(StatusCodes.BadApplicationSignatureInvalid); + // verify for certificate chain in endpoint. + // validate the signature with complete chain if the check with leaf certificate failed. + X509Certificate2Collection serverCertificateChain = Utils.ParseCertificateChainBlob(m_endpoint.ServerCertificate); + + if (serverCertificateChain.Count > 1) + { + List serverCertificateChainList = new List(); + + for (int i = 0; i < serverCertificateChain.Count; i++) + { + serverCertificateChainList.AddRange(serverCertificateChain[i].RawData); + } + + byte[] serverCertificateChainData = serverCertificateChainList.ToArray(); + dataToSign = Utils.Append(serverCertificateChainData, m_serverNonce); + + if (!SecurityPolicies.Verify(m_clientCertificate, m_endpoint.SecurityPolicyUri, dataToSign, clientSignature)) + { + throw new ServiceResultException(StatusCodes.BadApplicationSignatureInvalid); + } + } + else + { + throw new ServiceResultException(StatusCodes.BadApplicationSignatureInvalid); + } } } - + if (!m_activated) { // must active the session on the channel that was used to create it. @@ -565,7 +589,7 @@ namespace Opc.Ua.Server if (clientSoftwareCertificates != null && clientSoftwareCertificates.Count > 0) { throw new ServiceResultException(StatusCodes.BadInvalidArgument); - } + } } // validate the user identity token. @@ -1024,11 +1048,35 @@ namespace Opc.Ua.Server if (!token.Verify(dataToSign, userTokenSignature, securityPolicyUri)) { - throw new ServiceResultException(StatusCodes.BadUserSignatureInvalid, "Invalid user signature!"); + // verify for certificate chain in endpoint. + // validate the signature with complete chain if the check with leaf certificate failed. + X509Certificate2Collection serverCertificateChain = Utils.ParseCertificateChainBlob(m_endpoint.ServerCertificate); + + if (serverCertificateChain.Count > 1) + { + List serverCertificateChainList = new List(); + + for (int i = 0; i < serverCertificateChain.Count; i++) + { + serverCertificateChainList.AddRange(serverCertificateChain[i].RawData); + } + + byte[] serverCertificateChainData = serverCertificateChainList.ToArray(); + dataToSign = Utils.Append(serverCertificateChainData, m_serverNonce); + + if (!token.Verify(dataToSign, userTokenSignature, securityPolicyUri)) + { + throw new ServiceResultException(StatusCodes.BadUserSignatureInvalid, "Invalid user signature!"); + } + } + else + { + throw new ServiceResultException(StatusCodes.BadUserSignatureInvalid, "Invalid user signature!"); + } } } } - + // validate user identity token. return token; } @@ -1043,7 +1091,7 @@ namespace Opc.Ua.Server IUserIdentity effectiveIdentity) { if (identityToken == null) throw new ArgumentNullException("identityToken"); - + lock (m_lock) { bool changed = m_effectiveIdentity == null && effectiveIdentity != null; @@ -1057,7 +1105,7 @@ namespace Opc.Ua.Server m_identityToken = identityToken; m_identity = identity; m_effectiveIdentity = effectiveIdentity; - + // update diagnostics. lock (DiagnosticsLock) { @@ -1097,39 +1145,39 @@ namespace Opc.Ua.Server } ServiceCounterDataType counter = null; - + switch (requestType) - { - case RequestType.Read: { counter = m_diagnostics.ReadCount; break; } - case RequestType.HistoryRead: { counter = m_diagnostics.HistoryReadCount; break; } - case RequestType.Write: { counter = m_diagnostics.WriteCount; break; } - case RequestType.HistoryUpdate: { counter = m_diagnostics.HistoryUpdateCount; break; } - case RequestType.Call: { counter = m_diagnostics.CallCount; break; } - case RequestType.CreateMonitoredItems: { counter = m_diagnostics.CreateMonitoredItemsCount; break; } - case RequestType.ModifyMonitoredItems: { counter = m_diagnostics.ModifyMonitoredItemsCount; break; } - case RequestType.SetMonitoringMode: { counter = m_diagnostics.SetMonitoringModeCount; break; } - case RequestType.SetTriggering: { counter = m_diagnostics.SetTriggeringCount; break; } - case RequestType.DeleteMonitoredItems: { counter = m_diagnostics.DeleteMonitoredItemsCount; break; } - case RequestType.CreateSubscription: { counter = m_diagnostics.CreateSubscriptionCount; break; } - case RequestType.ModifySubscription: { counter = m_diagnostics.ModifySubscriptionCount; break; } - case RequestType.SetPublishingMode: { counter = m_diagnostics.SetPublishingModeCount; break; } - case RequestType.Publish: { counter = m_diagnostics.PublishCount; break; } - case RequestType.Republish: { counter = m_diagnostics.RepublishCount; break; } - case RequestType.TransferSubscriptions: { counter = m_diagnostics.TransferSubscriptionsCount; break; } - case RequestType.DeleteSubscriptions: { counter = m_diagnostics.DeleteSubscriptionsCount; break; } - case RequestType.AddNodes: { counter = m_diagnostics.AddNodesCount; break; } - case RequestType.AddReferences: { counter = m_diagnostics.AddReferencesCount; break; } - case RequestType.DeleteNodes: { counter = m_diagnostics.DeleteNodesCount; break; } - case RequestType.DeleteReferences: { counter = m_diagnostics.DeleteReferencesCount; break; } - case RequestType.Browse: { counter = m_diagnostics.BrowseCount; break; } - case RequestType.BrowseNext: { counter = m_diagnostics.BrowseNextCount; break; } - case RequestType.TranslateBrowsePathsToNodeIds: { counter = m_diagnostics.TranslateBrowsePathsToNodeIdsCount; break; } - case RequestType.QueryFirst: { counter = m_diagnostics.QueryFirstCount; break; } - case RequestType.QueryNext: { counter = m_diagnostics.QueryNextCount; break; } - case RequestType.RegisterNodes: { counter = m_diagnostics.RegisterNodesCount; break; } - case RequestType.UnregisterNodes: { counter = m_diagnostics.UnregisterNodesCount; break; } + { + case RequestType.Read: { counter = m_diagnostics.ReadCount; break; } + case RequestType.HistoryRead: { counter = m_diagnostics.HistoryReadCount; break; } + case RequestType.Write: { counter = m_diagnostics.WriteCount; break; } + case RequestType.HistoryUpdate: { counter = m_diagnostics.HistoryUpdateCount; break; } + case RequestType.Call: { counter = m_diagnostics.CallCount; break; } + case RequestType.CreateMonitoredItems: { counter = m_diagnostics.CreateMonitoredItemsCount; break; } + case RequestType.ModifyMonitoredItems: { counter = m_diagnostics.ModifyMonitoredItemsCount; break; } + case RequestType.SetMonitoringMode: { counter = m_diagnostics.SetMonitoringModeCount; break; } + case RequestType.SetTriggering: { counter = m_diagnostics.SetTriggeringCount; break; } + case RequestType.DeleteMonitoredItems: { counter = m_diagnostics.DeleteMonitoredItemsCount; break; } + case RequestType.CreateSubscription: { counter = m_diagnostics.CreateSubscriptionCount; break; } + case RequestType.ModifySubscription: { counter = m_diagnostics.ModifySubscriptionCount; break; } + case RequestType.SetPublishingMode: { counter = m_diagnostics.SetPublishingModeCount; break; } + case RequestType.Publish: { counter = m_diagnostics.PublishCount; break; } + case RequestType.Republish: { counter = m_diagnostics.RepublishCount; break; } + case RequestType.TransferSubscriptions: { counter = m_diagnostics.TransferSubscriptionsCount; break; } + case RequestType.DeleteSubscriptions: { counter = m_diagnostics.DeleteSubscriptionsCount; break; } + case RequestType.AddNodes: { counter = m_diagnostics.AddNodesCount; break; } + case RequestType.AddReferences: { counter = m_diagnostics.AddReferencesCount; break; } + case RequestType.DeleteNodes: { counter = m_diagnostics.DeleteNodesCount; break; } + case RequestType.DeleteReferences: { counter = m_diagnostics.DeleteReferencesCount; break; } + case RequestType.Browse: { counter = m_diagnostics.BrowseCount; break; } + case RequestType.BrowseNext: { counter = m_diagnostics.BrowseNextCount; break; } + case RequestType.TranslateBrowsePathsToNodeIds: { counter = m_diagnostics.TranslateBrowsePathsToNodeIdsCount; break; } + case RequestType.QueryFirst: { counter = m_diagnostics.QueryFirstCount; break; } + case RequestType.QueryNext: { counter = m_diagnostics.QueryNextCount; break; } + case RequestType.RegisterNodes: { counter = m_diagnostics.RegisterNodesCount; break; } + case RequestType.UnregisterNodes: { counter = m_diagnostics.UnregisterNodesCount; break; } } - + if (counter != null) { counter.TotalCount = counter.TotalCount + 1; diff --git a/SampleApplications/Samples/Client.Net4/Opc.Ua.SampleClient.Config.xml b/SampleApplications/Samples/Client.Net4/Opc.Ua.SampleClient.Config.xml index 1c32ac7307..3053e12411 100644 --- a/SampleApplications/Samples/Client.Net4/Opc.Ua.SampleClient.Config.xml +++ b/SampleApplications/Samples/Client.Net4/Opc.Ua.SampleClient.Config.xml @@ -83,6 +83,9 @@ Directory %CommonApplicationData%\OPC Foundation\CertificateStores\RejectedCertificates + + + false diff --git a/SampleApplications/Samples/Controls.Net4/Sessions/SessionTreeCtrl.cs b/SampleApplications/Samples/Controls.Net4/Sessions/SessionTreeCtrl.cs index 3f3f65c7ac..cba62bc92e 100644 --- a/SampleApplications/Samples/Controls.Net4/Sessions/SessionTreeCtrl.cs +++ b/SampleApplications/Samples/Controls.Net4/Sessions/SessionTreeCtrl.cs @@ -227,6 +227,7 @@ namespace Opc.Ua.Sample.Controls endpoint.Description, endpoint.Configuration, clientCertificate, + m_configuration.SecurityConfiguration.SendCertificateChain ? clientCertificateChain : null, m_messageContext); // create the session. diff --git a/SampleApplications/Samples/NetCoreConsoleClient/Program.cs b/SampleApplications/Samples/NetCoreConsoleClient/Program.cs index 226a4cd334..ffa5587b0e 100644 --- a/SampleApplications/Samples/NetCoreConsoleClient/Program.cs +++ b/SampleApplications/Samples/NetCoreConsoleClient/Program.cs @@ -93,7 +93,8 @@ namespace NetCoreConsoleClient if (extraArgs.Count == 0) { // use OPC UA .Net Sample server - endpointURL = "opc.tcp://localhost:51210/UA/SampleServer"; + //endpointURL = "opc.tcp://localhost:51210/UA/SampleServer"; + endpointURL = "opc.tcp://localhost:61210/UA/SampleClient"; } else { diff --git a/Stack/Opc.Ua.Core/Schema/ApplicationConfiguration.cs b/Stack/Opc.Ua.Core/Schema/ApplicationConfiguration.cs index 81971c39a8..a1db8b1b50 100644 --- a/Stack/Opc.Ua.Core/Schema/ApplicationConfiguration.cs +++ b/Stack/Opc.Ua.Core/Schema/ApplicationConfiguration.cs @@ -796,6 +796,7 @@ namespace Opc.Ua m_rejectSHA1SignedCertificates = true; m_minCertificateKeySize = CertificateFactory.defaultKeySize; m_addAppCertToTrustedStore = true; + m_sendCertificateChain = false; } /// @@ -943,6 +944,7 @@ namespace Opc.Ua get { return m_minCertificateKeySize; } set { m_minCertificateKeySize = value; } } + /// /// Gets or sets a value indicating whether the application cert should be copied to the trusted store. /// @@ -955,6 +957,19 @@ namespace Opc.Ua get { return m_addAppCertToTrustedStore; } set { m_addAppCertToTrustedStore = value; } } + + /// + /// Gets or sets a value indicating whether the application should send the complete certificate chain. + /// + /// + /// If set to true the complete certificate chain will be sent for CA signed certificates. + /// + [DataMember(IsRequired = false, EmitDefaultValue = false, Order = 9)] + public bool SendCertificateChain + { + get { return m_sendCertificateChain; } + set { m_sendCertificateChain = value; } + } #endregion #region Private Fields @@ -968,6 +983,7 @@ namespace Opc.Ua private bool m_rejectSHA1SignedCertificates; private ushort m_minCertificateKeySize; private bool m_addAppCertToTrustedStore; + private bool m_sendCertificateChain; #endregion } #endregion diff --git a/Stack/Opc.Ua.Core/Schema/ApplicationConfiguration.xsd b/Stack/Opc.Ua.Core/Schema/ApplicationConfiguration.xsd index 8f02d4fd44..d306fd7e85 100644 --- a/Stack/Opc.Ua.Core/Schema/ApplicationConfiguration.xsd +++ b/Stack/Opc.Ua.Core/Schema/ApplicationConfiguration.xsd @@ -48,6 +48,10 @@ + + + + diff --git a/Stack/Opc.Ua.Core/Stack/Client/SessionChannel.cs b/Stack/Opc.Ua.Core/Stack/Client/SessionChannel.cs index 71120eb2c4..d1c2034592 100644 --- a/Stack/Opc.Ua.Core/Stack/Client/SessionChannel.cs +++ b/Stack/Opc.Ua.Core/Stack/Client/SessionChannel.cs @@ -10,19 +10,13 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. */ -using System; using System.Security.Cryptography.X509Certificates; -using System.ServiceModel; -using System.ServiceModel.Channels; -using System.ServiceModel.Description; -using System.ServiceModel.Security; -using Opc.Ua.Bindings; namespace Opc.Ua { /// - /// A channel object used by clients to access a UA service. - /// + /// A channel object used by clients to access a UA service. + /// public partial class SessionChannel { #region Constructors @@ -41,6 +35,27 @@ namespace Opc.Ua EndpointConfiguration endpointConfiguration, X509Certificate2 clientCertificate, ServiceMessageContext messageContext) + { + return Create(configuration, description, endpointConfiguration, clientCertificate, null, messageContext); + } + + /// + /// Creates a new transport channel that supports the ISessionChannel service contract. + /// + /// The application configuration. + /// The description for the endpoint. + /// The configuration to use with the endpoint. + /// The client certificate. + /// The client certificate chain. + /// The message context to use when serializing the messages. + /// + public static ITransportChannel Create( + ApplicationConfiguration configuration, + EndpointDescription description, + EndpointConfiguration endpointConfiguration, + X509Certificate2 clientCertificate, + X509Certificate2Collection clientCertificateChain, + ServiceMessageContext messageContext) { // create a UA binary channel. ITransportChannel channel = CreateUaBinaryChannel( @@ -48,8 +63,9 @@ namespace Opc.Ua description, endpointConfiguration, clientCertificate, + clientCertificateChain, messageContext); - + return channel; } diff --git a/Stack/Opc.Ua.Core/Stack/Client/WcfChannelBase.cs b/Stack/Opc.Ua.Core/Stack/Client/WcfChannelBase.cs index 173954dd5a..3acdc4e32b 100644 --- a/Stack/Opc.Ua.Core/Stack/Client/WcfChannelBase.cs +++ b/Stack/Opc.Ua.Core/Stack/Client/WcfChannelBase.cs @@ -669,6 +669,27 @@ namespace Opc.Ua EndpointConfiguration endpointConfiguration, X509Certificate2 clientCertificate, ServiceMessageContext messageContext) + { + return CreateUaBinaryChannel(configuration, description, endpointConfiguration, clientCertificate, null, messageContext); + } + + /// + /// Creates a new UA-binary transport channel if requested. Null otherwise. + /// + /// The application configuration. + /// The description for the endpoint. + /// The configuration to use with the endpoint. + /// The client certificate. + /// The client certificate chain. + /// The message context to use when serializing the messages. + /// + public static ITransportChannel CreateUaBinaryChannel( + ApplicationConfiguration configuration, + EndpointDescription description, + EndpointConfiguration endpointConfiguration, + X509Certificate2 clientCertificate, + X509Certificate2Collection clientCertificateChain, + ServiceMessageContext messageContext) { bool useUaTcp = description.EndpointUrl.StartsWith(Utils.UriSchemeOpcTcp); bool useHttps = description.EndpointUrl.StartsWith(Utils.UriSchemeHttps); @@ -693,7 +714,7 @@ namespace Opc.Ua if (!useUaTcp && !useHttps) { throw ServiceResultException.Create( - StatusCodes.BadServiceUnsupported, + StatusCodes.BadProtocolVersionUnsupported, "Unsupported transport profile\r\n"); } @@ -706,6 +727,7 @@ namespace Opc.Ua settings.Description = description; settings.Configuration = endpointConfiguration; settings.ClientCertificate = clientCertificate; + settings.ClientCertificateChain = clientCertificateChain; if (description.ServerCertificate != null && description.ServerCertificate.Length > 0) { diff --git a/Stack/Opc.Ua.Core/Stack/Server/ServerBase.cs b/Stack/Opc.Ua.Core/Stack/Server/ServerBase.cs index 5f4631c068..c019a144d7 100644 --- a/Stack/Opc.Ua.Core/Stack/Server/ServerBase.cs +++ b/Stack/Opc.Ua.Core/Stack/Server/ServerBase.cs @@ -528,6 +528,22 @@ namespace Opc.Ua } } + /// + /// Gets the instance certificate chain. + /// + protected X509Certificate2Collection InstanceCertificateChain + { + get + { + return m_instanceCertificateChain; + } + + private set + { + m_instanceCertificateChain = value; + } + } + /// /// The non-configurable properties for the server. /// @@ -737,6 +753,19 @@ namespace Opc.Ua if (requireEncryption) { description.ServerCertificate = InstanceCertificate.RawData; + + // check if complete chain should be sent. + if (configuration.SecurityConfiguration.SendCertificateChain && InstanceCertificateChain != null && InstanceCertificateChain.Count >0) + { + List serverCertificateChain = new List(); + + for (int i = 0; i < InstanceCertificateChain.Count; i++) + { + serverCertificateChain.AddRange(InstanceCertificateChain[i].RawData); + } + + description.ServerCertificate = serverCertificateChain.ToArray(); + } } endpoints.Add( description ); @@ -749,10 +778,15 @@ namespace Opc.Ua settings.Descriptions = endpoints; settings.Configuration = endpointConfiguration; - settings.ServerCertificate = this.InstanceCertificate; settings.CertificateValidator = configuration.CertificateValidator.GetChannelValidator(); settings.NamespaceUris = this.MessageContext.NamespaceUris; settings.Factory = this.MessageContext.Factory; + settings.ServerCertificate = this.InstanceCertificate; + + if (configuration.SecurityConfiguration.SendCertificateChain) + { + settings.ServerCertificateChain = this.InstanceCertificateChain; + } ITransportListener listener = new Opc.Ua.Bindings.UaTcpChannelListener(); @@ -855,6 +889,19 @@ namespace Opc.Ua if (InstanceCertificate != null) { description.ServerCertificate = InstanceCertificate.RawData; + + // check if complete chain should be sent. + if (configuration.SecurityConfiguration.SendCertificateChain && InstanceCertificateChain != null && InstanceCertificateChain.Count > 0) + { + List serverCertificateChain = new List(); + + for (int i = 0; i < InstanceCertificateChain.Count; i++) + { + serverCertificateChain.AddRange(InstanceCertificateChain[i].RawData); + } + + description.ServerCertificate = serverCertificateChain.ToArray(); + } } description.SecurityMode = bestPolicy.SecurityMode; @@ -1324,6 +1371,16 @@ namespace Opc.Ua "Server does not have access to the private key for the instance certificate."); } + // load certificate chain. + InstanceCertificateChain = new X509Certificate2Collection(InstanceCertificate); + List issuers = new List(); + configuration.CertificateValidator.GetIssuers(InstanceCertificateChain, issuers).Wait(); + + for (int i = 0; i < issuers.Count; i++) + { + InstanceCertificateChain.Add(issuers[i].Certificate); + } + // use the message context from the configuration to ensure the channels are using the same one. MessageContext = configuration.CreateMessageContext(); @@ -1487,6 +1544,7 @@ namespace Opc.Ua private object m_serverError; private object m_certificateValidator; private object m_instanceCertificate; + private X509Certificate2Collection m_instanceCertificateChain; private object m_serverProperties; private object m_configuration; private object m_serverDescription; diff --git a/Stack/Opc.Ua.Core/Stack/Tcp/TcpListener.cs b/Stack/Opc.Ua.Core/Stack/Tcp/TcpListener.cs index 83e88ca277..b120fe077f 100644 --- a/Stack/Opc.Ua.Core/Stack/Tcp/TcpListener.cs +++ b/Stack/Opc.Ua.Core/Stack/Tcp/TcpListener.cs @@ -344,6 +344,7 @@ namespace Opc.Ua.Bindings m_bufferManager, m_quotas, m_serverCertificate, + m_serverCertificateChain, m_descriptions); if (m_callback != null) diff --git a/Stack/Opc.Ua.Core/Stack/Tcp/TcpServerChannel.cs b/Stack/Opc.Ua.Core/Stack/Tcp/TcpServerChannel.cs index 59cb9270fd..2b0743dbc4 100644 --- a/Stack/Opc.Ua.Core/Stack/Tcp/TcpServerChannel.cs +++ b/Stack/Opc.Ua.Core/Stack/Tcp/TcpServerChannel.cs @@ -26,7 +26,6 @@ namespace Opc.Ua.Bindings /// public class TcpServerChannel : UaSCUaBinaryChannel { - #region Constructors /// /// Attaches the object to an existing socket. /// @@ -38,7 +37,24 @@ namespace Opc.Ua.Bindings X509Certificate2 serverCertificate, EndpointDescriptionCollection endpoints) : - base(contextId, bufferManager, quotas, serverCertificate, endpoints, MessageSecurityMode.None, SecurityPolicies.None) + this(contextId, listener, bufferManager, quotas, serverCertificate, null, endpoints) + { + } + + #region Constructors + /// + /// Attaches the object to an existing socket. + /// + public TcpServerChannel( + string contextId, + UaTcpChannelListener listener, + BufferManager bufferManager, + ChannelQuotas quotas, + X509Certificate2 serverCertificate, + X509Certificate2Collection serverCertificateChain, + EndpointDescriptionCollection endpoints) + : + base(contextId, bufferManager, quotas, serverCertificate, serverCertificateChain, endpoints, MessageSecurityMode.None, SecurityPolicies.None) { m_listener = listener; m_queuedResponses = new SortedDictionary(); @@ -135,7 +151,7 @@ namespace Opc.Ua.Bindings // need to assign a new token id. token.TokenId = GetNewTokenId(); - // put channel back in open state. + // put channel back in open state. ActivateToken(token); State = TcpChannelState.Open; @@ -735,7 +751,7 @@ namespace Opc.Ua.Bindings /// private bool ProcessOpenSecureChannelRequest(uint messageType, ArraySegment messageChunk) { - // validate the channel state. + // validate the channel state. if (State != TcpChannelState.Opening && State != TcpChannelState.Open) { ForceChannelFault(StatusCodes.BadTcpMessageTypeInvalid, "Client sent an unexpected OpenSecureChannel message."); @@ -984,6 +1000,7 @@ namespace Opc.Ua.Bindings TcpMessageType.Open, requestId, ServerCertificate, + ServerCertificateChain, ClientCertificate, new ArraySegment(buffer, 0, buffer.Length)); diff --git a/Stack/Opc.Ua.Core/Stack/Tcp/UaSCBinaryChannel.Asymmetric.cs b/Stack/Opc.Ua.Core/Stack/Tcp/UaSCBinaryChannel.Asymmetric.cs index 8d5dac16a1..fc55e80664 100644 --- a/Stack/Opc.Ua.Core/Stack/Tcp/UaSCBinaryChannel.Asymmetric.cs +++ b/Stack/Opc.Ua.Core/Stack/Tcp/UaSCBinaryChannel.Asymmetric.cs @@ -15,6 +15,7 @@ using System.Text; using System.IO; using System.Security.Cryptography.X509Certificates; using System.Security.Cryptography; +using System.Collections.Generic; namespace Opc.Ua.Bindings { @@ -56,6 +57,15 @@ namespace Opc.Ua.Bindings get { return m_serverCertificate; } } + /// + /// The server certificate chain. + /// + protected X509Certificate2Collection ServerCertificateChain + { + get { return m_serverCertificateChain; } + set { m_serverCertificateChain = value; } + } + /// /// The security mode used with the channel. /// @@ -92,6 +102,14 @@ namespace Opc.Ua.Bindings set { m_clientCertificate = value; } } + /// + /// The client certificate chain. + /// + internal X509Certificate2Collection ClientCertificateChain + { + get { return m_clientCertificateChain; } + set { m_clientCertificateChain = value; } + } /// /// Creates a new nonce. @@ -399,8 +417,35 @@ namespace Opc.Ua.Bindings string securityPolicyUri, X509Certificate2 senderCertificate, X509Certificate2 receiverCertificate) + { + int senderCertificateSize = 0; + + WriteAsymmetricMessageHeader( + encoder, + messageType, + secureChannelId, + securityPolicyUri, + senderCertificate, + null, + receiverCertificate, + out senderCertificateSize); + } + + /// + /// Writes the asymmetric security header to the buffer. + /// + protected void WriteAsymmetricMessageHeader( + BinaryEncoder encoder, + uint messageType, + uint secureChannelId, + string securityPolicyUri, + X509Certificate2 senderCertificate, + X509Certificate2Collection senderCertificateChain, + X509Certificate2 receiverCertificate, + out int senderCertificateSize) { int start = encoder.Position; + senderCertificateSize = 0; encoder.WriteUInt32(null, messageType); encoder.WriteUInt32(null, 0); @@ -409,7 +454,36 @@ namespace Opc.Ua.Bindings if (SecurityMode != MessageSecurityMode.None) { - encoder.WriteByteString(null, senderCertificate.RawData); + if (senderCertificateChain != null && senderCertificateChain.Count >0) + { + X509Certificate2 currentCertificate = senderCertificateChain[0]; + int maxSenderCertificateSize = GetMaxSenderCertificateSize(currentCertificate, securityPolicyUri); + List senderCertificateList = new List(currentCertificate.RawData); + senderCertificateSize = currentCertificate.RawData.Length; + + for (int i = 1; i < senderCertificateChain.Count; i++) + { + currentCertificate = senderCertificateChain[i]; + senderCertificateSize += currentCertificate.RawData.Length; + + if (senderCertificateSize < maxSenderCertificateSize) + { + senderCertificateList.AddRange(currentCertificate.RawData); + } + else + { + senderCertificateSize -= currentCertificate.RawData.Length; + break; + } + } + + encoder.WriteByteString(null, senderCertificateList.ToArray()); + } + else + { + encoder.WriteByteString(null, senderCertificate.RawData); + } + encoder.WriteByteString(null, GetThumbprintBytes(receiverCertificate.Thumbprint)); } else @@ -452,7 +526,7 @@ namespace Opc.Ua.Bindings } /// - /// Sends a OpenSecureChannel response. + /// Sends a OpenSecureChannel request. /// protected BufferCollection WriteAsymmetricMessage( uint messageType, @@ -460,6 +534,20 @@ namespace Opc.Ua.Bindings X509Certificate2 senderCertificate, X509Certificate2 receiverCertificate, ArraySegment messageBody) + { + return WriteAsymmetricMessage(messageType, requestId, senderCertificate, null, receiverCertificate, messageBody); + } + + /// + /// Sends a OpenSecureChannel request. + /// + protected BufferCollection WriteAsymmetricMessage( + uint messageType, + uint requestId, + X509Certificate2 senderCertificate, + X509Certificate2Collection senderCertificateChain, + X509Certificate2 receiverCertificate, + ArraySegment messageBody) { bool success = false; BufferCollection chunksToSend = new BufferCollection(); @@ -468,18 +556,39 @@ namespace Opc.Ua.Bindings try { - int headerSize = GetAsymmetricHeaderSize(SecurityPolicyUri, senderCertificate); - int signatureSize = GetAsymmetricSignatureSize(senderCertificate); - BinaryEncoder encoder = new BinaryEncoder(buffer, 0, SendBufferSize, Quotas.MessageContext); + int headerSize = 0; - WriteAsymmetricMessageHeader( - encoder, - messageType | TcpMessageType.Intermediate, - ChannelId, - SecurityPolicyUri, - senderCertificate, - receiverCertificate); + if (senderCertificateChain != null && senderCertificateChain.Count >0) + { + int senderCertificateSize = 0; + + WriteAsymmetricMessageHeader( + encoder, + messageType | TcpMessageType.Intermediate, + ChannelId, + SecurityPolicyUri, + senderCertificate, + senderCertificateChain, + receiverCertificate, + out senderCertificateSize); + + headerSize = GetAsymmetricHeaderSize(SecurityPolicyUri, senderCertificate, senderCertificateSize); + } + else + { + WriteAsymmetricMessageHeader( + encoder, + messageType | TcpMessageType.Intermediate, + ChannelId, + SecurityPolicyUri, + senderCertificate, + receiverCertificate); + + headerSize = GetAsymmetricHeaderSize(SecurityPolicyUri, senderCertificate); + } + + int signatureSize = GetAsymmetricSignatureSize(senderCertificate); // save the header. ArraySegment header = new ArraySegment(buffer, 0, headerSize); @@ -637,11 +746,11 @@ namespace Opc.Ua.Bindings protected void ReadAsymmetricMessageHeader( BinaryDecoder decoder, X509Certificate2 receiverCertificate, - out uint secureChannelId, - out X509Certificate2 senderCertificate, + out uint secureChannelId, + out X509Certificate2Collection senderCertificateChain, out string securityPolicyUri) { - senderCertificate = null; + senderCertificateChain = null; uint messageType = decoder.ReadUInt32(null); uint messageSize = decoder.ReadUInt32(null); @@ -665,14 +774,14 @@ namespace Opc.Ua.Bindings "The asymmetric security header could not be parsed."); } - // verify sender certificate. + // verify sender certificate chain. if (certificateData != null && certificateData.Length > 0) { - senderCertificate = CertificateFactory.Create(certificateData, true); + senderCertificateChain = Utils.ParseCertificateChainBlob(certificateData); try { - string thumbprint = senderCertificate.Thumbprint; + string thumbprint = senderCertificateChain[0].Thumbprint; if (thumbprint == null) { @@ -790,19 +899,38 @@ namespace Opc.Ua.Bindings BinaryDecoder decoder = new BinaryDecoder(buffer.Array, buffer.Offset, buffer.Count, Quotas.MessageContext); string securityPolicyUri = null; + X509Certificate2Collection senderCertificateChain; // parse the security header. ReadAsymmetricMessageHeader( decoder, receiverCertificate, out channelId, - out senderCertificate, + out senderCertificateChain, out securityPolicyUri); + if (senderCertificateChain != null && senderCertificateChain.Count > 0) + { + senderCertificate = senderCertificateChain[0]; + } + else + { + senderCertificate = null; + } + // validate the sender certificate. if (senderCertificate != null && Quotas.CertificateValidator != null && securityPolicyUri != SecurityPolicies.None) { - Quotas.CertificateValidator.Validate(senderCertificate); + CertificateValidator certificateValidator = Quotas.CertificateValidator as CertificateValidator; + + if (certificateValidator != null) + { + certificateValidator.Validate(senderCertificateChain); + } + else + { + Quotas.CertificateValidator.Validate(senderCertificate); + } } // check if this is the first open secure channel request. @@ -1095,7 +1223,9 @@ namespace Opc.Ua.Bindings private bool m_discoveryOnly; private EndpointDescription m_selectedEndpoint; private X509Certificate2 m_serverCertificate; + private X509Certificate2Collection m_serverCertificateChain; private X509Certificate2 m_clientCertificate; + private X509Certificate2Collection m_clientCertificateChain; private bool m_uninitialized; #endregion } diff --git a/Stack/Opc.Ua.Core/Stack/Tcp/UaSCBinaryChannel.cs b/Stack/Opc.Ua.Core/Stack/Tcp/UaSCBinaryChannel.cs index 4adbc6281e..0392edcf52 100644 --- a/Stack/Opc.Ua.Core/Stack/Tcp/UaSCBinaryChannel.cs +++ b/Stack/Opc.Ua.Core/Stack/Tcp/UaSCBinaryChannel.cs @@ -24,6 +24,7 @@ namespace Opc.Ua.Bindings public partial class UaSCUaBinaryChannel : IMessageSink, IDisposable { #region Constructors + /// /// Attaches the object to an existing socket. /// @@ -35,6 +36,22 @@ namespace Opc.Ua.Bindings EndpointDescriptionCollection endpoints, MessageSecurityMode securityMode, string securityPolicyUri) + : + this(contextId, bufferManager, quotas, serverCertificate, null, endpoints, securityMode, securityPolicyUri) + { } + + /// + /// Attaches the object to an existing socket. + /// + public UaSCUaBinaryChannel( + string contextId, + BufferManager bufferManager, + ChannelQuotas quotas, + X509Certificate2 serverCertificate, + X509Certificate2Collection serverCertificateChain, + EndpointDescriptionCollection endpoints, + MessageSecurityMode securityMode, + string securityPolicyUri) { if (bufferManager == null) throw new ArgumentNullException("bufferManager"); if (quotas == null) throw new ArgumentNullException("quotas"); @@ -75,6 +92,7 @@ namespace Opc.Ua.Bindings m_bufferManager = bufferManager; m_quotas = quotas; m_serverCertificate = serverCertificate; + m_serverCertificateChain = serverCertificateChain; m_endpoints = endpoints; m_securityMode = securityMode; m_securityPolicyUri = securityPolicyUri; diff --git a/Stack/Opc.Ua.Core/Stack/Tcp/UaSCBinaryClientChannel.cs b/Stack/Opc.Ua.Core/Stack/Tcp/UaSCBinaryClientChannel.cs index 9bd0b51c62..a5c53caefa 100644 --- a/Stack/Opc.Ua.Core/Stack/Tcp/UaSCBinaryClientChannel.cs +++ b/Stack/Opc.Ua.Core/Stack/Tcp/UaSCBinaryClientChannel.cs @@ -39,6 +39,23 @@ namespace Opc.Ua.Bindings X509Certificate2 clientCertificate, X509Certificate2 serverCertificate, EndpointDescription endpoint) + : + this(contextId, bufferManager, socketFactory, quotas, clientCertificate, null, serverCertificate, endpoint) + { + } + + /// + /// Creates a channel for for a client. + /// + public UaSCUaBinaryClientChannel( + string contextId, + BufferManager bufferManager, + IMessageSocketFactory socketFactory, + ChannelQuotas quotas, + X509Certificate2 clientCertificate, + X509Certificate2Collection clientCertificateChain, + X509Certificate2 serverCertificate, + EndpointDescription endpoint) : base( contextId, @@ -61,6 +78,7 @@ namespace Opc.Ua.Bindings } ClientCertificate = clientCertificate; + ClientCertificateChain = clientCertificateChain; } m_requests = new Dictionary(); @@ -471,7 +489,7 @@ namespace Opc.Ua.Bindings request.ClientNonce = token.ClientNonce; request.RequestedLifetime = (uint)Quotas.SecurityTokenLifetime; - // encode the request. + // encode the request. byte[] buffer = BinaryEncoder.EncodeMessage(request, Quotas.MessageContext); // write the asymmetric message. @@ -479,6 +497,7 @@ namespace Opc.Ua.Bindings TcpMessageType.Open, m_handshakeOperation.RequestId, ClientCertificate, + ClientCertificateChain, ServerCertificate, new ArraySegment(buffer, 0, buffer.Length)); diff --git a/Stack/Opc.Ua.Core/Stack/Tcp/UaSCBinaryTransportChannel.cs b/Stack/Opc.Ua.Core/Stack/Tcp/UaSCBinaryTransportChannel.cs index a3adef373b..300f5469b6 100644 --- a/Stack/Opc.Ua.Core/Stack/Tcp/UaSCBinaryTransportChannel.cs +++ b/Stack/Opc.Ua.Core/Stack/Tcp/UaSCBinaryTransportChannel.cs @@ -144,6 +144,7 @@ namespace Opc.Ua.Bindings m_messageSocketFactory, m_quotas, m_settings.ClientCertificate, + m_settings.ClientCertificateChain, m_settings.ServerCertificate, m_settings.Description); @@ -389,6 +390,7 @@ namespace Opc.Ua.Bindings m_messageSocketFactory, m_quotas, m_settings.ClientCertificate, + m_settings.ClientCertificateChain, m_settings.ServerCertificate, m_settings.Description); } diff --git a/Stack/Opc.Ua.Core/Stack/Types/UserIdentityToken.cs b/Stack/Opc.Ua.Core/Stack/Types/UserIdentityToken.cs index b0ce33c3eb..4f29998c0d 100644 --- a/Stack/Opc.Ua.Core/Stack/Types/UserIdentityToken.cs +++ b/Stack/Opc.Ua.Core/Stack/Types/UserIdentityToken.cs @@ -213,7 +213,7 @@ namespace Opc.Ua if (certificate == null) { certificate = CertificateFactory.Create(m_certificateData, true); - } + } bool valid = SecurityPolicies.Verify( certificate, diff --git a/Stack/Opc.Ua.Core/Types/Utils/Utils.cs b/Stack/Opc.Ua.Core/Types/Utils/Utils.cs index bac02233a1..b521e1f243 100644 --- a/Stack/Opc.Ua.Core/Types/Utils/Utils.cs +++ b/Stack/Opc.Ua.Core/Types/Utils/Utils.cs @@ -2585,7 +2585,6 @@ namespace Opc.Ua certificatesBytes.RemoveRange(0, certificate.RawData.Length); } - return certificateChain; }