Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 16 additions & 6 deletions Libraries/Opc.Ua.Client/Session/Session.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1288,7 +1288,7 @@ await m_configuration
identityToken.Encrypt(
serverCertificate,
serverNonce,
m_userTokenSecurityPolicyUri,
m_endpoint.Description.SecurityPolicyUri,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@opcfoundation-org Is this correct? On Client side to encrypt with endpoints security policy?

MessageContext,
m_eccServerEphemeralKey,
m_instanceCertificate,
Expand Down Expand Up @@ -1440,7 +1440,8 @@ public async Task UpdateSessionAsync(
m_endpoint.Description.FindUserTokenPolicy(
identity.TokenType,
identity.IssuedTokenType,
securityPolicyUri)
securityPolicyUri,
[.. m_configuration.SecurityConfiguration.SupportedSecurityPolicies])
?? throw ServiceResultException.Create(
StatusCodes.BadIdentityTokenRejected,
"Endpoint does not support the user identity type provided.");
Expand Down Expand Up @@ -2321,7 +2322,8 @@ public async Task ReconnectAsync(
UserTokenPolicy identityPolicy = endpoint.FindUserTokenPolicy(
m_identity.TokenType,
m_identity.IssuedTokenType,
endpoint.SecurityPolicyUri);
endpoint.SecurityPolicyUri,
[.. m_configuration.SecurityConfiguration.SupportedSecurityPolicies]);

if (identityPolicy == null)
{
Expand Down Expand Up @@ -3779,15 +3781,16 @@ private void OpenValidateIdentity(

// check that the user identity is supported by the endpoint.
identityPolicy = m_endpoint.Description
.FindUserTokenPolicy(identityToken.PolicyId, securityPolicyUri);
.FindUserTokenPolicy(identityToken.PolicyId);

if (identityPolicy == null)
{
// try looking up by TokenType if the policy id was not found.
identityPolicy = m_endpoint.Description.FindUserTokenPolicy(
identity.TokenType,
identity.IssuedTokenType,
securityPolicyUri);
securityPolicyUri,
[.. m_configuration.SecurityConfiguration.SupportedSecurityPolicies]);

if (identityPolicy == null)
{
Expand Down Expand Up @@ -4767,10 +4770,17 @@ private RequestHeader CreateRequestHeaderPerUserTokenPolicy(
string? endpointSecurityPolicyUri)
{
var requestHeader = new RequestHeader();

if (!EccUtils.IsEccPolicy(endpointSecurityPolicyUri))
{
// No need to add additional parameters if not using an ECC policy
return requestHeader;
}

string? userTokenSecurityPolicyUri = identityTokenSecurityPolicyUri;
if (string.IsNullOrEmpty(userTokenSecurityPolicyUri))
{
userTokenSecurityPolicyUri = m_endpoint.Description.SecurityPolicyUri;
userTokenSecurityPolicyUri = endpointSecurityPolicyUri;
}
m_userTokenSecurityPolicyUri = userTokenSecurityPolicyUri;

Expand Down
6 changes: 6 additions & 0 deletions Libraries/Opc.Ua.Server/Server/IServerInternal.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Opc.Ua.Security.Certificates;

namespace Opc.Ua.Server
{
Expand Down Expand Up @@ -172,6 +173,11 @@ public interface IServerInternal : IAuditEventServer, IDisposable
/// </summary>
ITelemetryContext Telemetry { get; }

/// <summary>
/// Provides access to the certificate types supported by the server.
/// </summary>
CertificateTypesProvider InstanceCertificateProvider { get; }

/// <summary>
/// Whether the server is currently running.
/// </summary>
Expand Down
7 changes: 6 additions & 1 deletion Libraries/Opc.Ua.Server/Server/ServerInternalData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ public ServerInternalData(
m_serverDescription = serverDescription;
m_configuration = configuration;
MessageContext = messageContext;

InstanceCertificateProvider = instanceCertificateProvider;
m_endpointAddresses = [];

foreach (string baseAddresses in m_configuration.ServerConfiguration.BaseAddresses)
Expand Down Expand Up @@ -246,6 +246,11 @@ public void SetModellingRulesManager(ModellingRulesManager modellingRulesManager
/// <value>The message context.</value>
public IServiceMessageContext MessageContext { get; }

/// <summary>
/// Provides access to the certificate types supported by the server.
/// </summary>
public CertificateTypesProvider InstanceCertificateProvider { get; }

/// <summary>
/// The default system context for the server.
/// </summary>
Expand Down
31 changes: 13 additions & 18 deletions Libraries/Opc.Ua.Server/Session/Session.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
using System.Security.Cryptography.X509Certificates;
using System.Threading;
using Microsoft.Extensions.Logging;
using Opc.Ua.Security.Certificates;

namespace Opc.Ua.Server
{
Expand Down Expand Up @@ -90,6 +91,7 @@ public Session(
m_serverNonce = serverNonce;
m_sessionName = sessionName;
m_serverCertificate = serverCertificate;
m_certificateTypesProvider = server.InstanceCertificateProvider;
ClientCertificate = clientCertificate;

m_clientIssuerCertificates = clientCertificateChain;
Expand Down Expand Up @@ -314,7 +316,7 @@ public virtual EphemeralKeyType GetNewEccKey()

key.Signature = EccUtils.Sign(
new ArraySegment<byte>(key.PublicKey),
m_serverCertificate,
m_certificateTypesProvider.GetInstanceCertificate(m_eccUserTokenSecurityPolicyUri),
m_eccUserTokenSecurityPolicyUri);

return key;
Expand Down Expand Up @@ -883,8 +885,7 @@ private UserIdentityToken ValidateUserIdentityToken(
}

policy = EndpointDescription.FindUserTokenPolicy(
newToken.PolicyId,
EndpointDescription.SecurityPolicyUri);
newToken.PolicyId);
if (policy == null)
{
throw ServiceResultException.Create(
Expand Down Expand Up @@ -951,8 +952,7 @@ private UserIdentityToken ValidateUserIdentityToken(

// find the user token policy.
policy = EndpointDescription.FindUserTokenPolicy(
token.PolicyId,
EndpointDescription.SecurityPolicyUri);
token.PolicyId);

if (policy == null)
{
Expand All @@ -977,27 +977,21 @@ private UserIdentityToken ValidateUserIdentityToken(

if (ServerBase.RequireEncryption(EndpointDescription))
{
X509Certificate2 certificate = m_certificateTypesProvider.GetInstanceCertificate(EndpointDescription.SecurityPolicyUri);
// decrypt the token.
if (m_serverCertificate == null)
if (certificate == null)
{
m_serverCertificate = X509CertificateLoader.LoadCertificate(
EndpointDescription.ServerCertificate);

// check for valid certificate.
if (m_serverCertificate == null)
{
throw ServiceResultException.Create(
StatusCodes.BadConfigurationError,
"ApplicationCertificate cannot be found.");
}
throw ServiceResultException.Create(
StatusCodes.BadConfigurationError,
"ApplicationCertificate cannot be found.");
}

try
{
token.Decrypt(
m_serverCertificate,
certificate,
m_serverNonce,
securityPolicyUri,
EndpointDescription.SecurityPolicyUri,
m_server.MessageContext,
m_eccUserTokenNonce,
ClientCertificate,
Expand Down Expand Up @@ -1257,6 +1251,7 @@ private void UpdateDiagnosticCounters(
private readonly IServerInternal m_server;
private readonly string m_sessionName;
private X509Certificate2 m_serverCertificate;
private CertificateTypesProvider m_certificateTypesProvider;
private Nonce m_serverNonce;
private string m_eccUserTokenSecurityPolicyUri;
private Nonce m_eccUserTokenNonce;
Expand Down
105 changes: 69 additions & 36 deletions Stack/Opc.Ua.Core/Stack/Configuration/EndpointDescription.cs
Original file line number Diff line number Diff line change
Expand Up @@ -90,44 +90,76 @@ public BinaryEncodingSupport EncodingSupport
/// <summary>
/// Finds the user token policy with the specified id and securtyPolicyUri
/// </summary>
[Obsolete("Use FindUserTokenPolicy without tokenSecurityPolicyUri")]
public UserTokenPolicy FindUserTokenPolicy(string policyId, string tokenSecurityPolicyUri)
{
UserTokenPolicy sameEncryptionAlgorithm = null;
UserTokenPolicy unspecifiedSecPolicy = null;
return FindUserTokenPolicy(policyId);
}

/// <summary>
/// Finds the user token policy with the specified id and securtyPolicyUri
/// </summary>
public UserTokenPolicy FindUserTokenPolicy(string policyId)
{
// The specified security policies take precedence
foreach (UserTokenPolicy policy in m_userIdentityTokens)
{
if (policy.PolicyId == policyId)
{
if (policy.SecurityPolicyUri == tokenSecurityPolicyUri)
{
return policy;
}
else if ((
policy.SecurityPolicyUri != null &&
tokenSecurityPolicyUri != null &&
EccUtils.IsEccPolicy(policy.SecurityPolicyUri) &&
EccUtils.IsEccPolicy(tokenSecurityPolicyUri)
) ||
(
!EccUtils.IsEccPolicy(policy.SecurityPolicyUri) &&
!EccUtils.IsEccPolicy(tokenSecurityPolicyUri)))
{
sameEncryptionAlgorithm ??= policy;
}
else if (policy.SecurityPolicyUri == null)
{
unspecifiedSecPolicy = policy;
}
return policy;
}
}
// The first token with the same encryption algorithm (RSA/ECC) follows
if (sameEncryptionAlgorithm != null)

return null;
}

/// <summary>
/// Finds a token policy that matches the user identity specified.
/// </summary>
public UserTokenPolicy FindUserTokenPolicy(
UserTokenType tokenType,
XmlQualifiedName issuedTokenType,
string preferredSecurityPolicyUri,
string[] fallbackSecurityPolicyUris)
{
// Use the namespace uri for the issued token type.
string issuedTokenTypeDef = issuedTokenType?.Namespace;

// Iterate twice: first for exact matches, then for relaxed matches.
foreach (bool exactMatch in new[] { true, false })
{
return sameEncryptionAlgorithm;
// Check preferred policy.
UserTokenPolicy match = FindUserTokenPolicy(
tokenType,
issuedTokenTypeDef,
preferredSecurityPolicyUri,
exactMatch);

if (match != null)
{
return match;
}

// Check fallback policies.
if (fallbackSecurityPolicyUris != null)
{
foreach (string policy in fallbackSecurityPolicyUris)
{
match = FindUserTokenPolicy(
tokenType,
issuedTokenTypeDef,
policy,
exactMatch);

if (match != null)
{
return match;
}
}
}
}
// The first token with unspecified security policy follows / no policy
return unspecifiedSecPolicy;

return null;
}

/// <summary>
Expand All @@ -136,17 +168,18 @@ public UserTokenPolicy FindUserTokenPolicy(string policyId, string tokenSecurity
public UserTokenPolicy FindUserTokenPolicy(
UserTokenType tokenType,
XmlQualifiedName issuedTokenType,
string tokenSecurityPolicyUri)
string tokenSecurityPolicyUri,
bool matchSecurityPolicyUriExactly = false)
{
if (issuedTokenType == null)
{
return FindUserTokenPolicy(tokenType, (string)null, tokenSecurityPolicyUri);
return FindUserTokenPolicy(tokenType, (string)null, tokenSecurityPolicyUri, matchSecurityPolicyUriExactly);
}

return FindUserTokenPolicy(
tokenType,
issuedTokenType.Namespace,
tokenSecurityPolicyUri);
tokenSecurityPolicyUri, matchSecurityPolicyUriExactly);
}

/// <summary>
Expand All @@ -155,7 +188,8 @@ public UserTokenPolicy FindUserTokenPolicy(
public UserTokenPolicy FindUserTokenPolicy(
UserTokenType tokenType,
string issuedTokenType,
string tokenSecurityPolicyUri)
string tokenSecurityPolicyUri,
bool matchSecurityPolicyUriExactly = false)
{
// construct issuer type.
string issuedTokenTypeText = issuedTokenType;
Expand All @@ -174,6 +208,7 @@ public UserTokenPolicy FindUserTokenPolicy(
return policy;
}
else if ((
!matchSecurityPolicyUriExactly &&
policy.SecurityPolicyUri != null &&
tokenSecurityPolicyUri != null &&
EccUtils.IsEccPolicy(policy.SecurityPolicyUri) &&
Expand All @@ -185,12 +220,10 @@ public UserTokenPolicy FindUserTokenPolicy(
{
sameEncryptionAlgorithm ??= policy;
}
else if (policy.SecurityPolicyUri == null)
else if (
(!matchSecurityPolicyUriExactly || tokenSecurityPolicyUri == null) && policy.SecurityPolicyUri == null)
{
if (sameEncryptionAlgorithm == null)
{
unspecifiedSecPolicy = policy;
}
unspecifiedSecPolicy = policy;
}
}
}
Expand Down
23 changes: 22 additions & 1 deletion Stack/Opc.Ua.Core/Stack/Server/ServerBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -901,6 +901,16 @@ public virtual UserTokenPolicyCollection GetUserTokenPolicies(

foreach (UserTokenPolicy policy in configuration.ServerConfiguration.UserTokenPolicies)
{
if (policy.SecurityPolicyUri != null &&
!configuration.SecurityConfiguration.SupportedSecurityPolicies.Contains(policy.SecurityPolicyUri))
{
m_logger.LogWarning(
"The UserTokenPolicy {Policy} specifies a security policy {SecurityPolicyUri} that is not supported by the server. " +
"Please check if all necessary application certificates are configured",
policy.ToString(),
policy.SecurityPolicyUri);
}

var clone = (UserTokenPolicy)policy.Clone();

if (string.IsNullOrEmpty(policy.SecurityPolicyUri) &&
Expand All @@ -914,7 +924,18 @@ public virtual UserTokenPolicyCollection GetUserTokenPolicies(
else
{
// ensure a security policy is specified for user tokens.
clone.SecurityPolicyUri = SecurityPolicies.Basic256Sha256;
if (configuration.SecurityConfiguration.SupportedSecurityPolicies.Contains(SecurityPolicies.Basic256Sha256))
{
clone.SecurityPolicyUri = SecurityPolicies.Basic256Sha256;
}
else if (configuration.SecurityConfiguration.SupportedSecurityPolicies.Contains(SecurityPolicies.ECC_nistP256))
{
clone.SecurityPolicyUri = SecurityPolicies.ECC_nistP256;
}
else
{
clone.SecurityPolicyUri = SecurityPolicies.Basic256Sha256;
}
}
}

Expand Down
Loading
Loading