示例#1
0
        public static void Init(VaultConfig config, AcmeClient client)
        {
            client.Init();

            if (config.GetInitialDirectory)
                client.GetDirectory(config.UseRelativeInitialDirectory);
        }
        public void Initialize()
        {
            Log.Information("Initializing ACME client");

            ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12;

            if (!File.Exists(_options.WellKnownFilePaths[WellKnownFile.AcmeSigner]))
            {
                throw new FileNotFoundException($"Signer file '{_options.WellKnownFilePaths[WellKnownFile.AcmeSigner]}' not found");
            }
            if (!File.Exists(_options.WellKnownFilePaths[WellKnownFile.AcmeRegistration]))
            {
                throw new FileNotFoundException($"Registration file '{_options.WellKnownFilePaths[WellKnownFile.AcmeRegistration]}' not found");
            }

            _signer.Init();

            using (FileStream signerStream = File.OpenRead(_options.WellKnownFilePaths[WellKnownFile.AcmeSigner]))
                _signer.Load(signerStream);

            _client.Signer  = _signer;
            _client.RootUrl = new Uri(_options.BaseUri);

            _client.Init();
            _client.GetDirectory(true);

            using (FileStream registrationStream = File.OpenRead(_options.WellKnownFilePaths[WellKnownFile.AcmeRegistration]))
                _client.Registration = AcmeRegistration.Load(registrationStream);

            _isInitialized = true;
        }
        private async void loadToolStripMenuItem_Click(object sender, EventArgs e)
        {
            var account = cmbAccounts.SelectedItem as Account;

            if (account == null)
            {
                log.Error("not account.");
                await Task.FromResult(0);

                return;
            }

            RS256Signer signer = null;
            AcmeClient  client = null;

            try
            {
                signer = account.Signer;

                client = new AcmeClient(new Uri(account.uri), new AcmeServerDirectory(), signer);
                client.Init();
                log.Info("\nGetting AcmeServerDirectory");
                client.GetDirectory(true);

                client.Registration = account.Registration;

                this.client  = client;
                this.account = account;
                log.Info("load done.");
                labInfo.Text = account.ToString();
                btnRefreshDomains.PerformClick();
            }
            catch (Exception ex)
            {
                if (client != null)
                {
                    client.Dispose();
                    client = null;
                }

                if (signer != null)
                {
                    signer.Dispose();
                    signer = null;
                }

                var acmeWebException = ex as AcmeClient.AcmeWebException;
                if (acmeWebException != null)
                {
                    log.Error(acmeWebException.Message);
                    log.Error("ACME Server Returned:");
                    log.Error(acmeWebException.Response.ContentAsString);
                }
                else
                {
                    log.Error(ex);
                }
            }
        }
示例#4
0
        public static void Init(VaultInfo config, AcmeClient client)
        {
            client.Init();

            if (config.GetInitialDirectory)
            {
                client.GetDirectory(config.UseRelativeInitialDirectory);
            }
        }
        private bool TryLoad(bool force = false)
        {
            if (force == false && this.client != null)
            {
                return(true);
            }

            RS256Signer signer = null;
            AcmeClient  client = null;

            try
            {
                signer = account.Signer;

                client = new AcmeClient(new Uri(account.uri), new AcmeServerDirectory(), signer);
                client.Init();
                log.Info("\nGetting AcmeServerDirectory");
                client.GetDirectory(true);

                client.Registration = account.Registration;

                this.client = client;
                log.Info("load done.");
                labInfo.Text = account.ToString();
                //
                return(true);
            }
            catch (Exception ex)
            {
                if (client != null)
                {
                    client.Dispose();
                    client = null;
                }

                if (signer != null)
                {
                    signer.Dispose();
                    signer = null;
                }

                var acmeWebException = ex as AcmeClient.AcmeWebException;
                if (acmeWebException != null)
                {
                    log.Error(acmeWebException.Message);
                    log.Error("ACME Server Returned:");
                    log.Error(acmeWebException.Response.ContentAsString);
                }
                else
                {
                    log.Error(ex);
                }
            }
            return(false);
        }
示例#6
0
        private static void ConfigureAcmeClient(AcmeClient client)
        {
            if (!string.IsNullOrWhiteSpace(Options.Proxy))
            {
                client.Proxy            = new WebProxy(Options.Proxy);
                Console.ForegroundColor = ConsoleColor.Yellow;
                Console.WriteLine("Proxying via " + Options.Proxy);
                Console.ResetColor();
            }

            var signerPath = Path.Combine(_configPath, "Signer");

            if (File.Exists(signerPath))
            {
                LoadSignerFromFile(client.Signer, signerPath);
            }

            _client.Init();

            Log.Information("Getting AcmeServerDirectory");
            _client.GetDirectory(true);

            var registrationPath = Path.Combine(_configPath, "Registration");

            if (File.Exists(registrationPath))
            {
                LoadRegistrationFromFile(registrationPath);
            }
            else
            {
                string email = Options.SignerEmail;
                if (string.IsNullOrWhiteSpace(email))
                {
                    Console.Write("Enter an email address (not public, used for renewal fail notices): ");
                    email = Console.ReadLine().Trim();
                }

                string[] contacts = GetContacts(email);

                AcmeRegistration registration = CreateRegistration(contacts);

                if (!Options.AcceptTos && !Options.Renew)
                {
                    if (!Input.PromptYesNo($"Do you agree to {registration.TosLinkUri}?"))
                    {
                        return;
                    }
                }

                UpdateRegistration();
                SaveRegistrationToFile(registrationPath);
                SaveSignerToFile(_client.Signer, signerPath);
            }
        }
示例#7
0
        private static void ConfigureAcmeClient(AcmeClient client)
        {
            if (!string.IsNullOrWhiteSpace(Properties.Settings.Default.Proxy))
            {
                client.Proxy = new WebProxy(Properties.Settings.Default.Proxy);
                Log.Warning("Proxying via {proxy}", Properties.Settings.Default.Proxy);
            }

            var signerPath = Path.Combine(_configPath, "Signer");

            if (File.Exists(signerPath))
            {
                LoadSignerFromFile(client.Signer, signerPath);
            }

            _client.Init();
            _client.BeforeGetResponseAction = (x) =>
            {
                Log.Debug("Send {method} request to {uri}", x.Method, x.RequestUri);
            };
            Log.Debug("Getting AcmeServerDirectory");
            _client.GetDirectory(true);

            var registrationPath = Path.Combine(_configPath, "Registration");

            if (File.Exists(registrationPath))
            {
                LoadRegistrationFromFile(registrationPath);
            }
            else
            {
                string email = Options.EmailAddress;
                if (string.IsNullOrWhiteSpace(email))
                {
                    email = Input.RequestString("Enter an email address (not public, used for renewal fail notices)");
                }

                string[] contacts = GetContacts(email);

                AcmeRegistration registration = CreateRegistration(contacts);

                if (!Options.AcceptTos && !Options.Renew)
                {
                    if (!Input.PromptYesNo($"Do you agree to {registration.TosLinkUri}?"))
                    {
                        return;
                    }
                }

                UpdateRegistration();
                SaveRegistrationToFile(registrationPath);
                SaveSignerToFile(_client.Signer, signerPath);
            }
        }
示例#8
0
        public static AcmeClient CreateAcmeClient(ISigner signer, AcmeRegistration registration)
        {
            var client = new AcmeClient(new Uri(Program.GlobalConfiguration.AcmeServerBaseUri), new AcmeServerDirectory(), signer, registration);

            if (!string.IsNullOrWhiteSpace(Program.GlobalConfiguration.ProxyUri))
            {
                client.Proxy = new WebProxy(Program.GlobalConfiguration.ProxyUri, false, new string[0], new NetworkCredential(Program.GlobalConfiguration.ProxyUserName, Program.GlobalConfiguration.ProxyPassword));
            }

            client.Init();
            client.GetDirectory(true);
            return(client);
        }
示例#9
0
        private void ConfigureAcmeClient()
        {
            _client.Proxy = _proxyService.GetWebProxy();

            var signerPath = Path.Combine(_settings.ConfigPath, "Signer");

            if (File.Exists(signerPath))
            {
                LoadSignerFromFile(_client.Signer, signerPath);
            }

            _client.Init();
            _client.BeforeGetResponseAction = (x) =>
            {
                _log.Debug("Send {method} request to {uri}", x.Method, x.RequestUri);
            };
            _log.Debug("Getting AcmeServerDirectory");
            _client.GetDirectory(true);

            var registrationPath = Path.Combine(_settings.ConfigPath, "Registration");

            if (File.Exists(registrationPath))
            {
                LoadRegistrationFromFile(registrationPath);
            }
            else
            {
                var email = _optionsService.Options.EmailAddress;
                if (string.IsNullOrWhiteSpace(email))
                {
                    email = _input.RequestString("Enter an email address (not public, used for renewal fail notices)");
                }

                var contacts = GetContacts(email);

                var registration = CreateRegistration(contacts);

                if (!_optionsService.Options.AcceptTos && !_optionsService.Options.Renew)
                {
                    if (!_input.PromptYesNo($"Do you agree to {registration.TosLinkUri}?"))
                    {
                        return;
                    }
                }

                UpdateRegistration();
                SaveRegistrationToFile(registrationPath);
                SaveSignerToFile(_client.Signer, signerPath);
            }
        }
        public AcmeClient Configure(TargetApplication targetApplication)
        {
            var contacts = NormalizeContacts();
            var signer   = new RS256Signer();

            var basePath = _configuration.GetBaseOutPutPath(targetApplication);

            ConfigureSigner(signer, basePath);

            var acmeServerBaseUri = _configuration.GetAcmeServerBaseUri(targetApplication);
            var acmeClient        = new AcmeClient(acmeServerBaseUri, new AcmeServerDirectory(), signer);

            acmeClient.Init();
            acmeClient.GetDirectory(saveRelative: true);

            ProcessRegistration(acmeClient, contacts.ToArray(), basePath);

            return(acmeClient);
        }
示例#11
0
        static void SetupAcmeClient(AcmeClient client, RS256Signer signer, string registrationFile, string signerFile, string email)
        {
            client.Init();
            client.GetDirectory(true);

            if (!File.Exists(registrationFile))
            {
                client.Register(new string[] { $"mailto:{email}" });

                using (var registrationStream = File.OpenWrite(registrationFile))
                    client.Registration.Save(registrationStream);
            }

            using (var registrationStream = File.OpenRead(registrationFile))
                client.Registration = AcmeRegistration.Load(registrationStream);

            client.UpdateRegistration(true, true);

            using (var signerStream = File.OpenWrite(signerFile))
                signer.Save(signerStream);
        }
        public void Init()
        {
            _RegistrationJsonPath = Path.Combine(Config.Path, "Registration.json");
            _SignerXmlPath        = Path.Combine(Config.Path, "Signer.xml");

            _Signer = new RS256Signer();
            _Signer.Init();

            // Load signer from file if it exists
            if (File.Exists(_SignerXmlPath))
            {
                Globals.Log($"Loading {_SignerXmlPath}");
                using (var FS = File.OpenRead(_SignerXmlPath)) _Signer.Load(FS);
            }

            // Init client
            _Client = new AcmeClient(new Uri(Config.BaseUri), new AcmeServerDirectory(), _Signer);
            _Client.Init();
            _Client.GetDirectory(true);

            // Load registration from file, or prompt for email and create registration
            LoadOrCreateRegistration();
        }
示例#13
0
        public static string RequestAndInstallInternal(Target target)
        {
            BaseURI    = target.BaseUri ?? "https://acme-staging.api.letsencrypt.org/";
            configPath = ConfigPath(BaseURI);
            try
            {
                webSiteClient = ArmHelper.GetWebSiteManagementClient(target);
            }
            catch (Exception ex)
            {
                Trace.TraceError("Unabled to create Azure Web Site Management client " + ex.ToString());
                throw;
            }

            if (!Directory.Exists(configPath))
            {
                Directory.CreateDirectory(configPath);
            }
            var email = target.Email;

            try
            {
                using (var signer = new RS256Signer())
                {
                    signer.Init();

                    var signerPath = Path.Combine(configPath, "Signer");
                    if (File.Exists(signerPath))
                    {
                        Trace.TraceInformation($"Loading Signer from {signerPath}");
                        using (var signerStream = File.OpenRead(signerPath))
                            signer.Load(signerStream);
                    }

                    using (client = new AcmeClient(new Uri(BaseURI), new AcmeServerDirectory(), signer))
                    {
                        client.Init();
                        Trace.TraceInformation("\nGetting AcmeServerDirectory");
                        client.GetDirectory(true);

                        var registrationPath = Path.Combine(configPath, "Registration");
                        if (File.Exists(registrationPath))
                        {
                            Trace.TraceInformation($"Loading Registration from {registrationPath}");
                            using (var registrationStream = File.OpenRead(registrationPath))
                                client.Registration = AcmeRegistration.Load(registrationStream);
                        }
                        else
                        {
                            var contacts = new string[] { };
                            if (!String.IsNullOrEmpty(email))
                            {
                                email    = "mailto:" + email;
                                contacts = new string[] { email };
                            }

                            Trace.TraceInformation("Calling Register");
                            var registration = client.Register(contacts);


                            Trace.TraceInformation("Updating Registration");
                            client.UpdateRegistration(true, true);

                            Trace.TraceInformation("Saving Registration");
                            using (var registrationStream = File.OpenWrite(registrationPath))
                                client.Registration.Save(registrationStream);

                            Trace.TraceInformation("Saving Signer");
                            using (var signerStream = File.OpenWrite(signerPath))
                                signer.Save(signerStream);
                        }

                        //                        if (Options.Renew)
                        //                        {
                        //                            CheckRenewals();
                        //#if DEBUG
                        //                            Trace.TraceInformation("Press enter to continue.");
                        //                            Trace.ReadLine();
                        //#endif
                        //                            return;
                        //                        }
                        return(Auto(target));
                    }
                }
            }
            catch (Exception e)
            {
                var acmeWebException = e as AcmeClient.AcmeWebException;
                if (acmeWebException != null)
                {
                    Trace.TraceError(acmeWebException.Message);
                    Trace.TraceError("ACME Server Returned:");
                    Trace.TraceError(acmeWebException.Response.ContentAsString);
                }
                else
                {
                    Trace.TraceError(e.ToString());
                }
                throw;
            }
        }
        private async void btnCreate_Click(object sender, EventArgs e)
        {
            Working(true);
            var ok = CheckInput();

            if (ok == false)
            {
                Working(false);
                await Task.FromResult(0);

                return;
            }

            var c = db.Accounts.Find(o => o.email == txtEmail.Text && o.uri == cmbUrl.Text)
                    .Count();

            if (c > 0)
            {
                log.Warn($"该账号已存在。");
                return;
            }
            RS256Signer signer = null;

            try
            {
                signer = new RS256Signer();
                signer.Init();

                client = new AcmeClient(new Uri(cmbUrl.Text), new AcmeServerDirectory(), signer);

                client.Init();
                log.Info("Getting AcmeServerDirectory");
                client.GetDirectory(true);

                var email = txtEmail.Text;

                var contacts = new string[] { };
                if (!String.IsNullOrEmpty(email))
                {
                    email    = "mailto:" + email;
                    contacts = new string[] { email };
                }
                log.Info("Calling Register");
                var registration = client.Register(contacts);

                log.Info("Updating Registration");
                client.UpdateRegistration(true, true);

                log.Info("Saving Registration");

                account = new Account()
                {
                    email        = txtEmail.Text,
                    Registration = client.Registration,
                    Signer       = signer,
                    uri          = cmbUrl.Text,
                };
                if (string.IsNullOrWhiteSpace(txtTokens.Text) == false)
                {
                    var m = txtTokens.Text.Split("\r\n".ToArray(), StringSplitOptions.RemoveEmptyEntries)
                            .Select(o => o.Trim())
                            .Distinct().ToList();
                    account.dnspod_tokens = m.Where(o => Dnspod.DnspodApi.TokenRegex.IsMatch(o)).ToList();
                }
                db.Accounts.Insert(account);
                Working(false);
                log.Info("register done.");
                this.DialogResult = DialogResult.OK;
            }
            catch (Exception ex)
            {
                var acmeWebException = ex as AcmeClient.AcmeWebException;
                if (acmeWebException != null)
                {
                    log.Error(acmeWebException.Message);
                    log.Error("ACME Server Returned:");
                    log.Error(acmeWebException.Response.ContentAsString);
                }
                else
                {
                    log.Error(e);
                }
                if (client != null)
                {
                    client.Dispose();
                    client = null;
                }

                if (signer != null)
                {
                    signer.Dispose();
                    signer = null;
                }
                account = null;
                Working(false);
                return;
            }
        }
示例#15
0
        private static async Task Process(bool Verbose, Uri Directory, string[] ContactURLs, bool TermsOfServiceAgreed,
                                          bool NewKey, string[] DomainNames, DateTime?NotBefore, DateTime?NotAfter, string HttpRootFolder, int PollingInterval,
                                          int KeySize, string EMail, string Country, string Locality, string StateOrProvince, string Organization,
                                          string OrganizationalUnit, string FileName, string Password)
        {
            using (AcmeClient Client = new AcmeClient(Directory))
            {
                Log.Informational("Connecting to directory.",
                                  new KeyValuePair <string, object>("URL", Directory.ToString()));

                AcmeDirectory AcmeDirectory = await Client.GetDirectory();

                if (AcmeDirectory.ExternalAccountRequired)
                {
                    Log.Warning("An external account is required.");
                }

                if (AcmeDirectory.TermsOfService != null)
                {
                    Log.Informational("Terms of service available.",
                                      new KeyValuePair <string, object>("URL", AcmeDirectory.TermsOfService.ToString()));
                }

                if (AcmeDirectory.Website != null)
                {
                    Log.Informational("Web site available.",
                                      new KeyValuePair <string, object>("URL", AcmeDirectory.Website.ToString()));
                }


                Log.Informational("Getting account.");

                AcmeAccount Account;

                try
                {
                    Account = await Client.GetAccount();

                    Log.Informational("Account found.",
                                      new KeyValuePair <string, object>("Created", Account.CreatedAt),
                                      new KeyValuePair <string, object>("Initial IP", Account.InitialIp),
                                      new KeyValuePair <string, object>("Status", Account.Status),
                                      new KeyValuePair <string, object>("Contact", Account.Contact));

                    if (ContactURLs != null && !AreEqual(Account.Contact, ContactURLs))
                    {
                        Log.Informational("Updating contact URIs in account.");

                        Account = await Account.Update(ContactURLs);

                        Log.Informational("Account updated.",
                                          new KeyValuePair <string, object>("Created", Account.CreatedAt),
                                          new KeyValuePair <string, object>("Initial IP", Account.InitialIp),
                                          new KeyValuePair <string, object>("Status", Account.Status),
                                          new KeyValuePair <string, object>("Contact", Account.Contact));
                    }
                }
                catch (AcmeAccountDoesNotExistException)
                {
                    Log.Warning("Account not found. Creating account.",
                                new KeyValuePair <string, object>("Contact", ContactURLs),
                                new KeyValuePair <string, object>("TermsOfServiceAgreed", TermsOfServiceAgreed));

                    Account = await Client.CreateAccount(ContactURLs, TermsOfServiceAgreed);

                    Log.Informational("Account created.",
                                      new KeyValuePair <string, object>("Created", Account.CreatedAt),
                                      new KeyValuePair <string, object>("Initial IP", Account.InitialIp),
                                      new KeyValuePair <string, object>("Status", Account.Status),
                                      new KeyValuePair <string, object>("Contact", Account.Contact));
                }

                if (NewKey)
                {
                    Log.Informational("Generating new key.");

                    await Account.NewKey();

                    Log.Informational("New key generated.");
                }


                if (DomainNames != null)
                {
                    if (!string.IsNullOrEmpty(HttpRootFolder))
                    {
                        CheckExists(HttpRootFolder);
                        HttpRootFolder = Path.Combine(HttpRootFolder, ".well-known");
                        CheckExists(HttpRootFolder);
                        HttpRootFolder = Path.Combine(HttpRootFolder, "acme-challenge");
                        CheckExists(HttpRootFolder);
                    }

                    Log.Informational("Creating order.");

                    AcmeOrder Order = await Account.OrderCertificate(DomainNames, NotBefore, NotAfter);

                    Log.Informational("Order created.",
                                      new KeyValuePair <string, object>("Status", Order.Status),
                                      new KeyValuePair <string, object>("Expires", Order.Expires),
                                      new KeyValuePair <string, object>("NotBefore", Order.NotBefore),
                                      new KeyValuePair <string, object>("NotAfter", Order.NotAfter),
                                      new KeyValuePair <string, object>("Identifiers", Order.Identifiers));

                    List <string> FileNames = null;

                    try
                    {
                        foreach (AcmeAuthorization Authorization in await Order.GetAuthorizations())
                        {
                            Log.Informational("Processing authorization.",
                                              new KeyValuePair <string, object>("Type", Authorization.Type),
                                              new KeyValuePair <string, object>("Value", Authorization.Value),
                                              new KeyValuePair <string, object>("Status", Authorization.Status),
                                              new KeyValuePair <string, object>("Expires", Authorization.Expires),
                                              new KeyValuePair <string, object>("Wildcard", Authorization.Wildcard));

                            AcmeChallenge Challenge;
                            bool          Manual       = true;
                            int           Index        = 1;
                            int           NrChallenges = Authorization.Challenges.Length;
                            string        s;

                            for (Index = 1; Index <= NrChallenges; Index++)
                            {
                                Challenge = Authorization.Challenges[Index - 1];

                                if (Challenge is AcmeHttpChallenge HttpChallenge)
                                {
                                    Log.Informational(Index.ToString() + ") HTTP challenge.",
                                                      new KeyValuePair <string, object>("Resource", HttpChallenge.ResourceName),
                                                      new KeyValuePair <string, object>("Response", HttpChallenge.KeyAuthorization),
                                                      new KeyValuePair <string, object>("Content-Type", "application/octet-stream"));

                                    if (!string.IsNullOrEmpty(HttpRootFolder))
                                    {
                                        string ChallengeFileName = Path.Combine(HttpRootFolder, HttpChallenge.Token);
                                        File.WriteAllBytes(ChallengeFileName, Encoding.ASCII.GetBytes(HttpChallenge.KeyAuthorization));

                                        if (FileNames == null)
                                        {
                                            FileNames = new List <string>();
                                        }

                                        FileNames.Add(ChallengeFileName);

                                        Log.Informational("Acknowleding challenge.");

                                        Challenge = await HttpChallenge.AcknowledgeChallenge();

                                        Log.Informational("Challenge acknowledged.",
                                                          new KeyValuePair <string, object>("Status", Challenge.Status));

                                        Manual = false;
                                    }
                                    else if (!Verbose)
                                    {
                                        Console.Out.WriteLine(Index.ToString() + ") HTTP challenge.");
                                        Console.Out.WriteLine("Resource: " + HttpChallenge.ResourceName);
                                        Console.Out.WriteLine("Response: " + HttpChallenge.KeyAuthorization);
                                        Console.Out.WriteLine("Content-Type: " + "application/octet-stream");
                                    }
                                }
                                else if (Challenge is AcmeDnsChallenge DnsChallenge)
                                {
                                    Log.Informational(Index.ToString() + ") DNS challenge.",
                                                      new KeyValuePair <string, object>("Domain", DnsChallenge.ValidationDomainNamePrefix + Authorization.Value),
                                                      new KeyValuePair <string, object>("TXT Record", DnsChallenge.KeyAuthorization));

                                    if (!Verbose)
                                    {
                                        Console.Out.WriteLine(Index.ToString() + ") DNS challenge.");
                                        Console.Out.WriteLine("Domain: " + DnsChallenge.ValidationDomainNamePrefix + Authorization.Value);
                                        Console.Out.WriteLine("TXT Record: " + DnsChallenge.KeyAuthorization);
                                    }
                                }
                            }

                            if (Manual)
                            {
                                Console.Out.WriteLine();
                                Console.Out.WriteLine("No automated method found to respond to any of the authorization challenges. " +
                                                      "You can respond to a challenge manually. After configuring the corresponding " +
                                                      "resource, enter the number of the corresponding challenge and press ENTER to acknowledge it.");

                                do
                                {
                                    Console.Out.Write("Challenge to acknowledge: ");
                                    s = Console.In.ReadLine();
                                }while (!int.TryParse(s, out Index) || Index <= 0 || Index > NrChallenges);

                                Log.Informational("Acknowleding challenge.");

                                Challenge = await Authorization.Challenges[Index - 1].AcknowledgeChallenge();

                                Log.Informational("Challenge acknowledged.",
                                                  new KeyValuePair <string, object>("Status", Challenge.Status));
                            }

                            AcmeAuthorization Authorization2 = Authorization;

                            do
                            {
                                Log.Informational("Waiting to poll authorization status.",
                                                  new KeyValuePair <string, object>("ms", PollingInterval));

                                System.Threading.Thread.Sleep(PollingInterval);

                                Log.Informational("Polling authorization.");

                                Authorization2 = await Authorization2.Poll();

                                Log.Informational("Authorization polled.",
                                                  new KeyValuePair <string, object>("Type", Authorization2.Type),
                                                  new KeyValuePair <string, object>("Value", Authorization2.Value),
                                                  new KeyValuePair <string, object>("Status", Authorization2.Status),
                                                  new KeyValuePair <string, object>("Expires", Authorization2.Expires),
                                                  new KeyValuePair <string, object>("Wildcard", Authorization2.Wildcard));
                            }while (Authorization2.Status == AcmeAuthorizationStatus.pending);

                            if (Authorization2.Status != AcmeAuthorizationStatus.valid)
                            {
                                switch (Authorization2.Status)
                                {
                                case AcmeAuthorizationStatus.deactivated:
                                    throw new Exception("Authorization deactivated.");

                                case AcmeAuthorizationStatus.expired:
                                    throw new Exception("Authorization expired.");

                                case AcmeAuthorizationStatus.invalid:
                                    throw new Exception("Authorization invalid.");

                                case AcmeAuthorizationStatus.revoked:
                                    throw new Exception("Authorization revoked.");

                                default:
                                    throw new Exception("Authorization not validated.");
                                }
                            }
                        }

                        using (RSACryptoServiceProvider RSA = new RSACryptoServiceProvider(KeySize))
                        {
                            Log.Informational("Finalizing order.");

                            SignatureAlgorithm SignAlg = new RsaSha256(RSA);

                            Order = await Order.FinalizeOrder(new Security.ACME.CertificateRequest(SignAlg)
                            {
                                CommonName = DomainNames[0],
                                SubjectAlternativeNames = DomainNames,
                                EMailAddress            = EMail,
                                Country            = Country,
                                Locality           = Locality,
                                StateOrProvince    = StateOrProvince,
                                Organization       = Organization,
                                OrganizationalUnit = OrganizationalUnit
                            });

                            Log.Informational("Order finalized.",
                                              new KeyValuePair <string, object>("Status", Order.Status),
                                              new KeyValuePair <string, object>("Expires", Order.Expires),
                                              new KeyValuePair <string, object>("NotBefore", Order.NotBefore),
                                              new KeyValuePair <string, object>("NotAfter", Order.NotAfter),
                                              new KeyValuePair <string, object>("Identifiers", Order.Identifiers));

                            if (Order.Status != AcmeOrderStatus.valid)
                            {
                                switch (Order.Status)
                                {
                                case AcmeOrderStatus.invalid:
                                    throw new Exception("Order invalid.");

                                default:
                                    throw new Exception("Unable to validate oder.");
                                }
                            }

                            if (Order.Certificate == null)
                            {
                                throw new Exception("No certificate URI provided.");
                            }

                            System.Security.Cryptography.X509Certificates.X509Certificate2[] Certificates =
                                await Order.DownloadCertificate();

                            string CertificateFileName;
                            string CertificateFileName2;
                            int    Index = 1;
                            byte[] Bin;

                            DerEncoder KeyOutput = new DerEncoder();
                            SignAlg.ExportPrivateKey(KeyOutput);

                            StringBuilder PemOutput = new StringBuilder();

                            PemOutput.AppendLine("-----BEGIN RSA PRIVATE KEY-----");
                            PemOutput.AppendLine(Convert.ToBase64String(KeyOutput.ToArray(), Base64FormattingOptions.InsertLineBreaks));
                            PemOutput.AppendLine("-----END RSA PRIVATE KEY-----");

                            CertificateFileName = FileName + ".key";

                            Log.Informational("Saving private key.",
                                              new KeyValuePair <string, object>("FileName", CertificateFileName));

                            File.WriteAllText(CertificateFileName, PemOutput.ToString(), Encoding.ASCII);

                            foreach (X509Certificate2 Certificate in Certificates)
                            {
                                if (Index == 1)
                                {
                                    CertificateFileName = FileName;
                                }
                                else
                                {
                                    CertificateFileName = FileName + Index.ToString();
                                }

                                CertificateFileName2 = CertificateFileName + ".pem";
                                CertificateFileName += ".cer";

                                Bin = Certificate.Export(X509ContentType.Cert);

                                Log.Informational("Saving certificate.",
                                                  new KeyValuePair <string, object>("FileName", CertificateFileName),
                                                  new KeyValuePair <string, object>("FileName2", CertificateFileName2),
                                                  new KeyValuePair <string, object>("FriendlyName", Certificate.FriendlyName),
                                                  new KeyValuePair <string, object>("HasPrivateKey", Certificate.HasPrivateKey),
                                                  new KeyValuePair <string, object>("Issuer", Certificate.Issuer),
                                                  new KeyValuePair <string, object>("NotAfter", Certificate.NotAfter),
                                                  new KeyValuePair <string, object>("NotBefore", Certificate.NotBefore),
                                                  new KeyValuePair <string, object>("SerialNumber", Certificate.SerialNumber),
                                                  new KeyValuePair <string, object>("Subject", Certificate.Subject),
                                                  new KeyValuePair <string, object>("Thumbprint", Certificate.Thumbprint));

                                File.WriteAllBytes(CertificateFileName, Bin);

                                PemOutput.Clear();
                                PemOutput.AppendLine("-----BEGIN CERTIFICATE-----");
                                PemOutput.AppendLine(Convert.ToBase64String(Bin, Base64FormattingOptions.InsertLineBreaks));
                                PemOutput.AppendLine("-----END CERTIFICATE-----");

                                File.WriteAllText(CertificateFileName2, PemOutput.ToString(), Encoding.ASCII);

                                Index++;
                            }
                        }
                    }
                    finally
                    {
                        if (FileNames != null)
                        {
                            foreach (string FileName2 in FileNames)
                            {
                                File.Delete(FileName2);
                            }
                        }
                    }
                }
            }
        }
示例#16
0
        internal bool MakeCertificate()
        {
            try
            {
                using (var signer = new RS256Signer())
                {
                    signer.Init();

                    var signerPath = Path.Combine(configPath, "Signer");
                    if (File.Exists(signerPath))
                    {
                        logger.Debug("Loading Signer from {0}", signerPath);
                        using (var signerStream = File.OpenRead(signerPath))
                        {
                            signer.Load(signerStream);
                        }
                    }

                    using (client = new AcmeClient(new Uri(acmeUri), new AcmeServerDirectory(), signer))
                    {
                        client.Init();
                        client.GetDirectory(true);

                        var registrationPath = Path.Combine(configPath, "Registration");
                        if (File.Exists(registrationPath))
                        {
                            logger.Debug("Loading Registration from {0}", registrationPath);
                            using (var registrationStream = File.OpenRead(registrationPath))
                            {
                                client.Registration = AcmeRegistration.Load(registrationStream);
                            }
                        }
                        else
                        {
                            var email        = "mailto:" + contactEmail;
                            var registration = client.Register(new string[] { email });

                            client.UpdateRegistration(true, true);

                            using (var registrationStream = File.OpenWrite(registrationPath))
                            {
                                client.Registration.Save(registrationStream);
                            }

                            using (var signerStream = File.OpenWrite(signerPath))
                            {
                                signer.Save(signerStream);
                            }
                        }

                        List <AuthorizationState> authStatus = Authorize();

                        if (authStatus.Any(a => a.Status == "invalid"))
                        {
                            return(false);
                        }

                        PathToCertificate = RequestCertificate();
                    }
                }
            }
            catch (Exception e)
            {
                if (e is AcmeClient.AcmeWebException acmeWebException)
                {
                    logger.Fatal(acmeWebException.Message);
                    logger.Fatal("ACME Server Returned: {0}", acmeWebException.Response.ContentAsString);
                }
                else
                {
                    logger.Fatal(e);
                }

                return(false);
            }

            return(true);
        }
示例#17
0
        static void Main(string[] args)
        {
            var commandLineParseResult = Parser.Default.ParseArguments<Options>(args);
            var parsed = commandLineParseResult as Parsed<Options>;
            if (parsed == null)
            {
#if DEBUG
                Console.WriteLine("Press enter to continue.");
                Console.ReadLine();
#endif
                return; // not parsed
            }
            options = parsed.Value;

            Console.WriteLine("Let's Encrypt (Simple Windows ACME Client)");

            BaseURI = options.BaseURI;
            if (options.Test)
                BaseURI = "https://acme-staging.api.letsencrypt.org/";

            //Console.Write("\nUse production Let's Encrypt server? (Y/N) ");
            //if (PromptYesNo())
            //    BaseURI = ProductionBaseURI;

            Console.WriteLine($"\nACME Server: {BaseURI}");

            settings = new Settings(clientName, BaseURI);

            configPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), clientName, CleanFileName(BaseURI));
            Console.WriteLine("Config Folder: " + configPath);
            Directory.CreateDirectory(configPath);

            using (var signer = new RS256Signer())
            {
                signer.Init();

                var signerPath = Path.Combine(configPath, "Signer");
                if (File.Exists(signerPath))
                {
                    Console.WriteLine($"Loading Signer from {signerPath}");
                    using (var signerStream = File.OpenRead(signerPath))
                        signer.Load(signerStream);
                }

                using (client = new AcmeClient(new Uri(BaseURI), new AcmeServerDirectory(), signer))
                {
                    client.Init();
                    Console.WriteLine("\nGetting AcmeServerDirectory");
                    client.GetDirectory(true);

                    var registrationPath = Path.Combine(configPath, "Registration");
                    if (File.Exists(registrationPath))
                    {
                        Console.WriteLine($"Loading Registration from {registrationPath}");
                        using (var registrationStream = File.OpenRead(registrationPath))
                            client.Registration = AcmeRegistration.Load(registrationStream);
                    }
                    else
                    {
                        Console.WriteLine("Calling Register");
                        var registration = client.Register(new string[] { });

                        if (!options.AcceptTOS && !options.Renew)
                        {
                            Console.WriteLine($"Do you agree to {registration.TosLinkUri}? (Y/N) ");
                            if (!PromptYesNo())
                                return;
                        }

                        Console.WriteLine("Updating Registration");
                        client.UpdateRegistration(true, true);

                        Console.WriteLine("Saving Registration");
                        using (var registrationStream = File.OpenWrite(registrationPath))
                            client.Registration.Save(registrationStream);

                        Console.WriteLine("Saving Signer");
                        using (var signerStream = File.OpenWrite(signerPath))
                            signer.Save(signerStream);
                    }

                    if (options.Renew)
                    {
                        CheckRenewals();
#if DEBUG
                        Console.WriteLine("Press enter to continue.");
                        Console.ReadLine();
#endif
                        return;
                    }

                    Console.WriteLine("\nScanning IIS 7 Site Bindings for Hosts (Elevated Permissions Required)");
                    if (!IsElevated)
                    {
                        Console.WriteLine("Elevated Permissions Required. Please run under an administrator console.");
#if DEBUG
                        Console.WriteLine("Press enter to continue.");
                        Console.ReadLine();
#endif
                        return;
                    }


                    var bindings = GetHostNames();
                    if (bindings.Count == 0)
                    {
                        Console.WriteLine("No IIS bindings with host names were found. Please add one using IIS Manager. A host name and site path are required to verify domain ownership.");
                        return;
                    }

                    Console.WriteLine("IIS Bindings");
                    var count = 1;
                    foreach (var binding in bindings)
                    {
                        Console.WriteLine($" {count}: {binding}");
                        count++;
                    }

                    Console.WriteLine();
                    Console.WriteLine(" A: Get Certificates for All Bindings");
                    Console.WriteLine(" Q: Quit");
                    Console.Write("Which binding do you want to get a cert for: ");
                    var response = Console.ReadLine();
                    switch (response.ToLowerInvariant())
                    {
                        case "a":
                            foreach (var binding in bindings)
                            {
                                Auto(binding);
                            }
                            break;
                        case "q":
                            return;
                        default:
                            var bindingId = 0;
                            if (Int32.TryParse(response, out bindingId))
                            {
                                bindingId--;
                                if (bindingId >= 0 && bindingId < bindings.Count)
                                {
                                    var binding = bindings[bindingId];
                                    Auto(binding);
                                }
                            }
                            break;
                    }
                }
            }

#if DEBUG
            Console.WriteLine("Press enter to continue.");
            Console.ReadLine();
#endif
        }
示例#18
0
        static void Main(string[] args)
        {
            var commandLineParseResult = Parser.Default.ParseArguments <Options>(args);
            var parsed = commandLineParseResult as Parsed <Options>;

            if (parsed == null)
            {
#if DEBUG
                Console.WriteLine("Press enter to continue.");
                Console.ReadLine();
#endif
                return; // not parsed
            }
            Options = parsed.Value;

            Console.WriteLine("Let's Encrypt (Simple Windows ACME Client)");

            BaseURI = Options.BaseURI;
            if (Options.Test)
            {
                BaseURI = "https://acme-staging.api.letsencrypt.org/";
            }

            //Console.Write("\nUse production Let's Encrypt server? (Y/N) ");
            //if (PromptYesNo())
            //    BaseURI = ProductionBaseURI;

            Console.WriteLine($"\nACME Server: {BaseURI}");

            if (!string.IsNullOrWhiteSpace(Options.CentralSSLStore))
            {
                Console.WriteLine("Using Centralized SSL Path: " + Options.CentralSSLStore);
                CentralSSL = true;
            }

            settings = new Settings(clientName, BaseURI);

            configPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), clientName, CleanFileName(BaseURI));
            Console.WriteLine("Config Folder: " + configPath);
            Directory.CreateDirectory(configPath);

            try
            {
                using (var signer = new RS256Signer())
                {
                    signer.Init();

                    var signerPath = Path.Combine(configPath, "Signer");
                    if (File.Exists(signerPath))
                    {
                        Console.WriteLine($"Loading Signer from {signerPath}");
                        using (var signerStream = File.OpenRead(signerPath))
                            signer.Load(signerStream);
                    }

                    using (client = new AcmeClient(new Uri(BaseURI), new AcmeServerDirectory(), signer))
                    {
                        client.Init();
                        Console.WriteLine("\nGetting AcmeServerDirectory");
                        client.GetDirectory(true);

                        var registrationPath = Path.Combine(configPath, "Registration");
                        if (File.Exists(registrationPath))
                        {
                            Console.WriteLine($"Loading Registration from {registrationPath}");
                            using (var registrationStream = File.OpenRead(registrationPath))
                                client.Registration = AcmeRegistration.Load(registrationStream);
                        }
                        else
                        {
                            Console.Write("Enter an email address (not public, used for renewal fail notices): ");
                            var email = Console.ReadLine().Trim();

                            var contacts = new string[] { };
                            if (!String.IsNullOrEmpty(email))
                            {
                                email    = "mailto:" + email;
                                contacts = new string[] { email };
                            }

                            Console.WriteLine("Calling Register");
                            var registration = client.Register(contacts);

                            if (!Options.AcceptTOS && !Options.Renew)
                            {
                                Console.WriteLine($"Do you agree to {registration.TosLinkUri}? (Y/N) ");
                                if (!PromptYesNo())
                                {
                                    return;
                                }
                            }

                            Console.WriteLine("Updating Registration");
                            client.UpdateRegistration(true, true);

                            Console.WriteLine("Saving Registration");
                            using (var registrationStream = File.OpenWrite(registrationPath))
                                client.Registration.Save(registrationStream);

                            Console.WriteLine("Saving Signer");
                            using (var signerStream = File.OpenWrite(signerPath))
                                signer.Save(signerStream);
                        }

                        if (Options.Renew)
                        {
                            CheckRenewals();
#if DEBUG
                            Console.WriteLine("Press enter to continue.");
                            Console.ReadLine();
#endif
                            return;
                        }

                        var targets = new List <Target>();
                        foreach (var plugin in Target.Plugins.Values)
                        {
                            targets.AddRange(plugin.GetTargets());
                        }

                        if (targets.Count == 0)
                        {
                            Console.WriteLine("No targets found.");
                        }
                        else
                        {
                            var count = 1;
                            foreach (var binding in targets)
                            {
                                Console.WriteLine($" {count}: {binding}");
                                count++;
                            }
                        }

                        Console.WriteLine();
                        foreach (var plugin in Target.Plugins.Values)
                        {
                            plugin.PrintMenu();
                        }

                        Console.WriteLine(" A: Get certificates for all hosts");
                        Console.WriteLine(" Q: Quit");
                        Console.Write("Which host do you want to get a certificate for: ");
                        var response = Console.ReadLine().ToLowerInvariant();
                        switch (response)
                        {
                        case "a":
                            foreach (var target in targets)
                            {
                                Auto(target);
                            }
                            break;

                        case "q":
                            return;

                        default:
                            var targetId = 0;
                            if (Int32.TryParse(response, out targetId))
                            {
                                targetId--;
                                if (targetId >= 0 && targetId < targets.Count)
                                {
                                    var binding = targets[targetId];
                                    Auto(binding);
                                }
                            }
                            else
                            {
                                foreach (var plugin in Target.Plugins.Values)
                                {
                                    plugin.HandleMenuResponse(response, targets);
                                }
                            }
                            break;
                        }
                    }
                }
            }
            catch (Exception e)
            {
                Console.ForegroundColor = ConsoleColor.Red;
                var acmeWebException = e as AcmeClient.AcmeWebException;
                if (acmeWebException != null)
                {
                    Console.WriteLine(acmeWebException.Message);
                    Console.WriteLine("ACME Server Returned:");
                    Console.WriteLine(acmeWebException.Response.ContentAsString);
                }
                else
                {
                    Console.WriteLine(e);
                }
                Console.ResetColor();
            }

            Console.WriteLine("Press enter to continue.");
            Console.ReadLine();
        }
示例#19
0
        public AcmeClient Register(RS256Signer signer)
        {
            if (!Directory.Exists(configPath))
            {
                Directory.CreateDirectory(configPath);
            }
            var email = config.RegistrationEmail;

            try
            {
                signer.Init();

                var signerPath = Path.Combine(configPath, "Signer");
                if (File.Exists(signerPath))
                {
                    Trace.TraceInformation($"Loading Signer from {signerPath}");
                    using (var signerStream = File.OpenRead(signerPath))
                        signer.Load(signerStream);
                }

                var client = new AcmeClient(new Uri(this.baseURI), new AcmeServerDirectory(), signer);

                client.Init();
                Trace.TraceInformation("\nGetting AcmeServerDirectory");
                client.GetDirectory(true);

                var registrationPath = Path.Combine(configPath, "Registration");
                if (File.Exists(registrationPath))
                {
                    Trace.TraceInformation($"Loading Registration from {registrationPath}");
                    using (var registrationStream = File.OpenRead(registrationPath))
                        client.Registration = AcmeRegistration.Load(registrationStream);
                }
                else
                {
                    var contacts = new string[] { };
                    if (!String.IsNullOrEmpty(email))
                    {
                        email    = "mailto:" + email;
                        contacts = new string[] { email };
                    }

                    Trace.TraceInformation("Calling Register");
                    var registration = client.Register(contacts);


                    Trace.TraceInformation("Updating Registration");
                    client.UpdateRegistration(true, true);

                    Trace.TraceInformation("Saving Registration");
                    using (var registrationStream = File.OpenWrite(registrationPath))
                        client.Registration.Save(registrationStream);

                    Trace.TraceInformation("Saving Signer");
                    using (var signerStream = File.OpenWrite(signerPath))
                        signer.Save(signerStream);
                }
                return(client);
            }
            catch (Exception e)
            {
                var acmeWebException = e as AcmeClient.AcmeWebException;
                if (acmeWebException != null)
                {
                    Trace.TraceError(acmeWebException.Message);
                    Trace.TraceError("ACME Server Returned:");
                    Trace.TraceError(acmeWebException.Response.ContentAsString);
                }
                else
                {
                    Trace.TraceError(e.ToString());
                }
                throw;
            }
        }
示例#20
0
        static void Main(string[] args)
        {
            Log.Logger = new LoggerConfiguration()
                         .ReadFrom.AppSettings()
                         .CreateLogger();
            Log.Information("The global logger has been configured");

            ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12;

            var commandLineParseResult = Parser.Default.ParseArguments <Options>(args);
            var parsed = commandLineParseResult as Parsed <Options>;

            if (parsed == null)
            {
#if DEBUG
                Log.Debug("Program Debug Enabled");
                Console.WriteLine("Press enter to continue.");
                Console.ReadLine();
#endif
                return; // not parsed
            }
            Options = parsed.Value;
            Log.Debug("{@Options}", Options);
            Console.WriteLine("Let's Encrypt (Simple Windows ACME Client)");
            BaseURI = Options.BaseURI;
            if (Options.Test)
            {
                BaseURI = "https://acme-staging.api.letsencrypt.org/";
                Log.Debug("Test paramater set: {BaseURI}", BaseURI);
            }
            if (Options.SAN)
            {
                Log.Debug("SAN Option Enabled: Running per site and not per host");
            }

            Console.WriteLine($"\nACME Server: {BaseURI}");
            Log.Information("ACME Server: {BaseURI}", BaseURI);

            if (!string.IsNullOrWhiteSpace(Options.CentralSSLStore))
            {
                Console.WriteLine("Using Centralized SSL Path: " + Options.CentralSSLStore);
                Log.Information("Using Centralized SSL Path: {CentralSSLStore}", Options.CentralSSLStore);
                CentralSSL = true;
            }

            settings = new Settings(clientName, BaseURI);
            Log.Debug("{@settings}", settings);
            configPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), clientName, CleanFileName(BaseURI));
            Console.WriteLine("Config Folder: " + configPath);
            Log.Information("Config Folder: {configPath}", configPath);
            Directory.CreateDirectory(configPath);

            certificatePath = Properties.Settings.Default.CertificatePath;

            if (string.IsNullOrWhiteSpace(certificatePath))
            {
                certificatePath = configPath;
            }
            else
            {
                try
                {
                    Directory.CreateDirectory(certificatePath);
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"Error creating the certificate directory, {certificatePath}. Defaulting to config path");
                    Log.Warning("Error creating the certificate directory, {certificatePath}. Defaulting to config path. Error: {@ex}", certificatePath, ex);

                    certificatePath = configPath;
                }
            }

            Console.WriteLine("Certificate Folder: " + certificatePath);
            Log.Information("Certificate Folder: {certificatePath}", certificatePath);

            try
            {
                using (var signer = new RS256Signer())
                {
                    signer.Init();

                    var signerPath = Path.Combine(configPath, "Signer");
                    if (File.Exists(signerPath))
                    {
                        Console.WriteLine($"Loading Signer from {signerPath}");
                        Log.Information("Loading Signer from {signerPath}", signerPath);
                        using (var signerStream = File.OpenRead(signerPath))
                            signer.Load(signerStream);
                    }

                    using (client = new AcmeClient(new Uri(BaseURI), new AcmeServerDirectory(), signer))
                    {
                        client.Init();
                        Console.WriteLine("\nGetting AcmeServerDirectory");
                        Log.Information("Getting AcmeServerDirectory");
                        client.GetDirectory(true);

                        var registrationPath = Path.Combine(configPath, "Registration");
                        if (File.Exists(registrationPath))
                        {
                            Console.WriteLine($"Loading Registration from {registrationPath}");
                            Log.Information("Loading Registration from {registrationPath}", registrationPath);
                            using (var registrationStream = File.OpenRead(registrationPath))
                                client.Registration = AcmeRegistration.Load(registrationStream);
                        }
                        else
                        {
                            Console.Write("Enter an email address (not public, used for renewal fail notices): ");
                            var email = Console.ReadLine().Trim();

                            var contacts = new string[] { };
                            if (!String.IsNullOrEmpty(email))
                            {
                                Log.Debug("Registration email: {email}", email);
                                email    = "mailto:" + email;
                                contacts = new string[] { email };
                            }

                            Console.WriteLine("Calling Register");
                            Log.Information("Calling Register");
                            var registration = client.Register(contacts);

                            if (!Options.AcceptTOS && !Options.Renew)
                            {
                                Console.WriteLine($"Do you agree to {registration.TosLinkUri}? (Y/N) ");
                                if (!PromptYesNo())
                                {
                                    return;
                                }
                            }

                            Console.WriteLine("Updating Registration");
                            Log.Information("Updating Registration");
                            client.UpdateRegistration(true, true);

                            Console.WriteLine("Saving Registration");
                            Log.Information("Saving Registration");
                            using (var registrationStream = File.OpenWrite(registrationPath))
                                client.Registration.Save(registrationStream);

                            Console.WriteLine("Saving Signer");
                            Log.Information("Saving Signer");
                            using (var signerStream = File.OpenWrite(signerPath))
                                signer.Save(signerStream);
                        }

                        if (Options.Renew)
                        {
                            CheckRenewals();
#if DEBUG
                            Console.WriteLine("Press enter to continue.");
                            Console.ReadLine();
#endif
                            return;
                        }
                        var targets = new List <Target>();
                        if (!Options.SAN)
                        {
                            foreach (var plugin in Target.Plugins.Values)
                            {
                                targets.AddRange(plugin.GetTargets());
                            }
                        }
                        else
                        {
                            foreach (var plugin in Target.Plugins.Values)
                            {
                                targets.AddRange(plugin.GetSites());
                            }
                        }

                        if (targets.Count == 0)
                        {
                            Console.WriteLine("No targets found.");
                            Log.Error("No targets found.");
                        }
                        else
                        {
                            int HostsPerPage = 50;
                            try
                            {
                                HostsPerPage = Properties.Settings.Default.HostsPerPage;
                            }
                            catch (Exception ex)
                            {
                                Log.Error("Error getting HostsPerPage setting, setting to default value. Error: {@ex}", ex);
                            }
                            var count = 1;
                            if (targets.Count > HostsPerPage)
                            {
                                do
                                {
                                    if ((count + HostsPerPage) <= targets.Count)
                                    {
                                        int stop = count + HostsPerPage;
                                        for (int i = count; i < stop; i++)
                                        {
                                            if (!Options.SAN)
                                            {
                                                Console.WriteLine($" {count}: {targets[count - 1]}");
                                            }
                                            else
                                            {
                                                Console.WriteLine($" {count}: SAN - {targets[count - 1]}");
                                            }
                                            count++;
                                        }
                                    }
                                    else
                                    {
                                        for (int i = count; i <= targets.Count; i++)
                                        {
                                            if (!Options.SAN)
                                            {
                                                Console.WriteLine($" {count}: {targets[count - 1]}");
                                            }
                                            else
                                            {
                                                Console.WriteLine($" {count}: SAN - {targets[count - 1]}");
                                            }
                                            count++;
                                        }
                                    }

                                    if (count < targets.Count)
                                    {
                                        Console.WriteLine(" Q: Quit");
                                        Console.Write("Press enter to continue to next page ");
                                        var continueResponse = Console.ReadLine().ToLowerInvariant();
                                        switch (continueResponse)
                                        {
                                        case "q":
                                            throw new Exception($"Requested to quit application");

                                        default:
                                            break;
                                        }
                                    }
                                }while (count < targets.Count);
                            }
                            else
                            {
                                foreach (var binding in targets)
                                {
                                    Console.WriteLine($" {count}: {binding}");
                                    count++;
                                }
                            }
                        }

                        Console.WriteLine();
                        foreach (var plugin in Target.Plugins.Values)
                        {
                            plugin.PrintMenu();
                        }

                        Console.WriteLine(" A: Get certificates for all hosts");
                        Console.WriteLine(" Q: Quit");
                        Console.Write("Which host do you want to get a certificate for: ");
                        var response = Console.ReadLine().ToLowerInvariant();
                        switch (response)
                        {
                        case "a":
                            foreach (var target in targets)
                            {
                                Auto(target);
                            }
                            break;

                        case "q":
                            return;

                        default:
                            var targetId = 0;
                            if (Int32.TryParse(response, out targetId))
                            {
                                targetId--;
                                if (targetId >= 0 && targetId < targets.Count)
                                {
                                    var binding = targets[targetId];
                                    Auto(binding);
                                }
                            }
                            else
                            {
                                foreach (var plugin in Target.Plugins.Values)
                                {
                                    plugin.HandleMenuResponse(response, targets);
                                }
                            }
                            break;
                        }
                    }
                }
            }
            catch (Exception e)
            {
                Log.Error("Error {@e}", e);
                Console.ForegroundColor = ConsoleColor.Red;
                var acmeWebException = e as AcmeClient.AcmeWebException;
                if (acmeWebException != null)
                {
                    Console.WriteLine(acmeWebException.Message);
                    Console.WriteLine("ACME Server Returned:");
                    Console.WriteLine(acmeWebException.Response.ContentAsString);
                }
                else
                {
                    Console.WriteLine(e);
                }
                Console.ResetColor();
            }

            Console.WriteLine("Press enter to continue.");
            Console.ReadLine();
        }
        internal async Task <bool> CreateCertificate(string TabID)
        {
            try
            {
                string        URL = this.customCA ? this.acmeDirectory : "https://acme-v02.api.letsencrypt.org/directory";
                RSAParameters Parameters;
                CspParameters CspParams = new CspParameters()
                {
                    Flags            = CspProviderFlags.UseMachineKeyStore,
                    KeyContainerName = "IoTGateway:" + URL
                };

                try
                {
                    bool Ok;

                    using (RSACryptoServiceProvider RSA = new RSACryptoServiceProvider(4096, CspParams))
                    {
                        Parameters = RSA.ExportParameters(true);

                        if (RSA.KeySize < 4096)
                        {
                            RSA.PersistKeyInCsp = false;
                            RSA.Clear();
                            Ok = false;
                        }
                        else
                        {
                            Ok = true;
                        }
                    }

                    if (!Ok)
                    {
                        using (RSACryptoServiceProvider RSA = new RSACryptoServiceProvider(4096, CspParams))
                        {
                            Parameters = RSA.ExportParameters(true);
                        }
                    }
                }
                catch (CryptographicException ex)
                {
                    throw new CryptographicException("Unable to get access to cryptographic key for \"IoTGateway:" + URL +
                                                     "\". Was the database created using another user?", ex);
                }

                using (AcmeClient Client = new AcmeClient(new Uri(URL), Parameters))
                {
                    ClientEvents.PushEvent(new string[] { TabID }, "ShowStatus", "Connecting to directory.", false, "User");

                    AcmeDirectory AcmeDirectory = await Client.GetDirectory();

                    if (AcmeDirectory.ExternalAccountRequired)
                    {
                        ClientEvents.PushEvent(new string[] { TabID }, "ShowStatus", "An external account is required.", false, "User");
                    }

                    if (AcmeDirectory.TermsOfService != null)
                    {
                        URL = AcmeDirectory.TermsOfService.ToString();
                        ClientEvents.PushEvent(new string[] { TabID }, "ShowStatus", "Terms of service available on: " + URL, false, "User");
                        ClientEvents.PushEvent(new string[] { TabID }, "TermsOfService", URL, false, "User");

                        this.urlToS = URL;

                        if (!this.acceptToS)
                        {
                            ClientEvents.PushEvent(new string[] { TabID }, "CertificateError", "You need to accept the terms of service.", false, "User");
                            return(false);
                        }
                    }

                    if (AcmeDirectory.Website != null)
                    {
                        ClientEvents.PushEvent(new string[] { TabID }, "ShowStatus", "Web site available on: " + AcmeDirectory.Website.ToString(), false, "User");
                    }

                    ClientEvents.PushEvent(new string[] { TabID }, "ShowStatus", "Getting account.", false, "User");

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

                    if (!string.IsNullOrEmpty(this.domain))
                    {
                        Names.Add(this.domain);
                    }

                    if (this.alternativeDomains != null)
                    {
                        foreach (string Name in this.alternativeDomains)
                        {
                            if (!Names.Contains(Name))
                            {
                                Names.Add(Name);
                            }
                        }
                    }
                    string[] DomainNames = Names.ToArray();

                    AcmeAccount Account;

                    try
                    {
                        Account = await Client.GetAccount();

                        ClientEvents.PushEvent(new string[] { TabID }, "ShowStatus", "Account found.", false, "User");
                        ClientEvents.PushEvent(new string[] { TabID }, "ShowStatus", "Created: " + Account.CreatedAt.ToString(), false, "User");
                        ClientEvents.PushEvent(new string[] { TabID }, "ShowStatus", "Initial IP: " + Account.InitialIp, false, "User");
                        ClientEvents.PushEvent(new string[] { TabID }, "ShowStatus", "Status: " + Account.Status.ToString(), false, "User");

                        if (string.IsNullOrEmpty(this.contactEMail))
                        {
                            if (Account.Contact != null && Account.Contact.Length != 0)
                            {
                                ClientEvents.PushEvent(new string[] { TabID }, "ShowStatus", "Updating contact URIs in account.", false, "User");
                                Account = await Account.Update(new string[0]);

                                ClientEvents.PushEvent(new string[] { TabID }, "ShowStatus", "Account updated.", false, "User");
                            }
                        }
                        else
                        {
                            if (Account.Contact is null || Account.Contact.Length != 1 || Account.Contact[0] != "mailto:" + this.contactEMail)
                            {
                                ClientEvents.PushEvent(new string[] { TabID }, "ShowStatus", "Updating contact URIs in account.", false, "User");
                                Account = await Account.Update(new string[] { "mailto:" + this.contactEMail });

                                ClientEvents.PushEvent(new string[] { TabID }, "ShowStatus", "Account updated.", false, "User");
                            }
                        }
                    }
                    catch (AcmeAccountDoesNotExistException)
                    {
                        ClientEvents.PushEvent(new string[] { TabID }, "ShowStatus", "Account not found.", false, "User");
                        ClientEvents.PushEvent(new string[] { TabID }, "ShowStatus", "Creating account.", false, "User");

                        Account = await Client.CreateAccount(string.IsNullOrEmpty(this.contactEMail)?new string[0] : new string[] { "mailto:" + this.contactEMail },
                                                             this.acceptToS);

                        ClientEvents.PushEvent(new string[] { TabID }, "ShowStatus", "Account created.", false, "User");
                        ClientEvents.PushEvent(new string[] { TabID }, "ShowStatus", "Status: " + Account.Status.ToString(), false, "User");
                    }

                    ClientEvents.PushEvent(new string[] { TabID }, "ShowStatus", "Generating new key.", false, "User");
                    await Account.NewKey();

                    using (RSACryptoServiceProvider RSA = new RSACryptoServiceProvider(4096, CspParams))
                    {
                        RSA.ImportParameters(Client.ExportAccountKey(true));
                    }

                    ClientEvents.PushEvent(new string[] { TabID }, "ShowStatus", "New key generated.", false, "User");

                    ClientEvents.PushEvent(new string[] { TabID }, "ShowStatus", "Creating order.", false, "User");
                    AcmeOrder Order = await Account.OrderCertificate(DomainNames, null, null);

                    ClientEvents.PushEvent(new string[] { TabID }, "ShowStatus", "Order created.", false, "User");

                    foreach (AcmeAuthorization Authorization in await Order.GetAuthorizations())
                    {
                        ClientEvents.PushEvent(new string[] { TabID }, "ShowStatus", "Processing authorization for " + Authorization.Value, false, "User");

                        AcmeChallenge Challenge;
                        bool          Acknowledged = false;
                        int           Index        = 1;
                        int           NrChallenges = Authorization.Challenges.Length;

                        for (Index = 1; Index <= NrChallenges; Index++)
                        {
                            Challenge = Authorization.Challenges[Index - 1];

                            if (Challenge is AcmeHttpChallenge HttpChallenge)
                            {
                                this.challenge = "/" + HttpChallenge.Token;
                                this.token     = HttpChallenge.KeyAuthorization;

                                ClientEvents.PushEvent(new string[] { TabID }, "ShowStatus", "Acknowleding challenge.", false, "User");
                                Challenge = await HttpChallenge.AcknowledgeChallenge();

                                ClientEvents.PushEvent(new string[] { TabID }, "ShowStatus", "Challenge acknowledged: " + Challenge.Status.ToString(), false, "User");

                                Acknowledged = true;
                            }
                        }

                        if (!Acknowledged)
                        {
                            ClientEvents.PushEvent(new string[] { TabID }, "CertificateError", "No automated method found to respond to any of the authorization challenges.", false, "User");
                            return(false);
                        }

                        AcmeAuthorization Authorization2 = Authorization;

                        do
                        {
                            ClientEvents.PushEvent(new string[] { TabID }, "ShowStatus", "Waiting to poll authorization status.", false, "User");
                            await Task.Delay(5000);

                            ClientEvents.PushEvent(new string[] { TabID }, "ShowStatus", "Polling authorization.", false, "User");
                            Authorization2 = await Authorization2.Poll();

                            ClientEvents.PushEvent(new string[] { TabID }, "ShowStatus", "Authorization polled: " + Authorization2.Status.ToString(), false, "User");
                        }while (Authorization2.Status == AcmeAuthorizationStatus.pending);

                        if (Authorization2.Status != AcmeAuthorizationStatus.valid)
                        {
                            switch (Authorization2.Status)
                            {
                            case AcmeAuthorizationStatus.deactivated:
                                throw new Exception("Authorization deactivated.");

                            case AcmeAuthorizationStatus.expired:
                                throw new Exception("Authorization expired.");

                            case AcmeAuthorizationStatus.invalid:
                                throw new Exception("Authorization invalid.");

                            case AcmeAuthorizationStatus.revoked:
                                throw new Exception("Authorization revoked.");

                            default:
                                throw new Exception("Authorization not validated.");
                            }
                        }
                    }

                    using (RSACryptoServiceProvider RSA = new RSACryptoServiceProvider(4096))                       // TODO: Make configurable
                    {
                        ClientEvents.PushEvent(new string[] { TabID }, "ShowStatus", "Finalizing order.", false, "User");

                        SignatureAlgorithm SignAlg = new RsaSha256(RSA);

                        Order = await Order.FinalizeOrder(new CertificateRequest(SignAlg)
                        {
                            CommonName = this.domain,
                            SubjectAlternativeNames = DomainNames,
                            EMailAddress            = this.contactEMail
                        });

                        ClientEvents.PushEvent(new string[] { TabID }, "ShowStatus", "Order finalized: " + Order.Status.ToString(), false, "User");

                        if (Order.Status != AcmeOrderStatus.valid)
                        {
                            switch (Order.Status)
                            {
                            case AcmeOrderStatus.invalid:
                                throw new Exception("Order invalid.");

                            default:
                                throw new Exception("Unable to validate oder.");
                            }
                        }

                        if (Order.Certificate is null)
                        {
                            throw new Exception("No certificate URI provided.");
                        }

                        ClientEvents.PushEvent(new string[] { TabID }, "ShowStatus", "Downloading certificate.", false, "User");

                        X509Certificate2[] Certificates = await Order.DownloadCertificate();

                        X509Certificate2 Certificate = Certificates[0];

                        ClientEvents.PushEvent(new string[] { TabID }, "ShowStatus", "Exporting certificate.", false, "User");

                        this.certificate = Certificate.Export(X509ContentType.Cert);
                        this.privateKey  = RSA.ExportCspBlob(true);
                        this.pfx         = null;
                        this.password    = string.Empty;

                        ClientEvents.PushEvent(new string[] { TabID }, "ShowStatus", "Adding private key.", false, "User");

                        try
                        {
                            Certificate.PrivateKey = RSA;
                        }
                        catch (PlatformNotSupportedException)
                        {
                            ClientEvents.PushEvent(new string[] { TabID }, "ShowStatus", "Platform does not support adding of private key.", false, "User");
                            ClientEvents.PushEvent(new string[] { TabID }, "ShowStatus", "Searching for OpenSSL on machine.", false, "User");

                            string[] Files;
                            string   Password      = Hashes.BinaryToString(Gateway.NextBytes(32));
                            string   CertFileName  = null;
                            string   CertFileName2 = null;
                            string   KeyFileName   = null;

                            if (string.IsNullOrEmpty(this.openSslPath) || !File.Exists(this.openSslPath))
                            {
                                Files = GetFiles(new string(Path.DirectorySeparatorChar, 1), "openssl.exe");
                                if (Files is null)
                                {
                                    List <string> Files2 = new List <string>();

                                    Files = GetFiles(Environment.SpecialFolder.ProgramFiles, "openssl.exe");
                                    if (Files != null)
                                    {
                                        Files2.AddRange(Files);
                                    }

                                    Files = GetFiles(Environment.SpecialFolder.CommonProgramFilesX86, "openssl.exe");
                                    if (Files != null)
                                    {
                                        Files2.AddRange(Files);
                                    }

                                    Files = GetFiles(Environment.SpecialFolder.Programs, "openssl.exe");
                                    if (Files != null)
                                    {
                                        Files2.AddRange(Files);
                                    }

                                    Files = GetFiles(Environment.SpecialFolder.System, "openssl.exe");
                                    if (Files != null)
                                    {
                                        Files2.AddRange(Files);
                                    }

                                    Files = GetFiles(Environment.SpecialFolder.SystemX86, "openssl.exe");
                                    if (Files != null)
                                    {
                                        Files2.AddRange(Files);
                                    }

                                    Files = GetFiles(Environment.SpecialFolder.Windows, "openssl.exe");
                                    if (Files != null)
                                    {
                                        Files2.AddRange(Files);
                                    }

                                    Files = GetFiles(Environment.SpecialFolder.CommonProgramFiles, "openssl.exe");
                                    if (Files != null)
                                    {
                                        Files2.AddRange(Files);
                                    }

                                    Files = GetFiles(Environment.SpecialFolder.CommonProgramFilesX86, "openssl.exe");
                                    if (Files != null)
                                    {
                                        Files2.AddRange(Files);
                                    }

                                    Files = GetFiles(Environment.SpecialFolder.CommonPrograms, "openssl.exe");
                                    if (Files != null)
                                    {
                                        Files2.AddRange(Files);
                                    }

                                    Files = GetFiles(Path.DirectorySeparatorChar + "OpenSSL-Win32", "openssl.exe");
                                    if (Files != null)
                                    {
                                        Files2.AddRange(Files);
                                    }

                                    Files = GetFiles(Path.DirectorySeparatorChar + "OpenSSL-Win64", "openssl.exe");
                                    if (Files != null)
                                    {
                                        Files2.AddRange(Files);
                                    }

                                    Files = Files2.ToArray();
                                }
                            }
                            else
                            {
                                Files = new string[] { this.openSslPath }
                            };

                            try
                            {
                                if (Files.Length == 0)
                                {
                                    ClientEvents.PushEvent(new string[] { TabID }, "CertificateError", "Unable to join certificate with private key. Try installing <a target=\"_blank\" href=\"https://wiki.openssl.org/index.php/Binaries\">OpenSSL</a> and try again.", false, "User");
                                    return(false);
                                }
                                else
                                {
                                    foreach (string OpenSslFile in Files)
                                    {
                                        if (CertFileName is null)
                                        {
                                            ClientEvents.PushEvent(new string[] { TabID }, "ShowStatus", "Generating temporary certificate file.", false, "User");

                                            StringBuilder PemOutput = new StringBuilder();
                                            byte[]        Bin       = Certificate.Export(X509ContentType.Cert);

                                            PemOutput.AppendLine("-----BEGIN CERTIFICATE-----");
                                            PemOutput.AppendLine(Convert.ToBase64String(Bin, Base64FormattingOptions.InsertLineBreaks));
                                            PemOutput.AppendLine("-----END CERTIFICATE-----");

                                            CertFileName = Path.Combine(Gateway.AppDataFolder, "Certificate.pem");
                                            File.WriteAllText(CertFileName, PemOutput.ToString(), Encoding.ASCII);

                                            ClientEvents.PushEvent(new string[] { TabID }, "ShowStatus", "Generating temporary key file.", false, "User");

                                            DerEncoder KeyOutput = new DerEncoder();
                                            SignAlg.ExportPrivateKey(KeyOutput);

                                            PemOutput.Clear();
                                            PemOutput.AppendLine("-----BEGIN RSA PRIVATE KEY-----");
                                            PemOutput.AppendLine(Convert.ToBase64String(KeyOutput.ToArray(), Base64FormattingOptions.InsertLineBreaks));
                                            PemOutput.AppendLine("-----END RSA PRIVATE KEY-----");

                                            KeyFileName = Path.Combine(Gateway.AppDataFolder, "Certificate.key");

                                            File.WriteAllText(KeyFileName, PemOutput.ToString(), Encoding.ASCII);
                                        }

                                        ClientEvents.PushEvent(new string[] { TabID }, "ShowStatus", "Converting to PFX using " + OpenSslFile, false, "User");

                                        Process P = new Process()
                                        {
                                            StartInfo = new ProcessStartInfo()
                                            {
                                                FileName               = OpenSslFile,
                                                Arguments              = "pkcs12 -nodes -export -out Certificate.pfx -inkey Certificate.key -in Certificate.pem -password pass:"******"ShowStatus", "Output: " + P.StandardOutput.ReadToEnd(), false, "User");
                                            }

                                            if (!P.StandardError.EndOfStream)
                                            {
                                                ClientEvents.PushEvent(new string[] { TabID }, "ShowStatus", "Error: " + P.StandardError.ReadToEnd(), false, "User");
                                            }

                                            continue;
                                        }

                                        ClientEvents.PushEvent(new string[] { TabID }, "ShowStatus", "Loading PFX.", false, "User");

                                        CertFileName2    = Path.Combine(Gateway.AppDataFolder, "Certificate.pfx");
                                        this.pfx         = File.ReadAllBytes(CertFileName2);
                                        this.password    = Password;
                                        this.openSslPath = OpenSslFile;

                                        ClientEvents.PushEvent(new string[] { TabID }, "ShowStatus", "PFX successfully generated using OpenSSL.", false, "User");
                                        break;
                                    }

                                    if (this.pfx is null)
                                    {
                                        this.openSslPath = string.Empty;
                                        ClientEvents.PushEvent(new string[] { TabID }, "CertificateError", "Unable to convert to PFX using OpenSSL.", false, "User");
                                        return(false);
                                    }
                                }
                            }
                            finally
                            {
                                if (CertFileName != null && File.Exists(CertFileName))
                                {
                                    ClientEvents.PushEvent(new string[] { TabID }, "ShowStatus", "Deleting temporary certificate file.", false, "User");
                                    File.Delete(CertFileName);
                                }

                                if (KeyFileName != null && File.Exists(KeyFileName))
                                {
                                    ClientEvents.PushEvent(new string[] { TabID }, "ShowStatus", "Deleting temporary key file.", false, "User");
                                    File.Delete(KeyFileName);
                                }

                                if (CertFileName2 != null && File.Exists(CertFileName2))
                                {
                                    ClientEvents.PushEvent(new string[] { TabID }, "ShowStatus", "Deleting temporary pfx file.", false, "User");
                                    File.Delete(CertFileName2);
                                }
                            }
                        }


                        if (this.Step < 2)
                        {
                            this.Step = 2;
                        }

                        this.Updated = DateTime.Now;
                        await Database.Update(this);

                        ClientEvents.PushEvent(new string[] { TabID }, "CertificateOk", string.Empty, false, "User");

                        Gateway.UpdateCertificate(this);

                        return(true);
                    }
                }
            }
            catch (Exception ex)
            {
                Log.Critical(ex);
                ClientEvents.PushEvent(new string[] { TabID }, "CertificateError", "Unable to create certificate: " + XML.HtmlValueEncode(ex.Message), false, "User");
                return(false);
            }
            finally
            {
                this.inProgress = false;
            }
        }
示例#22
0
        static void Main(string[] args)
        {
            var commandLineParseResult = Parser.Default.ParseArguments<Options>(args);
            var parsed = commandLineParseResult as Parsed<Options>;
            if (parsed == null)
            {
#if DEBUG
                Console.WriteLine("Press enter to continue.");
                Console.ReadLine();
#endif
                return; // not parsed
            }
            Options = parsed.Value;

            Console.WriteLine("Let's Encrypt (Simple Windows ACME Client)");

            BaseURI = Options.BaseURI;
            if (Options.Test)
                BaseURI = "https://acme-staging.api.letsencrypt.org/";

            //Console.Write("\nUse production Let's Encrypt server? (Y/N) ");
            //if (PromptYesNo())
            //    BaseURI = ProductionBaseURI;

            Console.WriteLine($"\nACME Server: {BaseURI}");

            settings = new Settings(clientName, BaseURI);

            configPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), clientName, CleanFileName(BaseURI));
            Console.WriteLine("Config Folder: " + configPath);
            Directory.CreateDirectory(configPath);

            try
            {
                using (var signer = new RS256Signer())
                {
                    signer.Init();

                    var signerPath = Path.Combine(configPath, "Signer");
                    if (File.Exists(signerPath))
                    {
                        Console.WriteLine($"Loading Signer from {signerPath}");
                        using (var signerStream = File.OpenRead(signerPath))
                            signer.Load(signerStream);
                    }

                    using (client = new AcmeClient(new Uri(BaseURI), new AcmeServerDirectory(), signer))
                    {
                        client.Init();
                        Console.WriteLine("\nGetting AcmeServerDirectory");
                        client.GetDirectory(true);

                        var registrationPath = Path.Combine(configPath, "Registration");
                        if (File.Exists(registrationPath))
                        {
                            Console.WriteLine($"Loading Registration from {registrationPath}");
                            using (var registrationStream = File.OpenRead(registrationPath))
                                client.Registration = AcmeRegistration.Load(registrationStream);
                        }
                        else
                        {
                            Console.WriteLine("Calling Register");
                            var registration = client.Register(new string[] { });

                            if (!Options.AcceptTOS && !Options.Renew)
                            {
                                Console.WriteLine($"Do you agree to {registration.TosLinkUri}? (Y/N) ");
                                if (!PromptYesNo())
                                    return;
                            }

                            Console.WriteLine("Updating Registration");
                            client.UpdateRegistration(true, true);

                            Console.WriteLine("Saving Registration");
                            using (var registrationStream = File.OpenWrite(registrationPath))
                                client.Registration.Save(registrationStream);

                            Console.WriteLine("Saving Signer");
                            using (var signerStream = File.OpenWrite(signerPath))
                                signer.Save(signerStream);
                        }

                        if (Options.Renew)
                        {
                            CheckRenewals();
#if DEBUG
                            Console.WriteLine("Press enter to continue.");
                            Console.ReadLine();
#endif
                            return;
                        }

                        var targets = new List<Target>();
                        foreach (var plugin in Target.Plugins.Values)
                        {
                            targets.AddRange(plugin.GetTargets());
                        }

                        if (targets.Count == 0)
                        {
                            Console.WriteLine("No targets found.");
                        }
                        else
                        {
                            var count = 1;
                            foreach (var binding in targets)
                            {
                                Console.WriteLine($" {count}: {binding}");
                                count++;
                            }
                        }

                        Console.WriteLine();
                        foreach (var plugin in Target.Plugins.Values)
                        {
                            plugin.PrintMenu();
                        }

                        Console.WriteLine(" A: Get certificates for all hosts");
                        Console.WriteLine(" Q: Quit");
                        Console.Write("Which host do you want to get a certificate for: ");
                        var response = Console.ReadLine().ToLowerInvariant();
                        switch (response)
                        {
                            case "a":
                                foreach (var target in targets)
                                {
                                    Auto(target);
                                }
                                break;
                            case "q":
                                return;
                            default:
                                var targetId = 0;
                                if (Int32.TryParse(response, out targetId))
                                {
                                    targetId--;
                                    if (targetId >= 0 && targetId < targets.Count)
                                    {
                                        var binding = targets[targetId];
                                        Auto(binding);
                                    }
                                }
                                else
                                {
                                    foreach (var plugin in Target.Plugins.Values)
                                    {
                                        plugin.HandleMenuResponse(response, targets);
                                    }
                                }
                                break;
                        }
                    }
                }
            }
            catch (Exception e)
            {
                Console.ForegroundColor = ConsoleColor.Red;
                var acmeWebException = e as AcmeClient.AcmeWebException;
                if (acmeWebException != null)
                {
                    Console.WriteLine(acmeWebException.Message);
                    Console.WriteLine("ACME Server Returned:");
                    Console.WriteLine(acmeWebException.Response.ContentAsString);
                }
                else
                {
                    Console.WriteLine(e);
                }
                Console.ResetColor();
            }

#if DEBUG
            Console.WriteLine("Press enter to continue.");
            Console.ReadLine();
#endif
        }