WCF: How to send two different client certificates as credentials at both Transport and Message Level

WCF: How to send two different client certificates as credentials at both Transport and Message Level

 

Requirement:

Transport Security Details

A secure transport layer via mutually authenticated SSL/TLS (MASSL) is required.

Mutually authenticated SSL requires the use of a client certificate to be provided to the server and validated by the server. If a request is made and a client certificate is unavailable, the request will not be accepted.

 

Message Security Details

Web service SOAP message protocol will be used for communication.

In addition to transport tier, message signing will be applied to guarantee the authenticity and non-repudiation of the message and integrity of the payload.

Requirements for request (inbound) message signing:

The message body should be signed.

 

The message should be signed with the client’s message signing key which is distinct from the client’s client certificate used for transport layer.

The signing certificate (binary security token [BST]) should be embedded in the message header in the <wsse:BinarySecurityToken> element.

The embedded BST should be signed.

 

Option 1:

Custom binding with - certificate over transport.

However this binding does not sign the body as needed for our scenario.

Option 2:

Binding:

<customBinding>

<binding name="NewBinding0">

<textMessageEncoding />

<security authenticationMode="MutualCertificateDuplex" requireDerivedKeys="false"

securityHeaderLayout="Lax" messageProtectionOrder="SignBeforeEncrypt"

requireSecurityContextCancellation="false">

<secureConversationBootstrap />

</security>

<httpsTransport requireClientCertificate="true" />

</binding>

</customBinding>

 

Reference.cs file:

[System.ServiceModel.OperationContractAttribute(Action="https://tempuri.org/IService1/GetData",

ReplyAction="https://tempuri.org/IService1/GetDataResponse", ProtectionLevel = ProtectionLevel.Sign)]

string GetData(int value);

 

Or it can be set inside the code as:

ServiceReference1.Service1Client proxy = new ServiceReference1.Service1Client();

proxy.Endpoint.Contract.ProtectionLevel = System.Net.Security.ProtectionLevel.Sign;

  1. It does allow use to sign the body.
  2. Make sure client present the client cert to get authenticated
  3. I tried to specify the endpoint behavior to set a client cert and look like the same is used for both SSL handshake and soap envelope level.
  4. If I try to specify it via code (proxy.clientCredential.ClientCertificate), now code one will be used for both SSL and soap envelop level and looks like we don’t care what is specified inside the end point behavior anymore.

 

Left over

How to tell WCF to use the separate client cert at SSL handshake level.

 

 

Solution:

Custom binding with custom client credential

 <system.serviceModel>

<bindings>

<customBinding>

<binding name="NewBinding0">

<textMessageEncoding />

<security authenticationMode="MutualCertificateDuplex" requireDerivedKeys="false"

securityHeaderLayout="Lax" messageProtectionOrder="SignBeforeEncrypt"

requireSecurityContextCancellation="false">

<secureConversationBootstrap />

</security>

<httpsTransport requireClientCertificate="true" />

</binding>

</customBinding>

</bindings>

 

<client>

<endpoint address="https://saurabh31.fareast.corp.microsoft.com/Https/WcfService1/service1.svc"

binding="customBinding" behaviorConfiguration="cliBeh"

bindingConfiguration="NewBinding0" contract="ServiceReference1.IService1"

name="WSHttpBinding_IService1">

<identity>

<dns value="Saurabh Somani"/>

</identity>

</endpoint>

</client>

 

<behaviors>

<endpointBehaviors>

<behavior name="cliBeh">

<CustomClientCreds>

<clientCertificate findValue="MSIT Enterprise CA 3"

x509FindType="FindByIssuerName"

storeLocation="LocalMachine"

storeName="My" />

<ClientMessageCertificate findValue="MSIT Enterprise CA 3"

x509FindType="FindByIssuerName"

storeLocation="CurrentUser"

storeName="My" />

</CustomClientCreds>

</behavior>

</endpointBehaviors>

</behaviors>

 

<extensions>

<behaviorExtensions>

<add name="CustomClientCreds" type="CustomWcfExtension.MyClientCredentialsElement, CustomWcfExtension, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>

</behaviorExtensions>

</extensions>

</system.serviceModel>

 

Where the clientCertificate is the cert to be used at the transport layer and the ClientMessageCertificate element will be used at the Message layer.

 

Below is the code of these classes (link updated).

https://1drv.ms/f/s\!ArgnWb8iHXB6lxTzzOEtPWk1Wkk8

 

I hope this help !

 

Thanks

Saurabh Somani