Search code examples
.netweb-servicessoapcredentials

how to add SOAP Security header


I've read a lot of articles and answers but I couldn't work it out.

I'm using .NET Framework 4.0 on my project. So, I first added the WebService as a Service Reference and get the bindings on my app.config. And I'm going to be listing my attempts


Attempt #1

I instantiated the service like this and added the credentials:

On App.Config

<binding name="SubscriptionWSImplServiceSoapBinding" >
  <security mode="TransportWithMessageCredential">
    <transport clientCredentialType="Basic"/>
  </security>
</binding>

SubscriptionWSImplServiceClient ClientWs = new SubscriptionWSImplServiceClient();
ClientWs.ClientCredentials.UserName.UserName = "some-username";
ClientWs.ClientCredentials.UserName.Password = "some-password";

var SomeResposne = ClientWs.RunMethod(); //I have exception at this point

I get an Exception as below:

 System.ArgumentException: The provided URI scheme 'http' is invalid;
 expected 'https'. Parameter name: via at System.ServiceModel.Channels

The exceptions says that the web service's url must be HTTPS but the given web service url to me is HTTP not HTTPS.

But I did not mind the HttpS and went for my 2nd attempt.


Attempt #2

As stated in this answer I tried as below:

BasicHttpBinding binding = new BasicHttpBinding(BasicHttpSecurityMode.TransportWithMessageCredential);
binding.Security.Message.ClientCredentialType = BasicHttpMessageCredentialType.UserName;
CustomBinding customBinding = new CustomBinding(binding);
SecurityBindingElement element = customBinding.Elements.Find<SecurityBindingElement>();
element.IncludeTimestamp = false;

EndpointAddress address = new EndpointAddress("https://www.GivenServiceUrl.com/WebService"); //Note that the url does not end as ..svc or ..asmx! Just the name like this with no dot or extension


SubscriptionWSImplServiceClient ClientWs = new SubscriptionWSImplService(customBinding, address);
ClientWs.ClientCredentials.UserName.UserName = "some-username";
ClientWs.ClientCredentials.UserName.Password = "some-password";

var SomeResposne = ClientWs.RunMethod(); //I have exception at this point

I get an Exception as below:

System.ServiceModel.Security.SecurityNegotiationException: Could not establish trust relationship for the SSL/TLS secure channel with authority 'https://www.GivenServiceUrl.com/WebService'

Therefore, I went for my 3rd attempt which I gave the reference as WebReference not as ServiceReference on my project.


Attempt #3

SubscriptionWSImplService ClientWs2 = new SubscriptionWSImplService();
var SomeResposne = ClientWs.RunMethod(); //I have exception at this point

I don't know how to send the security header at this point.

I get an Exception as below:

 System.Web.Services.Protocols.SoapHeaderException: An error was
 discovered processing the <wsse: Security> header

But in here, I could not add the necessary header. Could you help me with this?


Attempt 1 or Attempt 2 or Attempt 3, which one should be used also considering that I use Framework 4.0? And how can I solve the problem?

According to document the final request SOAP must be looking like this:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:sub="http://subscription.services.ws.fourplay.com.tr/">
   <soapenv:Header>
      <wsse:Security soapenv:mustUnderstand="1" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
         <wsse:UsernameToken wsu:Id="UsernameToken-12" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
            <wsse:Username>admin</wsse:Username>
            <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">123456</wsse:Password>
           <wsse:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">1lcj+WbCMlrPyhcud4QxiQ==</wsse:Nonce>
           <wsu:Created>2012-06-05T10:23:26.879Z</wsu:Created>
        </wsse:UsernameToken>
     </wsse:Security>
   </soapenv:Header>
   <soapenv:Body>
      <sub:RunMethod>
         <!--Optional:-->
         <sub:opaque id>555555555</sub:opaque id>
         <sub:params>
            <!--Optional:-->
            <name>GUID</name>
            <!--Optional:-->
            <value>2fc4ce1d-645e-41f4-811e-28510a02a17f </value>
         </sub:params>      
</sub: RunMethod >

Solution

  • I solved it by using WCF without any credentials declared. I did it by building my custom header I got help from this link

    public class SoapSecurityHeader : MessageHeader
        {
            private readonly string _password, _username, _nonce;
            private readonly DateTime _createdDate;
    
            public SoapSecurityHeader(string id, string username, string password, string nonce)
            {
                _password = password;
                _username = username;
                _nonce = nonce;
                _createdDate = DateTime.Now;
                this.Id = id;
            }
    
            public string Id { get; set; }
    
            public override string Name
            {
                get { return "Security"; }
            }
    
            public override string Namespace
            {
                get { return "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"; }
            }
    
            protected override void OnWriteStartHeader(XmlDictionaryWriter writer, MessageVersion messageVersion)
            {
                writer.WriteStartElement("wsse", Name, Namespace);
                writer.WriteXmlnsAttribute("wsse", Namespace);
            }
    
            protected override void OnWriteHeaderContents(XmlDictionaryWriter writer, MessageVersion messageVersion)
            {
                writer.WriteStartElement("wsse", "UsernameToken", Namespace);
                writer.WriteAttributeString("Id", "UsernameToken-10");
                writer.WriteAttributeString("wsu", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd");
    
                writer.WriteStartElement("wsse", "Username", Namespace);
                writer.WriteValue(_username);
                writer.WriteEndElement();
    
                writer.WriteStartElement("wsse", "Password", Namespace);
                writer.WriteAttributeString("Type", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText");
                writer.WriteValue(_password);
                writer.WriteEndElement();
    
                writer.WriteStartElement("wsse", "Nonce", Namespace);
                writer.WriteAttributeString("EncodingType", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary");
                writer.WriteValue(_nonce);
                writer.WriteEndElement();
    
                writer.WriteStartElement("wsse", "Created", Namespace);
                writer.WriteValue(_createdDate.ToString("YYYY-MM-DDThh:mm:ss"));
                writer.WriteEndElement();
    
                writer.WriteEndElement();
            }
        }
    

    and to how to use the header I got it from this link.