WCF : WS-Security Hell

dotnetLastly, I had to build a client to consume a web service. This task seems a bit trivial at first, but the service required an authentication mechanism based on WS-Security. Nothing really complex, the only requirement was to send a username and a password in plain text.

As usual when I build this kind of application, I used Visual Studio wizard to create the proxy to the web service, and after some tweaking in the app.config file, my application was ready to consume this web service !

Unfortunately, there is a security behavior in WCF that prevent from using plain text message based authentication over unsecure transport : i.e. you cannot send a clear password if you are using HTTP as the transport layer instead of HTTPS. That would have been nice of Microsoft to give us a way to override this mechanism easily, but they didn't (and they didn't bother to add an explicit error message in the thrown exception...)

So, if you want to send a plain-text username and password using WCF, the only way I found was to stuff the corresponding soap header when sending the request. 

Here is my code, use it at your own risks...

public class SecurityBehavior : MessageHeader, IClientMessageInspector, IEndpointBehavior
{
     private string m_sUserName;
     private string m_sPassword;
     public override string Name         
     {             
        get { return "wsse:Security"; }
     }
 
     public override string Namespace         
     {             
        get { return ""; }
     }          
 
     public string UserName         
     {             
        get { return m_sUserName; }
        set { m_sUserName = value; }         
     }          
 
     public string Password         
     {             
        get { return m_sPassword; }             
        set { m_sPassword = value; }         
     }          
 
     public void AfterReceiveReply(ref System.ServiceModel.Channels.Message reply, object correlationState)         
     {                      
 
     }          
 
     public object BeforeSendRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel)         
     {              
        request.Headers.Add(this);
        return null;
     }          
 
     public void AddBindingParameters(ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)         
     {                      
 
     }          
 
     public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)         
     {             
        clientRuntime.MessageInspectors.Add((IClientMessageInspector)this);
     }          
 
     public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)         
     {          
 
     }          
 
     public void Validate(ServiceEndpoint endpoint)         
     {          
 
     }           
 
     protected override void OnWriteHeaderContents(XmlDictionaryWriter writer, MessageVersion messageVersion)         
     {             
        writer.WriteAttributeString("xmlns", "wsse", null, "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd");
        writer.WriteStartElement("wsse:UsernameToken");
        writer.WriteElementString("wsse:Username", m_sUserName);
        writer.WriteStartElement("wsse:Password");
        writer.WriteAttributeString("Type", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText");
        writer.WriteValue(m_sPassword);                          
        writer.WriteEndElement(); //wsse:Password             
        writer.WriteEndElement(); //wsse:UsernameToken         
     }               
}

What's the trick....

So I added a behavior like this, but the client still throws an exception, complaining about the authentication type.

{"BasicHttp binding requires that BasicHttpBinding.Security.Message.ClientCredentialType be equivalent to the BasicHttpMessageCredentialType.Certificate credential type for secure messages. Select Transport or TransportWithMessageCredential security for UserName credentials."} System.Exception {System.InvalidOperationException}

There is a hack to put in the

There is a hack to put in the config file, but I can't remember exactly. Unfortunately, I lost the files associated with this project...

Contact

JP
LinkedIn - JP Thomasset
Email - Contact form

Google Followers