C# – BasicHttpBinding equivalent CustomBinding using WCF Client and PHP WebService

basichttpbindingcntlmwcf-securityweb services

I was running my Visual Studio 2008 Unit Test C# with a WebService PHP using WCF and I received the following error:

System.ServiceModel.Security.MessageSecurityException:
The HTTP request is unauthorized with
client authentication scheme
'Anonymous'. The authentication header
received from the server was
'NTLM,Basic realm="(null)"'. —>
System.Net.WebException: The remote
server returned an error: (401)
Unauthorized..

I am using Windows XP SP3 machine, unit test VS 2008 and WCF for connect to PHP WebService.

I have no control about PHP WebService. I cannot modify it (neither configuration security).

Here is my config file for the client that use the webservice PHP:

 <system.serviceModel>

    <extensions>
      <bindingElementExtensions>
        <add name="customTextMessageEncoding"
             type="COMPANY.IntegracionEasyVista.CustomTextEncoder.CustomTextMessageEncodingElement,COMPANY.IntegracionEasyVista.CustomTextEncoder, Version=1.0.0.0, Culture=neutral, PublicKeyToken=9744987c0853bf9e" />
      </bindingElementExtensions>

    </extensions>


        <bindings>

              <customBinding>
                    <binding name="ISO8859Binding">
                          <customTextMessageEncoding messageVersion="Soap11WSAddressing10"
                                encoding="ISO-8859-1" />
                          <httpTransport />
                    </binding>
              </customBinding>
        </bindings>
        <client>
              <endpoint address="http://serverphp/webservice/SmoBridge.php"
                    binding="customBinding" bindingConfiguration="ISO8859Binding"
                    contract="ServiceEasyVista.WebServicePortType" name="EasyVistaSvcEndPoint" />
        </client>
    </system.serviceModel>

Really, I use C# code:

var endpointAddress = Config.AppSettings.Settings[EasyVistaSvcEndPointAddress].Value;
var endpoint = new System.ServiceModel.EndpointAddress(endpointAddress);

var endpointBinding = new System.ServiceModel.Channels.CustomBinding();
endpointBinding.Name = "ISO8859Binding";

var bindingElement = new  IntegracionEasyVista.CustomTextEncoder.CustomTextMessageBindingElement();

bindingElement.Encoding = "ISO-8859-1";
bindingElement.MessageVersion = System.ServiceModel.Channels.MessageVersion.Soap11WSAddressing10; // "Soap11WSAddressing10" 
endpointBinding.Elements.Add(bindingElement);

var transportBinding = new HttpTransportBindingElement();
endpointBinding.Elements.Add(transportBinding);

ConfigurarBinding(endpointBinding);

var svcClient = new WebServicePortTypeClient(endpointBinding, endpoint);
return svcClient;

Update: tests

I add this line:

transportBinding.AuthenticationScheme = System.Net.AuthenticationSchemes.Negotiate;

I get this error: The HTTP request is unauthorized with client authentication scheme 'Anonymous'. The authentication header received from the server was 'NTLM,Basic realm="(null)"'

I add this line:

transportBinding.AuthenticationScheme = System.Net.AuthenticationSchemes.Basic;

I get thiserror:

System.ServiceModel.CommunicationObjectFaultedException:
El objeto de comunicación,
System.ServiceModel.Channels.ServiceChannel,
no se puede usar para la comunicación
porque se encuentra en el estado
Faulted.

//Server stack trace: // en
System.ServiceModel.Channels.CommunicationObject.Close(TimeSpan
timeout)

I add this line:

 transportBinding.AuthenticationScheme = System.Net.AuthenticationSchemes.Ntlm;

I get thiserror:

/Exception =
System.ServiceModel.CommunicationException:
Versión de mensaje no reconocida.

//Server stack trace: // en
System.ServiceModel.Channels.ReceivedMessage.ReadStartEnvelope(XmlDictionaryReader
reader)

// en
System.ServiceModel.Channels.StreamedMessage..ctor(XmlDictionaryReader
reader, Int32 maxSizeOfHeaders,
MessageVersion desiredVersion)

// en
System.ServiceModel.Channels.Message.CreateMessage(XmlDictionaryReader
envelopeReader, Int32
maxSizeOfHeaders, MessageVersion
version)

// en
System.ServiceModel.Channels.Message.CreateMessage(XmlReader
envelopeReader, Int32
maxSizeOfHeaders, MessageVersion
version)

// en
CustomTextEncoder.CustomTextMessageEncoder.ReadMessage(Stream
stream, Int32 maxSizeOfHeaders, String
contentType) en
E:\TFS\pro\CustomTextEncoder\CustomTextMessageEncoder.cs:línea
66

// en
System.ServiceModel.Channels.MessageEncoder.ReadMessage(Stream
stream, Int32 maxSizeOfHeaders)

// en
CustomTextEncoder.CustomTextMessageEncoder.ReadMessage
(ArraySegment`1 buffer, BufferManager
bufferManager, String contentType) en
E:\TFS\pro\CustomTextEncoder\CustomTextMessageEncoder.cs:línea
60

// en
System.ServiceModel.Channels.MessageEncoder.ReadMessage(Stream
stream, BufferManager bufferManager,
Int32 maxBufferSize, String
contentType)

// en
System.ServiceModel.Channels.HttpInput.ReadChunkedBufferedMessage(Stream
inputStream)

I think, the following is right configuration (if were basichttpbinding)

<security mode="TransportCredentialOnly">
    <transport clientCredentialType="Basic" />
</security>

Web service of WCF in basichttpbinding might not be flexible enough. CustomBinding as the name describes that it alllows users design their own web service binding.

I need use CustomBinding and SecurityMode (like basichttpbinding): WCF BasicHttpBinding equivalent CustomBinding

I need Security Mode: TransportCredentialOnly, Basic, HTTP transport and encoding="ISO-8859-1"

http://www.codemeit.com/security/wcf-basichttpbinding-equivalent-custombinding.html

http://social.msdn.microsoft.com/Forums/en/wcf/thread/6cefadf1-9939-4f3b-a502-2d79a30c7d3a

http://offroadcoder.com/2008/03/23/CallingYourNusoapPHPWebServiceFromWCF.aspx

http://msdn.microsoft.com/en-us/library/ms731092(v=VS.90).aspx

I try again using this configuration, and I get the error:

svcPro.ClientCredentials.UserName.UserName = "MY USER";
svcPro.ClientCredentials.UserName.Password = "XXXX";

Config:

<customBinding>

<binding name="ISO8859Binding">
    <customTextMessageEncoding messageVersion="Soap11WSAddressing10"        encoding="ISO-8859-1" />

    <!--<textMessageEncoding MessageVersion="Soap11" />-->
    <!--<httpTransport />-->
    <httpTransport authenticationScheme="Basic" />

</binding>

</customBinding>

The error (InnerException is null):

Error: Unrecognized message version

Tipo:
System.ServiceModel.CommunicationException
Mensaje: Versión de mensaje no
reconocida. StackTrace: Server stack
trace:

en
System.ServiceModel.Channels.ReceivedMessage.ReadStartEnvelope(XmlDictionaryReader
reader)

en
System.ServiceModel.Channels.StreamedMessage..ctor(XmlDictionaryReader
reader, Int32 maxSizeOfHeaders,
MessageVersion desiredVersion)

en
System.ServiceModel.Channels.Message.CreateMessage(XmlDictionaryReader
envelopeReader, Int32
maxSizeOfHeaders, MessageVersion
version)

en
System.ServiceModel.Channels.Message.CreateMessage(XmlReader
envelopeReader, Int32
maxSizeOfHeaders, MessageVersion
version)

en
Reale.IntegracionEasyVista.CustomTextEncoder.CustomTextMessageEncoder.ReadMessage(Stream
stream, Int32 maxSizeOfHeaders, String
contentType) en
E:\IntegracionEasyVista\CustomTextEncoder\CustomTextMessageEncoder.cs:línea
66

en
System.ServiceModel.Channels.MessageEncoder.ReadMessage(Stream
stream, Int32 maxSizeOfHeaders)
en Reale.IntegracionEasyVista.CustomTextEncoder.CustomTextMessageEncoder.ReadMessage(ArraySegment`1
buffer, BufferManager bufferManager,
String contentType) en
E:\IntegracionEasyVista\CustomTextEncoder\CustomTextMessageEncoder.cs:línea
60

en
System.ServiceModel.Channels.MessageEncoder.ReadMessage(Stream
stream, BufferManager bufferManager,
Int32 maxBufferSize, String
contentType)

en
System.ServiceModel.Channels.HttpInput.ReadChunkedBufferedMessage(Stream
inputStream)

en
System.ServiceModel.Channels.HttpInput.ParseIncomingMessage(Exception&
requestException)

en
System.ServiceModel.Channels.HttpChannelFactory.HttpRequestChannel.HttpChannelRequest.WaitForReply(TimeSpan
timeout)

en
System.ServiceModel.Channels.RequestChannel.Request(Message
message, TimeSpan timeout)

en
System.ServiceModel.Dispatcher.RequestChannelBinder.Request(Message
message, TimeSpan timeout)

en
System.ServiceModel.Channels.ServiceChannel.Call(String
action, Boolean oneway,
ProxyOperationRuntime operation,
Object[] ins, Object[] outs, TimeSpan
timeout)

en
System.ServiceModel.Channels.ServiceChannel.Call(String
action, Boolean oneway,
ProxyOperationRuntime operation,
Object[] ins, Object[] outs)

en
System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage
methodCall, ProxyOperationRuntime
operation)

en
System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage
message)

More info: part of WDSL of Service.php

<?xml version="1.0" encoding="ISO-8859-1" ?> 
 <definitions 
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" 
xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:si="http://soapinterop.org/xsd" 
xmlns:tns="http://192.168.110.50/WebService" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" 
xmlns="http://schemas.xmlsoap.org/wsdl/" targetNamespace="http://192.168.110.50/WebService">
 <types>
 <xsd:schema targetNamespace="http://192.168.110.50/WebService">
  <xsd:import namespace="http://schemas.xmlsoap.org/soap/encoding/" /> 
  <xsd:import namespace="http://schemas.xmlsoap.org/wsdl/" /> 
  </xsd:schema>
  </types>

Best Answer

Your PHP service requires authentication but your client doesn't send any. Change your custom binding to support authentication. You can set authentication in httpTransport element:

<httpTranposrt authenticationScheme="..." />

Use Basic or Negotiate. Basic will require you to provide client credentials when communicating with the service. Negotiate will require that both service and client are in the same domain.

Related Topic