Adding WS-Security Configuration in SOAP Request for API call

Hi All,

I am trying to perform API Automation.
I am using SOAP service and the request Body is in XML format. Using Uipath HTTPS activity I was able to make API call and get valid response during development in dev instance.
Currently I am assigned pre-prod env, To make API call Following settings needs to be done in SOAP which includes-

  1. Adding certificate file(.jks) & password in SOAP keystore
    image
    2.Creating a new Outgoing WS- Security Configuration and adding password of the certificate
    image
    3.Adding the signature configuration
    image

After the above settings are completed, we make the soap request after applying outgoing WS Configuration-> which auto-adds security configuration in the request header.
Soap setup links-https://www.soapui.org/docs/soapui-projects/ws-security/
I need to achieve the same using uipath. Any help will be appreciated.
@Palaniyappan @loginerror, @ClaytonM, @vikaskulhari

1 Like

Hi @Lipika_Porey

I think the easiest way to consume a SOAP service is by creating a library project with a web service, please refer to this article:
#FeatureBlog - 19.4 - SOAP and Swagger (based REST services for Library projects)

I have tried calling soap request as web service inside library. However, this activity only supports primitive datatype and does not support custom or composite data type.

Could you please provide a concrete example? Let’s see what can be improved here in case it is lacking some features.

Hello Lipika,
In this video, I use HTTP Request to call any SOAP API. I use these steps in case of special WSLD that are not compatible with SOAP request Wizards, also not compatible with new service from the library.

0:54 SoapUI application used to test SOAP API
3:05 Test commands from SoapUI
4:30 How can we test SOAP API from Postman
5:50 In UiPath Studio you need to install Web.Activities
7:30 SOAP Request activity
9:30 Create a New Service inside of Library
11:10 RAW approach to SOAP API command
13:30 How we build a simple XML
15:00 Run the process that builds XML to see the result
15:20 Create a complex XML
16:30 Step by Step XML creation
20:10 Main element creation that also includes attributes
23:00 Test the complex XML creation
23:50 Deserialize XML
25:15 How to extract Data from XML Nodes
27:40 How to extract Data from XML Attributes

Thanks,
Cristian Negulescu

1 Like

Hi @Cristian_Negulescu I’ve problems with passing WS-Security Header to my SOAP Request (or the entire query) by HTTP Request Activity.

I’ve made workflow for generating the header, which have Base64 encoding and SHA1 encoding included. I’m not sure the encoding is correct.

WS-Security Header in my case consists of TimeStamp, UsernameToken with nonce and PasswordDigest.
When I test my query, I get an error: 500:

No binding operation info while invoking unknown method with params unknown.

image

Here is e.g. correct header with some e.g. body:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:obj="http://endpoint.com/object">
<soapenv:Header>
<wsse:Security xmlns:wsse='http://docs.oasis-open.org/wss/2004/01/oasis-200401-wsswssecurity-secext-1.0.xsd' xmlns:wsu='http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd'>
<wsu:Timestamp wsu:Id='TS-11b7d3261c994de099eb8c431b33947b'>
<wsu:Created>2019-09-06T12:09:15.604Z</wsu:Created>
<wsu:Expires>2019-09-06T12:09:25.619Z</wsu:Expires>
</wsu:Timestamp>
<wsse:UsernameToken wsu:Id='UsernameToken-238be95be3bf445fb8534666a7a8693c'>
<wsse:Username>***login***</wsse:Username>
<wsse:Password Type='http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-usernametoken-profile-1.0#PasswordDigest'>***Base64 (SHA-1 (nonce + created + password) )***</wsse:Password>
<wsse:Nonce EncodingType='http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soapmessage-security-1.0#Base64Binary'>***Base64 nonce***</wsse:Nonce>
<wsu:Created>2019-09-06T12:09:15.604Z</wsu:Created>
</wsse:UsernameToken>
</wsse:Security>
</soapenv:Header>
<soapenv:Body>
<obj:getDictionary>
<objectDictionaryType>CATEGORY</objectDictionaryType>
<operator>
<id>?</id>
<name>?</name>
<surname>?</surname>
</operator>
</obj:getDictionary>
</soapenv:Body>
</soapenv:Envelope>

I have read the WS-Security documentation:

and I’m not sure what nonce is and what wsu: Id should look like for Timestamp and UsernameToken. In my case I put to wsu:Id creation date as Base64 string. Is that correct?

My workflow is here:

WSS_Header.7z (5,3 KB)

Can someone help me?

Hello Adrian,
I see your post from the beginning, but I don’t have ideas after I look a lot on the internet. Try to write your code C# or VB.NET and the do invoke code. Unfortunately, I don’t find a nice example on the internet.
Thanks,
Cristian Negulescu

@Cristian_Negulescu I’ve prepared C# code to invoke it in “Invoke code activity” basic on this link: c# - Client to send SOAP request and receive response - Stack Overflow :

C# Code for GET SOAP request using HttpWebRequest:

// XML Soap Envelope Document
System.Xml.XmlDocument soapDoc = new XmlDocument();
	soapDoc.LoadXml(soap_body);

// Request
System.Net.HttpWebRequest request = (System.Net.HttpWebRequest)WebRequest.Create(url);
	request.Headers.Add("SOAPAction", action);
	request.ContentType = "text/xml;charset=\"utf-8\"";
	request.Accept = "text/xml";
	request.Method = "GET";

// Insert Soap Envelope Into WebRequest
using (System.IO.Stream stream = request.GetRequestStream())
	{
        soapDoc.Save(stream);
    }

// Begin async call to web request.
System.IAsyncResult asyncResult = request.BeginGetResponse(null, null);

// Suspend this thread until call is complete.
asyncResult.AsyncWaitHandle.WaitOne();

// Fet the response from the completed web request.
using (System.Net.WebResponse response = request.EndGetResponse(asyncResult))
	{
		using (System.IO.StreamReader rd = new StreamReader(response.GetResponseStream()))
        {
            soap_result = rd.ReadToEnd();
        }   
    }

Variables:

var action = ""
var url = "https://url.com/xxx/services/orders2" (endpoint of web service)
var soap_body = "
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ord="http://endpoint.com/order">
  <soapenv:Header>
    <wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wsswssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
      <wsu:Timestamp wsu:Id="TS-xxxxxxlWdEp6NXhaRS9UeGVyY3VKUT09">
        <wsu:Created>2021-03-15T09:19:10.575Z</wsu:Created>
        <wsu:Expires>2021-03-15T09:19:20.575Z</wsu:Expires>
      </wsu:Timestamp>
      <wsse:UsernameToken wsu:Id="UsernameToken-xxxxxxlWdEp6NXhaRS9UeGVyY3VKUT09">
        <wsse:Username>service_login</wsse:Username>
        <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-usernametoken-profile-1.0#PasswordDigest">xxxxxxM0NjFmMDlhOTliODE2ZWYwM2Q4YTA5YzQyODFiZDJlZjkxZg==</wsse:Password>
        <wsse:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soapmessage-security-1.0#Base64Binary">xxxxxxlWdEp6NXhaRS9UeGVyY3VKUT09</wsse:Nonce>
        <wsu:Created>2021-03-15T09:19:10.575Z</wsu:Created>
      </wsse:UsernameToken>
    </wsse:Security>
  </soapenv:Header>
  <soapenv:Body>
    <ord:getDictionary>
      <operatorId>1234</operatorId>
      <slownik>orderStatus</slownik>
    </ord:getDictionary>
  </soapenv:Body>
</soapenv:Envelope>"

Where:
wsu:Id="TS-xxxxxx” and wsu:Id="UsernameToken-xxxxxx”
have “xxxxxxlWdEp6NXhaRS9UeGVyY3VKUT09” equals nonce with is
Base64String(New Microsoft.Web.Services3.Security.Nonce(16))

wsse:Password
have “xxxxxxM0NjFmMDlhOTliODE2ZWYwM2Q4YTA5YzQyODFiZDJlZjkxZg==” equals
Base64 (SHA1 (nonce_base64string + date_wsu_Created + password))

I have error while invoking code:

Exception (click to expand)
RemoteException wrapping System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation.  ---> RemoteException wrapping System.Net.ProtocolViolationException: Cannot send a content-body with this verb-type. 
   at System.Net.HttpWebRequest.CheckProtocol(Boolean onRequestStream)
   at System.Net.HttpWebRequest.GetRequestStream(TransportContext& context)
   at System.Net.HttpWebRequest.GetRequestStream()
   at UiPath.CodeRunner.UiPathCodeRunner_b80cb45c28f94a5f890e0ccd3f111b2b.Run(String url,
String action,
String soap_body,
String soap_result)
	--- End of inner exception stack trace ---
   at System.RuntimeMethodHandle.InvokeMethod(Object target,
Object[] arguments,
Signature sig,
Boolean constructor)
   at System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal(Object obj,
Object[] parameters,
Object[] arguments)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj,
BindingFlags invokeAttr,
Binder binder,
Object[] parameters,
CultureInfo culture)
   at System.RuntimeType.InvokeMember(String name,
BindingFlags bindingFlags,
Binder binder,
Object target,
Object[] providedArgs,
ParameterModifier[] modifiers,
CultureInfo culture,
String[] namedParams)
   at UiPath.Activities.System.Utilities.InvokeCode.CompilerRunner.Run(Object[] args)
   at UiPath.Activities.System.Utilities.InvokeCode.NetCodeInvoker.Run(String userCode,
List`1 inArgs,
IEnumerable`1 imps,
Object[] args)
   at UiPath.Core.Activities.InvokeCode.Execute(CodeActivityContext context)
   at System.Activities.CodeActivity.InternalExecute(ActivityInstance instance,
ActivityExecutor executor,
BookmarkManager bookmarkManager)
   at System.Activities.ActivityInstance.Execute(ActivityExecutor executor,
BookmarkManager bookmarkManager)
   at System.Activities.Runtime.ActivityExecutor.ExecuteActivityWorkItem.ExecuteBody(ActivityExecutor executor,
BookmarkManager bookmarkManager,
Location resultLocation)

Below the correct transmission of the header (from the documentation of service provider) for POST service:

Encoding: UTF-8
Http-Method: POST
Content-Type: multipart/related; type="application/xop+xml";start="<http://tempuri.org/0>";boundary="uuid:xxxxxx65-d7d2-44c5- ae3c-280f9e3c4493+id=1";start-info="text/xml"
Headers: {accept-encoding=[gzip, deflate], Content-Length=[1922], contenttype=[ multipart/related;type="application/xop+xml";start="<http://tempuri.org/0>";boundary="uuid:xxxxxx65-d7d2-44c5- ae3c-280f9e3c4493+id=1";start-info="text/xml"], Expect=[100-continue], host=[xxxmsp. provider.com], MIME-Version=[1.0], SOAPAction=[""]}
Payload:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:obj="http://endpoint.com/object">
<soapenv:Header>
<wsse:Security xmlns:wsse='http://docs.oasis-open.org/wss/2004/01/oasis-200401-wsswssecurity-secext-1.0.xsd' xmlns:wsu='http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd'>
<wsu:Timestamp wsu:Id='TS-11b7d3261c994de099eb8c431b33947b'>
<wsu:Created>2019-09-06T12:09:15.604Z</wsu:Created>
<wsu:Expires>2019-09-06T12:09:25.619Z</wsu:Expires>
</wsu:Timestamp>
<wsse:UsernameToken wsu:Id='UsernameToken-238be95be3bf445fb8534666a7a8693c'>
<wsse:Username>***login***</wsse:Username>
<wsse:Password Type='http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-usernametoken-profile-1.0#PasswordDigest'>***Base64 (SHA-1 (nonce + created + password) )***</wsse:Password>
<wsse:Nonce EncodingType='http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soapmessage-security-1.0#Base64Binary'>***Base64 nonce***</wsse:Nonce>
<wsu:Created>2019-09-06T12:09:15.604Z</wsu:Created>
</wsse:UsernameToken>
</wsse:Security>
</soapenv:Header>
<soapenv:Body>
<obj:getDictionary>
<objectDictionaryType>CATEGORY</objectDictionaryType>
<operator>
<id>?</id>
<name>?</name>
<surname>?</surname>
</operator>
</obj:getDictionary>
</soapenv:Body>
</soapenv:Envelope>

Where am I making a mistake?

Hello Adrian,
My question for you is your code is working on visual Studio 2019? That should be the first step.
If you have a problem with creating XML you can do this dynamically I tried to do it like this:

  XNamespace soapenv = "http://schemas.xmlsoap.org/soap/envelope/";
        XNamespace ord = "http://endpoint.com/order";
        XNamespace wsse = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wsswssecurity-secext-1.0.xsd";
        XNamespace wsu = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd";
        XAttribute atr1 = new XAttribute(XNamespace.Xmlns + "soapenv", soapenv.NamespaceName);
        XAttribute atr2 = new XAttribute(XNamespace.Xmlns + "ord", ord.NamespaceName);
        XAttribute atr3 = new XAttribute(XNamespace.Xmlns + "wsse", wsse.NamespaceName);
        XAttribute atr4 = new XAttribute(XNamespace.Xmlns + "wsu", wsu.NamespaceName);
        XAttribute type = new XAttribute("Type", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-usernametoken-profile-1.0#PasswordDigest");
        XAttribute EncodingType = new XAttribute("EncodingType", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soapmessage-security-1.0#Base64Binary");

XElement soap_body =
new XElement(soapenv + “Envelope”, atr1, atr2,

        new XElement(soapenv + "Header",
        new XElement(wsse + "Security", atr3, atr4,
            new XElement(wsu + "Timestamp",
                new XElement(wsu + "Id", "TS-xxxxxxlWdEp6NXhaRS9UeGVyY3VKUT09"),
                new XElement(wsu + "Created", "2021 - 03 - 15T09: 19:10.575Z"),
                new XElement(wsu + "Expires", "2021 - 03 - 15T09: 19:20.575Z")
            ),
            new XElement(wsse + "UsernameToken",
                new XElement(wsu + "Id", "UsernameToken - xxxxxxlWdEp6NXhaRS9UeGVyY3VKUT09"),
                new XElement(wsse + "Username", "service_login"),
                new XElement(wsse + "Password", type, "xxxxxxM0NjFmMDlhOTliODE2ZWYwM2Q4YTA5YzQyODFiZDJlZjkxZg =="),
                new XElement(wsse + "Nonce", EncodingType, "xxxxxxlWdEp6NXhaRS9UeGVyY3VKUT09"),
                new XElement(wsu + "Created", "2021 - 03 - 15T09: 19:10.575Z")
            )
        )),
        new XElement(soapenv + "Body",
        new XElement(ord + "getDictionary",
            new XElement("operatorId", "WA"),
            new XElement("slownik", "68042")
        ))
    ); 

Please test stuff in VS2019 and then will check what is the issue in UiPath unfortunately I don’t have Good experience with SOAP API.
Thanks,
Cristian Negulescu

1 Like

Hi we are not working on VS2019 but I will try to access and check it out.

I’ve read so much documentation that I don’t really know if I’m approaching it the right way.

While building the query, I was able to extract some nice namespaces: Microsoft.Web.Services3.Security here you can generate a nonce, as well as an ID for UsernameToken and Timestamp Token. General purpose library for SOAP.

Another library: System.ServiceModel.Http - here you can e.g. create Password Digest

Unfortunately, using these libraries will mainly work for code execution. My programming knowledge here is small and I cannot use it.