简体   繁体   中英

WCF wsHttpBinding Client Certificate Authentication without using store in client

I have a WCF service registered as such using wsHttpBinding it is hosted in IIS with HTTPS binding to a valid and active certificate:

<?xml version="1.0"?>
<configuration>
  <system.webServer>
    <security>
      <access sslFlags="Ssl, SslNegotiateCert, SslRequireCert"/>
      <authorization>
        <add users="*" accessType="Allow" />
      </authorization>
    </security>
  </system.webServer>
  <system.web>
    <authorization>
      <allow users="*" />
    </authorization>
  </system.web>
  <system.serviceModel>
    <protocolMapping>
      <add scheme="https" binding="wsHttpBinding" />
    </protocolMapping>
    <bindings>
      <wsHttpBinding>
        <binding name="MyNameSpace.WebService_TransportSecurity">
          <security mode="Transport">
            <transport clientCredentialType="Certificate" />
          </security>
        </binding>
      </wsHttpBinding>
    </bindings>
    <behaviors>
      <serviceBehaviors>
        <behavior name="MyNameSpace.WebService_Behaviour">
          <serviceMetadata httpGetEnabled="false" httpsGetEnabled="true" />
          <serviceDebug includeExceptionDetailInFaults="true" />
          <serviceCredentials>
            <clientCertificate>
              <authentication certificateValidationMode="PeerOrChainTrust" revocationMode="NoCheck" />
            </clientCertificate>
          </serviceCredentials>
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <services>
      <service name="MyNameSpace.WebService" behaviorConfiguration="MyNameSpace.WebService_Behaviour">
        <endpoint address=""
                  binding="wsHttpBinding"
                  bindingConfiguration="MyNameSpace.WebService_TransportSecurity"
                  contract="MyNameSpace.IMyServiceContract">
        </endpoint>

        <endpoint address="mex"
                             binding="mexHttpsBinding"
                  contract="IMetadataExchange" />
      </service>
    </services>
  </system.serviceModel>
</configuration>

I'm using .NET 4 so as far as I can tell this is the only binding that works with SSL and client certificate authentication.

I've generated the standard proxy using svcutil and trying to set the certificate (self signed that is also in the server) using it's base64 representation:

X509Certificate2 certificate = new System.Security.Cryptography.X509Certificates.X509Certificate2(System.Convert.FromBase64String("thebase64ofthecertificate"));
if (certificate == null)
{
    return null;
}
IMyServiceContractClient client = new IMyServiceContractClient(new System.ServiceModel.WSHttpBinding
{
    Security = new System.ServiceModel.WSHttpSecurity
    {
        Mode = System.ServiceModel.SecurityMode.Transport,
        Transport = new System.ServiceModel.HttpTransportSecurity
        {

            ClientCredentialType = System.ServiceModel.HttpClientCredentialType.Certificate
        }
    }
},
new System.ServiceModel.EndpointAddress(new System.Uri("https://myserviceendpoint/webservice.svc")));
client.ClientCredentials.ClientCertificate.Certificate = certificate;

But it does not work if I don't also have the certificate in my local computer store, I get this error:

在此处输入图片说明

I'm not an expert in security, ssl or certificates, but is this feasible?

All I'm trying to achieve is to ensure that my service is only called by this code, and thought that using self-signed client certificates that are validated in the server would do, but if they need to be in the store it adds unnecessary complexity to the whole thing!


UPDATE 1:

As suggested by Yacoub Massad exporting certificate's Base64 with X509ContentType.Pkcs12 yields exception:

An unhandled exception of type 'System.Security.Cryptography.CryptographicException' occurred in mscorlib.dll

Additional information: Key not valid for use in specified state.

I'm loading the certificate from store with:

    X509Certificate2 certificate = null;
    X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
    try
    {
        store.Open(OpenFlags.ReadWrite);
        var r = store.Certificates.Find(X509FindType.FindBySubjectName, "LicensingCert", false);
        if (r.Count > 0)
            certificate = r[0];
    }
    catch
    {
        certificate = null;
    }
    finally
    {
        if (store != null)
            store.Close();
    }

    if (certificate == null)
    {
        return null;
    }
    File.WriteAllText(@"C:\tmp\certs\ExportLicensingCert.txt", Convert.ToBase64String(certificate.Export(X509ContentType.Pkcs12)));

UPDATE 2:

Made sure the certificate had been imported with Mark as exportable and it did the trick, I must have skipped that the first time I imported the certificate. Now testing the compiled code on another computer has stopped doing the error. Thank you so much Yacoub Massad for pointing me in the right direction :)

For certificate authentication to work, the client needs the private key to prove its identity to the server. The certificate alone will not work.

Make sure that the X509Certificate2 that you setup the WCF client to use has a corresponding private key.

You need a PFX file that contains the certificate and the private key and you need to import them into a X509Certificate2 object via the Import method.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM