How to send digitally-signed SOAP message

Hi

Using VC 2.3 for VS Community 2015, we need to send a digitally-signed SOAP XML to an Irish Goverment website and process the response we get back, which should be more XML in another SOAP envelope.

The application is a Windows Forms app.  We use System.Xml, System.Security.Cryptography.X509Certificates, System.Security.Cryptography.Xml, etc to create the SOAP message.

What I have tried so far is the below:

method-id SEND-SOAP.

01 Client type HttpClient.

01 Request type HttpRequestMessage.

01 Response type HttpResponseMessage.

*01 Uri String.
01 Uri type Uri.

procedure division.

set Uri to new Uri("https://???.ie/...").  *>> actual URI replaced here

set Client to new HttpClient().

set Request to new HttpRequestMessage(type HttpMethod::Post, Uri).

set Request::Content to new StringContent(theMsg, type Encoding::UTF8, "application/soap xml").  *>> theMsg is the SOAP envelope string

* set Response to Client::SendAsync(Request) as type HttpResponseMessage.
set Response to Client::SendAsync(Request)::Result.

* set Response to Client::PostAsync(Uri,Request::Content)::Result.

exit method.

end method.

 

This is returning Http status code 500 "Internal Server Error", which is not very helpful.

Using System.Net.Http is probably not the best way to go about it.  I have looked at some WCF examples but I do not understand them.

We have been provided with the .wsdl's, endpoints, and sample code written in Java.

Any help would be appreciated.

 

Many thanks

Brendan.

 
  • Some comments, in no particular order:

    • I wouldn't recommend WCF. WCF is well-suited as an RPC mechanism for .NET components to talk to one another, but I'm hesitant to use it for interoperable applications. It's powerful but very complicated, and can be difficult to debug. Once you start down the WCF route, you're probably looking at training a WCF expert. (Consider the O'Reilly WCF book is over a thousand pages...)
    • That said, it may not be straightforward to create a valid signed SOAP message in .NET without using WCF. I did a little browsing online and didn't quickly come across a suitable approach specific to SOAP. However, the XmlDocument and SignedXml classes might well do the trick.
    • I'm going to assume the issue isn't in how you're creating the signed SOAP request, because you haven't shown any of that code. But frankly that's where I'd look first.
    • The server's 500 response may contain additional useful information in the message body, in plain text. You should check for that and log it somewhere; it may help diagnose the problem.
    • There are numerous C# examples of making SOAP requests using the .NET Framework. Typically they use the HttpWebRequest class. HttpClient doesn't appear to be a .NET Framework class, so I don't know exactly what it's doing.
    • Stylistic note: I would recommend not putting periods at the end of each statement - they can create non-obvious control flow when combined with control statements and ANSI scope terminators (which should always be used in modern COBOL dialects). For OO COBOL I also prefer using the declare verb rather than traditional COBOL declarations, and the other modern OO COBOL constructs, but that's arguably just a question of personal style.
  • Hi Michael

    Yes, I can create the signed SOAP message alright, just need help sending it! HttpClient is under System.Net.Http, Microsoft recommend using it for new coding instead of WebRequest.

    The code I wrote was based on C# code I found in Stackoverflow.

    Regards

    Brendan
  • So you've verified the signature on the SOAP message?

    In that case, I'm afraid you'll need to look at the body of the server's reply to see if it tells you why the server is rejecting the message. If that doesn't help, you'll need to raise it with the server's administrator.
  • Hi

    I am able to send the request and get back a valid response, HTTP status 200 OK, with required content.  The issue was I was using the wrong encoded content from the certificate file to sign the message.

    Now I just want to be sure that I process the entire response from the PostAsync method, say for large volumns of data.  The "Async" part is confusing me a bit, and also the fact that the method return value is defined as Task<HttpResponseMessage>.  I want the full response back before I process it.  I have come across the below code in C#:

    using (HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, uri))
    {
        request.Content = new StringContent(envelope, Encoding.UTF8, "application/soap xml");  // envelope is xml message
        using (HttpResponseMessage response = Client.SendAsync(request).Result)
        {
            if (response.IsSuccessStatusCode)
            {
                response.Content.ReadAsStringAsync().ContinueWith(task =>
                {
                    string thirdparty_envelope = task.Result;
                    XElement thirdparty_root = XElement.Parse(thirdparty_envelope);
                    // etc
                }, TaskContinuationOptions.ExecuteSynchronously);
            }
        }
    }
    

    but I do not know if I need it, and if I do, how to translate it.

    I have also come across the await keyword in C#, is there something similar in VC that I should be using?

    Thank

    Brendan

  • Async / await is not yet available in Visual COBOL. The code you posted though uses the alternative method (ContinueWith) so that can be written in COBOL.

               perform using request as type HttpRequestMessage = new HttpRequestMessage(type HttpMethod::Post, uri)
                   set request::Content to new StringContent(envelope, type Encoding::UTF8, "application/soap xml")   *> envelope is xml message
                   perform using response as type HttpResponseMessage = Client::SendAsync(request)::Result
                       if response::IsSuccessStatusCode
                           invoke response::Content::ReadAsStringAsync()::ContinueWith(
                               delegate using task as type Task[string]
                                   declare thirdparty_envelope as string = task::Result
                                   declare thirdparty_root as type XElement = type XElement::Parse(thirdparty_envelope);
                                   *> etc
                               end-delegate,
                               type TaskContinuationOptions::ExecuteSynchronously)
                       end-if
                   end-perform
               end-perform
    
  • Good morning, let me try to explain, today I already sign the XML with digital certificate using a .NET class and I have a program in NetExpress 3.1 that does the transmission, however, when I will transmit the XML to connect in the Web Service I need the same digital certificate that I signed, when I sign I already choose the digital certificate that I want to use, but I can not do it for transmission. In short, I need a .NET class that makes that connection using the same certificate, pass it on and get the answer. Thank you.
  • Good morning, let me try to explain, today I already sign the XML with digital certificate using a .NET class and I have a program in NetExpress 3.1 that does the transmission, however, when I will transmit the XML to connect in the Web Service I need the same digital certificate that I signed, when I sign I already choose the digital certificate that I want to use, but I can not do it for transmission. In short, I need a .NET class that makes that connection using the same certificate, pass it on and get the answer. Thank you.
  • Renato, I think you posted this to the wrong conversation. Your topic is this one:

    community.microfocus.com/.../

    I will respond there.