0

My organization hosts a WCF service where external users can send us data. We have several senders who are fully operational but I am assisting one who is having trouble. The sender is not using Microsoft technologies so I am assisting them in building an XML packet that will connect to our service.

At the moment I am trying to build XML to call RequestSecurityToken on my service. Here is a sample of the XML I am sending, with security details removed:

<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://www.w3.org/2003/05/soap-envelope"
   xmlns:SOAP-ENC="http://www.w3.org/2003/05/soap-encoding"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns:xsd="http://www.w3.org/2001/XMLSchema"
   xmlns:us="our namespace url"
   xmlns:arrays="http://schemas.microsoft.com/2003/10/Serialization/Arrays"
   xmlns:addr="http://www.w3.org/2005/08/addressing"
   xmlns:wsu="http://doc.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
    <SOAP-ENV:Header>
        <addr:Action SOAP-ENV:mustUnderstand="1">http://schemas.xmlsoap.org/ws/2005/02/trust/RST/SCT</addr:Action>
        <addr:MessageID>urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx</addr:MessageID>
        <addr:ReplyTo>
            <addr:Address>http://www.w3.org/2005/08/addressing/anonymous</addr:Address>
        </addr:ReplyTo>
        <addr:To SOAP-ENV:mustUnderstand="1">https://our.domain/path/ServiceName.svc</addr:To>
        <wsse:Security SOAP-ENV:mustUnderstand="1" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
            <wsu:Timestamp wsu:Id="uuid-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx-x">
                <wsu:Created>2014-08-14T10:07:00.095Z</wsu:Created>
                <wsu:Expires>2014-08-14T10:09:05.095Z</wsu:Expires>
            </wsu:Timestamp>
            <wsse:UsernameToken wsu:Id="uuid-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx-x">
                <wsse:Username>TST</wsse:Username>
                <wsse:Password Type="http://docs.oasis-open.org/wsse/2004/01/oasis-200401-wsse-username-token-profile-1.0#PasswordText">Test</wsse:Password>
            </wsse:UsernameToken>
        </wsse:Security>
    </SOAP-ENV:Header>
    <SOAP-ENV:Body>
        <trust:RequestSecurityToken xmlns:trust="http://schemas.xmlsoap.org/ws/2005/02/trust">
            <trust:TokenType>http://schemas.xmlsoap.org/ws/2005/02/sc/sct</trust:TokenType>
            <trust:RequestType>http://schemas.xmlsoap.org/ws/2005/02/trust/Issue</trust:RequestType>
            <trust:Entropy>
                <trust:BinarySecret wsu:Id="uuid-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx-x" Type="http://schemas.xmlsoap.org/ws/2005/02/trust/Nonce">xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=</trust:BinarySecret>
            </trust:Entropy>
            <trust:KeySize>256</trust:KeySize>
        </trust:RequestSecurityToken>
    </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

here is the response:

<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://www.w3.org/2005/08/addressing/soap/fault</a:Action>
        <a:RelatesTo>urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx</a:RelatesTo>
    </s:Header>
    <s:Body>
        <s:Fault>
            <s:Code>
                <s:Value>s:Sender</s:Value>
                <s:Subcode>
                    <s:Value xmlns:a="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">a:InvalidSecurity</s:Value>
                </s:Subcode>
            </s:Code>
            <s:Reason>
                <s:Text xml:lang="en-US">An error occurred when verifying security for the message.</s:Text>
            </s:Reason>
        </s:Fault>
    </s:Body>
</s:Envelope>
Jacob
  • 453
  • 5
  • 17
  • 1
    Wow, looks like a hard one to solve with SOAP. You might check that the client is sending the content length in the header. I know it's not the answer you're looking for but wonder if it would be easier to use REST. I've had better luck with REST in cross platform web services. – William Walseth Aug 14 '14 at 18:54
  • Yes the content length is being sent in the test app that sent the above. Unfortunately this system is built and in production; rebuilding it as a REST service is not likely. – Jacob Aug 18 '14 at 16:10
  • @WilliamWalseth . We are going to rebuild it as a REST service using Web API. If you would post something resembling your comment as an answer, I will be happy to accept it. – Jacob Aug 17 '15 at 18:31

1 Answers1

0

Here's a simple C# REST(ish) Server that accepts XML in an HTTP Post.
You can also modify it to use HTTP Get Parameters, hide your security credentials in the header, use basic authentication, etc.

Hope you find this useful.

namespace webservice {
    public partial class datasvc : System.Web.UI.Page {
        protected void Page_Load(object sender, EventArgs e) {
            XmlDocument xmlResponse = new XmlDocument();
            xmlResponse.LoadXml("<xml/>");



            try {
                XmlDocument xmlRequest = new XmlDocument();
                xmlRequest.Load(Request.InputStream);

                if (xmlRequest.DocumentElement.HasAttribute("id") &&
                    xmlRequest.DocumentElement.HasAttribute("pw")) {
                    // OK verify
                } else {
                    throw new Exception("Invalid credentials");
                }


                switch (xmlRequest.DocumentElement.GetAttribute("service")) {
                    case "time":
                        xmlResponse.DocumentElement.SetAttribute("content", DateTime.Now.ToString());
                        break;

                    case "hello":
                        xmlResponse.DocumentElement.SetAttribute("content", "Hello World");
                        break;

                    default:
                        throw new Exception("Unknown Service");
                }


            } catch (Exception ex) {
                xmlResponse.DocumentElement.SetAttribute("err", ex.Message);
            } finally {
                Response.ContentType = "text/xml";
                Response.Write(xmlResponse.OuterXml);
            }
        }
    }
}

To call it from .NET here's a client

namespace webservice {
    public partial class client : System.Web.UI.Page {

        /// <summary>Posts the specified document to the server using UTF-8 encoding.</summary>             
        public XmlDocument postXml(XmlDocument xml, String strURL, string strID, string strPassword) {
            HttpWebRequest req = (HttpWebRequest)WebRequest.Create(strURL);
            req.Method = "POST";
            req.ContentType = "text/xml";
            XmlDocument xmlReturn = new XmlDocument();
            req.Timeout = 180000;


            // UGH! not set by default!
            req.Proxy.Credentials = System.Net.CredentialCache.DefaultCredentials;

            // Write the serialized XML to the request
            Stream rs = req.GetRequestStream();

            XmlTextWriter tw = new XmlTextWriter(rs, Encoding.UTF8); // Encoding.GetEncoding("UTF-8"));
            xml.WriteTo(tw);
            tw.Flush();
            rs.Close();
            HttpWebResponse resp = (HttpWebResponse)req.GetResponse();
            string msg;

            try {
                // Get the response from the server
                StreamReader reader = new StreamReader(resp.GetResponseStream());
                msg = reader.ReadToEnd();
                xmlReturn.LoadXml(msg);
            } catch (Exception e) {
                throw new Exception("Error posting to server: " + strURL, e);
            }
            return xmlReturn;
        }


        protected void Page_Load(object sender, EventArgs e) {
            try {
                XmlDocument xml = new XmlDocument();
                xml.LoadXml("<xml/>");
                xml.DocumentElement.SetAttribute("userid", "test");
                string strID = "userid";
                string strPassword = "password";

                xml.DocumentElement.SetAttribute("id", strID);
                xml.DocumentElement.SetAttribute("pw", strPassword);

                // post XML to /datasvc.aspx on this .NET project
                string strURL = Request.Url.ToString().Replace( "client", "datasvc" );

                xml.DocumentElement.SetAttribute("service", "hello");
                XmlDocument xmlResponse = this.postXml(xml, strURL, strID, strPassword);
                Response.Write(xmlResponse.DocumentElement.GetAttribute("content"));

                xml.DocumentElement.SetAttribute("service", "time");
                xmlResponse = this.postXml(xml, strURL, strID, strPassword);
                Response.Write(xmlResponse.DocumentElement.GetAttribute("content"));


            } catch (Exception ex) {
                Response.Write(ex.Message);

            }

        }
    }
William Walseth
  • 2,803
  • 1
  • 23
  • 25