Convert Binding to Customer Binding in Code. Particularly "establishSecurityContext=false" attribute - wcf

I'm trying to convert this binding to a custom binding:
<wsHttpBinding>
<binding name="ICB">
<security mode="TransportWithMessageCredential">
<transport clientCredentialType="Certificate" proxyCredentialType="None"></transport>
<message clientCredentialType="UserName"
negotiateServiceCredential="false"
establishSecurityContext="false" />
</security>
</binding>
</wsHttpBinding>
.. and this behavior..
<serviceBehaviors>
<behavior name="SCB">
<serviceCredentials>
<serviceCertificate findValue="a0 1e dd 76 89 78 a2 67 e3 7d c7 e5 55 5a ec 34 b7 ed 81 f6"
storeLocation="LocalMachine"
storeName="TrustedPeople"
x509FindType="FindByThumbprint" />
<userNameAuthentication
userNamePasswordValidationMode="Custom"
customUserNamePasswordValidatorType="WcfTesting.UsernameValidator, WcfTesting" />
<clientCertificate>
<authentication certificateValidationMode="None" revocationMode="NoCheck" />
</clientCertificate>
</serviceCredentials>
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="false" />
</behavior>
</serviceBehaviors>
This works as long as the client has establishSecurityContext=true, how do I change it to false?
class Program
{
static void Main(string[] args)
{
var c = new CustomBinding();
c.Elements.Add(new TransactionFlowBindingElement());
//MessageSecurityVersion version = MessageSecurityVersion.WSSecurity11WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10;
var sec = // SecurityBindingElement.CreateUserNameOverTransportBindingElement();
SecurityBindingElement.CreateSecureConversationBindingElement(
SecurityBindingElement.CreateUserNameOverTransportBindingElement());
c.Elements.Add(sec);
c.Elements.Add(new TextMessageEncodingBindingElement()); // {MessageVersion = MessageVersion.Soap11});
c.Elements.Add(new HttpsTransportBindingElement()); // { RequireClientCertificate = true });
ServicePointManager.ServerCertificateValidationCallback = (sender, certificate, chain, errors) => true;
using (var host = new System.ServiceModel.ServiceHost( typeof (TestService) ))
{
ServiceEndpoint endpoint = host.AddServiceEndpoint(typeof(TestService), c, "https://localhost:1235");
host.Credentials.ServiceCertificate.SetCertificate(
StoreLocation.LocalMachine,
StoreName.TrustedPeople,
X509FindType.FindByThumbprint,
"a01edd768978a267e37dc7e5555aec34b7ed81f6");
host.Credentials.UserNameAuthentication.UserNamePasswordValidationMode = UserNamePasswordValidationMode.Custom;
host.Credentials.UserNameAuthentication.CustomUserNamePasswordValidator = new UsernameValidator();
host.Open();
Console.WriteLine("SERVICE STARTED>>");
Console.ReadLine();
}
}
}
I used the results of WCFBindingBox as the basis of my conversion, here is the results:
<!-- generated via Yaron Naveh's http://webservices20.blogspot.com/ -->
<customBinding>
<binding name="NewBinding0">
<transactionFlow />
<security authenticationMode="UserNameOverTransport" messageSecurityVersion="WSSecurity11WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10" />
<textMessageEncoding />
<httpsTransport />
</binding>
</customBinding>
<!-- generated via Yaron Naveh's http://webservices20.blogspot.com/ -->

You can automatically convert to a custom binding via the binding box. Generally speaking if establishSecurityContext is false you do not need CreateSecureConversationBindingElement. Instead directly use CreateUserNameOverTransportBindingElement.

Related

Configure autofac to use app.config in wcf client

Hi I'm trying to use autofac and use a wcf client.
But wondering how i can use the "client" configuration from my app.config ?
I would like to keep the settings in the config if possible?
public void ConfigureContainer()
{
var builder = new ContainerBuilder();
builder
.Register(c => new ChannelFactory<apiSoapType>(new BasicHttpsBinding("?????")) ?????.SingleInstance();
builder.Build();
}
//My app.config
<? xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<behaviors>
<endpointBehaviors>
<behavior name = "DisableServiceCertificateValidation" >
< clientCredentials >
< serviceCertificate >
< authentication certificateValidationMode="None" revocationMode="NoCheck" />
</serviceCertificate>
</clientCredentials>
</behavior>
</endpointBehaviors>
</behaviors>
<bindings>
<basicHttpsBinding>
<binding name = "apiSoapBinding" maxBufferPoolSize="20000000" maxBufferSize="20000000" maxReceivedMessageSize="20000000">
<readerQuotas maxDepth = "32" maxStringContentLength="200000000" maxArrayLength="200000000" />
<security mode = "Transport" >
< transport clientCredentialType="None" proxyCredentialType="None" realm="" />
</security>
</binding>
</basicHttpsBinding>
</bindings>
<client>
<endpoint
address = "https://somesite/api.wso"
binding="basicHttpsBinding"
bindingConfiguration="apiSoapBinding"
behaviorConfiguration="DisableServiceCertificateValidation"
contract="somename.apiSoapType"
name="**somename.apiSoapType**"
/>
</client>
</system.serviceModel>
</configuration>
Try registering the ChannelFactory<T> using the ChannelFactory<T>(string) constructor and passing in the endpoint name found in your config file.
builder
.Register(c => new ChannelFactory<apiSoapType>("**somename.apiSoapType**"))
.SingleInstance();

WCF transport security: socket connection was aborted

I have a problem in getting transport security to work.
I have 2 services (A & B) running in the same server. service A will call service B to perform some task. Without any security, I can communicate just fine. But when I turn on transport security with the following settings:
security mode = transport
TransportClientCredentialType = Windows
ProtectionLevel = EncryptAndSign
I got error when service A calls service B:
System.ServiceModel.CommunicationException: The socket connection was aborted. This could be caused by an error processing your message or a receive timeout being exceeded by the remote host, or an underlying network resource issue. Local socket timeout was '00:00:09.7810000'. ---> System.IO.IOException: The read operation failed, see inner exception. ---> System.ServiceModel.CommunicationException: The socket connection was aborted. This could be caused by an error processing your message or a receive timeout being exceeded by the remote host, or an underlying network resource issue. Local socket timeout was '00:00:09.7810000'. ---> System.Net.Sockets.SocketException: An existing connection was forcibly closed by the remote host
I tried changing the receive and send timeout to 5 mins but I still get the same error with roughly the same timeout duration. The only difference is I need to wait out the 5 minutes instead of 1 minute.
Can anyone provide an insight to what's the cause and how to resolve this?
Attached is the configuration file for both service:
ServiceA
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.web>
<compilation targetFramework="4.5" debug="true" defaultLanguage="c#" />
</system.web>
<system.serviceModel>
<protocolMapping>
<remove scheme="net.tcp" />
<add scheme="net.tcp" binding="netTcpBinding" bindingConfiguration="ReliableTCP" />
</protocolMapping>
<client/>
<behaviors>
<serviceBehaviors>
<behavior name="mexTag">
<serviceMetadata httpGetEnabled="false" />
<serviceDebug includeExceptionDetailInFaults="false" />
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="tryBehavior">
<dataContractSerializer maxItemsInObjectGraph="2147483647" />
</behavior>
</endpointBehaviors>
</behaviors>
<bindings>
<customBinding>
<binding name="mexTcp">
<tcpTransport portSharingEnabled="true" />
</binding>
</customBinding>
<netTcpBinding>
<binding name="ReliableTCP" portSharingEnabled="true" sendTimeout="00:05:00" receiveTimeout="00:05:00"
maxBufferPoolSize="2147483647" maxReceivedMessageSize="2147483647" maxBufferSize="2147483647">
<reliableSession enabled="true" />
<security mode="Transport">
<transport clientCredentialType="Windows" protectionLevel="EncryptAndSign" />
</security>
</binding>
</netTcpBinding>
</bindings>
<services>
<service behaviorConfiguration="mexTag" name="Test.Service.ServiceAImpl">
<endpoint address="net.tcp://app-svr:10010/ServiceA/ServiceAImpl/" behaviorConfiguration="tryBehavior"
binding="netTcpBinding" bindingConfiguration="ReliableTCP" contract="Test.Service.IServiceA" />
<endpoint address="net.tcp://app-svr:10012/ServiceA/ServiceAImpl/mex"
binding="customBinding" bindingConfiguration="mexTcp" contract="IMetadataExchange" />
</service>
</services>
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true" />
<!--
To browse web app root directory during debugging, set the value below to true.
Set to false before deployment to avoid disclosing web app folder information.
-->
<directoryBrowse enabled="true" />
</system.webServer>
</configuration>
ServiceB
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.web>
<compilation targetFramework="4.5" debug="true" defaultLanguage="c#" />
</system.web>
<system.serviceModel>
<client>
<endpoint address="net.tcp://app-svr:10010/ServiceA/ServiceAImpl/"
binding="netTcpBinding" bindingConfiguration="NetTcpBinding_IServiceA"
behaviorConfiguration="tryBehavior"
contract="ServiceAReference.IServiceA" name="NetTcpBinding_IServiceA" />
</client>
<behaviors>
<serviceBehaviors>
<behavior name="MEXGET" >
<!-- Add the following element to your service behavior configuration. -->
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="tryBehavior">
<dataContractSerializer maxItemsInObjectGraph="2147483647" />
</behavior>
</endpointBehaviors>
</behaviors>
<bindings>
<customBinding>
<binding name="MexTcp">
<tcpTransport portSharingEnabled="true" />
</binding>
</customBinding>
<netTcpBinding>
<binding name="ReliableTCP" portSharingEnabled="true">
<reliableSession enabled="true" />
<security mode="Transport">
<transport clientCredentialType="Windows" protectionLevel="EncryptAndSign" />
</security>
</binding>
<binding name="NetTcpBinding_IServiceA" receiveTimeout="00:05:00" sendTimeout="00:05:00" maxBufferPoolSize="2147483647" maxReceivedMessageSize="2147483647" maxBufferSize="2147483647">
<reliableSession enabled="true" />
<security mode="Transport">
<transport clientCredentialType="Windows" protectionLevel="EncryptAndSign" />
</security>
</binding>
</netTcpBinding>
<mexTcpBinding>
<binding name="MexTcp" />
</mexTcpBinding>
</bindings>
<services>
<service name="Test.Service.ServiceBImpl" behaviorConfiguration="MEXGET" >
<endpoint address="mex"
binding="customBinding"
bindingConfiguration="MexTcp"
contract="IMetadataExchange" />
<endpoint address="mex"
binding="mexHttpBinding"
contract="IMetadataExchange" />
<endpoint
address="net.tcp://app-svr:10010/ServiceB/ServiceBImpl"
binding="netTcpBinding" behaviorConfiguration="tryBehavior"
bindingConfiguration="ReliableTCP"
contract="Test.Service.ServiceB" />
<host>
<baseAddresses>
<add baseAddress="http://app-svr:10011/ServiceB/ServiceBImpl" />
<add baseAddress="net.tcp://app-svr:10010/ServiceB/ServiceBImpl" />
</baseAddresses>
</host>
</service>
</services>
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true" />
<!--
To browse web app root directory during debugging, set the value below to true.
Set to false before deployment to avoid disclosing web app folder information.
-->
<directoryBrowse enabled="true" />
</system.webServer>
</configuration>
There is a solution here ...you should try it...
Added these behaviors at both service and client config.
<behaviors>
<endpointBehaviors>
<behavior name="endpointBehavior">
<dataContractSerializer maxItemsInObjectGraph="2147483647"/>
</behavior>
</endpointBehaviors>
</behaviors>
Update these values to maximum size in both Client and Server config.
<binding name="tcpBinding" receiveTimeout="00:15:00" sendTimeout="00:15:00" maxBufferPoolSize="2147483647" maxReceivedMessageSize="2147483647" maxBufferSize="2147483647">
<security mode="None">
<transport clientCredentialType="None" protectionLevel="None" />
<message clientCredentialType="None" />
</security>
</binding>
Hope it helps.
I had that same error and it was caused by the service credentials being wrong or missing. Since you're using tcp binding first create the binding and set the security properly:
NetTcpBinding binding = new NetTcpBinding(SecurityMode.Transport)
{
CloseTimeout = TimeSpan.FromSeconds(timeoutInSeconds),
OpenTimeout = TimeSpan.FromSeconds(timeoutInSeconds),
SendTimeout = TimeSpan.FromSeconds(timeoutInSeconds),
ReceiveTimeout = TimeSpan.FromSeconds(timeoutInSeconds)
};
binding.Security.Transport.ClientCredentialType =
TcpClientCredentialType.Windows;
binding.MaxReceivedMessageSize = int.MaxValue;
binding.MaxBufferSize = int.MaxValue;
binding.MaxBufferPoolSize = int.MaxValue;
Make sure to set the username and password after creating the client:
var serviceClient = new MyServiceClient(binding, endpointYouDefine);
serviceClient.ClientCredentials.Windows.ClientCredential = new NetworkCredential("usernameInActiveDirectory", "passwordForTheADUser", "yourdomain.com");
Don't do anything else to the client after that. I've seen weird behavior like setting reader quotas programmatically after assigning the credentials, and the credentials get wiped out.
I managed to solve this problem by doing the following in the server where the net.tcp web service was hosted:
Restart NET TCP port sharing service from services.msc
Open command prompt as administrator and run the IIS Reset
I've created extension method for this.
One for NetTcpBinding and one for NetNamedPipeBinding. Only to be used on internal services of course. Timings too as when a default service is not used during the night it will fail on the first call in the morning.
public static void ActivateMaxValues(this NetTcpBinding b)
{
b.OpenTimeout = TimeSpan.FromHours(10);
b.CloseTimeout = TimeSpan.FromMinutes(10);
b.ReceiveTimeout = TimeSpan.FromHours(10);
b.SendTimeout = TimeSpan.FromHours(10);
b.MaxBufferSize = int.MaxValue;
b.MaxReceivedMessageSize = int.MaxValue;
b.ReaderQuotas = new System.Xml.XmlDictionaryReaderQuotas()
{
MaxArrayLength = int.MaxValue,
MaxBytesPerRead = int.MaxValue,
MaxDepth = int.MaxValue,
MaxNameTableCharCount = int.MaxValue,
MaxStringContentLength = int.MaxValue
};
}
public static void ActivateMaxValues(this NetNamedPipeBinding b)
{
b.TransactionFlow = true;
b.OpenTimeout = TimeSpan.FromHours(1);
b.CloseTimeout = TimeSpan.FromMinutes(10);
b.ReceiveTimeout = TimeSpan.FromHours(1);
b.SendTimeout = TimeSpan.FromHours(1);
b.MaxBufferSize = int.MaxValue;
b.MaxReceivedMessageSize = int.MaxValue;
b.ReaderQuotas = new System.Xml.XmlDictionaryReaderQuotas()
{
MaxArrayLength = int.MaxValue,
MaxBytesPerRead = int.MaxValue,
MaxDepth = int.MaxValue,
MaxNameTableCharCount = int.MaxValue,
MaxStringContentLength = int.MaxValue
};
}

Duplication of EndPoint but i cannot see it

I have a web service.
This is (part of my) my web config:
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="LicensedBehaviour">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="false" />
</behavior>
<behavior name="NetTCPBehaviour">
<serviceTimeouts transactionTimeout="0.00:00:30" />
<serviceDebug includeExceptionDetailInFaults="false" />
<dataContractSerializer maxItemsInObjectGraph="65536" />
<serviceMetadata httpGetEnabled="true" />
</behavior>
<behavior name="">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service name="License" behaviorConfiguration="LicensedBehaviour">
<endpoint address="License.svc" binding="basicHttpBinding" bindingConfiguration="NormalHttpBindingEndPoint" contract="ILicense" name="wsLicense" />
</service>
<service name="testme" behaviorConfiguration="NetTCPBehaviour">
<endpoint address="net.tcp://localhost:808/Sync2.svc" binding="netTcpBinding" contract="ISync2" name="wsMotionUploader" bindingConfiguration="NetTCPBindingEndPoint"/>
</service>
</services>
<bindings>
<basicHttpBinding>
<binding name="NormalHttpBindingEndPoint" closeTimeout="00:02:00" openTimeout="00:02:00">
<readerQuotas maxArrayLength="32768" maxStringContentLength="2147483647" />
</binding>
</basicHttpBinding>
<netTcpBinding>
<binding name="NetTCPBindingEndPoint" receiveTimeout="00:15:00" sendTimeout="00:15:00" transferMode="Streamed" closeTimeout="00:02:00" openTimeout="00:02:00"
maxBufferPoolSize="2147483647" maxReceivedMessageSize="2147483647" maxBufferSize="2147483647">
<readerQuotas maxArrayLength="32768" />
<security mode="None">
<transport clientCredentialType="None" protectionLevel="None" />
<message clientCredentialType="None" />
</security>
</binding>
</netTcpBinding>
</bindings>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
</system.serviceModel>
This is my service:
public void DoWork(Stream image)
{
var img = System.Drawing.Bitmap.FromStream(image);
}
This is my interface:
[OperationContract(IsOneWay = true)]
void DoWork(Stream image);
This is my client:
wsSyncFastest.Sync2Client client = new wsSyncFastest.Sync2Client();
using (Bitmap bmp = new Bitmap("d:\\bf.jpg"))
{
using (MemoryStream ms = new MemoryStream())
{
bmp.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
client.DoWork(ms);
ms.Close();
}
}
This is my error message:
An endpoint configuration section for contract 'wsSyncFastest.ISync2' could not be loaded because more than one endpoint configuration for that contract was found. Please indicate the preferred endpoint configuration section by name.
this is my stack Error:
at System.ServiceModel.Description.ConfigLoader.LookupChannel(ContextInformation configurationContext, String configurationName, ContractDescription contract, EndpointAddress address, Boolean wildcard, Boolean useChannelElementKind, ServiceEndpoint& serviceEndpoint)
at System.ServiceModel.Description.ConfigLoader.LookupEndpoint(String configurationName, EndpointAddress address, ContractDescription contract, ContextInformation configurationContext)
at System.ServiceModel.Description.ConfigLoader.LookupEndpoint(String configurationName, EndpointAddress address, ContractDescription contract)
at System.ServiceModel.ChannelFactory.InitializeEndpoint(String configurationName, EndpointAddress address)
at System.ServiceModel.ChannelFactory1..ctor(String endpointConfigurationName, EndpointAddress remoteAddress)
at System.ServiceModel.ChannelFactory1..ctor(String endpointConfigurationName)
at System.ServiceModel.ConfigurationEndpointTrait1.CreateSimplexFactory()
at System.ServiceModel.ConfigurationEndpointTrait1.CreateChannelFactory()
at System.ServiceModel.ClientBase1.CreateChannelFactoryRef(EndpointTrait1 endpointTrait)
at System.ServiceModel.ClientBase1.InitializeChannelFactoryRef()
at System.ServiceModel.ClientBase1..ctor()
at LiteEdition.wsSyncFastest.Sync2Client..ctor() in g:\dev20140604\LiteEdition\LiteEdition\Service References\wsSyncFastest\Reference.cs:line 51
at LiteEdition.StartUp.button1_Click(Object sender, EventArgs e) in g:\dev20140604\LiteEdition\LiteEdition\StartUp.cs:line 2646
I cannot see any probelms but obviously there is.
Can anyone assist?
ADDITIONAL:
My client app.config:
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="wsLicense" />
<binding name="BasicHttpBinding_ISync2" />
</basicHttpBinding>
<netTcpBinding>
<binding name="NetTcpBinding_ISync2" />
</netTcpBinding>
</bindings>
<client>
<endpoint address="http://aurl/License.svc/License.svc"
binding="basicHttpBinding" bindingConfiguration="wsLicense"
contract="wsLicense.ILicense" name="wsLicense" />
<endpoint address="http://www.informedmotion.co.uk/Sync2.svc"
binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_ISync2"
contract="wsSyncFastest.ISync2" name="BasicHttpBinding_ISync2" />
<endpoint address="net.tcp://dsvr019492/Sync2.svc" binding="netTcpBinding"
bindingConfiguration="NetTcpBinding_ISync2" contract="wsSyncFastest.ISync2"
name="NetTcpBinding_ISync2">
<identity>
<servicePrincipalName value="host/DSVR019492" />
</identity>
</endpoint>
</client>
</system.serviceModel>
Try passing the name of the endpoint in when you create the proxy object and see if that fixes it.
If you intend on using the basicHttpBinding, it will be:
wsSyncFastest.Sync2Client client = new wsSyncFastest.Sync2Client("BasicHttpBinding_ISync2");
If you intend on using the netTcpBinding it will be:
wsSyncFastest.Sync2Client client = new wsSyncFastest.Sync2Client("NetTcpBinding_ISync2");

InvalidSecurity when I try to soap call my WCF with Username and Password

Error [a:InvalidSecurity] An error occurred when verifying security for the message.
I try to call my service like this.
php
<?php
$soapURL = "https://localhost:8888/wcf/?wsdl" ;
$soapParameters = Array('userName' => "user23", 'password' => "pass123") ;
$soapClient = new SoapClient($soapURL, $soapParameters);
var_dump($soapClient->GetVersion());
?>
I got it to work before with no authentication so the fault lies here in the php code or in my app.config on the WCF i believe.
here is my app.config
<?xml version="1.0"?>
<configuration>
<system.serviceModel>
<services>
<service
behaviorConfiguration="MYDLL.Behavior"
name="MYDLL.WCFService">
<endpoint
address=""
binding="basicHttpBinding"
bindingConfiguration="UsernameAndSSL"
name="basicHttpBinding"
contract="MYDLL.IService"/>
<endpoint
address="mex"
binding="mexHttpsBinding"
bindingConfiguration=""
name="MexBinding"
contract="IMetadataExchange"/>
<host>
<baseAddresses>
<add baseAddress="https://localhost:8734/wcf/"/>
</baseAddresses>
</host>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="MYDLL.Behavior">
<serviceMetadata httpsGetEnabled="true"/>
<serviceCredentials>
<userNameAuthentication userNamePasswordValidationMode="Custom" customUserNamePasswordValidatorType="MYDLL.CustomUserNameValidator, MYDLL"/>
<serviceCertificate findValue="localhost" storeLocation="LocalMachine" storeName="My" x509FindType="FindBySubjectName"/>
</serviceCredentials>
<useRequestHeadersForMetadataAddress />
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<basicHttpBinding>
<binding name="UsernameAndSSL">
<security mode="TransportWithMessageCredential">
<transport clientCredentialType="Basic"/>
<message clientCredentialType="UserName"/>
</security>
</binding>
</basicHttpBinding>
</bindings>
</system.serviceModel>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0,Profile=Client"/>
</startup>
</configuration>
and the customusernamevalidator
public class CustomUserNameValidator : UserNamePasswordValidator
{
public override void Validate(string userName, string password)
{
Console.WriteLine("User validation succeeded (Username: {0}; Password: {1})", userName, password);
}
}
What I dont get is if the fault are in my php code or in the config. the consolewrite does not run, and I just get that invalidSecurity exception so it seems it knows I have an customusernamevalidation that it cant find?
I struggled with this same issue for several weeks and finally got PHP clients to authenticate with my WCF service. This configuration is what I currently have working in production.
First off, you have to enable Anonymous auth AND Basic auth. You have to leave Anonymous turned on so the client can read the WSDL before they authenticate. Even though anonymouse auth is enabled they will not be able to use your service without being authenticated first.
Put this in your web.config or turn on Anonymous AND Basic Authentication in IIS directly:
<security>
<authentication>
<anonymousAuthentication enabled="true" />
<basicAuthentication enabled="true" />
</authentication>
</security>
Then setup your WCF security to use Basic auth and Transport security:
<bindings>
<basicHttpBinding>
<binding name="SSLBinding">
<security mode="Transport">
<transport clientCredentialType="Basic" />
</security>
</binding>
</basicHttpBinding>
</bindings>
And here is my behavior:
<behaviors>
<serviceBehaviors>
<behavior name="SSLBehavior">
<serviceMetadata httpsGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
<serviceSecurityAudit auditLogLocation="Application"
serviceAuthorizationAuditLevel="Failure"
messageAuthenticationAuditLevel="Failure"
suppressAuditFailure="true" />
</behavior>
</serviceBehaviors>
</behaviors>
Use this to decorate your methods to control who can access what:
[PrincipalPermission(SecurityAction.Demand, Role = #"DOMAIN\ActiveDirectoryGroup")]
Using this setup the PHP client should be able to get the WSDL then call your method after authorization:
$url = 'https://domain.com/service.svc?wsdl';
$username = 'username';
$password = 'password';
$options = array(
'login' => $username,
'password' => $password,
'soap_version' => SOAP_1_1,
'exceptions' => true,
'trace' => 1,
'cache_wsdl' => WSDL_CACHE_NONE,
'compression' => SOAP_COMPRESSION_ACCEPT | SOAP_COMPRESSION_GZIP,
'connection_timeout' => 60
);
$client = new SoapClient($url, $options);
$obj = new YourObject();
$obj->MyProperty = 'Test';
$obj->MyOtherProperty = 'Testing';
$result = $client->WCFMethodName(array('paramName' => $obj));
It's VERY important that the parameter name in your PHP code match the WCF parameter name EXACTLY (case sensitive).

How to set WCF security to require client certificate?

I have WCF service. I demand clients to authenticate with certificate.
This is service configuration:
<system.serviceModel>
<services>
<service name="FilmLibrary.FilmManager" behaviorConfiguration="FilmService.Service1Behavior">
<endpoint address="manager" name="certBinding" binding="basicHttpBinding" contract="FilmContract.IFilmManager" />
</service>
</services>
<bindings>
<basicHttpBinding>
<binding name="certBinding">
<security mode="Message">
<message clientCredentialType="Certificate" />
</security>
</binding>
</basicHttpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior name="FilmService.Service1Behavior">
<serviceCredentials>
<clientCertificate>
<authentication trustedStoreLocation="LocalMachine"
certificateValidationMode="PeerTrust" />
</clientCertificate>
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
</configuration>
Public key is installed in LocalMachine, Trusted People
Client configuration is as follows:
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="certBinding" 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="Message">
<message clientCredentialType="Certificate"/>
</security>
</binding>
</basicHttpBinding>
</bindings>
<behaviors>
<endpointBehaviors>
<behavior name="certBehaviour">
<clientCredentials>
<clientCertificate findValue="SubjectKey" storeLocation="CurrentUser" storeName="My" x509FindType="FindBySubjectName"/>
</clientCredentials>
</behavior>
</endpointBehaviors>
</behaviors>
<client>
<endpoint address="[...]/Service1.svc/manager"
binding="basicHttpBinding" bindingConfiguration="certBinding" behaviorConfiguration="certBehaviour"
contract="FilmsService.IFilmManager" name="certBinding" />
</client>
</system.serviceModel>
Private key is installed in Personal, current user.
Without security, service works. With security enabled - it does not. I tried several configurations and I got errors like authentication failed or that I have to set service certificate in clientCredentials element. Which I don't understand because I do not want to authenticate service at all.
I found the following guide extremely helpful and very detailed.
https://notgartner.wordpress.com/2007/09/06/using-certificate-based-authentication-and-protection-with-windows-communication-foundation-wcf/
It covers creating the service, the client, the certificates and adjusting the 2 configs.
Server:
<bindings>
<basicHttpBinding>
<binding name="secureHttpBinding">
<security mode="TransportWithMessageCredential">
<message clientCredentialType="Certificate" />
</security>
</binding>
</basicHttpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior>
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
<serviceCredentials>
<clientCertificate>
<!--only accept certificates in "Trusted People"-->
<authentication certificateValidationMode="PeerTrust" trustedStoreLocation="LocalMachine" />
</clientCertificate>
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
Client:
<bindings>
<basicHttpBinding>
<binding name="customBinding1">
<security mode="TransportWithMessageCredential">
<message clientCredentialType="Certificate" />
</security>
</binding>
</basicHttpBinding>
</bindings>
<behaviors>
<endpointBehaviors>
<behavior name="customBehavior1">
<clientCredentials>
<!--fabrkam-->
<clientCertificate storeName="My" storeLocation="CurrentUser" x509FindType="FindByThumbprint" findValue="d2 31 6a 73 1b 59 68 3e 74 41 09 27 8c 80 e2 61 45 03 b1 7e"/>
</clientCredentials>
</behavior>
</endpointBehaviors>
</behaviors>
We auto-redirect any HTTP requests to HTTPS so we have to use TransportWithMessageCredential type security. For normal Http using just Message as security type should also work.
Instead of
<serviceCredentials>
<clientCertificate>
<authentication trustedStoreLocation="LocalMachine"
certificateValidationMode="PeerTrust" />
</clientCertificate>
</serviceCredentials>
I think you are supposed to have
<serviceCredentials>
<serviceCertificate findValue="SubjectKey" storeLocation="LocalMachine" storeName="TrustedPeople" x509FindType="FindBySubjectName"/>
</serviceCredentials>
You are not authenticating the service by this, instead you are telling the service how the client is to be authenticated.
I was able to accomplish the same thing by using a customBinding, like this:
<customBinding>
<binding name="bindingName">
<security authenticationMode="UserNameOverTransport" />
<httpsTransport requireClientCertificate="true"/>
</binding>
</customBinding>
(I omitted the attributes not relevant to your case.)
As far as authenticationMode, I think you can probably use any of them--the httpsTransport with requireClientCertificate are the important parts here.