Canonicalizing and digitally signing xml

Posted by asgt1974 on 29-Jun-2010 11:43

Hi, Apologies if I have posted this on the incorrect forum!

We have to connect to a web service provided by the Irish government and there is a requirement for the xml in our webservice to be canonicalized and signed with a digital signature. We are using a SAX writer within the ABL to create the XML and then using cURL to send it via web-service after signing it using OpenSSL. After spending several days trying to get our messages accepted we're still at the stage where the OpenEdge (10.2A) ABL code we have written to canonicalize the xml is seemingly not creating correctly canonicalized xml. I was hoping someone could give us some pointers. It may be that Progress isn't the correct way forward - we've already given up on using Progress to do the digital signing of the xml and we're instead using OpenSSL to do that (fairly sure that works but always a chance out problem is there) but we've looked at various xml toolkits to do the canonicalization outside of Progress but the main problem there is that we're on a Windows server and getting a Perl xml toolkit to work on Windows is proving quite a problem - I suspect were we running on Linux it would be far easier but unfortunately thats not an option!

Any assistance at all would be very appreciated!

Thanks, Andrew.

All Replies

Posted by asgt1974 on 05-Jul-2010 10:25

Hi, Having solved the issue we were having digitally signing our xml I thought I should post something just in case anyone else out there has similar issues. Apologies if this is quite a long post but hopefully it might help someone! We make no claims that this was the perfect solution - but it worked - eventually.

The following can be used where a Web Service call needs to be made where a Digital Certificate is required and the XML request message has to include a Digital Signature for XML Security.
Note:  We have manually built our SOAP message including the Security node and its child nodes/elements and then used the os-command to make a synchronous cURL command-line call to send the XML message and receive the response.
To use a provided Digital Certificate:
We were provided with a P12 certificate that has a private key to be able communicate with the destination Web Service with a valid “handshake”.  To use this in the cURL command, the following was performed:
·         A PEM file was produced that contained the Client Certificate and Private Key from the P12 file by using the openssl command:  “openssl pkcs12 –in [cert file].p12 –out [cert file].pem –clcerts”
·         The cURL command includes a –E parameter of the PEM file (all programmatically included in the command from our software by being able to reference the PEM file and this is stored in a cLOB field on our database).  The command is:  “cURL -E [cert file].pem:[PEM Pass Phrase] -H "Content-Type:text/xml; charset=UTF-8" -H "SOAPAction:[service name]" -d @[request message].xml -X POST [Web Service URL] > [response message].xml”
To build the XML Security elements:
To be able to have a request message validated, as well as the digital certificate “handshake” the message had to include security elements for a digital signature value, which was calculated as follows (using pre-OASIS security standards http://schemas.xmlsoap.org/ws/2002/07/secext - OASIS security standards can also be implemented):
·         A single digest value of a sub-document section of the XML message was required.  The sub-document was built using the SAX Writer and then this longchar was converted using the SHA1-DIGEST function and then the BASE64-ENCODE function.  Note that the sub-document to be included in the digest value must have an “Id” attribute with a value that is the same as the Reference element’s “URI” attribute (e.g.  and a .
·         The signature value is digest of the node (which includes the digest value produced above) using the private key from the PEM file.  To do this, the SHA1-DIGEST function seemed to produce an incorrect value.  Therefore, the value was calculated using the openssl command (via os-command) as follows:
  • openssl dgst -sha1 –sign [cert file].pem –passin pass:[PEM Pass Phrase] –out [sha1 file] [XML sub-document]
  • openssl base64 –in [sha1 file] –out [base64 file]
  • copy-lob from file “[base64 file]” to [longchar variable – signature value]
·         The XML sub-documents used to calculate the digest value and signature value must both be canonicalised (exclusive canonicalization in this case - http://www.w3.org/2001/10/xml-exc-c14n#).  There is no canonicalization (C14N) conversion functionality provided by OpenEdge, so the following was performed on the XML sub-documents before they were hashed/base64-encoded:
  • All empty elements must be defined with start and end tags e.g.  “ instead of .  Therefore, when building the XML using SAX Writer the START-ELEMENT and END-ELEMENT methods were used instead of WRITE-EMPTY-ELEMENT.  Note that the XML document was re-opened using DOM (X-Document) to set a node value on some empty elements (digest value, etc), but on the SAVE method OpenEdge decided to automatically re-format the XML document so empty start and end tags were replaced with the shorted , which was a pain.  We wrote a function to manipulate the XML longchar to put the start and end tags back (function included below)
  • Leave the FORMATTED attribute of the SAX Writer object as FALSE, so no whitespace/line-feeds should be included.
  • Ensure the namespaces and attributes of a node are supplied in alphabetical order, with all namespaces first before attributes.  Therefore, ensure all DECLARE-NAMESPACE methods are applied before INSERT-ATTRIBUTE.  Note that with a node without a prefix, it’s namespace declaration will be 1st in order.   E,g,  
  • Any namespace prefix referenced within the XML message to be signed must have the xmlns declared once at the highest point where referenced (as this is a sub-document of the entire XML message it may require a namespace declaration declared in the whole XML document in a parent node).
  • All element and attribute values must be html-encoded for special characters.
  • Trim any line-feed characters from the XML longchar after it has been written.
Some of the data enclosed in the following XML documents has been removed for confidentiality reasons:
Example XML message (complete):
-
-
-
  MIICvzCCAacCEHu...
-
-
 
 
-
-
 
 
 
  9hlO6am...
 
 
  RbEmOsSusAq7tx...
-
-
 
 
 
 
 
 
-
 
 
 
XML sub-document for Digest Value:
XML sub-document for Signature Value:
9hlO6am...
Function to replace empty element tags with start and end tags - note this worked for us but may well need some changes when applied elsewhere:
function fnCanonical returns longchar (input p-xml as longchar):
  def var v-index as int no-undo.
  def var v-start as int no-undo.
  def var v-elementName as char no-undo.
  def var v-char as char no-undo.
 
  do while index(p-xml, "/>") > 0:
    assign
      v-elementName = ""
      v-index = index(p-xml, "/>")
      v-start = r-index(p-xml, "
     
    if v-start > 0 then do:
      do while true:
        assign
          v-start = v-start + 1
          v-char = substring(p-xml, v-start, 1).
        if v-char = " " or v-char = ">" then leave.
       
        v-elementName = v-elementName + v-char.
      end.
     
      if v-elementName <> "" then
        p-xml = substring(p-xml, 1, v-index - 1) + ">" +
                 substring(p-xml, v-index + 2).
    end.
  end.
 
  p-xml = trim(p-xml, chr(10)).
 
  return p-xml.
end function.

Posted by Admin on 13-Jul-2010 18:19

Andrew,

Gracias por el post. La función que utilizaste tiene problemas. Te dejo la que yo implementé.

Saludos.

Juan Carlos.

function fnCanonical returns longchar (input p-xml as longchar):

  define variable pi as integer no-undo.
  define variable pf as integer no-undo.
  define variable tg as character no-undo.

  do while index(p-xml, "/>") > 0:
    pf = index(p-xml, "/>").
    pi = r-index(p-xml,"
    tg = substring(p-xml,pi + 1,pf - pi - 1).
    substring(p-xml,pf,2) = ">".
  end.

  p-xml = trim(p-xml, chr(10)).

  return p-xml.
end function.

This thread is closed