13

I'm have near to none experience with SOAP protocol. The service I need to connect to required header. I think this is somewhat standard in Java but in C# one must create this header by hand.

Does anyone here been able to connect to similar service: have created the header or maybe even know about some standard library which would simplify creation of header? Can you share some code or references?

I also found a clue that maybe header will be generated if using WS2005, because there is WS3 addin for it. Can anybody comment this? After quick look at this addin I found simmilar fields as in Security header, but still wasn't able to create the header.

Sergej Andrejev
  • 8,543
  • 11
  • 65
  • 107

6 Answers6

14

We were able to solve it with the following code:

public class SecurityHeader : System.ServiceModel.Channels.MessageHeader {
    public string userName;
    public string password;

    protected override void OnWriteStartHeader (System.Xml.XmlDictionaryWriter writer, System.ServiceModel.Channels.MessageVersion messageVersion)
    {
        writer.WriteStartElement("wsse", Name, Namespace);
        writer.WriteXmlnsAttribute("wsse", Namespace);
    }

    protected override void OnWriteHeaderContents (System.Xml.XmlDictionaryWriter writer, System.ServiceModel.Channels.MessageVersion messageVersion)
    {
        writer.WriteStartElement("wsse", "UsernameToken", Namespace);

        writer.WriteStartElement("wsse", "Username", Namespace);
        writer.WriteValue(userName);
        writer.WriteEndElement();

        writer.WriteStartElement("wsse", "Password", Namespace);
        writer.WriteValue(password);
        writer.WriteEndElement();

        writer.WriteEndElement();

    }

    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"; }
    }
}

This wrote the header that was required by the DataPower box.

How to use the class SecurityHeader

    public static void Main(string[] args)
    {

        var webService = new ServiceReference1.MyWebService();
        ....
       webService.Open();


        using (OperationContextScope scope = new OperationContextScope((IContextChannel)webService.InnerChannel))
        {

            var myObjRequest = GetMyObjRequest();

            MessageHeaders messageHeadersElement = OperationContext.Current.OutgoingMessageHeaders;
            messageHeadersElement.Add(SecurityHeader("UserName", "Password"))


             var res = webService.MyServe(myObjRequest);
            Console.WriteLine(res.ToString());
        }
    }
Domenico Zinzi
  • 826
  • 9
  • 16
David
  • 149
  • 1
  • 2
  • Dude, I love you! I used your code and it worked! I was integrating with "oracle on demand crm". I just had to add this line after the "Password" StartElement: `writer.WriteAttributeString("Type", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText");` Thanks! – Luís Deschamps Rudge Jan 22 '15 at 02:56
  • @David I having difficulties with passing a SOAP WSSE authentication header. When I try your code it says that there is no Open() method for the web service. By the way how did you generate the proxy class and what are the external references? – Vesnog May 24 '18 at 21:09
  • @LuísDeschampsRudge Hello, I am in a similar situation, but I could not get the above code to work in Visual Studio 2017. The compiler complains about lacking of an Open() method in the web service. Which external references did you add and how did you generate the proxy class (WSE 3.0 or Visual Studio itself)? https://stackoverflow.com/questions/50430398/c-sharp-runtime-error-when-implementing-wsse-security-headers-with-custom-fields – Vesnog May 24 '18 at 21:11
  • 2020 and it still useful, thanks for the concept – Sruit A.Suk Jan 02 '21 at 22:40
6

Funny you should mention that - I've been doing exactly that recently.

I've managed to do it using a SoapExtension which uses ChainStream to keep a copy of the original stream, just copies the stream during BeforeDeserialize and adds the header during AfterSerialize.

Adding the header is a case of reading the contents of the "new" stream (returned from ChainStream) into an XML document (XDocument in my case), adding the header, and then writing it to the original stream passed into ChainStream.

Unfortunately this is pretty dirty, and you can't (as far as I'm aware) use a new instance with appropriate authentication information when you need to.

I've got most of the way using a SoapHeader instead, adding an appropriate attribute to each method of the web service and also an appropriate field/property with an instance of the required header - but the SOAP serialization is currently giving me headaches in terms of specifying the right element names (with namespaces). It's something I've been planning to ask others about when I get the time.

Sorry not to be able to give you a full answer - and also apologies for the lack of code, it belonging to the company rather than me - but hopefully it'll at least give you a starting point.

Jon Skeet
  • 1,261,211
  • 792
  • 8,724
  • 8,929
6

it's generally very easy to add a SOAP header to your web serivce proxy in .Net. Here's a quick code sample.

Create a new SOAP header

using System.Web.Services.Protocols;

public class SoapAuthHeader : SoapHeader
{
public string Username;
public string Password;
}

In your web service proxy class:

public class MyWebServicesProxy : System.Web.Services.Protocols.SoapHttpClientProtocol {

    public SoapAuthHeader AuthHeader;

    ...

}

And then to use:

SoapAuthHeader authHeader = new SoapAuthHeader();
authHeader.Username = "username";
authHeader.Password = "password";

MyWebServicesProxy myProxy = new MyWebServicesProxy();
myProxy.AuthHeader = authHeader;

Edit: There are other ways to this and Microsoft do have a WSE library that includes WS-Security taht gives much more functionality then the simple sample above. If you need Kerberos tokens or certificate signing in your SOAP header then it's the way to go. if you jsut need to add a simple username and password for a web service operating over SSL then the exmaple may be all you need.

Edit: Quick blurb on WSE Earlier this decade when web services were going to take over the World a bunch of industry players (Microsoft, IBM, Sun etc.) got together to come up with standard ways of doing things over them. The body formed was OASIS. Since then Microsoft has released a number of versions of its WSE library to support some of the specifications but interestingly they've never been incorporated into the .Net framework even though the first version was made public around 2003.

Web services while still very popular and in my opinion a great way to integrate between different internet applications have gone a bit out of favour. One of the reasons is undoubtedly because AJAX and web services weren't the best of bed fellows, although that has improved. Web services also get pretty complicated once you start including all the additional sWSE specs and one of the thinge web services was suppossed to solve was the complexity in other RPC protocols, CORBA etc. In the meantime REST has gained a lot of popularity at the expense of Web Services and AJAX libraries often prefer it.

Web services aren't going to disappear soon by any means but they're probably not going to take over the World anytime soon either.

sipsorcery
  • 28,885
  • 23
  • 95
  • 145
  • 3
    WSE doesn't work for Visual Studio 2008 as far as I'm aware, which is really annoying. The SoapHeader route is nice in theory, but in my *particular* case I've got a problem getting the right namespace for the elements within the header :( – Jon Skeet Apr 09 '09 at 14:12
  • @Jon: That's a bummer alright. I've had occassion in the past to have to delve into the Remoting and SOAP serialisation chains and while it's not prohibitively difficult it's definitely not as elegant as creating custom headers or WSE. – sipsorcery Apr 09 '09 at 14:18
  • 1
    Just to make it clear - I'm find for the elements which are my own types, where I can use SoapTypeAttribute and specify the name/namespace there. It's the lack of a namespace property in SoapElementAttribute which is biting me :( – Jon Skeet Apr 09 '09 at 14:23
  • 1
    Can you tell more about WSE library? – Sergej Andrejev Apr 09 '09 at 14:28
  • Also it seems a bit old. Released on 2004 – Sergej Andrejev Apr 09 '09 at 14:29
  • @sipwiz: I know this question is few years old, but I am just curious why couldn't you use the WCF here? Also, http://stackoverflow.com/q/32703632/247184 - then does it also mean that WSE needs to be used here? It doesn't make sense - could you please provide some input? – VoodooChild Sep 25 '15 at 10:16
  • 1
    @VoodooChild well the original question was about SOAP which is one of the transport layers that WCF can use. In the 6.5 years since this question was asked WCF has indeed made some aspects of integrating web services easier but for my own hosted WCF services I still need to set a custom authentication header. Thankfully it's now a lot easier to do so and doesn't require the WSE library. – sipsorcery Sep 25 '15 at 22:30
1

There is an open source custom Binding called ClearUserNameBinding, this binding helps passing UserNameToken as clearText on Http. This helped me out when a java based web service needed to be consumed using a WCF cllient.

http://code.google.com/p/wcf-clear-username-binding/ http://webservices20.blogspot.com/2008/11/introducing-wcf-clearusernamebinding.html

K Man
  • 27
  • 2
1

Thank you folks. All your codes and ideas collected as follows.

Class:

 public class SecurityHeader : System.ServiceModel.Channels.MessageHeader
{
    public string userName;
    public string password;

    public SecurityHeader(string userName, string password)
    {
        this.userName = userName;
        this.password = password;
    }

    protected override void OnWriteStartHeader(System.Xml.XmlDictionaryWriter writer, System.ServiceModel.Channels.MessageVersion messageVersion)
    {
        writer.WriteStartElement("wsse", Name, Namespace);
        writer.WriteXmlnsAttribute("wsse", Namespace);
    }

    protected override void OnWriteHeaderContents(System.Xml.XmlDictionaryWriter writer, System.ServiceModel.Channels.MessageVersion messageVersion)
    {
        writer.WriteStartElement("wsse", "UsernameToken", Namespace);
        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.WriteEndElement();
    }

    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"; }
    }
}

Using:

     using (IdsTestSvc.MedulaIlacDagitimSistemiIslemleriClient client = new IdsTestSvc.MedulaIlacDagitimSistemiIslemleriClient("MedulaIlacDagitimSistemiIslemleriPort"))
                    {
                        using (OperationContextScope scope = new OperationContextScope(client.InnerChannel))
                        {
                            MessageHeaders messageHeadersElement = OperationContext.Current.OutgoingMessageHeaders;
                            messageHeadersElement.Add(new SecurityHeader(_main.IdsFirmCode, _main.IdsFirmCode));
 ....Do something
                        }
                    }
0

Solution by Domenico Zinzi worked for me with below amendments: I created a constructor in SecurityHeader Class:

public SecurityHeader(string userName, string password){ userName = userName; password = password; }

and while calling it I used "New" keyword, as mentioned below:

messageHeadersElement.Add(New SecurityHeader("UserName", "Password"))

And it worked :)

P.S. Not able to add this as comments thats why adding it here.

Nazir A
  • 31
  • 1
  • 4