diff --git a/SampleApplications/SDK/Opc.Ua.Client/Session.cs b/SampleApplications/SDK/Opc.Ua.Client/Session.cs index 274b84e50..0e579a58f 100644 --- a/SampleApplications/SDK/Opc.Ua.Client/Session.cs +++ b/SampleApplications/SDK/Opc.Ua.Client/Session.cs @@ -40,6 +40,7 @@ using System.Xml; using System.Threading.Tasks; using System.Reflection; +using Opc.Ua.Bindings; namespace Opc.Ua.Client { @@ -311,7 +312,7 @@ private void ValidateClientConfiguration(ApplicationConfiguration configuration) /// /// Validates the server nonce and security parameters of user identity. /// - private void ValidateServerNonce(IUserIdentity identity, byte[] serverNonce, string securityPolicyUri) + private void ValidateServerNonce(IUserIdentity identity, byte[] serverNonce, string securityPolicyUri, byte[] previousServerNonce) { // skip validation if server nonce is not used for encryption. if (String.IsNullOrEmpty(securityPolicyUri) || securityPolicyUri == SecurityPolicies.None) @@ -326,6 +327,15 @@ private void ValidateServerNonce(IUserIdentity identity, byte[] serverNonce, str { throw ServiceResultException.Create(StatusCodes.BadNonceInvalid, "Server nonce is not the correct length or not random enough."); } + + // check that new nonce is different from the previously returned server nonce. + if (previousServerNonce!= null && Utils.CompareNonce(serverNonce, previousServerNonce)) + { + if (!m_configuration.SecurityConfiguration.SuppressNonceValidationErrors) + { + throw ServiceResultException.Create(StatusCodes.BadNonceInvalid, "Server nonce is equal with previously returned nonce."); + } + } } } @@ -1082,7 +1092,7 @@ public void Reconnect() } // validate server nonce and security parameters for user identity. - ValidateServerNonce(m_identity, m_serverNonce, securityPolicyUri); + ValidateServerNonce(m_identity, m_serverNonce, securityPolicyUri, m_previousServerNonce); // sign data with user token. UserIdentityToken identityToken = m_identity.GetIdentityToken(); @@ -1150,6 +1160,7 @@ public void Reconnect() lock (SyncRoot) { Utils.Trace("Session RECONNECT completed successfully."); + m_previousServerNonce = m_serverNonce; m_serverNonce = serverNonce; m_reconnecting = false; publishCount = m_subscriptions.Count; @@ -2571,8 +2582,15 @@ public ReferenceDescriptionCollection FetchReferences(NodeId nodeId) securityPolicyUri = m_endpoint.Description.SecurityPolicyUri; } + byte[] previousServerNonce = null; + + if (TransportChannel.CurrentToken!= null) + { + previousServerNonce = TransportChannel.CurrentToken.ServerNonce; + } + // validate server nonce and security parameters for user identity. - ValidateServerNonce(identity, serverNonce, securityPolicyUri); + ValidateServerNonce(identity, serverNonce, securityPolicyUri, previousServerNonce); // sign data with user token. SignatureData userTokenSignature = identityToken.Sign(dataToSign, securityPolicyUri); @@ -2625,6 +2643,7 @@ public ReferenceDescriptionCollection FetchReferences(NodeId nodeId) // save nonces. m_sessionName = sessionName; m_identity = identity; + m_previousServerNonce = previousServerNonce; m_serverNonce = serverNonce; m_serverCertificate = serverCertificate; @@ -2735,7 +2754,7 @@ public void UpdateSession(IUserIdentity identity, StringCollection preferredLoca } // validate server nonce and security parameters for user identity. - ValidateServerNonce(identity, serverNonce, securityPolicyUri); + ValidateServerNonce(identity, serverNonce, securityPolicyUri, m_previousServerNonce); // sign data with user token. identityToken = identity.GetIdentityToken(); @@ -2771,6 +2790,7 @@ public void UpdateSession(IUserIdentity identity, StringCollection preferredLoca m_identity = identity; } + m_previousServerNonce = m_serverNonce; m_serverNonce = serverNonce; m_preferredLocales = preferredLocales; @@ -4390,6 +4410,7 @@ private void OnRaisePublishNotification(object state) private object m_handle; private IUserIdentity m_identity; private byte[] m_serverNonce; + private byte[] m_previousServerNonce; private X509Certificate2 m_serverCertificate; private long m_publishCounter; private DateTime m_lastKeepAliveTime; diff --git a/Stack/Opc.Ua.Core/Schema/ApplicationConfiguration.cs b/Stack/Opc.Ua.Core/Schema/ApplicationConfiguration.cs index a210f2467..241270410 100644 --- a/Stack/Opc.Ua.Core/Schema/ApplicationConfiguration.cs +++ b/Stack/Opc.Ua.Core/Schema/ApplicationConfiguration.cs @@ -1037,6 +1037,19 @@ public CertificateTrustList TrustedHttpsCertificates } } } + + /// + /// Gets or sets a value indicating whether the server nonce validation errors should be suppressed. + /// + /// + /// If set to true the server nonce validation errors are suppressed. + /// + [DataMember(IsRequired = false, EmitDefaultValue = false, Order = 19)] + public bool SuppressNonceValidationErrors + { + get { return m_suppressNonceValidationErrors; } + set { m_suppressNonceValidationErrors = value; } + } #endregion #region Private Fields @@ -1056,6 +1069,7 @@ public CertificateTrustList TrustedHttpsCertificates private ushort m_minCertificateKeySize; private bool m_addAppCertToTrustedStore; private bool m_sendCertificateChain; + private bool m_suppressNonceValidationErrors; #endregion } #endregion diff --git a/Stack/Opc.Ua.Core/Schema/ApplicationConfiguration.xsd b/Stack/Opc.Ua.Core/Schema/ApplicationConfiguration.xsd index 9796c0fad..b74b82420 100644 --- a/Stack/Opc.Ua.Core/Schema/ApplicationConfiguration.xsd +++ b/Stack/Opc.Ua.Core/Schema/ApplicationConfiguration.xsd @@ -57,6 +57,7 @@ + diff --git a/Stack/Opc.Ua.Core/Stack/Client/WcfChannelBase.cs b/Stack/Opc.Ua.Core/Stack/Client/WcfChannelBase.cs index 350ef14c1..462ae1b8f 100644 --- a/Stack/Opc.Ua.Core/Stack/Client/WcfChannelBase.cs +++ b/Stack/Opc.Ua.Core/Stack/Client/WcfChannelBase.cs @@ -208,6 +208,14 @@ public ServiceMessageContext MessageContext } } + /// + /// Gets the the channel's current security token. + /// + public ChannelToken CurrentToken + { + get { return null; } + } + /// /// Gets or sets the default timeout for requests send via the channel. /// diff --git a/Stack/Opc.Ua.Core/Stack/Https/HttpsTransportChannel.cs b/Stack/Opc.Ua.Core/Stack/Https/HttpsTransportChannel.cs index 4f197e4eb..0bbd96853 100644 --- a/Stack/Opc.Ua.Core/Stack/Https/HttpsTransportChannel.cs +++ b/Stack/Opc.Ua.Core/Stack/Https/HttpsTransportChannel.cs @@ -52,6 +52,11 @@ public ServiceMessageContext MessageContext get { return m_quotas.MessageContext; } } + public ChannelToken CurrentToken + { + get { return null; } + } + public int OperationTimeout { get { return m_operationTimeout; } diff --git a/Stack/Opc.Ua.Core/Stack/Tcp/UaSCBinaryChannel.Symmetric.cs b/Stack/Opc.Ua.Core/Stack/Tcp/UaSCBinaryChannel.Symmetric.cs index c243bb63f..5391dc4e7 100644 --- a/Stack/Opc.Ua.Core/Stack/Tcp/UaSCBinaryChannel.Symmetric.cs +++ b/Stack/Opc.Ua.Core/Stack/Tcp/UaSCBinaryChannel.Symmetric.cs @@ -23,7 +23,7 @@ public partial class UaSCUaBinaryChannel /// /// Returns the current security token. /// - protected ChannelToken CurrentToken => m_currentToken; + protected internal ChannelToken CurrentToken => m_currentToken; /// /// Returns the current security token. diff --git a/Stack/Opc.Ua.Core/Stack/Tcp/UaSCBinaryTransportChannel.cs b/Stack/Opc.Ua.Core/Stack/Tcp/UaSCBinaryTransportChannel.cs index 69fbb562e..ca646791c 100644 --- a/Stack/Opc.Ua.Core/Stack/Tcp/UaSCBinaryTransportChannel.cs +++ b/Stack/Opc.Ua.Core/Stack/Tcp/UaSCBinaryTransportChannel.cs @@ -81,6 +81,14 @@ public IMessageSocket Socket /// public ServiceMessageContext MessageContext => m_quotas.MessageContext; + /// + /// Gets the the channel's current security token. + /// + public ChannelToken CurrentToken + { + get { lock (m_lock) { return m_channel?.CurrentToken; } } + } + /// /// Gets or sets the default timeout for requests send via the channel. /// diff --git a/Stack/Opc.Ua.Core/Stack/Transport/ITransportChannel.cs b/Stack/Opc.Ua.Core/Stack/Transport/ITransportChannel.cs index 224c37b9c..803dceba8 100644 --- a/Stack/Opc.Ua.Core/Stack/Transport/ITransportChannel.cs +++ b/Stack/Opc.Ua.Core/Stack/Transport/ITransportChannel.cs @@ -13,6 +13,7 @@ using System; using System.Collections.Generic; using System.Text; +using Opc.Ua.Bindings; namespace Opc.Ua { @@ -54,6 +55,11 @@ public interface ITransportChannel : IDisposable /// ServiceMessageContext MessageContext { get; } + /// + /// Gets the the channel's current security token. + /// + ChannelToken CurrentToken { get; } + /// /// Gets or sets the default timeout for requests send via the channel. ///