C# Runtime Error when implementing WSSE Security Headers with custom fields in SOAP request
Asked Answered
L

2

4

I am trying to send a SOAP request to a web service that uses WSSE and UsernameToken for authentication. The sample query is as follows (masking confidential data):

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:kas="http://webservice.com">
        <soapenv:Header>
      <wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
         <wsse:UsernameToken>
            <wsse:Username>abc</wsse:Username>
            <wsse:CustomField>123</wsse:CustomField>
         </wsse:UsernameToken>
      </wsse:Security>
   </soapenv:Header>
   <soapenv:Body>
      <kas:method1>
         <!--Optional:-->
         <method1>
            <!--Optional:-->
            <queryNo>12345678901</queryNo>
         </method1>
      </kas:method1>
   </soapenv:Body>
</soapenv:Envelope> 

I have generated a proxy class using WSE 3.0 and the problem is that I get the error: "Object reference not set to an instance of an object." The problematic part of my C# code is below:

queryNoSorguType q = new queryNoSorguType();
string query_parameter = query_no;
q.queryNo = query_parameter;

ResultType[] r = new ResultType[10];

UsernameToken token = new UsernameToken("abc", "123",PasswordOption.SendPlainText);
//mWebService.SetClientCredential<UsernameToken>(token);
//Policy webServiceClientPolicy = new Policy();
mWebService.RequestSoapContext.Security.Tokens.Add(token);
//mWebService.SetPolicy(webServiceClientPolicy);

//r = mWebService.documentQuerybyQueryNo(q);

System.Data.DataTable outputDataTable = new System.Data.DataTable();
//System.Data.DataRow outRow = outputDataTable.Rows.Add();
//outRow["field1"] = r;
output = outputDataTable;

I located the problematic part by systemically commenting out portions of my code. I am quite unfamiliar with web services, C# and I am actually implementing this in Blue Prism. Although this program works with SOAP web services out of the box, unfortunately it does not natively support SOAP headers.

The SOAP request works fine in SOAP UI and there are no compiler errors in Blue Prism. I tried adding the headers as instructed in the manual and on the web, but it did not work. I would appreciate it if you could point me in the right direction.

EDIT After writing, compiling a console application in Visual Studio 2017 I get the following error. As far as I understand it does not have the definitions for the headers.

Unhandled Exception: System.Web.Services.Protocols.SoapHeaderException: MustUnderstand headers:[{http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd}Security] are not understood
   at System.Web.Services.Protocols.SoapHttpClientProtocol.ReadResponse(SoapClientMessage message, WebResponse response, Stream responseStream, Boolean asyncCall)
   at System.Web.Services.Protocols.SoapHttpClientProtocol.Invoke(String methodName, Object[] parameters)
   at WebService.queryByQueryNo(queryNoQueryType queryByQueryNo1) in C:\Users\user\source\repos\ConsoleApp1\ConsoleApp1\Web References\WebService\Reference.cs:line 1533
   at ConsoleApp1.Program.Main(String[] args) in C:\Users\user\source\repos\ConsoleApp1\ConsoleApp1\Program.cs:line 33
Lammastide answered 19/5, 2018 at 23:35 Comment(7)
if you are debugging, I suggest inspecting every part of mWebService.RequestSoapContext.Security.Tokens, if mWebService.RequestSoapContext or mWebService.RequestSoapContext.Security or mWebService.RequestSoapContext.Security.Tokens is null, your code will fail.Arkose
@Arkose Thanks for the comment. Actually after isolating the program and running it through Visual Studio I get different errors. Please see the edited question above.Lammastide
@Lammastide you mentioned, that SoapUI request works fine. Could you please provide soap request dumps from SoapUI and from your .net app?Superstructure
@Lammastide and corresponding server responsesSuperstructure
do you have any security certificate involved in this process?Waterless
@Lammastide could you see my answer?Anastigmatic
@Waterless No I do not have a certificate involved in this process.Lammastide
L
1

I decided to utilize a different method and quit trying to use a proxy class for the time being as there were problems associated with it. Making use of the answers on this link: Client to send SOAP request and receive response I came up with my own solution after some customization.

However, I still wonder how to proceed in order to make it work using wrapper classes defined by Visual Studio or WSE 3.0. After writing the code and testing it in Visual Studio it was pretty easy to port it into Blue Prism.

using System;
using System.Collections.Generic;
using System.Data;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using System.Net;
using System.IO;
using System.Xml.Linq;
namespace WebService
{
    class Program
    {
        /// <summary>
        /// Execute a Soap WebService call
        /// </summary>
        public static string Execute(string queryNo)
        {
            HttpWebRequest request = CreateWebRequest();
            XmlDocument soapEnvelopeXml = new XmlDocument();
            soapEnvelopeXml.LoadXml(@"<?xml version=""1.0"" encoding=""utf-8""?>
                <soapenv:Envelope xmlns:soapenv=""http://schemas.xmlsoap.org/soap/envelope/"" xmlns:customNAMESPACE=""http://webservice.com"">
                <soapenv:Header>
                    <wsse:Security xmlns:wsse=""http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"">
                        <wsse:UsernameToken>
                            <wsse:Username>USER</wsse:Username>
                            <wsse:CustomField>CODE</wsse:CustomField>
                        </wsse:UsernameToken>
                     </wsse:Security>
                  </soapenv:Header>
                  <soapenv:Body>
                     <customNAMESPACE:QueryByQueryNo>
                        <!--Optional:-->
                        <QueryByQueryNo>
                            <!--Optional:-->
                            <queryNo>" + queryNo + @"</queryNo>
                        </QueryByQueryNo>
                      </customNAMESPACE:QueryByQueryNo>
                  </soapenv:Body>
                </soapenv:Envelope>");

            using (Stream stream = request.GetRequestStream())
            {
                soapEnvelopeXml.Save(stream);
            }

            using (WebResponse response = request.GetResponse())
            {
                using (StreamReader rd = new StreamReader(response.GetResponseStream()))
                {
                    string soapResult = rd.ReadToEnd();
                    Console.WriteLine(soapResult);
                    return soapResult;
                }
            }
        }
        /// <summary>
        /// Create a soap webrequest to [Url]
        /// </summary>
        /// <returns></returns>
        public static HttpWebRequest CreateWebRequest()
        {
            HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(@"https://webservice.com/webservice?wsdl");
            webRequest.Headers.Add(@"SOAP:Action");
            webRequest.ContentType = "text/xml;charset=\"utf-8\"";
            webRequest.Accept = "text/xml";
            webRequest.Method = "POST";
            return webRequest;
        }
        static void Main(string[] args)
        {
            if (args.Length == 0 || args.Length > 1)
            {
                System.Console.WriteLine("Please provide a query no");
                System.Console.WriteLine("Usage: WebService.exe 3523423333");
                return;
            }

            string output, XMLresponse;
            try
            {
                XMLresponse = Execute(args[0]);
                output = "Successful query";
                XmlDocument xml = new XmlDocument();
                xml.LoadXml(XMLresponse);  // suppose that str string contains the XML data. You may load XML data from a file too.
                XmlNodeList resultCodeList = xml.GetElementsByTagName("resultCode");
                XmlNodeList resultNoList = xml.GetElementsByTagName("resultNo");
                int i = 0;
                var OutputTable = new DataTable();
                OutputTable.Columns.Add("Result Code", typeof(string));
                OutputTable.Columns.Add("Result No", typeof(string));
                foreach (XmlNode xn in resultCodeList)
                {
                    Console.WriteLine(resultCodeList[i].InnerText + "  " + resultNoList[i].InnerText);
                    OutputTable.Rows.Add(resultCodeList[i].InnerText, resultNoList[i].InnerText);
                    i++;
                }

            }
            catch (System.Net.WebException exc)
            {
                Console.WriteLine("HTTP POST request failed!");
                output = "!!!HTTP POST request failed!!!";
            }
        }
    }
}
Lammastide answered 3/6, 2018 at 19:14 Comment(0)
A
1

I think is the xml structure punctually the headers when you use

<wsse:Security wsse is not defined, I know you defined in the same line but why not try put it on the document, something like this

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:kas="http://webservice.com" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
   <soapenv:Header>
      <wsse:Security>
         <wsse:UsernameToken>
            <wsse:Username>abc</wsse:Username>
            <wsse:CustomField>123</wsse:CustomField>
         </wsse:UsernameToken>
      </wsse:Security>
   </soapenv:Header>
Anastigmatic answered 30/5, 2018 at 19:42 Comment(2)
@Lammastide Could you see my answer?Anastigmatic
Hello thanks for the answer, the problem is I was trying to send the SOAP request by using wrapper classes generated by Visual Studio. I did not exactly understand your point.Lammastide
L
1

I decided to utilize a different method and quit trying to use a proxy class for the time being as there were problems associated with it. Making use of the answers on this link: Client to send SOAP request and receive response I came up with my own solution after some customization.

However, I still wonder how to proceed in order to make it work using wrapper classes defined by Visual Studio or WSE 3.0. After writing the code and testing it in Visual Studio it was pretty easy to port it into Blue Prism.

using System;
using System.Collections.Generic;
using System.Data;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using System.Net;
using System.IO;
using System.Xml.Linq;
namespace WebService
{
    class Program
    {
        /// <summary>
        /// Execute a Soap WebService call
        /// </summary>
        public static string Execute(string queryNo)
        {
            HttpWebRequest request = CreateWebRequest();
            XmlDocument soapEnvelopeXml = new XmlDocument();
            soapEnvelopeXml.LoadXml(@"<?xml version=""1.0"" encoding=""utf-8""?>
                <soapenv:Envelope xmlns:soapenv=""http://schemas.xmlsoap.org/soap/envelope/"" xmlns:customNAMESPACE=""http://webservice.com"">
                <soapenv:Header>
                    <wsse:Security xmlns:wsse=""http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"">
                        <wsse:UsernameToken>
                            <wsse:Username>USER</wsse:Username>
                            <wsse:CustomField>CODE</wsse:CustomField>
                        </wsse:UsernameToken>
                     </wsse:Security>
                  </soapenv:Header>
                  <soapenv:Body>
                     <customNAMESPACE:QueryByQueryNo>
                        <!--Optional:-->
                        <QueryByQueryNo>
                            <!--Optional:-->
                            <queryNo>" + queryNo + @"</queryNo>
                        </QueryByQueryNo>
                      </customNAMESPACE:QueryByQueryNo>
                  </soapenv:Body>
                </soapenv:Envelope>");

            using (Stream stream = request.GetRequestStream())
            {
                soapEnvelopeXml.Save(stream);
            }

            using (WebResponse response = request.GetResponse())
            {
                using (StreamReader rd = new StreamReader(response.GetResponseStream()))
                {
                    string soapResult = rd.ReadToEnd();
                    Console.WriteLine(soapResult);
                    return soapResult;
                }
            }
        }
        /// <summary>
        /// Create a soap webrequest to [Url]
        /// </summary>
        /// <returns></returns>
        public static HttpWebRequest CreateWebRequest()
        {
            HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(@"https://webservice.com/webservice?wsdl");
            webRequest.Headers.Add(@"SOAP:Action");
            webRequest.ContentType = "text/xml;charset=\"utf-8\"";
            webRequest.Accept = "text/xml";
            webRequest.Method = "POST";
            return webRequest;
        }
        static void Main(string[] args)
        {
            if (args.Length == 0 || args.Length > 1)
            {
                System.Console.WriteLine("Please provide a query no");
                System.Console.WriteLine("Usage: WebService.exe 3523423333");
                return;
            }

            string output, XMLresponse;
            try
            {
                XMLresponse = Execute(args[0]);
                output = "Successful query";
                XmlDocument xml = new XmlDocument();
                xml.LoadXml(XMLresponse);  // suppose that str string contains the XML data. You may load XML data from a file too.
                XmlNodeList resultCodeList = xml.GetElementsByTagName("resultCode");
                XmlNodeList resultNoList = xml.GetElementsByTagName("resultNo");
                int i = 0;
                var OutputTable = new DataTable();
                OutputTable.Columns.Add("Result Code", typeof(string));
                OutputTable.Columns.Add("Result No", typeof(string));
                foreach (XmlNode xn in resultCodeList)
                {
                    Console.WriteLine(resultCodeList[i].InnerText + "  " + resultNoList[i].InnerText);
                    OutputTable.Rows.Add(resultCodeList[i].InnerText, resultNoList[i].InnerText);
                    i++;
                }

            }
            catch (System.Net.WebException exc)
            {
                Console.WriteLine("HTTP POST request failed!");
                output = "!!!HTTP POST request failed!!!";
            }
        }
    }
}
Lammastide answered 3/6, 2018 at 19:14 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.