Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Correct way communicate WSSE Usernametoken for SOAP webservice

I am attempting to consume a web service through its corresponding wsdl. This service is dependent upon authentication conforming to Web Services Security Basic Security Profile 1.0 including that the correct xmls namespace of http://docs.oasis-open.org/wss/2004/01/oasis-200401wss-wssecurity-secext-1.0.xsd must be included in the request.

Example:

<wsse:UsernameToken xmlns:wsse='http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd' >    <wsse:Username>       Bob    </wsse:Username>    <wsse:Password Type='http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText'>       1234    </wsse:Password> </wsse:UsernameToken> 

My first attempts were along the lines of Add Service Reference targeting the wsdl and from the generated proxies using them as such

ServicePointManager.ServerCertificateValidationCallback =      (object s, X509Certificate certificate, X509Chain chain,                      SslPolicyErrors sslPolicyErrors) => true;  var basicHttpBinding = new BasicHttpBinding(BasicHttpSecurityMode.Transport); basicHttpBinding.Security.Transport.ClientCredentialType =                                                  HttpClientCredentialType.Basic;  var endpoint = new EndpointAddress("https://secure-ausomxana.crmondemand.com/..."  using (var client = new ContactClient(basicHttpBinding, endpoint)) {      var credential = client.ClientCredentials.UserName;     credential.UserName = "bob";     credential.Password = "1234";      var input = ...         var output = client.ContactQueryPage(input); } 

However attempting to interrogate the SOAP messages with Fiddler I see that no UsernameToken element has been added.

What is the correct way to fulfill this contract?

Edit: following the response from @John Saunders I attempted to alter my code to use a wsHttpBinding

var wsHttpBinding = new WSHttpBinding(SecurityMode.Transport); wsHttpBinding.Security.Transport.ClientCredentialType =                                          HttpClientCredentialType.Basic; 

Using this binding the SOAP message becomes

<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">document/urn:crmondemand/ws/ecbs/contact/10/2004:ContactQueryPage</a:Action>     <a:MessageID>urn:uuid:17807f44-1fcasfdsfd</a:MessageID>     <a:ReplyTo>       <a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>     </a:ReplyTo>     <a:To s:mustUnderstand="1">https://secure-ausomxana.crmondemand.com/Services/Integration</a:To>   </s:Header>   <s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">     <ContactQueryPage_Input xmlns="urn:crmondemand/ws/ecbs/contact/10/2004">       <ListOfContact xmlns="urn:/crmondemand/xml/Contact/Query">         <Contact>           <Id>1-asdfd</Id>         </Contact>       </ListOfContact>     </ContactQueryPage_Input>   </s:Body> </s:Envelope> 

This adds the Header element, as opposed to the wsse:UsernameToken element for reference the original soap message using the BasicHttpBinding is

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">   <s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">     <ContactQueryPage_Input xmlns="urn:crmondemand/ws/ecbs/contact/10/2004">       <ListOfContact xmlns="urn:/crmondemand/xml/Contact/Query">         <Contact>           <Id>1-asdfds</Id>         </Contact>       </ListOfContact>     </ContactQueryPage_Input>   </s:Body> </s:Envelope> 

If I change the binding to be

var wsHttpBinding = new WSHttpBinding(SecurityMode.TransportWithMessageCredential); wsHttpBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic; wsHttpBinding.Security.Message.ClientCredentialType = MessageCredentialType.UserName; 

The SOAP message I get out is

<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">   <s:Header>     <a:Action s:mustUnderstand="1">http://schemas.xmlsoap.org/ws/2005/02/trust/RST/SCT</a:Action>     <a:MessageID>urn:uuid:eeb75457-f29e-4c65-b4bf-b580da26e0c5</a:MessageID>     <a:ReplyTo>       <a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>     </a:ReplyTo>     <a:To s:mustUnderstand="1">https://secure-ausomxana.crmondemand.com/Services/Integration</a:To>     <o:Security xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" s:mustUnderstand="1">       <u:Timestamp u:Id="_0">         <u:Created>2011-05-02T13:30:09.360Z</u:Created>         <u:Expires>2011-05-02T13:35:09.360Z</u:Expires>       </u:Timestamp>       <o:UsernameToken u:Id="uuid-dc3605a0-6878-42f4-b1f2-37d5c04ed7b4-2">         <o:Username>Bob</o:Username>         <o:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">1234</o:Password>       </o:UsernameToken>     </o:Security>   </s:Header>   <s:Body>     <t:RequestSecurityToken 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:Entropy>         <t:BinarySecret u:Id="uuid-7195ad74-580b-4e52-9e2c-682e5a684345-1" Type="http://schemas.xmlsoap.org/ws/2005/02/trust/Nonce">bI4xuyKwZ8OkQYBRnz2LDNV+zhIOnl0nwP24yI1QAwA=</t:BinarySecret>       </t:Entropy>       <t:KeySize>256</t:KeySize>     </t:RequestSecurityToken>   </s:Body> </s:Envelope> 

This seems to be very close however this appears to have actually encrypted the body of the soap message which is something I do NOT want to happen.

If I specify wsHttpBinding.Security.Message.ClientCredentialType = MessageCredentialType.UserName; with only using SecurityMode.Transport it goes back to where it says its anonymous.

What is the final hurdle I'm not able to clear on this?

Final Solution: Figured I'd post this incase it helps someone, there's not really much different here other the UserToken object is wrapped in a Security node which is what my service provider required and seems to be how its output from my previous examples from what I could get generated.

<system.serviceModel>   <bindings>         <basicHttpBinding>       <binding name="Contact" closeTimeout="00:01:00" openTimeout="00:01:00"           receiveTimeout="00:10:00" sendTimeout="00:01:00" allowCookies="false"           bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard"           maxBufferSize="524288" maxBufferPoolSize="524288" maxReceivedMessageSize="524288"           messageEncoding="Text" textEncoding="utf-8" transferMode="Buffered"           useDefaultWebProxy="true">         <readerQuotas maxDepth="32" maxStringContentLength="65536" maxArrayLength="131072"             maxBytesPerRead="32768" maxNameTableCharCount="131072" />         <security mode="Transport">           <transport clientCredentialType="None" proxyCredentialType="None"               realm="" />           <message clientCredentialType="UserName" algorithmSuite="Default" />         </security>       </binding>              </basicHttpBinding>   </bindings>   <client>     <endpoint address="https://secure-ausomxana.crmondemand.com/Services/Integration"        binding="basicHttpBinding" bindingConfiguration="Contact"        contract="OnDemandContactService.Contact" name="OnDemand.Contact.Endpoint">       <headers>                 <wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">           <wsse:UsernameToken>             <wsse:Username>USERNAME</wsse:Username>             <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">PASSWORD</wsse:Password>           </wsse:UsernameToken>         </wsse:Security>       </headers>     </endpoint>   </client> </system.serviceModel> 

See With C#, WCF SOAP consumer that uses WSSE plain text authentication? for how to configure it using code and not config

like image 508
Chris Marisic Avatar asked Apr 29 '11 19:04

Chris Marisic


People also ask

How do you handle security in SOAP services?

You add the username and password as variables so that each time you generate a SOAP message, you generate these as part of the header. This way, whenever the user calls the web service, it requires the password and username. DreamFactory can do more than these basic security measures.

What is Wsse UsernameToken?

The WSS 1.1 Username Token Profile allows digest passwords to be sent in a wsse:UsernameToken of a SOAP message. Two more optional elements are included in the wsse:UsernameToken in this case: wsse:Nonce and wsse:Created . A nonce is a random value that the sender creates to include in each UsernameToken that it sends.

How can SOAP authentication help in a multi server environment?

In a multiple server environments, the above technique of SOAP authentication helps in the following way. Since the SOAP body is encrypted, it will only be able to be decrypted by the web server that hosts the web service. This is because of how the SOAP protocol is designed.

What is WS Security explain in detail with an example?

WS-Security mechanisms can be used to accommodate a wide variety of security models and encryption technologies. WS-Security is a message-level standard that is based on securing SOAP messages through XML digital signature, confidentiality through XML encryption, and credential propagation through security tokens.


1 Answers

If you need to send UserName over HTTPS you can use standard approach (if your WSDL is correctly defined this should be created for you automatically by adding service reference):

<bindings>   <basicHttpBinding>     <binding name="secured">       <security mode="TransportWithMessageCredential">         <message clientCredentialType="UserName" />       </security>     </binding>   </basicHttpBinding> </bindings> <client>   <endpoint name="..." address="https://..." contract="..." binding="basicHttpBinding"             bindingConfiguration="secured" /> </client> 

Ar you can define binding in code:

var basicHttpBinding = new BasicHttpBinding(BasicHttpSecurityMode.TransportWithMessageCredential); basicHttpBinding.Security.Message.ClientCredentialType = BasicHttpMessageCredentialType.UserName; 

You will set credentials in proxy as you do it now:

client.ClientCredentials.UserName.UserName = "bob"; client.ClientCredentials.UserName.Password = "1234"; 

If you only need UserNameToken profile over HTTP without any other WS-Security infrastructure the simplest approach is using ClearUserNameBinding.

If you need same user name and password for all requests from the client you can use simple basicHttpBinding without any security and include the static header from configuration:

<client>   <endpoint ...>     <headers>       <wsse:UsernameToken xmlns:wsse='http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd' >         <wsse:Username>Bob</wsse:Username>         <wsse:Password Type='http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText'>            1234         </wsse:Password>       </wsse:UsernameToken>     </headers>   </endpoint> </client>  

If you need anything more complex show the relevant part of WSDL (security assertion) or sample SOAP request. Also mention if you are required to use HTTP or HTTPS.

like image 171
Ladislav Mrnka Avatar answered Oct 02 '22 10:10

Ladislav Mrnka