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.
///