private void btnRequestCertificate_Click(object sender, EventArgs e) { if (lstSites.SelectedItem == null) { MessageBox.Show("No IIS Site Selected"); return; } if (VaultManager == null) { MessageBox.Show("Vault Manager is null. Please report this problem."); } //prevent further clicks on request button btnRequestCertificate.Enabled = false; ShowProgressBar(); this.Cursor = Cursors.WaitCursor; bool certsApproved = false; bool certsStored = false; CertRequestConfig config = new CertRequestConfig(); var selectItem = (SiteListItem)lstSites.SelectedItem; config.Domain = selectItem.Host; config.PerformChallengeFileCopy = true; config.WebsiteRootPath = Environment.ExpandEnvironmentVariables(selectItem.PhysicalPath); var vaultConfig = VaultManager.GetVaultConfig(); //check if domain already has an associated identifier var identifierAlias = VaultManager.ComputeIdentifierAlias(config.Domain); //try alias or DNS name before creating a new identifier var identifier = VaultManager.GetIdentifier(identifierAlias); if (identifier == null) { identifier = VaultManager.GetIdentifier(config.Domain); } if (identifier != null) { //domain already exists in vault //check if has pending authorization challenges if (identifier.Authorization != null && identifier.Authorization.Challenges != null) { var challenge = identifier.Authorization.Challenges.FirstOrDefault(c => c.Type == "http-01"); if (challenge != null) { if (challenge.Status != "invalid") { //update challenge status MessageBox.Show("An existing challenge was already in progress, status will now be updated. " + challenge.Token); VaultManager.UpdateIdentifierStatus(identifierAlias); identifier = VaultManager.GetIdentifier(identifierAlias , true); challenge = identifier.Authorization.Challenges.FirstOrDefault(c => c.Type == "http-01"); if (challenge.Status == "valid") { certsApproved = true; } } else { MessageBox.Show("The existing challenge for this identifier failed. We will need to create a new one."); identifierAlias += "_" + Guid.NewGuid().ToString().Substring(0, 6); } } } } if (!certsApproved) { var authorization = VaultManager.DomainInitAndRegistration(config, identifierAlias); if (authorization != null) { if (!authorization.ExtensionlessConfigCheckedOK) { MessageBox.Show("Automated checks for extensionless content failed. Authorisations 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."); return; } //at this point we can either get the user to manually copy the file to web site folder structure //if file has already been copied we can go ahead and ask the server to verify it //ask server to check our challenge answer is present and correct VaultManager.SubmitChallenge(authorization.Identifier.Alias); //give LE time to check our challenge answer stored on our server Thread.Sleep(2000); VaultManager.UpdateIdentifierStatus(authorization.Identifier.Alias); VaultManager.ReloadVaultConfig(); //check status of the challenge var updatedIdentifier = VaultManager.GetIdentifier(authorization.Identifier.Alias); var challenge = updatedIdentifier.Authorization.Challenges.FirstOrDefault(c => c.Type == "http-01"); //if all OK, we will be ready to fetch our certificate if (challenge?.Status == "valid") { certsApproved = true; } else { if (challenge != null) { MessageBox.Show("Challenge not yet completed. Check that http://" + config.Domain + "/" + challenge.ToString() + " path/file is present and accessible in your web browser."); } else { if (challenge.Status == "invalid") { MessageBox.Show("Challenge failed to complete. Check that http://" + config.Domain + "/" + challenge.ToString() + " path/file is present and accessible in your web browser. You may require extensionless file type mappings"); } } } } else { MessageBox.Show("Could not begin authorization. Check Logs. Ensure the domain being authorized is whitelisted with LetsEncrypt service."); } } //create certs for current authorization string certRef = null; //if (certsApproved) { certRef = VaultManager.CreateCertificate(identifierAlias); VaultManager.UpdateIdentifierStatus(identifierAlias); identifier = VaultManager.GetIdentifier(identifierAlias, true); VaultManager.ReloadVaultConfig(); if (VaultManager.CertExists(identifierAlias)) { certsStored = true; } } //auto setup/install var certInfo = VaultManager.GetCertificate(certRef); if (certInfo != null && certInfo.CrtDerFile == null) { //failed to get cert first time, try again certRef = VaultManager.CreateCertificate(identifierAlias); VaultManager.UpdateIdentifierStatus(identifierAlias); certInfo = VaultManager.GetCertificate(certRef); } //txtOutput.Text = "To complete this request copy the file " + CurrentAuthorization.TempFilePath + " to the following location under your website root (note: no file extension): " + CurrentAuthorization.Challenge.ChallengeAnswer.Key; //ReloadVault(); this.Cursor = Cursors.Default; if (!certsStored) { if (certsApproved) { MessageBox.Show("Certificates approved but not yet stored in vault. Try again later."); CloseParentForm(); return; } else { MessageBox.Show("Certificates not approved yet. Authorization challenge may have failed. Try again later."); CloseParentForm(); return; } } else { if (certInfo != null) { string certFolderPath = VaultManager.GetCertificateFilePath(certInfo.Id, LocalDiskVault.ASSET); string pfxFile = certInfo.Id.ToString() + "-all.pfx"; string pfxPath = Path.Combine(certFolderPath, pfxFile); if (!System.IO.Directory.Exists(certFolderPath)) { System.IO.Directory.CreateDirectory(certFolderPath); } if (!File.Exists(pfxPath)) { //hmm, no pfx, try to create pfx again TODO: shouldn't need this VaultManager.ExportCertificate("=" + certInfo.Id.ToString(), pfxOnly: true); } if (File.Exists(pfxPath)) { //VaultManager.UpdateIdentifierStatus(certInfo.IdentifierRef); //identifier = VaultManager.GetIdentifier(certInfo.IdentifierRef, true); IISManager iisManager = new IISManager(); if (identifier == null || identifier.Dns == null) { MessageBox.Show("Error: identifier/dns is null. Cannot match domain for binding"); } else { if (iisManager.InstallCertForDomain(identifier.Dns, pfxPath, cleanupCertStore: true, skipBindings: !chkAutoBindings.Checked)) { //all done MessageBox.Show("Certificate installed and SSL bindings updated for " + identifier.Dns, Properties.Resources.AppName); CloseParentForm(); return; } if (chkAutoBindings.Checked) { //auto store and create site bindings MessageBox.Show("Your certificate has been imported and SSL bindings updated for " + config.Domain, Properties.Resources.AppName); CloseParentForm(); return; } else { //auto store cert MessageBox.Show("Your certificate has been imported and is ready for you to configure IIS bindings.", Properties.Resources.AppName); CloseParentForm(); return; } } } else { MessageBox.Show("Failed to generate PFX file for Certificate.", Properties.Resources.AppName); CloseParentForm(); return; } } else { //cert was null MessageBox.Show("Certification was not successful. Certificate not valid or not yet authorized.", Properties.Resources.AppName); CloseParentForm(); return; } } }