Example #1
0
        private async Task PrepareHttpChallengeResponseAsync(
            IAuthorizationContext authorizationContext,
            string domainName,
            CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
            if (_client == null)
            {
                throw new InvalidOperationException();
            }

            var httpChallenge = await _client.CreateChallengeAsync(authorizationContext, ChallengeTypes.Http01);

            if (httpChallenge == null)
            {
                throw new InvalidOperationException(
                          $"Did not receive challenge information for challenge type {ChallengeTypes.Http01}");
            }

            var keyAuth = httpChallenge.KeyAuthz;

            _challengeStore.AddChallengeResponse(httpChallenge.Token, keyAuth);

            _logger.LogTrace("Waiting for server to start accepting HTTP requests");
            await _appStarted.Task;

            _logger.LogTrace("Requesting server to validate HTTP challenge");
            await _client.ValidateChallengeAsync(httpChallenge);
        }
        private async void RefreshCertificates(object state)
        {
            _httpChallengeResponseStore.ClearPendingOrders();

            var hostNames = _certificateSelector.GetCertificatesAboutToExpire();

            if (hostNames.Length > 0)
            {
                var account = await _accountManager.GetAccountKey();

                if (string.IsNullOrEmpty(account))
                {
                    _logger.LogError("Canot get Let´s Encrpyt account");
                }
                else
                {
                    try
                    {
                        foreach (var host in hostNames)
                        {
                            var acme = new AcmeContext(_options.AcmeServer, KeyFactory.FromPem(account));

                            _logger.LogInformation("LetsEncrypt: Creating order");

                            var order = await acme.NewOrder(new[] { host });

                            var authz         = (await order.Authorizations()).First();
                            var httpChallenge = await authz.Http();

                            var orderInfo = new OrderInfo
                            {
                                Order     = order,
                                Challenge = httpChallenge,
                                HostName  = host
                            };

                            _httpChallengeResponseStore.AddChallengeResponse(httpChallenge.Token, orderInfo);
                            await httpChallenge.Validate();
                        }
                    }
                    catch (Exception ex)
                    {
                        _logger.LogError(ex.ToString());
                        throw;
                    }
                }
            }
        }
Example #3
0
        protected override async Task ExecuteAsync(CancellationToken cancellationToken)
        {
            try {
                var directoryUri = new Uri(_options.Authority.DirectoryUri);
                var(acme, account) = await InitializeAccount(directoryUri, _options.Email, _options.AccountKey);

                // Todo: Save the account key for later use
                _logger?.LogWarning("Account key is not yet persisted for later use.");
                _options.AccountKey = acme.AccountKey.ToPem();

                _logger?.LogDebug("Create new ACME order with http challenge for hostname {hostname}", _options.Hostname);
                var order = await acme.NewOrder(new[] { _options.Hostname });

                var authorization = (await order.Authorizations()).First();
                var httpChallenge = await authorization.Http();

                // Save the expected response
                _responseStore.AddChallengeResponse(httpChallenge.Token, httpChallenge.KeyAuthz);
                _logger?.LogInformation("ACME http challenge initialized and tokens cached. About to execute http challenge.");

                // Execute http challenge
                await httpChallenge.Validate();

                await httpChallenge.WaitForCompletion(TimeSpan.FromSeconds(1), _logger);

                // Download certificate, generate pfx and write to file
                var certificate = await order.GetFinalCertificate(
                    _options.CsrInfo,
                    _options.Hostname,
                    _options.FriendlyName);

                _logger?.LogInformation("Certificate successfully downloaded and is about to get persisted.");
                _certificateSaver.Save(certificate, _options.Hostname);
            } catch (Exception ex) {
                _logger?.LogError(ex, "Error while running the ACME http challenge protocol procedure.");
                _errorReporter.ReportException(ex);
            } finally {
                // Stop intermediary application
                _logger?.LogInformation("Intermediary server for ACME challenge is shutting down...");
                _application.StopApplication();
            }
        }
Example #4
0
        private async Task ValidateDomainOwnershipAsync(IAuthorizationContext authorizationContext, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();

            var authorization = await authorizationContext.Resource();

            var domainName = authorization.Identifier.Value;

            if (authorization.Status == AuthorizationStatus.Valid)
            {
                // Short circuit if authorization is already complete
                return;
            }

            _logger.LogDebug("Requesting authorization to create certificate for {domainName}", domainName);

            cancellationToken.ThrowIfCancellationRequested();

            var httpChallenge = await authorizationContext.Http();

            cancellationToken.ThrowIfCancellationRequested();

            if (httpChallenge == null)
            {
                throw new InvalidOperationException($"Did not receive challenge information for challenge type {ChallengeTypes.Http01}");
            }

            var keyAuth = httpChallenge.KeyAuthz;

            _challengeStore.AddChallengeResponse(httpChallenge.Token, keyAuth);

            cancellationToken.ThrowIfCancellationRequested();

            _logger.LogDebug("Requesting completion of challenge to prove ownership of domain {domainName}", domainName);

            var challenge = await httpChallenge.Validate();

            var retries = 60;
            var delay   = TimeSpan.FromSeconds(2);

            while (retries > 0)
            {
                retries--;

                cancellationToken.ThrowIfCancellationRequested();

                authorization = await authorizationContext.Resource();

                _logger.LogAcmeAction("GetAuthorization");

                switch (authorization.Status)
                {
                case AuthorizationStatus.Valid:
                    return;

                case AuthorizationStatus.Pending:
                    await Task.Delay(delay);

                    continue;

                case AuthorizationStatus.Invalid:
                    throw InvalidAuthorizationError(authorization);

                case AuthorizationStatus.Revoked:
                    throw new InvalidOperationException($"The authorization to verify domainName '{domainName}' has been revoked.");

                case AuthorizationStatus.Expired:
                    throw new InvalidOperationException($"The authorization to verify domainName '{domainName}' has expired.");

                default:
                    throw new ArgumentOutOfRangeException("Unexpected response from server while validating domain ownership.");
                }
            }

            throw new TimeoutException("Timed out waiting for domain ownership validation.");
        }
Example #5
0
        private async Task ValidateDomainOwnershipAsync(string hostName, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();

            _logger.LogDebug("Requesting authorization to create certificates for {hostname}", hostName);
            var auth = await _client.NewAuthorization(new AuthorizationIdentifier
            {
                Type  = AuthorizationIdentifierTypes.Dns,
                Value = hostName,
            });

            _logger.LogResponse("NewAuthorization", auth);

            cancellationToken.ThrowIfCancellationRequested();

            var httpChallenge = auth.Data.Challenges.FirstOrDefault(c => c.Type == ChallengeTypes.Http01);

            if (httpChallenge == null)
            {
                throw new InvalidOperationException($"Did not receive challenge information for challenge type {ChallengeTypes.Http01}");
            }

            var keyAuth = _client.ComputeKeyAuthorization(httpChallenge);

            _challengeStore.AddChallengeResponse(httpChallenge.Token, keyAuth);

            cancellationToken.ThrowIfCancellationRequested();

            _logger.LogDebug("Requesting completion of challenge to prove ownership of {hostname}", hostName);

            var challengeCompletion = await _client.CompleteChallenge(httpChallenge);

            _logger.LogResponse("CompleteChallenge", challengeCompletion);

            var retries = 60;
            var delay   = TimeSpan.FromSeconds(2);

            AcmeResult <AuthorizationEntity> authorization;

            while (retries > 0)
            {
                retries--;

                cancellationToken.ThrowIfCancellationRequested();

                authorization = await _client.GetAuthorization(challengeCompletion.Location);

                _logger.LogResponse("GetAuthorization", authorization);

                switch (authorization.Data.Status)
                {
                case EntityStatus.Valid:
                    return;

                case EntityStatus.Pending:
                case EntityStatus.Processing:
                    await Task.Delay(delay);

                    continue;

                case EntityStatus.Invalid:
                    throw InvalidAuthorizationError(hostName, authorization);

                case EntityStatus.Revoked:
                    throw new InvalidOperationException($"The authorization to verify hostname '{hostName}' has been revoked.");

                case EntityStatus.Unknown:
                default:
                    throw new ArgumentOutOfRangeException("Unexpected response from server while validating domain ownership.");
                }
            }

            throw new TimeoutException("Timed out waiting for domain ownership validation.");
        }