【发布时间】:2020-11-09 11:00:44
【问题描述】:
当第三方尝试使用 .cer 格式的证书调用我的 API 端点时,我从 .pfx 文件中导出并发送给他们。 他们将得到 403 - Forbidden: Access is denied。您无权使用您提供的凭据查看此目录或页面。
我正在调查可能导致此问题的原因。
- 当我在“个人”下的证书存储中安装/导入 .cer 格式的证书,然后尝试调用我的端点时,我可以看到我的证书不在证书列表中,然后我点击“确定”按钮,我将得到 403 - 禁止:访问被拒绝。
但是
- 当我在 Personal 下的证书存储中安装/导入 .pfx 格式的证书,然后我尝试在浏览器和邮递员上调用我的端点这一次我可以在浏览器上的证书列表中看到我的证书,然后我选择证书并点击按钮,我将成功进入目录,并且我还会在邮递员中得到 200 ok 响应,当然在邮递员中添加 .pfx 格式的证书。
我现在很困惑,第 3 方只接受 .cer 格式的客户证书,据我了解,.pfx 是用于内部组织而不是外部组织。
** 我应该注意我的客户端证书不包含私钥,它只包含公钥。
** 我确定服务器和 IIS 上的所有配置都是正确的。
** 我不确定如何使用 .cer 格式将私钥添加到我的客户端证书中!还是我应该?!
我在这里错过了什么吗!它已经为此工作了 4 天,但仍然没有运气:(
任何人都可以帮助我或指出正确的方向!谢谢:)
这就是我在 ASP.NET Core 3.1 中获取客户端证书的方式:
MyCertificateValidationService.cs
我将我拥有的客户端证书与我从请求中获得的客户端证书进行了比较:
public class MyCertificateValidationService
{
public bool ValidateCertificate(X509Certificate2 clientCertificate)
{
try
{
var _path = @"c:\temp\ClientCertification.cer";
var cert2 = new X509Certificate2(File.ReadAllBytes(_path));
if (clientCertificate.Thumbprint == cert2.Thumbprint)
{
return true;
}
}
catch (System.Exception)
{
throw;
}
return false;
}
我的 API 端点:
[Authorize]
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
[Consumes("application/xml")]
[Produces("application/xml")]
[ProducesResponseType(typeof(DespatchAdvice), (int)HttpStatusCode.OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
[ProducesDefaultResponseType]
[HttpPost("SendDespatch")]
public IActionResult SendDespatch([FromBody] DespatchAdvice despatches)
{
//do something
}
}
Startup.cs
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<MyCertificateValidationService>();
services.AddSingleton<MailHandler>();
services.AddScoped<IDespatch, DespatchRepo>();
services.AddAuthentication(CertificateAuthenticationDefaults.AuthenticationScheme)
.AddCertificate(options => // code from ASP.NET Core sample
{
// https://docs.microsoft.com/en-us/aspnet/core/security/authentication/certauth
options.AllowedCertificateTypes = CertificateTypes.All;
//options.RevocationMode = X509RevocationMode.NoCheck;
options.Events = new CertificateAuthenticationEvents
{
OnCertificateValidated = context =>
{
var validationService =
context.HttpContext.RequestServices.GetService<MyCertificateValidationService>();
if (validationService.ValidateCertificate(context.ClientCertificate))
{
var claims = new[]
{
new Claim(ClaimTypes.NameIdentifier, context.ClientCertificate.Subject, ClaimValueTypes.String, context.Options.ClaimsIssuer),
new Claim(ClaimTypes.Name, context.ClientCertificate.Subject, ClaimValueTypes.String, context.Options.ClaimsIssuer)
};
context.Principal = new ClaimsPrincipal(new ClaimsIdentity(claims, context.Scheme.Name));
context.Success();
}
else
{
context.Fail("invalid cert");
}
return Task.CompletedTask;
}
};
});
services.AddAuthorization();
services.AddCertificateForwarding(options =>
{
options.CertificateHeader = "X-ARR-ClientCert";
options.HeaderConverter = (headerValue) =>
{
X509Certificate2 clientCertificate = null;
if (!string.IsNullOrWhiteSpace(headerValue))
{
byte[] bytes = StringToByteArray(headerValue);
clientCertificate = new X509Certificate2(bytes);
}
return clientCertificate;
};
});
services.AddControllers().AddXmlSerializerFormatters();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseCertificateForwarding();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
private static byte[] StringToByteArray(string hex)
{
int NumberChars = hex.Length;
byte[] bytes = new byte[NumberChars / 2];
for (int i = 0; i < NumberChars; i += 2)
bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16);
return bytes;
}
Program.cs
public class Program
{
public static void Main(string[] args)
{
BuildWebHost(args).Run();
}
public static IWebHost BuildWebHost(string[] args)
=> WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.ConfigureKestrel(options =>
{
var cert = new X509Certificate2(Path.Combine("cert.pfx"), "password");
options.ConfigureHttpsDefaults(o =>
{
o.ServerCertificate = cert;
o.ClientCertificateMode = ClientCertificateMode.RequireCertificate;
});
})
.Build();
}
【问题讨论】:
-
该错误消息与证书无关,它表示您无法列出 IIS 中特定文件夹的内容
-
但是如何使用没有私钥的证书进行客户端身份验证?
-
应该反过来——他们应该给你公钥(在 .cer 或其他文件中),而将私钥留给他们自己。
-
@Evk 那么我怎样才能获得私钥?!因为当我导出 .pfx 时我不能选择是,导出私钥
-
见上文 - 你不应该处理对方的私钥,它们被称为私有是有原因的。
标签: c# asp.net-core ssl client-certificates