I'm trying to build an AuthorizeAttribute that requires that an HTTPRequest contain a specified client certificate.
I found what I thought was a good series of blog posts on the issues, by Andras Nemes, here:
Using Client Certificates in .NET Part 2 Creating Self-Signed Client Certificates
Using Client Certificates in .NET Part 3 -Installing the Client Certficate
Using Client Certificates in .NET Part 4 - Working with Client Certificates in Code
Using Client Certificates in .NET Part 5 - Working with Client Certificates in a Web Project
Using Client Certificates in .NET Part 6 - Setting up Client Certificates for Local Test Usage
I have a test Web API project running, in VS2015, debugging against a site running on my Local IIS, instead of IIS Express, with https configured and SSL Settings set to allow client certificates.
I'm pretty sure it's setup right, because it works fine for one of the certificates I've created.
My attribute is simple:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class KtRequireClientCertAttribute : AuthorizeAttribute
{
public override void OnAuthorization(System.Web.Http.Controllers.HttpActionContext actionContext)
{
byte[] cert = HttpContext.Current.Request.ClientCertificate.Certificate;
// if the ClientCertificate is empty, pass null
X509Certificate2 suppliedCert = cert.Any() ? new X509Certificate2(cert) : null;
if (suppliedCert != null && isExpectedCert(suppliedCert)
return;
base.OnAuthorization(actionContext);
}
}
My problem - this works fine for one of the certificates I created following Andras's instructions, last week.
ClientCertificate.Certificate is a byte[] containing 808 elements, the X509Certificate2 constructs properly, and my validation logic works as expected.
But with every certificate I try creating today, HttpContext.Current.Request.ClientCertificate.Certificate is empty.
I'm making the certs thus:
MAKECERT.EXE -ic DevRootCertificate.cer -iv DevRootCertificate.pvk -pe -sv testclientcert.pvk -a sha1 -n "CN=testclientcert" -len 2048 -b 01/01/2015 -e 01/01/2030 -sky exchange testclientcert.cer -eku 1.3.6.1.5.5.7.3.2
My test client app is simple:
using (var requestHandler = new WebRequestHandler())
{
var certificate = new X509Certificate2(certificateFile);
requestHandler.ClientCertificates.Add(certificate);
var url = new Uri(baseUrl);
using (var client = new HttpClient(requestHandler){BaseAddress = url})
{
var response = client.GetAsync(endPoint).Result;
response.EnsureSuccessStatusCode();
var content = response.Content.ReadAsStringAsync().Result;
Console.Out.WriteLine(JToken.Parse(content).ToString(Formatting.Indented));
}
}
The question is why does the one certificate get passed to the attribute and the other not?
Additional info:
I'm running the same client against the same server, running on IIS on my local machine. The website is configured to accept, but not to require, client certificates.
The only difference between a test run that works and one that doesn't is which .cer file I load the cert from.
I'm not sure whether the cert that works has or has not a private key, but the file that works is a .cer, not a .pfx.
The file that hasn't worked was a .cer, as well. And I knew it wasn't signed. So I created a signed cert and tried to constuct X509Certificate2() with the .pfx file. I got a 403 error without even hitting the server.