Нет сертификата в OnAuthorization (HttpActionContext actionContext) - PullRequest
0 голосов
/ 01 марта 2019

Я создал CertificateTestController и ValuesController из этого примера Как использовать сертификат клиента для аутентификации и авторизации в веб-API .Если вы прокрутите вниз до «Обновить» от пользователя Ogglas.Я взял его пример и получил «CertificateTestController» для работы, где я могу взять сертификат из своего магазина и добавить его в «обработчик».Когда я вызываю «ValuesController», сертификат не инициализируется

X509Certificate2 cert = actionContext.Request.GetClientCertificate();

. Вот полный код, который у меня есть

ValuesController

{
    [RequireSpecificCert]
    public class ValuesController : ApiController
    {
        // GET api/values
        public IHttpActionResult Get()
        {
            return Ok("It works!");
        }

        public class RequireSpecificCertAttribute : AuthorizationFilterAttribute
        {
            public override void OnAuthorization(HttpActionContext actionContext)
            {

                if (actionContext.Request.RequestUri.Scheme != Uri.UriSchemeHttps)
                {
                    actionContext.Response = new HttpResponseMessage(System.Net.HttpStatusCode.Forbidden)
                    {
                        ReasonPhrase = "HTTPS Required"
                    };
                }
                else
                {
                    X509Certificate2 cert = actionContext.Request.GetClientCertificate();
                    X509Certificate2 cert2 = actionContext.RequestContext.ClientCertificate;

                    if (cert == null)
                    {
                        actionContext.Response = new HttpResponseMessage(System.Net.HttpStatusCode.Forbidden)
                        {
                            ReasonPhrase = "Client Certificate Required"
                        };
                    }
                    else
                    {
                        X509Chain chain = new X509Chain();

                        //Needed because the error "The revocation function was unable to check revocation for the certificate" happened to me otherwise
                        chain.ChainPolicy = new X509ChainPolicy()
                        {
                            RevocationMode = X509RevocationMode.NoCheck,
                        };
                        try
                        {
                            var chainBuilt = chain.Build(cert);
                            Debug.WriteLine(string.Format("Chain building status: {0}", chainBuilt));

                            var validCert = CheckCertificate(chain, cert);

                            if (chainBuilt == false || validCert == false)
                            {
                                actionContext.Response = new HttpResponseMessage(System.Net.HttpStatusCode.Forbidden)
                                {
                                    ReasonPhrase = "Client Certificate not valid"
                                };
                                foreach (X509ChainStatus chainStatus in chain.ChainStatus)
                                {
                                    Debug.WriteLine(string.Format("Chain error: {0} {1}", chainStatus.Status, chainStatus.StatusInformation));
                                }
                            }
                        }
                        catch (Exception ex)
                        {
                            Debug.WriteLine(ex.ToString());
                        }
                    }

                    base.OnAuthorization(actionContext);
                }
            }

            private bool CheckCertificate(X509Chain chain, X509Certificate2 cert)
            {
                var rootThumbprint = WebConfigurationManager.AppSettings["rootThumbprint"].ToUpper().Replace(" ", string.Empty);

                var clientThumbprint = WebConfigurationManager.AppSettings["clientThumbprint"].ToUpper().Replace(" ", string.Empty);

                //Check that the certificate have been issued by a specific Root Certificate
                var validRoot = chain.ChainElements.Cast<X509ChainElement>().Any(x => x.Certificate.Thumbprint.Equals(rootThumbprint, StringComparison.InvariantCultureIgnoreCase));

                //Check that the certificate thumbprint matches our expected thumbprint
                var validCert = cert.Thumbprint.Equals(clientThumbprint, StringComparison.InvariantCultureIgnoreCase);

                return validRoot && validCert;
            }
        }

, вызывающий выше ValuesController.с нижеуказанным CertificateTestController

{
    [RoutePrefix("api/certificatetest")]
    public class CertificateTestController : ApiController
    {
        public IHttpActionResult Get()
        {
            var handler = new WebRequestHandler();
            handler.ClientCertificateOptions = ClientCertificateOption.Manual;
            handler.ClientCertificates.Add(GetClientCert());
            handler.UseProxy = false;
            var client = new HttpClient(handler);
            var result = client.GetAsync("https://localhost:44301//values").GetAwaiter().GetResult();
            var resultString = result.Content.ReadAsStringAsync().GetAwaiter().GetResult();
            return Ok(resultString);
        }

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

                var certificateSerialNumber = "2bc034466b6960d2fee84d86e6c2532a".ToUpper().Replace(" ", string.Empty);

                var cert = store.Certificates.Cast<X509Certificate>().FirstOrDefault(x => x.GetSerialNumberString().Equals(certificateSerialNumber, StringComparison.InvariantCultureIgnoreCase));
                return cert;
            }
            finally
            {
                store.Close();
            }
        }
    }
}

Пожалуйста, помогите!

1 Ответ

0 голосов
/ 04 июня 2019

Вот вопросы / проблемы, на которые я ответил, когда пытался решить эту проблему.

Q1.

Почему мой сертификат не попал на сторону клиента (код)?

A1.

VS выполняет начальное согласование SSL, которое происходит до того, как будет нажата кнопка OnAuthorization (HttpActionContext actionContext).В этот момент сервер ищет сертификат клиента в хранилище сертификатов, для которого установлен закрытый ключ.Если нет закрытого ключа, то он не работает.Я нашел проблему, включив многословный.Пожалуйста, см. Ниже.

Вот конфигурации, которые необходимо изменить, чтобы это работало.

1.Изменения в файле Web Config

• Согласование SSL

Ниже в конфигурации указано, что Visual Studio ожидает согласование ssl для URL «api / values».Это помогает нам ограничивать, когда и где ожидается согласование сертификата.Свойство пути к местоположению

<location path="api/values">
  <system.webServer>
     <security>
        <access sslFlags="SslNegotiateCert" />
        <authentication>
          <iisClientCertificateMappingAuthentication enabled="true">
          </iisClientCertificateMappingAuthentication>
        </authentication>
      </security>
    </system.webServer>
  </location>

• Подробный сертификат

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

<system.diagnostics>
    <trace autoflush="true" />
    <sources>
      <source name="System.Net">
        <listeners>
          <add name="System.Net"/>
        </listeners>
      </source>
      <source name="System.Net.HttpListener">
        <listeners>
          <add name="System.Net"/>
        </listeners>
      </source>
      <source name="System.Net.Sockets">
        <listeners>
          <add name="System.Net"/>
        </listeners>
      </source>
      <source name="System.Net.Cache">
        <listeners>
          <add name="System.Net"/>
        </listeners>
      </source>
    </sources>
    <sharedListeners>
      <add name="System.Net"
       type="System.Diagnostics.TextWriterTraceListener"
       initializeData="System.Net.trace.log"
       traceOutputOptions = "ProcessId, DateTime"/>
    </sharedListeners>
    <switches>
      <add name="System.Net" value="Verbose" />
      <add name="System.Net.Sockets" value="Verbose" />
      <add name="System.Net.Cache" value="Verbose" />
      <add name="System.Net.HttpListener" value="Verbose" />
    </switches>
  </system.diagnostics>

•Настройки приложения

Измените значение rootThumbprint на отпечаток любого сертификата сервера, а clientThumprint будет тем же отпечатком сертификата клиента.ceritificateSerialNumber должен быть серийным номером исходящего сертификата.

<appSettings>
            <add key="webpages:Version" value="3.0.0.0" />
            <add key="webpages:Enabled" value="false" />
            <add key="ClientValidationEnabled" value="true" />
            <add key="UnobtrusiveJavaScriptEnabled" value="true" />
            <add key="rootThumbprint" value="change"/>
            <add key="clientThumbprint" value="change"/>
<add key="certificateSerialNumber" value="change"/>
</appSettings>

2.Локальная Visual Studio и IIS express

1) Applicationhost.config

 Путь Visual Studio ранее, чем в 2015 году, но после 2008 года Местоположение -> c: \ Users \ e # \ Documents \ IISExpress \ Config VisualStudio 2015 & 2017 {имя_проекта} .vs \ config \ config.host.config
 Изменения Убедитесь, что для свойств ниже установлено значение «Разрешить»

2) Включить SSL

Включить SSLв окне свойств визуальной студии.это важно, потому что URL меняется с http на https.

enter image description here

3) WebApiConfig.cs

Создайте папку фильтров классов.Назовите это соответствующим образом.Проверка сертификата должна быть самой первой проверкой перед выполнением любого другого кода.Вот где WebApiConfig.cs пригодится.Например, я назвал свой класс RequireHttpsAttribute.Чтобы запустить проверку, просто поместите следующую строку в файл WebApiConfig.cs config.Filters.Add (new RequireHttpsAttribute ());enter image description here

4) CertificateTestController.cs

Этот класс действует как клиент.Этот класс используется для прикрепления сертификата к запросу и его отправки.В этом классе есть одно изменение.Это было проверено только локально на компьютере.var result = client.GetAsync ("https://localhost:44300//api//values").GetAwaiter().GetResult(); Изменить URL-адрес. Мы прилагаем сертификат, основанный на серийном номере, указанном ниже. Сертификат также может быть прикреплен на основе Thumprint, Subject, Expiration /Отзыв, проверка цепочки и т. Д.

var certificateSerialNumber = WebConfigurationManager.AppSettings["certificateSerialNumber"].ToUpper().Replace(" ", string.Empty);

5) ValueController.cs

Этот класс действует как сервер. Именно здесь происходит проверка сертификата. В методе «CheckCertificate» есть два изменения/ function, которая ссылается на webApiConfig.cs. Если вы следили за изменениями параметров приложения, приведенными выше в WebApiConfig.cs, тогда все готово.

3. Файлы

1) CertificateTestController.cs

2) ValuesController.cs

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