private async Task <AccountDetails> LoadAccount(IJwsTool signer) { AccountDetails account = null; if (File.Exists(AccountPath)) { if (signer != null) { _log.Debug("Loading account information from {registrationPath}", AccountPath); account = JsonConvert.DeserializeObject <AccountDetails>(File.ReadAllText(AccountPath)); _client.Account = account; // Maybe we should update the account details // on every start of the program to figure out // if it hasn't been suspended or cancelled? // UpdateAccount(); } else { _log.Error("Account found but no valid signer could be loaded"); } } else { var contacts = await GetContacts(); var(_, filename, content) = await _client.GetTermsOfServiceAsync(); if (!_arguments.MainArguments.AcceptTos) { var tosPath = Path.Combine(_settings.Client.ConfigurationPath, filename); File.WriteAllBytes(tosPath, content); _input.Show($"Terms of service", tosPath); if (await _input.PromptYesNo($"Open in default application?", false)) { Process.Start(tosPath); } if (!await _input.PromptYesNo($"Do you agree with the terms?", true)) { return(null); } } account = await _client.CreateAccountAsync(contacts, termsOfServiceAgreed : true); _log.Debug("Saving registration"); var accountKey = new AccountSigner { KeyType = _client.Signer.JwsAlg, KeyExport = _client.Signer.Export(), }; AccountSigner = accountKey; File.WriteAllText(AccountPath, JsonConvert.SerializeObject(account)); } return(account); }
private async Task <AccountDetails> LoadAccount(IJwsTool signer) { AccountDetails account = null; if (File.Exists(AccountPath)) { if (signer != null) { _log.Debug("Loading account information from {registrationPath}", AccountPath); account = JsonConvert.DeserializeObject <AccountDetails>(File.ReadAllText(AccountPath)); } else { _log.Error("Account found but no valid Signer could be loaded"); } } else { var contacts = GetContacts(); var(contentType, filename, content) = await _client.GetTermsOfServiceAsync(); if (!_optionsService.MainArguments.AcceptTos) { var tosPath = Path.Combine(_settings.ConfigPath, filename); File.WriteAllBytes(tosPath, content); _input.Show($"Terms of service", tosPath); if (_input.PromptYesNo($"Open in default application?")) { Process.Start(tosPath); } if (!_input.PromptYesNo($"Do you agree with the terms?")) { return(null); } } account = await _client.CreateAccountAsync(contacts, termsOfServiceAgreed : true); _log.Debug("Saving registration"); var accountKey = new AccountSigner { KeyType = _client.Signer.JwsAlg, KeyExport = _client.Signer.Export(), }; AccountSigner = accountKey; File.WriteAllText(AccountPath, JsonConvert.SerializeObject(account)); } return(account); }
private async Task <AccountDetails?> LoadAccount(AcmeProtocolClient client, IJwsTool?signer) { AccountDetails?account = null; if (File.Exists(AccountPath)) { if (signer != null) { _log.Debug("Loading account information from {registrationPath}", AccountPath); account = JsonConvert.DeserializeObject <AccountDetails>(File.ReadAllText(AccountPath)); client.Account = account; // Maybe we should update the account details // on every start of the program to figure out // if it hasn't been suspended or cancelled? // UpdateAccount(); } else { _log.Error("Account found but no valid signer could be loaded"); } } else { var contacts = await GetContacts(); var(_, filename, content) = await client.GetTermsOfServiceAsync(); if (!string.IsNullOrEmpty(filename)) { if (!await AcceptTos(filename, content)) { return(null); } } account = await client.CreateAccountAsync(contacts, termsOfServiceAgreed : true); _log.Debug("Saving registration"); var accountKey = new AccountSigner { KeyType = client.Signer.JwsAlg, KeyExport = client.Signer.Export(), }; AccountSigner = accountKey; File.WriteAllText(AccountPath, JsonConvert.SerializeObject(account)); } return(account); }
/// <summary> /// Setup a new ACME account /// </summary> /// <param name="client"></param> /// <returns></returns> private async Task SetupAccount(AcmeProtocolClient client) { // Accept the terms of service, if defined by the server try { var(_, filename, content) = await client.GetTermsOfServiceAsync(); _log.Verbose("Terms of service downloaded"); if (!string.IsNullOrEmpty(filename)) { if (!await AcceptTos(filename, content)) { return; } } } catch (Exception ex) { _log.Error(ex, "Error getting terms of service"); } var contacts = default(string[]); var eabKid = _accountArguments.EabKeyIdentifier; var eabKey = _accountArguments.EabKey; var eabAlg = _accountArguments.EabAlgorithm ?? "HS256"; var eabFlow = client.Directory?.Meta?.ExternalAccountRequired == "true"; var zeroSslFlow = _settings.BaseUri.Host.Contains("zerossl.com"); // Warn about unneeded EAB if (!eabFlow && !string.IsNullOrWhiteSpace(eabKid)) { eabFlow = true; _input.CreateSpace(); _input.Show(null, "You have provided an external account binding key, even though " + "the server does not indicate that this is required. We will attempt to register " + "using this key anyway."); } if (zeroSslFlow) { async Task emailRegistration() { var registration = await GetContacts(allowMultiple : false, prefix : ""); var eab = await _zeroSsl.Register(registration.FirstOrDefault() ?? ""); if (eab != null) { eabKid = eab.Kid; eabKey = eab.Hmac; } else { _log.Error("Unable to retrieve EAB credentials using the provided email address"); } } async Task apiKeyRegistration() { var accessKey = await _input.ReadPassword("API access key"); var eab = await _zeroSsl.Obtain(accessKey ?? ""); if (eab != null) { eabKid = eab.Kid; eabKey = eab.Hmac; } else { _log.Error("Unable to retrieve EAB credentials using the provided API access key"); } } if (!string.IsNullOrWhiteSpace(_accountArguments.EmailAddress)) { await emailRegistration(); } else { var instruction = "ZeroSsl can be used either by setting up a new " + "account using your email address or by connecting it to your existing " + "account using the API access key or pre-generated EAB credentials, which can " + "be obtained from the Developer section of the dashboard."; _input.CreateSpace(); _input.Show(null, instruction); var chosen = await _input.ChooseFromMenu( "How would you like to create the account?", new List <Choice <Func <Task> > >() { Choice.Create(apiKeyRegistration, "API access key"), Choice.Create(emailRegistration, "Email address"), Choice.Create <Func <Task> >(() => Task.CompletedTask, "Input EAB credentials directly") }); await chosen.Invoke(); } } if (eabFlow) { if (string.IsNullOrWhiteSpace(eabKid)) { var instruction = "This ACME endpoint requires an external account. " + "You will need to provide a key identifier and a key to proceed. " + "Please refer to the providers instructions on how to obtain these."; _input.CreateSpace(); _input.Show(null, instruction); eabKid = await _input.RequestString("Key identifier"); } if (string.IsNullOrWhiteSpace(eabKey)) { eabKey = await _input.ReadPassword("Key (base64url encoded)"); } contacts = await GetContacts(runLevel : RunLevel.Unattended); } else { contacts = await GetContacts(); } var signer = _accountManager.DefaultSigner(); try { await CreateAccount(client, signer, contacts, eabAlg, eabKid, eabKey); } catch (AcmeProtocolException apex) { // Some non-ACME compliant server may not support ES256 or other // algorithms, so attempt fallback to RS256 if (apex.ProblemType == acme.ProblemType.BadSignatureAlgorithm && signer.KeyType != "RS256") { signer = _accountManager.NewSigner("RS256"); await CreateAccount(client, signer, contacts, eabAlg, eabKid, eabKey); } else { throw; } } catch (Exception ex) { _log.Error(ex, "Error creating account"); throw; } }
private async Task <AccountDetails?> LoadAccount(AcmeProtocolClient client, IJwsTool?signer) { _log.Verbose("Loading ACME account"); AccountDetails?account = null; if (File.Exists(AccountPath)) { if (signer != null) { _log.Debug("Loading account information from {registrationPath}", AccountPath); account = JsonConvert.DeserializeObject <AccountDetails>(File.ReadAllText(AccountPath)); client.Account = account; // Maybe we should update the account details // on every start of the program to figure out // if it hasn't been suspended or cancelled? // UpdateAccount(); } else { _log.Error("Account found but no valid signer could be loaded"); } } else { _log.Verbose("No account found at {path}, creating new one", AccountPath); try { var(_, filename, content) = await client.GetTermsOfServiceAsync(); _log.Verbose("Terms of service downloaded"); if (!string.IsNullOrEmpty(filename)) { if (!await AcceptTos(filename, content)) { return(null); } } } catch (Exception ex) { _log.Error(ex, "Error getting terms of service"); } var contacts = default(string[]); var externalAccount = default(ExternalAccountBinding); var kid = _accountArguments.EabKeyIdentifier; var key = _accountArguments.EabKey; var alg = _accountArguments.EabAlgorithm ?? "HS256"; var eabFlow = client.Directory?.Meta?.ExternalAccountRequired == "true"; if (eabFlow) { _input.CreateSpace(); _input.Show(null, "This ACME endpoint requires an external account. You will " + "need to provide a key identifier and a key to proceed. Please refer to the " + "providers instructions on how to obtain these."); } else if (!string.IsNullOrWhiteSpace(kid)) { eabFlow = true; _input.CreateSpace(); _input.Show(null, "You have provided external account binding key, but the server" + "claims that is not required. We will attempt to register using this key anyway."); } if (eabFlow) { if (string.IsNullOrWhiteSpace(kid)) { kid = await _input.RequestString("Key identifier"); } if (string.IsNullOrWhiteSpace(key)) { key = await _input.ReadPassword("Key (base64url encoded)"); } externalAccount = new ExternalAccountBinding( alg, JsonConvert.SerializeObject(client.Signer.ExportJwk(), Formatting.None), kid, key ?? "", client.Directory?.NewAccount ?? ""); } else { contacts = await GetContacts(); } try { account = await client.CreateAccountAsync( contacts, termsOfServiceAgreed : true, externalAccountBinding : externalAccount?.Payload() ?? null); } catch (Exception ex) { _log.Error(ex, "Error creating account"); } if (account != null) { try { _log.Debug("Saving account"); var accountKey = new AccountSigner { KeyType = client.Signer.JwsAlg, KeyExport = client.Signer.Export(), }; AccountSigner = accountKey; await File.WriteAllTextAsync(AccountPath, JsonConvert.SerializeObject(account)); } catch (Exception ex) { _log.Error(ex, "Error saving account"); account = null; } } } return(account); }
protected async Task DoTheWorkAsync(object state) { _logger.LogInformation("** DOING WORKING *****************************************"); _logger.LogInformation($"DNS Names: {string.Join(",", _options.DnsNames)}"); if (_state.Certificate != null) { var now = DateTime.Now; if (_state.Certificate.NotBefore > now && _state.Certificate.NotAfter < now) { _logger.LogInformation("Existing certificate is Good!"); return; } { _logger.LogWarning("Existing Certificate is Expired!"); } } else { _logger.LogWarning("Missing Certificate"); } var acmeUrl = new Uri(_options.CaUrl); using (var acme = new AcmeProtocolClient(acmeUrl)) { _state.ServiceDirectory = await acme.GetDirectoryAsync(); Save(_state.ServiceDirectoryFile, _state.ServiceDirectory); acme.Directory = _state.ServiceDirectory; Save(_state.TermsOfServiceFile, await acme.GetTermsOfServiceAsync()); await acme.GetNonceAsync(); if (!await ResolveAccount(acme)) { return; } if (!await ResolveOrder(acme)) { return; } if (!await ResolveChallenges(acme)) { return; } if (!await ResolveAuthorizations(acme)) { return; } if (!await ResolveCertificate(acme)) { return; } } }
private async Task <AccountDetails?> LoadAccount(AcmeProtocolClient client, IJwsTool?signer) { _log.Verbose("Loading ACME account"); AccountDetails?account = null; if (File.Exists(AccountPath)) { if (signer != null) { _log.Debug("Loading account information from {registrationPath}", AccountPath); account = JsonConvert.DeserializeObject <AccountDetails>(File.ReadAllText(AccountPath)); client.Account = account; // Maybe we should update the account details // on every start of the program to figure out // if it hasn't been suspended or cancelled? // UpdateAccount(); } else { _log.Error("Account found but no valid signer could be loaded"); } } else { _log.Verbose("No account found at {path}, creating new one", AccountPath); try { var(_, filename, content) = await client.GetTermsOfServiceAsync(); _log.Verbose("Terms of service downloaded"); if (!string.IsNullOrEmpty(filename)) { if (!await AcceptTos(filename, content)) { return(null); } } } catch (Exception ex) { _log.Error(ex, "Error getting terms of service"); } var contacts = default(string[]); var externalAccount = default(ExternalAccountBinding); var kid = _accountArguments.EabKeyIdentifier; var key = _accountArguments.EabKey; var alg = _accountArguments.EabAlgorithm ?? "HS256"; var eabFlow = client.Directory?.Meta?.ExternalAccountRequired == "true"; var zeroSslFlow = _settings.BaseUri.Host.Contains("zerossl.com"); // Warn about unneeded EAB if (!eabFlow && !string.IsNullOrWhiteSpace(kid)) { eabFlow = true; _input.CreateSpace(); _input.Show(null, "You have provided an external account binding key, even though " + "the server does not indicate that this is required. We will attempt to register " + "using this key anyway."); } if (zeroSslFlow) { async Task emailRegistration() { var registration = await GetContacts(allowMultiple : false, prefix : ""); var eab = await _zeroSsl.Register(registration.FirstOrDefault() ?? ""); if (eab != null) { kid = eab.Kid; key = eab.Hmac; } else { _log.Error("Unable to retrieve EAB credentials using the provided email address"); } } async Task apiKeyRegistration() { var accessKey = await _input.ReadPassword("API access key"); var eab = await _zeroSsl.Obtain(accessKey ?? ""); if (eab != null) { kid = eab.Kid; key = eab.Hmac; } else { _log.Error("Unable to retrieve EAB credentials using the provided API access key"); } } if (!string.IsNullOrWhiteSpace(_accountArguments.EmailAddress)) { await emailRegistration(); } else { var instruction = "ZeroSsl can be used either by setting up a new " + "account using your email address or by connecting it to your existing " + "account using the API access key or pre-generated EAB credentials, which can " + "be obtained from the Developer section of the dashboard."; _input.CreateSpace(); _input.Show(null, instruction); var chosen = await _input.ChooseFromMenu( "How would you like to create the account?", new List <Choice <Func <Task> > >() { Choice.Create((Func <Task>)apiKeyRegistration, "API access key"), Choice.Create((Func <Task>)emailRegistration, "Email address"), Choice.Create <Func <Task> >(() => Task.CompletedTask, "Input EAB credentials directly") }); await chosen.Invoke(); } } if (eabFlow) { if (string.IsNullOrWhiteSpace(kid)) { var instruction = "This ACME endpoint requires an external account. " + "You will need to provide a key identifier and a key to proceed. " + "Please refer to the providers instructions on how to obtain these."; _input.CreateSpace(); _input.Show(null, instruction); kid = await _input.RequestString("Key identifier"); } if (string.IsNullOrWhiteSpace(key)) { key = await _input.ReadPassword("Key (base64url encoded)"); } externalAccount = new ExternalAccountBinding( alg, JsonConvert.SerializeObject( client.Signer.ExportJwk(), Formatting.None), kid, key ?? "", client.Directory?.NewAccount ?? ""); } else { contacts = await GetContacts(); } try { account = await client.CreateAccountAsync( contacts, termsOfServiceAgreed : true, externalAccountBinding : externalAccount?.Payload() ?? null); } catch (Exception ex) { _log.Error(ex, "Error creating account"); } if (account != null) { try { _log.Debug("Saving account"); var accountKey = new AccountSigner { KeyType = client.Signer.JwsAlg, KeyExport = client.Signer.Export(), }; AccountSigner = accountKey; await File.WriteAllTextAsync(AccountPath, JsonConvert.SerializeObject(account)); } catch (Exception ex) { _log.Error(ex, "Error saving account"); account = null; } } } return(account); }
protected async Task DoTheWorkAsync(object state) { var cts = new CancellationTokenSource(); cts.CancelAfter(15000); try { var suggestion = await _registryClient.Registry.SuggestAsync(_gameConfiguration.PublicURL, cts.Token); if (suggestion != "localhost") { _options.DnsNames = new[] { suggestion } } ; else { if (!WarnedLocalMode) { _logger.LogWarning("registry reports we are not accessible on http TCP/80 on our public IP. If you're in development mode, this is fine."); } WarnedLocalMode = true; return; } } catch (Exception e) { _logger.LogError("** failed to get suggestion from registry: " + e); } if (_options.DnsNames == null) { return; } _logger.LogInformation("** Checking LetsEncrypt status *****************************************"); _logger.LogInformation($"DNS Names: {string.Join(",", _options.DnsNames)}"); if (_state.Certificate != null) { var now = DateTime.Now; if (_state.Certificate.NotAfter > now) { _logger.LogInformation("Existing certificate is Good!"); return; } else { _logger.LogWarning($"Existing Certificate is Expired! {_state.Certificate.NotAfter} > {now}"); } } else { _logger.LogWarning("Missing Certificate"); } try { Directory.Delete(_state.RootDir, true); } catch (Exception) { } Reinitialize(); try { var acmeUrl = new Uri(_options.CaUrl); using (var acme = new AcmeProtocolClient(acmeUrl)) { _state.ServiceDirectory = await acme.GetDirectoryAsync(); Save(_state.ServiceDirectoryFile, _state.ServiceDirectory); acme.Directory = _state.ServiceDirectory; Save(_state.TermsOfServiceFile, await acme.GetTermsOfServiceAsync()); await acme.GetNonceAsync(); if (!await ResolveAccount(acme)) { return; } if (!await ResolveOrder(acme)) { return; } if (!await ResolveChallenges(acme)) { return; } if (!await ResolveAuthorizations(acme)) { return; } if (!await ResolveCertificate(acme)) { return; } } } catch (Exception e) { _logger.LogWarning($"Exception while getting SSL Certificate: {e}"); } }