/// <summary>
        /// Handles the Click event of the lbAccountRegisterSave control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param>
        protected void lbAccountRegisterSave_Click(object sender, EventArgs e)
        {
            var email = tbAccountEmail.Text;

            if (!cbTOSAgree.Checked)
            {
                divTOS.Attributes["class"] = "alert alert-danger";
                return;
            }

            var account = AcmeHelper.LoadAccountData();

            account.Email    = email;
            account.TestMode = cbTestMode.Checked;

            var acme = new AcmeService(account.TestMode);

            acme.Register(email, hlTOS.NavigateUrl);
            account.Key = Convert.ToBase64String(acme.RSA);

            AcmeHelper.SaveAccountData(account);

            ltAccountEmail.Text = email;

            pnlAccountRegister.Visible = false;
            pnlAccountDetail.Visible   = true;
            HideSecondaryBlocks(false);

            ShowDetails();
        }
Пример #2
0
        /// <summary>
        /// Handles the Click event of the lbEnableSiteRedirects control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param>
        protected void lbEnableSiteRedirects_Click(object sender, EventArgs e)
        {
            var siteNames = hfEnableSiteRedirects.Value.Split(',');
            var targetUrl = GetRedirectUrl();

            var errors = new List <string>();

            //
            // For each site that was detected as not properly configured, try to configure it.
            //
            foreach (var siteName in siteNames)
            {
                try
                {
                    AcmeHelper.EnableIISSiteRedirect(siteName, targetUrl);
                }
                catch (Exception ex)
                {
                    errors.Add(ex.Message);
                }
            }

            if (errors.Any())
            {
                nbIISError.Text = string.Format("Failed to enable the redirect on one or more sites. This may be due to insufficient permissions to make modifications to the other sites. <ul><li>{0}</li></ul>",
                                                string.Join("</li><li>", errors));
            }

            CheckIISState();
        }
Пример #3
0
 /// <summary>
 /// Handles the Click event of the lbEnableRedirectModule control.
 /// </summary>
 /// <param name="sender">The source of the event.</param>
 /// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param>
 protected void lbEnableRedirectModule_Click(object sender, EventArgs e)
 {
     if (!AcmeHelper.EnableIISHttpRedirectModule())
     {
         nbIISError.Text = "Failed to enable the IIS Http Redirect module. Rock may not have enough permissions to perform this task. Please manually enable the IIS Http Redirect module.";
     }
     else
     {
         NavigateToCurrentPage();
     }
 }
        /// <summary>
        /// Configure this block for display.
        /// </summary>
        protected void ShowDetails()
        {
            var account = AcmeHelper.LoadAccountData();

            ltAccountEmail.Text = account.Email;
            ltTestMode.Text     = account.TestMode.ToString();
            ltOfflineMode.Text  = account.OfflineMode.ToString();

            lbRegister.CssClass = string.IsNullOrWhiteSpace(account.Key) ? "btn btn-primary" : "btn btn-default";
            lbEdit.Visible      = !string.IsNullOrWhiteSpace(account.Key);
        }
        /// <summary>
        /// Handles the Click event of the lbEdit control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param>
        protected void lbEdit_Click(object sender, EventArgs e)
        {
            HideSecondaryBlocks(true);

            pnlAccountDetail.Visible = false;
            pnlAccountEdit.Visible   = true;

            var account = AcmeHelper.LoadAccountData();

            cbOfflineMode.Checked = account.OfflineMode;
        }
        /// <summary>
        /// Handles the Click event of the lbAccountEditSave control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param>
        protected void lbAccountEditSave_Click(object sender, EventArgs e)
        {
            var account = AcmeHelper.LoadAccountData();

            account.OfflineMode = cbOfflineMode.Checked;

            AcmeHelper.SaveAccountData(account);

            pnlAccountEdit.Visible   = false;
            pnlAccountDetail.Visible = true;
            HideSecondaryBlocks(false);

            ShowDetails();
        }
        /// <summary>
        /// Handles the Click event of the lbRegister control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param>
        protected void lbRegister_Click(object sender, EventArgs e)
        {
            HideSecondaryBlocks(true);

            pnlAccountDetail.Visible   = false;
            pnlAccountRegister.Visible = true;

            var account = AcmeHelper.LoadAccountData();

            tbAccountEmail.Text = account.Email;
            cbTestMode.Checked  = account.TestMode;

            nbExistingAccount.Visible = !string.IsNullOrWhiteSpace(account.Email);

            var acme = new AcmeService(false);

            hlTOS.NavigateUrl          = hlTOS.Text = acme.TermsOfServiceUrl;
            divTOS.Attributes["class"] = "alert alert-info";
        }
Пример #8
0
        /// <summary>
        /// Handles the Renew event of the gCertificates control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="RowEventArgs"/> instance containing the event data.</param>
        protected void gCertificates_Renew(object sender, RowEventArgs e)
        {
            try
            {
                string errorMessage;

                //
                // Attempt to renew the certificate, new bindings will be created as needed.
                //
                var tuple = AcmeHelper.RenewCertificate(e.RowKeyId, true, out errorMessage);

                if (tuple == null)
                {
                    nbRenewStatus.NotificationBoxType = NotificationBoxType.Danger;
                    nbRenewStatus.Text = errorMessage;
                }
                else
                {
                    if (AcmeHelper.LoadAccountData().OfflineMode)
                    {
                        string certData = Convert.ToBase64String(tuple.Item1) + "|" +
                                          string.Join("|", tuple.Item2.Select(c => Convert.ToBase64String(c)));

                        hfCertificate.Value            = Rock.Security.Encryption.EncryptString(certData);
                        pnlDownloadCertificate.Visible = true;
                    }
                    else
                    {
                        nbRenewStatus.NotificationBoxType = NotificationBoxType.Success;
                        nbRenewStatus.Text = "Certificate was renewed.";
                    }
                }

                BindGrid();
            }
            catch (Exception ex)
            {
                ExceptionLogService.LogException(ex, Context);
                throw;
            }
        }
Пример #9
0
        /// <summary>
        /// Handles the Click event of the lbPrepareCertificate control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param>
        protected void lbPrepareCertificate_Click(object sender, EventArgs e)
        {
            //
            // Decode the data in the hidden field into the raw certificate data.
            //
            var certData = Rock.Security.Encryption.DecryptString(hfCertificate.Value)
                           .Split(new char[] { '|' }, StringSplitOptions.RemoveEmptyEntries)
                           .Select(v => Convert.FromBase64String(v))
                           .ToList();

            var pkcs12 = AcmeHelper.GetPkcs12Certificate(tbCertificatePassword.Text,
                                                         certData.First(), certData.Skip(1).ToList());

            //
            // Store the password protected PKCS12 data as a binary file.
            //
            var rockContext      = new RockContext();
            var outputBinaryFile = new BinaryFile
            {
                IsTemporary      = true,
                ContentStream    = new System.IO.MemoryStream(pkcs12),
                FileName         = "Certfificate.p12",
                MimeType         = "application/x-pkcs12",
                BinaryFileTypeId = new BinaryFileTypeService(rockContext).Get(Rock.SystemGuid.BinaryFiletype.DEFAULT.AsGuid()).Id
            };

            new BinaryFileService(rockContext).Add(outputBinaryFile);

            rockContext.SaveChanges();

            //
            // Present a download link to the user.
            //
            pnlDownloadCertificate.Visible    = false;
            nbRenewStatus.Text                = string.Format("Your <a href='/GetFile.ashx?guid={0}'>certificate</a> is ready for download.", outputBinaryFile.Guid);
            nbRenewStatus.NotificationBoxType = NotificationBoxType.Success;
        }
Пример #10
0
        /// <summary>
        /// Handles the OnLoad event of the block.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param>
        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);

            if (!IsPostBack)
            {
                var account = AcmeHelper.LoadAccountData();

                pnlCertificates.Visible = !string.IsNullOrWhiteSpace(account.Key);

                if (!string.IsNullOrWhiteSpace(account.Key))
                {
                    CheckIISState();
                }

                BindGrid();
            }
            else
            {
                nbIISError.Text                = string.Empty;
                nbRenewStatus.Text             = string.Empty;
                pnlDownloadCertificate.Visible = false;
            }
        }
Пример #11
0
        /// <summary>
        /// Show the specified binding for editing.
        /// </summary>
        /// <param name="binding">The binding data to be edited or null to add a new binding.</param>
        protected void ShowBinding(BindingData binding)
        {
            ddlEditBindingSite.Items.Clear();
            ddlEditBindingSite.Items.Add(new ListItem());
            try
            {
                AcmeHelper.GetSites().ToList().ForEach(s => ddlEditBindingSite.Items.Add(s));
            }
            catch { /* Intentionally left blank */ }

            ddlEditBindingIPAddress.Items.Clear();
            ddlEditBindingIPAddress.Items.Add(new ListItem());
            try
            {
                AcmeHelper.GetIPv4Addresses().ToList().ForEach(a => ddlEditBindingIPAddress.Items.Add(a));
            }
            catch { /* Intentionally left blank */ }

            ddlEditBindingSite.SetValue(binding != null ? binding.Site : System.Web.Hosting.HostingEnvironment.SiteName);
            ddlEditBindingIPAddress.SetValue(binding != null ? binding.IPAddress : string.Empty);
            nbEditBindingPort.Text = binding != null?binding.Port.ToString() : "443";

            tbEditBindingDomain.Text = binding != null ? binding.Domain : string.Empty;
        }
Пример #12
0
        /// <summary>
        /// Performs various checks on IIS to ensure it is configured correctly for the certificates that
        /// have been setup for processing.
        /// </summary>
        protected void CheckIISState()
        {
            var rockContext = new RockContext();
            var targetUrl   = GetRedirectUrl();
            var groupTypeId = GroupTypeCache.Read(com.blueboxmoon.AcmeCertificate.SystemGuid.GroupType.ACME_CERTIFICATES).Id;
            var bindings    = new List <BindingData>();

            var groups = new GroupService(rockContext).Queryable()
                         .Where(g => g.GroupTypeId == groupTypeId);

            var siteNames = new List <string>();

            //
            // Determine if we have any certificates that edit bindings of a site other than the Rock site.
            //
            foreach (var group in groups)
            {
                var currentSiteName = System.Web.Hosting.HostingEnvironment.SiteName;

                group.LoadAttributes(rockContext);

                bindings.AddRange(group.GetAttributeValue("Bindings")
                                  .Split(new char[] { '|' }, StringSplitOptions.RemoveEmptyEntries)
                                  .Select(b => new BindingData(b))
                                  .Where(b => b.Site != currentSiteName));

                siteNames.AddRange(bindings.Where(b => !AcmeHelper.IsIISSiteRedirectEnabled(b.Site, targetUrl)).Select(b => b.Site));
            }

            //
            // If we have non-Rock sites to configure, ensure that the Http Redirect module has been
            // installed in IIS.
            //
            if (bindings.Any() && !AcmeHelper.IsHttpRedirectModuleEnabled())
            {
                pnlIISRedirectModuleWarning.Visible = true;
                pnlIISRedirectSiteWarning.Visible   = false;
            }
            else
            {
                pnlIISRedirectModuleWarning.Visible = false;
            }

            //
            // If the redirect module has been installed but we have sites that need to be
            // configured, then present a notice about those sites.
            //
            if (siteNames.Any() && !pnlIISRedirectModuleWarning.Visible)
            {
                siteNames = siteNames.Distinct().ToList();

                hfEnableSiteRedirects.Value = siteNames.AsDelimited(",");
                ltEnableSiteRedirects.Text  = "<li>" + siteNames.AsDelimited("</li><li>") + "</li>";

                ltTargetRedirect.Text             = targetUrl;
                pnlIISRedirectSiteWarning.Visible = true;
            }
            else
            {
                pnlIISRedirectSiteWarning.Visible = false;
            }
        }
Пример #13
0
        /// <summary>
        /// Called when another block on the page requests secondary blocks to hide or become visible.
        /// </summary>
        /// <param name="visible">True if this block should become visible.</param>
        public void SetVisible(bool visible)
        {
            var account = AcmeHelper.LoadAccountData();

            pnlCertificates.Visible = visible && !string.IsNullOrWhiteSpace(account.Key);
        }
Пример #14
0
        private static async Task FetchAndCreateDnsRecords(ILogger log, string subscriptionId, CertificateRenewalInputModel certifcate, AcmeHelper acmeHelper, string domainName)
        {
            var dnsHelper = new DnsHelper(subscriptionId);

            log.LogInformation("Fetching DNS authorization");
            var dnsText = await acmeHelper.GetDnsAuthorizationTextAsync();

            var dnsName = ("_acme-challenge." + domainName).Replace("." + certifcate.DnsZoneName, "").Trim();

            log.LogInformation($"Got DNS challenge {dnsText} for {dnsName}");
            await CreateDnsTxtRecordsIfNecessary(log, certifcate, dnsHelper, dnsText, dnsName);

            log.LogInformation("Waiting 60 seconds for DNS propagation");
            await Task.Delay(60 * 1000);
        }
Пример #15
0
        public static async Task Run([TimerTrigger("0 17 23 * * *")] TimerInfo myTimer, ILogger log, ExecutionContext executionContext)
        {
            log.LogInformation($"C# Timer trigger function executed at: {DateTime.Now}");

            string subscriptionId = Environment.GetEnvironmentVariable("SubscriptionId");
            var    config         = new ConfigurationBuilder()
                                    .SetBasePath(executionContext.FunctionAppDirectory)
                                    .AddJsonFile("local.settings.json", optional: true, reloadOnChange: true)
                                    .AddEnvironmentVariables()
                                    .Build();

            var certificateDetails = new List <CertificateRenewalInputModel>();

            config.GetSection("CertificateDetails").Bind(certificateDetails);

            foreach (var certifcate in certificateDetails)
            {
                log.LogInformation($"Processing certificate - {certifcate.DomainName}");
                var acmeHelper        = new AcmeHelper(log);
                var certificateHelper = new KeyVaultCertificateHelper(certifcate.KeyVaultName);

                await InitAcme(log, certifcate, acmeHelper);

                string domainName = certifcate.DomainName;
                if (domainName.StartsWith("*"))
                {
                    domainName = domainName.Substring(1);
                }
                log.LogInformation($"Calculated domain name is {domainName}");

                string keyVaultCertificateName = domainName.Replace(".", "");
                log.LogInformation($"Getting expiry for {keyVaultCertificateName} in Key Vault certifictes");
                var certificateExpiry = await certificateHelper.GetCertificateExpiryAsync(keyVaultCertificateName);

                if (certificateExpiry.HasValue && certificateExpiry.Value.Subtract(DateTime.UtcNow).TotalDays > 7)
                {
                    log.LogInformation("No certificates to renew.");
                    continue;
                }

                log.LogInformation("Creating order for certificates");

                await acmeHelper.CreateOrderAsync(certifcate.DomainName);

                log.LogInformation("Authorization created");

                await FetchAndCreateDnsRecords(log, subscriptionId, certifcate, acmeHelper, domainName);

                log.LogInformation("Validating DNS challenge");

                await acmeHelper.ValidateDnsAuthorizationAsync();

                log.LogInformation("Challenge validated");

                string password = Guid.NewGuid().ToString();
                var    pfx      = await acmeHelper.GetPfxCertificateAsync(password, certifcate.CertificateCountryName, certifcate.CertificateState, certifcate.CertificateLocality,
                                                                          certifcate.CertificateOrganization, certifcate.CertificateOrganizationUnit, certifcate.DomainName, domainName);

                log.LogInformation("Certificate built");

                (string certificateName, string certificateVerison) = await certificateHelper.ImportCertificate(keyVaultCertificateName, pfx, password);

                log.LogInformation("Certificate imported");

                var cdnHelper = new CdnHelper(subscriptionId);
                await cdnHelper.EnableHttpsForCustomDomain(certifcate.CdnResourceGroup, certifcate.CdnProfileName,
                                                           certifcate.CdnEndpointName, certifcate.CdnCustomDomainName, certificateName, certificateVerison, certifcate.KeyVaultName);

                log.LogInformation("HTTPS enabling started");
            }
        }
Пример #16
0
        private static async Task InitAcme(ILogger log, CertificateRenewalInputModel certifcate, AcmeHelper acmeHelper)
        {
            var secretHelper   = new KeyVaultSecretHelper(certifcate.KeyVaultName);
            var acmeAccountPem = await secretHelper.GetSecretAsync("AcmeAccountKeyPem");

            if (string.IsNullOrWhiteSpace(acmeAccountPem))
            {
                log.LogInformation("Acme Account not found.");
                string pem = await acmeHelper.InitWithNewAccountAsync(Environment.GetEnvironmentVariable("AcmeAccountEmail"));

                log.LogInformation("Acme account created");
                await secretHelper.SetSecretAsync("AcmeAccountKeyPem", pem);

                log.LogInformation("Secret uploaded to key vault");
            }
            else
            {
                acmeHelper.InitWithExistingAccount(acmeAccountPem);
            }
        }