/// <exception cref="LetsEncryptMikroTikException"/> private async Task ChallengeAlpnAsync(IChallengeContext challengeContext, string keyAuthString) { using (var alpnChallenge = new AlpnChallenge(_config, _domainName, keyAuthString)) { await ChallengeAsync(alpnChallenge, challengeContext).ConfigureAwait(false); } }
/// <summary> /// /// </summary> /// <param name="challengeUrl"></param> /// <param name="challengeContent"></param> /// <param name="challengeFilePath"></param> public void GenerateHttpChallenge(out string challengeUrl, out string challengeContent, out string challengeFilePath) { this.OrderContext = this.AcmeContext.NewOrder(new List <string>() { this.HostName }).Result; if (this.OrderContext == null) { throw new Exception("Could not create certificate order."); } // And fetching authorizations var orderAuthz = this.OrderContext.Authorizations().Result; // Looping through authorizations foreach (IAuthorizationContext authz in orderAuthz) { // Although this is a loop, we only allow one authorization per request in our implementation, this.HttpChallenge = this.GenerateChallengeRequests(authz).Result; if (this.HttpChallenge == null) { break; } // TODO: Revisar estos valores challengeContent = this.HttpChallenge.KeyAuthz; challengeUrl = $"http://{this.HostName}/.well-known/acme-challenge/" + this.HttpChallenge.Token; challengeFilePath = ".well-known/acme-challenge/" + this.HttpChallenge.Token; return; } throw new Exception("No authorization order was created."); }
public void Challenge(IChallengeContext context) { if (PriorHandler != null) { PriorHandler.Challenge(context); } }
/// <exception cref="LetsEncryptMikroTikException"/> private async Task HttpChallengeAsync(IChallengeContext httpChallenge, string keyAuthString) { using (var challenge = new HttpChallenge(_config, keyAuthString)) { await ChallengeAsync(challenge, httpChallenge).ConfigureAwait(false); } }
/// <summary> /// 执行HTTP-01验证 /// </summary> /// <param name="authz">授权上下文</param> /// <param name="challengeTokenPath">http-01验证令牌文件存放路径</param> /// <returns></returns> public static async Task <ChallengeStatus?> HttpChallengeValidateAsync(IAuthorizationContext authz, string challengeTokenPath = "") { IChallengeContext httpChallenge = await authz.Http(); var keyAuthz = httpChallenge.KeyAuthz; File.WriteAllText(Path.Combine(challengeTokenPath, httpChallenge.Token), keyAuthz); /* * 向ACME服务器发送申请/请求,以验证域名所有权 * 注意,此处需要延迟轮询等等,具体可以参考GITHUB的几个issues * 需要延迟轮询:https://github.com/fszlin/certes/issues/194 * 如何获取当前验证状态:https://github.com/fszlin/certes/issues/89 */ Challenge challengeResult = await httpChallenge.Validate(); //等待服务器验证结果,每隔3秒轮询一次,最多轮询10次 var attempts = 10; while (attempts > 0 && (challengeResult.Status == ChallengeStatus.Pending || challengeResult.Status == ChallengeStatus.Processing)) { await Task.Delay(3000); challengeResult = await httpChallenge.Resource(); attempts--; } return(challengeResult.Status); }
protected sealed override Task <IDisposable> CreateChallengeHandler(IChallengeContext ch, string hostName, IKey accountKey) { Log.Indent(); Log.WriteVerboseLine("Key:"); Log.WriteVerboseLine(ch.KeyAuthz); Log.Unindent(); return(Task.FromResult(CreateChallengeHandler(ch.Token, ch.KeyAuthz))); }
private string ComputeKeyAuthorization(IChallengeContext challenge, IKey key) { // From Certes/Acme/Challenge.cs var jwkThumbprintEncoded = key.Thumbprint(); var token = challenge.Token; return($"{token}.{jwkThumbprintEncoded}"); }
public async Task <string> GetDnsAuthorizationTextAsync() { var authorization = (await orderContext.Authorizations()).First(); challengeContext = await authorization.Dns(); return(acmeContext.AccountKey.DnsTxt(challengeContext.Token)); }
/// <summary> /// if not already validate, ask ACME CA to check we have answered the nominated challenges correctly /// </summary> /// <param name="log"> </param> /// <param name="challengeType"> </param> /// <param name="attemptedChallenge"> </param> /// <returns> </returns> public async Task <StatusMessage> SubmitChallenge(ILog log, string challengeType, AuthorizationChallengeItem attemptedChallenge) { if (!attemptedChallenge.IsValidated) { IChallengeContext challenge = (IChallengeContext)attemptedChallenge.ChallengeData; try { Challenge result = await challenge.Validate(); int attempts = 10; while (attempts > 0 && result.Status == ChallengeStatus.Pending || result.Status == ChallengeStatus.Processing) { result = await challenge.Resource(); } if (result.Status == ChallengeStatus.Valid) { return(new StatusMessage { IsOK = true, Message = "Submitted" }); } else { var challengeError = await challenge.Resource(); return(new StatusMessage { IsOK = false, Message = challengeError.Error?.Detail }); } } catch (AcmeRequestException exp) { var msg = $"Submit Challenge failed: {exp.Error?.Detail}"; log.Error(msg); return(new StatusMessage { IsOK = false, Message = msg }); } } else { return(new StatusMessage { IsOK = true, Message = "Validated" }); } }
public async Task <StatusMessage> SubmitChallenge(string domainIdentifierId, string challengeType, AuthorizationChallengeItem attemptedChallenge) { // if not already validate, ask ACME server to validate we have answered the required // challenge correctly if (!attemptedChallenge.IsValidated) { IChallengeContext challenge = (IChallengeContext)attemptedChallenge.ChallengeData; try { var result = await challenge.Validate(); if (result.Status == ChallengeStatus.Valid || result.Status == ChallengeStatus.Pending) { return(new StatusMessage { IsOK = true, Message = "Submitted" }); } else { var challengeError = await challenge.Resource(); return(new StatusMessage { IsOK = false, Message = challengeError.ToString() }); } } catch (Exception exp) { LogAction("SubmitChallenge failed. ", exp.Message); var challengeError = await challenge.Resource(); return(new StatusMessage { IsOK = false, Message = challengeError.ToString() }); } } else { return(new StatusMessage { IsOK = true, Message = "Validated" }); } }
public virtual void Challenge(IChallengeContext context) { if (ShouldHandleScheme(context.AuthenticationSchemes)) { ChallengeContext = context; context.Accept(BaseOptions.AuthenticationScheme, BaseOptions.Description.Dictionary); } if (PriorHandler != null) { PriorHandler.Challenge(context); } }
public virtual void Challenge(IChallengeContext context) { if (SecurityHelper.LookupChallenge(context.AuthenticationTypes, BaseOptions.AuthenticationType, BaseOptions.AuthenticationMode)) { ChallengeContext = context; context.Accept(BaseOptions.AuthenticationType, BaseOptions.Description.Dictionary); } if (PriorHandler != null) { PriorHandler.Challenge(context); } }
/// <summary> /// Validates a DNS challenge. Similar to HTTP Validation, but different because of DNSChallenge value which is signed by account key /// </summary> /// <param name="dnsChallenge"></param> /// <returns></returns> private async Task <bool> ValidateDNSChallenge(String domain, IChallengeContext dnsChallenge, IDNSChallengeValidator dnsChallengeValidator) { if (dnsChallenge == null) { throw new Exception("DNS Validation mode setup, but server returned no DNS challenge."); } // We get the resource fresh var dnsChallengeStatus = await dnsChallenge.Resource(); // If it's invalid, we stop right away. Should not happen, but anyway... if (dnsChallengeStatus.Status == ChallengeStatus.Invalid) { throw new Exception("DNS challenge has an invalid status"); } // Let's prepare for ACME-DNS validation var dnsValue = _acme.AccountKey.DnsTxt(dnsChallenge.Token); var dnsKey = $"_acme-challenge.{domain}".Replace("*.", ""); if (!dnsChallengeValidator.PrepareChallengeForValidation(dnsKey, dnsValue)) { return(false); } // We sleep 5 seconds gefore first check, in order to leave the time to DNS to propagate System.Threading.Thread.Sleep(5000); // Now let's ping the ACME service to validate the challenge token Challenge challengeRes = await dnsChallenge.Validate(); // We need to loop, because ACME service might need some time to validate the challenge token int retry = 0; while (((challengeRes.Status == ChallengeStatus.Pending) || (challengeRes.Status == ChallengeStatus.Processing)) && (retry < 10)) { // We sleep 2 seconds between each request, to leave time to ACME service to refresh System.Threading.Thread.Sleep(2000); // We refresh the challenge object from ACME service challengeRes = await dnsChallenge.Resource(); retry++; } // If challenge is Invalid, Pending or Processing, something went wrong... if (challengeRes.Status != ChallengeStatus.Valid) { return(false); } return(true); }
protected override async Task <IDisposable> CreateChallengeHandler(IChallengeContext ch, string hostName, IKey accountKey) { var cnameQuery = await lookupClient.QueryAsync($"_acme-challenge.{hostName}", QueryType.CNAME).ConfigureAwait(true); var cnameRecord = cnameQuery.Answers.CnameRecords().Single(); var fullName = cnameRecord.CanonicalName.Value.TrimEnd('.'); Log.WriteVerboseLine("DNS CNAME target:"); Log.WriteVerboseLine(fullName); var txt = accountKey.DnsTxt(ch.Token); Log.WriteVerboseLine("DNS value:"); Log.WriteVerboseLine(txt); return(new TxtRecord(this, fullName, txt)); }
private string ComputeDnsValue(IChallengeContext challenge, IKey key) { // From Certes/Acme/Challenge.cs var keyAuthString = ComputeKeyAuthorization(challenge, key); var keyAuthBytes = Encoding.UTF8.GetBytes(keyAuthString); var sha256 = new Sha256Digest(); var hashed = new byte[sha256.GetDigestSize()]; sha256.BlockUpdate(keyAuthBytes, 0, keyAuthBytes.Length); sha256.DoFinal(hashed, 0); var dnsValue = JwsConvert.ToBase64String(hashed); return(dnsValue); }
async Task <IChalageStatus> ValidateChalage(IChallengeContext challengeCtx) { // Now let's ping the ACME service to validate the challenge token try { Challenge challenge = await challengeCtx.Validate( ); if (challenge.Status == ChallengeStatus.Invalid) { _logger.Write("Error occured while validating acme challenge to {0} :: error==>{1}", domain.ZoneName, challenge.Error.Detail); return(new ChalageStatus { status = false, errorDescription = challenge.Error.Detail }); } // We need to loop, because ACME service might need some time to validate the challenge token int retry = 0; while (((challenge.Status == ChallengeStatus.Pending) || (challenge.Status == ChallengeStatus.Processing)) && (retry < 30)) { // We sleep 2 seconds between each request, to leave time to ACME service to refresh Thread.Sleep(2000); // We refresh the challenge object from ACME service challenge = await challengeCtx.Resource( ); retry++; } if (challenge.Status == ChallengeStatus.Invalid) { _logger.Write("Error occured while validating acme challenge to {0} :: error==>{1}", domain.ZoneName, challenge.Error.Detail); return(new ChalageStatus { status = false, errorDescription = challenge.Error.Detail }); } return(new ChalageStatus { status = true }); } catch (Exception e) { _logger.Write("Error occured while validating acme challenge to {0} :: error==>{1}", domain.ZoneName, e.Message); _logger.Write(e.StackTrace); return(new ChalageStatus { status = false, errorDescription = e.Message }); } }
/// <summary> /// Small method that validates one challenge using the specified validator /// </summary> /// <param name="httpChallenge"></param> /// <param name="challengeValidator"></param> /// <returns>true if validated, false otherwise</returns> private async Task <bool> ValidateChallenge(IChallengeContext httpChallenge, IHTTPChallengeValidator challengeValidator) { // We get the resource fresh var httpChallengeStatus = await httpChallenge.Resource(); // If it's invalid, we stop right away. Should not happen, but anyway... if (httpChallengeStatus.Status == ChallengeStatus.Invalid) { throw new Exception("HTTP challenge has an invalid status"); } // Else we start the challenge validation if (!challengeValidator.PrepareChallengeForValidation(httpChallenge.Token, httpChallenge.KeyAuthz)) { return(false); } // Now let's ping the ACME service to validate the challenge token Challenge challengeRes = await httpChallenge.Validate(); // We need to loop, because ACME service might need some time to validate the challenge token int retry = 0; while (((challengeRes.Status == ChallengeStatus.Pending) || (challengeRes.Status == ChallengeStatus.Processing)) && (retry < 10)) { // We sleep 2 seconds between each request, to leave time to ACME service to refresh System.Threading.Thread.Sleep(2000); // We refresh the challenge object from ACME service challengeRes = await httpChallenge.Resource(); retry++; } // Finally we cleanup everything that was needed for validation challengeValidator.CleanupChallengeAfterValidation(httpChallenge.Token); // If challenge is Invalid, Pending or Processing, something went wrong... if (challengeRes.Status != ChallengeStatus.Valid) { return(false); } return(true); }
public static async Task WaitForCompletion(this IChallengeContext challengeContext, TimeSpan pollInterval, ILogger logger = null) { // Get the challenges ressource to check if it's valid var challenge = await challengeContext.Resource(); while (!challenge.HasFinished()) { // If nor finished processing, poll every second challenge = await challengeContext.Resource(); await Task.Delay(pollInterval); } logger?.LogDebug("Http challenge finished with status {status}", challenge.Status?.ToString() ?? "[null]"); if (challenge.Status == ChallengeStatus.Invalid) { // Throw if invalid new AcmeException(challenge.Error?.Detail ?? "ACME http challenge not successful."); } }
private async Task Validate(IAuthorizationContext authorizationContext) { IChallengeContext httpChallenge = await authorizationContext.Http(); httpChallengeStore.AddChallengeResponse(httpChallenge.Token, httpChallenge.KeyAuthz); await httpChallenge.Validate(); for (int i = 0; i < 10; i++) { Authorization authorization = await authorizationContext.Resource(); if (authorization.Status is AuthorizationStatus.Valid) { return; } await Task.Delay(TimeSpan.FromMilliseconds(2)); } }
/// <summary> /// 创建订单 /// </summary> /// <param name="domain"></param> /// <param name="email"></param> /// <returns></returns> public async Task <bool> CreateOrder(string domain) { //todo:验证domain和email this.Domain = domain; try { this.order = await acme.NewOrder(new[] { Domain }); var authz = (await this.order.Authorizations()).First(); this.dnsChallenge = await authz.Dns(); this.ChallengeDomain = $"_acme-challenge.{this.FriendllyDomain}"; this.ChallengRecordValue = acme.AccountKey.DnsTxt(this.dnsChallenge.Token); return(true); } catch { return(false); } }
public async Task <IOrderResult> CreateOrRenewCert(int rec = 0, bool forceRenew = false) { try { { var(status, result) = IsValidConfig( ); if (!status) { _logger.Write(result); return(new OrderResult { success = false, taskType = TaskType.NOTHING, errorDescription = result }); } } _logger.Write("Create or Renew Certificate for {0}", domain.ZoneName); ICertificate oldCertificate = GetCertificate( ); if (oldCertificate.status) { if (!oldCertificate.isExpired && forceRenew == false) { _logger.Write("Certificate not expired for {0}", domain.ZoneName); return(new OrderResult { success = true, taskType = TaskType.INSTALL_CERT, oldCertificate = oldCertificate.Cert }); } } await this.PrepareContext( ); IOrderResult orderResult = new OrderResult { success = true }; if (oldCertificate.status) { _logger.Write("Revoke Certificate for {0}", domain.ZoneName); try { await _acmeCtx.Ctx.RevokeCertificate(oldCertificate.Cert.RawData, RevocationReason.Unspecified); } catch (Exception r) { _logger.Write("Error occured while Revoke Certificate for {0}; Error=>{1}", domain.ZoneName, r.Message); _logger.Write(r.StackTrace); if (r.Message.IndexOf("alreadyRevoked") <= -1) { return(new OrderResult { taskType = TaskType.NOTHING, errorDescription = r.Message, success = false }); } } } if (domain.IsWildcard) { _orderContext = await _acmeCtx.Ctx.NewOrder(new List <string> { domain.ZoneName, domain.DomainName }); } else { _orderContext = await _acmeCtx.Ctx.NewOrder(new List <string> { domain.DomainName }); } List <IChallengeCtx> challengeCtxs = new List <IChallengeCtx>( ); List <IDnsTxtStore> dnsTxt = this.GetDnsTXT( ); if (domain.IsWildcard) { if (dnsTxt == null) { dnsTxt = new List <IDnsTxtStore>( ); } List <IDnsTxtStore> writeDnsTxt = new List <IDnsTxtStore>( ); _logger.Write("Defined acme challenge type DNS for Wildcard(*) {0}", domain.ZoneName); _logger.Write("Get Authorization Context for {0}", domain.ZoneName); IEnumerable <IAuthorizationContext> authCtx = await _orderContext.Authorizations( ); _logger.Write("Authorization Context found for {0}", domain.ZoneName); foreach (IAuthorizationContext authz in authCtx) { IChallengeContext challengeCtx = await authz.Dns( ); string txt = _acmeCtx.Ctx.AccountKey.DnsTxt(challengeCtx.Token); IDnsTxtStore dnsTxtStore = dnsTxt.FirstOrDefault(a => a.content == txt); if (dnsTxtStore != null) { challengeCtxs.Add(new ChallengeCtx { ctx = challengeCtx, txtName = dnsTxtStore.name }); dnsTxt.Remove(dnsTxtStore); continue; } ICFAPIResponse cFAPIResponse = await _cfDns.AddRecord(new QueryConfig { DOMAIN_NAME = domain.ZoneName, RECORD_TYPE = CFRecordType.TXT, RECORD_NAME = "_acme-challenge", RECORD_CONTENT = txt, NAME = string.Concat("_acme-challenge.", domain.ZoneName) }); if (!cFAPIResponse.success) { orderResult.success = false; orderResult.errorDescription = JsonConvert.SerializeObject(cFAPIResponse.messages, _cfDns.jsonSettings); break; } IChallengeCtx cCtx = new ChallengeCtx { ctx = challengeCtx }; dnsTxtStore = new DnsTxtStore { content = txt }; string txtName = string.Empty; if (cFAPIResponse.result is JObject) { JObject jObject = ( JObject )cFAPIResponse.result; dnsTxtStore.id = GetJPropertyValue(jObject.Property("id")); txtName = GetJPropertyValue(jObject.Property("name")); } else { txtName = string.Concat("_acme-challenge.", domain.ZoneName); } dnsTxtStore.name = txtName; writeDnsTxt.Add(dnsTxtStore); cCtx.txtName = txtName; challengeCtxs.Add(cCtx); } if (orderResult.success == false) { return(orderResult); } ; if (writeDnsTxt.Count > 0) { this.WriteDnsTXT(writeDnsTxt); writeDnsTxt.Clear( ); } } else { throw new NotImplementedException("Not Implemented!!!"); } foreach (IChallengeCtx cCtx in challengeCtxs) { _logger.Write("Validating acme-challenge => {0} for Domain {1}", cCtx.txtName, domain.ZoneName); IChalageStatus chalageStatus = await this.ValidateChalage(cCtx.ctx); if (chalageStatus.status == false) { orderResult.success = false; orderResult.errorDescription = chalageStatus.errorDescription; break; } } if (domain.IsWildcard) { foreach (IDnsTxtStore txt in dnsTxt) { await _cfDns.RemoveRecord(new QueryConfig { DOMAIN_NAME = domain.ZoneName, RECORD_TYPE = CFRecordType.TXT, RECORD_NAME = "_acme-challenge", RECORD_CONTENT = txt.content, NAME = txt.name, RECORD_ID = txt.id }); } } if (!orderResult.success) { _logger.Write("Error occured while creating order request for {0} . Error=>{1}", domain.ZoneName, orderResult.errorDescription); return(orderResult); } orderResult.taskType = TaskType.DOWNLOAD_CERT; orderResult.oldCertificate = oldCertificate.Cert; return(orderResult); } catch (Exception e) { if (rec >= MAX_TRY) { _logger.Write("Error occured while creating order request for {0} . Error=>{1}", domain.ZoneName, e.Message); _logger.Write(e.StackTrace); return(new OrderResult { taskType = TaskType.NOTHING, errorDescription = e.Message, success = false }); } return(await CreateOrRenewCert(rec ++)); } }
protected abstract Task <IDisposable> CreateChallengeHandler(IChallengeContext ch, string hostName, IKey accountKey);
private record DnsChallenge(IChallengeContext Challenge, string FullDnsName, string RecordName);
/// <exception cref="LetsEncryptMikroTikException"/> private async Task ChallengeAsync(IChallenge challenge, IChallengeContext httpChallenge) { // Запустить asp net. Log.Information("Запускаем веб-сервер."); challenge.Start(); string mtNatId = MtAddDstNatRule(dstPort: challenge.PublicPort, toPorts: challenge.ListenPort); string mtFilterId = MtAllowPortFilter(dstPort: challenge.ListenPort, publicPort: challenge.PublicPort); string mtMangleId = MtAllowMangleRule(dstPort: challenge.ListenPort); // Правило в микротике начинает работать не мгновенно. await Task.Delay(2000).ConfigureAwait(false); //Challenge status; try { // Ask the ACME server to validate our domain ownership. Log.Information("Информируем Let's Encrypt что мы готовы пройти валидацию."); Challenge?status = await httpChallenge.Validate().ConfigureAwait(false); bool waitWithSem = true; while (status.Status == ChallengeStatus.Pending) { if (waitWithSem) { Log.Information("Ожидаем 20 сек. входящий HTTP запрос."); Task t = await Task.WhenAny(Task.Delay(20_000), challenge.Completion).ConfigureAwait(false); if (t == challenge.Completion) { waitWithSem = false; Log.Information("Успешно выполнили входящий запрос. Ждём 15 сек. перед запросом сертификата."); await Task.Delay(15_000).ConfigureAwait(false); } else // Таймаут. { waitWithSem = false; Log.Information("Запрос ещё не поступил. Дополнительно ожидаем ещё 5 сек."); await Task.Delay(5000).ConfigureAwait(false); } } else { Log.Information("Заказ всё ещё в статусе Pending. Делаем дополнительную паузу на 5 сек."); await Task.Delay(5_000).ConfigureAwait(false); } Log.Information("Запрашиваем статус нашего заказа."); status = await httpChallenge.Resource().ConfigureAwait(false); } if (status.Status == ChallengeStatus.Valid) { Log.Information("Статус заказа: Valid. Загружаем сертификат."); } else { Log.Error($"Статус заказа: {status.Status}"); throw new LetsEncryptMikroTikException($"Статус заказа: {status.Status}. Ошибка: {status.Error.Detail}"); } } finally { // Возвращаем NAT ClosePort(mtFilterId); RemoveNatRule(mtNatId); RemoveMangleRule(mtMangleId); } }
public static void GetFirstCert(bool staging = true) { ContextAccountBundle bundle; if (staging) { bundle = GetStagingParameters(); } else { bundle = GetNonStagingParameters(); } var account = bundle.Account; var ctx = bundle.Ctx; IOrderListContext orders = account.Orders().Result; List <IOrderContext> orderContexts = new List <IOrderContext>(orders.Orders().Result); IOrderContext currentOrder = null; if (orderContexts.Count == 0) { currentOrder = ctx.NewOrder(new[] { "jcf-ai.com" }).Result; } else { foreach (IOrderContext orderCtx in orderContexts) { if (orderCtx.Resource().Result.Status != OrderStatus.Valid) { currentOrder = orderCtx; break; } } if (currentOrder == null) { currentOrder = ctx.NewOrder(new[] { "jcf-ai.com" }).Result; } } Order order = currentOrder.Resource().Result; var authorizationList = currentOrder.Authorizations().Result; IAuthorizationContext currentAuthContext; IEnumerator <IAuthorizationContext> enumerator = authorizationList.GetEnumerator(); while (enumerator.Current == null) { enumerator.MoveNext(); } currentAuthContext = enumerator.Current; Authorization authResource = currentAuthContext.Resource().Result; IChallengeContext httpContext = currentAuthContext.Http().Result; if (httpContext.Resource().Result.Status != ChallengeStatus.Valid) { if (!System.IO.Directory.Exists("tokens")) { System.IO.Directory.CreateDirectory("tokens"); } StreamWriter writer = new StreamWriter("tokens/" + httpContext.Token); writer.Write(httpContext.KeyAuthz); writer.Close(); Challenge httpChallenge = httpContext.Validate().Result; } //Everything should be good to go on the whole validate everything front. IKey privateKey = KeyFactory.NewKey(KeyAlgorithm.ES384); var cert = currentOrder.Generate( new CsrInfo() { CountryName = "US", State = "Washington", Locality = "Tacoma", CommonName = "jcf-ai.com" }, privateKey).Result; byte[] pfx = cert.ToPfx(privateKey).Build("jcf-ai.com", "pass"); FileStream pfxOutStream = new FileStream("jcf-ai.pfx", FileMode.Create, FileAccess.Write); pfxOutStream.Write(pfx, 0, pfx.Length); pfxOutStream.Close(); ProcessStartInfo psi = new ProcessStartInfo("powershell", "-Command ./bindcert.ps1 jcf-ai.pfx pass"); psi.CreateNoWindow = true; Process.Start(psi); }
/// <summary> /// HTTP-01 challenge. /// </summary> /// <exception cref="LetsEncryptMikroTikException"/> public async Task <LetsEncryptCert> GetCertAsync(string?accountPemKey = null) { AcmeContext acme; IAccountContext account; //Uri knownServer = _letsEncryptAddress; if (accountPemKey != null) { // Load the saved account key var accountKey = KeyFactory.FromPem(accountPemKey); acme = new AcmeContext(_letsEncryptAddress, accountKey); Log.Information("Авторизация в Let's Encrypt."); account = await acme.Account().ConfigureAwait(false); } else { acme = new AcmeContext(_letsEncryptAddress); Log.Information("Авторизация в Let's Encrypt."); account = await acme.NewAccount(_config.Email, termsOfServiceAgreed : true).ConfigureAwait(false); // Save the account key for later use //string pemKey = acme.AccountKey.ToPem(); } Log.Information("Заказываем новый сертификат."); IOrderContext order = await acme.NewOrder(new[] { _config.DomainName }).ConfigureAwait(false); // Get the token and key authorization string. Log.Information("Получаем способы валидации заказа."); var authz = (await order.Authorizations().ConfigureAwait(false)).First(); if (_config.UseAlpn) { Log.Information("Выбираем TLS-ALPN-01 способ валидации."); IChallengeContext challenge = await authz.TlsAlpn().ConfigureAwait(false); string keyAuthz = challenge.KeyAuthz; string token = challenge.Token; await ChallengeAlpnAsync(challenge, keyAuthz).ConfigureAwait(false); } else { Log.Information("Выбираем HTTP-01 способ валидации."); IChallengeContext challenge = await authz.Http().ConfigureAwait(false); string keyAuthz = challenge.KeyAuthz; await HttpChallengeAsync(challenge, keyAuthz).ConfigureAwait(false); } Log.Information("Загружаем сертификат."); // Download the certificate once validation is done IKey privateKey = KeyFactory.NewKey(KeyAlgorithm.RS256); CertificateChain cert = await order.Generate(new CsrInfo { CommonName = _config.DomainName, CountryName = "CA", State = "Ontario", Locality = "Toronto", Organization = "Certes", OrganizationUnit = "Dev", }, privateKey) .ConfigureAwait(false); // Export full chain certification. string certPem = cert.ToPem(); string keyPem = privateKey.ToPem(); // Export PFX PfxBuilder pfxBuilder = cert.ToPfx(privateKey); byte[] pfx = pfxBuilder.Build(friendlyName: _config.DomainName, password: ""); //await acme.RevokeCertificate(pfx, RevocationReason.Superseded, privateKey); using (var cert2 = new X509Certificate2(pfx)) { return(new LetsEncryptCert(cert2.NotAfter, certPem, keyPem, cert2.GetCommonName(), cert2.GetSha2Thumbprint())); } }
/// <summary> /// if not already validate, ask ACME CA to check we have answered the nominated challenges correctly /// </summary> /// <param name="log"> </param> /// <param name="challengeType"> </param> /// <param name="attemptedChallenge"> </param> /// <returns> </returns> public async Task <StatusMessage> SubmitChallenge(ILog log, string challengeType, AuthorizationChallengeItem attemptedChallenge) { if (attemptedChallenge == null) { return(new StatusMessage { IsOK = false, Message = "Challenge could not be submitted. No matching attempted challenge." }); } if (!attemptedChallenge.IsValidated) { try { await _acme.HttpClient.ConsumeNonce(); } catch (Exception) { return(new StatusMessage { IsOK = false, Message = "Failed to resume communication with Certificate Authority API. Try again later." }); } IChallengeContext challenge = (IChallengeContext)attemptedChallenge.ChallengeData; try { var result = await challenge.Validate(); var attempts = 10; while (attempts > 0 && result.Status == ChallengeStatus.Pending || result.Status == ChallengeStatus.Processing) { result = await challenge.Resource(); await Task.Delay(500); } if (result.Status == ChallengeStatus.Valid) { return(new StatusMessage { IsOK = true, Message = "Submitted" }); } else { var challengeError = await challenge.Resource(); return(new StatusMessage { IsOK = false, Message = challengeError.Error?.Detail }); } } catch (AcmeRequestException exp) { var msg = $"Submit Challenge failed: {exp.Error?.Detail}"; log.Error(msg); return(new StatusMessage { IsOK = false, Message = msg }); } } else { return(new StatusMessage { IsOK = true, Message = "Validated" }); } }
public async Task <Challenge> ValidateChallengeAsync(IChallengeContext httpChallenge) { _logger.LogAcmeAction("ValidateChallenge", httpChallenge); return(await httpChallenge.Validate()); }
public void Challenge(IChallengeContext context) { throw new NotImplementedException(); }