在* some * WebAPI控制器上禁用SSL客户端证书?

时间:2014-12-05 20:08:55

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

  

为未来读者编辑 :不幸的是,赏金给予答案   没有工作;我现在无能为力。但请阅读我自己的答案   下面(通过测试) - 确认使用最少的代码更改

我们有一个Azure Cloud Service(WebRole)完全在ASP.NET WebAPI 2.2中(没有MVC,前端是Angular)。我们的一些控制器/ REST端点通过SSL(客户端证书身份验证/相互身份验证)与第三方云服务通信,其余控制器/端点与HTML5 / AngularJS前端通信,也通过SSL(但更传统的服务器身份验证) SSL)。我们没有任何非SSL端点。我们已通过云服务启动任务启用了客户端SSL,例如:

IF NOT DEFINED APPCMD SET APPCMD=%SystemRoot%\system32\inetsrv\AppCmd.exe
%APPCMD% unlock config /section:system.webServer/security/access

问题:该设置是站点范围的,因此即使用户点击第一页(例如https://domain.com,返回angular.html的index.html),他们的浏览器也会要求他们提供客户端SSL证书。 (如下图所示)

如果有办法

  1. 将客户端SSL证书请求限制为仅与第三方云服务通信的WebAPI控制器?
  2. OR

    1. 为我们的前端驱动webapi控制器跳过客户端SSL身份验证?
    2. 我们服务器的web.config很复杂,但相关的代码段如下:

      <system.webServer>
        <security>
          <access sslFlags="SslNegotiateCert" />
        </security>
      </system.webServer>
      

      客户端的屏幕截图,点击常规的WebAPI端点,但尝试进行客户端SSL身份验证(在任何浏览器,Chrome,Firefox或IE中都会发生) enter image description here

3 个答案:

答案 0 :(得分:13)

不幸的是,cleftheris的回答是,奖励该奖金是行不通的。它尝试在HTTP服务器管道/处理中工作太晚以获取客户端证书,但this post给了我一些想法。

该解决方案基于web.config,呼吁特殊处理&#34;目录&#34; (适用于虚拟文件夹或WebAPI路由)。

这是理想的逻辑:

  

https://www.server.com/acmeapi/ ** =&gt;使用客户证书的SSL

     

https://www.server.com/ ** =&gt; SSL

这是相应的配置

<configuration>
  ...
  <system.webServer>
    <!-- This is for the rest of the site -->
    <security>
      <access sslFlags="Ssl" />
    </security>
  </system.webServer>

  <!--This is for the 3rd party API endpoint-->
  <location path="acmeapi">
    <system.webServer>
      <security>
        <access sslFlags="SslNegotiateCert"/>
      </security>
    </system.webServer>
  </location>
...
</configuration>

加分

以上将相应地设置SSL握手。现在,您仍然需要在代码中检查客户端SSL证书(如果它是您期望的那个)。完成如下

控制器代码:

[RoutePrefix("acmeapi")]
[SslClientCertActionFilter] // <== key part!
public class AcmeProviderController : ApiController
{
    [HttpGet]
    [Route("{userId}")]
    public async Task<OutputDto> GetInfo(Guid userId)
    {
        // do work ...
    }
}

以下执行SSL客户端验证的实际属性如下。可以用来装饰整个控制器或只是特定的方法。

public class SslClientCertActionFilterAttribute : ActionFilterAttribute
{
    public List<string> AllowedThumbprints = new List<string>()
    {
        // Replace with the thumbprints the 3rd party
        // server will be presenting. You can make checks
        // more elaborate but always have thumbprint checking ...
        "0011223344556677889900112233445566778899",
        "1122334455667788990011223344556677889900" 
    };

    public override void OnActionExecuting(HttpActionContext actionContext)
    {
        var request = actionContext.Request;

        if (!AuthorizeRequest(request))
        {
            throw new HttpResponseException(HttpStatusCode.Forbidden);
        }
    }

    private bool AuthorizeRequest(HttpRequestMessage request)
    {
        if (request==null)
            throw new ArgumentNullException("request");

        var clientCertificate = request.GetClientCertificate();

        if (clientCertificate == null || AllowedThumbprints == null || AllowedThumbprints.Count < 1)
        {
            return false;
        }

        foreach (var thumbprint in AllowedThumbprints)
        {
            if (clientCertificate.Thumbprint != null && clientCertificate.Thumbprint.Equals(thumbprint, StringComparison.InvariantCultureIgnoreCase))
            {
                return true;
            }
        }
        return false;
    }
}

答案 1 :(得分:9)

您可以在web.config级别上简单地允许纯HTTP流量,并在Web Api管道中为此编写自定义委托处理程序。您可以找到客户端证书委派处理程序herehere。然后,您可以使此处理程序处于活动状态“Per-Route”,如本例here中所示:

这是你的路线配置。

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        config.Routes.MapHttpRoute(
            name: "Route1",
            routeTemplate: "api/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional }
        );

        config.Routes.MapHttpRoute(
            name: "Route2",
            routeTemplate: "api2/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional },
            constraints: null,
            handler: new CustomCertificateMessageHandler()  // per-route message handler
        );

        config.MessageHandlers.Add(new SomeOtherMessageHandler());  // global message handler
    }
}

请注意,如果您需要“按路由”委派处理程序,则必须将它们放在全局消息处理程序列表中。

答案 2 :(得分:2)

不幸的是,这不能在控制器级别配置。服务器根据HTTP请求的内容(通常是请求路径)决定使用哪个控制器。 SSL通过加密来保护HTTP消息的内容,并且请求路径是加密消息的一部分。 SSL通道需要在发送任何HTTP消息之前进行设置,这就是SSL通道的配置(例如,服务器是否尝试协商客户端证书)不能依赖任何HTTP的内容的原因。消息。

所以这是您的选择:

  1. 启动第二个配置为不协商客户端证书的Web角色。你需要第二个域,因为它本质上是一个单独的服务。所以你有https://domain.com指向非客户端证书,https://foo.domain.com指向需要客户证书的那个。

  2. 使用相同的Web角色,但为IIS设置第二个端口以进行侦听,并将其配置为不协商客户端证书。但是,使用非标准端口很麻烦,因为您的一个客户将不得不做https://domain.com:444(或443之外的其他一些端口)。

  3. 全面禁用客户端证书协商。这可能不起作用,具体取决于您的服务如何访问客户端证书,但通常在您访问System.Web.HttpRequest对象(或等效对象)上的ClientCertificate属性时,它将根据需要协商证书。这意味着它透明地拆除了现有的SSL会话并设置了一个新会话,这次要求客户端提供证书。这是非常低效的,因为首先设置SSL连接需要多次往返,并且做两次是痛苦的。但是,根据您的可用选项,使用客户端证书的请求的性能要求,以及是否从保持活动中获得大量连接重用,此选项可能有意义。

  4. 希望这有帮助。