OnAuthorization(HttpActionContext actionContext)中没有证书

时间:2019-03-01 16:30:27

标签: c# security asp.net-web-api ssl-certificate client-certificates

我已从此示例创建了CertificateTestController和ValuesController How to use a client certificate to authenticate and authorize in a Web 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;
            }
        }

使用下面的CertificateTestController调用上面的ValuesController

{
    [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 :(得分:0)

这是我尝试解决此问题时一路回答的问题/问题。

Q1。

为什么我的证书没有到达客户端(代码)?

A1。

VS进行了一次初始SSL协商,该协商发生在甚至未命中OnAuthorization(HttpActionContext actionContext)之前。此时,服务器会在证书存储中搜索已安装私钥进行验证的客户端证书。如果没有私钥,则失败。我通过打开详细来发现问题。请参见下面。

这里有一些配置需要更改才能使它正常工作。

1。 Web Config文件更改

•SSL协商

下面的配置告诉Visual Studio,“ api / values” URL需要进行SSL协商。这有助于我们限制预期在何时何地进行证书协商。位置路径属性

<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 Visual Studio 2015和2017 {project_name} .vs \ config \ config.host.config
变更 确保以下属性设置为“允许”

2)启用SSL

在visual studio属性窗口中启用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。 我们将根据以下序列号附加证书。也可以根据“缩略图”,“主题”,“到期/吊销”,“链验证”等附加证书。

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

5)ValueController.cs

此类充当服务器。这是证书验证发生的地方。在引用WebApiConfig.cs的“ CheckCertificate”方法/函数中有两项更改。如果您在WebApiConfig.cs下从上方进行了“应用程序设置”更改,那么您就很好了。

3。文件

1)CertificateTestController.cs

2)ValuesController.cs