11

I have an application deployed to IIS that needs to invoke a SOAP service. It's using WCF from .NET Framework. That SOAP service requires that requests made be authenticated with a client-side certificate which is given at runtime. Admin users of the application can update the used certificate in a back-office. The goal is for autonomy and the certificate lifecycle management be independent from IIS or the underlying system so using the machine certificate store is not an option. Here's the initial code:

var binding = new BasicHttpBinding(BasicHttpSecurityMode.Transport);
var client = new ServiceReference1.myClient(binding, new EndpointAddress(serviceUrl));
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Certificate;
var certificate = new X509Certificate2(certificateBinary, certificatePassword);
client.ClientCredentials.ClientCertificate.Certificate = certificate;

//use the client
var result = client.myMethod(new ServiceReference1.MethodRequest());

certificateBinary is the result of loading a PFX file containing the full certificate chain (client certificate, intermediate and root CAs) and certificatePassword the password used to create that file. But the request is rejected by the server. From looking at Wireshark, it seems only the client-certificate is sent. This is different from what happens if we install the PFX on the machine store which works fine.

So the next step I tried was to install the certificates at runtime. First load them:

X509Certificate2Collection collection = new X509Certificate2Collection();
try {
    collection.Import(ssCertificateFile, ssPassword, X509KeyStorageFlags.UserKeySet | X509KeyStorageFlags.PersistKeySet);
}

Then identify what kind of certificates they are and finally installing them on the current user store:

private static void InstallCertificates(X509Certificate2Collection clientCerts, X509Certificate2Collection intermediateCAs, X509Certificate2Collection RootCAs) {
    using (X509Store personalStore = new X509Store(StoreName.My, StoreLocation.CurrentUser)) {
        personalStore.Open(OpenFlags.ReadWrite);
        personalStore.AddRange(clientCerts);
    }

    using (X509Store intermediateStore = new X509Store(StoreName.CertificateAuthority, StoreLocation.CurrentUser)) {
        intermediateStore.Open(OpenFlags.ReadWrite);
        intermediateStore.AddRange(intermediateCAs);
    }

    using (X509Store trustedCAsStore = new X509Store(StoreName.Root, StoreLocation.CurrentUser)) {
        trustedCAsStore.Open(OpenFlags.ReadWrite);
        trustedCAsStore.AddRange(rootCAs);
    }
}

This fails when installing the root CAs in trusted root certificate authorities (StoreName.Root) with:

System.Security.Cryptography.CryptographicException: The request is not supported.
   at System.Security.Cryptography.X509Certificates.X509Store.Add(X509Certificate2 certificate)
   at System.Security.Cryptography.X509Certificates.X509Store.AddRange(X509Certificate2Collection certificates)
   at OutSystems.NssCertificationExtremeXP.CssCertificationExtremeXP.InstallCertificates(CertificatesClassifier certificates)

so only the client certificate and the intermediate CAs get installed and at runtime apparently this is not enough.

But if I take the exact same code as it is and run it with in a separate C# project, when installing the root CAs there's a confirmation dialog and if I click OK the certificate is installed.

From here and here, it looks like every time we want to install something in the user Trusted Root Certificate Authorities, that prompt happens and it probably is not supported on the context of a non-GUI usage.

The problem is that even if I don't install the root CA in the store, I can successfully call the SOAP service when running this stand-alone app, only when running under IIS this fails.

Does anyone know why this happens and how to solve it?

peterguim
  • 111
  • 3
  • 1
    My guess is, when you are running standalone app its running under your user identity who happen to have access to import cert in Trusted Root authorities while when app is running in IIS, identity of application's app pool's may not have required permissions to import trusted root certs. – Pankaj Kapare May 15 '19 at 17:04
  • @PankajKapare I don't know if that's the only thing happening here, because like I wrote above, when running the stand-alone app _without installing the cert into Trusted Root authorities_ it still works... Still if you have information about how to configure the necessary permissions I would appreciate it. – peterguim May 16 '19 at 08:35
  • 1
    If you import cert in certificate store then you need to grant access on private key of client cert to identity which is being used to run your application's app pool. That's only permission needed here. – Pankaj Kapare May 16 '19 at 10:58
  • I'm installing the certificate, at runtime, in the user store that's running the App Pool. I shouldn't need to give that account permission to access something that it was just installed on that account store, right? – peterguim May 17 '19 at 08:26

0 Answers0