Сертификат клиента .netcore 3 с помощью HttpClient - PullRequest
0 голосов
/ 07 марта 2020

У меня есть проект .netcore 3 (шаблон WorkerService), который отправляет данные JSON в конечную точку REST. Запросы отправляются через HttpClient и настроены на использование сертификата клиента, который требуется серверу. Ответ сервера всегда 200 и HTML символов. По словам менеджеров серверов, запрос перенаправляется на домашнюю страницу веб-сервера, поскольку клиентская машина правильно обрабатывается с указанным пользователем c, но сертификат недоступен. Я использую следующий код:

public void ConfigureServices(IServiceCollection services)
{
  services.AddHttpClient("client").ConfigurePrimaryHttpMessageHandler(() => 
  {
    var handler = new HttpClientHandler();
    handler.ClientCertificateOptions = ClientCertificateOption.Manual;
    handler.SslProtocols = SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12;
    handler.ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => true;
    X509Certificate2 certificate = GetCertificate(Configuration.CertificateSubjectKeyIdentifier);
    handler.ClientCertificates.Add(certificate);
    return handler;
  }
}

GetCertificate получает сертификат из хранилища сертификатов:

    private X509Certificate2 GetCertificate(string subjectIdentifier)
    {
        X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
        store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);

        var collection = store.Certificates;
        var certificates = collection.Find(X509FindType.FindBySubjectKeyIdentifier, subjectIdentifier, true);
        foreach (var certificate in certificates)
        {
            if (DateTime.Compare(DateTime.Parse(certificate.GetExpirationDateString()), DateTime.Now) >= 0)
            {

                Logger.LogInformation($"Loaded X.509 certificate {certificate.Subject} issued by {certificate.Issuer}, valid from {certificate.GetEffectiveDateString()} to {certificate.GetExpirationDateString()}");
                return certificate;
            }
        }

        Logger.LogError($"X.509 certificate not loaded: No valid certificate could be found.");
        return null;
    }

Код, который отправляет запрос:

public async Task<ResponseData> PostAsync<T>(string url, T dataToSend)
    {
        ResponseData result = null;
        HttpResponseMessage httpResponseMessage = null;

        try
        {
            var errorHttp = false;
            HttpClient httpClient;
            using (httpClient = HttpClientFactory.CreateClient("client)) // IHttpClientFactory initialized in ctor
            {
                HttpContent httpContent;
                using (httpContent = CreateJsonHttpContent(dataToSend, MediaType.ApplicationJson)) //build JSON from data
                {  
                    httpResponseMessage = await httpClient.PostAsync(url, httpContent).ConfigureAwait(false);

                    result = BuildResponseData(httpResponseMessage); //writes response data in a class

                    if (httpResponseMessage?.IsSuccessStatusCode == true)
                    {
                        result.Content = await httpResponseMessage.Content.ReadAsStringAsync().ConfigureAwait(false);
                    }
                    else
                    {
                        errorHttp = true;
                    }

                    if (errorHttp)
                    {
                        var httpRequestException = new HttpRequestException($"The http request to {url} was not successful.");
                        Logger.LogError($"{httpRequestException.Message} : {httpRequestException.InnerException}");
                        Logger.LogError(httpRequestException.StackTrace);
                    }
                }
            }
        }
        catch (SocketException socketException)
        {
            Logger.LogError($"{socketException.Message} : {socketException.InnerException}");
            result = new ResponseData(socketException);
        }
        catch (WebException wex)
        {
            Logger.LogError($"{wex.Message} : {wex.InnerException}");
            if (wex.Status == WebExceptionStatus.ConnectFailure || wex.Status == WebExceptionStatus.Timeout)
            {
                Logger.LogError($"Cannot connect to the rest service : {WebExceptionStatus.Timeout}");
            }
        }
        catch (Exception ex)
        {

            LogException(ref ex);
            result = new ResponseData(ex);
        }
        finally
        {
            httpResponseMessage?.Dispose();
        }

        return result;
    }

класс, который использует метод PostAsyn c, также зарегистрирован в ServiceCollection. Есть идеи, что тут может быть не так? Может ли быть так, что сертификат неправильно обрабатывается на стороне сервера?

1 Ответ

0 голосов
/ 07 марта 2020

Мое сильное подозрение - неверная конфигурация на стороне клиента (вашего). Ваша заявка читает сертификат из магазина LocalMachine. По умолчанию только локальный администратор и учетная запись SYSTEM могут читать / использовать личные ключи для сертификатов, установленных в LocalMachine хранилище.

Либо установить сертификат в CurrentUser хранилище учетной записи пользователя, под которой клиент приложение запущено или явно предоставило разрешения на закрытый ключ учетной записи пользователя, под которой запущено клиентское приложение. Для этого:

  1. Открыть сертификат MM C оснастка в контексте LocalMachine ( certlm.ms c)
  2. Развернуть Personal\Certificates
  3. Выберите нужный сертификат, щелкните правой кнопкой мыши и затем пункт меню Manage Private Keys.
  4. Предоставьте Read разрешения учетной записи пользователя, под которой запущено клиентское приложение.

В этом случае вам не нужно изменять код или перемещать сертификат между магазинами.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...