Check for rogue certificates. In my case there was one non–self-signed certificate in the Trusted Root Certification Authorities certificate store. Our admins have had added this government-issued certificate to the entire forest (via Group Policy) without proper testing beforehand.
I have put here some general troubleshooting guidelines in order to help people to solve such problems faster than 3 weeks as it was in my case.
AD Group Policies
If you are not familiar with Group Policies use the command below to get human-readable report (best viewed by IE):
GPRESULT /H C:\GPReport.html
To view more details use
RSOP.MSC as per @twconnell recommendation. RSOP.MSC shows effective snapshot of both local and group polices applied. Verify security at
Computer Configuration > Windows Settings > Security Settings > Local Policies > User Rights Assignment. In my case I have thoroughly read this excellent SO answer and followed every link there. Then I have checked access rights on the each file, folder and app pool. There was nothing wrong there.
Look at the IIS logs. There should be more details than just
403 code provided to the client. In my case there was
403 16 code there.
Look at this troubleshooting guide. In my case it was that short:
Client certificate is untrusted or invalid
Certificate Revocation List (CRL)
My certificate CA was an AD-provided company CA, so I have initially thought that there might be some problem with certificate revocation list (CRL). Best practical guide is this one. To understand further details read this, this and finally this monster. In my case there were no problems with CRL as I have followed this, this and this guides beforehand. Result:
netsh http show sslcert ipport=0.0.0.0:443
Verify Client Certificate Revocation : Disabled
Verify Revocation Using Cached Client Certificate Only : Disabled
Usage Check : Disabled
Revocation Freshness Time : 0
URL Retrieval Timeout : 0
Ctl Identifier : (null)
Ctl Store Name : (null)
DS Mapper Usage : Disabled
Negotiate Client Certificate : Enabled
To debug SSL use
openssl tool from client side (given myserver uses common 443 port for https):
openssl s_client -connect myserver:443
In my case
acceptable CAs for client certificates was initially empty so I have set
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\SendTrustedIssuerList DWORD value to 1 and rebooted server as per this guide (near bottom). Note that the empty list of acceptable CAs for client certificates IIS problem may also occur due to huge list of CA as per this SF question.
When you have a list of
acceptable CAs for client certificates in the
openssl output you can compare it with contents of configured certificate stores at your server. In my case there were only one certificate there belonging to
Trusted Root Certification Authorities. So after a few days of investigation that lead me to this ms-support article #2802568. Effectively one improperly placed non-self-signed certificate doomed entire auth chain.
SSL client authentication is a mess. It's good because it's absolutely secure, but it's crappy because it's difficult both to set up and to maintain. Unless you have a really good reason, go with shared-key authentication instead (i.e. "password"). You'll save time and money. SSL authentication is good for when security trumps cost by a fairly wide margin.