Trustchain error NetTcpBinding with message (username) security - wcf

After a week(!) of searching, I hope someone is able to tell me what I'm doing wrong.
I keep on getting the following error message:
The X.509 certificate CN=localhost chain building failed. The certificate that was used has a trust chain that cannot be verified. Replace the certificate or change the certificateValidationMode.
To start from the beginning. This is my code/config serverside:
ServiceHost svcHost = new ServiceHost(typeof(TestService));
// Enable metadata publishing.
ServiceMetadataBehavior smb = svcHost.Description.Behaviors.Find<ServiceMetadataBehavior>();
if (smb == null)
smb = new ServiceMetadataBehavior();
smb.HttpGetEnabled = true;
smb.MetadataExporter.PolicyVersion = PolicyVersion.Policy15;
svcHost.Description.Behaviors.Add(smb);
ServiceDebugBehavior sdb = svcHost.Description.Behaviors.Find<ServiceDebugBehavior>();
if (sdb == null) { sdb = new ServiceDebugBehavior(); svcHost.Description.Behaviors.Add(sdb); }
sdb.IncludeExceptionDetailInFaults = true;
// Meta data servicepoint
svcHost.AddServiceEndpoint(ServiceMetadataBehavior.MexContractName, MetadataExchangeBindings.CreateMexHttpBinding(), "mex");
// Credentials
CustomUserNamePasswordValidator clientAuthenticationValidator = new TCustomUserNamePasswordValidator();
svcHost.Credentials.UserNameAuthentication.UserNamePasswordValidationMode = UserNamePasswordValidationMode.Custom;
svcHost.Credentials.UserNameAuthentication.CustomUserNamePasswordValidator = (UserNamePasswordValidator) clientAuthenticationValidator;
// Certificate
svcHost.Credentials.ServiceCertificate.Certificate = new X509Certificate2("TestServer.pfx", "password");
svcHost.Credentials.IssuedTokenAuthentication.CertificateValidationMode = X509CertificateValidationMode.ChainTrust;
svcHost.Credentials.IssuedTokenAuthentication.RevocationMode = X509RevocationMode.None;
svcHost.Credentials.IssuedTokenAuthentication.TrustedStoreLocation = StoreLocation.LocalMachine;
// Binding
NetTcpBinding netTcpBinding = new NetTcpBinding();
netTcpBinding.Security.Mode = SecurityMode.Message;
netTcpBinding.Security.Transport.ProtectionLevel = System.Net.Security.ProtectionLevel.EncryptAndSign;
netTcpBinding.Security.Message.ClientCredentialType = MessageCredentialType.UserName;
netTcpBinding.ReliableSession.Enabled = true;
svcHost.AddServiceEndpoint(typeof(ITestService), netTcpBinding, "");
svcHost.Open();
With the following app.config file:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<services>
<service name="TestService">
<host>
<baseAddresses>
<add baseAddress="http://localhost:8080/" />
<add baseAddress="net.tcp://localhost:9595" />
</baseAddresses>
</host>
</service>
</services>
</system.serviceModel>
</configuration>
The following commands I used to get the certificates:
Root CA:
makecert.exe -n "CN=TestCA" -r -pe -a sha512 -len 4096 -cy authority -sr LocalMachine -ss Root -sv TestCA.pvk TestCA.cer
pvk2pfx.exe -pvk TestCA.pvk -spc TestCA.cer -pfx TestCA.pfx -po aaaaa
Service:
makecert.exe -n "CN=localhost" -iv TestCA.pvk -ic TestCA.cer -pe -a sha512 -len 4096 -b 12/14/2014 -e 01/01/2020 -sky exchange -eku 1.3.6.1.5.5.7.3.1 -sv TestServer.pvk TestServer.cer
pvk2pfx.exe -pvk TestServer.pvk -spc TestServer.cer -pfx TestServer.pfx -po aaaaa
Where I imported TestCA.cer in Trusted Root CA and, as shown in code, the pfx file is used as servicecertificate.
As far as I understood, I do not have to give a certificate for the client, since it uses username credentials to authenticate itself, where the servicecertificate is used for ssl.
As said, as soon as I start up the client and call a function, I get the above exception.
I already tried to attach the TestServer certificate to the 9595 port with netsh. I run both client and service on the same computer.
I truly hope someone can help.

The issues is most likely caused by a self-signed certificate that is not “trusted” by the client. In order to accommodate this scenario, you have a few options:
Import the self-signed certificate into the client machine’s
“Trusted People” store.
Follow the MSDN guidance to Create and Install Temporary Certificates in WCF for Message Security During Development
Update the client software to bypass the certificate verification check as defined here.

Related

wcf NetworkCredential with empty user name and password

I created a WCF service and the security mode has been set to Transport and ClientCredentialType is Windows. Below is my client side code:
NetTcpBinding binding = new NetTcpBinding();
binding.Security.Mode = SecurityMode.Transport;
binding.Security.Transport.ClientCredentialType = TcpClientCredentialType.Windows;
binding.Security.Transport.ProtectionLevel = System.Net.Security.ProtectionLevel.EncryptAndSign;
ChannelFactory<IServices> factory factory = new ChannelFactory<IServices>(binding, service);
NetworkCredential credential = factory.Credentials.Windows.ClientCredential;
credential.UserName = string.Empty;
credential.Password = string.Empty;
IServices connect = factory.CreateChannel();
bResult = connect.IsServerOnline();
Server config:
<system.serviceModel>
<bindings>
<netTcpBinding>
<binding name="tcpConSecure" />
<security mode="Transport">
<transport clientCredentialType="Windows" protectionLevel="EncryptAndSign" />
</security>
</binding>
</netTcpBinding>
</bindings>
<services>
<service name="TestService.Services">
<endpoint address="tcp" behaviorConfiguration="EndpointBe" binding="netTcpBinding" bindingConfiguration="tcpConSecure" contract="TestServiceInterface.IServices" />
</service>
</services>
</system.serviceModel>
In theory, I should input the correct windows account name and password, but during test, I found I could set the UserName and Password as empty, and channel still could be created. Why?
Client and Server are not on the same machine, but they are in same domain. The logon account of Client machine could login Server machine. In this case, I could use empty user name and password to create connection and call WCF service.
The channel factory created by the client has nothing to do with the WCF server. Even if the server closes the client, the channel factory can be created successfully, but an error will occur when the method is called.
If you only create a channel, then you set the Username and Password in the credential has nothing to do with the WCF Service. Only when the call is made, the client will pass the Username and Password to the server, and the value of Username and Password will be verified.
This is the explanation in the Microsoft documentation:
UPDATE:
There is another possibility that can cause this problem. If the client and server are on the same machine, the client does not need to provide windows credentials.

WS-Security from soapUI to WCF - binding & configuration

I need to create a WCF client to call a service that I have no control over and we have been given a wsdl only (with schemas). The web service uses the X.509 certificate with the WS-Security specification version 1.0
The web service provider has shared raw xml of the soap message to highlight the ws-security header. Using the soapUI, I have been able to create the exact same wsse-Security header as shown below:
<soapenv:Envelope xmlns:oas="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:oas1="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xd="http://www.w3.org/2000/09/xmldsig#">
<soapenv:Header>
<wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<ds:Signature Id="SIG-32" xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:SignedInfo>
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments"/>
<ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
<ds:Reference URI="#id-31">
<ds:Transforms>
<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#">
<InclusiveNamespaces PrefixList="oas oas1 urn urn1 urn2 urn3 xd" xmlns="http://www.w3.org/2001/10/xml-exc-c14n#"/>
</ds:Transform>
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
<ds:DigestValue>ucSFZEOTHpe/IOlPVWtU+1xT4sM=</ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>
<ds:SignatureValue>
d4CKqie==
</ds:SignatureValue>
<ds:KeyInfo Id="KI-9A8D1F611E86CFB79E144316684667546">
<wsse:SecurityTokenReference oas:Id="STR-9A8D1F611E86CFB79E144316684667547">
<wsse:KeyIdentifier EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary"
ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3">5lAB5TaqeFwo23mRVm31LngBT1dQMf94mxeVkyKog==
</wsse:KeyIdentifier>
</wsse:SecurityTokenReference>
</ds:KeyInfo>
</ds:Signature>
<oas:Timestamp oas:Id="TS-30">
<oas:Created>2015-09-25T07:40:46.670Z</oas:Created>
<oas:Expires>2015-09-26T11:27:26.670Z</oas:Expires>
</oas:Timestamp>
</wsse:Security>
</soapenv:Header>
<soapenv:Body oas:Id="id-31">
...
</soapenv:Body>
</soapenv:Envelope>
In soapUI, under the "WS-Security Configurations" I added the Keystores (jks with my private cert) and Truststores (jks with CA root public key). Finally I added "Outgoing WS-Security Configurations" with the following setting.
With this WS-Security setting, the soap message adds the wsse:Security as shown above.
Now, we are building the WS client on the .NET with WCF, and need help with the Binding and Security settings. What binding and configuration settings can be used to have the same WS-Security header as required or shown above?
From the WSDL, I created a client-side proxy using WSCF.blue. Although, I could have also used svcutil.exe or using Add Service Reference from VS.
I have tried creating the customBinding in code as following, but when inspecting this message the wsse header is not the same as what I want.
For example, it adds a BinarySecurityToken in the Security header which is not desired.
Or the KeyIdentifier is X509SubjectKeyIdentifier instead of X509v3 for example:
<o:KeyIdentifier ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509SubjectKeyIdentifier" ...</o:KeyIdentifier>
It also adds additional multiple Reference under the SignedInfo, and even though it passes schema validation, I am not sure what the additional Reference is doing.
internal static Binding GetCustomBinding()
{
CustomBinding myBinding = new CustomBinding();
AsymmetricSecurityBindingElement asBindingElement = new AsymmetricSecurityBindingElement();
//Have tried these also
//asBindingElement.MessageSecurityVersion = MessageSecurityVersion.WSSecurity11WSTrust13WSSecureConversation13WSSecurityPolicy12;
//asBindingElement.MessageSecurityVersion = MessageSecurityVersion.WSSecurity10WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10;
//WSSecurity10WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10
asBindingElement.MessageSecurityVersion = MessageSecurityVersion.WSSecurity10WSTrust13WSSecureConversation13WSSecurityPolicy12BasicSecurityProfile10;
asBindingElement.InitiatorTokenParameters = new System.ServiceModel.Security.Tokens.X509SecurityTokenParameters { InclusionMode = SecurityTokenInclusionMode.Never };
asBindingElement.RecipientTokenParameters = new System.ServiceModel.Security.Tokens.X509SecurityTokenParameters
{
//X509ReferenceStyle = X509KeyIdentifierClauseType.SubjectKeyIdentifier,
InclusionMode = SecurityTokenInclusionMode.Never
};
asBindingElement.MessageProtectionOrder = System.ServiceModel.Security.MessageProtectionOrder.SignBeforeEncrypt;
//asBindingElement.SecurityHeaderLayout = SecurityHeaderLayout.Strict;
asBindingElement.SecurityHeaderLayout = SecurityHeaderLayout.LaxTimestampLast;
asBindingElement.EnableUnsecuredResponse = true;
asBindingElement.IncludeTimestamp = true;
asBindingElement.SetKeyDerivation(false);
asBindingElement.DefaultAlgorithmSuite = System.ServiceModel.Security.SecurityAlgorithmSuite.Basic128Rsa15;
asBindingElement.EndpointSupportingTokenParameters.Signed.Add(new X509SecurityTokenParameters());
myBinding.Elements.Add(asBindingElement);
myBinding.Elements.Add(new TextMessageEncodingBindingElement(MessageVersion.Soap11, Encoding.UTF8));
//myBinding.Elements.Add(new MtomMessageEncodingBindingElement(MessageVersion.Soap11, Encoding.UTF8));
HttpsTransportBindingElement httpsBindingElement = new HttpsTransportBindingElement();
httpsBindingElement.RequireClientCertificate = true;
myBinding.Elements.Add(httpsBindingElement);
return myBinding;
}
Additionally, I am using this behaviour for the x509 cert
<behaviors>
<endpointBehaviors>
<behavior name="MyBehavior">
<clientCredentials>
<clientCertificate findValue="xxx"
storeLocation="LocalMachine" storeName="My" x509FindType="FindByThumbprint" />
<serviceCertificate>
<defaultCertificate findValue="xxx"
storeLocation="LocalMachine" storeName="My" x509FindType="FindByThumbprint" />
<authentication certificateValidationMode="None" revocationMode="NoCheck"
trustedStoreLocation="LocalMachine" />
</serviceCertificate>
</clientCredentials>
</behavior>
</endpointBehaviors>
</behaviors>
If you want to send the exact same message than you should probably use the SignedXml class to generate the signature outside of WCF. With WCF you will not be able to get the exact canonicalization algorithn / InclusiveNamespace.
Having said that most of the time producing a similar message is fine with the server. You have two possible approaches. One is to create a customMessageEncoder which will change the outgoing WCF message before it hits the network. So you can tell WCF to create a minimalist message (e.g. no timestamp) and in the encoder add the timestamp yourself (if wcf would create it then it would also sign it) and also remove/change the x.509 token references. You should be careful not to change any signed xml nodes when doing do (body, signedInfo) including whitespaces (so use preserveWhitepsaces if loading to XmlDocument).
Another approach is to try and configure WCF to send the same message:
to exclude the extra token reference create the x.509 params like this:
X509SecurityTokenParameters x509Params = new X509SecurityTokenParameters();
x509Params.X509ReferenceStyle =
X509KeyIdentifierClauseType.SubjectKeyIdentifier;
x509Params.RequireDerivedKeys = false;
;
x509Params.InclusionMode = SecurityTokenInclusionMode.Never;
x509Params.ReferenceStyle = SecurityTokenReferenceStyle.Internal;
x509Params.X509ReferenceStyle = X509KeyIdentifierClauseType.Any;
((AsymmetricSecurityBindingElement) sec).InitiatorTokenParameters = x509Params;
timestamp will be signed by wcf, but possibly the server does not care, or does not require a timestamp, so try requireTimestamp=true/false and see what works. You might be able to programatically disable signing for timestamps by using MessagePartSpecification.
The important in either approach is to understand the server might be more flexible than you think and to understand from its error messages what are the real show stoppers.

WCF Message Security with Certificates error: Security token could not be validated

I have a WCF service setup with Messager security using certificates. I am using self-signed certificates for the moment. I do not use the certificate store, but instead load my certificates on the fly.
The service works when I use MessageSecurity=None, but with security turned on, as soon as I make a call to a service endpoint, I get the error:
An unsecured or incorrectly secured fault was received from the other party. See the inner FaultException for the fault code and detail. --> At least one security token of the message could not be validated.
The relevant sections of my service configuration look like this:
<endpoint name="DB_msgAnonymousSecured" address="Secure"
binding="wsHttpBinding" bindingConfiguration="UploadServiceSecure"
contract="DataStoreInterfaces.IDataStoreServices" >
</endpoint>
<wsHttpBinding>
<binding name="UploadServiceSecure" >
<security mode="Message">
<!-- Message security with certificate authentication-->
<message clientCredentialType="Certificate"
negotiateServiceCredential="false" establishSecurityContext="false"/>
</security>
</binding>
</wsHttpBinding>
The certificates are configured in the code like this:
Server:
using (WebServiceHost queryHost = new WebServiceHost(typeof(DataStoreServices.DatabaseServicesImplementation)))
{
try
{
queryHost.Credentials.ServiceCertificate.Certificate = new System.Security.Cryptography.X509Certificates.X509Certificate2(Application.StartupPath + "\\Server.pfx");
queryHost.Credentials.ClientCertificate.Certificate = new System.Security.Cryptography.X509Certificates.X509Certificate2(Application.StartupPath + "\\Client.cer");
queryHost.Open();
while (true) //endless loop
{
Thread.Sleep(100);
}
}
catch (Exception ex)
{
}
}
Client:
var binding = new WSHttpBinding();
binding.Security.Mode = SecurityMode.Message;
binding.Security.Message.ClientCredentialType = MessageCredentialType.Certificate;
binding.Security.Message.EstablishSecurityContext = false;
binding.Security.Message.NegotiateServiceCredential = false;
EndpointAddress address = new EndpointAddress(new Uri(serviceEndpointAddress), EndpointIdentity.CreateDnsIdentity("CompanyXYZ Server")); //usually, the DnsIdentity is the domain name. In our case, it's the value that was entered during the creation of the self-signed certificate.
ChannelFactory<IDataStoreServices> cf = new ChannelFactory<IDataStoreServices>(binding, address);
cf.Credentials.ServiceCertificate.Authentication.CertificateValidationMode = X509CertificateValidationMode.None; //since our certificate is self-signed, we don't want it to be validated
cf.Credentials.ClientCertificate.Certificate = new System.Security.Cryptography.X509Certificates.X509Certificate2(Environment.CurrentDirectory + "\\Client.pfx");
cf.Credentials.ServiceCertificate.DefaultCertificate = new System.Security.Cryptography.X509Certificates.X509Certificate2(Environment.CurrentDirectory + "\\Server.cer");
IDataStoreServices channel = cf.CreateChannel();
response = channel.TestConnection(); //this line causes the exception
I have been struggling with this issue for weeks now and I am out of ideas.
Does anybody have any hints for me why this isn't working?

Custom X509CertificateValidator with BasicHttpSecurityMode.Transport?

Is there an easy way to tie custom X509 cert validation to BasicHttpBinding (or CustomHttpBinding for the same matter, which will implement transport-only security)?
EDIT1: I added a ServerCertificateValidationCallback to the code for the sake of showing that it doesn't fire up either
Here's what I'm trying to do:
1) wrote custom X509CertificateValidator:
public class MyX509Validator : X509CertificateValidator
{
public override void Validate(X509Certificate2 certificate)
{
Console.WriteLine("Incoming validation: subj={0}, thumb={1}",
certificate.Subject, certificate.Thumbprint);
}
}
2) created host:
var soapBinding = new BasicHttpBinding() { Namespace = "http://test.com" };
soapBinding.Security.Mode = BasicHttpSecurityMode.Transport;
soapBinding.Security.Transport.ClientCredentialType =
HttpClientCredentialType.Certificate;
var sh = new ServiceHost(typeof(Service1), uri);
sh.AddServiceEndpoint(typeof(IService1), soapBinding, "");
sh.Credentials.ServiceCertificate.SetCertificate(StoreLocation.LocalMachine,
StoreName.My, X509FindType.FindBySubjectName, "localhost");
sh.Credentials.ClientCertificate.Authentication.CertificateValidationMode =
System.ServiceModel.Security.X509CertificateValidationMode.Custom;
sh.Credentials.ClientCertificate.Authentication.CustomCertificateValidator =
new MyX509Validator();
System.Net.ServicePointManager.ServerCertificateValidationCallback +=
delegate(object sender, X509Certificate certificate,
X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
Console.WriteLine("Incoming validation: subj={0}, thumb={1}",
certificate.Subject, certificate.GetIssuerName());
return true;
};
sh.Open();
3) Created WCF Client:
var binding = new BasicHttpBinding();
binding.Security.Mode = BasicHttpSecurityMode.Transport;
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Certificate;
var cli = new ServiceReference2.Service1Client(binding,
new EndpointAddress("https://localhost:801/Service1"));
cli.ClientCredentials.ClientCertificate.SetCertificate(StoreLocation.LocalMachine,
StoreName.My, X509FindType.FindBySubjectName, "localhost");
cli.HelloWorld();
Authentication works fine, but MyX509Validator.Validate() never gets called. I have suspicion that X509CertificateValidator only works on message security, not on transport. Is that right? Is there something I could do to override transport-level cert validation?
I'm assuming that you are talking about the certificate that is tied to HTTPS...
If so, here is the answer: The X509CertificateValidationMode is used within the header of the document that you are exchanging to provide authentication. It does nothing for the underlying transport itself, which in this case is HTTPS with its' associated x.509 cert. So, you are spot on with your assumption.
If you want to provide custom certificate validation of the transport, use System.Net.ServicePointManager.ServerCertificateValidationCallback instead. Be careful though, this is an app wide setting. If you are cycling through multiple endpoints and don't set this to null, it will remain active.
I am doing this using the following settings and my custom validator definitely gets hit:
serviceHost.Credentials.ClientCertificate.Authentication.CertificateValidationMode =
X509CertificateValidationMode.Custom;
serviceHost.Credentials.ClientCertificate.Authentication.CustomCertificateValidator =
new CertificateValidator(ConfigurationManager.AppSettings["Cerficate"]);
My behaviour is:
<behavior name="CustomCertificateBehavior">
<serviceCredentials>
<clientCertificate>
<authentication certificateValidationMode="Custom" />
</clientCertificate>
</serviceCredentials>
</behavior>
</serviceBehaviors>
And my binding:
<basicHttpBinding>
<binding name="SecureBinding" maxReceivedMessageSize="5242880">
<security mode="Transport">
<transport clientCredentialType="Certificate"></transport>
</security>
</binding>
</basicHttpBinding>

BasicHttpBinding with Certificate authentication - error "forbidden"?

I'm trying go get WCF server and client mutually authenticate each other using SSL certificates on transport level using BasicHttpBinding. Here's how the server is getting created:
var soapBinding = new BasicHttpBinding() { Namespace = "http://test.com" };
soapBinding.Security.Mode = BasicHttpSecurityMode.Transport;
soapBinding.Security.Transport.ClientCredentialType =
HttpClientCredentialType.Certificate;
var sh = new ServiceHost(typeof(Service1), uri);
sh.AddServiceEndpoint(typeof(IService1), soapBinding, "");
sh.Credentials.ServiceCertificate.SetCertificate(
StoreLocation.LocalMachine, StoreName.My,
X509FindType.FindBySubjectName, "localhost");
sh.Open();
Here's the client:
var binding = new BasicHttpBinding();
binding.Security.Mode = BasicHttpSecurityMode.Transport;
var service = new ServiceReference2.Service1Client(binding,
new EndpointAddress("https://localhost:801/Service1"));
service.ClientCredentials.ClientCertificate.SetCertificate(
StoreLocation.LocalMachine, StoreName.My,
X509FindType.FindBySubjectName, "localhost");
service.ClientCredentials.ServiceCertificate.Authentication.
CertificateValidationMode =
System.ServiceModel.Security.X509CertificateValidationMode.PeerTrust;
service.HelloWorld();
Certificate for localhost is in Personal, Trusted Root and Trusted 3rd Party containers. Internet Explorer can connect to host and see WSDL. Also, SSL calls work fine with ClientCredentialType = HttpClientCredentialType.None
HelloWorld() fails with:
System.ServiceModel.Security.MessageSecurityException occurred<br/>
Message="The HTTP request was forbidden with client authentication
scheme 'Anonymous'."
which is a rethrown exception from: "The remote server returned an error: (403) Forbidden."
how does one go around figuring out wtf is going on?
Try adding this in the client just after setting Security.Mode:
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Certificate;
If you use standard generated proxy class you can to set transport client credential type to Certificate in App.Config:
<binding name="SpoDataServiceSoap">
<security mode="Transport">
<transport clientCredentialType="Certificate"></transport>
</security>
</binding>
C#
var client = new MyServiceSoapClient()
X509Certificate2 cert = CertificateHelper.GetClientCertificate();
client.ClientCredentials.ClientCertificate.Certificate = cert;