/// <summary>
 /// Register with the server certificate.
 /// </summary>
 /// <param name="serverCertificate"></param>
 /// <param name="endpointUrl"></param>
 /// <param name="onConnectionWaiting"></param>
 public Registration(
     X509Certificate2 serverCertificate,
     Uri endpointUrl,
     EventHandler <ConnectionWaitingEventArgs> onConnectionWaiting) :
     this(endpointUrl, onConnectionWaiting)
 {
     ServerUri = X509Utils.GetApplicationUriFromCertificate(serverCertificate);
 }
Exemple #2
0
        public async void CreateCertificate()
        {
            ApplicationInstance application = new ApplicationInstance {
                ApplicationType   = ApplicationType.Client,
                ConfigSectionName = "Opc.Ua.SampleClient"
            };

            if (Device.RuntimePlatform == "Android")
            {
                string currentFolder = DependencyService.Get <IPathService>().PublicExternalFolder.ToString();
                string filename      = application.ConfigSectionName + ".Config.xml";
                string content       = DependencyService.Get <IAssetService>().LoadFile(filename);

                File.WriteAllText(currentFolder + filename, content);
                // load the application configuration.
                config = await application.LoadApplicationConfiguration(currentFolder + filename, false);
            }
            else
            {
                // load the application configuration.
                config = await application.LoadApplicationConfiguration(false);
            }

            // check the application certificate.
            haveAppCertificate = await application.CheckApplicationInstanceCertificate(false, 0);

            switch (Device.RuntimePlatform)
            {
            case "Android":
                config.ApplicationName = "OPC UA Xamarin Sample Client Android";
                break;

            case "UWP":
                config.ApplicationName = "OPC UA Xamarin Sample Client UWP";
                break;

            case "iOS":
                config.ApplicationName = "OPC UA Xamarin Sample Client IOS";
                break;
            }

            if (haveAppCertificate)
            {
                config.ApplicationUri = X509Utils.GetApplicationUriFromCertificate(config.SecurityConfiguration.ApplicationCertificate.Certificate);
                config.CertificateValidator.CertificateValidation += new CertificateValidationEventHandler(CertificateValidator_CertificateValidation);
            }
        }
        /// <summary>
        /// Create OPC UA client configuration
        /// </summary>
        /// <returns></returns>
        /// <param name="clientName"></param>
        /// <param name="configPath"></param>
        /// <exception cref="ArgumentNullException"></exception>
        public async Task <ApplicationConfiguration> CreateOpcUaConfiguration(string clientName,
                                                                              string configPath)
        {
            if (string.IsNullOrWhiteSpace(clientName))
            {
                throw new ArgumentNullException(nameof(clientName));
            }
            if (string.IsNullOrWhiteSpace(configPath))
            {
                throw new ArgumentNullException(nameof(configPath));
            }

            var application = new ApplicationInstance
            {
                ApplicationName   = clientName,
                ApplicationType   = ApplicationType.Client,
                ConfigSectionName = clientName,
            };

            var clientApplicationConfig = await application.LoadApplicationConfiguration(filePath : configPath,
                                                                                         silent : false);

            var haveAppCertificate = await application.CheckApplicationInstanceCertificate(silent : false,
                                                                                           minimumKeySize : 0);

            if (!haveAppCertificate)
            {
                throw new Exception(Text.InvalidCertificate);
            }

            clientApplicationConfig.ApplicationUri = X509Utils.GetApplicationUriFromCertificate(certificate: clientApplicationConfig.SecurityConfiguration.ApplicationCertificate.Certificate);

            clientApplicationConfig.CertificateValidator.CertificateValidation += new CertificateValidationEventHandler(this.SessionAutoAcceptCertificate_Event);

            return(clientApplicationConfig);
        }
Exemple #4
0
        /// <summary>
        /// Creates an application instance certificate if one does not already exist.
        /// </summary>
        private static async Task <bool> CheckApplicationInstanceCertificate(
            ApplicationConfiguration configuration,
            X509Certificate2 certificate,
            bool silent,
            ushort minimumKeySize)
        {
            if (certificate == null)
            {
                return(false);
            }

            Utils.Trace(Utils.TraceMasks.Information, "Checking application instance certificate. {0}", certificate.Subject);

            try
            {
                // validate certificate.
                configuration.CertificateValidator.Validate(certificate);
            }
            catch (Exception ex)
            {
                string message = Utils.Format(
                    "Error validating certificate. Exception: {0}. Use certificate anyway?", ex.Message);
                if (!await ApproveMessage(message, silent))
                {
                    return(false);
                }
            }

            // check key size.
            int keySize = X509Utils.GetRSAPublicKeySize(certificate);

            if (minimumKeySize > keySize)
            {
                string message = Utils.Format(
                    "The key size ({0}) in the certificate is less than the minimum provided ({1}). Use certificate anyway?",
                    keySize,
                    minimumKeySize);

                if (!await ApproveMessage(message, silent))
                {
                    return(false);
                }
            }

            // check domains.
            if (configuration.ApplicationType != ApplicationType.Client)
            {
                if (!await CheckDomainsInCertificate(configuration, certificate, silent))
                {
                    return(false);
                }
            }

            // check uri.
            string applicationUri = X509Utils.GetApplicationUriFromCertificate(certificate);

            if (String.IsNullOrEmpty(applicationUri))
            {
                string message = "The Application URI could not be read from the certificate. Use certificate anyway?";
                if (!await ApproveMessage(message, silent))
                {
                    return(false);
                }
            }
            else
            {
                configuration.ApplicationUri = applicationUri;
            }

            // update configuration.
            configuration.SecurityConfiguration.ApplicationCertificate.Certificate = certificate;

            return(true);
        }
            private async Task ConsoleClient()
            {
                Log(conn_name + " - " + "Create an Application Configuration...");
                exitCode = ExitCode.ErrorCreateApplication;

                ApplicationInstance application = new ApplicationInstance
                {
                    ApplicationName   = "JSON-SCADA OPC-UA Client",
                    ApplicationType   = ApplicationType.Client,
                    ConfigSectionName = "",
                };

                bool haveAppCertificate         = false;
                ApplicationConfiguration config = null;

                try
                {
                    // load the application configuration.
                    Log(conn_name + " - " + "Load config from " + OPCUA_conn.configFileName);
                    config = await application.LoadApplicationConfiguration(OPCUA_conn.configFileName, false);

                    // config.SecurityConfiguration.AutoAcceptUntrustedCertificates = true;

                    // check the application certificate.
                    haveAppCertificate = await application.CheckApplicationInstanceCertificate(false, 0);

                    if (!haveAppCertificate)
                    {
                        Log(conn_name + " - " + "FATAL: Application instance certificate invalid!", LogLevelNoLog);
                        Environment.Exit(1);
                    }

                    if (haveAppCertificate)
                    {
                        config.ApplicationUri = X509Utils.GetApplicationUriFromCertificate(config.SecurityConfiguration.ApplicationCertificate.Certificate);
                        if (config.SecurityConfiguration.AutoAcceptUntrustedCertificates)
                        {
                            autoAccept = true;
                        }
                        config.CertificateValidator.CertificateValidation += new CertificateValidationEventHandler(CertificateValidator_CertificateValidation);
                    }
                    else
                    {
                        Log(conn_name + " - " + "WARN: missing application certificate, using unsecure connection.");
                    }
                }
                catch (Exception e)
                {
                    Log(conn_name + " - WARN: " + e.Message);
                }

                if (config == null)
                {
                    Log(conn_name + " - " + "FATAL: error in XML config file!", LogLevelNoLog);
                    Environment.Exit(1);
                }

                try
                {
                    Log(conn_name + " - " + "Discover endpoints of " + OPCUA_conn.endpointURLs[0]);
                    exitCode = ExitCode.ErrorDiscoverEndpoints;
                    var selectedEndpoint = CoreClientUtils.SelectEndpoint(OPCUA_conn.endpointURLs[0], haveAppCertificate && OPCUA_conn.useSecurity, 15000);
                    Log(conn_name + " - " + "Selected endpoint uses: " +
                        selectedEndpoint.SecurityPolicyUri.Substring(selectedEndpoint.SecurityPolicyUri.LastIndexOf('#') + 1));

                    Log(conn_name + " - " + "Create a session with OPC UA server.");
                    exitCode = ExitCode.ErrorCreateSession;
                    var endpointConfiguration = EndpointConfiguration.Create(config);
                    var endpoint = new ConfiguredEndpoint(null, selectedEndpoint, endpointConfiguration);

                    await Task.Delay(50);

                    session = await Session.Create(config, endpoint, false, "OPC UA Console Client", 60000, new UserIdentity(new AnonymousIdentityToken()), null);

                    // Log("" + session.KeepAliveInterval); // default is 5000
                    session.KeepAliveInterval = System.Convert.ToInt32(OPCUA_conn.timeoutMs);

                    // register keep alive handler
                    session.KeepAlive += Client_KeepAlive;
                }
                catch (Exception e)
                {
                    Log(conn_name + " - WARN: " + e.Message);
                }

                if (session == null)
                {
                    Log(conn_name + " - " + "FATAL: error creating session!", LogLevelNoLog);
                    Environment.Exit(1);
                }

                Log(conn_name + " - " + "Browsing the OPC UA server namespace.");
                exitCode = ExitCode.ErrorBrowseNamespace;

                await FindObjects(session, ObjectIds.ObjectsFolder);

                await Task.Delay(50);

                Log(conn_name + " - " + "Add a list of items (server current time and status) to the subscription.");
                exitCode = ExitCode.ErrorMonitoredItem;
                ListMon.ForEach(i => i.Notification += OnNotification);
                //ListMon.ForEach(i => i.SamplingInterval = System.Convert.ToInt32(System.Convert.ToDouble(OPCUA_conn.autoCreateTagSamplingInterval) * 1000);
                // ListMon.ForEach(i => Log(conn_name + " - " + i.DisplayName));
                Log(conn_name + " - " + ListMon.Count + " Objects found");

                Log(conn_name + " - " + "Create a subscription with publishing interval of " + System.Convert.ToDouble(OPCUA_conn.autoCreateTagPublishingInterval) + "seconds");
                exitCode = ExitCode.ErrorCreateSubscription;
                var subscription =
                    new Subscription(session.DefaultSubscription)
                {
                    PublishingInterval = System.Convert.ToInt32(System.Convert.ToDouble(OPCUA_conn.autoCreateTagPublishingInterval) * 1000),
                    PublishingEnabled  = true
                };

                await Task.Delay(50);

                subscription.AddItems(ListMon);

                await Task.Delay(50);

                Log(conn_name + " - " + "Add the subscription to the session.");
                Log(conn_name + " - " + subscription.MonitoredItemCount + " Monitored items");
                exitCode = ExitCode.ErrorAddSubscription;
                session.AddSubscription(subscription);
                subscription.Create();

                subscription.ApplyChanges();

                Log(conn_name + " - " + "Running...");
                exitCode = ExitCode.ErrorRunning;
            }
Exemple #6
0
        public static void VerifySignedApplicationCert(ApplicationTestData testApp, byte[] rawSignedCert, byte[][] rawIssuerCerts)
        {
            X509Certificate2 signedCert = new X509Certificate2(rawSignedCert);
            X509Certificate2 issuerCert = new X509Certificate2(rawIssuerCerts[0]);

            TestContext.Out.WriteLine($"Signed cert: {signedCert}");
            TestContext.Out.WriteLine($"Issuer cert: {issuerCert}");

            Assert.NotNull(signedCert);
            Assert.False(signedCert.HasPrivateKey);
            Assert.True(X509Utils.CompareDistinguishedName(testApp.Subject, signedCert.Subject));
            Assert.False(X509Utils.CompareDistinguishedName(signedCert.Issuer, signedCert.Subject));
            Assert.True(X509Utils.CompareDistinguishedName(signedCert.Issuer, issuerCert.Subject));
            TestContext.Out.WriteLine($"Signed Subject: {signedCert.Subject}");
            TestContext.Out.WriteLine($"Issuer Subject: {issuerCert.Subject}");

            // test basic constraints
            X509BasicConstraintsExtension constraints = X509Extensions.FindExtension <X509BasicConstraintsExtension>(signedCert);

            Assert.NotNull(constraints);
            TestContext.Out.WriteLine($"Constraints: {constraints.Format(true)}");
            Assert.True(constraints.Critical);
            Assert.False(constraints.CertificateAuthority);
            Assert.False(constraints.HasPathLengthConstraint);

            // key usage
            X509KeyUsageExtension keyUsage = X509Extensions.FindExtension <X509KeyUsageExtension>(signedCert);

            Assert.NotNull(keyUsage);
            TestContext.Out.WriteLine($"KeyUsage: {keyUsage.Format(true)}");
            Assert.True(keyUsage.Critical);
            Assert.True((keyUsage.KeyUsages & X509KeyUsageFlags.CrlSign) == 0);
            Assert.True((keyUsage.KeyUsages & X509KeyUsageFlags.DataEncipherment) == X509KeyUsageFlags.DataEncipherment);
            Assert.True((keyUsage.KeyUsages & X509KeyUsageFlags.DecipherOnly) == 0);
            Assert.True((keyUsage.KeyUsages & X509KeyUsageFlags.DigitalSignature) == X509KeyUsageFlags.DigitalSignature);
            Assert.True((keyUsage.KeyUsages & X509KeyUsageFlags.EncipherOnly) == 0);
            Assert.True((keyUsage.KeyUsages & X509KeyUsageFlags.KeyAgreement) == 0);
            Assert.True((keyUsage.KeyUsages & X509KeyUsageFlags.KeyCertSign) == 0);
            Assert.True((keyUsage.KeyUsages & X509KeyUsageFlags.KeyEncipherment) == X509KeyUsageFlags.KeyEncipherment);
            Assert.True((keyUsage.KeyUsages & X509KeyUsageFlags.NonRepudiation) == X509KeyUsageFlags.NonRepudiation);

            // enhanced key usage
            X509EnhancedKeyUsageExtension enhancedKeyUsage = X509Extensions.FindExtension <X509EnhancedKeyUsageExtension>(signedCert);

            Assert.NotNull(enhancedKeyUsage);
            TestContext.Out.WriteLine($"Enhanced Key Usage: {enhancedKeyUsage.Format(true)}");
            Assert.True(enhancedKeyUsage.Critical);

            // test for authority key
            X509AuthorityKeyIdentifierExtension authority = X509Extensions.FindExtension <X509AuthorityKeyIdentifierExtension>(signedCert);

            Assert.NotNull(authority);
            TestContext.Out.WriteLine($"Authority Key Identifier: {authority.Format(true)}");
            Assert.NotNull(authority.SerialNumber);
            Assert.NotNull(authority.KeyIdentifier);
            Assert.NotNull(authority.Issuer);
            Assert.AreEqual(issuerCert.SubjectName.RawData, authority.Issuer.RawData);
            Assert.AreEqual(issuerCert.SubjectName.RawData, authority.Issuer.RawData);

            // verify authority key in signed cert
            X509SubjectKeyIdentifierExtension subjectKeyId = X509Extensions.FindExtension <X509SubjectKeyIdentifierExtension>(issuerCert);

            TestContext.Out.WriteLine($"Issuer Subject Key Identifier: {subjectKeyId}");
            Assert.AreEqual(subjectKeyId.SubjectKeyIdentifier, authority.KeyIdentifier);
            Assert.AreEqual(issuerCert.SerialNumber, authority.SerialNumber);

            X509SubjectAltNameExtension subjectAlternateName = X509Extensions.FindExtension <X509SubjectAltNameExtension>(signedCert);

            Assert.NotNull(subjectAlternateName);
            TestContext.Out.WriteLine($"Issuer Subject Alternate Name: {subjectAlternateName}");
            Assert.False(subjectAlternateName.Critical);
            var domainNames = X509Utils.GetDomainsFromCertficate(signedCert);

            foreach (var domainName in testApp.DomainNames)
            {
                Assert.True(domainNames.Contains(domainName, StringComparer.OrdinalIgnoreCase));
            }
            Assert.True(subjectAlternateName.Uris.Count == 1);
            var applicationUri = X509Utils.GetApplicationUriFromCertificate(signedCert);

            Assert.True(testApp.ApplicationRecord.ApplicationUri == applicationUri);
        }
Exemple #7
0
        /// <summary>
        /// Creates an application instance certificate if one does not already exist.
        /// </summary>
        private async Task <bool> CheckApplicationInstanceCertificate(
            ApplicationConfiguration configuration,
            X509Certificate2 certificate,
            bool silent,
            ushort minimumKeySize)
        {
            if (certificate == null)
            {
                return(false);
            }

            // set suppressible errors
            var certValidator = new CertValidationSuppressibleStatusCodes(
                new StatusCode[] {
                StatusCodes.BadCertificateUntrusted,
                StatusCodes.BadCertificateTimeInvalid,
                StatusCodes.BadCertificateHostNameInvalid,
                StatusCodes.BadCertificateRevocationUnknown,
                StatusCodes.BadCertificateIssuerRevocationUnknown,
            });

            Utils.LogCertificate("Check application instance certificate.", certificate);

            try
            {
                // validate certificate.
                configuration.CertificateValidator.CertificateValidation += certValidator.OnCertificateValidation;
                configuration.CertificateValidator.Validate(certificate.HasPrivateKey ? new X509Certificate2(certificate.RawData) : certificate);
            }
            catch (Exception ex)
            {
                string message = Utils.Format(
                    "Error validating certificate. Exception: {0}. Use certificate anyway?", ex.Message);
                if (!await ApproveMessage(message, silent).ConfigureAwait(false))
                {
                    return(false);
                }
            }
            finally
            {
                configuration.CertificateValidator.CertificateValidation -= certValidator.OnCertificateValidation;
            }

            // check key size.
            int keySize = X509Utils.GetRSAPublicKeySize(certificate);

            if (minimumKeySize > keySize)
            {
                string message = Utils.Format(
                    "The key size ({0}) in the certificate is less than the minimum allowed ({1}). Use certificate anyway?",
                    keySize,
                    minimumKeySize);

                if (!await ApproveMessage(message, silent).ConfigureAwait(false))
                {
                    return(false);
                }
            }

            // check domains.
            if (configuration.ApplicationType != ApplicationType.Client)
            {
                if (!await CheckDomainsInCertificate(configuration, certificate, silent).ConfigureAwait(false))
                {
                    return(false);
                }
            }

            // check uri.
            string applicationUri = X509Utils.GetApplicationUriFromCertificate(certificate);

            if (String.IsNullOrEmpty(applicationUri))
            {
                string message = "The Application URI could not be read from the certificate. Use certificate anyway?";
                if (!await ApproveMessage(message, silent).ConfigureAwait(false))
                {
                    return(false);
                }
            }
            else if (!configuration.ApplicationUri.Equals(applicationUri, StringComparison.InvariantCulture))
            {
                Utils.LogInfo("Updated the ApplicationUri: {0} --> {1}", configuration.ApplicationUri, applicationUri);
                configuration.ApplicationUri = applicationUri;
            }

            Utils.LogInfo("Using the ApplicationUri: {0}", applicationUri);

            // update configuration.
            configuration.SecurityConfiguration.ApplicationCertificate.Certificate = certificate;

            return(true);
        }
        public static void VerifyApplicationCert(ApplicationTestData testApp, X509Certificate2 cert, X509Certificate2 issuerCert = null)
        {
            bool signedCert = issuerCert != null;

            if (issuerCert == null)
            {
                issuerCert = cert;
            }
            TestContext.Out.WriteLine($"{nameof(VerifyApplicationCert)}:");
            Assert.NotNull(cert);
            TestContext.Out.WriteLine(cert);
            Assert.False(cert.HasPrivateKey);
            Assert.True(X509Utils.CompareDistinguishedName(testApp.Subject, cert.Subject));
            Assert.True(X509Utils.CompareDistinguishedName(issuerCert.Subject, cert.Issuer));

            // test basic constraints
            X509BasicConstraintsExtension constraints = X509Extensions.FindExtension <X509BasicConstraintsExtension>(cert);

            Assert.NotNull(constraints);
            TestContext.Out.WriteLine(constraints.Format(true));
            Assert.True(constraints.Critical);
            if (signedCert)
            {
                Assert.False(constraints.CertificateAuthority);
                Assert.False(constraints.HasPathLengthConstraint);
            }
            else
            {
                Assert.True(constraints.CertificateAuthority);
                Assert.True(constraints.HasPathLengthConstraint);
                Assert.AreEqual(0, constraints.PathLengthConstraint);
            }

            // key usage
            X509KeyUsageExtension keyUsage = X509Extensions.FindExtension <X509KeyUsageExtension>(cert);

            Assert.NotNull(keyUsage);
            TestContext.Out.WriteLine(keyUsage.Format(true));
            Assert.True(keyUsage.Critical);
            Assert.True((keyUsage.KeyUsages & X509KeyUsageFlags.CrlSign) == 0);
            Assert.True((keyUsage.KeyUsages & X509KeyUsageFlags.DataEncipherment) == X509KeyUsageFlags.DataEncipherment);
            Assert.True((keyUsage.KeyUsages & X509KeyUsageFlags.DecipherOnly) == 0);
            Assert.True((keyUsage.KeyUsages & X509KeyUsageFlags.DigitalSignature) == X509KeyUsageFlags.DigitalSignature);
            Assert.True((keyUsage.KeyUsages & X509KeyUsageFlags.EncipherOnly) == 0);
            Assert.True((keyUsage.KeyUsages & X509KeyUsageFlags.KeyAgreement) == 0);
            Assert.True((keyUsage.KeyUsages & X509KeyUsageFlags.KeyCertSign) == (signedCert ? 0 : X509KeyUsageFlags.KeyCertSign));
            Assert.True((keyUsage.KeyUsages & X509KeyUsageFlags.KeyEncipherment) == X509KeyUsageFlags.KeyEncipherment);
            Assert.True((keyUsage.KeyUsages & X509KeyUsageFlags.NonRepudiation) == X509KeyUsageFlags.NonRepudiation);

            // enhanced key usage
            X509EnhancedKeyUsageExtension enhancedKeyUsage = X509Extensions.FindExtension <X509EnhancedKeyUsageExtension>(cert);

            Assert.NotNull(enhancedKeyUsage);
            TestContext.Out.WriteLine(enhancedKeyUsage.Format(true));
            Assert.True(enhancedKeyUsage.Critical);

            // test for authority key
            X509AuthorityKeyIdentifierExtension authority = X509Extensions.FindExtension <X509AuthorityKeyIdentifierExtension>(cert);

            Assert.NotNull(authority);
            TestContext.Out.WriteLine(authority.Format(true));
            Assert.NotNull(authority.SerialNumber);
            Assert.NotNull(authority.KeyIdentifier);
            Assert.NotNull(authority.Issuer);
            if (issuerCert == null)
            {
                Assert.AreEqual(cert.SubjectName.RawData, authority.Issuer.RawData);
                Assert.True(X509Utils.CompareDistinguishedName(cert.SubjectName.Name, authority.Issuer.Name), $"{cert.SubjectName.Name} != {authority.Issuer.Name}");
            }
            else
            {
                Assert.AreEqual(issuerCert.SubjectName.RawData, authority.Issuer.RawData);
                Assert.True(X509Utils.CompareDistinguishedName(issuerCert.SubjectName.Name, authority.Issuer.Name), $"{cert.SubjectName.Name} != {authority.Issuer.Name}");
            }

            // verify authority key in signed cert
            X509SubjectKeyIdentifierExtension subjectKeyId = X509Extensions.FindExtension <X509SubjectKeyIdentifierExtension>(cert);

            TestContext.Out.WriteLine(subjectKeyId.Format(true));
            if (signedCert)
            {
                var caCertSubjectKeyId = X509Extensions.FindExtension <X509SubjectKeyIdentifierExtension>(issuerCert);
                Assert.NotNull(caCertSubjectKeyId);
                Assert.AreEqual(caCertSubjectKeyId.SubjectKeyIdentifier, authority.KeyIdentifier);
            }
            else
            {
                Assert.AreEqual(subjectKeyId.SubjectKeyIdentifier, authority.KeyIdentifier);
            }
            Assert.AreEqual(issuerCert.GetSerialNumber(), authority.GetSerialNumber());
            Assert.AreEqual(issuerCert.SerialNumber, authority.SerialNumber);

            X509SubjectAltNameExtension subjectAlternateName = X509Extensions.FindExtension <X509SubjectAltNameExtension>(cert);

            Assert.NotNull(subjectAlternateName);
            TestContext.Out.WriteLine(subjectAlternateName.Format(true));
            Assert.False(subjectAlternateName.Critical);
            var domainNames = X509Utils.GetDomainsFromCertficate(cert);

            foreach (var domainName in testApp.DomainNames)
            {
                Assert.True(domainNames.Contains(domainName, StringComparer.OrdinalIgnoreCase));
            }
            Assert.True(subjectAlternateName.Uris.Count == 1);
            var applicationUri = X509Utils.GetApplicationUriFromCertificate(cert);

            TestContext.Out.WriteLine("ApplicationUri: ");
            TestContext.Out.WriteLine(applicationUri);
            Assert.AreEqual(testApp.ApplicationUri, applicationUri);
        }
        /// <summary>
        /// Displays the dialog.
        /// </summary>
        public async Task<bool> ShowDialog(CertificateIdentifier certificate)
        {
            m_certificate = certificate;

            CertificateStoreCTRL.StoreType = null;
            CertificateStoreCTRL.StorePath = null;
            CertificateStoreCTRL.ReadOnly = true;
            ApplicationNameTB.Text = null;
            ApplicationUriTB.Text = null;
            OrganizationTB.Text = null;
            DomainsTB.Text = System.Net.Dns.GetHostName();
            SubjectNameTB.Text = null;
            IssuerNameTB.Text = null;
            ValidFromTB.Text = null;
            ValidToTB.Text = null;
            ThumbprintTB.Text = null;

            if (certificate != null)
            {
                CertificateStoreCTRL.StoreType = certificate.StoreType;
                CertificateStoreCTRL.StorePath = certificate.StorePath;
                SubjectNameTB.Text = certificate.SubjectName;
                ThumbprintTB.Text = certificate.Thumbprint;

                X509Certificate2 data = await certificate.Find();

                if (data != null)
                {
                    // fill in subject name.
                    StringBuilder buffer = new StringBuilder();

                    foreach (string element in X509Utils.ParseDistinguishedName(data.Subject))
                    {
                        if (element.StartsWith("CN="))
                        {
                            ApplicationNameTB.Text = element.Substring(3);
                        }

                        if (element.StartsWith("O="))
                        {

                            OrganizationTB.Text = element.Substring(2);
                        }

                        if (buffer.Length > 0)
                        {
                            buffer.Append('/');
                        }

                        buffer.Append(element);
                    }

                    if (buffer.Length > 0)
                    {
                        SubjectNameTB.Text = buffer.ToString();
                    }

                    // fill in issuer name.
                    buffer = new StringBuilder();

                    foreach (string element in X509Utils.ParseDistinguishedName(data.Issuer))
                    {
                        if (buffer.Length > 0)
                        {
                            buffer.Append('/');
                        }

                        buffer.Append(element);
                    }

                    if (buffer.Length > 0)
                    {
                        IssuerNameTB.Text = buffer.ToString();
                    }

                    // fill in application uri.
                    string applicationUri = X509Utils.GetApplicationUriFromCertificate(data);

                    if (!String.IsNullOrEmpty(applicationUri))
                    {
                        ApplicationUriTB.Text = applicationUri;
                    }

                    // fill in domains.
                    buffer = new StringBuilder();

                    foreach (string domain in X509Utils.GetDomainsFromCertficate(data))
                    {
                        if (buffer.Length > 0)
                        {
                            buffer.Append(", ");
                        }

                        buffer.Append(domain);
                    }

                    if (buffer.Length > 0)
                    {
                        DomainsTB.Text = buffer.ToString();
                    }

                    ValidFromTB.Text = data.NotBefore.ToLocalTime().ToString("yyyy-MM-dd HH:mm:ss");
                    ValidToTB.Text = data.NotAfter.ToLocalTime().ToString("yyyy-MM-dd HH:mm:ss");
                    ThumbprintTB.Text = data.Thumbprint;
                }
            }

            if (ShowDialog() != DialogResult.OK)
            {
                return false;
            }

            return true;
        }
        private async Task <Session> ConsoleSampleClient()
        {
            var application = new ApplicationInstance {
                ApplicationType = ApplicationType.Client
            };

            #region Create an Application Configuration
            Console.WriteLine(" 1 - Create an Application Configuration.");
            ExitCode = ExitCode.ErrorCreateApplication;

            // Load the Application Configuration and use the specified config section "Technosoftware.ModelDesignClient"
            var config = await application.LoadConfigurationAsync("Technosoftware.ModelDesignClient");

            // check the application certificate.
            var haveAppCertificate = await application.CheckApplicationInstanceCertificateAsync(false, CertificateFactory.DefaultKeySize, CertificateFactory.DefaultLifeTime);

            if (haveAppCertificate)
            {
                config.ApplicationUri = X509Utils.GetApplicationUriFromCertificate(config.SecurityConfiguration.ApplicationCertificate.Certificate);
                if (config.SecurityConfiguration.AutoAcceptUntrustedCertificates)
                {
                    autoAccept_ = true;
                }
                config.CertificateValidator.CertificateValidation += OnCertificateValidation;
            }
            else
            {
                Console.WriteLine("    WARN: missing application certificate, using unsecured connection.");
            }
            #endregion

            #region Discover endpoints
            Console.WriteLine(" 2 - Discover endpoints of {0}.", endpointUrl_);
            ExitCode = ExitCode.ErrorDiscoverEndpoints;
            var selectedEndpoint = Discover.SelectEndpoint(endpointUrl_, haveAppCertificate, 15000);

            Console.WriteLine("    Selected endpoint uses: {0}",
                              selectedEndpoint.SecurityPolicyUri.Substring(selectedEndpoint.SecurityPolicyUri.LastIndexOf('#') + 1));
            #endregion

            #region Create a session with OPC UA server
            Console.WriteLine(" 3 - Create a session with OPC UA server.");
            ExitCode = ExitCode.ErrorCreateSession;

            // create the user identity
            UserIdentity userIdentity;
            if (String.IsNullOrEmpty(Username) && String.IsNullOrEmpty(Password))
            {
                userIdentity = new UserIdentity(new AnonymousIdentityToken());
            }
            else
            {
                userIdentity = new UserIdentity(Username, Password);
            }

            // create worker session
            session_ = await CreateSessionAsync(config, selectedEndpoint, userIdentity).ConfigureAwait(false);

            // register keep alive handler
            session_.SessionKeepAliveEvent += OnSessionKeepAliveEvent;
            #endregion

            #region Browse the OPC UA Server
            Console.WriteLine(" 4 - Browse address space.");
            // Create the browser
            var browser = new Browser(session_)
            {
                BrowseDirection   = BrowseDirection.Forward,
                ReferenceTypeId   = ReferenceTypeIds.HierarchicalReferences,
                IncludeSubtypes   = true,
                NodeClassMask     = 0,
                ContinueUntilDone = false
            };

            // Browse from the RootFolder
            var references = browser.Browse(Objects.ObjectsFolder);

            GetElements(session_, browser, 0, references, Verbose);

            #endregion

            #region Read a single value
            Console.WriteLine(" 5 - Read a single value.");
            var simulatedDataValue = session_.ReadValue(machine1LevelNodeId_);
            Console.WriteLine("Measurement Value:" + simulatedDataValue.Value);
            #endregion

            #region Create a subscription with publishing interval of 1 second
            Console.WriteLine(" 6 - Create a subscription with publishing interval of 1 second.");
            ExitCode      = ExitCode.ErrorCreateSubscription;
            subscription_ = new Subscription(session_.DefaultSubscription)
            {
                PublishingInterval = 1000
            };
            #endregion

            #region Add all dynamic values and the server time to the subscription
            Console.WriteLine(" 7 - Add the server time to the subscription.");
            ExitCode = ExitCode.ErrorMonitoredItem;
            var list = new List <MonitoredItem> {
                new MonitoredItem(subscription_.DefaultItem)
                {
                    DisplayName = "ServerStatusCurrentTime", StartNodeId = "i=" + Variables.Server_ServerStatus_CurrentTime
                }
            };
            list.ForEach(i => i.MonitoredItemNotificationEvent += OnNotification);

            subscription_.AddItems(list);
            #endregion

            #region Add the subscription to the session
            Console.WriteLine(" 8 - Add the subscription to the session.");
            ExitCode = ExitCode.ErrorAddSubscription;
            session_.AddSubscription(subscription_);
            subscription_.Create();
            #endregion

            #region Running...Press Ctrl-C to exit...
            Console.WriteLine(" 9 - Running...Press Ctrl-C to exit...");
            ExitCode = ExitCode.ErrorRunning;
            #endregion

            return(session_);
        }
        private async Task <Session> ConsoleSampleClient()
        {
            Console.WriteLine("1 - Create an Application Configuration.");
            ExitCode = ExitCode.ErrorCreateApplication;

            ApplicationInstance application = new ApplicationInstance {
                ApplicationName   = "UA Core Complex Client",
                ApplicationType   = ApplicationType.Client,
                ConfigSectionName = "Opc.Ua.ComplexClient"
            };

            // load the application configuration.
            ApplicationConfiguration config = await application.LoadApplicationConfiguration(false).ConfigureAwait(false);

            // check the application certificate.
            bool haveAppCertificate = await application.CheckApplicationInstanceCertificate(false, 0).ConfigureAwait(false);

            if (!haveAppCertificate)
            {
                throw new Exception("Application instance certificate invalid!");
            }

            if (ReverseConnectUri != null)
            {
                // start the reverse connection manager
                m_reverseConnectManager = new ReverseConnectManager();
                m_reverseConnectManager.AddEndpoint(ReverseConnectUri);
                m_reverseConnectManager.StartService(config);
            }

            if (haveAppCertificate)
            {
                config.ApplicationUri = X509Utils.GetApplicationUriFromCertificate(config.SecurityConfiguration.ApplicationCertificate.Certificate);
                if (config.SecurityConfiguration.AutoAcceptUntrustedCertificates)
                {
                    m_autoAccept = true;
                }
                config.CertificateValidator.CertificateValidation += new CertificateValidationEventHandler(CertificateValidator_CertificateValidation);
            }
            else
            {
                Console.WriteLine("    WARN: missing application certificate, using unsecure connection.");
            }

            Console.WriteLine("2 - Discover endpoints of {0}.", m_endpointURL);
            ExitCode = ExitCode.ErrorDiscoverEndpoints;
            EndpointDescription selectedEndpoint;

            if (m_reverseConnectManager == null)
            {
                selectedEndpoint = CoreClientUtils.SelectEndpoint(m_endpointURL, haveAppCertificate && !SecurityNone, 15000);
            }
            else
            {
                Console.WriteLine("   Waiting for reverse connection.");
                ITransportWaitingConnection connection = await m_reverseConnectManager.WaitForConnection(
                    new Uri(m_endpointURL), null, new CancellationTokenSource(60000).Token);

                if (connection == null)
                {
                    throw new ServiceResultException(StatusCodes.BadTimeout, "Waiting for a reverse connection timed out.");
                }
                selectedEndpoint = CoreClientUtils.SelectEndpoint(config, connection, haveAppCertificate && !SecurityNone, 15000);
            }

            Console.WriteLine("    Selected endpoint uses: {0}",
                              selectedEndpoint.SecurityPolicyUri.Substring(selectedEndpoint.SecurityPolicyUri.LastIndexOf('#') + 1));

            Console.WriteLine("3 - Create a session with OPC UA server.");
            ExitCode = ExitCode.ErrorCreateSession;

            // create the user identity
            UserIdentity userIdentity;

            if (String.IsNullOrEmpty(Username) && String.IsNullOrEmpty(Password))
            {
                userIdentity = new UserIdentity(new AnonymousIdentityToken());
            }
            else
            {
                userIdentity = new UserIdentity(Username, Password);
            }

            // create worker session
            if (m_reverseConnectManager == null)
            {
                m_session = await CreateSession(config, selectedEndpoint, userIdentity).ConfigureAwait(false);
            }
            else
            {
                Console.WriteLine("   Waiting for reverse connection.");
                ITransportWaitingConnection connection = await m_reverseConnectManager.WaitForConnection(
                    new Uri(m_endpointURL), null, new CancellationTokenSource(60000).Token).ConfigureAwait(false);

                if (connection == null)
                {
                    throw new ServiceResultException(StatusCodes.BadTimeout, "Waiting for a reverse connection timed out.");
                }
                m_session = await CreateSession(config, connection, selectedEndpoint, userIdentity).ConfigureAwait(false);
            }

            // register keep alive handler
            m_session.KeepAlive += Client_KeepAlive;

            Console.WriteLine("4 - Browse for all custom type variables.");
            ExitCode = ExitCode.ErrorReadComplexTypes;

            Stopwatch stopWatch = new Stopwatch();

            stopWatch.Start();
            var allVariableNodes       = BrowseAdddressSpace ? BrowseAllVariables() : new List <INode>();
            var allCustomTypeVariables = allVariableNodes.Where(n => ((VariableNode)n).DataType.NamespaceIndex != 0).ToList();

            stopWatch.Stop();

            Console.WriteLine($" -- Browse all nodes took {stopWatch.ElapsedMilliseconds}ms.");
            Console.WriteLine($" -- Browsed {allVariableNodes.Count} nodes, from which {allCustomTypeVariables.Count} are custom type variables.");

            stopWatch.Reset();
            // for testing clear the nodecache
            m_session.NodeCache.Clear();
            stopWatch.Start();

            if (LoadTypeSystem)
            {
                Console.WriteLine("5 - Load the server type dictionary.");
                ExitCode = ExitCode.ErrorLoadTypeDictionary;

                stopWatch.Reset();
                stopWatch.Start();

                var complexTypeSystem = new ComplexTypeSystem(m_session);
                await complexTypeSystem.Load().ConfigureAwait(false);

                stopWatch.Stop();

                Console.WriteLine($"Load type system took {stopWatch.ElapsedMilliseconds}ms.");

                Console.WriteLine($"Custom types defined for this session:");
                foreach (var type in complexTypeSystem.GetDefinedTypes())
                {
                    Console.WriteLine($"{type.Namespace}.{type.Name}");
                }

                Console.WriteLine($"Loaded {m_session.DataTypeSystem.Count} dictionaries:");
                foreach (var dictionary in m_session.DataTypeSystem)
                {
                    Console.WriteLine($" + {dictionary.Value.Name}");
                    foreach (var type in dictionary.Value.DataTypes)
                    {
                        Console.WriteLine($" -- {type.Key}:{type.Value}");
                    }
                }
            }
            else
            {
                Console.WriteLine("4 - Not loading the server type dictionary.");
            }

            foreach (VariableNode variableNode in allCustomTypeVariables)
            {
                try
                {
                    var value = m_session.ReadValue(variableNode.NodeId);

                    CastInt32ToEnum(variableNode, value);
                    Console.WriteLine($" -- {variableNode}:{value}");

                    if (value.Value is ExtensionObject extensionObject)
                    {
                        if (extensionObject.Body is BaseComplexType complexType)
                        {
                            foreach (var item in complexType.GetPropertyEnumerator())
                            {
                                if (Verbose)
                                {
                                    Console.WriteLine($" -- -- {item.Name}:{complexType[item.Name]}");
                                }
                                if (WriteComplexInt && item.PropertyType == typeof(Int32))
                                {
                                    var data = complexType[item.Name];
                                    if (data != null)
                                    {
                                        complexType[item.Name] = (Int32)data + 1;
                                    }
                                    Console.WriteLine($" -- -- Increment: {item.Name}, {complexType[item.Name]}");
                                    WriteValue(m_session, variableNode.NodeId, value);
                                }
                            }
                        }
                    }

                    if (PrintAsJson)
                    {
                        PrintValueAsJson(variableNode.BrowseName.Name, value);
                    }
                }
                catch (ServiceResultException sre)
                {
                    if (sre.StatusCode == StatusCodes.BadUserAccessDenied)
                    {
                        Console.WriteLine($" -- {variableNode}: Access denied!");
                    }
                }
            }

            Console.WriteLine("6 - Create test sessions which load only single types as needed.");
            if (LoadTypeSystem)
            {
                foreach (VariableNode variableNode in allCustomTypeVariables)
                {
                    Session testSession = null;
                    try
                    {
                        Console.WriteLine($"Open session for {variableNode}:");
                        testSession = await CreateSession(config, selectedEndpoint, userIdentity).ConfigureAwait(false);

                        var    complexTypeSystem = new ComplexTypeSystem(testSession);
                        NodeId dataType          = variableNode.DataType;
                        Type   nullType          = testSession.Factory.GetSystemType(dataType);
                        var    valueBefore       = testSession.ReadValue(variableNode.NodeId);
                        Console.WriteLine($" -- {valueBefore}");
                        Type systemType = await complexTypeSystem.LoadType(dataType).ConfigureAwait(false);

                        var valueAfter = testSession.ReadValue(variableNode.NodeId);
                        Console.WriteLine($" -- {variableNode}: {systemType} {dataType}");
                        Console.WriteLine($" -- {valueAfter}");
                        Console.WriteLine($"Custom types defined for {variableNode}:");
                        foreach (var type in complexTypeSystem.GetDefinedTypes())
                        {
                            Console.WriteLine($" -- {type.Namespace}.{type.Name}");
                        }
                    }
                    catch (ServiceResultException sre)
                    {
                        if (sre.StatusCode == StatusCodes.BadUserAccessDenied)
                        {
                            Console.WriteLine($" -- {variableNode}: Access denied!");
                        }
                    }
                    finally
                    {
                        testSession?.Close();
                    }
                }
            }
            else
            {
                Console.WriteLine("6 - Not testing to load individual types.");
            }

            Console.WriteLine("7 - Create a subscription with publishing interval of 1 second.");
            ExitCode = ExitCode.ErrorCreateSubscription;
            var subscription = new Subscription(m_session.DefaultSubscription)
            {
                PublishingInterval = 1000
            };

            Console.WriteLine("8 - Add all custom values and the server time to the subscription.");
            ExitCode = ExitCode.ErrorMonitoredItem;
            var list = new List <MonitoredItem> {
                new MonitoredItem(subscription.DefaultItem)
                {
                    DisplayName = "ServerStatusCurrentTime", StartNodeId = "i=" + Variables.Server_ServerStatus_CurrentTime.ToString()
                }
            };

            list.ForEach(i => i.Notification += OnNotification);

            foreach (var customVariable in allCustomTypeVariables)
            {
                var newItem = new MonitoredItem(subscription.DefaultItem)
                {
                    DisplayName = customVariable.DisplayName.Text,
                    StartNodeId = ExpandedNodeId.ToNodeId(customVariable.NodeId, m_session.NamespaceUris)
                };
                newItem.Notification += OnComplexTypeNotification;
                list.Add(newItem);
            }

            subscription.AddItems(list);

            Console.WriteLine("9 - Add the subscription to the session.");
            ExitCode = ExitCode.ErrorAddSubscription;
            m_session.AddSubscription(subscription);
            subscription.Create();

            Console.WriteLine("10 - Running...Press Ctrl-C to exit...");
            ExitCode = ExitCode.ErrorRunning;

            return(m_session);
        }
Exemple #12
0
        /// <summary>
        /// Updates an item in the view.
        /// </summary>
        protected override void UpdateItem(ListViewItem listItem, object item)
        {
            X509Certificate2 certificate = item as X509Certificate2;

            if (certificate == null)
            {
                base.UpdateItem(listItem, item);
                return;
            }

            listItem.SubItems[0].Text = null;
            listItem.SubItems[1].Text = null;
            listItem.SubItems[2].Text = null;
            listItem.SubItems[3].Text = null;
            listItem.SubItems[4].Text = null;
            listItem.SubItems[5].Text = null;

            if (certificate != null)
            {
                List <string> fields = X509Utils.ParseDistinguishedName(certificate.Subject);

                for (int ii = 0; ii < fields.Count; ii++)
                {
                    if (fields[ii].StartsWith("CN="))
                    {
                        listItem.SubItems[0].Text = fields[ii].Substring(3);
                    }

                    if (fields[ii].StartsWith("DC="))
                    {
                        listItem.SubItems[1].Text = fields[ii].Substring(3);
                    }
                }

                if (String.IsNullOrEmpty(listItem.SubItems[0].Text))
                {
                    listItem.SubItems[0].Text = String.Format("{0}", certificate.Subject);
                }

                // determine certificate type.
                foreach (X509Extension extension in certificate.Extensions)
                {
                    X509BasicConstraintsExtension basicContraints = extension as X509BasicConstraintsExtension;

                    if (basicContraints != null)
                    {
                        if (basicContraints.CertificateAuthority)
                        {
                            listItem.SubItems[1].Text = "CA";
                        }
                        else
                        {
                            listItem.SubItems[1].Text = "End-Entity";
                        }

                        break;
                    }
                }

                // check if a private key is available.
                if (certificate.HasPrivateKey)
                {
                    listItem.SubItems[2].Text = "Yes";
                }
                else
                {
                    listItem.SubItems[2].Text = "No";
                }

                // look up domains.
                IList <string> domains = X509Utils.GetDomainsFromCertficate(certificate);

                StringBuilder buffer = new StringBuilder();

                for (int ii = 0; ii < domains.Count; ii++)
                {
                    if (buffer.Length > 0)
                    {
                        buffer.Append(";");
                    }

                    buffer.Append(domains[ii]);
                }

                listItem.SubItems[3].Text = buffer.ToString();
                listItem.SubItems[4].Text = X509Utils.GetApplicationUriFromCertificate(certificate);
                listItem.SubItems[5].Text = String.Format("{0:yyyy-MM-dd}", certificate.NotAfter);
            }

            listItem.ImageKey = GuiUtils.Icons.Certificate;
            listItem.Tag      = item;
        }
Exemple #13
0
        /// <summary>
        /// Connects to the OPC server asynchronously.
        /// </summary>
        public async Task <bool> ConnectAsync(OpcConnectionOptions connectionOptions, int operationTimeout = -1)
        {
            ApplicationInstance application = new ApplicationInstance
            {
                ApplicationName   = string.Format("DrvOpcUa_{0} Driver", deviceNumStr),
                ApplicationType   = ApplicationType.Client,
                ConfigSectionName = "Scada.Comm.Drivers.DrvOpcUa"
            };

            // load the application configuration
            // TODO: use stream instead of file after updating OPCFoundation.NetStandard.Opc.Ua
            WriteConfigFile(out string configFileName);
            ApplicationConfiguration config = await application.LoadApplicationConfiguration(configFileName, false);

            // check the application certificate
            bool haveAppCertificate = await application.CheckApplicationInstanceCertificate(false, 0);

            if (!haveAppCertificate)
            {
                throw new ScadaException(Locale.IsRussian ?
                                         "Сертификат экземпляра приложения недействителен!" :
                                         "Application instance certificate invalid!");
            }

            if (haveAppCertificate)
            {
                config.ApplicationUri = X509Utils.GetApplicationUriFromCertificate(
                    config.SecurityConfiguration.ApplicationCertificate.Certificate);

                if (config.SecurityConfiguration.AutoAcceptUntrustedCertificates)
                {
                    AutoAccept = true;
                }

                config.CertificateValidator.CertificateValidation += CertificateValidator_CertificateValidation;
            }
            else
            {
                log.WriteLine(Locale.IsRussian ?
                              "Предупреждение: отсутствует сертификат приложения, используется незащищенное соединение." :
                              "Warning: missing application certificate, using unsecure connection.");
            }

            // create session
            EndpointDescription selectedEndpoint = operationTimeout > 0
                ? CoreClientUtils.SelectEndpoint(connectionOptions.ServerUrl, haveAppCertificate, operationTimeout)
                : CoreClientUtils.SelectEndpoint(connectionOptions.ServerUrl, haveAppCertificate);

            selectedEndpoint.SecurityMode      = connectionOptions.SecurityMode;
            selectedEndpoint.SecurityPolicyUri = connectionOptions.GetSecurityPolicy();
            EndpointConfiguration endpointConfiguration = EndpointConfiguration.Create(config);
            ConfiguredEndpoint    endpoint     = new ConfiguredEndpoint(null, selectedEndpoint, endpointConfiguration);
            UserIdentity          userIdentity = connectionOptions.AuthenticationMode == AuthenticationMode.Username ?
                                                 new UserIdentity(connectionOptions.Username, connectionOptions.Password) :
                                                 new UserIdentity(new AnonymousIdentityToken());

            OpcSession = await Session.Create(config, endpoint, false,
                                              "Rapid SCADA DrvOpcUa_" + deviceNumStr,
                                              (uint)config.ClientConfiguration.DefaultSessionTimeout, userIdentity, null);

            log.WriteLine(Locale.IsRussian ?
                          "OPC сессия успешно создана: {0}" :
                          "OPC session created successfully: {0}", connectionOptions.ServerUrl);
            return(true);
        }
        private async Task <Session> ConsoleSampleClient()
        {
            var application = new ApplicationInstance {
                ApplicationType = ApplicationType.Client
            };

            #region Create an Application Configuration
            Console.WriteLine(" 1 - Create an Application Configuration.");
            ExitCode = ExitCode.ErrorCreateApplication;

            // Load the Application Configuration and use the specified config section "Technosoftware.SimpleClient"
            var config = await application.LoadConfigurationAsync("Technosoftware.SimpleClient");

            // check the application certificate.
            var haveAppCertificate = await application.CheckApplicationInstanceCertificateAsync(false, CertificateFactory.DefaultKeySize, CertificateFactory.DefaultLifeTime);

            reverseConnectManager_ = null;
            if (ReverseConnectUri != null)
            {
                // start the reverse connection manager
                reverseConnectManager_ = new ReverseConnectManager();
                reverseConnectManager_.AddEndpoint(ReverseConnectUri);
                reverseConnectManager_.StartService(config);
            }

            if (haveAppCertificate)
            {
                config.ApplicationUri = X509Utils.GetApplicationUriFromCertificate(config.SecurityConfiguration.ApplicationCertificate.Certificate);
                if (config.SecurityConfiguration.AutoAcceptUntrustedCertificates)
                {
                    autoAccept_ = true;
                }
                config.CertificateValidator.CertificateValidation += OnCertificateValidation;
            }
            else
            {
                Console.WriteLine("    WARN: missing application certificate, using unsecured connection.");
            }
            #endregion

            #region Discover endpoints
            Console.WriteLine(" 2 - Discover endpoints of {0}.", endpointUrl_);
            ExitCode = ExitCode.ErrorDiscoverEndpoints;
            EndpointDescription selectedEndpoint;
            if (reverseConnectManager_ == null)
            {
                selectedEndpoint = Discover.SelectEndpoint(endpointUrl_, haveAppCertificate && !SecurityNone, 15000);
            }
            else
            {
                Console.WriteLine("   Waiting for reverse connection.");
                var connection = await reverseConnectManager_.WaitForConnection(
                    new Uri(endpointUrl_), null, new CancellationTokenSource(60000).Token);

                if (connection == null)
                {
                    throw new ServiceResultException(StatusCodes.BadTimeout, "Waiting for a reverse connection timed out.");
                }
                selectedEndpoint = Discover.SelectEndpoint(config, connection, haveAppCertificate && !SecurityNone, 15000);
            }

            Console.WriteLine("    Selected endpoint uses: {0}",
                              selectedEndpoint.SecurityPolicyUri.Substring(selectedEndpoint.SecurityPolicyUri.LastIndexOf('#') + 1));
            #endregion

            #region Create a session with OPC UA server
            Console.WriteLine(" 3 - Create a session with OPC UA server.");
            ExitCode = ExitCode.ErrorCreateSession;

            // create the user identity
            UserIdentity userIdentity;
            if (String.IsNullOrEmpty(Username) && String.IsNullOrEmpty(Password))
            {
                userIdentity = new UserIdentity(new AnonymousIdentityToken());
            }
            else
            {
                userIdentity = new UserIdentity(Username, Password);
            }

            // create worker session
            if (reverseConnectManager_ == null)
            {
                session_ = await CreateSessionAsync(config, selectedEndpoint, userIdentity).ConfigureAwait(false);
            }
            else
            {
                Console.WriteLine("   Waiting for reverse connection.");
                // Define the cancellation token.
                var source = new CancellationTokenSource(60000);
                var token  = source.Token;
                try
                {
                    var connection = await reverseConnectManager_.WaitForConnection(
                        new Uri(endpointUrl_), null, token);

                    if (connection == null)
                    {
                        throw new ServiceResultException(StatusCodes.BadTimeout,
                                                         "Waiting for a reverse connection timed out.");
                    }

                    session_ = await CreateSessionAsync(config, connection, selectedEndpoint, userIdentity)
                               .ConfigureAwait(false);
                }
                finally
                {
                    source.Dispose();
                }
            }

            // register keep alive handler
            session_.SessionKeepAliveEvent += OnSessionKeepAliveEvent;
            #endregion

            #region Browse the OPC UA Server
            Console.WriteLine(" 4 - Browse address space.");
            // Create the browser
            var browser = new Browser(session_)
            {
                BrowseDirection   = BrowseDirection.Forward,
                ReferenceTypeId   = ReferenceTypeIds.HierarchicalReferences,
                IncludeSubtypes   = true,
                NodeClassMask     = 0,
                ContinueUntilDone = false
            };

            // Browse from the RootFolder
            var references = browser.Browse(Objects.ObjectsFolder);

            GetElements(session_, browser, 0, references, Verbose);

            #endregion

            #region Read a single value
            Console.WriteLine(" 5 - Read a single value.");
            var simulatedDataValue = session_.ReadValue(simulatedDataNodeId_);
            Console.WriteLine("Node Value:" + simulatedDataValue.Value);
            #endregion

            #region Read multiple values
            Console.WriteLine(" 6 - Read multiple values.");
            // The input parameters of the ReadValues() method
            var variableIds   = new List <NodeId>();
            var expectedTypes = new List <Type>();

            // Add a node to the list
            variableIds.Add(simulatedDataNodeId_);
            // Add an expected type to the list (null means we get the original type from the server)
            expectedTypes.Add(null);

            // Add another node to the list
            variableIds.Add(staticDataNodeId1_);
            // Add an expected type to the list (null means we get the original type from the server)
            expectedTypes.Add(null);

            // Add another node to the list
            variableIds.Add(staticDataNodeId2_);

            // Add an expected type to the list (null means we get the original type from the server)
            expectedTypes.Add(null);

            session_.ReadValues(variableIds, expectedTypes, out var values, out var errors);
            // write the result to the console.
            for (var i = 0; i < values.Count; i++)
            {
                Console.WriteLine("Status of Read of Node {0} is: {1}", variableIds[i].ToString(), errors[i]);
            }
            for (var i = 0; i < values.Count; i++)
            {
                Console.WriteLine("Value of Read of Node {0} is: Value: {1}", variableIds[i].ToString(), values[i]);
            }
            #endregion

            #region Read multiple values asynchronous
            Console.WriteLine(" 7 - Read multiple values asynchronous.");
            // start reading the value (setting a 10 second timeout).
            session_.BeginReadValues(
                variableIds,
                0,
                TimestampsToReturn.Both,
                OnReadComplete,
                new UserData {
                Session = session_, NodeIds = variableIds
            });
            #endregion

            #region Write a value
            Console.WriteLine(" 8 - Write a value.");
            short writeInt = 1234;

            Console.WriteLine("Write Value: " + writeInt);
            session_.WriteValue(staticDataNodeId1_, new DataValue(writeInt));

            // read it again to check the new value
            Console.WriteLine("Node Value (should be {0}): {1}", session_.ReadValue(staticDataNodeId1_).Value, writeInt);
            #endregion

            #region Write multiple values at once
            Console.WriteLine(" 9 - Write multiple values at once.");

            writeInt = 5678;
            var writeDouble = 1234.1234;

            var nodeIds    = new List <NodeId>();
            var dataValues = new List <DataValue>();

            nodeIds.Add(staticDataNodeId1_);
            nodeIds.Add(staticDataNodeId2_);

            dataValues.Add(new DataValue(writeInt));
            dataValues.Add(new DataValue(writeDouble));

            Console.WriteLine("Write Values: {0} and {1}", writeInt, writeDouble);
            var statusCodes = session_.WriteValues(nodeIds, dataValues);

            Console.WriteLine("Returned status codes:");
            foreach (var statusCode in statusCodes)
            {
                Console.WriteLine("Status: {0}", statusCode.ToString());
            }

            // read it again to check the new value
            Console.WriteLine("Node Value (should be {0}): {1}", session_.ReadValue(staticDataNodeId1_).Value, writeInt);
            Console.WriteLine("Node Value (should be {0}): {1}", session_.ReadValue(staticDataNodeId2_).Value, writeDouble);
            #endregion

            #region Write multiple values asynchronous
            Console.WriteLine("10 - Write multiple values asynchronous.");

            // start writing the values.
            session_.BeginWriteValues(
                nodeIds,
                dataValues,
                OnWriteComplete,
                new UserData {
                Session = session_, NodeIds = nodeIds
            });
            #endregion

            #region Call a Method
            Console.WriteLine("11 - Call a Method.");
            INode node = session_.ReadNode(callHelloMethodNodeId_);


            if (node is MethodNode)
            {
                var methodId = callHelloMethodNodeId_;

                var objectId = methodsNodeId_;

                var inputArguments = new VariantCollection {
                    new Variant("from Technosoftware")
                };

                var request = new CallMethodRequest {
                    ObjectId = objectId, MethodId = methodId, InputArguments = inputArguments
                };

                var requests = new CallMethodRequestCollection {
                    request
                };

                var responseHeader = session_.Call(
                    null,
                    requests,
                    out var results,
                    out var diagnosticInfos);

                if (StatusCode.IsBad(results[0].StatusCode))
                {
                    throw new ServiceResultException(new ServiceResult(results[0].StatusCode, 0, diagnosticInfos,
                                                                       responseHeader.StringTable));
                }

                Console.WriteLine("{0}", results[0].OutputArguments[0]);
            }

            #endregion

            #region Create a subscription with publishing interval of 1 second
            Console.WriteLine("12 - Create a subscription with publishing interval of 1 second.");
            ExitCode      = ExitCode.ErrorCreateSubscription;
            subscription_ = new Subscription(session_.DefaultSubscription)
            {
                PublishingInterval = 1000
            };
            #endregion

            #region Add all dynamic values and the server time to the subscription
            Console.WriteLine("13 - Add all dynamic values and the server time to the subscription.");
            ExitCode = ExitCode.ErrorMonitoredItem;
            var list = new List <MonitoredItem> {
                new MonitoredItem(subscription_.DefaultItem)
                {
                    DisplayName = "ServerStatusCurrentTime", StartNodeId = "i=" + Variables.Server_ServerStatus_CurrentTime
                }
            };
            list.ForEach(i => i.MonitoredItemNotificationEvent += OnNotification);

            var newItem = new MonitoredItem(subscription_.DefaultItem)
            {
                DisplayName = "Simulated Data Value",
                StartNodeId = new NodeId(simulatedDataNodeId_)
            };
            newItem.MonitoredItemNotificationEvent += OnMonitoredItemNotificationEvent;
            list.Add(newItem);

            subscription_.AddItems(list);
            #endregion

            #region Add the subscription to the session
            Console.WriteLine("14 - Add the subscription to the session.");
            ExitCode = ExitCode.ErrorAddSubscription;
            session_.AddSubscription(subscription_);
            subscription_.Create();
            #endregion

            #region Running...Press Ctrl-C to exit...
            Console.WriteLine("15 - Running...Press Ctrl-C to exit...");
            ExitCode = ExitCode.ErrorRunning;
            #endregion

            return(session_);
        }