8

I have a WCF SOAP consumer that is implemented by Visual Studio 2012 from a WSDL. The WSDL was generated by PeopleTools. The base object is of type System.ServiceModel.ClientBase.

I need the SOAP request to resemble:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:sch="http://xmlns.oracle.com/Enterprise/Tools/schemas">
    <soapenv:Header>
        <wsse:Security soap:mustUnderstand="1" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
            <wsse:UsernameToken>
                <wsse:Username>[plain text username goes here]</wsse:Username>
                <wsse:Password>[plain text password goes here]</wsse:Password>
            </wsse:UsernameToken>
        </wsse:Security>
    </soapenv:Header>
    <soapenv:Body>
        <sch:InputParameters>
            <Last_Name>Aren</Last_Name>
            <First_Name>Cambre</First_Name>
        </sch:InputParameters>
    </soapenv:Body>
</soapenv:Envelope>

Here's the closest we can get:

<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing">
    <s:Header>
        <a:Action s:mustUnderstand="1">http://schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue</a:Action>
        <a:MessageID>urn:uuid:3cc3f2ca-c647-466c-b38b-f2423462c837</a:MessageID>
        <a:ReplyTo>
            <a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>
        </a:ReplyTo>
        <a:To s:mustUnderstand="1">http://[internal URL to soap listener]</a:To>
    </s:Header>
    <s:Body>
        <t:RequestSecurityToken Context="uuid-7db82975-2b22-4236-94a1-b3344a0bf04d-1" xmlns:t="http://schemas.xmlsoap.org/ws/2005/02/trust">
            <t:TokenType>http://schemas.xmlsoap.org/ws/2005/02/sc/sct</t:TokenType>
            <t:RequestType>http://schemas.xmlsoap.org/ws/2005/02/trust/Issue</t:RequestType>
            <t:KeySize>256</t:KeySize>
            <t:BinaryExchange ValueType=" http://schemas.xmlsoap.org/ws/2005/02/trust/tlsnego" EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">FgMBAFoBAABWAwFQ9IhUFGUO6tCH+0baQ0n/3us//MMXzQA78Udm4xFj5gAAGAAvADUABQAKwBPAFMAJwAoAMgA4ABMABAEAABX/AQABAAAKAAYABAAXABgACwACAQA=</t:BinaryExchange>
        </t:RequestSecurityToken>
    </s:Body>
</s:Envelope>

You'll notice two problems:

  • No plaintext WSSE credentials. Passes a binary form of the credentials that the service won't use.
  • Authentication is in Body, not Header.
  • The request omits InputParameters.

Here's the essential C# code:

var service = new ServiceWithBizarreNameFromPeoplesoft();

if (service.ClientCredentials == null)
   throw new NullReferenceException();
service.ClientCredentials.UserName.UserName = "test";
service.ClientCredentials.UserName.Password = "password";

var binding = new WSHttpBinding(SecurityMode.TransportWithMessageCredential) {Security = new WSHttpSecurity()};
service.Endpoint.Binding = binding;

binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;
binding.Security.Message.ClientCredentialType = MessageCredentialType.UserName;
binding.Security.Mode = SecurityMode.Message;

var input = new InputParameters { Last_Name = "Cambre", First_Name = "Aren" };

var returnData = service.BizarrePeopleSoftNameForMethod(input);

There's no HTTP layer security, and transport is SSL-encrypted. Authentication is only based on the SOAP message.

Aren Cambre
  • 6,540
  • 9
  • 30
  • 36

2 Answers2

11

That is request for WS-SecureConversation token. It is used by WSHttpSecurity by default unless you change its EstablishSecurityContext property to false. Use this binding instead:

var binding = new BasicHttpBinding(BasicHttpSecurityMode.TransportWithMessageCredential);    
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;
binding.Security.Message.ClientCredentialType = BasicHttpMessageCredentialType.UserName;

It will use SOAP 1.1 with UserName token and it will require HTTPS transport.

Edit:

For testing without HTTPS try to use this custom binding:

var securityElement = SecurityBindingElement.CreateUserNameOverTransportBindingElement();
securityElement.AllowInsecureTransport = true;

var encodingElement = new TextMessageEncodingBindingElement(MessageVersion.Soap11, Encoding.UTF8);
var transportElement = new HttpTransportBindingElement();

var binding = new CustomBinding(securityElement, encodingElement, transportElement);
Ladislav Mrnka
  • 360,892
  • 59
  • 660
  • 670
  • Thanks. Looks like the `ClientCredentialType` has to be a `BasicHttpMessageCredentialType`, not a `MessageCredentialType`. Now one problem: I am using HTTP for now so that I can sniff the network conneciton using Wireshark. Is there any way to override the behavior to require HTTPS? I investigated changing `binding.Security.Mode`, but seems none of the options would allow this. – Aren Cambre Jan 15 '13 at 15:31
  • Thanks, I have fixed the first example and added one more option for testing without HTTPS. – Ladislav Mrnka Jan 15 '13 at 16:04
  • Ooh, this is really close! Wireshark shows the communication was successful, but .NET Framework doesn't like the response: "**Security processor was unable to find a security header in the message. This might be because the message is an unsecured fault or because there is a binding mismatch between the communicating parties. This can occur if the service is configured for security and the client is not using security.**" Investigating options now... – Aren Cambre Jan 15 '13 at 16:52
  • 2
    Got it. Also needs `securityElement.EnableUnsecuredResponse = true`. – Aren Cambre Jan 15 '13 at 17:01
  • In such case also try adding `securityElement.IncludeTimestamp = false` and when you move to HTTPS remove `AllowInsecureTransport` and change `transportElement` to `HttpsTransportBindingElement`. The original binding will not work due to unsecured response. – Ladislav Mrnka Jan 15 '13 at 17:07
  • Thank you. So I see that in this case, there were two problems at play: problems getting this to work over HTTP, and problems with .NET WCF not liking the lack of a security header in the response? – Aren Cambre Jan 15 '13 at 17:13
  • Yes. WCF is quite restrictive when it comes to security. It tries to push best practices so hard that interoperability with other platforms is often very hard. – Ladislav Mrnka Jan 15 '13 at 17:19
  • Really, these other platforms should also be following best practices. – Aren Cambre Jan 15 '13 at 17:41
-1

This looks to me like wsHttpBindings with Transport security using basic username password authentication.

These lines look wrong to me:

binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;
binding.Security.Message.ClientCredentialType = MessageCredentialType.UserName;
binding.Security.Mode = SecurityMode.Message;

Here's how I would expect to see this configured in your app or web.config

<bindings>
  <wsHttpBinding>
    <binding name="ws" >
      <security mode="Transport">
        <transport clientCredentialType="Basic" proxyCredentialType="Basic" />
      </security>
    </binding>
  </wsHttpBinding>
</bindings>

<client>
  <endpoint address="http://www.bla.com/webservice" binding="basicHttpBinding" contract="bla.IService" name="ws" />
</client>

Then the code would look like this:

var service = new GeneratedProxyClient("basic");
service.ClientCredentials.UserName.UserName = "test";
service.ClientCredentials.UserName.Password = "password";
var input = new InputParameters { Last_Name = "Cambre", First_Name = "Aren" };
var returnData = service.BizarrePeopleSoftNameForMethod(input);

Might be better explained here --> http://msdn.microsoft.com/en-us/library/ms733775.aspx

Frank.Germain
  • 628
  • 6
  • 11
  • Thanks, but not using basic authentication. The HTTP layer has no authentication. Also, I get an error when using `"basic"` as an argument of `var service = new ServiceWithBizarreNameFromPeoplesoft()`. Apparently, if you pass a single string, it expects that to be the name of an endpoint element. That appears to be supported by http://msdn.microsoft.com/en-us/library/ms574925.aspx. – Aren Cambre Jan 15 '13 at 14:58