Подтвердить что ты не робот

WebApi HttpClient не отправляет сертификат клиента

Я пытаюсь защитить свой сервис RESTful WebApi с помощью аутентификации ssl и клиента с помощью клиентских сертификатов.

Проверить; Я создал самоподписанный сертификат и размещен на локальной машине, доверенной корневой папке сертификации, и я создал сертификаты "сервер" и "клиент". Стандартный https для сервера работает без проблем.

Однако у меня есть код на сервере для проверки сертификата, который никогда не вызывается при подключении с использованием моего тестового клиента, который поставляет мой клиентский сертификат, а тестовому клиенту возвращается статус запрещенного 403.

Это указывает на то, что сервер не выполнил мой сертификат, прежде чем он достигнет моего кода проверки. Однако, если я запускаю скрипач, он знает, что требуется сертификат клиента, и просит его предоставить его в "Мои документы \Fiddler2". Я дал ему тот же клиентский сертификат, который я использую в своем тестовом клиенте, и теперь мой сервер работает и получил сертификат клиента, которого я ожидаю! Это означает, что клиент WebApi не отправляет сертификат надлежащим образом, мой код клиента ниже примерно такой же, как и другие примеры, которые я нашел.

    static async Task RunAsync()
    {
        try
        {
            var handler = new WebRequestHandler();
            handler.ClientCertificateOptions = ClientCertificateOption.Manual;
            handler.ClientCertificates.Add(GetClientCert());
            handler.ServerCertificateValidationCallback += Validate;
            handler.UseProxy = false;

            using (var client = new HttpClient(handler))
            {
                client.BaseAddress = new Uri("https://hostname:10001/");

                client.DefaultRequestHeaders.Accept.Clear();
                client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/xml"));

                var response = await client.GetAsync("api/system/");
                var str = await response.Content.ReadAsStringAsync();

                Console.WriteLine(str);
            }
        } catch(Exception ex)
        {
            Console.Write(ex.Message);
        }
    }

Любые идеи, почему это будет работать у скрипача, но не на моем тестовом клиенте?

Изменить: Вот код GetClientCert()

private static X509Certificate GetClientCert()
    {            
        X509Store store = null;
        try
        {
            store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
            store.Open(OpenFlags.OpenExistingOnly | OpenFlags.ReadOnly);

            var certs = store.Certificates.Find(X509FindType.FindBySubjectName, "Integration Client Certificate", true);

            if (certs.Count == 1)
            {
                var cert = certs[0];
                return cert;
            }
        }
        finally
        {
            if (store != null) 
                store.Close();
        }

        return null;
    }

Предоставлено, что тестовый код не обрабатывает нулевой сертификат, но я отлаживаю его, чтобы убедиться, что находится правильный сертификат.

4b9b3361

Ответ 1

Является ли ваш код выше в той же учетной записи пользователя, в которой работает Fiddler? В противном случае он может иметь доступ к файлу сертификата, но не к правильному закрытому ключу. Аналогично, что возвращает ваша функция GetClientCert()? В частности, имеет ли он свойство свойства PrivateKey?

Ответ 2

Существует два типа сертификатов. Первый - это публичный файл .cer, который отправляется вам от владельца сервера. Этот файл представляет собой просто длинную строку символов. Второй - это сертификат хранилища ключей, это самоподписанный сертификат, который вы создаете и отправляете на сервер, на который вы звоните, и устанавливаете его. В зависимости от того, сколько у вас безопасности, вам может потребоваться добавить один или оба из них клиенту (обработчик в вашем случае). Я видел только сертификат хранилища ключей, используемый на одном сервере, где безопасность ОЧЕНЬ безопасна. Этот код получает оба сертификата из папки bin/deployed:

#region certificate Add
                // KeyStore is our self signed cert
                // TrustStore is cer file sent to you.

                // Get the path where the cert files are stored (this should handle running in debug mode in Visual Studio and deployed code) -- Not tested with deployed code
                string executableLocation = Path.GetDirectoryName(AppDomain.CurrentDomain.RelativeSearchPath ?? AppDomain.CurrentDomain.BaseDirectory);

                #region Add the TrustStore certificate

                // Get the cer file location
                string pfxLocation = executableLocation + "\\Certificates\\TheirCertificate.cer";

                // Add the certificate
                X509Certificate2 theirCert = new X509Certificate2();
                theirCert.Import(pfxLocation, "Password", X509KeyStorageFlags.DefaultKeySet);
                handler.ClientCertificates.Add(theirCert);
                #endregion

                #region Add the KeyStore 
                // Get the location
                pfxLocation = executableLocation + "\\Certificates\\YourCert.pfx";

                // Add the Certificate
                X509Certificate2 YourCert = new X509Certificate2();
                YourCert.Import(pfxLocation, "PASSWORD", X509KeyStorageFlags.DefaultKeySet);
                handler.ClientCertificates.Add(YourCert);
                #endregion

                #endregion

Кроме того, вам нужно обрабатывать ошибки сертификата (обратите внимание: это BAD - он говорит, что все проблемы с сертификатом в порядке), вы должны изменить этот код для обработки определенных проблем с сертификатами, таких как "Несоответствие имен". это в моем списке. Есть много примеров того, как это сделать.

Этот код в верхней части вашего метода

// Ignore Certificate errors  need to fix to only handle 
ServicePointManager.ServerCertificateValidationCallback = MyCertHandler;

Метод где-то в вашем классе

private bool MyCertHandler(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors error)
    {
        // Ignore errors
        return true;
    }

Ответ 3

В коде, который вы используете store = new X509Store(StoreName.My, StoreLocation.LocalMachine);.

Сертификаты клиентов не выбраны из LocalMachine, вместо этого следует использовать StoreLocation.CurrentUser.

Проверяя MMC -> File -> Add or Remove Snap-ins -> Certificates -> My user account, вы увидите сертификат, который использует скрипт. Если вы удалите его из My user account и импортируете только в Computer account, вы увидите, что Fiddler не может его забрать.

Обратите внимание, что при поиске сертификатов вы также должны обращаться к культуре.

Пример:

var certificateSerialNumber= "‎83 c6 62 0a 73 c7 b1 aa 41 06 a3 ce 62 83 ae 25".ToUpper().Replace(" ", string.Empty);

//0 certs
var certs = store.Certificates.Find(X509FindType.FindBySerialNumber, certificateSerialNumber, true);

//null
var cert = store.Certificates.Cast<X509Certificate>().FirstOrDefault(x => x.GetSerialNumberString() == certificateSerialNumber);

//1 cert
var cert1 = store.Certificates.Cast<X509Certificate>().FirstOrDefault(x =>
                x.GetSerialNumberString().Equals(certificateSerialNumber, StringComparison.InvariantCultureIgnoreCase));