只为特定路由或控制器要求SSL客户端证书

12

我有一个使用Kestrel作为服务器的ASP.NET MVC Core项目。它不仅提供用户内容(asp.net mvc),还托管了Web API控制器,用于与代理(软件)通信。我已启用了HTTPS和客户端证书支持。问题是我希望要求代理(软件)调用Web API时提供客户端证书,但我不想要求/提示常规基于浏览器的用户提供客户端证书。

我已按以下方式启用了HTTPS/客户端证书支持:

var host = new WebHostBuilder()
.UseKestrel(options =>
{
    HttpsConnectionFilterOptions httpsoptions = new    HttpsConnectionFilterOptions();
    httpsoptions.ServerCertificate = CertUtil.GetServerCert();
    httpsoptions.ClientCertificateMode = ClientCertificateMode.AllowCertificate;
    httpsoptions.CheckCertificateRevocation = false;

    options.UseHttps(httpsoptions);
})
.UseUrls("http://0.0.0.0:5000", "https://0.0.0.0:5001")
.UseContentRoot(Directory.GetCurrentDirectory())
.UseStartup<Startup>()
.Build();
host.Run();

我在Startup.cs中设置了单独的中间件处理程序来处理客户端证书的自定义验证。这段代码能够成功执行,并且在这方面一切都正常。

问题是这个处理程序是全局性的,而我只想将客户端证书应用于特定的控制器和/或路由;或者说,此时我会接受任何粒度。

本质上,我试图创建与在IIS中创建两个虚拟目录并将SSL设置为在一个上接受,在另一个上忽略相同行为的方式。设置HttpsConnectionFilterOptions仅指定ServerCertificate,希望不设置任何客户端证书相关选项将允许服务器在接收到客户端证书时接收它们,但否则不提示浏览器。当调用此功能时,我的中间件客户端证书处理程序似乎从未看到客户端证书(当ClientCertificateMode设置为AllowCertificate时,它确实可以看到)。

context.Connection.GetClientCertificateAsync();

简短来说,Kestrel托管是否允许更精细的客户端证书映射/处理,或者只能使用IIS实现?对于这个项目,IIS不是选择,我宁愿不创建一个单独的项目/进程来处理客户端证书api方面。感谢任何帮助!


顺便提一下,这里的问题与使用Kestrel的IIS自托管asp.net core相似。https://dev59.com/Nl4c5IYBdhLWcg3w-OU4 - Paul W.
4个回答

5
我一直在尝试做同样的事情,和你提出的要求完全相同。
我得到的结论是这是不可能的。我的解决方法是使用 2 个 WebHostBuilder 对象 - 一个用于不需要客户端证书的位置,另一个用于需要客户端证书的位置。这样做的缺点是每个 IWebHost 必须监听不同的端口,但根据你描述的场景,我想这并不是一个大问题。
我在同一个进程中执行此操作,因此此解决方案符合该要求。

我最近也发现这似乎是目前唯一可行的解决方案。开发团队似乎也推荐同样的方法:https://github.com/aspnet/KestrelHttpServer/issues/1162#issuecomment-253291237 谢谢! - Paul W.
1
有没有人在 asp.net core 2.0 发布后有新的/更好的方法来做这件事情呢? - Paul W.

4
我曾经遇到和你一样的问题,使用`context.Connection.GetClientCertificateAsync();`方法返回值总是为空。后来我发现我一直在通过IIS Express运行Kestrel。

所以在Visual Studio脚本工具栏中,我将运行环境从IIS Express改成了我的项目。 Kestrel作为控制台应用程序启动后,我就能够获取客户端证书了。

enter image description here

我认为IIS Express不支持客户端证书,因此证书始终被忽略。

对于问题的另一部分; 我认为当使用HttpsConnectionFilterOptions时,Kestrel默认并不支持你需要的这种细粒度设置。从Kestrel Connection Filter Options源代码,如果客户端证书为空,则该连接将被断开。也许您可以修改HttpsConnectionFilterOptions的源代码,并从中创建自己的过滤器。然后,您可以使用ClientCertificateValidation属性指定自定义证书验证方法,以允许在未发送客户端证书时进行连接。

希望这能帮到你。


1
我已经找出了如何只在某些路由上使用客户端证书,但在运行 Azure Web 应用程序时,客户端证书没有被传递到代码中。在运行 IIS Express 时也存在相同的问题。
在这个例子中,一个控制器不需要证书,另外两个需要不同的证书。 https://github.com/xavierjohn/ClientCertificateMiddleware 如果不在 IIS Express 下运行,则证书会被传递。

那是你构建的一个非常好的解决方案。从安全角度来看,你还应该验证证书指纹,否则攻击者可以轻松地欺骗颁发者和主题,而指纹是整个证书的哈希值等等... 你的解决方案在Kestrel下确实运行良好。我不确定在IIS Express或Azure Web App下可能会出现什么问题,但你可以在这方面开启一个单独的Stack Overflow线程。 - Paul W.
这篇文章似乎与您在Azure上遇到的问题有关,即无法获取客户端证书。https://blogs.msdn.microsoft.com/kaevans/2016/04/13/azure-web-app-client-certificate-authentication-with-asp-net-core-2/ - Paul W.
一旦我弄清楚如何在Azure中使其运行,我将添加指纹选项。指纹的问题在于,当证书过期时,我们需要不断更新它。对于有许多合作伙伴的服务而言,这可能会很痛苦。 - Xavier John
这是一份关于如何在Azure Web应用程序上启用证书的文档。 https://msftplayground.com/2016/06/azure-app-service-and-client-certificate-authentication/ - Xavier John
确实,保持所有指纹的最新状态可能更费力。但至少您应该考虑执行证书链验证以确保证书是有效的,并与您信任的CA相关。实际上,您需要非常小心地自行验证证书。关于此有很多值得研究的最佳实践。 - Paul W.
显示剩余2条评论

-1

没有必要使用孤立的(新的)IWebHost来控制访问专用的MVC控制器。 只需要使用 Athorization Filter就可以了。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接