none
Using .NET client to connect to CMS HETS Eligibility (java) webservice... RRS feed

  • General discussion

  • I hope this is an appropriate question to ask here...

    I am struggling to get my soap message past the WS protocol layer on the HETS system.  Has anyone else had experience with this?  Since I need to sign parts of message body, I can't use a built in WCF security binding - so I have a custom binding that uses a custom encoder to futz with the xml, adding the WS headers, signing using SignedXML class with wsu ID tags on two body parts.  I have sort of cobbled together the message, so when viewing in Fiddler, it looks ok, but something is not right as I always get back a "RSA signature did not verify".  I am looking for any pointers as I'm at my wits end :-)

    Thanks and hopefully there are other devs out there who have connected to HETS successfully from .NET clients...

            public override ArraySegment<byte> WriteMessage(Message message, int maxMessageSize, BufferManager bufferManager, int messageOffset)
            {
                XmlDocument doc = ConvertMessageToXML(message);
    
                // get rid of debugger info
                doc.DocumentElement.FirstChild.RemoveChild(doc.DocumentElement.FirstChild.FirstChild);
    
                // build namespace into request element, not sure if this helps....
                var withNS1 = doc.CreateElement("ns1", "COREEnvelopeRealTimeRequest", "http://www.caqh.org/SOAP/WSDL/CORERule2.2.0.xsd");
                foreach (XmlElement childNode in doc.DocumentElement.LastChild.FirstChild.ChildNodes)
                {
                    withNS1.AppendChild(childNode.Clone());
                }
                doc.DocumentElement.LastChild.ReplaceChild(withNS1,doc.DocumentElement.LastChild.FirstChild);
    
                // build security header
                var wsse = CreateSecurityHeader(doc);
                var timeStamp = CreateTimeStampHeader(doc);
                wsse.AppendChild(timeStamp);
                AddAttributeToPayload(doc);
    
                // get certificate
                var c = GetCertificate();
    
                // add binary security token
                var bst = CreateBinarySecurityToken(c);
                wsse.AppendChild(bst.GetXml(doc));
    
                var sigElement = CreateSignature(doc, c, bst);
                wsse.AppendChild(doc.ImportNode(sigElement, true));
    
                //get rid of document decl
                if (doc.FirstChild is XmlDeclaration)
                {
                    doc.RemoveChild(doc.FirstChild);
                }
                //verify
    
                //var signedXml = new SignedXmlWithId(doc);
    
                // //Find the "Signature" node and create a new 
                //XmlNodeList nodeList = doc.GetElementsByTagName("Signature");
    
                //// Load the signature node.
                //signedXml.LoadXml((XmlElement)nodeList[0]);
    
                //// Check the signature and return the result. 
                //var ok = signedXml.CheckSignature(c.PublicKey.Key);
                return ConvertXmlToBytes(doc, bufferManager, messageOffset);
    
            }
    
            private ArraySegment<byte> ConvertXmlToBytes(XmlDocument doc, BufferManager bufferManager, int messageOffset)
            {
                MemoryStream streamOut = new MemoryStream();
                XmlWriter writer2 = XmlWriter.Create(streamOut, this.writerSettings);
                doc.PreserveWhitespace = preserveWhiteSpace;
                doc.WriteTo(writer2);
                writer2.Close();
    
                byte[] messageBytes = streamOut.GetBuffer();
                int messageLength = (int)streamOut.Position;
                streamOut.Close();
    
                int totalLength = messageLength + messageOffset;
                byte[] totalBytes = bufferManager.TakeBuffer(totalLength);
                Array.Copy(messageBytes, 0, totalBytes, messageOffset, messageLength);
    
                ArraySegment<byte> byteArray = new ArraySegment<byte>(totalBytes, messageOffset, messageLength);
                return byteArray;
            }
    
            private XmlDocument ConvertMessageToXML(Message message)
            {
                MemoryStream stream = new MemoryStream();
                XmlWriter writer = XmlWriter.Create(stream, this.writerSettings);
                message.WriteMessage(writer);
                writer.Close();
                stream.Position = 0;
                XmlDocument doc = new XmlDocument();
                doc.PreserveWhitespace = preserveWhiteSpace;
                doc.Load(stream);
                return doc;
            }
    
            private XmlElement CreateSignature(XmlDocument doc, X509Certificate2 c, BinarySecurityToken bst)
            {
                // Create a SignedXml object.
                var signedXml = new SignedXmlWithId(doc);
    
                // Add the key to the SignedXml document. 
                signedXml.SigningKey = (RSA)c.PrivateKey;
                
                // Create a reference to the timestamp.
                var reference = new Reference("#id1");
                var env = new XmlDsigExcC14NTransform();
                env.Algorithm = SignedXml.XmlDsigExcC14NTransformUrl;
                env.InclusiveNamespacesPrefixList = "ns1 s";
                reference.AddTransform(env);
                reference.DigestMethod = "http://www.w3.org/2001/04/xmlenc#sha256";
                
                // Create a reference to the payload.
                var reference2 = new Reference("#id2");
                var env2 = new XmlDsigExcC14NTransform();
                env2.Algorithm = SignedXml.XmlDsigExcC14NTransformUrl;
                env2.InclusiveNamespacesPrefixList = "ns1 s";
                reference2.AddTransform(env2);
                reference2.DigestMethod = "http://www.w3.org/2001/04/xmlenc#sha256";
    
                // Add the reference to the SignedXml object.
                signedXml.AddReference(reference);
                signedXml.AddReference(reference2);
    
                var keyInfo = new KeyInfo();
                var bstRef = new SecurityTokenReference(bst);
                keyInfo.AddClause(bstRef);
                signedXml.KeyInfo = keyInfo;
    
                // Compute the signature.
                signedXml.ComputeSignature();
    
                // Get the XML representation of the signature and save
                // it to an XmlElement object.
                return signedXml.GetXml();
            }
    
            private BinarySecurityToken CreateBinarySecurityToken(X509Certificate2 c)
            {
                var bst = new X509SecurityToken(c);
                bst.Id = "X509-" + Guid.NewGuid().ToString();
                return bst;
            }
    
            private void AddAttributeToPayload(XmlDocument doc)
            {
                foreach (XmlElement childNode in doc.DocumentElement.LastChild.FirstChild.ChildNodes)
                {
                    if (childNode.Name == "Payload")
                    {
                        var rawHL7 = childNode.InnerText;
                        var cdata = doc.CreateCDataSection(rawHL7);
                        childNode.InnerText = "";
                        childNode.AppendChild(cdata);
                        var attr2 = doc.CreateAttribute("wsu:Id", "http://docs.oasis-open.org/uss/2004/01/oasis-200401-wss-wsssecurity-utility-1.0.xsd");
                        attr2.Value = "id2";
                        childNode.Attributes.Append(attr2);
    
                    }
                    else if (childNode.Name == "TimeStamp")
                    {
                        var attr1 = doc.CreateAttribute("wsu:Id", "http://docs.oasis-open.org/uss/2004/01/oasis-200401-wss-wsssecurity-utility-1.0.xsd");
                        attr1.Value = "id1";
                        childNode.Attributes.Append(attr1);
                    }
    
                }
            }
    
            private XmlElement CreateTimeStampHeader(XmlDocument doc)
            {
                    var timeStamp = doc.CreateElement("wsu", "Timestamp",
             "http://docs.oasis-open.org/uss/2004/01/oasis-200401-wss-wsssecurity-utility-1.0.xsd");
    
                    var created = doc.CreateElement("wsu", "Created",
                        "http://docs.oasis-open.org/uss/2004/01/oasis-200401-wss-wsssecurity-utility-1.0.xsd");
                    created.InnerText = string.Format("{0:yyyy-mm-ddThh:mm:ss.fff}Z", DateTime.Now);
                    timeStamp.AppendChild(created);
                    var expires = doc.CreateElement("wsu", "Expires",
                     "http://docs.oasis-open.org/uss/2004/01/oasis-200401-wss-wsssecurity-utility-1.0.xsd");
                    expires.InnerText = string.Format("{0:yyyy-mm-ddThh:mm:ss.fff}Z", DateTime.Now.AddMinutes(20));
                    timeStamp.AppendChild(expires);
     
                return timeStamp;
            }
    
            private XmlElement CreateSecurityHeader(XmlDocument doc)
            {
    
                var wsse = doc.CreateElement("wsse", "Security",
                    "http://docs.oasis-open.org/uss/2004/01/oasis-200401-wss-wsssecurity-secext-1.0.xsd");
                var mustUnderstandAttr = doc.CreateAttribute("s:mustUnderstand", "http://www.w3.org/2003/05/soap-envelope");
                mustUnderstandAttr.Value = "true";
                wsse.Attributes.Append(mustUnderstandAttr);
                doc.DocumentElement.FirstChild.AppendChild(wsse);
                return wsse;
            }
    
    


    Tuesday, June 3, 2014 1:51 PM

All replies

  • I understand your problem is how to call a SOAP (JAX-WS) web service from Java and get it's returning object. In that case, you have two possible approaches:

      • Generate the Java classes through wsimport and use them; or
      • Create a SOAP client that:
        1. Serializes the object to XML;
        2. Calls the web method through HTTP manipulation; and
        3. Parse the returning XML response back into an object.

    About the first approach (using wsimport):

    I understand that you already have the services' (entities or other) business classes and that the wsimport generates a whole new set of classes (that are somehow duplicates of the classes you already have).

    I'm afraid, though, in this scenario, you can only either:

    • Adapt (edit) the wsimport generated code to make it use your business classes (this is difficult and somehow not worth it - bear in mind everytime the WSDL changes, you'll have to regenerate and readapt the code); or
    • Give up and use the wsimport generated classes.

    About the second approach (create your custom SOAP client):

    In order to implement the second approach, you'll have to:

      • Make the call:
        • Use the SAAJ (SOAP with Attachments API for Java) framework (see below, it's shipped with JSE 1.6) to make the calls; or
        • You can also do it through java.net.HttpUrlconnection (and some java.io handling).
      • Turn the objects into XML and vice-versa:
        • Use an OXM (Object to Xml Mapping) framework such as JAXB to serialize/deserialize the XML from/into objects
        • Or, if you must, manually create/parse the XML (this can be the best solution if the received object is only a little bit differente from the sent one).

    Creating a SOAP client using classic java.net.HttpUrlConnection is not that hard (but not that simple either), and you can find in this link a very good starting code.

    I recomment you use the SAAJ framework:

    SOAP with Attachments API for Java (SAAJ) is mainly used for dealing directly with SOAP Request/Response messages which happens behind the scenes in any Web Service API. It allows the developers to directly send and receive soap messages instead of using JAX-WS.

    See below a working example (run it!) of a SOAP web service call using SAAJ. It calls this web service.

    import javax.xml.soap.*;
    import javax.xml.transform.*;
    import javax.xml.transform.stream.*;
    
    public class SOAPClientSAAJ {
    
        /**
         * Starting point for the SAAJ - SOAP Client Testing
         */
        public static void main(String args[]) {
            try {
                // Create SOAP Connection
                SOAPConnectionFactory soapConnectionFactory = SOAPConnectionFactory.newInstance();
                SOAPConnection soapConnection = soapConnectionFactory.createConnection();
    
                // Send SOAP Message to SOAP Server
                String url = "http://ws.cdyne.com/emailverify/Emailvernotestemail.asmx";
                SOAPMessage soapResponse = soapConnection.call(createSOAPRequest(), url);
    
                // Process the SOAP Response
                printSOAPResponse(soapResponse);
    
                soapConnection.close();
            } catch (Exception e) {
                System.err.println("Error occurred while sending SOAP Request to Server");
                e.printStackTrace();
            }
        }
    
        private static SOAPMessage createSOAPRequest() throws Exception {
            MessageFactory messageFactory = MessageFactory.newInstance();
            SOAPMessage soapMessage = messageFactory.createMessage();
            SOAPPart soapPart = soapMessage.getSOAPPart();
    
            String serverURI = "http://ws.cdyne.com/";
    
            // SOAP Envelope
            SOAPEnvelope envelope = soapPart.getEnvelope();
            envelope.addNamespaceDeclaration("example", serverURI);
    
            /*
            Constructed SOAP Request Message:
            <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:example="http://ws.cdyne.com/">
                <SOAP-ENV:Header/>
                <SOAP-ENV:Body>
                    <example:VerifyEmail>
                        <example:email>mutantninja@gmail.com</example:email>
                        <example:LicenseKey>123</example:LicenseKey>
                    </example:VerifyEmail>
                </SOAP-ENV:Body>
            </SOAP-ENV:Envelope>
             */
    
            // SOAP Body
            SOAPBody soapBody = envelope.getBody();
            SOAPElement soapBodyElem = soapBody.addChildElement("VerifyEmail", "example");
            SOAPElement soapBodyElem1 = soapBodyElem.addChildElement("email", "example");
            soapBodyElem1.addTextNode("mutantninja@gmail.com");
            SOAPElement soapBodyElem2 = soapBodyElem.addChildElement("LicenseKey", "example");
            soapBodyElem2.addTextNode("123");
    
            MimeHeaders headers = soapMessage.getMimeHeaders();
            headers.addHeader("SOAPAction", serverURI  + "VerifyEmail");
    
            soapMessage.saveChanges();
    
            /* Print the request message */
            System.out.print("Request SOAP Message = ");
            soapMessage.writeTo(System.out);
            System.out.println();
    
            return soapMessage;
        }
    
        /**
         * Method used to print the SOAP Response
         */
        private static void printSOAPResponse(SOAPMessage soapResponse) throws Exception {
            TransformerFactory transformerFactory = TransformerFactory.newInstance();
            Transformer transformer = transformerFactory.newTransformer();
            Source sourceContent = soapResponse.getSOAPPart().getContent();
            System.out.print("\nResponse SOAP Message = ");
            StreamResult result = new StreamResult(System.out);
            transformer.transform(sourceContent, result);
        }
    
    }

    (The code above was taken and adapted from this page.)

    About using JAXB for serializing/deserializing, it is very easy to find information about it. You can start here: http://www.mkyong.com/java/jaxb-hello-world-example/.

    Thursday, June 5, 2014 10:14 AM
  • Hi,

    Could you please try to post some part of the code here? Then it will be better to help find a solution.

    Also for the question about the java web service, then it will be better to post it in the following forum:
    #WCF, ASMX and other Web Services:
    http://forums.asp.net/28.aspx/1?WCF+ASMX+and+other+Web+Services .

    Thanks for your understanding.

    Best Regards,
    Amy Peng


    We are trying to better understand customer views on social support experience, so your participation in this interview project would be greatly appreciated if you have time. Thanks for helping make community forums a great place.
    Click HERE to participate the survey.

    Friday, June 6, 2014 7:57 AM
    Moderator
  • Hi, Thanks for replying, but the challenge is that I'm using .NET client to contact the java webservice.  I tried using WCF but because I need to sign individual parts of the message body, I need to do some custom work.  Currently, I am using a custom encoder, and constructing the security headers 'by hand', and also using some helpers form WSE 3.0, but I'm not very familiar with WSE so I don't have the whole thing implemented as WSE solution.
    Friday, June 13, 2014 1:16 PM
  • Could you please let me know how to solve the problem?

    I am doing the almost same thing and I would like to know the best approach.

    Thanks in advance.

    // Stanley Haun

    Wednesday, May 6, 2015 4:10 AM
  • We ended up deciding to not use SOAP for this connection.   The time we were spending trying to get the SOAP connection to work correctly was not worth it.

    CMS HETS also supports MIME, it was much easier to implement this than it was for the SOAP request.

    Here are some of the pieces that we used to send the request:

    HttpRequestMessage requestMessage = new HttpRequestMessage(HttpMethod.Post, webService); requestMessage.Headers.ExpectContinue = false; MultipartFormDataContent multiPartContent = new MultipartFormDataContent("----SOME ARBITRARY STRING"); ByteArrayContent byteArrayContent = new ByteArrayContent(Encoding.ASCII.GetBytes(payload)); byteArrayContent.Headers.Add("Content-Type", "text/plain"); multiPartContent.Add(new StringContent("X12_270_Request_005010X279A1"), "PayloadType"); multiPartContent.Add(new StringContent("RealTime"), "ProcessingMode"); - - - requestMessage.Content = multiPartContent; WebRequestHandler handler = new WebRequestHandler();

    handler.ClientCertificates.Add(YOURCERTIFICATE); handler.ClientCertificateOptions = ClientCertificateOption.Automatic; HttpClient httpClient = new HttpClient(handler); try { Task<HttpResponseMessage> httpRequest = httpClient.SendAsync(requestMessage, HttpCompletionOption.ResponseContentRead, CancellationToken.None); HttpResponseMessage httpResponse = httpRequest.Result; HttpStatusCode statusCode = httpResponse.StatusCode; --- ETC




    • Edited by J Massie Wednesday, May 6, 2015 6:07 PM
    Wednesday, May 6, 2015 6:07 PM
  • Thanks for the information.

    Changed to MIME based on the codes you provided. Implementation was much simpler than SOAP as you described. But I got the following exception while sending the request to HETS. Have been spending lot of time but it is hard to figure out the causes. Could please share your experience if you met the similar issue? Also, I would like to know your development environment and any configuration changes in the web.config. I developed the Azure Web-site application using WCF.

    I did not include the code because my implementation is almost same as yours.

    Thanks again for your support.

    // Stanley Haun

    System.AggregateException: One or more errors occurred. ---> System.Net.Http.HttpRequestException: An error occurred while sending the request. ---> System.Net.WebException: Unable to connect to the remote server ---> System.Net.Sockets.SocketException: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond 158.73.232.12:443 at System.Net.Sockets.Socket.EndConnect(IAsyncResult asyncResult) at System.Net.ServicePoint.ConnectSocketInternal(Boolean connectFailure, Socket s4, Socket s6, Socket& socket, IPAddress& address, ConnectSocketState state, IAsyncResult asyncResult, Exception& exception) --- End of inner exception stack trace --- at System.Net.HttpWebRequest.EndGetRequestStream(IAsyncResult asyncResult, TransportContext& context) at System.Net.Http.HttpClientHandler.GetRequestStreamCallback(IAsyncResult ar) --- End of inner exception stack trace --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at HetsEligibilityService.Backend.CustomRequestHandler.<SendAsync>d__0.MoveNext() --- End of inner exception stack trace --- at HetsEligibilityService.Backend.BuildRequestPackage.BuildMimeRequestTransaction(Object requestPayload) at HetsEligibilityService.Backend.BuildRequestPackage.BuildRealTimeRequestTransaction(Object requestPayload) at HetsEligibilityService.ServiceEligibility.InvokeRealTimeTransactionJsonObj(EligibilityBenefitRequest request) at HetsEligibilityService.ServiceEligibility.InvokeRealTimeTransactionJson(JsonString jsonStr)---> (Inner Exception #0) System.Net.Http.HttpRequestException: An error occurred while sending the request. ---> System.Net.WebException: Unable to connect to the remote server ---> System.Net.Sockets.SocketException: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond 158.73.232.12:443 at System.Net.Sockets.Socket.EndConnect(IAsyncResult asyncResult) at System.Net.ServicePoint.ConnectSocketInternal(Boolean connectFailure, Socket s4, Socket s6, Socket& socket, IPAddress& address, ConnectSocketState state, IAsyncResult asyncResult, Exception& exception) --- End of inner exception stack trace --- at System.Net.HttpWebRequest.EndGetRequestStream(IAsyncResult asyncResult, TransportContext& context) at System.Net.Http.HttpClientHandler.GetRequestStreamCallback(IAsyncResult ar) --- End of inner exception stack trace --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at HetsEligibilityService.Backend.CustomRequestHandler.<SendAsync>d__0.MoveNext()<---




    Tuesday, June 9, 2015 6:21 PM
  • Just taking a quick look at this, my first guess would be that it has something to do with the certificate.

    Did you submit a certificate to HETS?  If so did they say that it had been properly setup?  Also you need to make sure that the certificate is added to the machine you are sending from.

    In the code above you will see the 2 lines:

    handler.ClientCertificates.Add(YOURCERTIFICATE);
    
    handler.ClientCertificateOptions = ClientCertificateOption.Automatic;

    I would make sure that the certificate that you have set to the YOURCERTIFICATE value is the correct one, and make sure the requesting client has no problem getting it when this code is called.

    That is where I would start.  It is tricky because if you do not have a valid certificate, you can not connect, it will not return any message stating that is the reason.

    Wednesday, June 10, 2015 2:02 PM
  • Thanks for a prompt response.

    Sent the .cer file to HETS and they said that it is properly installed.

    I am using the client certification .pfx file and build using the following code.

            private X509Certificate2 BuildClientCertificates()
            {
                const string certClientPass = @"##########";

                Trace.TraceInformation(@"Reading the certification...client_info_pfx");
                byte[] rawBytes = Properties.Resources.client_info_pfx;
                Trace.TraceInformation(@"Reading the certification...client_info_pfx...{0}", rawBytes.Length);
                X509Certificate2 x509 = new X509Certificate2();
                Trace.TraceInformation(@"Importing the certification...client_info_pfx");
                x509.Import(rawBytes, certClientPass, X509KeyStorageFlags.DefaultKeySet);
                Trace.TraceInformation(@"Loading the certification...client_info_pfx...Done...{0}", x509.ToString());
                return x509;
            }

    The client certification information (.pfx) is exactly matched with the .cer certification file sent to HETS.

    Thanks again for your support.

    // Stanley Haun

    Wednesday, June 10, 2015 3:54 PM
  • The MIME implmentation is much simpler than SOAP.

    The problem is due to the outbound IP address.

    https://social.msdn.microsoft.com/Forums/azure/en-US/d2ce89a1-1dcd-4959-8c7e-10c94f05c608/unable-to-reach-target-services-outside-azure?forum=windowsazurewebsitespreview#51ab03ca-4fe9-44bb-aeba-d7a151bdf646

    Thanks for your support.

    // Stanley Haun


    Saturday, June 27, 2015 6:19 PM
  • Hi JeanBB,

    Did you get any solution for this ?

    Am also doing the same thing to connect CMS over SOAP from .Net client , getting below fault strings

    Error1   

    <faultcode>soap:Client</faultcode>
           <faultstring>*RSA signature did not verify*</faultstring>
    Error2
    <faultcode>soap:Client</faultcode>
     <faultstring>*Decode signature failed*</faultstring>

    Please give me your valuable inputs to resolve them .

    Thanks in advance


    Wednesday, August 19, 2015 2:45 PM
  • Hi J Massie,

    I stuck with the same issue from last  10 days :( over soap, now am trying with MIME connectivity based on your code piece , can you please share your full code its helps me a lot to resolve the connectivity issue .

    Please post the code or send me to my mail sreenivasd7@gmail.com

    Awaiting for your reply ....

    Thanks in advance 

    Sreenivas Devishetti


    Thursday, August 27, 2015 2:47 PM
  • Hi Stanley Haun,

    I stuck with the same issue from last  10 days :( over soap, now am trying with MIME connectivity, if you have code part please share ...


    Awaiting for your reply ....

    Thanks in advance 

    Sreenivas Devishetti
    sreenivasd7@gmail.com
    Thursday, August 27, 2015 2:48 PM
  • Hi  J Massie ,

    Please send me the code for MIME part . I wasted many days on this task , please help me :(

    Thanks in advance 

    Sreenivas Devishetty


    Thursday, September 10, 2015 7:48 AM
  • Hi Sreenivas Devishetty,

    Have you got anything on CMS soap request or MIME request.

    I have been working on soap request for past month and were unable to do anything.

    It always gives :

    • The underlying connection was closed: An unexpected error occurred on a send.
    • Authentication failed because the remote party has closed the transport stream.

    What you have used MIME or SOAP.

    Please help me out with this.


    Thursday, October 4, 2018 7:42 AM
  • We're also struggling with this and trying to move to MIME, would you be able post more of code? Particularly how byteArrayContent fits in with relation to multiPartContent.

    Any help would be appreciated.


    Friday, July 31, 2020 5:19 AM