private async Task BuildAsync()

in src/Azure.IIoT.OpcUa.Publisher/src/Stack/Services/OpcUaApplication.cs [479:637]


        private async Task<ApplicationConfiguration> BuildAsync()
        {
            Debug.Assert(!string.IsNullOrWhiteSpace(_options.Value.ApplicationName));

            var appInstance = new ApplicationInstance
            {
                ApplicationName = _options.Value.ApplicationName,
                ApplicationType = Opc.Ua.ApplicationType.Client
            };

            Exception innerException = new InvalidConfigurationException("Missing network.");
            for (var attempt = 0; attempt < 60; attempt++)
            {
                // wait with the configuration until network is up
                if (!NetworkInterface.GetIsNetworkAvailable())
                {
                    _logger.LogWarning("Network not available...");
                    await Task.Delay(3000).ConfigureAwait(false);
                    continue;
                }

                var hostname = !string.IsNullOrWhiteSpace(_identity) ?
                    Uri.CheckHostName(_identity) != UriHostNameType.Unknown ? _identity :
#pragma warning disable CA5350 // Do Not Use Weak Cryptographic Algorithms
                    new IPAddress(SHA1.HashData(Encoding.UTF8.GetBytes(_identity))
                        .AsSpan()[..16], 0).ToString() :
#pragma warning restore CA5350 // Do Not Use Weak Cryptographic Algorithms
                    Utils.GetHostName();

                var applicationUri = _options.Value.ApplicationUri;
                if (_options.Value.Security.TryUseConfigurationFromExistingAppCert == true)
                {
                    (applicationUri, appInstance.ApplicationName, hostname) =
                        await UpdateFromExistingCertificateAsync(
                            applicationUri, appInstance.ApplicationName, hostname,
                            _options.Value.Security).ConfigureAwait(false);
                }
                if (applicationUri == null)
                {
                    applicationUri = $"urn:{hostname}:opc-publisher";
                }
                else
                {
                    applicationUri = applicationUri.Replace("urn:localhost", $"urn:{hostname}",
                        StringComparison.Ordinal);
                }
                var appBuilder = appInstance.Build(applicationUri, _options.Value.ProductUri)
                    .SetTransportQuotas(ToTransportQuotas(_options.Value.Quotas))
                    .AsClient();
                try
                {
                    var appConfig = await BuildSecurityConfigurationAsync(appBuilder,
                        _options.Value.Security, appInstance.ApplicationConfiguration, hostname)
                        .ConfigureAwait(false);

                    var ownCertificate =
                        appConfig.SecurityConfiguration.ApplicationCertificate.Certificate;

                    if (ownCertificate == null)
                    {
                        _logger.LogInformation(
                            "No application own certificate found. Creating a self-signed " +
                            "own certificate valid since yesterday for {DefaultLifeTime} months, " +
                            "with a {DefaultKeySize} bit key and {DefaultHashSize} bit hash.",
                            CertificateFactory.DefaultLifeTime,
                            CertificateFactory.DefaultKeySize,
                            CertificateFactory.DefaultHashSize);
                    }
                    else
                    {
                        _logger.LogInformation(
                            "Own certificate Subject '{Subject}' (Thumbprint: {Tthumbprint}) loaded.",
                            ownCertificate.Subject, ownCertificate.Thumbprint);
                    }

                    var hasAppCertificate =
                        await appInstance.CheckApplicationInstanceCertificates(true).ConfigureAwait(false);
                    if (!hasAppCertificate ||
                        appConfig.SecurityConfiguration.ApplicationCertificate.Certificate == null)
                    {
                        _logger.LogError("Failed to load or create application own certificate.");
                        throw new InvalidConfigurationException(
                            "OPC UA application own certificate invalid");
                    }

                    if (ownCertificate == null)
                    {
                        ownCertificate =
                            appConfig.SecurityConfiguration.ApplicationCertificate.Certificate;
                        _logger.LogInformation(
                            "Own certificate Subject '{Subject}' (Thumbprint: {Thumbprint}) created.",
                            ownCertificate.Subject, ownCertificate.Thumbprint);
                    }
                    await ShowCertificateStoreInformationAsync(appConfig).ConfigureAwait(false);
                    return appInstance.ApplicationConfiguration;
                }
                catch (Exception e)
                {
                    _logger.LogInformation(
                        "Error {Message} while configuring OPC UA stack - retry...", e.Message);
                    _logger.LogDebug(e, "Detailed error while configuring OPC UA stack.");
                    innerException = e;

                    await Task.Delay(3000).ConfigureAwait(false);
                }
            }

            _logger.LogCritical("Failed to configure OPC UA stack - exit.");
            throw new InvalidProgramException("OPC UA stack configuration not possible.",
                innerException);

            async ValueTask<(string?, string, string)> UpdateFromExistingCertificateAsync(
                string? applicationUri, string appName, string hostName, SecurityOptions options)
            {
                try
                {
                    var now = _timeProvider.GetUtcNow();
                    if (options.ApplicationCertificates?.StorePath != null &&
                        options.ApplicationCertificates.StoreType != null)
                    {
                        using var certStore = CertificateStoreIdentifier.CreateStore(
                            options.ApplicationCertificates.StoreType);
                        certStore.Open(options.ApplicationCertificates.StorePath, false);
                        var certs = await certStore.Enumerate().ConfigureAwait(false);
                        var subjects = new List<string>();
                        foreach (var cert in certs.Where(c => c != null).OrderBy(c => c.NotAfter))
                        {
                            // Select first certificate that has valid information
                            options.ApplicationCertificates.SubjectName = cert.Subject;
                            var name = cert.SubjectName.EnumerateRelativeDistinguishedNames()
                                .Where(dn => dn.GetSingleElementType().FriendlyName == "CN")
                                .Select(dn => dn.GetSingleElementValue())
                                .FirstOrDefault(dn => dn != null);
                            if (name != null)
                            {
                                appName = name;
                            }
                            var san = cert.FindExtension<X509SubjectAltNameExtension>();
                            var uris = san?.Uris;
                            var hostNames = san?.DomainNames;
                            if (uris != null && hostNames != null &&
                                uris.Count > 0 && hostNames.Count > 0)
                            {
                                return (uris[0], appName, hostNames[0]);
                            }
                            _logger.LogDebug(
                                "Found invalid certificate for {Subject} [{Thumbprint}].",
                                cert.Subject, cert.Thumbprint);
                        }
                    }
                    _logger.LogDebug("Could not find a certificate to take information from.");
                }
                catch (Exception ex)
                {
                    _logger.LogDebug(ex, "Failed to find a certificate to take information from.");
                }
                return (applicationUri, appName, hostName);
            }
        }