/* ======================================================================== * Copyright (c) 2005-2025 The OPC Foundation, Inc. All rights reserved. * * OPC Foundation MIT License 1.00 * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. * * The complete license agreement can be found here: * http://opcfoundation.org/License/MIT/1.00/ * ======================================================================*/ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; using System.Xml; namespace Opc.Ua.Configuration { /// /// A class that builds a configuration for a UA application. /// /// /// This builder uses a fluent staged-interface pattern: callers progress through /// (which initializes /// and /// ), /// then through / /// (which initialize /// and respectively), /// before reaching the per-section setter methods. As a result the /// ! operators on accesses to those nullable sub-configuration objects in /// this class are guaranteed to be non-null at the time the methods are invoked. /// public class ApplicationConfigurationBuilder : IApplicationConfigurationBuilder { /// /// Create the application instance builder. /// public ApplicationConfigurationBuilder(ApplicationInstance applicationInstance) { ApplicationInstance = applicationInstance; } /// /// The application instance used to build the configuration. /// public IApplicationInstance ApplicationInstance { get; } /// /// The application configuration. /// /// /// This builder is constructed by , which assigns /// before returning the builder, /// so the underlying configuration is guaranteed to be non-null. /// public ApplicationConfiguration ApplicationConfiguration => ApplicationInstance.ApplicationConfiguration!; /// public IApplicationConfigurationBuilderGlobalConfiguration SetHiResClockDisabled( bool hiResClockDisabled) { ApplicationConfiguration.DisableHiResClock = hiResClockDisabled; return this; } /// public IApplicationConfigurationBuilderClientSelected AsClient() { switch (ApplicationInstance.ApplicationType) { case ApplicationType.Client: case ApplicationType.ClientAndServer: break; case ApplicationType.Server: ApplicationInstance.ApplicationType = m_typeSelected ? ApplicationType.ClientAndServer : ApplicationType.Client; break; case ApplicationType.DiscoveryServer: throw new ArgumentException("Discovery server type is not allowed as client."); default: throw new ArgumentException("Invalid application type for client."); } m_typeSelected = true; ApplicationConfiguration.ClientConfiguration = new ClientConfiguration(); return this; } /// public IApplicationConfigurationBuilderSecurityOptions AddSecurityConfiguration( string subjectName, string? pkiRoot = null, string? appRoot = null, string? rejectedRoot = null) { pkiRoot = DefaultPKIRoot(pkiRoot); appRoot = appRoot == null ? pkiRoot : DefaultPKIRoot(appRoot); rejectedRoot = rejectedRoot == null ? pkiRoot : DefaultPKIRoot(rejectedRoot); string appStoreType = CertificateStoreIdentifier.DetermineStoreType(appRoot); string pkiRootType = CertificateStoreIdentifier.DetermineStoreType(pkiRoot); string rejectedRootType = CertificateStoreIdentifier.DetermineStoreType(rejectedRoot); ApplicationConfiguration.SecurityConfiguration = new SecurityConfiguration { // app cert store ApplicationCertificate = new CertificateIdentifier { StoreType = appStoreType, StorePath = DefaultCertificateStorePath(TrustlistType.Application, appRoot), SubjectName = Utils.ReplaceDCLocalhost(subjectName) }, // App trusted & issuer TrustedPeerCertificates = new CertificateTrustList { StoreType = pkiRootType, StorePath = DefaultCertificateStorePath(TrustlistType.Trusted, pkiRoot) }, TrustedIssuerCertificates = new CertificateTrustList { StoreType = pkiRootType, StorePath = DefaultCertificateStorePath(TrustlistType.Issuer, pkiRoot) }, // Https trusted & issuer TrustedHttpsCertificates = new CertificateTrustList { StoreType = pkiRootType, StorePath = DefaultCertificateStorePath(TrustlistType.TrustedHttps, pkiRoot) }, HttpsIssuerCertificates = new CertificateTrustList { StoreType = pkiRootType, StorePath = DefaultCertificateStorePath(TrustlistType.IssuerHttps, pkiRoot) }, // User trusted & issuer TrustedUserCertificates = new CertificateTrustList { StoreType = pkiRootType, StorePath = DefaultCertificateStorePath(TrustlistType.TrustedUser, pkiRoot) }, UserIssuerCertificates = new CertificateTrustList { StoreType = pkiRootType, StorePath = DefaultCertificateStorePath(TrustlistType.IssuerUser, pkiRoot) }, // rejected store RejectedCertificateStore = new CertificateStoreIdentifier { StoreType = rejectedRootType, StorePath = DefaultCertificateStorePath(TrustlistType.Rejected, rejectedRoot) } }; SetSecureDefaults(ApplicationConfiguration.SecurityConfiguration); return this; } /// public IApplicationConfigurationBuilderSecurityOptions AddSecurityConfiguration( ArrayOf certIdList, string? pkiRoot = null, string? rejectedRoot = null) { pkiRoot = DefaultPKIRoot(pkiRoot); rejectedRoot = rejectedRoot == null ? pkiRoot : DefaultPKIRoot(rejectedRoot); string pkiRootType = CertificateStoreIdentifier.DetermineStoreType(pkiRoot); string rejectedRootType = CertificateStoreIdentifier.DetermineStoreType(rejectedRoot); ApplicationConfiguration.SecurityConfiguration = new SecurityConfiguration { // app cert store ApplicationCertificates = certIdList, // App trusted & issuer TrustedPeerCertificates = new CertificateTrustList { StoreType = pkiRootType, StorePath = DefaultCertificateStorePath(TrustlistType.Trusted, pkiRoot) }, TrustedIssuerCertificates = new CertificateTrustList { StoreType = pkiRootType, StorePath = DefaultCertificateStorePath(TrustlistType.Issuer, pkiRoot) }, // Https trusted & issuer TrustedHttpsCertificates = new CertificateTrustList { StoreType = pkiRootType, StorePath = DefaultCertificateStorePath(TrustlistType.TrustedHttps, pkiRoot) }, HttpsIssuerCertificates = new CertificateTrustList { StoreType = pkiRootType, StorePath = DefaultCertificateStorePath(TrustlistType.IssuerHttps, pkiRoot) }, // User trusted & issuer TrustedUserCertificates = new CertificateTrustList { StoreType = pkiRootType, StorePath = DefaultCertificateStorePath(TrustlistType.TrustedUser, pkiRoot) }, UserIssuerCertificates = new CertificateTrustList { StoreType = pkiRootType, StorePath = DefaultCertificateStorePath(TrustlistType.IssuerUser, pkiRoot) }, // rejected store RejectedCertificateStore = new CertificateStoreIdentifier { StoreType = rejectedRootType, StorePath = DefaultCertificateStorePath(TrustlistType.Rejected, rejectedRoot) } }; SetSecureDefaults(ApplicationConfiguration.SecurityConfiguration); return this; } /// public IApplicationConfigurationBuilderSecurityOptionStores AddSecurityConfigurationStores( string subjectName, string appRoot, string trustedRoot, string issuerRoot, string? rejectedRoot = null) { string appStoreType = CertificateStoreIdentifier.DetermineStoreType(appRoot); string issuerRootType = CertificateStoreIdentifier.DetermineStoreType(issuerRoot); string trustedRootType = CertificateStoreIdentifier.DetermineStoreType(trustedRoot); rejectedRoot ??= DefaultPKIRoot(null); string rejectedRootType = CertificateStoreIdentifier.DetermineStoreType(rejectedRoot); ApplicationConfiguration.SecurityConfiguration = new SecurityConfiguration { // app cert store ApplicationCertificate = new CertificateIdentifier { StoreType = appStoreType, StorePath = DefaultCertificateStorePath(TrustlistType.Application, appRoot), SubjectName = Utils.ReplaceDCLocalhost(subjectName) }, // App trusted & issuer TrustedPeerCertificates = new CertificateTrustList { StoreType = trustedRootType, StorePath = DefaultCertificateStorePath(TrustlistType.Trusted, trustedRoot) }, TrustedIssuerCertificates = new CertificateTrustList { StoreType = issuerRootType, StorePath = DefaultCertificateStorePath(TrustlistType.Issuer, issuerRoot) }, // rejected store RejectedCertificateStore = new CertificateStoreIdentifier { StoreType = rejectedRootType, StorePath = DefaultCertificateStorePath(TrustlistType.Rejected, rejectedRoot) } }; SetSecureDefaults(ApplicationConfiguration.SecurityConfiguration); return this; } /// public IApplicationConfigurationBuilderSecurityOptionStores AddSecurityConfigurationUserStore( string trustedRoot, string issuerRoot) { string trustedRootType = CertificateStoreIdentifier.DetermineStoreType(trustedRoot); string issuerRootType = CertificateStoreIdentifier.DetermineStoreType(issuerRoot); // User trusted & issuer ApplicationConfiguration.SecurityConfiguration.TrustedUserCertificates = new CertificateTrustList { StoreType = trustedRootType, StorePath = DefaultCertificateStorePath(TrustlistType.TrustedUser, trustedRoot) }; ApplicationConfiguration.SecurityConfiguration.UserIssuerCertificates = new CertificateTrustList { StoreType = issuerRootType, StorePath = DefaultCertificateStorePath(TrustlistType.IssuerUser, issuerRoot) }; return this; } /// public IApplicationConfigurationBuilderSecurityOptionStores AddSecurityConfigurationHttpsStore( string trustedRoot, string issuerRoot) { string trustedRootType = CertificateStoreIdentifier.DetermineStoreType(trustedRoot); string issuerRootType = CertificateStoreIdentifier.DetermineStoreType(issuerRoot); // Https trusted & issuer ApplicationConfiguration.SecurityConfiguration.TrustedHttpsCertificates = new CertificateTrustList { StoreType = trustedRootType, StorePath = DefaultCertificateStorePath( TrustlistType.TrustedHttps, trustedRootType) }; ApplicationConfiguration.SecurityConfiguration.HttpsIssuerCertificates = new CertificateTrustList { StoreType = issuerRootType, StorePath = DefaultCertificateStorePath(TrustlistType.IssuerHttps, issuerRoot) }; return this; } /// public async Task CreateAsync(CancellationToken ct = default) { // sanity checks if (ApplicationInstance.ApplicationType is ApplicationType.Server or ApplicationType.ClientAndServer && ApplicationConfiguration.ServerConfiguration == null) { throw new ArgumentException("ApplicationType Server is not configured."); } if (ApplicationInstance.ApplicationType is ApplicationType.Client or ApplicationType.ClientAndServer && ApplicationConfiguration.ClientConfiguration == null) { throw new ArgumentException("ApplicationType Client is not configured."); } // ensure for a user token policy if (ApplicationConfiguration.ServerConfiguration?.UserTokenPolicies.Count == 0) { ApplicationConfiguration.ServerConfiguration!.UserTokenPolicies += new UserTokenPolicy(UserTokenType.Anonymous); } // ensure for secure transport profiles if (ApplicationConfiguration.ServerConfiguration?.SecurityPolicies.Count == 0) { AddSecurityPolicies(); } // Legacy Utils trace pipeline; kept for 1.5.378 -> 2.0 migration. #pragma warning disable CS0618 // Type or member is obsolete ApplicationConfiguration.TraceConfiguration?.ApplySettings(); #pragma warning restore CS0618 // Type or member is obsolete await ApplicationConfiguration.ValidateAsync(ApplicationInstance.ApplicationType, ct) .ConfigureAwait(false); return ApplicationConfiguration; } /// public IApplicationConfigurationBuilderServerSelected AsServer( string[] baseAddresses, string[]? alternateBaseAddresses = null) { switch (ApplicationInstance.ApplicationType) { case ApplicationType.Client: ApplicationInstance.ApplicationType = m_typeSelected ? ApplicationType.ClientAndServer : ApplicationType.Server; break; case ApplicationType.Server: case ApplicationType.ClientAndServer: break; case ApplicationType.DiscoveryServer: throw new ArgumentException("Discovery server type is not allowed as server."); default: throw new ArgumentException("Invalid application type for server."); } m_typeSelected = true; // configure a server var serverConfiguration = new ServerConfiguration { // by default disable LDS registration MaxRegistrationInterval = 0 }; // base addresses foreach (string baseAddress in baseAddresses) { // ReplaceLocalhost only returns null when its input is null; the foreach variable is non-null. serverConfiguration.BaseAddresses += Utils.ReplaceLocalhost(baseAddress)!; } // alternate base addresses if (alternateBaseAddresses != null) { foreach (string alternateBaseAddress in alternateBaseAddresses) { // ReplaceLocalhost only returns null when its input is null; the foreach variable is non-null. serverConfiguration.AlternateBaseAddresses += Utils.ReplaceLocalhost(alternateBaseAddress)!; } } // add container for policies serverConfiguration.SecurityPolicies = []; // add user token policy container and Anonymous serverConfiguration.UserTokenPolicies = []; ApplicationConfiguration.ServerConfiguration = serverConfiguration; return this; } /// public IApplicationConfigurationBuilderServerSelected AddUnsecurePolicyNone( bool addPolicy = true) { if (addPolicy) { var policies = ApplicationConfiguration .ServerConfiguration! .SecurityPolicies.ToList(); InternalAddPolicy(policies, MessageSecurityMode.None, SecurityPolicies.None); ApplicationConfiguration.ServerConfiguration!.SecurityPolicies = policies.ToArrayOf(); } return this; } /// public IApplicationConfigurationBuilderServerSelected AddSignPolicies( bool addPolicies = true) { if (addPolicies) { AddSecurityPolicies(true, false, false); } return this; } /// public IApplicationConfigurationBuilderServerSelected AddSignAndEncryptPolicies( bool addPolicies = true) { if (addPolicies) { AddSecurityPolicies(false, false, false); } return this; } /// public IApplicationConfigurationBuilderServerSelected AddEccSignPolicies() { AddEccSecurityPolicies(true); return this; } /// public IApplicationConfigurationBuilderServerSelected AddEccSignAndEncryptPolicies() { AddEccSecurityPolicies(false); return this; } /// public IApplicationConfigurationBuilderServerSelected AddPolicy( MessageSecurityMode securityMode, string securityPolicy) { if (!SecurityPolicies.IsValidSecurityPolicyUri(securityPolicy)) { throw new ArgumentException("Unknown security policy", nameof(securityPolicy)); } if (securityMode == MessageSecurityMode.None || securityPolicy.Equals(SecurityPolicies.None, StringComparison.Ordinal)) { throw new ArgumentException("Use AddUnsecurePolicyNone to add no security policy."); } var policies = ApplicationConfiguration.ServerConfiguration!.SecurityPolicies.ToList(); InternalAddPolicy( policies, securityMode, securityPolicy); ApplicationConfiguration.ServerConfiguration!.SecurityPolicies = policies.ToArrayOf(); return this; } /// public IApplicationConfigurationBuilderServerSelected AddUserTokenPolicy( UserTokenType userTokenType) { ApplicationConfiguration.ServerConfiguration!.UserTokenPolicies += new UserTokenPolicy(userTokenType); return this; } /// public IApplicationConfigurationBuilderServerSelected AddUserTokenPolicy( UserTokenPolicy userTokenPolicy) { ApplicationConfiguration.ServerConfiguration!.UserTokenPolicies += userTokenPolicy ?? throw new ArgumentNullException(nameof(userTokenPolicy)); return this; } /// public IApplicationConfigurationBuilderSecurityOptions SetApplicationCertificates( ArrayOf certIdList) { ApplicationConfiguration.SecurityConfiguration.ApplicationCertificates = certIdList; return this; } /// public IApplicationConfigurationBuilderSecurityOptions SetMaxRejectedCertificates( int maxRejectedCertificates) { ApplicationConfiguration.SecurityConfiguration.MaxRejectedCertificates = maxRejectedCertificates; return this; } /// public IApplicationConfigurationBuilderSecurityOptions SetAutoAcceptUntrustedCertificates( bool autoAccept) { ApplicationConfiguration.SecurityConfiguration.AutoAcceptUntrustedCertificates = autoAccept; return this; } /// public IApplicationConfigurationBuilderSecurityOptions SetAddAppCertToTrustedStore( bool addToTrustedStore) { ApplicationConfiguration.SecurityConfiguration.AddAppCertToTrustedStore = addToTrustedStore; return this; } /// public IApplicationConfigurationBuilderSecurityOptions SetRejectSHA1SignedCertificates( bool rejectSHA1Signed) { ApplicationConfiguration.SecurityConfiguration.RejectSHA1SignedCertificates = rejectSHA1Signed; return this; } /// public IApplicationConfigurationBuilderSecurityOptions SetRejectUnknownRevocationStatus( bool rejectUnknownRevocationStatus) { ApplicationConfiguration.SecurityConfiguration.RejectUnknownRevocationStatus = rejectUnknownRevocationStatus; return this; } /// public IApplicationConfigurationBuilderSecurityOptions SetUseValidatedCertificates( bool useValidatedCertificates) { ApplicationConfiguration.SecurityConfiguration.UseValidatedCertificates = useValidatedCertificates; return this; } /// public IApplicationConfigurationBuilderSecurityOptions SetSuppressNonceValidationErrors( bool suppressNonceValidationErrors) { ApplicationConfiguration.SecurityConfiguration.SuppressNonceValidationErrors = suppressNonceValidationErrors; return this; } /// public IApplicationConfigurationBuilderSecurityOptions SetSendCertificateChain( bool sendCertificateChain) { ApplicationConfiguration.SecurityConfiguration.SendCertificateChain = sendCertificateChain; return this; } /// public IApplicationConfigurationBuilderSecurityOptions SetMinimumCertificateKeySize( ushort keySize) { ApplicationConfiguration.SecurityConfiguration.MinimumCertificateKeySize = keySize; return this; } /// public IApplicationConfigurationBuilderSecurityOptions AddCertificatePasswordProvider( ICertificatePasswordProvider certificatePasswordProvider) { ApplicationConfiguration.SecurityConfiguration.CertificatePasswordProvider = certificatePasswordProvider; return this; } /// public IApplicationConfigurationBuilderTransportQuotasSet SetTransportQuotas( TransportQuotas transportQuotas) { ApplicationConfiguration.TransportQuotas = transportQuotas; return this; } /// public IApplicationConfigurationBuilderTransportQuotas SetOperationTimeout( int operationTimeout) { ApplicationConfiguration.TransportQuotas!.OperationTimeout = operationTimeout; return this; } /// public IApplicationConfigurationBuilderTransportQuotas SetMaxStringLength( int maxStringLength) { ApplicationConfiguration.TransportQuotas!.MaxStringLength = maxStringLength; return this; } /// public IApplicationConfigurationBuilderTransportQuotas SetMaxByteStringLength( int maxByteStringLength) { ApplicationConfiguration.TransportQuotas!.MaxByteStringLength = maxByteStringLength; return this; } /// public IApplicationConfigurationBuilderTransportQuotas SetMaxArrayLength(int maxArrayLength) { ApplicationConfiguration.TransportQuotas!.MaxArrayLength = maxArrayLength; return this; } /// public IApplicationConfigurationBuilderTransportQuotas SetMaxMessageSize(int maxMessageSize) { ApplicationConfiguration.TransportQuotas!.MaxMessageSize = maxMessageSize; return this; } /// public IApplicationConfigurationBuilderTransportQuotas SetMaxBufferSize(int maxBufferSize) { ApplicationConfiguration.TransportQuotas!.MaxBufferSize = maxBufferSize; return this; } /// public IApplicationConfigurationBuilderTransportQuotas SetChannelLifetime( int channelLifetime) { ApplicationConfiguration.TransportQuotas!.ChannelLifetime = channelLifetime; return this; } /// public IApplicationConfigurationBuilderTransportQuotas SetSecurityTokenLifetime( int securityTokenLifetime) { ApplicationConfiguration.TransportQuotas!.SecurityTokenLifetime = securityTokenLifetime; return this; } /// public IApplicationConfigurationBuilderTransportQuotas SetMaxEncodingNestingLevels( int maxEncodingNestingLevels) { ApplicationConfiguration.TransportQuotas!.MaxEncodingNestingLevels = maxEncodingNestingLevels; return this; } /// public IApplicationConfigurationBuilderTransportQuotas SetMaxDecoderRecoveries( int maxDecoderRecoveries) { ApplicationConfiguration.TransportQuotas!.MaxDecoderRecoveries = maxDecoderRecoveries; return this; } /// public IApplicationConfigurationBuilderServerOptions SetMinRequestThreadCount( int minRequestThreadCount) { ApplicationConfiguration.ServerConfiguration!.MinRequestThreadCount = minRequestThreadCount; return this; } /// public IApplicationConfigurationBuilderServerOptions SetMaxRequestThreadCount( int maxRequestThreadCount) { ApplicationConfiguration.ServerConfiguration!.MaxRequestThreadCount = maxRequestThreadCount; return this; } /// public IApplicationConfigurationBuilderServerOptions SetMaxQueuedRequestCount( int maxQueuedRequestCount) { ApplicationConfiguration.ServerConfiguration!.MaxQueuedRequestCount = maxQueuedRequestCount; return this; } /// public IApplicationConfigurationBuilderServerOptions SetDiagnosticsEnabled( bool diagnosticsEnabled) { ApplicationConfiguration.ServerConfiguration!.DiagnosticsEnabled = diagnosticsEnabled; return this; } /// public IApplicationConfigurationBuilderServerOptions SetMaxSessionCount(int maxSessionCount) { ApplicationConfiguration.ServerConfiguration!.MaxSessionCount = maxSessionCount; return this; } /// public IApplicationConfigurationBuilderServerOptions SetMaxChannelCount(int maxChannelCount) { ApplicationConfiguration.ServerConfiguration!.MaxChannelCount = maxChannelCount; return this; } /// public IApplicationConfigurationBuilderServerOptions SetMinSessionTimeout( int minSessionTimeout) { ApplicationConfiguration.ServerConfiguration!.MinSessionTimeout = minSessionTimeout; return this; } /// public IApplicationConfigurationBuilderServerOptions SetMaxSessionTimeout( int maxSessionTimeout) { ApplicationConfiguration.ServerConfiguration!.MaxSessionTimeout = maxSessionTimeout; return this; } /// public IApplicationConfigurationBuilderServerOptions SetMaxBrowseContinuationPoints( int maxBrowseContinuationPoints) { ApplicationConfiguration.ServerConfiguration!.MaxBrowseContinuationPoints = maxBrowseContinuationPoints; return this; } /// public IApplicationConfigurationBuilderServerOptions SetMaxQueryContinuationPoints( int maxQueryContinuationPoints) { ApplicationConfiguration.ServerConfiguration!.MaxQueryContinuationPoints = maxQueryContinuationPoints; return this; } /// public IApplicationConfigurationBuilderServerOptions SetMaxHistoryContinuationPoints( int maxHistoryContinuationPoints) { ApplicationConfiguration.ServerConfiguration!.MaxHistoryContinuationPoints = maxHistoryContinuationPoints; return this; } /// public IApplicationConfigurationBuilderServerOptions SetMaxRequestAge(int maxRequestAge) { ApplicationConfiguration.ServerConfiguration!.MaxRequestAge = maxRequestAge; return this; } /// public IApplicationConfigurationBuilderServerOptions SetMinPublishingInterval( int minPublishingInterval) { ApplicationConfiguration.ServerConfiguration!.MinPublishingInterval = minPublishingInterval; return this; } /// public IApplicationConfigurationBuilderServerOptions SetMaxPublishingInterval( int maxPublishingInterval) { ApplicationConfiguration.ServerConfiguration!.MaxPublishingInterval = maxPublishingInterval; return this; } /// public IApplicationConfigurationBuilderServerOptions SetPublishingResolution( int publishingResolution) { ApplicationConfiguration.ServerConfiguration!.PublishingResolution = publishingResolution; return this; } /// public IApplicationConfigurationBuilderServerOptions SetMaxSubscriptionLifetime( int maxSubscriptionLifetime) { ApplicationConfiguration.ServerConfiguration!.MaxSubscriptionLifetime = maxSubscriptionLifetime; return this; } /// public IApplicationConfigurationBuilderServerOptions SetMaxMessageQueueSize( int maxMessageQueueSize) { ApplicationConfiguration.ServerConfiguration!.MaxMessageQueueSize = maxMessageQueueSize; return this; } /// public IApplicationConfigurationBuilderServerOptions SetMaxNotificationQueueSize( int maxNotificationQueueSize) { ApplicationConfiguration.ServerConfiguration!.MaxNotificationQueueSize = maxNotificationQueueSize; return this; } /// public IApplicationConfigurationBuilderServerOptions SetMaxNotificationsPerPublish( int maxNotificationsPerPublish) { ApplicationConfiguration.ServerConfiguration!.MaxNotificationsPerPublish = maxNotificationsPerPublish; return this; } /// public IApplicationConfigurationBuilderServerOptions SetMinMetadataSamplingInterval( int minMetadataSamplingInterval) { ApplicationConfiguration.ServerConfiguration!.MinMetadataSamplingInterval = minMetadataSamplingInterval; return this; } /// public IApplicationConfigurationBuilderServerOptions SetAvailableSamplingRates( ArrayOf availableSampleRates) { ApplicationConfiguration.ServerConfiguration!.AvailableSamplingRates = availableSampleRates; return this; } /// public IApplicationConfigurationBuilderServerOptions SetRegistrationEndpoint( EndpointDescription registrationEndpoint) { ApplicationConfiguration.ServerConfiguration!.RegistrationEndpoint = registrationEndpoint; return this; } /// public IApplicationConfigurationBuilderServerOptions SetMaxRegistrationInterval( int maxRegistrationInterval) { ApplicationConfiguration.ServerConfiguration!.MaxRegistrationInterval = maxRegistrationInterval; return this; } /// public IApplicationConfigurationBuilderServerOptions SetNodeManagerSaveFile( string nodeManagerSaveFile) { ApplicationConfiguration.ServerConfiguration!.NodeManagerSaveFile = nodeManagerSaveFile; return this; } /// public IApplicationConfigurationBuilderServerOptions SetMinSubscriptionLifetime( int minSubscriptionLifetime) { ApplicationConfiguration.ServerConfiguration!.MinSubscriptionLifetime = minSubscriptionLifetime; return this; } /// public IApplicationConfigurationBuilderServerOptions SetMaxPublishRequestCount( int maxPublishRequestCount) { ApplicationConfiguration.ServerConfiguration!.MaxPublishRequestCount = maxPublishRequestCount; return this; } /// public IApplicationConfigurationBuilderServerOptions SetMaxSubscriptionCount( int maxSubscriptionCount) { ApplicationConfiguration.ServerConfiguration!.MaxSubscriptionCount = maxSubscriptionCount; return this; } /// public IApplicationConfigurationBuilderServerOptions SetMaxEventQueueSize( int setMaxEventQueueSize) { ApplicationConfiguration.ServerConfiguration!.MaxEventQueueSize = setMaxEventQueueSize; return this; } /// public IApplicationConfigurationBuilderServerOptions AddServerProfile(string serverProfile) { ApplicationConfiguration.ServerConfiguration!.ServerProfileArray += serverProfile; return this; } /// public IApplicationConfigurationBuilderServerOptions SetShutdownDelay(int shutdownDelay) { ApplicationConfiguration.ServerConfiguration!.ShutdownDelay = shutdownDelay; return this; } /// public IApplicationConfigurationBuilderServerOptions AddServerCapabilities( string serverCapability) { ApplicationConfiguration.ServerConfiguration!.ServerCapabilities += serverCapability; return this; } /// public IApplicationConfigurationBuilderServerOptions SetSupportedPrivateKeyFormats( ArrayOf supportedPrivateKeyFormats) { ApplicationConfiguration.ServerConfiguration!.SupportedPrivateKeyFormats = supportedPrivateKeyFormats; return this; } /// public IApplicationConfigurationBuilderServerOptions SetMaxTrustListSize( int maxTrustListSize) { ApplicationConfiguration.ServerConfiguration!.MaxTrustListSize = maxTrustListSize; return this; } /// public IApplicationConfigurationBuilderServerOptions SetMultiCastDnsEnabled( bool multiCastDnsEnabled) { ApplicationConfiguration.ServerConfiguration!.MultiCastDnsEnabled = multiCastDnsEnabled; return this; } /// public IApplicationConfigurationBuilderServerOptions SetReverseConnect( ReverseConnectServerConfiguration reverseConnectConfiguration) { ApplicationConfiguration.ServerConfiguration!.ReverseConnect = reverseConnectConfiguration; return this; } /// public IApplicationConfigurationBuilderServerOptions SetOperationLimits( OperationLimits operationLimits) { ApplicationConfiguration.ServerConfiguration!.OperationLimits = operationLimits; return this; } /// public IApplicationConfigurationBuilderServerOptions SetAuditingEnabled( bool auditingEnabled) { ApplicationConfiguration.ServerConfiguration!.AuditingEnabled = auditingEnabled; return this; } /// public IApplicationConfigurationBuilderServerOptions SetHttpsMutualTls( bool mTlsEnabled) { ApplicationConfiguration.ServerConfiguration!.HttpsMutualTls = mTlsEnabled; return this; } /// public IApplicationConfigurationBuilderClientOptions SetDefaultSessionTimeout( int defaultSessionTimeout) { ApplicationConfiguration.ClientConfiguration!.DefaultSessionTimeout = defaultSessionTimeout; return this; } /// public IApplicationConfigurationBuilderClientOptions AddWellKnownDiscoveryUrls( string wellKnownDiscoveryUrl) { ApplicationConfiguration.ClientConfiguration!.WellKnownDiscoveryUrls += wellKnownDiscoveryUrl; return this; } /// public IApplicationConfigurationBuilderClientOptions AddDiscoveryServer( EndpointDescription discoveryServer) { ApplicationConfiguration.ClientConfiguration!.DiscoveryServers += discoveryServer; return this; } /// public IApplicationConfigurationBuilderClientOptions SetEndpointCacheFilePath( string endpointCacheFilePath) { ApplicationConfiguration.ClientConfiguration!.EndpointCacheFilePath = endpointCacheFilePath; return this; } /// IApplicationConfigurationBuilderClientOptions IApplicationConfigurationBuilderClientOptions.SetMinSubscriptionLifetime( int minSubscriptionLifetime) { ApplicationConfiguration.ClientConfiguration!.MinSubscriptionLifetime = minSubscriptionLifetime; return this; } /// public IApplicationConfigurationBuilderClientOptions SetReverseConnect( ReverseConnectClientConfiguration reverseConnect) { ApplicationConfiguration.ClientConfiguration!.ReverseConnect = reverseConnect; return this; } /// public IApplicationConfigurationBuilderClientOptions SetClientOperationLimits( OperationLimits operationLimits) { ApplicationConfiguration.ClientConfiguration!.OperationLimits = operationLimits; return this; } /// public IApplicationConfigurationBuilderTraceConfiguration SetOutputFilePath( string outputFilePath) { ApplicationConfiguration.TraceConfiguration!.OutputFilePath = outputFilePath; return this; } /// public IApplicationConfigurationBuilderTraceConfiguration SetDeleteOnLoad(bool deleteOnLoad) { ApplicationConfiguration.TraceConfiguration!.DeleteOnLoad = deleteOnLoad; return this; } /// public IApplicationConfigurationBuilderTraceConfiguration SetTraceMasks(int traceMasks) { ApplicationConfiguration.TraceConfiguration!.TraceMasks = traceMasks; return this; } /// [Experimental("UA_NETStandard_1")] public IApplicationConfigurationBuilderExtension AddExtension( XmlQualifiedName elementName, T value, Action encoderFunc) { ApplicationConfiguration.UpdateExtension(elementName, value, encoderFunc); return this; } /// public IApplicationConfigurationBuilderExtension AddExtension( XmlQualifiedName? elementName, T value) where T : IEncodeable { ApplicationConfiguration.UpdateExtension(elementName, value); return this; } /// public IApplicationConfigurationBuilderServerOptions SetDurableSubscriptionsEnabled( bool durableSubscriptionsEnabled) { ApplicationConfiguration.ServerConfiguration!.DurableSubscriptionsEnabled = durableSubscriptionsEnabled; return this; } /// public IApplicationConfigurationBuilderServerOptions SetMaxDurableNotificationQueueSize( int maxDurableNotificationQueueSize) { ApplicationConfiguration.ServerConfiguration!.MaxDurableNotificationQueueSize = maxDurableNotificationQueueSize; return this; } /// public IApplicationConfigurationBuilderServerOptions SetMaxDurableEventQueueSize( int maxDurableEventQueueSize) { ApplicationConfiguration.ServerConfiguration!.MaxDurableEventQueueSize = maxDurableEventQueueSize; return this; } /// public IApplicationConfigurationBuilderServerOptions SetMaxDurableSubscriptionLifetime( int maxDurableSubscriptionLifetimeInHours) { ApplicationConfiguration.ServerConfiguration!.MaxDurableSubscriptionLifetimeInHours = maxDurableSubscriptionLifetimeInHours; return this; } /// /// Create ApplicationCertificates from a PKI root. /// /// The subject name. /// The cert store type: ex: "Directory" /// The PKI root. /// The application certificates. public static ArrayOf CreateDefaultApplicationCertificates( string subjectName, string? storeType = null, string? storePath = null) { var certificateIdentifiers = new List { new() { StoreType = storeType, StorePath = storePath, SubjectName = subjectName, CertificateType = ObjectTypeIds.RsaSha256ApplicationCertificateType }, new() { StoreType = storeType, StorePath = storePath, SubjectName = subjectName, CertificateType = ObjectTypeIds.EccNistP256ApplicationCertificateType }, new() { StoreType = storeType, StorePath = storePath, SubjectName = subjectName, CertificateType = ObjectTypeIds.EccNistP384ApplicationCertificateType } }; if (!RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { certificateIdentifiers.AddRange( [ new CertificateIdentifier { StoreType = storeType, StorePath = storePath, SubjectName = subjectName, CertificateType = ObjectTypeIds .EccBrainpoolP256r1ApplicationCertificateType }, new CertificateIdentifier { StoreType = storeType, StorePath = storePath, SubjectName = subjectName, CertificateType = ObjectTypeIds .EccBrainpoolP384r1ApplicationCertificateType } ]); } return certificateIdentifiers.ToArrayOf(); } /// /// Internal enumeration of supported trust lists. /// private enum TrustlistType { Application, Trusted, Issuer, TrustedHttps, IssuerHttps, TrustedUser, IssuerUser, Rejected } /// /// Return the default PKI root path if root is unspecified, directory or X509Store. /// /// A real root path or the store type. private static string DefaultPKIRoot(string? root) { if (root == null || root.Equals(CertificateStoreType.Directory, StringComparison.OrdinalIgnoreCase)) { return CertificateStoreIdentifier.DefaultPKIRoot; } else if (root.Equals( CertificateStoreType.X509Store, StringComparison.OrdinalIgnoreCase)) { return CertificateStoreIdentifier.CurrentUser; } return root; } /// /// Determine the default store path for a given trust list type. /// /// The trust list type. /// A PKI root for which the store path is needed. /// private static string DefaultCertificateStorePath( TrustlistType trustListType, string pkiRoot) { string pkiRootType = CertificateStoreIdentifier.DetermineStoreType(pkiRoot); if (pkiRootType.Equals( CertificateStoreType.Directory, StringComparison.OrdinalIgnoreCase)) { // see https://reference.opcfoundation.org/v104/GDS/docs/F.1/ string leafPath; switch (trustListType) { case TrustlistType.Application: leafPath = "own"; break; case TrustlistType.Trusted: leafPath = "trusted"; break; case TrustlistType.Issuer: leafPath = "issuer"; break; case TrustlistType.TrustedHttps: leafPath = "trustedHttps"; break; case TrustlistType.IssuerHttps: leafPath = "issuerHttps"; break; case TrustlistType.TrustedUser: leafPath = "trustedUser"; break; case TrustlistType.IssuerUser: leafPath = "issuerUser"; break; case TrustlistType.Rejected: leafPath = "rejected"; break; default: leafPath = string.Empty; break; } // Caller may have already provided the leaf path, then no need to add. int startIndex = pkiRoot.Length - leafPath.Length; char lastChar = pkiRoot[^1]; if (lastChar == Path.DirectorySeparatorChar || lastChar == Path.AltDirectorySeparatorChar) { startIndex--; } if (startIndex > 0 && pkiRoot .Substring(startIndex, leafPath.Length) .Equals(leafPath, StringComparison.OrdinalIgnoreCase)) { return pkiRoot; } return Path.Combine(pkiRoot, leafPath); } else if (pkiRootType.Equals( CertificateStoreType.X509Store, StringComparison.OrdinalIgnoreCase)) { switch (trustListType) { case TrustlistType.Application: #if !NETFRAMEWORK if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && pkiRoot.StartsWith( CertificateStoreIdentifier.CurrentUser, StringComparison.OrdinalIgnoreCase)) { return pkiRoot + "My"; } #endif return pkiRoot + "UA_MachineDefault"; case TrustlistType.Trusted: return pkiRoot + "UA_Trusted"; case TrustlistType.Issuer: return pkiRoot + "UA_Issuer"; case TrustlistType.TrustedHttps: return pkiRoot + "UA_Trusted_Https"; case TrustlistType.IssuerHttps: return pkiRoot + "UA_Issuer_Https"; case TrustlistType.TrustedUser: return pkiRoot + "UA_Trusted_User"; case TrustlistType.IssuerUser: return pkiRoot + "UA_Issuer_User"; case TrustlistType.Rejected: return pkiRoot + "UA_Rejected"; default: throw new NotSupportedException("Unsupported store type."); } } else { // return custom root store return pkiRoot; } } /// /// Add specified groups of security policies and security modes. /// /// Include the Sign only policies. /// Include the deprecated policies. /// Include policy 'None'. (no security!) private void AddSecurityPolicies( bool includeSign = false, bool deprecated = false, bool policyNone = false) { // create list of supported policies List defaultPolicyUris = [.. SecurityPolicies .GetDefaultUris()]; if (deprecated) { defaultPolicyUris.AddRange(SecurityPolicies.GetDefaultDeprecatedUris()); } #if NET5_0_OR_GREATER foreach (MessageSecurityMode securityMode in Enum.GetValues()) #else foreach (MessageSecurityMode securityMode in typeof(MessageSecurityMode) .GetEnumValues()) #endif { var policies = ApplicationConfiguration .ServerConfiguration! .SecurityPolicies.ToList(); if (policyNone && securityMode == MessageSecurityMode.None) { InternalAddPolicy(policies, MessageSecurityMode.None, SecurityPolicies.None); } else if (securityMode >= MessageSecurityMode.SignAndEncrypt || (includeSign && securityMode == MessageSecurityMode.Sign)) { foreach (string policyUri in defaultPolicyUris) { InternalAddPolicy(policies, securityMode, policyUri); } } ApplicationConfiguration.ServerConfiguration!.SecurityPolicies = policies.ToArrayOf(); } } /// /// Add default Ecc policies. /// private void AddEccSecurityPolicies(bool sign = false) { // create list of supported policies string[] defaultPolicyUris = SecurityPolicies.GetDefaultEccUris(); MessageSecurityMode securityMode = sign ? MessageSecurityMode.Sign : MessageSecurityMode.SignAndEncrypt; { var policies = ApplicationConfiguration .ServerConfiguration! .SecurityPolicies.ToList(); foreach (string policyUri in defaultPolicyUris) { InternalAddPolicy(policies, securityMode, policyUri); } ApplicationConfiguration.ServerConfiguration!.SecurityPolicies = policies.ToArrayOf(); } } /// /// Set secure defaults for flags. /// private static void SetSecureDefaults(SecurityConfiguration securityConfiguration) { // ensure secure default settings securityConfiguration.AutoAcceptUntrustedCertificates = false; securityConfiguration.AddAppCertToTrustedStore = false; securityConfiguration.RejectSHA1SignedCertificates = true; securityConfiguration.RejectUnknownRevocationStatus = true; securityConfiguration.SuppressNonceValidationErrors = false; securityConfiguration.SendCertificateChain = true; securityConfiguration.MinimumCertificateKeySize = CertificateFactory.DefaultKeySize; securityConfiguration.MaxRejectedCertificates = 5; } /// /// Add security policy if it doesn't exist yet. /// /// The list to which the policies are added. /// The message security mode. /// The security policy Uri. /// private static bool InternalAddPolicy( List policies, MessageSecurityMode securityMode, string policyUri) { if (securityMode == MessageSecurityMode.Invalid) { throw new ArgumentException("Invalid security mode selected", nameof(securityMode)); } var newPolicy = new ServerSecurityPolicy { SecurityMode = securityMode, SecurityPolicyUri = policyUri }; if (policies.Find(s => s.SecurityMode == newPolicy.SecurityMode && string.Equals( s.SecurityPolicyUri, newPolicy.SecurityPolicyUri, StringComparison.Ordinal) ) == null) { policies.Add(newPolicy); return true; } return false; } private bool m_typeSelected; } }