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); } } }
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); }
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); } }
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); } }
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); }
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); }
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(); }
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; } }
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); } } } } } }
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); }
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 }
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(); }
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; } }
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; } }
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 }