我正在尝试获得相互的客户端认证以在Azure中工作。我正在使用以下配置运行Web应用程序:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services
.AddAuthentication(CertificateAuthenticationDefaults.AuthenticationScheme)
.AddCertificate();
services.AddCertificateForwarding(options =>
options.CertificateHeader = "X-ARR-ClientCert");
services.AddHttpClient();
services.AddControllers();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseAuthentication();
app.UseHttpsRedirection();
app.UseRouting();
app.UseCertificateForwarding();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
为此,我添加了扩展名,以便Web应用程序将客户端证书发送到我的应用程序。当我部署它时,这很好。我前面有cloudflare并启用了Origin Pull,并且可以验证客户端证书是否通过发送。我可以看到,当我尝试直接在azurewebsites.net域上转到Web应用程序时,我的浏览器正在请求证书。如果我尝试浏览Cloudflare,它将显示该网页。我以为这是可行的,但是当我检查日志时,我得到了:
2020-07-02 13:30:52.711 +00:00 [信息] Microsoft.AspNetCore.Hosting.Diagnostics:请求启动HTTP / 1.1 GET https:// [REMOVED] / api / ping
2020-07-02 13:30:52.718 +00:00 [Trace] Microsoft.AspNetCore.HostFiltering.HostFilteringMiddleware:允许所有主机。
2020-07-02 13:30:53.107 +00:00 [警告] Microsoft.AspNetCore.Authentication.Certificate.CertificateAuthenticationHandler:证书验证失败,主题为OU = Origin Pull,O =“ Cloudflare,Inc.” ,L =旧金山,S =加利福尼亚,C =美国。UntrustedRoot一个证书链,但已终止于受信任的提供者不信任的根证书。 RevocationStatusUnknown撤消功能无法检查证书的吊销。OfflineRevocation撤消功能由于吊销服务器处于脱机状态而无法检查吊销。
2020-07-02 13:30:53.107 +00:00 [信息] Microsoft.AspNetCore.Authentication.Certificate.CertificateAuthenticationHandler:证书未通过身份验证。失败消息:客户端证书验证失败。
2020-07-02 13:30:53.110 +00:00 [Debug] Microsoft.AspNetCore.Routing.Matching.DfaMatcher:为请求路径'/ api / ping'找到1个候选对象
看来客户证书未被接受。应该是吗?我的意思是,这是Cloudflare。我在设置中做错了吗?我是否应该在自己的侧面安装一些额外的东西?我在这里浏览了本指南:https://support.cloudflare.com/hc/en-us/articles/204899617-Authenticated-Origin-Pulls,但没有提及有关证书额外安装的任何内容。我是否应该在Web应用本身上安装origin-pull-ca.pem?
当我比较发送给我的证书和origin-pull-ca.pem时,两者不相等:
他们不应该平等吗?
请注意:我不是证书,SSL等方面的专家。我想在这里学习:)
答案 0 :(得分:1)
我在这里https://community.cloudflare.com/t/manual-authenticated-origin-pulls-verification/145614问了完全相同的问题。 Dunno为什么,但是A27996CBA564D24731BC76439C48920C1F7D4AA3是正确的。
编辑:使用链更新
public class CloudflareClientCertificateMiddleware
{
private static X509Certificate2 _cloudflareOriginPullCert;
private readonly RequestDelegate _next;
public CloudflareClientCertificateMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
if (_cloudflareOriginPullCert == null)
_cloudflareOriginPullCert = Helpers.CertificateHelper.GetCertificateInSpecifiedStore("origin-pull.cloudflare.net", StoreName.Root, StoreLocation.LocalMachine);
bool isCloudflareCertificate = true;
X509Certificate2 clientCertificate = context.Connection.ClientCertificate;
using (X509Chain chain = new X509Chain())
{
chain.ChainPolicy.ExtraStore.Add(_cloudflareOriginPullCert);
chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
// https://stackoverflow.com/questions/6097671/how-to-verify-x509-cert-without-importing-root-cert (Azure)
//chain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllowUnknownCertificateAuthority;
// https://stackoverflow.com/a/7332193
if (clientCertificate == null || chain.Build(clientCertificate) == false)
isCloudflareCertificate = false;
// Make sure we have the same number of elements.
if (isCloudflareCertificate && chain.ChainElements.Count != chain.ChainPolicy.ExtraStore.Count + 1)
isCloudflareCertificate = false;
// Make sure all the thumbprints of the CAs match up.
// The first one should be 'primaryCert', leading up to the root CA.
if (isCloudflareCertificate)
{
for (int i = 1; i < chain.ChainElements.Count; i++)
{
if (chain.ChainElements[i].Certificate.Thumbprint != chain.ChainPolicy.ExtraStore[i - 1].Thumbprint)
isCloudflareCertificate = false;
}
}
}
if (isCloudflareCertificate)
await _next.Invoke(context);
else
context.Response.StatusCode = StatusCodes.Status403Forbidden;
}
}
答案 1 :(得分:1)
基本上我可以这样做,以验证链条
private bool VerifyCertificate(X509Certificate2 client, ILogger<Startup> logger)
{
X509Chain chain = new X509Chain();
var authority = GetInstalledCert();
chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
chain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllowUnknownCertificateAuthority;
if (!chain.Build(client))
return false;
var valid = chain.ChainElements
.Cast<X509ChainElement>()
.Any(x => x.Certificate.Thumbprint == authority.Thumbprint);
if (!valid)
return false;
return true;
}
private X509Certificate2 GetInstalledCert()
{
X509Certificate2 cert = null;
X509Store certStore = new X509Store(StoreName.My, StoreLocation.CurrentUser);
certStore.Open(OpenFlags.ReadOnly);
X509Certificate2Collection certCollection = certStore.Certificates.Find(
X509FindType.FindByThumbprint,
"1F5BA8DCF83E6453DD75C47780906710901AD641",
false);
if (certCollection.Count > 0)
{
cert = certCollection[0];
}
certStore.Close();
return cert;
}
private X509Certificate2 GetClientCert(IHeaderDictionary headers)
{
var certHeader = headers["X-ARR-ClientCert"];
if (certHeader.Any())
{
byte[] clientCertBytes = Convert.FromBase64String(certHeader);
var certificate = new X509Certificate2(clientCertBytes);
return certificate;
}
return null;
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILogger<Startup> logger)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.Use(async (context, next) =>
{
var clientCert = GetClientCert(context.Request.Headers);
bool verify = VerifyCertificate(clientCert, logger);
if(verify)
{
await next.Invoke();
}
else
{
context.Response.StatusCode = 404;
}
});
app.UseHttpsRedirection();
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}