1

I have this SOAP flux that I need to send to a remote service, with a wsse:Security node that is expected to look like this:

<wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
                xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
    <wsse:UsernameToken wsu:Id="UsernameToken-5FJPET4ATPQZMREXU8C51DK9P6OTW022">
        <wsse:Username>NotQuiteThatStupid</wsse:Username>
        <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">DonTBotherReadingThisNode</wsse:Password>
    </wsse:UsernameToken>
</wsse:Security>

I've tried to do it with WCF, as it looks like the current standard and is rather well documented. Long story short, I couldn't find a way to make it work.

At the end of my wits, I took inspiration from http://blog.aggregatedintelligence.com/2010/06/wcf-transportwithmessagecredential-and.html and wrote my XML nodes directly into the header :

Dim z_bieTextBinding As TextMessageEncodingBindingElement = New TextMessageEncodingBindingElement(MessageVersion.Soap11, Encoding.UTF8)
Dim z_bieHttpBinding As HttpTransportBindingElement = New HttpTransportBindingElement()
    Dim z_cbiBinding As CustomBinding = New CustomBinding(z_bieTextBinding, z_bieHttpBinding)

Dim z_epaFinalAddress As EndpointAddress = New EndpointAddress("http://some.where.over/the/rainbow")

Dim z_chfChannels As ChannelFactory(Of IRequestChannel) = New ChannelFactory(Of IRequestChannel)(z_cbiBinding, z_epaFinalAddress)
Dim z_rqcChannel As IRequestChannel = z_chfChannels.CreateChannel()

z_rqcChannel.Open()
Dim z_msgToSend As Message = Message.CreateMessage(MessageVersion.Soap11, p_strActionName, p_objMessageBody)
Dim z_strUserHeader As String = "<wsse:UsernameToken " & _
            "xmlns:wsse=""http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"" " & _
            "xmlns:wsu=""http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"" " & _
            "wsu:Id=""UsernameToken-5FJPET4ATPQZMREXU8C51DK9P6OTW022"">" & _
            "<wsse:Username>NoOneYaKnowPardner</wsse:Username>" & _
            "<wsse:Password Type=""http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText"">DonTBotherPal</wsse:Password>" & _
            "</wsse:UsernameToken>"
Dim z_xdoDocUserHeader As XmlDocument = New XmlDocument()
z_xdoDocUserHeader.LoadXml(z_strUserHeader)
Dim z_mheSecurityHeader As MessageHeader = MessageHeader.CreateHeader("Security", _
                                "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd", _
                                z_xdoDocUserHeader.DocumentElement)
z_msgToSend.Headers.Add(z_mheSecurityHeader)

It does work. (xmlns attributes are a bit different from the model, but they still pass.) Still, I can't shake the feeling that it should be feasible without resorting to XML manipulation. So :

  1. Am I right to feel this is a "dirty" solution?
  2. Is there an alternate way to do this using only WCF objects, properties and methods?

EDIT: I tried another approach. I set up bindings and endpoints in the Web.config thus:

<system.serviceModel>
    <bindings>
        <basicHttpBinding>
            <binding name="bndSimpleGeneric"
                        messageEncoding="Text"/>
        </basicHttpBinding>
    </bindings>
    <client>
        <endpoint name="enpBasicEndpoint"
                    address="http://dms-uat.athoris.net/Athoris"
                    binding="basicHttpBinding"
                    bindingConfiguration="bndSimpleGeneric"
                    contract="System.ServiceModel.Channels.IRequestChannel">
            <headers>
                <wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
                                xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
                    <wsse:UsernameToken>
                        <wsse:Username>CanTRememberWho</wsse:Username>
                        <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">SomethingSomethingOrangesSomething</wsse:Password>
                    </wsse:UsernameToken>
                </wsse:Security>
            </headers>
        </endpoint>
    </client>
</system.serviceModel>

Which lets me have much simpler code:

    Dim z_chfChannels As New ChannelFactory(Of IRequestChannel)("enpBasicEndpoint")

    Dim z_rqcChannel As IRequestChannel = z_chfChannels.CreateChannel()

    z_rqcChannel.Open()

    Dim z_msgToSend As Message = Message.CreateMessage(MessageVersion.Soap11, "Action Name", "<MessageBody>Words</MessageBody>")
    Dim z_mbuToSend As MessageBuffer = z_msgToSend.CreateBufferedCopy(Integer.MaxValue)
    Dim z_msgResponse As Message = z_rqcChannel.Request(z_mbuToSend.CreateMessage())

    z_rqcChannel.Close()
    z_chfChannels.Close()

    Return z_msgResponse

As indicated in the comments below, I'm not sure I should be satisfied with that since the username and, worse, the password are in the Web.config. Still, it looks much better (to me) than cobbling XML code together with character strings. As before, opinions on the matter are welcome.

Jean-David Lanz
  • 865
  • 9
  • 18
  • did u check this https://stackoverflow.com/questions/7247536/creating-headers-wsse-section-of-wcf-client-programatically-in-c-sharp – A Khudairy Nov 18 '18 at 12:57
  • 1
    Interesting. It turns out that I changed my approach on my own, using bindings and endpoints definitions in the Web.config. The result is similar to what was proposed in https://stackoverflow.com/questions/5836685/correct-way-communicate-wsse-usernametoken-for-soap-webservice (the page you linked to had a link to that page), but I'm not entirely satisfied with putting username and password in a configuration file. – Jean-David Lanz Nov 27 '18 at 13:40

0 Answers0