Disclaimer: Questions with titles like this one are common, but no answer has provided a solution for me so I need to ask it anyway (with a new set of parameters).
Problem
A webservice client endpoint is declared in web.config like this:
<behaviors>
<endpointBehaviors>
<behavior name="bankid">
<clientCredentials>
<clientCertificate findValue="FP Testcert 2"
storeLocation="LocalMachine"
storeName="Root"
x509FindType="FindBySubjectName"/>
<serviceCertificate>
<defaultCertificate findValue="Test BankID SSL Root CA v1 Test"
storeLocation="LocalMachine"
storeName="Root"
x509FindType="FindBySubjectName"/>
<authentication certificateValidationMode="None"
revocationMode="NoCheck"
trustedStoreLocation="LocalMachine"/>
</serviceCertificate>
</clientCredentials>
</behavior>
</endpointBehaviors>
</behaviors>
The certificates (client and server certificate) are installed using the "Manage computer certificates" app. They are stored in a .cer file (server certificate) and a .pfx file (client certificate) respectively. They are both stored in "Trusted Root Certification Authorities".
Success
Running the client using the visual studio debug webserver (IIS Express) is successful.
Failure
However, when I try to run it in IIS, I get the error message
Could not establish secure channel for SSL/TLS with authority 'site.com'
Problem solving method
I have tried to create a web api function that lets me know if the server finds the certificates in question. It does. The code looks like
[HttpGet]
[Route("Debug/certs")]
public CertsOutput certs()
{
var certStore = new X509Store(StoreName.Root, StoreLocation.LocalMachine);
certStore.Open(OpenFlags.ReadOnly);
var config = System.Web.Configuration.WebConfigurationManager
.OpenWebConfiguration("~");
var group = ServiceModelSectionGroup.GetSectionGroup(config);
var endPointBehaviors = group.Behaviors.EndpointBehaviors;
var endpointBehavior = endPointBehaviors[0];
var ClientCredential = (ClientCredentialsElement) endpointBehavior[0];
var clientCert = ClientCredential.ClientCertificate;
var serverCert = ClientCredential.ServiceCertificate.DefaultCertificate;
var result = new CertsOutput
{
clientCert = new CertsOutput.Cert
{
FindValue = clientCert.FindValue,
StoreName = clientCert.StoreName.ToString(),
StoreLocation = clientCert.StoreLocation.ToString(),
FindType = clientCert.X509FindType.ToString()
},
serverCert = new CertsOutput.Cert
{
FindValue = serverCert.FindValue,
StoreName = serverCert.StoreName.ToString(),
StoreLocation = serverCert.StoreLocation.ToString(),
FindType = serverCert.X509FindType.ToString()
}
};
return result;
}
public class CertsOutput
{
public Cert clientCert { get; set; }
public Cert serverCert { get; set; }
public class Cert
{
public string FindValue { get; set; }
public string StoreName { get; set; }
public string StoreLocation { get; set; }
public string FindType { get; set; }
public string Expiration => Certificate?.GetExpirationDateString()
?? "Cant find cert";
X509Certificate _certificate = null;
private X509Certificate Certificate
{
get
{
if (_certificate != null)
return _certificate;
StoreName storeNameEnum;
switch(StoreName)
{
case "My":
storeNameEnum = System_StoreName.My;
break;
case "Root":
storeNameEnum = System_StoreName.Root;
break;
default:
throw new Exception("Unknown store name: " + StoreName);
}
StoreLocation storeLocationEnum;
switch(StoreLocation)
{
case "LocalMachine":
storeLocationEnum = System_StoreLocation.LocalMachine;
break;
case "CurrentUser":
storeLocationEnum = System_StoreLocation.CurrentUser;
break;
default:
throw new Exception("Unknown store location: " + StoreLocation);
}
var certStore = new X509Store(storeNameEnum, storeLocationEnum);
certStore.Open(OpenFlags.ReadOnly);
var certCollection = certStore.Certificates.Find
(X509FindType.FindBySubjectName, FindValue, validOnly:false);
certStore.Close();
var result = certCollection[0];
_certificate = result;
return result;
}
}
}
}
Even if I am running this on IIS, I get an output like this (using console.log in chrome):
So the certs are clearly visible to the IIS, although they are stored in "Trusted Root Certification Authorities". The only way expire dates could have been retrieved is using the store.
Enabling CAPI2 log in event log might give you the answer why it
Could not create SSL/TLS secure channel.
CAPI2 log is by default disabled. When you enable it try to make the request again. There should be some error events that will contain helpful info about the cause.I would also check (and maybe change) some things:
LocalMachine/My
(Personal) store. Root CA certificates should be placed in Trusted Root Certification Authorities store (you got that right) and intermediate CA certificates in Intermediate Certification Authorities storecertlm.msc
tool.PrivateKey
property, sign some hello world data with it etc.