Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
185 commits
Select commit Hold shift + click to select a range
19d4c45
Fix CA2000 warnings in Core.Tests by adding using declarations
marcschier Apr 15, 2026
8e20ea8
Fix CA2000 warnings in Libraries/ directory
marcschier Apr 15, 2026
4fa01c4
Fix CA2000 warnings in PubSub tests by adding using declarations
marcschier Apr 15, 2026
8f87dfd
Dispose analyzer
marcschier Apr 16, 2026
03c4b61
Migrate Opc.Ua.Security.Certificates internals to Certificate type
marcschier Apr 16, 2026
643e109
Fixes
marcschier Apr 16, 2026
14dc40e
Merge branch 'master' of https://github.com/OPCFoundation/UA-.NETStan…
marcschier Apr 16, 2026
3e8e5e8
update
marcschier Apr 16, 2026
ec74626
Merge
marcschier Apr 17, 2026
c5b7f2a
update
marcschier Apr 17, 2026
3284123
Merge
marcschier Apr 17, 2026
2a71a2e
Update
marcschier Apr 17, 2026
46028ed
testpass
marcschier Apr 17, 2026
e4346c5
Fix CA2000 dispose warnings in test projects
marcschier Apr 17, 2026
98ff96d
Fix CA2000: remove using from CertificateCollection in EncryptedSecret
marcschier Apr 17, 2026
b9c06fb
testfixes
marcschier Apr 20, 2026
52ddb35
Merge
marcschier Apr 20, 2026
66921a0
Add Certificate.AddRef() reference counting for shared cert lifetime …
marcschier Apr 20, 2026
7c92681
Add CertificateManager with segregated interfaces, implementations, a…
marcschier Apr 20, 2026
fb3ba13
Wire CertificateManager into Server, Client, Configuration, and GDS l…
marcschier Apr 20, 2026
c0ff422
Fix multi-TFM build, remove #region directives, add test coverage
marcschier Apr 20, 2026
c84711e
Complete remaining plan items: lifecycle monitor, trust-list file acc…
marcschier Apr 20, 2026
c9d516f
Suppress CS0618 in test files that exercise obsolete CertificateFacto…
marcschier Apr 20, 2026
f56f452
Add CertificateManager API documentation and usage guide
marcschier Apr 20, 2026
a435b45
Update Certificates.md and MigrationGuide.md for CertificateManager m…
marcschier Apr 20, 2026
374559a
Merge branch 'master' of https://github.com/OPCFoundation/UA-.NETStan…
marcschier Apr 20, 2026
021d49c
Merge branch 'master' of https://github.com/OPCFoundation/UA-.NETStan…
marcschier Apr 21, 2026
70d88a6
Fix ExtraStore.AddRange ref-counting: dispose intermediate Certificat…
marcschier Apr 21, 2026
f80000d
Add Certificate.Export(), replace .X509.AsLogSafeString() with ToStri…
marcschier Apr 21, 2026
7320cbc
Fix CertificateCache double-dispose on eviction
marcschier Apr 21, 2026
60f8de3
Migrate to new API surface, fix naming, add certificate cache
marcschier Apr 21, 2026
a725fd4
Potential fix for pull request finding 'CodeQL / Clear text storage o…
marcschier Apr 21, 2026
c58539d
Potential fix for pull request finding 'CodeQL / Clear text storage o…
marcschier Apr 21, 2026
88de2da
Merge
marcschier Apr 22, 2026
79c1cc2
Fix build
marcschier Apr 22, 2026
c1c5e32
Merge branch 'master' of https://github.com/OPCFoundation/UA-.NETStan…
marcschier Apr 22, 2026
c13a0cb
Update migration guide
marcschier Apr 22, 2026
4122626
update
marcschier Apr 22, 2026
2e9db55
Update
marcschier Apr 23, 2026
66e139b
udpate
marcschier Apr 24, 2026
575d187
udpate
marcschier Apr 24, 2026
0973f9f
Update
marcschier Apr 24, 2026
14f47a0
Re-apply Certificate.Equals fix and remove stale cache Set in AddAsync
marcschier Apr 24, 2026
a973c1e
Fix CertificateCollection ownership and lifetime management
marcschier Apr 24, 2026
c965d02
update
marcschier Apr 24, 2026
f7f6403
Merge branch 'master' of https://github.com/OPCFoundation/UA-.NETStan…
marcschier Apr 24, 2026
f484817
Systematic certificate resource management
marcschier Apr 24, 2026
c03f597
Replace fire-and-forget SaveCertificatesAsync with Channel-based writer
marcschier Apr 24, 2026
7e423bd
Remove .X509 internal access after InternalsVisibleTo removal
marcschier Apr 25, 2026
6067d7e
Add Certificate leak detection for all TFMs
marcschier Apr 25, 2026
b4e54c6
Change leak detection from Assert.Warn to Assert.Fail
marcschier Apr 25, 2026
33aab00
Add #nullable enable to Opc.Ua.Security.Certificates source files
marcschier Apr 26, 2026
88d831d
Enable #nullable on all Security/Certificates code
marcschier Apr 26, 2026
7855ca9
Fix certificate leaks in CertificateValidator test category
marcschier Apr 28, 2026
b0cd7c4
Fix all analyzer warnings with RunAnalyzersDuringBuild=true
marcschier Apr 28, 2026
35af4fa
Merge remote-tracking branch 'origin/master' into x509
marcschier Apr 28, 2026
04a2e1e
Fix CI build errors after master merge
marcschier Apr 29, 2026
11787e5
Fix ParseCertificateChainBlob double-dispose of created certs
marcschier Apr 29, 2026
1d91a5c
Revert "Fix ParseCertificateChainBlob double-dispose of created certs"
marcschier Apr 29, 2026
6792e96
Fix certificate leaks across production and test code
marcschier Apr 29, 2026
b4eb04e
Fix additional certificate leaks in test code and production code
marcschier Apr 29, 2026
32ea6ce
Fix PEMTests and CertificateEntryTests leaks
marcschier Apr 29, 2026
c7796fd
Change leak detection from Assert.Fail back to Assert.Warn
marcschier Apr 29, 2026
88829fd
Merge remote-tracking branch 'origin/master' into x509
marcschier Apr 29, 2026
4e19085
Fix 568 certificate leaks in Core.Tests
marcschier Apr 29, 2026
c0617fa
Fix certificate leaks from FindAsync and LoadCertificateChainAsync ca…
marcschier Apr 29, 2026
67f45f2
Allow 2-cert leak tolerance for pre-existing VerifyLoopChain failures
marcschier Apr 29, 2026
e48c45c
Fix VerifyLoopChainIsDetectedAsync and reduce Core leaks to 2
marcschier Apr 30, 2026
0533a56
Fix certificate leaks in DirectoryCertificateStore.AddRejectedAsync
marcschier Apr 30, 2026
8f2d810
Merge remote-tracking branch 'origin/master' into x509
marcschier Apr 30, 2026
add7f2c
Fix RejectedCertificateWriter chain disposal on drop and dispose
marcschier Apr 30, 2026
386976b
Fix CI: McpServer build, Reconnect ObjectDisposedException
marcschier Apr 30, 2026
0ea5cf9
Fix CI: ModelDependency test wrap, AsX509Certificate2 non-exportable …
marcschier Apr 30, 2026
b3aab5e
Fix CI: net48 PubSub, Reconnect ownership transfer, Core leak tolerance
marcschier May 1, 2026
d663dca
Fix CI: GDS LoadSigningKeyAsync returns disposed certificate
marcschier May 1, 2026
14f377c
Address GDS PushTest flakiness around ApplyChanges/cert teardown
marcschier May 1, 2026
a335941
Address GDS PushTest flakiness: also catch BadRequestInterrupted
marcschier May 1, 2026
3cc41b5
Make GDS push-test cert teardown handler more robust + bump Core leak…
marcschier May 2, 2026
8a9bfcc
Bump Core leak tolerance from 25 to 100 for macOS
marcschier May 2, 2026
b3383c0
Skip Core leak detection on macOS
marcschier May 2, 2026
7141f86
Fix all Certificate leaks across all platforms
Copilot May 2, 2026
75bc0ff
Fix CertificateCollection.CopyTo leaking refs
Copilot May 2, 2026
5c8f932
Retrigger CI
Copilot May 2, 2026
b33c000
Merge remote-tracking branch 'origin/master' into x509
marcschier May 2, 2026
3c128d3
Fix macOS X509Store.Add AppleCrypto failure for private-key certs
Copilot May 3, 2026
b406094
Merge remote-tracking branch 'origin/master' into x509
marcschier May 3, 2026
bc16780
Migrate to new ICertificateFactory design; obsolete static Certificat…
marcschier May 2, 2026
75da6eb
PubSub: migrate MqttPubSubConnection from CertificateValidator to Cer…
marcschier May 3, 2026
743eacb
CertificateValidatorAdapter: add TrustListIdentifier-scoped overload;…
marcschier May 3, 2026
1021891
Docs: update Certificates / Observability / MigrationGuide / Certific…
marcschier May 3, 2026
39ef98d
CertificateValidationOptions: add AcceptError per-error accept callback
marcschier May 3, 2026
6a7d2a3
Docs: document the new CertificateValidationOptions.AcceptError per-e…
marcschier May 3, 2026
ebed55a
Mark CertificateValidator.GetChannelValidator() obsolete; migrate tra…
marcschier May 3, 2026
3c9be95
ServerInternalData: drop unused certificateValidator/instanceCertific…
marcschier May 3, 2026
1f3163f
ApplicationInstance: migrate CheckApplicationInstanceCertificate to m…
marcschier May 3, 2026
24d83d4
Bridge upstream union-prep build errors on older .NET 10 preview SDKs
marcschier May 3, 2026
1b9f2fa
GDS: migrate VerifyX509IdentityToken + DefaultCertificateIssuer singl…
marcschier May 3, 2026
162cd3f
Migrate field-init and inline 'new DefaultCertificateFactory()' / 'ne…
marcschier May 3, 2026
2705b83
Atomic transport refactor S1+S2: fill manager surface, bridge legacy …
marcschier May 3, 2026
ec80b02
Atomic transport refactor S3+S4: validation extensions and Applicatio…
marcschier May 3, 2026
5a3e14d
Atomic transport refactor S10 (partial): Session.cs (Client) prefers …
marcschier May 3, 2026
913004e
Atomic transport refactor S10+S11: Session client chain validation + …
marcschier May 3, 2026
af9a743
Atomic transport refactor S5+S6+S7: type-swap on transport types and …
marcschier May 3, 2026
7c947c7
Atomic transport refactor S8: identity-token handler validator parame…
marcschier May 3, 2026
cea2f10
Type-swap ApplicationConfiguration/ServerBase.CertificateValidator to…
marcschier May 3, 2026
d86bd8a
Drop dedicated CertificateValidator from discovery registration; shar…
marcschier May 3, 2026
f8a70f4
Replace transient CertificateValidator with X509Chain in CSR signing …
marcschier May 3, 2026
f41f4c9
Mark CertificateValidator class, ICertificateValidator interface, Cer…
marcschier May 3, 2026
f25016d
Mark ApplicationConfiguration/ServerBase legacy properties [Obsolete]…
marcschier May 3, 2026
3ee336c
Update Certificates docs for full obsolete cascade (S20)
marcschier May 3, 2026
61c84e6
Add global AcceptError property on ICertificateValidatorEx; migrate s…
marcschier May 4, 2026
3342e74
Document new ICertificateValidatorEx.AcceptError global property
marcschier May 4, 2026
0774842
Make CertificateTypesProvider registry-backed when constructed with a…
marcschier May 4, 2026
020885f
Remove union attribution until c#15
marcschier May 4, 2026
bbef98c
CertificateManager: propagate SecurityConfiguration validation flags …
marcschier May 4, 2026
c9f3f42
Merge branch 'x509' of https://github.com/OPCFoundation/UA-.NETStanda…
marcschier May 4, 2026
601f5e8
CertificateManager: skip empty trust-list paths in MapFromSecurityCon…
marcschier May 4, 2026
d0f9358
Remove global.json
marcschier May 4, 2026
b0f4b21
Merge remote-tracking branch 'origin/master' into x509
marcschier May 4, 2026
ac96967
Fix CI failures: SerializableVariant.Equals object dispatch + cert ho…
marcschier May 4, 2026
85d29ea
Merge remote-tracking branch 'origin/master' into x509
Copilot May 5, 2026
cac48ea
Replace file-level CS0618 pragmas with narrow per-call wrappers
Copilot May 5, 2026
0a56aee
Address PR review: refresh registry on cert update + fix CA2000 in Co…
marcschier May 5, 2026
18360dc
Pin HTTPS TLS certificates to listener/channel lifetime to fix concur…
marcschier May 5, 2026
5825799
CertificateManager: synchronize m_applicationCertificates and validat…
marcschier May 5, 2026
5caf6a4
Add [Retry(3)] to concurrent NodeCache stress tests on HTTPS variants
marcschier May 5, 2026
9fd866a
Increase retry on concurrent NodeCache stress tests from 3 to 5
marcschier May 5, 2026
0cea7c3
Skip concurrent NodeCache stress tests on HTTPS variants
marcschier May 5, 2026
2b8119f
Merge remote-tracking branch 'origin/master' into x509
marcschier May 5, 2026
f5bbe41
test: stabilize Client CI flakes after master merge
marcschier May 5, 2026
85819b7
fix(tcp-listener): send fault response on request handler exception
marcschier May 5, 2026
940069e
Implement Phases 1, 2, 5, 6, 7 of the certificate-manager migration
Copilot May 6, 2026
1b06c30
Phase 3: retire CertificateTypesProvider from transports
Copilot May 6, 2026
59499e9
Phase 4: drop ApplicationConfiguration.CertificateValidator init
Copilot May 6, 2026
ada475c
cleanup
marcschier May 6, 2026
6fd4d6a
Phase 1 cleanup (partial): add new validation knobs to CertificateMan…
Copilot May 6, 2026
51cffcb
Update CertificateManagerMigration doc to reflect current state
Copilot May 6, 2026
66695c1
cleanup
marcschier May 6, 2026
4143505
cleanup
marcschier May 6, 2026
a14861a
cleanup
marcschier May 6, 2026
f71b84c
cleanup
marcschier May 6, 2026
5a20a7a
cleanup
marcschier May 6, 2026
0117cd0
Phase 1 finish: retire TemporaryCertValidator
Copilot May 6, 2026
60f113a
Phase B (partial Phase 8): delete CertificateValidator obsolete forwa…
Copilot May 6, 2026
eed6457
cleanup
marcschier May 6, 2026
4937735
cleanup
marcschier May 6, 2026
c62fe4e
Phase 8 commit A: add CertificateValidationCore + helpers
Copilot May 6, 2026
1bfac87
Phase 8 commit B: CertificateManager uses CertificateValidationCore
Copilot May 6, 2026
f52437e
Phase 8 commit C: centralise rejected-store enqueue in CertificateMan…
Copilot May 6, 2026
ac99543
Phase 8 commit D: simplify CertificateValidationExtensions
Copilot May 6, 2026
cf0137c
Phase 8 commit E: retire X509TestUtils legacy validator usage
Copilot May 6, 2026
8221e66
Phase 8 commit F: delete the legacy CertificateValidator bridge
Copilot May 6, 2026
e0b2e13
Remove CertificateManagerMigration.md now that migration is complete
Copilot May 6, 2026
c5eceae
fix: NRE on PubSub Mqtt dispose and ReferenceServer node-manager dispose
marcschier May 7, 2026
2f2e443
test(SubscriptionTests): fix indefinite hang in OnKeepAlive callback
marcschier May 7, 2026
c940966
cleanup
marcschier May 7, 2026
3d098c3
cleanup
marcschier May 7, 2026
0058563
Merge remote-tracking branch 'origin/x509' into x509
Copilot May 7, 2026
dd19d1c
fix: resolve baseline analyzer errors blocking RunAnalyzersDuringBuild
Copilot May 7, 2026
4e58ef3
style: dotnet format whitespace
Copilot May 7, 2026
f444c09
fix(server): share CertificateManager across ServerBase, refresh cert…
marcschier May 7, 2026
10bf85f
style: dotnet format style IDE0032 IDE0049 (using directive ordering)
Copilot May 7, 2026
10f823b
style: dotnet format analyzers RCS1043 RCS1061 RCS1090
Copilot May 7, 2026
0950240
style: dotnet format analyzers RCS1132 RCS1140 RCS1142
Copilot May 7, 2026
4c74b7e
refactor(certs): add CertificateIdentifierResolver helper (additive)
marcschier May 7, 2026
29ee58a
style: dotnet format analyzers RCS1049 RCS1085
Copilot May 7, 2026
d758800
style: dotnet format analyzers RCS1249 RCS1252
Copilot May 7, 2026
5d5690b
style: dotnet format analyzers RCS0027 RCS0029
Copilot May 7, 2026
d939ffa
style: dotnet format analyzers RCS0050 RCS1186
Copilot May 7, 2026
60c7c77
refactor(certs): route CertificateManager + GDS push through resolver
marcschier May 7, 2026
9cad019
style: dotnet format analyzers NUnit2009 NUnit2010
Copilot May 7, 2026
682145d
docs: add skill.md for the dotnet format cleanup recipe
Copilot May 7, 2026
0997980
update
marcschier May 7, 2026
5818b53
Merge remote-tracking branch 'origin/x509' into x509
marcschier May 7, 2026
2139d76
fix(certs): in-memory CertificateIdentifier(cert) fallback in resolver
marcschier May 7, 2026
1af0d94
refactor(certs): migrate UserIdentity, GDS server, ConsoleReferenceCl…
marcschier May 7, 2026
eff3a94
fix(certs): restore in-memory cert fast-path in resolver LoadPrivateK…
Copilot May 7, 2026
8ead710
Merge branch 'x509' of https://github.com/OPCFoundation/UA-.NETStanda…
Copilot May 7, 2026
5809788
Phase 1e: route trust list / validator / SecurityConfiguration throug…
marcschier May 7, 2026
5e5b9c2
Phase 2-validator: replace CertificateIdentifier issuer wrappers with…
marcschier May 7, 2026
c7610c6
Phase 2-collection + p2-secconfig: refactor CertificateIdentifierColl…
marcschier May 7, 2026
87f8558
Phase 3+4+5: strip CertificateIdentifier cache, migrate consumers, up…
marcschier May 7, 2026
0b73a51
fix(codeql): wrap certificate log args in Redact.Create
marcschier May 8, 2026
86b7272
fix(codeql): wrap StorePath log args in Redact.Create
marcschier May 8, 2026
9dcdaa2
test(certs): add coverage for CertificateIdentifierResolver and Certi…
marcschier May 8, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
1 change: 1 addition & 0 deletions Applications/ConsoleReferenceClient/ClientSamples.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1101,6 +1101,7 @@ ArrayOf<BrowseDescription> browseDescriptionCollection
/// Outputs elapsed time information for perf testing and lists all
/// types that were successfully added to the session encodeable type factory.
/// </remarks>
/// <exception cref="ServiceResultException"></exception>
public async Task LoadTypeSystemAsync(ComplexTypeSystem complexTypeSystem, CancellationToken ct = default)
{
m_logger.LogInformation("Load the server type system.");
Expand Down
62 changes: 33 additions & 29 deletions Applications/ConsoleReferenceClient/ConnectTester.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
* ======================================================================*/

using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
Expand All @@ -40,6 +39,7 @@
using Opc.Ua;
using Opc.Ua.Client;
using Opc.Ua.Configuration;
using Opc.Ua.Security.Certificates;

namespace Quickstarts
{
Expand Down Expand Up @@ -87,20 +87,22 @@ public async Task RunAsync(CancellationToken ct)
// Define the UA Client application
var passwordProvider = new CertificatePasswordProvider([]);

var application = new ApplicationInstance(m_telemetry)
#pragma warning disable CA2007
await using var application = new ApplicationInstance(m_telemetry)
{
ApplicationName = applicationName,
ApplicationType = ApplicationType.Client,
ConfigSectionName = configSectionName,
CertificatePasswordProvider = passwordProvider
};
#pragma warning restore CA2007

// load the application configuration.
ApplicationConfiguration configuration = m_configuration = await application
.LoadApplicationConfigurationAsync(silent: false, ct: ct)
.ConfigureAwait(false);

m_configuration.CertificateValidator.CertificateValidation += CertificateValidation;
m_configuration.CertificateManager.AcceptError = AcceptCertificate;

// check the application certificate.
bool haveAppCertificate = await application
Expand All @@ -121,7 +123,7 @@ public async Task RunAsync(CancellationToken ct)

var endpointConfiguration = EndpointConfiguration.Create(m_configuration);
var sessionFactory = new DefaultSessionFactory(m_telemetry);
var userNameidentity = new UserIdentity(kUserName, new UTF8Encoding(false).GetBytes(kPassword));
using var userNameidentity = new UserIdentity(kUserName, new UTF8Encoding(false).GetBytes(kPassword));

foreach (EndpointDescription ii in endpoints.ToArray())
{
Expand Down Expand Up @@ -252,6 +254,7 @@ internal async Task<SessionWrapper> RunTestAsync(
endpointConfiguration);

// Create the session
#pragma warning disable CA2000 // Dispose objects before losing scope
ISession isession = await sessionFactory
.CreateAsync(
m_configuration,
Expand All @@ -266,6 +269,7 @@ internal async Task<SessionWrapper> RunTestAsync(
ct
)
.ConfigureAwait(false);
#pragma warning restore CA2000 // Dispose objects before losing scope

SessionWrapper wrapper = m_wrapper = new SessionWrapper { Session = isession };

Expand All @@ -288,23 +292,25 @@ internal async Task<SessionWrapper> RunTestAsync(
return wrapper;
}

#if NET8_0_OR_GREATER
private async Task<UserIdentity> LoadUserCertificateAsync(
string thumbprint,
string password,
CancellationToken ct)
{
CertificateTrustList store = m_configuration.SecurityConfiguration.TrustedUserCertificates;
#if NET8_0_OR_GREATER
// get user certificate with matching thumbprint
X509Certificate2Collection certificates =
using CertificateCollection certificates =
await store.GetCertificatesAsync(m_telemetry, ct).ConfigureAwait(false);
X509Certificate2 hit = certificates
using Certificate hit = certificates
.Find(X509FindType.FindByThumbprint, thumbprint, false)
.FirstOrDefault();

// create Certificate Identifier
var cid = new CertificateIdentifier(hit)
var cid = new CertificateIdentifier
{
Thumbprint = hit.Thumbprint,
SubjectName = hit.Subject,
StorePath = store.StorePath,
StoreType = store.StoreType
};
Expand All @@ -314,11 +320,18 @@ private async Task<UserIdentity> LoadUserCertificateAsync(
new CertificatePasswordProvider(new UTF8Encoding(false).GetBytes(password)),
m_telemetry,
ct).ConfigureAwait(false);
}
#else
await Task.Delay(1, ct).ConfigureAwait(false);
throw new NotSupportedException("User certificate identity is only supported on .NET 8 or greater.");
#endif
private Task<UserIdentity> LoadUserCertificateAsync(
string thumbprint,
string password,
CancellationToken ct)
{
return Task.FromException<UserIdentity>(
new NotSupportedException(
"User certificate identity is only supported on .NET 8 or greater."));
}
#endif

private static async ValueTask<ArrayOf<EndpointDescription>> GetEndpointsAsync(
ApplicationConfiguration application,
Expand All @@ -336,38 +349,29 @@ private static async ValueTask<ArrayOf<EndpointDescription>> GetEndpointsAsync(
return await client.GetEndpointsAsync(default, ct).ConfigureAwait(false);
}

private void CertificateValidation(
CertificateValidator sender,
CertificateValidationEventArgs e)
private bool AcceptCertificate(Certificate certificate, ServiceResult error)
{
bool certificateAccepted = false;

// ****
// Implement a custom logic to decide if the certificate should be
// accepted or not and set certificateAccepted flag accordingly.
// The certificate can be retrieved from the e.Certificate field
// accepted. Return true to accept, false to reject.
// ***

ServiceResult error = e.Error;
m_logger.LogInformation("{ServiceResult}", error);
if (error.StatusCode == StatusCodes.BadCertificateUntrusted)
{
certificateAccepted = true;
}
bool certificateAccepted = error.StatusCode == StatusCodes.BadCertificateUntrusted;

if (certificateAccepted)
{
m_logger.LogInformation(
"Untrusted Certificate accepted. Subject = {Subject}",
e.Certificate.Subject);
e.Accept = true;
certificate.Subject);
}
else
{
m_logger.LogInformation(
"Untrusted Certificate rejected. Subject = {Subject}",
e.Certificate.Subject);
certificate.Subject);
}

return certificateAccepted;
}

/// <summary>
Expand Down Expand Up @@ -487,8 +491,8 @@ internal sealed class SessionWrapper : IUAClient
private SessionReconnectHandler m_reconnectHandler;
private ApplicationConfiguration m_configuration;
private SessionWrapper m_wrapper;
private ILogger m_logger;
private ITelemetryContext m_telemetry;
private readonly ILogger m_logger;
private readonly ITelemetryContext m_telemetry;
private readonly ManualResetEvent m_quitEvent;

private const string kServerUrl = "opc.tcp://localhost:62541";
Expand Down
48 changes: 26 additions & 22 deletions Applications/ConsoleReferenceClient/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@
using System;
using System.Collections.Generic;
using System.CommandLine;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Linq;
Expand All @@ -43,6 +42,7 @@
using Opc.Ua.Client;
using Opc.Ua.Client.ComplexTypes;
using Opc.Ua.Configuration;
using Opc.Ua.Security.Certificates;

namespace Quickstarts.ConsoleReferenceClient
{
Expand Down Expand Up @@ -249,7 +249,7 @@ public static Task<int> Main(string[] args)
bool enableDurableSubscriptions =
parseResult.GetValue(durableSubscriptionOption);
var serverUrl = new Uri(parseResult.GetValue(serverUrlArgument));
var testallEndpoints = parseResult.GetValue(testallEndpointsOption);
bool testallEndpoints = parseResult.GetValue(testallEndpointsOption);

ReverseConnectManager reverseConnectManager = null;
using var telemetry = new ConsoleTelemetry();
Expand Down Expand Up @@ -363,24 +363,26 @@ await application.DeleteApplicationInstanceCertificateAsync(ct: cancellationToke
// set user identity of type certificate
if (!string.IsNullOrEmpty(userCertificateThumbprint))
{
CertificateIdentifier userCertificateIdentifier
= await FindUserCertificateIdentifierAsync(
userCertificateThumbprint,
application.ApplicationConfiguration.SecurityConfiguration
.TrustedUserCertificates,
telemetry,
ct
)
.ConfigureAwait(true);
CertificateIdentifier userCertificateIdentifier =
await FindUserCertificateIdentifierAsync(
userCertificateThumbprint,
application.ApplicationConfiguration.SecurityConfiguration
.TrustedUserCertificates,
telemetry,
ct).ConfigureAwait(true);

if (userCertificateIdentifier != null)
{
userIdentity = UserIdentity.CreateAsync(
userCertificateIdentifier,
new CertificatePasswordProvider(userCertificatePassword),
telemetry,
ct
).GetAwaiter().GetResult();
#pragma warning disable CA2025 // Do not pass 'IDisposable' instances into unawaited tasks
userIdentity = UserIdentity
.CreateAsync(
userCertificateIdentifier,
new CertificatePasswordProvider(userCertificatePassword),
telemetry,
ct)
.GetAwaiter()
.GetResult();
#pragma warning restore CA2025 // Do not pass 'IDisposable' instances into unawaited tasks

Console.WriteLine($"Connect with user certificate with Thumbprint {userCertificateThumbprint}");
}
Expand Down Expand Up @@ -775,18 +777,20 @@ private static async Task<CertificateIdentifier> FindUserCertificateIdentifierAs
{
CertificateIdentifier userCertificateIdentifier = null;

X509Certificate2Collection userCertificatesWithMatchingThumbprint =
using CertificateCollection certificates =
await trustedUserCertificates.GetCertificatesAsync(telemetry, ct).ConfigureAwait(false);
// get user certificate with matching thumbprint
userCertificatesWithMatchingThumbprint =
userCertificatesWithMatchingThumbprint.Find(X509FindType.FindByThumbprint, thumbprint, false);
using CertificateCollection userCertificatesWithMatchingThumbprint =
certificates.Find(X509FindType.FindByThumbprint, thumbprint, false);

// create Certificate Identifier
if (userCertificatesWithMatchingThumbprint.Count == 1)
{
userCertificateIdentifier = new CertificateIdentifier(
userCertificatesWithMatchingThumbprint[0])
Certificate userCert = userCertificatesWithMatchingThumbprint[0];
userCertificateIdentifier = new CertificateIdentifier
{
Thumbprint = userCert.Thumbprint,
SubjectName = userCert.Subject,
StorePath = trustedUserCertificates.StorePath,
StoreType = trustedUserCertificates.StoreType
};
Expand Down
44 changes: 20 additions & 24 deletions Applications/ConsoleReferenceClient/UAClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
using Microsoft.Extensions.Logging;
using Opc.Ua;
using Opc.Ua.Client;
using Opc.Ua.Security.Certificates;

namespace Quickstarts
{
Expand Down Expand Up @@ -70,7 +71,9 @@ public UAClient(
m_logger = telemetry.CreateLogger<UAClient>();
m_telemetry = telemetry;
m_configuration = configuration;
m_configuration.CertificateValidator.CertificateValidation += CertificateValidation;
// Modern global accept hook on ICertificateManager — fires for
// every certificate validation done via this manager.
m_configuration.CertificateManager.AcceptError = AcceptCertificate;
m_reverseConnectManager = reverseConnectManager;
}

Expand All @@ -95,7 +98,7 @@ protected virtual void Dispose(bool disposing)
{
m_reconnectHandler?.Dispose();
Session?.Dispose();
m_configuration.CertificateValidator.CertificateValidation -= CertificateValidation;
m_configuration.CertificateManager.AcceptError = null;
}
m_disposed = true;
}
Expand Down Expand Up @@ -216,13 +219,10 @@ public async Task<bool> ConnectAsync(
cts.Token);
connection = await m_reverseConnectManager
.WaitForConnectionAsync(new Uri(serverUrl), null, linkedCTS.Token)
.ConfigureAwait(false);
if (connection == null)
{
.ConfigureAwait(false) ??
throw new ServiceResultException(
StatusCodes.BadTimeout,
"Waiting for a reverse connection timed out.");
}
if (endpointDescription == null)
{
Console.WriteLine("Discover reverse connection endpoints....");
Expand Down Expand Up @@ -455,41 +455,37 @@ private void Client_ReconnectComplete(object sender, EventArgs e)
}

/// <summary>
/// Handles the certificate validation event.
/// This event is triggered every time an untrusted certificate is received from the server.
/// Per-error accept callback invoked by the new
/// <see cref="ICertificateValidatorEx.AcceptError"/> hook every time
/// an untrusted certificate is received from the server. Returns
/// <see langword="true"/> to accept the error, <see langword="false"/>
/// to reject it.
/// </summary>
protected virtual void CertificateValidation(
CertificateValidator sender,
CertificateValidationEventArgs e)
protected virtual bool AcceptCertificate(Certificate certificate, ServiceResult error)
{
bool certificateAccepted = false;

// ****
// Implement a custom logic to decide if the certificate should be
// accepted or not and set certificateAccepted flag accordingly.
// The certificate can be retrieved from the e.Certificate field
// accepted or not. Return true to accept, false to reject.
// ***

ServiceResult error = e.Error;
m_logger.LogInformation("{Error}", error);
if (error.StatusCode == StatusCodes.BadCertificateUntrusted && AutoAccept)
{
certificateAccepted = true;
}

bool certificateAccepted =
error.StatusCode == StatusCodes.BadCertificateUntrusted && AutoAccept;

if (certificateAccepted)
{
m_logger.LogInformation(
"Untrusted Certificate accepted. Subject = {Subject}",
e.Certificate.Subject);
e.Accept = true;
certificate.Subject);
}
else
{
m_logger.LogInformation(
"Untrusted Certificate rejected. Subject = {Subject}",
e.Certificate.Subject);
certificate.Subject);
}

return certificateAccepted;
}

private readonly Lock m_lock = new();
Expand Down
2 changes: 1 addition & 1 deletion Applications/ConsoleReferencePublisher/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -819,4 +819,4 @@ private static PublishedDataSetDataType CreatePublishedDataSetAllTypes()
return publishedDataSetAllTypes;
}
}
}
}
4 changes: 3 additions & 1 deletion Applications/ConsoleReferenceServer/ConsoleUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -174,12 +174,14 @@ public void ConfigureLogging(
string outputFilePath = configuration.TraceConfiguration.OutputFilePath;
if (!string.IsNullOrWhiteSpace(outputFilePath))
{
#pragma warning disable CA1305 // Specify IFormatProvider
loggerConfiguration.WriteTo.File(
Utils.ReplaceSpecialFolderNames(outputFilePath),
outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff} [{Level:u3}] {Message:lj}{NewLine}{Exception}",
restrictedToMinimumLevel: (LogEventLevel)fileLevel,
outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff} [{Level:u3}] {Message:lj}{NewLine}{Exception}",
rollOnFileSizeLimit: true
);
#pragma warning restore CA1305 // Specify IFormatProvider
}
}

Expand Down
Loading
Loading