Example #1
0
 /// <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);
     }
 }
Example #2
0
        /// <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.");
        }
Example #3
0
 public void Challenge(IChallengeContext context)
 {
     if (PriorHandler != null)
     {
         PriorHandler.Challenge(context);
     }
 }
Example #4
0
 /// <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)));
 }
Example #7
0
        private string ComputeKeyAuthorization(IChallengeContext challenge, IKey key)
        {
            // From Certes/Acme/Challenge.cs
            var jwkThumbprintEncoded = key.Thumbprint();
            var token = challenge.Token;

            return($"{token}.{jwkThumbprintEncoded}");
        }
Example #8
0
        public async Task <string> GetDnsAuthorizationTextAsync()
        {
            var authorization = (await orderContext.Authorizations()).First();

            challengeContext = await authorization.Dns();

            return(acmeContext.AccountKey.DnsTxt(challengeContext.Token));
        }
Example #9
0
        /// <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"
                });
            }
        }
Example #10
0
        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);
            }
        }
Example #13
0
        /// <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));
        }
Example #15
0
        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);
        }
Example #16
0
        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
                });
            }
        }
Example #17
0
        /// <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.");
            }
        }
Example #19
0
        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));
            }
        }
Example #20
0
        /// <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);
            }
        }
Example #21
0
        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);
Example #24
0
        /// <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 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 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);
        }
Example #27
0
        /// <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()));
            }
        }
Example #28
0
        /// <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"
                });
            }
        }
Example #29
0
 public async Task <Challenge> ValidateChallengeAsync(IChallengeContext httpChallenge)
 {
     _logger.LogAcmeAction("ValidateChallenge", httpChallenge);
     return(await httpChallenge.Validate());
 }
        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);
            }
        }
 public void Challenge(IChallengeContext context)
 {
     throw new NotImplementedException();
 }