EldoS | Feel safer!

Software components for data protection, secure storage and transfer

Signature of XML files

Also by EldoS: Rethync
The cross-platform framework that simplifies synchronizing data between mobile and desktop applications and servers and cloud storages
#26073
Posted: 08/09/2013 07:22:15
by Eduardo Helminsky (Standard support level)
Joined: 08/20/2010
Posts: 102

I have used Eldos components and they are amazing good. I have a doubt about sign the following XML because I have a routine (below) that I use to sign XML files (this is working OK) but the following XML signature must be and elemento inside "evento" tag. If I use my routine I got the wrong version. How can I solve this problem ? The tag that must be signed is "infEvento".
Code
cId := 'ID1101113513080932315700012255004000163764133701601401';
cXml := AssinarXML(cXml,'infEvento', cId);

Code
<envEvento xmlns="http://www.portalfiscal.inf.br/nfe" versao="1.00">
<idLote>130809091753445</idLote>
   <evento versao="1.00">
      <infEvento Id="ID1101113513080932315700012255004000163764133701601401">
         <cOrgao>35</cOrgao>
         <tpAmb>2</tpAmb>
         <CNPJ>09323157000122</CNPJ>
         <chNFe>35130809323157000122550040001637641337016014</chNFe>
         <dhEvento>2013-08-09T09:17:41-03:00</dhEvento>
         <tpEvento>110111</tpEvento>
         <nSeqEvento>1</nSeqEvento>
         <verEvento>1.00</verEvento>
         <detEvento versao="1.00">
            <descEvento>Cancelamento</descEvento>
            <nProt>135130004793429</nProt>
            <xJust>ERRO DURANTE A DIGITACAO DA NOTA FISCAL</xJust>
         </detEvento>
      </infEvento>
      <*** SIGNATURE *** (correct)>
   </evento>
   <*** SIGNATURE *** (wrong)>
</envEvento>

Code
function AssinarXML(cXml: String; cNod: String = ''; cUri: String = ''; bDetached: Boolean = False): String;
var Signer: TElXMLSigner;
    X509KeyData: TElXMLKeyInfoX509Data;
    SigNode: TElXMLDOMNode;
    Ref: TElXMLReference;
    Refs: TElXMLReferenceList;
    FXMLDocument: ElXMLDOMDocument;
    S: TStringStream;
begin
     if FCert = nil then begin
        raise Exception.Create('Nenhum certificado digital foi informado para assinar o XML');
     end;
     Refs := TElXMLReferenceList.Create;
     try
        FXMLDocument := TElXMLDOMDocument.Create;
        S := TStringStream.Create('');
        try
           if UpperCase(Copy(cXml,Length(cXml)-3,4)) = '.XML' then begin
              S.LoadFromFile(cXml);
           end else begin
              S.WriteString(cXml);
           end;
           S.Position := 0;
           FXMLDocument.LoadFromStream(S);
        finally
           S.Free;
        end;

        Ref := TElXMLReference.Create;
        if cNod <> '' then begin
           Ref.URINode := FXMLDocument.DocumentElement.FindNode(cNod);
           if Ref.URINode = nil then begin
              Ref.URINode := FXMLDocument.FirstChild;
           end;
        end else begin
           Ref.URINode := FXMLDocument.FirstChild;
        end;
        if cUri <> '' then begin
           Ref.URI := '#' + cUri;
        end else begin
           Ref.URI := '';
        end;

        Ref.TransformChain.Add(TElXMLEnvelopedSignatureTransform.Create);
        Ref.TransformChain.Add(TElXMLC14NTransform.Create);
        Refs.Add(Ref);

        Signer := TElXMLSigner.Create(nil);
        try
           if bDetached then begin
              Signer.SignatureType := xstDetached;
           end else begin
              Signer.SignatureType := xstEnveloped;
           end;
           Signer.CanonicalizationMethod := xcmCanon;
           Signer.SignatureMethodType := xmtSig;
           Signer.SignatureMethod := xsmRSA_SHA1;
           Signer.References := Refs;
           Signer.IncludeKey := True;

           if Assigned(FCert) and FCert.PrivateKeyExists then begin
              X509KeyData := TElXMLKeyInfoX509Data.Create(False);
              X509KeyData.IncludeDataParams := [xkidX509Certificate];
              X509KeyData.IncludeKeyValue := False;
              X509KeyData.Certificate := FCert;
              Signer.KeyData := X509KeyData;
           end;
           Signer.UpdateReferencesDigest;
           Signer.GenerateSignature;
           Signer.Signature.SignaturePrefix := '#default';

           if Signer.SignatureType = xstDetached then begin
              FreeAndNil(FXMLDocument);
              SigNode := nil;
              Signer.Save(SigNode);
              FXMLDocument := SigNode.OwnerDocument;
           end else begin
              SigNode := FXMLDocument.DocumentElement;
              Signer.Save(SigNode);
           end;

           if bDetached then begin
              Result := cXml + FXMLDocument.DocumentElement.OuterXML;
           end else begin
              Result := FXMLDocument.DocumentElement.OuterXML;
           end;
        finally
           FreeAndNil(FXMLDocument);
           FreeAndNil(Signer);
           FreeAndNil(X509KeyData);
        end;
     finally
        FreeAndNil(Refs);
     end;
end;
#26074
Posted: 08/09/2013 08:31:22
by Dmytro Bogatskyy (EldoS Corp.)

Thank you for contacting us.

Quote
the following XML signature must be and elemento inside "evento" tag

You need to pass "evento" element into Signer.Save(..) method. For the enveloped signature type, a "Signature" element is placed as a child node of the passed node.
#26075
Posted: 08/09/2013 08:46:15
by Eduardo Helminsky (Standard support level)
Joined: 08/20/2010
Posts: 102

Thanks for Always fast answers.

I think I understand but there is one doubt left.

The code below always return nil when I try to find the "infEvento" node

Ref.URINode := FXMLDocument.DocumentElement.FindNode('infEvento');
if Ref.URINode = nil then begin
Ref.URINode := FXMLDocument.FirstChild;
end;

What is wrong with my code ?

The same idea should be used to Signer.Save(... findnode('evento')...)

How can I do that ?
#26076
Posted: 08/09/2013 09:22:53
by Dmytro Bogatskyy (EldoS Corp.)

FindNode method by default search for a direct child only. To search deeply you'll need to pass a second parameter as true.

Note: FindNode method search for a node by comparing its node name (not a local name). If, for example, a target element would have a different prefix, like this:
Code
<env:envEvento env:xmlns="http://www.portalfiscal.inf.br/nfe" versao="1.00">
   <env:evento versao="1.00">
      <env:infEvento Id="ID1101113513080932315700012255004000163764133701601401">

Then FindNode method will not find a node. So, to be able to handle this case, you will need to use SelectNodes method with XPath expression or to use FindElementByName helper method from SBXMLUtils unit that will search for the first occurrences of the element.
#26077
Posted: 08/09/2013 09:59:41
by Eduardo Helminsky (Standard support level)
Joined: 08/20/2010
Posts: 102

Thank you very much.

This is exactly what I want.

It was my fault (using CTRL + space), I did not see the second parameter of FindNode.

Both problems were solved.
Also by EldoS: CallbackDisk
Create virtual disks backed by memory or custom location, expose disk images as disks and more.

Reply

Statistics

Topic viewed 863 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!