Search code examples
c#xmlformattingsamlxmldocument

How to use XmlElement.InnerXml WITHOUT any formatting?


I need to communicate with a WebService through XML. This service is using a saml:Assertion to authenticate the connection. I can communicate with the server, but the validation always fails. I searched for hours what the problem is, because when I use soapUI with the exact same parameters and saml ticket, it works. I tried to "manually" remove any formatting from the saml:Assertion because it was signed, so with a single-byte change, it won't work anymore.

Here's my code:

// Insert saml:Assertion string into soapenv:Header
private static void InsertSAML(ref XmlDocument soapXML, ref XmlNamespaceManager nsmgr, string saml)
{
    // Remove all formatting
    saml = saml.Replace("\r", "");
    saml = saml.Replace("\n", "");
    while(saml.IndexOf("  ") > -1)
    {
        saml = saml.Replace("  ", " ");
    }
    saml = saml.Replace("> <", "><");
    saml = saml.Replace("\" />", "\"/>");

    XmlElement soapHeader = (XmlElement)soapXML.SelectSingleNode("//soapenv:Envelope/soapenv:Header/wsse:Security", nsmgr);
    if (soapHeader == null)
    {
        throw new Exception("Can't find \"//soapenv:Envelope/soapenv:Header/wsse:Security\"");
    }

    soapHeader.InnerXml += saml;
}

But it seems like when I use soapHeader.InnerXml += saml; it causes some kind of formatting. A whitespace will appear before the closing tags of elements without inner content:

So, I need to add this:

<dsig:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>

But in the final XML looks like this, even if I replaced these occurences before inserting:

<dsig:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" />

How can I get rid of this behaviour?


Solution

  • As I said, the problem was with the additional bytes XmlDocument added to my xml when I append content to the InnerXml. I tried so hard to remove all formatting, and it was a good direction. But instead of "un-formatting" the saml:Assertion part, I magaged to un-format the whole request body, right before it was sent to the service. And now it works. I call this method right before sending the request:

    // Insert XML to request body
    private static void InsertSoapEnvelopeIntoWebRequest(XmlDocument soapEnvelopeXml, HttpWebRequest webRequest)
    {
        using (var stringWriter = new StringWriter())
        using (var xmlTextWriter = XmlWriter.Create(stringWriter))
        using (Stream stream = webRequest.GetRequestStream())
        {
            // Get XML contents as string
            soapEnvelopeXml.WriteTo(xmlTextWriter);
            xmlTextWriter.Flush();
            string str = stringWriter.GetStringBuilder().ToString();
    
            // Remove all formatting
            str = str.Replace("\r", "");
            str = str.Replace("\n", "");
            while (str.IndexOf("  ") > -1)
            {
                str = str.Replace("  ", " ");
            }
            str = str.Replace("> <", "><");
            str = str.Replace("\" />", "\"/>");
    
            // Write the unbeutified text to the request stream
            MemoryStream ms = new MemoryStream(UTF8Encoding.Default.GetBytes(str));
            ms.WriteTo(stream);
        }
    }