Reputation: 1322
I have an Asp.net API website which does custom client certificate validation. When hosting this website on IIS 10, I get the following from failed request logs when I call my API.
A certificate chain processed, but terminated in a root certificate which is not trusted by the trust provider.
My web.config has
<configuration>
<system.webServer>
<access sslFlags="Ssl, SslRequireCert" />
</system.webServer>
and in applicationHost.config I have
<section name="access" overrideModeDefault="Allow" />
What am I missing here? How do I configure IIS to just pass through the certificate and not validate it ?
The reason I want to do this is because, this is a test environment and I want to trust all clients who calls my API with their self-signed certificates. I will internal do the validation of the certificate inside my API.
Note: I hosted the same website on Azure AppService and set "Incoming client certificates" to ON. It worked like a charm. So, what is the difference when I host it on my machine IIS ?
Upvotes: 4
Views: 6567
Reputation: 443
We use Client Certificates to validate hardware devices connecting to our API. For context, our devices are provisioned with an SSL cert at manufacture, and that cert is self signed by us. When a device out in the wild attempts to connect to our API, we handle the client certificate validation within the .NET API application itself.
This requires the following IIS SSL settings, and also a manual step to rebind the SSL binding (which we do for a very specific technical limitation).
So firstly, within the web.config file we have this config:
<security>
<access sslFlags="Ssl" />
</security>
If we add the SslNegotiateCert or SslRequireCert sslFlags, then IIS attempts to validate the client certificate before our application code is even called. So we set only the Ssl flag.
Secondly, in the SSL settings of the IIS site we set:
Require SSL [x]
Client Certificate:
[x] Ignore
[ ] Accept
[ ] Require
So essentially we aren't asking IIS to negotiate the client certificates on our behalf.
The final configuration change we make is to Enable "Negotiate Client Certificate" on the SSL binding. By default, when you create an SSL binding in IIS the "Negotiate Client Certificate" property is set to false.
From my understanding this means that IIS will not negotiate client certificates on the initial TLS negotiation. What would happen is when client certificates are required, a TLS renegotiation is triggered, and the server would request a client certificate from the client.
In our case, our devices pass the client certificate on the initial request, and will not handle a TLS renegotiation. So, by Enabling "Negotiate Client Certificate" then client certificates can be passed in the initial request.
So rebind the SSL binding takes some command line magic to find the current binding, delete it, and readd the binding this time with "Negotiate Client Certificate" enabled.
Step 1 - Find your SSL binding:
Run the following command in a CMD terminal:
netsh http show sslcert > sslcerts.txt
This will push all details of your current SSL bindings into sslcerts.txt
The file will looks like the following:
Hostname:port : yourhostname:443
Certificate Hash : your_certificate_hash
Application ID : {your_applicationID_Guid}
Certificate Store Name : My
Verify Client Certificate Revocation : Enabled
Verify Revocation Using Cached Client Certificate Only : Disabled
Usage Check : Enabled
Revocation Freshness Time : 0
URL Retrieval Timeout : 0
Ctl Identifier : (null)
Ctl Store Name : (null)
DS Mapper Usage : Disabled
Negotiate Client Certificate : Disabled
Note, your sslcerts.txt file will contain many instances of these bindings. You need to find the correct one for the application/site you are working with.
Note also the above output shows "Negotiate Client Certificate : Disabled"
Step 2 - Delete the current binding
Run the following command to delete the current binding
netsh http delete sslcert hostnameport=yourhostname:443
This will delete the SSL binding for the site.
Step 3 - Rebind the SSL with "Negotiate Client Certificate" enabled
Run the following command at the CMD prompt:
netsh http add sslcert hostnameport=yourhostname:443 certhash=your_certificate_hash appid={your_applicationID_Guid} certstorename=MY verifyclientcertrevocation=Enable VerifyRevocationWithCachedClientCertOnly=Disable UsageCheck=Enable clientcertnegotiation=Enable
Note here you are filling in the properties of the binding from the details you retrieved in sslcerts.txt, except you are setting clientcertnegotiation=Enable
Now we have an IIS Application which will negotiate for a client certificate up front, but it will not validate it, and allow us to validate it in code.
We then use an AuthorizationFilterAttribute to grab the client certificate and validate it based on our rules.
public class ValidateDeviceClientCertificateAttribute : AuthorizationFilterAttribute
{
public override void OnAuthorization(HttpActionContext actionContext)
{
X509Certificate2 cert = actionContext.Request.GetClientCertificate();
// Validation rules here i.e. check Hash of the signing cert, does it match your accepted value?
}
}
In our validation we have a known Intermediate CA that we use to sign our device certificates, so we check to ensure that the client certificate was signed by that Intermediate Cert, or at least one of our device signing intermediate certificates.
Upvotes: 9