Esempio n. 1
0
 private void LogMessage(string managedSiteId, string msg, LogItemType logType = LogItemType.GeneralInfo)
 {
     ManagedSiteLog.AppendLog(managedSiteId, new ManagedSiteLogItem
     {
         EventDate   = DateTime.UtcNow,
         LogItemType = LogItemType.GeneralInfo,
         Message     = msg
     });
 }
Esempio n. 2
0
 internal static void LogMessage(string managedSiteId, string msg)
 {
     ManagedSiteLog.AppendLog(managedSiteId, new ManagedSiteLogItem
     {
         EventDate   = DateTime.UtcNow,
         LogItemType = LogItemType.GeneralInfo,
         Message     = msg
     });
 }
Esempio n. 3
0
        public async Task <List <CertificateRequestResult> > PerformRenewalAllManagedSites(bool autoRenewalOnly = true, Dictionary <string, Progress <RequestProgressState> > progressTrackers = null)
        {
            await Task.Delay(200); //allow UI to update

            //currently the vault won't let us run parallel requests due to file locks
            bool performRequestsInParallel = false;
            bool testModeOnly = false;

            _siteManager.LoadSettings();

            IEnumerable <ManagedSite> sites = _siteManager.GetManagedSites();

            if (autoRenewalOnly)
            {
                sites = sites.Where(s => s.IncludeInAutoRenew == true);
            }

            //check site list and examine current certificates. If certificate is less than n days old, don't attempt to renew it
            var sitesToRenew        = new List <ManagedSite>();
            var renewalIntervalDays = Properties.Settings.Default.RenewalIntervalDays;

#if DEBUG
            //in debug mode we renew every time instead of skipping based on days old
            renewalIntervalDays = 0;
#endif
            int numRenewalTasks = 0;
            int maxRenewalTasks = Properties.Settings.Default.MaxRenewalRequests;

            var renewalTasks = new List <Task <CertificateRequestResult> >();
            foreach (var s in sites.Where(s => s.IncludeInAutoRenew == true))
            {
                //if we know the last renewal date, check whether we should renew again, otherwise assume it's more than 30 days ago by default and attempt renewal
                var timeSinceLastRenewal = (s.DateRenewed != null ? s.DateRenewed : DateTime.Now.AddDays(-30)) - DateTime.Now;

                bool isRenewalRequired = Math.Abs(timeSinceLastRenewal.Value.TotalDays) > renewalIntervalDays;
                bool isSiteRunning     = true;

                //if we care about stopped sites being stopped, check for that
                if (Properties.Settings.Default.IgnoreStoppedSites)
                {
                    isSiteRunning = IsManagedSiteRunning(s.Id);
                }

                if (isRenewalRequired && isSiteRunning)
                {
                    //get matching progress tracker for this site
                    IProgress <RequestProgressState> tracker = null;
                    if (progressTrackers != null)
                    {
                        tracker = progressTrackers[s.Id];
                    }

                    // optionally limit the number of renewal tasks to attempt in this pass
                    if (maxRenewalTasks == 0 || maxRenewalTasks > 0 && numRenewalTasks < maxRenewalTasks)
                    {
                        if (testModeOnly)
                        {
                            //simulated request for UI testing
                            renewalTasks.Add(this.PerformDummyCertificateRequest(s, tracker));
                        }
                        else
                        {
                            renewalTasks.Add(this.PerformCertificateRequest(s, tracker));
                        }
                    }
                    numRenewalTasks++;
                }
                else
                {
                    var msg = "Skipping Renewal, existing certificate still OK. ";

                    if (isRenewalRequired && !isSiteRunning)
                    {
                        //TODO: show this as warning rather than success
                        msg = "Site stopped, renewal skipped as domain validation cannot be performed. ";
                    }

                    if (progressTrackers != null)
                    {
                        //send progress back to report skip
                        var progress = (IProgress <RequestProgressState>)progressTrackers[s.Id];
                        if (progress != null)
                        {
                            progress.Report(new RequestProgressState {
                                CurrentState = RequestState.Success, Message = msg
                            });
                        }
                    }

                    ManagedSiteLog.AppendLog(s.Id, new ManagedSiteLogItem {
                        EventDate = DateTime.UtcNow, LogItemType = LogItemType.GeneralInfo, Message = msg + s.Name
                    });
                }
            }

            if (!renewalTasks.Any())
            {
                //nothing to do
                return(new List <CertificateRequestResult>());
            }

            if (performRequestsInParallel)
            {
                var results = await Task.WhenAll(renewalTasks);

                //siteManager.StoreSettings();
                return(results.ToList());
            }
            else
            {
                var results = new List <CertificateRequestResult>();
                foreach (var t in renewalTasks)
                {
                    results.Add(await t);
                }

                return(results);
            }
        }
Esempio n. 4
0
        public async Task <CertificateRequestResult> PerformCertificateRequest(VaultManager vaultManager, ManagedSite managedSite, IProgress <RequestProgressState> progress = null)
        {
            // FIXME: refactor into different concerns, there's way to much being done here

            return(await Task.Run(async() =>
            {
                try
                {
                    ManagedSiteLog.AppendLog(managedSite.Id, new ManagedSiteLogItem {
                        EventDate = DateTime.UtcNow, LogItemType = LogItemType.GeneralInfo, Message = "Beginning Certificate Request Process: " + managedSite.Name
                    });

                    bool enableIdentifierReuse = false;

                    if (vaultManager == null)
                    {
                        vaultManager = GetVaultManager();
                    }

                    //enable or disable EFS flag on private key certs based on preference
                    vaultManager.UseEFSForSensitiveFiles = Properties.Settings.Default.EnableEFS;

                    //primary domain and each subject alternative name must now be registered as an identifier with LE and validated

                    if (progress != null)
                    {
                        progress.Report(new RequestProgressState {
                            IsRunning = true, CurrentState = RequestState.Running, Message = "Registering Domain Identifiers"
                        });
                    }

                    await Task.Delay(200); //allow UI update

                    var config = managedSite.RequestConfig;

                    List <string> allDomains = new List <string> {
                        config.PrimaryDomain
                    };

                    if (config.SubjectAlternativeNames != null)
                    {
                        allDomains.AddRange(config.SubjectAlternativeNames);
                    }

                    bool allIdentifiersValidated = true;

                    if (config.ChallengeType == null)
                    {
                        config.ChallengeType = "http-01";
                    }

                    List <PendingAuthorization> identifierAuthorizations = new List <PendingAuthorization>();
                    var distinctDomains = allDomains.Distinct();

                    foreach (var domain in distinctDomains)
                    {
                        var identifierAlias = vaultManager.ComputeIdentifierAlias(domain);

                        //check if this domain already has an associated identifier registerd with LetsEncrypt which hasn't expired yet
                        //await Task.Delay(200); //allow UI update

                        ACMESharp.Vault.Model.IdentifierInfo existingIdentifier = null;

                        if (enableIdentifierReuse)
                        {
                            existingIdentifier = vaultManager.GetIdentifier(domain.Trim().ToLower());
                        }

                        bool identifierAlreadyValid = false;
                        if (existingIdentifier != null &&
                            existingIdentifier.Authorization != null &&
                            (existingIdentifier.Authorization.Status == "valid" || existingIdentifier.Authorization.Status == "pending") &&
                            existingIdentifier.Authorization.Expires > DateTime.Now.AddDays(1))
                        {
                            //we have an existing validated identifier, reuse that for this certificate request
                            identifierAlias = existingIdentifier.Alias;

                            if (existingIdentifier.Authorization.Status == "valid")
                            {
                                identifierAlreadyValid = true;
                            }

                            // managedSite.AppendLog(new ManagedSiteLogItem { EventDate =
                            // DateTime.UtcNow, LogItemType = LogItemType.CertificateRequestStarted,
                            // Message = "Attempting Certificate Request: " + managedSite.SiteType });
                            System.Diagnostics.Debug.WriteLine("Reusing existing valid non-expired identifier for the domain " + domain);
                        }

                        ManagedSiteLog.AppendLog(managedSite.Id, new ManagedSiteLogItem {
                            EventDate = DateTime.UtcNow, LogItemType = LogItemType.CertificateRequestStarted, Message = "Attempting Domain Validation: " + domain
                        });

                        //begin authorization process (register identifier, request authorization if not already given)
                        if (progress != null)
                        {
                            progress.Report(new RequestProgressState {
                                Message = "Registering and Validating " + domain
                            });
                        }

                        //TODO: make operations async and yeild IO of vault

                        /*var authorization = await Task.Run(() =>
                         * {
                         *  return vaultManager.BeginRegistrationAndValidation(config, identifierAlias, challengeType: config.ChallengeType, domain: domain);
                         * });*/

                        var authorization = vaultManager.BeginRegistrationAndValidation(config, identifierAlias, challengeType: config.ChallengeType, domain: domain);

                        if (authorization != null && authorization.Identifier != null && !identifierAlreadyValid)
                        {
                            if (authorization.Identifier.Authorization.IsPending())
                            {
                                if (managedSite.ItemType == ManagedItemType.SSL_LetsEncrypt_LocalIIS)
                                {
                                    if (progress != null)
                                    {
                                        progress.Report(new RequestProgressState {
                                            Message = "Performing Challenge Response via IIS: " + domain
                                        });
                                    }

                                    //ask LE to check our answer to their authorization challenge (http), LE will then attempt to fetch our answer, if all accessible and correct (authorized) LE will then allow us to request a certificate
                                    //prepare IIS with answer for the LE challenege
                                    authorization = vaultManager.PerformIISAutomatedChallengeResponse(config, authorization);

                                    //if we attempted extensionless config checks, report any errors
                                    if (config.PerformAutoConfig && !authorization.ExtensionlessConfigCheckedOK)
                                    {
                                        ManagedSiteLog.AppendLog(managedSite.Id, new ManagedSiteLogItem {
                                            EventDate = DateTime.UtcNow, LogItemType = LogItemType.CertficateRequestFailed, Message = "Failed prerequisite configuration (" + managedSite.ItemType + ")"
                                        });
                                        siteManager.StoreSettings();

                                        var result = new CertificateRequestResult {
                                            ManagedItem = managedSite, IsSuccess = false, Message = "Automated configuration checks failed. Authorizations will not be able to complete.\nCheck you have http bindings for your site and ensure you can browse to http://" + domain + "/.well-known/acme-challenge/configcheck before proceeding."
                                        };
                                        if (progress != null)
                                        {
                                            progress.Report(new RequestProgressState {
                                                CurrentState = RequestState.Error, Message = result.Message, Result = result
                                            });
                                        }

                                        return result;
                                    }
                                    else
                                    {
                                        if (progress != null)
                                        {
                                            progress.Report(new RequestProgressState {
                                                CurrentState = RequestState.Running, Message = "Requesting Validation from Lets Encrypt: " + domain
                                            });
                                        }

                                        //ask LE to validate our challenge response
                                        vaultManager.SubmitChallenge(identifierAlias, config.ChallengeType);

                                        bool identifierValidated = vaultManager.CompleteIdentifierValidationProcess(authorization.Identifier.Alias);

                                        if (!identifierValidated)
                                        {
                                            if (progress != null)
                                            {
                                                progress.Report(new RequestProgressState {
                                                    CurrentState = RequestState.Error, Message = "Domain validation failed: " + domain
                                                });
                                            }

                                            allIdentifiersValidated = false;
                                        }
                                        else
                                        {
                                            if (progress != null)
                                            {
                                                progress.Report(new RequestProgressState {
                                                    CurrentState = RequestState.Running, Message = "Domain validation completed: " + domain
                                                });
                                            }

                                            identifierAuthorizations.Add(authorization);
                                        }
                                    }
                                }
                            }
                            else
                            {
                                if (authorization.Identifier.Authorization.Status == "valid")
                                {
                                    identifierAuthorizations.Add(new PendingAuthorization {
                                        Identifier = authorization.Identifier
                                    });
                                }
                            }
                        }
                        else
                        {
                            if (identifierAlreadyValid)
                            {
                                //we have previously validated this identifier and it has not yet expired, so we can just reuse it in our cert request
                                identifierAuthorizations.Add(new PendingAuthorization {
                                    Identifier = existingIdentifier
                                });
                            }
                        }
                    }

                    //check if all identifiers validates
                    if (identifierAuthorizations.Count == distinctDomains.Count())
                    {
                        allIdentifiersValidated = true;
                    }

                    if (allIdentifiersValidated)
                    {
                        string primaryDnsIdentifier = identifierAuthorizations.First().Identifier.Alias;
                        string[] alternativeDnsIdentifiers = identifierAuthorizations.Where(i => i.Identifier.Alias != primaryDnsIdentifier).Select(i => i.Identifier.Alias).ToArray();

                        if (progress != null)
                        {
                            progress.Report(new RequestProgressState {
                                CurrentState = RequestState.Running, Message = "Requesting Certificate via Lets Encrypt"
                            });
                        }
                        //await Task.Delay(200); //allow UI update

                        var certRequestResult = vaultManager.PerformCertificateRequestProcess(primaryDnsIdentifier, alternativeDnsIdentifiers);
                        if (certRequestResult.IsSuccess)
                        {
                            if (progress != null)
                            {
                                progress.Report(new RequestProgressState {
                                    CurrentState = RequestState.Success, Message = "Completed Certificate Request."
                                });
                            }

                            string pfxPath = certRequestResult.Result.ToString();

                            if (managedSite.ItemType == ManagedItemType.SSL_LetsEncrypt_LocalIIS && config.PerformAutomatedCertBinding)
                            {
                                if (progress != null)
                                {
                                    progress.Report(new RequestProgressState {
                                        CurrentState = RequestState.Running, Message = "Performing Automated Certificate Binding"
                                    });
                                }
                                //await Task.Delay(200); //allow UI update

                                var iisManager = new IISManager();

                                //Install certificate into certificate store and bind to IIS site
                                if (iisManager.InstallCertForRequest(managedSite.RequestConfig, pfxPath, cleanupCertStore: true))
                                {
                                    //all done
                                    ManagedSiteLog.AppendLog(managedSite.Id, new ManagedSiteLogItem {
                                        EventDate = DateTime.UtcNow, LogItemType = LogItemType.CertificateRequestSuccessful, Message = "Completed certificate request and automated bindings update (IIS)"
                                    });

                                    //udpate managed site summary

                                    try
                                    {
                                        var certInfo = new CertificateManager().GetCertificate(pfxPath);
                                        managedSite.DateStart = certInfo.NotBefore;
                                        managedSite.DateExpiry = certInfo.NotAfter;
                                        managedSite.DateRenewed = DateTime.Now;

                                        managedSite.CertificatePath = pfxPath;
                                    }
                                    catch (Exception)
                                    {
                                        ManagedSiteLog.AppendLog(managedSite.Id, new ManagedSiteLogItem {
                                            EventDate = DateTime.UtcNow, LogItemType = LogItemType.GeneralWarning, Message = "Failed to parse certificate dates"
                                        });
                                    }
                                    siteManager.UpdatedManagedSite(managedSite);

                                    var result = new CertificateRequestResult {
                                        ManagedItem = managedSite, IsSuccess = true, Message = "Certificate installed and SSL bindings updated for " + config.PrimaryDomain
                                    };
                                    if (progress != null)
                                    {
                                        progress.Report(new RequestProgressState {
                                            IsRunning = false, CurrentState = RequestState.Success, Message = result.Message
                                        });
                                    }

                                    return result;
                                }
                                else
                                {
                                    return new CertificateRequestResult {
                                        ManagedItem = managedSite, IsSuccess = false, Message = "An error occurred installing the certificate. Certificate file may not be valid: " + pfxPath
                                    };
                                }
                            }
                            else
                            {
                                //user has opted for manual binding of certificate
                                try
                                {
                                    var certInfo = new CertificateManager().GetCertificate(pfxPath);
                                    managedSite.DateStart = certInfo.NotBefore;
                                    managedSite.DateExpiry = certInfo.NotAfter;
                                    managedSite.DateRenewed = DateTime.Now;

                                    managedSite.CertificatePath = pfxPath;
                                }
                                catch (Exception)
                                {
                                    ManagedSiteLog.AppendLog(managedSite.Id, new ManagedSiteLogItem {
                                        EventDate = DateTime.UtcNow, LogItemType = LogItemType.GeneralWarning, Message = "Failed to parse certificate dates"
                                    });
                                }
                                siteManager.UpdatedManagedSite(managedSite);

                                return new CertificateRequestResult {
                                    ManagedItem = managedSite, IsSuccess = true, Message = "Certificate created ready for manual binding: " + pfxPath
                                };
                            }
                        }
                        else
                        {
                            return new CertificateRequestResult {
                                ManagedItem = managedSite, IsSuccess = false, Message = "The Let's Encrypt service did not issue a valid certificate in the time allowed. " + (certRequestResult.ErrorMessage ?? "")
                            };
                        }
                    }
                    else
                    {
                        return new CertificateRequestResult {
                            ManagedItem = managedSite, IsSuccess = false, Message = "Validation of the required challenges did not complete successfully. Please ensure all domains to be referenced in the Certificate can be used to access this site without redirection. "
                        };
                    }
                }
                catch (Exception exp)
                {
                    System.Diagnostics.Debug.WriteLine(exp.ToString());
                    return new CertificateRequestResult {
                        ManagedItem = managedSite, IsSuccess = false, Message = managedSite.Name + ": Request failed - " + exp.Message
                    };
                }
            }));
        }
Esempio n. 5
0
        public async Task <List <CertificateRequestResult> > PerformRenewalAllManagedSites(bool autoRenewalOnly = true, Dictionary <string, Progress <RequestProgressState> > progressTrackers = null)
        {
            await Task.Delay(200); //allow UI to update

            //currently the vault won't let us run parallel requests due to file locks
            bool performRequestsInParallel = false;

            bool testModeOnly = false;

            _siteManager.LoadSettings();

            IEnumerable <ManagedSite> sites = _siteManager.GetManagedSites();

            if (autoRenewalOnly)
            {
                sites = sites.Where(s => s.IncludeInAutoRenew == true);
            }

            // check site list and examine current certificates. If certificate is less than n days
            // old, don't attempt to renew it
            var sitesToRenew        = new List <ManagedSite>();
            var renewalIntervalDays = CoreAppSettings.Current.RenewalIntervalDays;

            int numRenewalTasks = 0;
            int maxRenewalTasks = CoreAppSettings.Current.MaxRenewalRequests;

            var renewalTasks = new List <Task <CertificateRequestResult> >();

            foreach (var s in sites.Where(s => s.IncludeInAutoRenew == true))
            {
                // determine if this site requires renewal
                bool isRenewalRequired = IsRenewalRequired(s, renewalIntervalDays);

                //if we care about stopped sites being stopped, check for that
                bool isSiteRunning = true;
                if (!CoreAppSettings.Current.IgnoreStoppedSites)
                {
                    isSiteRunning = IsManagedSiteRunning(s.Id);
                }

                if (isRenewalRequired && isSiteRunning)
                {
                    //get matching progress tracker for this site
                    IProgress <RequestProgressState> tracker = null;
                    if (progressTrackers != null)
                    {
                        tracker = progressTrackers[s.Id];
                    }

                    // optionally limit the number of renewal tasks to attempt in this pass
                    if (maxRenewalTasks == 0 || maxRenewalTasks > 0 && numRenewalTasks < maxRenewalTasks)
                    {
                        if (testModeOnly)
                        {
                            //simulated request for UI testing
                            renewalTasks.Add(this.PerformDummyCertificateRequest(s, tracker));
                        }
                        else
                        {
                            renewalTasks.Add(this.PerformCertificateRequest(s, tracker));
                        }
                    }
                    numRenewalTasks++;
                }
                else
                {
                    var msg = CoreSR.CertifyManager_SkipRenewalOk;

                    if (isRenewalRequired && !isSiteRunning)
                    {
                        //TODO: show this as warning rather than success
                        msg = CoreSR.CertifyManager_SiteStopped;
                    }

                    if (progressTrackers != null)
                    {
                        //send progress back to report skip
                        var progress = (IProgress <RequestProgressState>)progressTrackers[s.Id];
                        if (progress != null)
                        {
                            progress.Report(new RequestProgressState {
                                CurrentState = RequestState.Success, Message = msg
                            });
                        }
                    }

                    ManagedSiteLog.AppendLog(s.Id, new ManagedSiteLogItem {
                        EventDate = DateTime.UtcNow, LogItemType = LogItemType.GeneralInfo, Message = msg + s.Name
                    });
                }
            }

            if (!renewalTasks.Any())
            {
                //nothing to do
                return(new List <CertificateRequestResult>());
            }

            if (performRequestsInParallel)
            {
                var results = await Task.WhenAll(renewalTasks);

                //siteManager.StoreSettings();
                return(results.ToList());
            }
            else
            {
                var results = new List <CertificateRequestResult>();
                foreach (var t in renewalTasks)
                {
                    results.Add(await t);
                }

                return(results);
            }
        }
Esempio n. 6
0
        public async Task <List <CertificateRequestResult> > PerformRenewalAllManagedSites(bool autoRenewalOnly = true, Dictionary <string, Progress <RequestProgressState> > progressTrackers = null)
        {
            await Task.Delay(200); //allow UI to update

            //currently the vault won't let us run parallel requests due to file locks
            bool performRequestsInParallel = false;
            bool testModeOnly = false;

            siteManager.LoadSettings();

            IEnumerable <ManagedSite> sites = siteManager.GetManagedSites();

            if (autoRenewalOnly)
            {
                sites = sites.Where(s => s.IncludeInAutoRenew == true);
            }

            //check site list and examine current certificates. If certificate is less than 7 days old, don't attempt to renew it
            var sitesToRenew = new List <ManagedSite>();

            var renewalTasks = new List <Task <CertificateRequestResult> >();

            foreach (var s in sites.Where(s => s.IncludeInAutoRenew == true))
            {
                //if we know the last renewal date, check whether we shoudl renew again, otherwise assume it's more than 30 days ago by default and attempt renewal
                var timeSinceLastRenewal = (s.DateRenewed != null ? s.DateRenewed : DateTime.Now.AddDays(-30)) - DateTime.Now;
                if (Math.Abs(timeSinceLastRenewal.Value.TotalDays) > RENEWAL_THRESHOLD_DAYS)
                {
                    //get matching progress tracker for this site
                    IProgress <RequestProgressState> tracker = null;
                    if (progressTrackers != null)
                    {
                        tracker = progressTrackers[s.Id];
                    }
                    if (testModeOnly)
                    {
                        //simulated request for UI testing
                        renewalTasks.Add(this.PerformDummyCertificateRequest(null, s, tracker));
                    }
                    else
                    {
                        renewalTasks.Add(this.PerformCertificateRequest(null, s, tracker));
                    }
                }
                else
                {
                    if (progressTrackers != null)
                    {
                        //send progress back to report skip
                        var progress = (IProgress <RequestProgressState>)progressTrackers[s.Id];
                        if (progress != null)
                        {
                            progress.Report(new RequestProgressState {
                                CurrentState = RequestState.Success, Message = "Skipping Renewal, existing certificate still OK."
                            });
                        }
                    }

                    ManagedSiteLog.AppendLog(s.Id, new ManagedSiteLogItem {
                        EventDate = DateTime.UtcNow, LogItemType = LogItemType.GeneralInfo, Message = "Skipping Renewal, existing certificate still OK: " + s.Name
                    });
                }
            }

            if (!renewalTasks.Any())
            {
                //nothing to do
                return(new List <CertificateRequestResult>());
            }

            if (performRequestsInParallel)
            {
                var results = await Task.WhenAll(renewalTasks);

                //siteManager.StoreSettings();
                return(results.ToList());
            }
            else
            {
                var results = new List <CertificateRequestResult>();
                foreach (var t in renewalTasks)
                {
                    results.Add(await t);
                }

                return(results);
            }
        }
Esempio n. 7
0
        public async Task <CertificateRequestResult> PerformCertificateRequest(VaultManager vaultManager, ManagedSite managedSite)
        {
            if (vaultManager == null)
            {
                vaultManager = new VaultManager(Properties.Settings.Default.VaultPath, ACMESharp.Vault.Providers.LocalDiskVault.VAULT);
            }
            //primary domain and each subject alternative name must now be registered as an identifier with LE and validated

            var config = managedSite.RequestConfig;

            List <string> allDomains = new List <string>();

            allDomains.Add(config.PrimaryDomain);
            if (config.SubjectAlternativeNames != null)
            {
                allDomains.AddRange(config.SubjectAlternativeNames);
            }
            bool allIdentifiersValidated = true;

            if (config.ChallengeType == null)
            {
                config.ChallengeType = "http-01";
            }

            List <PendingAuthorization> identifierAuthorizations = new List <PendingAuthorization>();

            foreach (var domain in allDomains)
            {
                var identifierAlias = vaultManager.ComputeIdentifierAlias(domain);

                //check if this domain already has an associated identifier registerd with LetsEncrypt which hasn't expired yet

                var  existingIdentifier     = vaultManager.GetIdentifier(domain.Trim().ToLower());
                bool identifierAlreadyValid = false;
                if (existingIdentifier != null &&
                    existingIdentifier.Authorization != null &&
                    (existingIdentifier.Authorization.Status == "valid" || existingIdentifier.Authorization.Status == "pending") &&
                    existingIdentifier.Authorization.Expires > DateTime.Now.AddDays(1))
                {
                    //we have an existing validated identifier, reuse that for this certificate request
                    identifierAlias = existingIdentifier.Alias;

                    if (existingIdentifier.Authorization.Status == "valid")
                    {
                        identifierAlreadyValid = true;
                    }

                    // managedSite.AppendLog(new ManagedSiteLogItem { EventDate = DateTime.UtcNow, LogItemType = LogItemType.CertificateRequestStarted, Message = "Attempting Certificate Request: " + managedSite.SiteType });
                    System.Diagnostics.Debug.WriteLine("Reusing existing valid non-expired identifier for the domain " + domain);
                }

                ManagedSiteLog.AppendLog(managedSite.Id, new ManagedSiteLogItem {
                    EventDate = DateTime.UtcNow, LogItemType = LogItemType.CertificateRequestStarted, Message = "Attempting Certificate Request: " + managedSite.ItemType
                });

                //begin authorization process (register identifier, request authorization if not already given)
                var authorization = vaultManager.BeginRegistrationAndValidation(config, identifierAlias, challengeType: config.ChallengeType, domain: domain);

                if (authorization != null && !identifierAlreadyValid)
                {
                    if (authorization.Identifier.Authorization.IsPending())
                    {
                        if (managedSite.ItemType == ManagedItemType.SSL_LetsEncrypt_LocalIIS)
                        {
                            //ask LE to check our answer to their authorization challenge (http), LE will then attempt to fetch our answer, if all accessible and correct (authorized) LE will then allow us to request a certificate
                            //prepare IIS with answer for the LE challenege
                            authorization = vaultManager.PerformIISAutomatedChallengeResponse(config, authorization);

                            //if we attempted extensionless config checks, report any errors
                            if (config.PerformAutoConfig && !authorization.ExtensionlessConfigCheckedOK)
                            {
                                ManagedSiteLog.AppendLog(managedSite.Id, new ManagedSiteLogItem {
                                    EventDate = DateTime.UtcNow, LogItemType = LogItemType.CertficateRequestFailed, Message = "Failed prerequisite configuration (" + managedSite.ItemType + ")"
                                });
                                siteManager.StoreSettings();

                                return(new CertificateRequestResult {
                                    IsSuccess = false, ErrorMessage = "Automated checks for extensionless content failed. Authorizations will not be able to complete.Change the web.config in <your site>\\.well-known\\acme-challenge and ensure you can browse to http://<your site>/.well-known/acme-challenge/configcheck before proceeding."
                                });
                            }
                            else
                            {
                                //ask LE to validate our challenge response
                                vaultManager.SubmitChallenge(identifierAlias, config.ChallengeType);

                                bool identifierValidated = vaultManager.CompleteIdentifierValidationProcess(authorization.Identifier.Alias);

                                if (!identifierValidated)
                                {
                                    allIdentifiersValidated = false;
                                }
                                else
                                {
                                    identifierAuthorizations.Add(authorization);
                                }
                            }
                        }
                    }
                }
                else
                {
                    if (identifierAlreadyValid)
                    {
                        //we have previously validated this identifier and it has not yet expired, so we can just reuse it in our cert request
                        identifierAuthorizations.Add(new PendingAuthorization {
                            Identifier = existingIdentifier
                        });
                    }
                }
            }

            if (allIdentifiersValidated)
            {
                string   primaryDnsIdentifier      = identifierAuthorizations.First().Identifier.Alias;
                string[] alternativeDnsIdentifiers = identifierAuthorizations.Where(i => i.Identifier.Alias != primaryDnsIdentifier).Select(i => i.Identifier.Alias).ToArray();

                var certRequestResult = vaultManager.PerformCertificateRequestProcess(primaryDnsIdentifier, alternativeDnsIdentifiers);
                if (certRequestResult.IsSuccess)
                {
                    string pfxPath = certRequestResult.Result.ToString();

                    if (managedSite.ItemType == ManagedItemType.SSL_LetsEncrypt_LocalIIS && config.PerformAutomatedCertBinding)
                    {
                        var iisManager = new IISManager();

                        //Install certificate into certificate store and bind to IIS site
                        if (iisManager.InstallCertForDomain(config.PrimaryDomain, pfxPath, cleanupCertStore: true, skipBindings: !config.PerformAutomatedCertBinding))
                        {
                            //all done
                            ManagedSiteLog.AppendLog(managedSite.Id, new ManagedSiteLogItem {
                                EventDate = DateTime.UtcNow, LogItemType = LogItemType.CertificateRequestSuccessful, Message = "Completed certificate request and automated bindings update (IIS)"
                            });
                            siteManager.StoreSettings();

                            return(new CertificateRequestResult {
                                IsSuccess = true, ErrorMessage = "Certificate installed and SSL bindings updated for " + config.PrimaryDomain
                            });
                        }
                        else
                        {
                            return(new CertificateRequestResult {
                                IsSuccess = false, ErrorMessage = "An error occurred installing the certificate. Certificate file may not be valid: " + pfxPath
                            });
                        }
                    }
                    else
                    {
                        return(new CertificateRequestResult {
                            IsSuccess = true, ErrorMessage = "Certificate created ready for manual binding: " + pfxPath
                        });
                    }
                }
                else
                {
                    return(new CertificateRequestResult {
                        IsSuccess = false, ErrorMessage = "The Let's Encrypt service did not issue a valid certificate in the time allowed. " + (certRequestResult.ErrorMessage != null ? certRequestResult.ErrorMessage : "")
                    });
                }
            }
            else
            {
                return(new CertificateRequestResult {
                    IsSuccess = false, ErrorMessage = "Validation of the required challenges did not complete successfully. Please ensure all domains to be referenced in the Certificate can be used to access this site without redirection. "
                });
            }
        }