public HttpChallenge DecodeChallenge() { AuthorizeChallenge challenge = _client.DecodeChallenge(_authorizationState, AcmeProtocol.CHALLENGE_TYPE_HTTP); _authorizationState.Challenges = new[] { challenge }; return((HttpChallenge)challenge.Challenge); }
public static AuthorizationState Authorize(Target target) { List <string> identifiers = target.GetHosts(false); List <AuthorizationState> authStatus = new List <AuthorizationState>(); foreach (var dnsIdentifier in identifiers) { string answerUri; var challengeType = target.Plugin.ChallengeType; Log.Information("Authorizing identifier {dnsIdentifier} using {challengeType} challenge", dnsIdentifier, challengeType); var authzState = _client.AuthorizeIdentifier(dnsIdentifier); var challenge = _client.DecodeChallenge(authzState, challengeType); var cleanUp = challengeType == AcmeProtocol.CHALLENGE_TYPE_HTTP ? PrepareHttpChallenge(target, challenge, out answerUri) : PrepareDnsChallenge(target, challenge, out answerUri); try { Log.Debug("Submitting answer"); authzState.Challenges = new AuthorizeChallenge[] { challenge }; _client.SubmitChallengeAnswer(authzState, challengeType, true); // have to loop to wait for server to stop being pending. // TODO: put timeout/retry limit in this loop while (authzState.Status == "pending") { Log.Debug("Refreshing authorization"); Thread.Sleep(4000); // this has to be here to give ACME server a chance to think var newAuthzState = _client.RefreshIdentifierAuthorization(authzState); if (newAuthzState.Status != "pending") { authzState = newAuthzState; } } Log.Information("Authorization result: {Status}", authzState.Status); if (authzState.Status == "invalid") { target.Plugin.OnAuthorizeFail(target); } authStatus.Add(authzState); } finally { cleanUp(authzState); } } foreach (var authState in authStatus) { if (authState.Status != "valid") { return(authState); } } return(new AuthorizationState { Status = "valid" }); }
static Tuple <AuthorizeChallenge, AuthorizationState> RequestChallengeFile( AcmeClient client, string domain) { var state = client.AuthorizeIdentifier(domain); var challenge = client.DecodeChallenge(state, AcmeProtocol.CHALLENGE_TYPE_HTTP); var httpChallenge = challenge.Challenge as HttpChallenge; return(new Tuple <AuthorizeChallenge, AuthorizationState>(challenge, state)); }
public static void Authorize(AcmeClient client, IDnsProvider dnsProvider, string hostName, List <string> alternativeNames = null) { var dnsIdentifiers = new List <string>() { hostName }; if (alternativeNames != null) { dnsIdentifiers.AddRange(alternativeNames); } var authResults = new List <AuthorizationState>(); foreach (var dnsIdentifier in dnsIdentifiers) { var authzState = client.AuthorizeIdentifier(dnsIdentifier); var challenge = client.DecodeChallenge(authzState, AcmeProtocol.CHALLENGE_TYPE_DNS); var dnsChallenge = challenge.Challenge as DnsChallenge; var dnsRecordRef = AddRecordToDNS(dnsProvider, dnsChallenge); Thread.Sleep(3 * 1000); // wait for the newly created TXT record to take effect try { authzState.Challenges = new AuthorizeChallenge[] { challenge }; client.SubmitChallengeAnswer(authzState, AcmeProtocol.CHALLENGE_TYPE_DNS, true); // have to loop to wait for server to stop being pending. // todo: put timeout/retry limit in this loop while (authzState.Status == "pending") { Thread.Sleep(3 * 1000); // this has to be here to give ACME server a chance to think var newAuthzState = client.RefreshIdentifierAuthorization(authzState); if (newAuthzState.Status != "pending") { authzState = newAuthzState; } } authResults.Add(authzState); } finally { if (!string.IsNullOrEmpty(dnsRecordRef)) { dnsProvider.RemoveTxtRecord(dnsRecordRef); } } } if (authResults.Any(result => result.Status != "valid")) { throw new AuthorizationFailedException(authResults); } }
public AuthorizationState HandleVerificationChallenge(TargetApplication application, AuthorizationState authorizationState) { var challenge = _acmeClient.DecodeChallenge(authorizationState, AcmeProtocol.CHALLENGE_TYPE_HTTP); var httpChallenge = (HttpChallenge)challenge.Challenge; if (httpChallenge == null) { _logger.Error("Could not decode challenge for {hostname} using the {protocol} protocol", authorizationState.Identifier, AcmeProtocol.CHALLENGE_TYPE_HTTP); return(null); } _logger.Information("Expecting response on URL {responseUrl}", httpChallenge.FileUrl); CreateChallengeResponse(httpChallenge); CheckChallengeResponse(httpChallenge); return(_acmeClientService.RequestChallengeVerification(authorizationState, challenge)); }
public static async Task <bool> Authorize(AcmeClient client, Account account, List <Domain> ls) { if (!(account.dnspod_tokens?.Count > 0)) { log.Error("not dnspod token."); return(false); } List <DnspodApiItem> ds = new List <DnspodApiItem>(); foreach (var token in account.dnspod_tokens) { var p = await DnspodApiItem.Create(token); if (p.domains?.Length > 0) { ds.Add(p); } } if (ds.Count == 0) { log.Error("dnspod token error"); return(false); } List <DnspodApiPart> parts = new List <DnspodApiPart>(); var records = new Dictionary <int, Dnspod.Record.DnspodRecordListResultRecordItem[]>(); foreach (var d in ls) { foreach (var a in ds) { var found = a.domains.FirstOrDefault(o => o.name.Equals(d.domain, StringComparison.CurrentCultureIgnoreCase) || d.domain.EndsWith($".{o.name}", StringComparison.CurrentCultureIgnoreCase)); if (found != null) { var part = new DnspodApiPart() { api = a.api, dns_domain = found, domain = d }; if (records.ContainsKey(d.id) == false) { var rr = (await a.api.Record.List(found.id))?.records; records[found.id] = rr; part.records = rr; } else { part.records = records[found.id]; } parts.Add(part); break; } } } foreach (var part in parts) { log.Info( $"\nAuthorizing Identifier {part.domain.domain} Using Challenge Type {AcmeProtocol.CHALLENGE_TYPE_DNS}"); part.authzState = client.AuthorizeIdentifier(part.domain.domain); part.challenge = client.DecodeChallenge(part.authzState, AcmeProtocol.CHALLENGE_TYPE_DNS); part.dnsChallenge = part.challenge.Challenge as DnsChallenge; // We need to strip off any leading '/' in the path var name = part.dnsChallenge.RecordName.Substring(0, part.dnsChallenge.RecordName.Length - 1 - part.dns_domain.name.Length); var record = part.records?.FirstOrDefault(o => o.name.ToLower() == name.ToLower()); if (record == null) { var r = await part.api.Record.Create(part.dns_domain.id, name, part.dnsChallenge.RecordValue); part.record_id = r.record.id; } else { var r = await part.api.Record.Modify(part.dns_domain.id, record.id, name, part.dnsChallenge.RecordValue); part.record_id = r.record.id; } } foreach (var part in parts) { //while (DnspodApi.DnsGetTxtRecord(part.dnsChallenge.RecordName) != part.dnsChallenge.RecordValue) //{ // Thread.Sleep(10000); //} log.Info($" Answer should now be browsable at {part.dnsChallenge.RecordName}"); try { log.Info(" Submitting answer"); part.authzState.Challenges = new AuthorizeChallenge[] { part.challenge }; client.SubmitChallengeAnswer(part.authzState, AcmeProtocol.CHALLENGE_TYPE_DNS, true); // have to loop to wait for server to stop being pending. // TODO: put timeout/retry limit in this loop while (part.authzState.Status == "pending") { log.Info(" Refreshing authorization"); Thread.Sleep(4000); // this has to be here to give ACME server a chance to think var newAuthzState = client.RefreshIdentifierAuthorization(part.authzState); if (newAuthzState.Status != "pending") { part.authzState = newAuthzState; } } log.Info($" Authorization Result: {part.authzState.Status}"); part.domain.AuthorizationState = part.authzState; if (part.authzState.Status == "invalid") { log.Warn($"Authorization Failed {part.authzState.Status}"); } } finally { } } return(true); }
private bool AuthorizeBinding(Binding binding) { RetryAfterInvalidAuthorization: // If LetsEncrypt says a challenge is invalid, and the user hits Y to retry, it'll jump back up here if (_AuthorizedIdentifiers.ContainsKey(binding.Hostname)) { return(true); } else { Globals.Log(); Globals.Log($"Authorizing hostname {binding.Hostname} via {AcmeProtocol.CHALLENGE_TYPE_HTTP}"); Globals.Log(" - Decoding challenge"); var AuthState = _Client.AuthorizeIdentifier(binding.Hostname); var Challenge = _Client.DecodeChallenge(AuthState, AcmeProtocol.CHALLENGE_TYPE_HTTP); var HttpChallenge = Challenge.Challenge as ACMESharp.ACME.HttpChallenge; // Create the challenge file var AnswerPath = Environment.ExpandEnvironmentVariables(Path.Combine(binding.WebRootPath, HttpChallenge.FilePath.TrimStart('/'))); Globals.Log($" - Writing challenge answer to {AnswerPath}"); Directory.CreateDirectory(Path.GetDirectoryName(AnswerPath)); File.WriteAllText(AnswerPath, HttpChallenge.FileContent); // Create the web.config to allow extensionless file loading string WebConfigPath = Path.Combine(Path.GetDirectoryName(AnswerPath), "web.config"); Globals.Log($" - Copying extensionless-enabling web.config to {WebConfigPath}"); File.Copy(_Web_ConfigXmlPath, WebConfigPath, true); // Warmup the answer url Globals.WarmUpUrl(HttpChallenge.FileUrl, HttpChallenge.FileContent); try { int x = Console.CursorLeft; int y = Console.CursorTop; Globals.Log(" - Submitting challenge answer"); AuthState.Challenges = new AuthorizeChallenge[] { Challenge }; _Client.SubmitChallengeAnswer(AuthState, AcmeProtocol.CHALLENGE_TYPE_HTTP, true); // Give a quick 1 second delay before refreshing -- then we'll loop with a "nicer" 5 second delay if we're still pending if (AuthState.Status == "pending") { Thread.Sleep(1000); Globals.Log($" - Checking authorization status"); AuthState = _Client.RefreshIdentifierAuthorization(AuthState); } // Loop while in pending state int TryNumber = 2; // 2 because we submitted above, which counts as try #1 while (AuthState.Status == "pending") { Globals.Log(" - Authorization pending, waiting 5 seconds before trying again..."); Thread.Sleep(5000); // This prevents scrolling while retrying Console.CursorTop -= 1; Console.Write(new string(' ', Console.WindowWidth - 1)); Console.SetCursorPosition(x, y); Globals.Log($" - Refreshing authorization status (Try #{TryNumber++})"); AuthState = _Client.RefreshIdentifierAuthorization(AuthState); } Globals.Log($" - Authorization status: {AuthState.Status}"); if (AuthState.Status == "valid") { // Record the expiry date for the valid result, so we can skip authorization in the future _AuthorizedIdentifiers.Add(binding.Hostname, (DateTime)AuthState.Expires); } else if (AuthState.Status == "invalid") { // Prompt to see if we're going to fix and retry Console.WriteLine(" - LetsEncrypt Uri:"); Console.WriteLine($" {AuthState.Uri}"); Console.WriteLine(" - Check the URL above to see if it loads correctly"); Console.WriteLine(" If the site does a redirect, it may need to be disabled"); Console.WriteLine(" (The LetsEncrypt script contains 'letsencrypt.org' in the user-agent,"); Console.WriteLine(" so you could disable rewrite rules for that user-agent fragment)"); Console.WriteLine($" Hit Y to try again or N to skip creating a cert for {binding.IPAddress}"); if (Globals.PromptYesNo()) { goto RetryAfterInvalidAuthorization; // Suck it goto haters } // TODOX Add options for (1) to load challenge url and (2) for LetEncrypt info url } return(AuthState.Status == "valid"); } finally { // TODOX DeleteAuthorization? } } }
public static AuthorizationState Authorize(Target target) { List <AuthorizationState> authStatus = new List <AuthorizationState>(); var webRootPath = WebRootPath(); var directory = Path.Combine(webRootPath, ".well-known", "acme-challenge"); if (!Directory.Exists(directory)) { Directory.CreateDirectory(directory); } var webConfigPath = Path.Combine(directory, "web.config"); if (!File.Exists(webConfigPath) || File.ReadAllText(webConfigPath) != webConfig) { Trace.TraceInformation($"Writing web.config to {webConfigPath}"); File.WriteAllText(webConfigPath, webConfig); } foreach (var dnsIdentifier in target.AllDnsIdentifiers) { //var dnsIdentifier = target.Host; Console.WriteLine($"\nAuthorizing Identifier {dnsIdentifier} Using Challenge Type {AcmeProtocol.CHALLENGE_TYPE_HTTP}"); Trace.TraceInformation("Authorizing Identifier {0} Using Challenge Type {1}", dnsIdentifier, AcmeProtocol.CHALLENGE_TYPE_HTTP); var authzState = client.AuthorizeIdentifier(dnsIdentifier); var challenge = client.DecodeChallenge(authzState, AcmeProtocol.CHALLENGE_TYPE_HTTP); var httpChallenge = challenge.Challenge as HttpChallenge; // We need to strip off any leading '/' in the path var filePath = httpChallenge.FilePath; if (filePath.StartsWith("/", StringComparison.OrdinalIgnoreCase)) { filePath = filePath.Substring(1); } var answerPath = Environment.ExpandEnvironmentVariables(Path.Combine(webRootPath, filePath)); Console.WriteLine($" Writing challenge answer to {answerPath}"); Trace.TraceInformation("Writing challenge answer to {0}", answerPath); File.WriteAllText(answerPath, httpChallenge.FileContent); var answerUri = new Uri(httpChallenge.FileUrl); Console.WriteLine($" Answer should now be browsable at {answerUri}"); Trace.TraceInformation("Answer should now be browsable at {0}", answerUri); try { var retry = 10; while (true) { using (var handler = new WebRequestHandler()) { //Allow self-signed certs otherwise staging wont work handler.ServerCertificateValidationCallback = (sender, cert, chain, sslPolicyErrors) => true; using (var client = new HttpClient(handler)) { Thread.Sleep(1000); var x = client.GetAsync(answerUri).Result; Trace.TraceInformation("Checking status {0}", x.StatusCode); if (x.StatusCode == HttpStatusCode.OK) { break; } if (retry-- == 0) { break; } Trace.TraceInformation("Retrying {0}", retry); } } } Console.WriteLine(" Submitting answer"); Trace.TraceInformation("Submitting answer"); authzState.Challenges = new AuthorizeChallenge[] { challenge }; client.SubmitChallengeAnswer(authzState, AcmeProtocol.CHALLENGE_TYPE_HTTP, true); // have to loop to wait for server to stop being pending. retry = 0; while (authzState.Status == "pending" && retry < 6) { retry++; Console.WriteLine(" Refreshing authorization attempt " + retry); Trace.TraceInformation("Refreshing authorization attempt " + retry); Thread.Sleep(4000); // this has to be here to give ACME server a chance to think var newAuthzState = client.RefreshIdentifierAuthorization(authzState); if (newAuthzState.Status != "pending") { authzState = newAuthzState; } } Console.WriteLine($" Authorization Result: {authzState.Status}"); Trace.TraceInformation("Auth Result {0}", authzState.Status); if (authzState.Status == "invalid") { Trace.TraceError("Authorization Failed {0}", authzState.Status); Trace.TraceInformation("Full Error Details {0}", JsonConvert.SerializeObject(authzState)); Console.WriteLine($"The ACME server was probably unable to reach {answerUri}"); Trace.TraceError("Unable to reach {0}", answerUri); Console.WriteLine("\nCheck in a browser to see if the answer file is being served correctly."); throw new Exception($"The Lets Encrypt ACME server was probably unable to reach {answerUri} view error report from Lets Encrypt at {authzState.Uri} for more information"); } authStatus.Add(authzState); } finally { if (authzState.Status == "valid") { Console.WriteLine(" Deleting answer"); Trace.TraceInformation("Deleting answer"); File.Delete(answerPath); } } } foreach (var authState in authStatus) { if (authState.Status != "valid") { return(authState); } } return(new AuthorizationState { Status = "valid" }); }
private List <AuthorizationState> Authorize() { List <string> allDomains = new List <string> { this.mainDomain }; allDomains.AddRange(this.alternateNames); List <AuthorizationState> authStatus = new List <AuthorizationState>(); foreach (var domain in allDomains) { logger.Debug("Authorizing Identifier {0} Using Challenge Type {1}", domain, AcmeProtocol.CHALLENGE_TYPE_HTTP); var authState = client.AuthorizeIdentifier(domain); var challenge = client.DecodeChallenge(authState, AcmeProtocol.CHALLENGE_TYPE_HTTP); var httpChallenge = challenge.Challenge as HttpChallenge; // We need to strip off any leading '/' in the path var filePath = httpChallenge.FilePath; if (filePath.StartsWith("/", StringComparison.OrdinalIgnoreCase)) { filePath = filePath.Substring(1); } var answerPath = Path.Combine(this.websitePath, filePath); logger.Debug("Writing challenge answer to {0}", answerPath); var directory = Path.GetDirectoryName(answerPath); Directory.CreateDirectory(directory); File.WriteAllText(answerPath, httpChallenge.FileContent); var answerUri = new Uri(httpChallenge.FileUrl); logger.Debug("Answer should now be browsable at {0}", answerUri); logger.Debug("Submitting answer"); authState.Challenges = new AuthorizeChallenge[] { challenge }; client.SubmitChallengeAnswer(authState, AcmeProtocol.CHALLENGE_TYPE_HTTP, true); // have to loop to wait for server to stop being pending. // TODO: put timeout/retry limit in this loop int timeout = 10; while (authState.Status == "pending" && timeout > 0) { logger.Debug("Refreshing authorization"); Thread.Sleep(2000); var newAuthzState = client.RefreshIdentifierAuthorization(authState); if (newAuthzState.Status != "pending") { authState = newAuthzState; } timeout--; } if (timeout == 0) { logger.Error("Timeout waiting for {0}", domain); authStatus.Add(authState); return(authStatus); } logger.Debug("Authorization Result: {0}", authState.Status); if (authState.Status == "invalid") { logger.Error("Authorization Failed {0}", authState.Status); logger.Error("Full Error Details {0}", authState); logger.Error("The ACME server was probably unable to reach {0}", answerUri); } authStatus.Add(authState); } return(authStatus); }
public static AuthorizationState Authorize(Target target) { List <string> identifiers = target.GetHosts(false); List <AuthorizationState> authStatus = new List <AuthorizationState>(); foreach (var identifier in identifiers) { var authzState = _client.AuthorizeIdentifier(identifier); if (authzState.Status == "valid" && !_options.Test) { _log.Information("Cached authorization result: {Status}", authzState.Status); authStatus.Add(authzState); } else { var validation = target.GetValidationPlugin(); if (validation == null) { return(new AuthorizationState { Status = "invalid" }); } _log.Information("Authorizing {dnsIdentifier} using {challengeType} validation ({name})", identifier, validation.ChallengeType, validation.Name); var challenge = _client.DecodeChallenge(authzState, validation.ChallengeType); var cleanUp = validation.PrepareChallenge(target, challenge, identifier, _options, _input); try { _log.Debug("Submitting answer"); authzState.Challenges = new AuthorizeChallenge[] { challenge }; _client.SubmitChallengeAnswer(authzState, validation.ChallengeType, true); // have to loop to wait for server to stop being pending. // TODO: put timeout/retry limit in this loop while (authzState.Status == "pending") { _log.Debug("Refreshing authorization"); Thread.Sleep(4000); // this has to be here to give ACME server a chance to think var newAuthzState = _client.RefreshIdentifierAuthorization(authzState); if (newAuthzState.Status != "pending") { authzState = newAuthzState; } } _log.Information("Authorization result: {Status}", authzState.Status); authStatus.Add(authzState); } finally { cleanUp(authzState); } } } foreach (var authState in authStatus) { if (authState.Status != "valid") { return(authState); } } return(new AuthorizationState { Status = "valid" }); }
public static AuthorizationState Authorize(Target target) { List <string> identifiers = new List <string>(); if (!Options.San) { identifiers.Add(target.Host); } identifiers.AddRange(target.AlternativeNames); identifiers = identifiers.Where(x => !string.IsNullOrWhiteSpace(x)).Distinct().ToList(); if (identifiers.Count() == 0) { Log.Error("No DNS identifiers found."); throw new Exception("No DNS identifiers found."); } List <AuthorizationState> authStatus = new List <AuthorizationState>(); foreach (var dnsIdentifier in identifiers) { string answerUri; var challengeType = target.Plugin.ChallengeType; Log.Information("Authorizing Identifier {dnsIdentifier} Using Challenge Type {challengeType}", dnsIdentifier, challengeType); var authzState = _client.AuthorizeIdentifier(dnsIdentifier); var challenge = _client.DecodeChallenge(authzState, challengeType); var cleanUp = challengeType == AcmeProtocol.CHALLENGE_TYPE_HTTP ? PrepareHttpChallenge(target, challenge, out answerUri) : PrepareDnsChallenge(target, challenge, out answerUri); try { Log.Information("Submitting answer"); authzState.Challenges = new AuthorizeChallenge[] { challenge }; _client.SubmitChallengeAnswer(authzState, challengeType, true); // have to loop to wait for server to stop being pending. // TODO: put timeout/retry limit in this loop while (authzState.Status == "pending") { Log.Information("Refreshing authorization"); Thread.Sleep(4000); // this has to be here to give ACME server a chance to think var newAuthzState = _client.RefreshIdentifierAuthorization(authzState); if (newAuthzState.Status != "pending") { authzState = newAuthzState; } } Log.Information("Authorization Result: {Status}", authzState.Status); if (authzState.Status == "invalid") { Log.Error("Authorization Failed {Status}", authzState.Status); Log.Debug("Full Error Details {@authzState}", authzState); Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("\n******************************************************************************"); Log.Error("The ACME server was probably unable to reach {answerUri}", answerUri); Console.WriteLine("\nCheck in a browser to see if the answer file is being served correctly. If it is, also check the DNSSEC configuration."); target.Plugin.OnAuthorizeFail(target); Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("\n******************************************************************************"); Console.ResetColor(); } authStatus.Add(authzState); } finally { cleanUp(authzState); } } foreach (var authState in authStatus) { if (authState.Status != "valid") { return(authState); } } return(new AuthorizationState { Status = "valid" }); }
public static AuthorizationState Authorize(Target target) { List <string> dnsIdentifiers = new List <string>(); if (!Options.SAN) { dnsIdentifiers.Add(target.Host); } if (target.AlternativeNames != null) { dnsIdentifiers.AddRange(target.AlternativeNames); } List <AuthorizationState> authStatus = new List <AuthorizationState>(); foreach (var dnsIdentifier in dnsIdentifiers) { //var dnsIdentifier = target.Host; var webRootPath = target.WebRootPath; Console.WriteLine($"\nAuthorizing Identifier {dnsIdentifier} Using Challenge Type {AcmeProtocol.CHALLENGE_TYPE_HTTP}"); Log.Information("Authorizing Identifier {dnsIdentifier} Using Challenge Type {CHALLENGE_TYPE_HTTP}", dnsIdentifier, AcmeProtocol.CHALLENGE_TYPE_HTTP); var authzState = client.AuthorizeIdentifier(dnsIdentifier); var challenge = client.DecodeChallenge(authzState, AcmeProtocol.CHALLENGE_TYPE_HTTP); var httpChallenge = challenge.Challenge as HttpChallenge; // We need to strip off any leading '/' in the path var filePath = httpChallenge.FilePath; if (filePath.StartsWith("/", StringComparison.OrdinalIgnoreCase)) { filePath = filePath.Substring(1); } var answerPath = Environment.ExpandEnvironmentVariables(Path.Combine(webRootPath, filePath)); Console.WriteLine($" Writing challenge answer to {answerPath}"); Log.Information("Writing challenge answer to {answerPath}", answerPath); var directory = Path.GetDirectoryName(answerPath); Directory.CreateDirectory(directory); File.WriteAllText(answerPath, httpChallenge.FileContent); target.Plugin.BeforeAuthorize(target, answerPath); var answerUri = new Uri(httpChallenge.FileUrl); Console.WriteLine($" Answer should now be browsable at {answerUri}"); Log.Information("Answer should now be browsable at {answerUri}", answerUri); try { Console.WriteLine(" Submitting answer"); Log.Information("Submitting answer"); authzState.Challenges = new AuthorizeChallenge[] { challenge }; client.SubmitChallengeAnswer(authzState, AcmeProtocol.CHALLENGE_TYPE_HTTP, true); // have to loop to wait for server to stop being pending. // TODO: put timeout/retry limit in this loop while (authzState.Status == "pending") { Console.WriteLine(" Refreshing authorization"); Log.Information("Refreshing authorization"); Thread.Sleep(4000); // this has to be here to give ACME server a chance to think var newAuthzState = client.RefreshIdentifierAuthorization(authzState); if (newAuthzState.Status != "pending") { authzState = newAuthzState; } } Console.WriteLine($" Authorization Result: {authzState.Status}"); Log.Information("Auth Result {Status}", authzState.Status); if (authzState.Status == "invalid") { Log.Error("Authorization Failed {Status}", authzState.Status); Log.Debug("Full Error Details {@authzState}", authzState); Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("\n******************************************************************************"); Console.WriteLine($"The ACME server was probably unable to reach {answerUri}"); Log.Error("Unable to reach {answerUri}", answerUri); Console.WriteLine("\nCheck in a browser to see if the answer file is being served correctly."); target.Plugin.OnAuthorizeFail(target); Console.WriteLine("\n******************************************************************************"); Console.ResetColor(); } authStatus.Add(authzState); } finally { if (authzState.Status == "valid") { Console.WriteLine(" Deleting answer"); Log.Information("Deleting answer"); File.Delete(answerPath); } } } foreach (var authState in authStatus) { if (authState.Status != "valid") { return(authState); } } return(new AuthorizationState { Status = "valid" }); }
public async Task <AuthorizationState> Authorize(AcmeClient client, List <string> allDnsIdentifiers) { List <AuthorizationState> authStatus = new List <AuthorizationState>(); foreach (var dnsIdentifier in allDnsIdentifiers) { //var dnsIdentifier = target.Host; Console.WriteLine($"\nAuthorizing Identifier {dnsIdentifier} Using Challenge Type {AcmeProtocol.CHALLENGE_TYPE_DNS}"); Trace.TraceInformation("Authorizing Identifier {0} Using Challenge Type {1}", dnsIdentifier, AcmeProtocol.CHALLENGE_TYPE_DNS); var authzState = client.AuthorizeIdentifier(dnsIdentifier); var challenge = client.DecodeChallenge(authzState, AcmeProtocol.CHALLENGE_TYPE_DNS); var dnsChallenge = challenge.Challenge as DnsChallenge; await PersistsChallenge(dnsChallenge); var answerUri = new Uri(dnsChallenge.RecordValue); Console.WriteLine($" DNS Answer should now be available as {dnsChallenge.RecordName} with value {answerUri}"); Trace.TraceInformation($" DNS Answer should now be available as {dnsChallenge.RecordName} with value {answerUri}"); try { var retry = 10; //var handler = new WebRequestHandler(); //handler.ServerCertificateValidationCallback = (sender, cert, chain, sslPolicyErrors) => true; //var httpclient = new HttpClient(handler); //while (true) //{ // //Allow self-signed certs otherwise staging wont work // await Task.Delay(1000); // var x = await httpclient.GetAsync(answerUri); // Trace.TraceInformation("Checking status {0}", x.StatusCode); // if (x.StatusCode == HttpStatusCode.OK) // break; // if (retry-- == 0) // break; // Trace.TraceInformation("Retrying {0}", retry); //} Console.WriteLine(" Submitting answer"); Trace.TraceInformation("Submitting answer"); authzState.Challenges = new AuthorizeChallenge[] { challenge }; client.SubmitChallengeAnswer(authzState, AcmeProtocol.CHALLENGE_TYPE_DNS, true); // have to loop to wait for server to stop being pending. retry = 0; while (authzState.Status == "pending" && retry < 6) { retry++; Console.WriteLine(" Refreshing authorization attempt " + retry); Trace.TraceInformation("Refreshing authorization attempt " + retry); await Task.Delay(2000 *retry); // this has to be here to give ACME server a chance to think var newAuthzState = client.RefreshIdentifierAuthorization(authzState); if (newAuthzState.Status != "pending") { authzState = newAuthzState; } } Console.WriteLine($" Authorization Result: {authzState.Status}"); Trace.TraceInformation("Auth Result {0}", authzState.Status); if (authzState.Status == "invalid" || authzState.Status == "pending") { Trace.TraceError("Authorization Failed {0}", authzState.Status); Trace.TraceInformation("Full Error Details {0}", JsonConvert.SerializeObject(authzState)); Console.WriteLine($"The ACME server was probably unable to reach {answerUri}"); Trace.TraceError("Unable to reach {0}", answerUri); Console.WriteLine("\nCheck in a browser to see if the answer file is being served correctly."); throw new Exception($"The Lets Encrypt ACME server was probably unable to reach {answerUri} view error report from Lets Encrypt at {authzState.Uri} for more information"); } authStatus.Add(authzState); } finally { if (authzState.Status == "valid") { Console.WriteLine(" Deleting answer"); Trace.TraceInformation("Deleting answer"); await CleanupChallenge(dnsChallenge); } } } foreach (var authState in authStatus) { if (authState.Status != "valid") { return(authState); } } return(new AuthorizationState { Status = "valid" }); }
public async Task <AuthorizationState> Authorize(AcmeClient client, List <string> allDnsIdentifiers) { List <AuthorizationState> authStatus = new List <AuthorizationState>(); foreach (var dnsIdentifier in allDnsIdentifiers) { Trace.TraceInformation("Authorizing Identifier {0} Using Challenge Type {1}", dnsIdentifier, AcmeProtocol.CHALLENGE_TYPE_DNS); var authzState = client.AuthorizeIdentifier(dnsIdentifier); var challenge = client.DecodeChallenge(authzState, AcmeProtocol.CHALLENGE_TYPE_DNS); var dnsChallenge = challenge.Challenge as DnsChallenge; await PersistsChallenge(dnsChallenge); Trace.TraceInformation($" DNS Answer should now be available as {dnsChallenge.RecordName} with value {dnsChallenge.RecordValue}"); try { var retry = 10; Trace.TraceInformation("Submitting answer"); authzState.Challenges = new AuthorizeChallenge[] { challenge }; client.SubmitChallengeAnswer(authzState, AcmeProtocol.CHALLENGE_TYPE_DNS, true); // have to loop to wait for server to stop being pending. retry = 0; while (authzState.Status == "pending" && retry < 6) { retry++; Trace.TraceInformation("Refreshing authorization attempt " + retry); await Task.Delay(2000 *retry); // this has to be here to give ACME server a chance to think var newAuthzState = client.RefreshIdentifierAuthorization(authzState); if (newAuthzState.Status != "pending") { authzState = newAuthzState; } } Trace.TraceInformation("Auth Result {0}", authzState.Status); if (authzState.Status == "invalid" || authzState.Status == "pending") { Trace.TraceError("Authorization Failed {0}", authzState.Status); Trace.TraceInformation("Full Error Details {0}", JsonConvert.SerializeObject(authzState)); Trace.TraceError("Unable to find TXT record {0}", dnsChallenge.RecordValue); throw new Exception($"The Lets Encrypt ACME server was probably unable to find TXT record with value {dnsChallenge.RecordValue} view error report from Lets Encrypt at {authzState.Uri} for more information"); } authStatus.Add(authzState); } finally { if (authzState.Status == "valid") { Console.WriteLine(" Deleting answer"); Trace.TraceInformation("Deleting answer"); await CleanupChallenge(dnsChallenge); } } } foreach (var authState in authStatus) { if (authState.Status != "valid") { return(authState); } } return(new AuthorizationState { Status = "valid" }); }
public static AuthorizationState Authorize(Target target) { var dnsIdentifier = target.Host; var webRootPath = target.WebRootPath; Console.WriteLine($"\nAuthorizing Identifier {dnsIdentifier} Using Challenge Type {AcmeProtocol.CHALLENGE_TYPE_HTTP}"); var authzState = client.AuthorizeIdentifier(dnsIdentifier); var challenge = client.DecodeChallenge(authzState, AcmeProtocol.CHALLENGE_TYPE_HTTP); var httpChallenge = challenge.Challenge as HttpChallenge; // We need to strip off any leading '/' in the path var filePath = httpChallenge.FilePath; if (filePath.StartsWith("/", StringComparison.OrdinalIgnoreCase)) { filePath = filePath.Substring(1); } var answerPath = Environment.ExpandEnvironmentVariables(Path.Combine(webRootPath, filePath)); Console.WriteLine($" Writing challenge answer to {answerPath}"); var directory = Path.GetDirectoryName(answerPath); Directory.CreateDirectory(directory); File.WriteAllText(answerPath, httpChallenge.FileContent); target.Plugin.BeforeAuthorize(target, answerPath); var answerUri = new Uri(httpChallenge.FileUrl); Console.WriteLine($" Answer should now be browsable at {answerUri}"); try { Console.WriteLine(" Submitting answer"); authzState.Challenges = new AuthorizeChallenge[] { challenge }; client.SubmitChallengeAnswer(authzState, AcmeProtocol.CHALLENGE_TYPE_HTTP, true); // have to loop to wait for server to stop being pending. // TODO: put timeout/retry limit in this loop while (authzState.Status == "pending") { Console.WriteLine(" Refreshing authorization"); Thread.Sleep(4000); // this has to be here to give ACME server a chance to think var newAuthzState = client.RefreshIdentifierAuthorization(authzState); if (newAuthzState.Status != "pending") { authzState = newAuthzState; } } Console.WriteLine($" Authorization Result: {authzState.Status}"); if (authzState.Status == "invalid") { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("\n******************************************************************************"); Console.WriteLine($"The ACME server was probably unable to reach {answerUri}"); Console.WriteLine("\nCheck in a browser to see if the answer file is being served correctly."); target.Plugin.OnAuthorizeFail(target); Console.WriteLine("\n******************************************************************************"); Console.ResetColor(); } //if (authzState.Status == "valid") //{ // var authPath = Path.Combine(configPath, dnsIdentifier + ".auth"); // Console.WriteLine($" Saving authorization record to: {authPath}"); // using (var authStream = File.Create(authPath)) // authzState.Save(authStream); //} return(authzState); } finally { if (authzState.Status == "valid") { Console.WriteLine(" Deleting answer"); File.Delete(answerPath); } } }