none
SSL with client certificate

    Question

  •  

    Hello!

     

    I've got a problem with a WCF client consuming an older WSE3.0 service.

    The WSE3.0 service requires SSL with client certificates as transport level security and uses an UsernameToken for message level security.

    The problem is that I can't create an SSL session with client certificate, I always get following error message:

     

    "Could not establish trust relationship for the SSL/TLS secure channel with authority '192.168.1.32'."

     

    I was using an BasicHttpBinding with TransportWithMessageCredential, specifying the certificate as in the configuration file below:

     

    <?xml version="1.0" encoding="utf-8" ?>

    <configuration>

    <configSections>

    </configSections>

    <system.serviceModel>

    <behaviors>

    <endpointBehaviors>

    <behavior name="ConsumerBehavior">

    <clientCredentials>

    <clientCertificate findValue="69 b4 c3 17 00 af 00 00 00 f3" x509FindType="FindBySerialNumber" />

    </clientCredentials>

    </behavior>

    </endpointBehaviors>

    </behaviors>

    <bindings>

    <basicHttpBinding>

    <binding name="DefaultBinding" closeTimeout="00:01:00" openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00" allowCookies="false" bypassProxyOnLocal="false"  hostNameComparisonMode="StrongWildcard"

    maxBufferSize="65536" maxBufferPoolSize="524288" maxReceivedMessageSize="65536"

    messageEncoding="Text" textEncoding="utf-8" transferMode="Buffered"

    useDefaultWebProxy="true">

    <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"

    maxBytesPerRead="4096" maxNameTableCharCount="16384" />

    <security mode="TransportWithMessageCredential">

    <transport clientCredentialType="Certificate" proxyCredentialType="None" realm="" />

    <message clientCredentialType="UserName" algorithmSuite="Default" />

    </security>

    </binding>

    </basicHttpBinding>

    </bindings>

    <client>

    <endpoint address=https://192.168.1.32/SampleWebserviceProviderV2.4/PersonLookupV1.asmx

    behaviorConfiguration="ConsumerBehavior" binding="basicHttpBinding"

    bindingConfiguration="DefaultBinding" contract="PersonLookupV1"

    name="ConsumerEndpoint" />

    </client>

    </system.serviceModel>

    </configuration>

     

    What am I doing wrong?

    Monday, September 17, 2007 1:45 PM

Answers

  • When we say, SecurityMode = TransportWithMessageCredential, a secure transport (for example, HTTPS) provides integrity, confidentiality, and authentication while SOAP message security provides client authentication. 

     

    <security mode="TransportWithMessageCredential">

    <transport clientCredentialType="Certificate" proxyCredentialType="None" realm="" />

    <message clientCredentialType="UserName" algorithmSuite="Default" />

    </security>

     

    is equivalent to

     

    <security mode="TransportWithMessageCredential">

    <message clientCredentialType="UserName" algorithmSuite="Default" />

    </security>

     

    (transport credentials are essentially ignored and transport clientCredentialType is defaulted to None.).

     

    This is the reason, the client certificate is not being sent during SSL handshake.  This scenario is not possible with standard binding. I think this can be achieved through custom binding.

     

    Monday, September 24, 2007 6:29 PM
  • Thanx for the explanation, at least now I know the source of the problem.

     

    Regarding the solution, you're right, I've made a custom binding, reusing however the binding elements from the BasicHttpBinding and it works!

     

    Here's the sample code I used:

     

    BasicHttpBinding bbinding = new BasicHttpBinding(BasicHttpSecurityMode.TransportWithMessageCredential);

    BindingElementCollection bec = bbinding.CreateBindingElements();

    TransportSecurityBindingElement tsp = bec.Find<TransportSecurityBindingElement>();

    HttpsTransportBindingElement httpsBinding = bec.Find<HttpsTransportBindingElement>();

    TextMessageEncodingBindingElement encoding = bec.Find<TextMessageEncodingBindingElement>();

    httpsBinding.RequireClientCertificate = true;

     

    CustomBinding binding = new CustomBinding(tsp, encoding, httpsBinding);

    EndpointAddress endpoint = new EndpointAddres("https://192.168.1.32/SampleWebserviceProviderV2.4/PersonLookupV1.asmx");

    X509Certificate2 cert = null;

    X509Store store = new X509Store(StoreLocation.CurrentUser);

    store.Open(OpenFlags.ReadOnly);

    X509Certificate2Collection certCollection = store.Certificates;

    foreach (X509Certificate2 c in certCollection)

    {

    if(c.SerialNumber != "69 b4 c3 17 00 00 00 00 00 83")

    {

    cert = (X509Certificate2)c;

    break;

    }

    }

    ChannelFactory<PersonLookup> proxy = new ChannelFactory<PersonLookup>(binding, endpoint);

    proxy.Credentials.UserName.UserName = "*****";

    proxy.Credentials.UserName.Password = "++++";

    proxy.Credentials.ClientCertificate.Certificate = cert;

    proxy.Open();

    GetPersonResponse response = proxy.CreateChannel().GetPerson(new GetPersonRequest("Smith"));

     

    Tuesday, September 25, 2007 10:54 AM

All replies

  • I guess the identityCheck which compares the subject name of the ssl cert with the host-name in the URL is failing.

    Try replacing the IP address (192.168.1.32)  in the endpoint address with the subject name of the ssl cert.

     

     

     

    Friday, September 21, 2007 5:51 AM
  • Also the subject name of the cert should be either "localhost" or fully qualified machine name.

     

    Friday, September 21, 2007 4:19 PM
  • I'll try, but I'm not really optimistic, because I've already tried the scenario with the same client certificate and Transport security only and it works perfectly, so the endpoint resolution and certifcate subject are not really an issue...

     

    Monday, September 24, 2007 5:51 AM
  • The server certificate subject is structured as follows:

    CN = server FQDN

    O = Company name

    L = Location

    C = DE

     

    The client certificate subject is:

    CN = WebServiceTestConsumer

    O = Company name

    L = Location

    C = DE

     

     

    Everything works perfect with BasicHttpSecurityMode.Transport, but when I switch to BasicHttpSecurityMode.TransportWithMessageCredential i get the Error I've already described.

    It looks like the client certificate is not being sent on the initial SSL handshaking, which I really don't understand since the on the Transport only handshakinhg works allright...

     

    Monday, September 24, 2007 8:33 AM
  • When we say, SecurityMode = TransportWithMessageCredential, a secure transport (for example, HTTPS) provides integrity, confidentiality, and authentication while SOAP message security provides client authentication. 

     

    <security mode="TransportWithMessageCredential">

    <transport clientCredentialType="Certificate" proxyCredentialType="None" realm="" />

    <message clientCredentialType="UserName" algorithmSuite="Default" />

    </security>

     

    is equivalent to

     

    <security mode="TransportWithMessageCredential">

    <message clientCredentialType="UserName" algorithmSuite="Default" />

    </security>

     

    (transport credentials are essentially ignored and transport clientCredentialType is defaulted to None.).

     

    This is the reason, the client certificate is not being sent during SSL handshake.  This scenario is not possible with standard binding. I think this can be achieved through custom binding.

     

    Monday, September 24, 2007 6:29 PM
  • Thanx for the explanation, at least now I know the source of the problem.

     

    Regarding the solution, you're right, I've made a custom binding, reusing however the binding elements from the BasicHttpBinding and it works!

     

    Here's the sample code I used:

     

    BasicHttpBinding bbinding = new BasicHttpBinding(BasicHttpSecurityMode.TransportWithMessageCredential);

    BindingElementCollection bec = bbinding.CreateBindingElements();

    TransportSecurityBindingElement tsp = bec.Find<TransportSecurityBindingElement>();

    HttpsTransportBindingElement httpsBinding = bec.Find<HttpsTransportBindingElement>();

    TextMessageEncodingBindingElement encoding = bec.Find<TextMessageEncodingBindingElement>();

    httpsBinding.RequireClientCertificate = true;

     

    CustomBinding binding = new CustomBinding(tsp, encoding, httpsBinding);

    EndpointAddress endpoint = new EndpointAddres("https://192.168.1.32/SampleWebserviceProviderV2.4/PersonLookupV1.asmx");

    X509Certificate2 cert = null;

    X509Store store = new X509Store(StoreLocation.CurrentUser);

    store.Open(OpenFlags.ReadOnly);

    X509Certificate2Collection certCollection = store.Certificates;

    foreach (X509Certificate2 c in certCollection)

    {

    if(c.SerialNumber != "69 b4 c3 17 00 00 00 00 00 83")

    {

    cert = (X509Certificate2)c;

    break;

    }

    }

    ChannelFactory<PersonLookup> proxy = new ChannelFactory<PersonLookup>(binding, endpoint);

    proxy.Credentials.UserName.UserName = "*****";

    proxy.Credentials.UserName.Password = "++++";

    proxy.Credentials.ClientCertificate.Certificate = cert;

    proxy.Open();

    GetPersonResponse response = proxy.CreateChannel().GetPerson(new GetPersonRequest("Smith"));

     

    Tuesday, September 25, 2007 10:54 AM
  • @CGR, Could you post the WCF and client config file?

    Thanks




    • Edited by aalic Tuesday, July 01, 2014 2:13 PM
    Tuesday, July 01, 2014 2:12 PM