EldoS | Feel safer!

Software components for data protection, secure storage and transfer

Delphi, XML -- TElXMLDOMDocument alteres document

Also by EldoS: CallbackFilter
A component to monitor and control disk activity, track file and directory operations (create, read, write, rename etc.), alter file data, encrypt files, create virtual files.
Posted: 03/08/2014 03:19:20
by Jon Lennart Aasenden (Basic support level)
Joined: 03/08/2014
Posts: 1

My task: Create a simple delphi class that loads a government defined xml document, signs this using a smartcard, save the exact results to a stream, convert to BASE64 - and send to a soap server.

I have pieced together a signing class from the examples and online documentation. The signing process works fine. The certificate and hash are decoded properly by the reciever -- but for some reason the TElXMLDOMDocument alters the XML data.

It seems to generate the signaure based on the original xml, but when i save the xml from TElXMLDOMDocument with the signature, the xml that comes out is not the same that came in.

1. It replaces unix LF with CR + LF (one byte extra per line)
2. It changes uppercase encoding="UTF-8" into lowercase "utf-8"

So when i send the XML, the document never validates, because the signature doesnt match the output XML, only the input XML.

Is there some way of telling the TElXMLDOMDocument object to not modify the XML in any way - except the insertion of the signature tag?

As it stands now we first have to load the XML into TElXMLDOMDocument, save it out, then load it back in again for the source-xml and signed-xml to match.

procedure TNextXMLSign.LoadFromStream(aXMLStream: TStream);

  function CheckBOM(const aData:TStream):Boolean;
    mChar:  Byte;
    result:=( (aData.Read(mChar,1)=1) and (mChar=$EF))
    and     ( (aData.Read(mChar,1)=1) and (mChar=$BB))
    and     ( (aData.Read(mChar,1)=1) and (mChar=$BF));

  mCache: TStream;

      (* Does BOm exist? Skip 3 bytes to strip it *)
      if CheckBOM(aXMLStream) then
      end else
        (* No BOM, copy entire stream *)

      (* Load into XML *)

      (* Save out again, to make sure CR+LF+case matches later.
         The XML document alters things like uppercase/ CRLF etc.
         By doing it this way, the signed document will always match
         the data we send *)


      (* And re-load the now formated XML, so that the signature
         match what we except to find *)

    on e: exception do
      Raise Exception.CreateFmt
      ('Failed to load XML, TElXMLDOMDocument threw exception: %s',

The signing routine:


function TNextXMLSign.Sign(aProvider:String;var aTargetStream:TStream):Boolean;
  mSigner:  TElXMLSigner;
  mRefList: TELXMLReferenceList;
  mRef: TELXmlReference;
  mData:  TElXMLKeyInfoX509Data;
  mDummy: TELXmlDOMNode;

  (* Release current certificate if already used *)
  if assigned(FCert) then

  if length(aProvider)>0 then

    (* Check that XML is loaded *)
    if assigned(FXML) then
      if FindValidCert(aProvider, FCert) then
        (* Setup signer *)
          (* Initialize signing control *)

          (* Create reference list
             NOTE: The signer-control releases this on destruction!
                   DO NOT FREE *)
          mRef.URINode := FXml.DocumentElement;
          mRef.URI := '';

          (* Add XML transform for signature *)

          (* Get the certificate key data for export
             NOTE: The signer-control releases this on destruction!
                   DO NOT FREE *)
          mData := TElXMLKeyInfoX509Data.Create(False);
          mData.Certificate := FCert;
          mSigner.KeyData := mData;

          (* Hook up references *)

          (* Update the digest paths *)

          (* Generate signature and sign *)

          (* This triggers the certificate and causes a buypass dialog *)



      end else
      Raise Exception.Create('Sign failed, a valid certificate could not be found');
    end else
    Raise Exception.Create('Sign failed, no XML loaded error');
  end else
  raise Exception.Create('Sign failed, provider string was empty');
Posted: 03/08/2014 17:16:07
by Dmytro Bogatskyy (EldoS Corp.)

Thank you for contacting us.

1. It replaces unix LF with CR + LF (one byte extra per line)

TElXMLDOMDocument doesn't do such replacement. Maybe you meant that it replace in opposite direction (CR LF -> LF)?
Then, yes, TElXMLDOMDocument do perform new-line normalization on document loading (the normalization options is controlled by a third parameter in the TElXMLDOMDocument.LoadFromStream method).
See: http://www.eldos.com/documentation/sb...tream.html
If you set it to true (default value) a CRLF and CR characters will be replaced by LF character.
2. It changes uppercase encoding="UTF-8" into lowercase "utf-8"

The simplest way to change it, at the moment, is to you override GetCharsetName method of TElXMLUTF8Codec class.
Please see the sample here:

By the way, you don't need to remove byte order mark (BOM), the LoadFromStream method should correctly understand it.



Topic viewed 2316 times

Number of guests: 1, registered members: 0, in total hidden: 0


Back to top

As of July 15, 2016 EldoS Corporation will operate as a division of /n software inc. For more information, please read the announcement.

Got it!