Skip to content

Commit

Permalink
Merge pull request #2652 from OPCFoundation/master
Browse files Browse the repository at this point in the history
Prep 1.5.374 May release merge
  • Loading branch information
mregen authored Jun 19, 2024
2 parents 6bc90c9 + f2815bb commit 1ee3beb
Show file tree
Hide file tree
Showing 65 changed files with 2,343 additions and 345 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/docker-image.yml
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ jobs:
# https://github.com/docker/build-push-action
- name: Build and push Docker image
id: build-and-push
uses: docker/build-push-action@v5
uses: docker/build-push-action@v6
with:
context: .
build-args: |
Expand Down
6 changes: 4 additions & 2 deletions Applications/ConsoleReferenceClient/ClientSamples.cs
Original file line number Diff line number Diff line change
Expand Up @@ -699,7 +699,7 @@ public async Task<ReferenceDescriptionCollection> BrowseFullAddressSpaceAsync(
/// Outputs elapsed time information for perf testing and lists all
/// types that were successfully added to the session encodeable type factory.
/// </remarks>
public async Task LoadTypeSystemAsync(ISession session)
public async Task<ComplexTypeSystem> LoadTypeSystemAsync(ISession session)
{
m_output.WriteLine("Load the server type system.");

Expand Down Expand Up @@ -732,6 +732,8 @@ public async Task LoadTypeSystemAsync(ISession session)
}
}
}

return complexTypeSystem;
}
#endregion

Expand Down Expand Up @@ -900,7 +902,7 @@ public async Task SubscribeAllValuesAsync(
StartNodeId = item.NodeId,
AttributeId = Attributes.Value,
SamplingInterval = samplingInterval,
DisplayName = item.DisplayName?.Text ?? item.BrowseName.Name,
DisplayName = item.DisplayName?.Text ?? item.BrowseName?.Name ?? "unknown",
QueueSize = queueSize,
DiscardOldest = true,
MonitoringMode = MonitoringMode.Reporting,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,12 @@
<PackageReference Include="Microsoft.Extensions.Configuration" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="8.0.0" />
<PackageReference Include="Mono.Options" Version="6.12.0.148" />
<PackageReference Include="Serilog" Version="3.1.1" />
<PackageReference Include="Serilog.Expressions" Version="4.0.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="5.0.0" />
<PackageReference Include="Serilog" Version="4.0.0" />
<PackageReference Include="Serilog.Expressions" Version="5.0.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
<PackageReference Include="Serilog.Extensions.Logging" Version="3.1.0" />
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
<PackageReference Include="Serilog.Sinks.Debug" Version="2.0.0" />
<PackageReference Include="Serilog.Sinks.Debug" Version="3.0.0" />
</ItemGroup>

<ItemGroup>
Expand Down
99 changes: 87 additions & 12 deletions Applications/ConsoleReferenceClient/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
using System.Globalization;
using System.IO;
using System.Linq;
using System.Security.Cryptography.X509Certificates;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
Expand Down Expand Up @@ -69,6 +70,8 @@ public static async Task Main(string[] args)
bool autoAccept = false;
string username = null;
string userpassword = null;
string userCertificateThumbprint = null;
string userCertificatePassword = null;
bool logConsole = false;
bool appLog = false;
bool renewCertificate = false;
Expand All @@ -93,6 +96,8 @@ public static async Task Main(string[] args)
{ "nsec|nosecurity", "select endpoint with security NONE, least secure if unavailable", s => noSecurity = s != null },
{ "un|username=", "the name of the user identity for the connection", (string u) => username = u },
{ "up|userpassword=", "the password of the user identity for the connection", (string u) => userpassword = u },
{ "uc|usercertificate=", "the thumbprint of the user certificate for the user identity", (string u) => userCertificateThumbprint = u },
{ "ucp|usercertificatepassword=", "the password of the user certificate for the user identity", (string u) => userCertificatePassword = u },
{ "c|console", "log to console", c => logConsole = c != null },
{ "l|log", "log app output", c => appLog = c != null },
{ "p|password=", "optional password for private key", (string p) => password = p },
Expand Down Expand Up @@ -195,12 +200,12 @@ public static async Task Main(string[] args)
{
if (!forever)
{
break;
}
break;
}
else
{
waitTime = 0;
}
}
}

if (forever)
Expand All @@ -215,12 +220,29 @@ public static async Task Main(string[] args)
SessionLifeTime = 60_000,
})
{
// set user identity
if (!String.IsNullOrEmpty(username))
// set user identity of type username/pw
if (!string.IsNullOrEmpty(username))
{
uaClient.UserIdentity = new UserIdentity(username, userpassword ?? string.Empty);
}

// set user identity of type certificate
if (!string.IsNullOrEmpty(userCertificateThumbprint))
{
CertificateIdentifier userCertificateIdentifier =
await FindUserCertificateIdentifierAsync(userCertificateThumbprint,
application.ApplicationConfiguration.SecurityConfiguration.TrustedUserCertificates);

if (userCertificateIdentifier != null)
{
uaClient.UserIdentity = new UserIdentity(userCertificateIdentifier, new CertificatePasswordProvider(userCertificatePassword ?? string.Empty));
}
else
{
output.WriteLine($"Failed to load user certificate with Thumbprint {userCertificateThumbprint}");
}
}

bool connected = await uaClient.ConnectAsync(serverUrl.ToString(), !noSecurity, quitCTS.Token).ConfigureAwait(false);
if (connected)
{
Expand All @@ -234,7 +256,7 @@ public static async Task Main(string[] args)
var samples = new ClientSamples(output, ClientBase.ValidateResponse, quitEvent, verbose);
if (loadTypes)
{
await samples.LoadTypeSystemAsync(uaClient.Session).ConfigureAwait(false);
var complexTypeSystem = await samples.LoadTypeSystemAsync(uaClient.Session).ConfigureAwait(false);
}

if (browseall || fetchall || jsonvalues)
Expand Down Expand Up @@ -266,8 +288,8 @@ public static async Task Main(string[] args)

if (subscribe && (browseall || fetchall))
{
// subscribe to 100 random variables
const int MaxVariables = 100;
// subscribe to 1000 random variables
const int MaxVariables = 1000;
NodeCollection variables = new NodeCollection();
Random random = new Random(62541);
if (fetchall)
Expand All @@ -291,15 +313,41 @@ public static async Task Main(string[] args)

await samples.SubscribeAllValuesAsync(uaClient,
variableIds: new NodeCollection(variables),
samplingInterval: 1000,
publishingInterval: 5000,
samplingInterval: 100,
publishingInterval: 1000,
queueSize: 10,
lifetimeCount: 12,
lifetimeCount: 60,
keepAliveCount: 2).ConfigureAwait(false);

// Wait for DataChange notifications from MonitoredItems
output.WriteLine("Subscribed to {0} variables. Press Ctrl-C to exit.", MaxVariables);
quit = quitEvent.WaitOne(timeout > 0 ? waitTime : Timeout.Infinite);

// free unused memory
uaClient.Session.NodeCache.Clear();

waitTime = timeout - (int)DateTime.UtcNow.Subtract(start).TotalMilliseconds;
DateTime endTime = waitTime > 0 ? DateTime.UtcNow.Add(TimeSpan.FromMilliseconds(waitTime)) : DateTime.MaxValue;
var variableIterator = variables.GetEnumerator();
while (!quit && endTime > DateTime.UtcNow)
{
if (variableIterator.MoveNext())
{
try
{
var value = await uaClient.Session.ReadValueAsync(variableIterator.Current.NodeId).ConfigureAwait(false);
output.WriteLine("Value of {0} is {1}", variableIterator.Current.NodeId, value);
}
catch (Exception ex)
{
output.WriteLine("Error reading value of {0}: {1}", variableIterator.Current.NodeId, ex.Message);
}
}
else
{
variableIterator = variables.GetEnumerator();
}
quit = quitEvent.WaitOne(500);
}
}
else
{
Expand Down Expand Up @@ -346,5 +394,32 @@ await samples.SubscribeAllValuesAsync(uaClient,
output.Close();
}
}
/// <summary>
/// returns a CertificateIdentifier of the Certificate with the specified thumbprint if it is found in the trustedUserCertificates TrustList
/// </summary>
/// <param name="thumbprint">the thumbprint of the certificate to select</param>
/// <param name="trustedUserCertificates">the trustlist of the user certificates</param>
/// <returns>Certificate Identifier</returns>
private static async Task<CertificateIdentifier> FindUserCertificateIdentifierAsync(string thumbprint, CertificateTrustList trustedUserCertificates)
{
CertificateIdentifier userCertificateIdentifier = null;

// get user certificate with matching thumbprint
X509Certificate2Collection userCertifiactesWithMatchingThumbprint =
(await trustedUserCertificates
.GetCertificates())
.Find(X509FindType.FindByThumbprint, thumbprint, false);

// create Certificate Identifier
if (userCertifiactesWithMatchingThumbprint.Count == 1)
{
userCertificateIdentifier = new CertificateIdentifier(userCertifiactesWithMatchingThumbprint[0]) {
StorePath = trustedUserCertificates.StorePath,
StoreType = trustedUserCertificates.StoreType
};
}

return userCertificateIdentifier;
}
}
}
20 changes: 20 additions & 0 deletions Applications/ConsoleReferenceClient/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# OPC Foundation UA .NET Standard Reference Client

## Introduction

The console reference client can be configured using several console parameters.
Some of these parameters are explained in more detail below.

To see all available parameters call console reference client the with the parameter `-h`.

### How to specify User Identity
#### Username & Password
Specify as console parameters:
`-un YourUsername`
`-up YourPassword`

#### Certificate
Place your user certificate in the TrustedUserCertificatesStore (the path can be found in the client configuration XML). Make shure to include an accessible private key with the certificate.
Specify console parameters:
`-uc Thumbprint` (of the user certificate to select)
`-ucp Password` (of the user certificates private key (optional))
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,12 @@
<PackageReference Include="Microsoft.Extensions.Configuration" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="8.0.0" />
<PackageReference Include="Mono.Options" Version="6.12.0.148" />
<PackageReference Include="Serilog" Version="3.1.1" />
<PackageReference Include="Serilog.Expressions" Version="4.0.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="5.0.0" />
<PackageReference Include="Serilog" Version="4.0.0" />
<PackageReference Include="Serilog.Expressions" Version="5.0.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
<PackageReference Include="Serilog.Extensions.Logging" Version="3.1.0" />
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
<PackageReference Include="Serilog.Sinks.Debug" Version="2.0.0" />
<PackageReference Include="Serilog.Sinks.Debug" Version="3.0.0" />
</ItemGroup>

<ItemGroup>
Expand Down
4 changes: 2 additions & 2 deletions Applications/ReferenceServer/Reference Server.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -159,10 +159,10 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Serilog">
<Version>3.1.1</Version>
<Version>4.0.0</Version>
</PackageReference>
<PackageReference Include="Serilog.Sinks.Debug">
<Version>2.0.0</Version>
<Version>3.0.0</Version>
</PackageReference>
<PackageReference Include="Serilog.Sinks.File">
<Version>5.0.0</Version>
Expand Down
4 changes: 4 additions & 0 deletions Docs/Certificates.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ The UA .NET Standard stack supports the following certificate stores:

- The **Trusted Https** store `<root>/trustedHttps` which contains https certificates which are trusted by an application. To establish trust, the same rules apply as explained for the *Trusted* and the *Issuer* store.

### X509Store on Windows
Starting with Version 1.5.xx of the UA .NET Standard Stack the X509Store supports the storage and retrieval of CRLS, if used on the **Windows OS**.
This enables the usage of the X509Store instead of the Directory Store for stores requiring the use of crls, e.g. the issuer or the directory Store.

### Windows .NET applications
By default the self signed certificates are stored in a **X509Store** called **CurrentUser\\UA_MachineDefault**. The certificates can be viewed or deleted with the Windows Certificate Management Console (certmgr.msc). The *trusted*, *issuer* and *rejected* stores remain in a folder called **OPC Foundation\pki** with a root folder which is specified by the `SpecialFolder` variable **%CommonApplicationData%**. On Windows 7/8/8.1/10 this is usually the invisible folder **C:\ProgramData**.

Expand Down
1 change: 1 addition & 0 deletions Docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ UA Core stack related:

Reference application related:
* [Reference Server](../Applications/ReferenceServer/README.md) documentation for running against CTT.
* [Reference Client](../Applications/ConsoleReferenceClient/README.md) documentation for configuration of the console reference client using parameters.
* Using the [Container support](ContainerReferenceServer.md) of the Reference Server in VS2022 and for local testing.

For the PubSub support library:
Expand Down
8 changes: 4 additions & 4 deletions Fuzzing/Encoders/Fuzz.Tools/Encoders.Fuzz.Tools.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@
<PackageReference Include="SharpFuzz" Version="2.1.1" />
<PackageReference Include="Mono.Options" Version="6.12.0.148" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="6.0.0" />
<PackageReference Include="Serilog" Version="3.1.1" />
<PackageReference Include="Serilog.Expressions" Version="4.0.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="5.0.0" />
<PackageReference Include="Serilog" Version="4.0.0" />
<PackageReference Include="Serilog.Expressions" Version="5.0.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
<PackageReference Include="Serilog.Extensions.Logging" Version="3.1.0" />
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
<PackageReference Include="Serilog.Sinks.Debug" Version="2.0.0" />
<PackageReference Include="Serilog.Sinks.Debug" Version="3.0.0" />
</ItemGroup>

<ItemGroup>
Expand Down
13 changes: 5 additions & 8 deletions Libraries/Opc.Ua.Client/CoreClientUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Opc.Ua.Security;

namespace Opc.Ua.Client
{
Expand Down Expand Up @@ -280,14 +281,10 @@ public static EndpointDescription SelectEndpoint(
selectedEndpoint = endpoint;
}

// The security level is a relative measure assigned by the server to the
// endpoints that it returns. Clients should always pick the highest level
// unless they have a reason not too.
// Some servers however, mess this up a bit. So prefer a higher SecurityMode
// over the SecurityLevel.
if (endpoint.SecurityMode > selectedEndpoint.SecurityMode
|| (endpoint.SecurityMode == selectedEndpoint.SecurityMode
&& endpoint.SecurityLevel > selectedEndpoint.SecurityLevel))

//Select endpoint if it has a higher calculated security level, than the previously selected one
if (SecuredApplication.CalculateSecurityLevel(endpoint.SecurityMode, endpoint.SecurityPolicyUri)
> SecuredApplication.CalculateSecurityLevel(selectedEndpoint.SecurityMode, selectedEndpoint.SecurityPolicyUri))
{
selectedEndpoint = endpoint;
}
Expand Down
Loading

0 comments on commit 1ee3beb

Please sign in to comment.