TLDR: It may be because the certificate mappings are saved at the sub-application level, when in fact they are expected at the site or server level.
The setup is simple enough:
Yes, we have set Require SSL with Require Client Certificates for the sub-application.
SSL Settings for the sub-application
Yes, we have disabled the Anonymous authentication for that sub-application.
Authentication settings for the sub-application
Yes, we did install and enable IIS Client Certificate Mapping Authentication, disabling the Client Certificate Mapping Authentication (see the difference in the details section).
Use the Configuration Editor, Section drop-down, selecting system.webServer > security > authentication > clientCertificateMappingAuthentication and iisClientCertificateMappingAuthentication.
Yes, we did add the certificate mappings accordingly, as per documentation.
IIS client certificate mappings for authenticating the requests in the sub-application
And yes, both the server and client certificates are valid, they have their private keys on their respective machines, their usage flags are matching the intended purposes, and they are issued by Certificate Authorities (CAs) trusted by both IIS and client(s).
Everything is by the book, the client certificate is valid, has a private key, is trusted by both client and IIS etc. Yet accessing the sub application results in a 401-Unauthorized response:
The authentication error page on the client
Moreover, we take some Failed Request Tracing, and we see that the IIS Web Core module is sending a 401.2 HTTP response status code (a Logon failed due to server configuration, according to http://linqto.me/httpresponsecodes).
The Failed Request Tracing (FREB) log illustrating the error
The immediate cause is that all authentication modules were notified by IIS, but none of them managed to determine a user. The Authenticate stage is over, and we still don’t have a user set. Not even the Anonymous one; which is, of course, expected, since we disabled the Anonymous authentication at the sub-application level. With the User property being NULL, IIS ends the request sending the 401-Unauthorized.
But why didn’t the certificate mapping module determine the user? I mean the certificate is good, everything checks, right?
Well, there is a glitch with the configuration. More exactly, with the location level of where the certificate mappings and configuration are persisted.
We have enabled the IIS Client Certificate Mapping Authentication at the sub-application level, because this is where we needed it; not at the site level.
So the IIS Manager’s Configuration Editor happily obliged, resulting in a configuration like below in applicationHost.config:
NOT GOOD
<configuration> <location path="Client-Cert-Mapping-IIS-App"> <system.webServer> <security> <authentication> <clientCertificateMappingAuthentication enabled="false" /> </authentication> <access sslFlags="None" /> </security> </system.webServer> </location> <location path="Client-Cert-Mapping-IIS-App/Sub-Application"> <system.webServer> <security> <authentication> <anonymousAuthentication enabled="false" /> <iisClientCertificateMappingAuthentication enabled="true" manyToOneCertificateMappingsEnabled="false"> <oneToOneMappings> <add userName="Pingu-IIS.Local-User" password="[enc:IISCngProvider:Spo...Zqk=:enc]" certificate="MIIFwjDwAwggEKAo...O5U=" /> <add userName="Pingu-IIS.ARR-Acnt" password="[enc:IISCngProvider:gha...Yws=:enc]" certificate="MIIFwjDANBgkqhki...9DI=" /> </oneToOneMappings> </iisClientCertificateMappingAuthentication> </authentication> <access sslFlags="Ssl, SslNegotiateCert, SslRequireCert" /> </security> </system.webServer> </location> </configuration>
But according to the documentation for this feature, IIS Client Certificate Mapping Authentication:
The <iisClientCertificateMappingAuthentication> element of the <authentication> element can be configured at the server and site level.
So “at the server and site level”, huh?
In the above configuration example, it is enough that we move the <iisClientCertificateMappingAuthentication> element at the site level. The site level will still allow Anonymous users, as needed. It will still allow plain HTTP. It will not require a client certificate, but it would be able to map it to an account. This mapping ability will be inherited at sub-application level.
It is only at sub-application level that we disable Anonymous authentication, and we change the SSL/TLS behavior so that a client certificate is requested.
GOOD ONE
<configuration> <location path="Client-Cert-Mapping-IIS-App"> <system.webServer> <security> <authentication> <clientCertificateMappingAuthentication enabled="false" /> <iisClientCertificateMappingAuthentication enabled="true" manyToOneCertificateMappingsEnabled="false"> <oneToOneMappings> <add userName="Pingu-IIS.Local-User" password="[enc:IISCngProvider:Spo...Zqk=:enc]" certificate="MIIFwjDwAwggEKAo...O5U=" /> <add userName="Pingu-IIS.ARR-Acnt" password="[enc:IISCngProvider:gha...Yws=:enc]" certificate="MIIFwjDANBgkqhki...9DI=" /> </oneToOneMappings> </iisClientCertificateMappingAuthentication> </authentication> <access sslFlags="None" /> </security> </system.webServer> </location> <location path="Client-Cert-Mapping-IIS-App/Sub-Application"> <system.webServer> <security> <authentication> <anonymousAuthentication enabled="false" /> </authentication> <access sslFlags="Ssl, SslNegotiateCert, SslRequireCert" /> </security> </system.webServer> </location> </configuration>
Let’s now look at Failed Request Tracing (FREB) log, for a successfully served request.
(I guess this feature should rather be named Request Processing Pipeline Trace, or Pipeline Execution Trace, since you can collect for success too, not only for failures.)
A FREB pipeline execution trace for a successful request
We notice that:
There is a common misconception on how these modules are working. People think that these modules are performing Certificate Authentication as in “the request user is determined by inspecting the client certificate”. Which is wrong; such functionality, technically possible, would require custom code.
In fact, how the Client Certificate Mapping modules of IIS work a bit different. They would take the client certificate and try to map it to a user account; if successful, that account will be considered as the request user.
Now IIS has 2 modules that are performing the so-called Client Certificate Authentication.
The 2 client certificate mapping features in IIS
They differ in where they look for [certificate <-> account] mappings.
Failing to map the certificate sent by the client to a user account, local or in Active Directory, would mean that we can’t determine the request user from the client certificate.
Other authentication modules, if enabled or so configured, may (or may not) determine the request user.
But keep in mind that after the Authentication stage, in IIS request processing pipeline, we MUST have a user, even if that user is Anonymous (UserName field is empty-string).
If the User field is NULL after the Authentication stage, the IIS Web Core module would stop the request and respond 401-Unauthorized (more precisely, 401.2 = Logon failed due to server configuration).
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.