Sample code for 30+ languages & platforms
Lazarus Pascal

XML-DSig Add Object Reference with Transforms Specified Explicitly

Demonstrates how to use the new AddObjectRef2 method to explicitly specify the XML Transforms fragment.

Chilkat Lazarus Pascal Downloads

Lazarus Pascal
program ChilkatDemo;

// Demonstrates using the Chilkat Pascal wrapper via the C bridge DLL.
// Builds as a console application under Lazarus (FPC) or Delphi.

{$IFDEF FPC}
  {$MODE DELPHI}
{$ENDIF}
{$APPTYPE CONSOLE}

uses
  {$IFDEF UNIX}
  cthreads,
  {$ENDIF}
  SysUtils,
  CkDllLoader,
  Chilkat.StringBuilder,
  Chilkat.XmlDSigGen,
  Chilkat.Xml,
  Chilkat.XmlDSig,
  Chilkat.Cert;

// ---------------------------------------------------------------------------

procedure RunDemo;
var
  success: Boolean;
  xmlToSign: TXml;
  gen: TXmlDSigGen;
  object1: TXml;
  xml1: TXml;
  xml2: TXml;
  cert: TCert;
  sbXml: TStringBuilder;
  verifier: TXmlDSig;
  numSigs: Integer;
  verifyIdx: Integer;
  verified: Boolean;

begin
  success := False;

  //  This example requires the Chilkat API to have been previously unlocked.
  //  See Global Unlock Sample for sample code.

  success := True;

  //  Build the following XML to be signed:

  //  <?xml version="1.0" encoding="utf-8"?>
  //  <InitUpload xmlns="http://e-dokumenty.mf.gov.pl">
  //      <DocumentType>JPK</DocumentType>
  //      <Version>01.02.01.20160617</Version>
  //      <EncryptionKey algorithm="RSA" encoding="Base64" mode="ECB" padding="PKCS#1">xxxx</EncryptionKey>
  //      <DocumentList>
  //          <Document>
  //              <FormCode schemaVersion="1-1" systemCode="JPK_VAT (3)">JPK_VAT</FormCode>
  //              <FileName>JPK_VAT_3_v1-1_20181201.xml</FileName>
  //              <ContentLength>8736</ContentLength>
  //              <HashValue algorithm="SHA-256" encoding="Base64">JEDI1pItwh6dj/Xx1uts/x61qnjZ4DLHpkZMhmf1oKQ=</HashValue>
  //              <FileSignatureList filesNumber="1">
  //                  <Packaging>
  //                      <SplitZip mode="zip" type="split"/>
  //                  </Packaging>
  //                  <Encryption>
  //                      <AES block="16" mode="CBC" padding="PKCS#7" size="256">
  //                          <IV bytes="16" encoding="Base64">z64oN9zXHt1+S3XACRSCYw==</IV>
  //                      </AES>
  //                  </Encryption>
  //                  <FileSignature>
  //                      <OrdinalNumber>1</OrdinalNumber>
  //                      <FileName>JPK_VAT_3_v1-1_20181201-000.xml.zip.aes</FileName>
  //                      <ContentLength>16</ContentLength>
  //                      <HashValue algorithm="MD5" encoding="Base64">5MX0q1935fvMjLFV7E1yDw==</HashValue>
  //                  </FileSignature>
  //              </FileSignatureList>
  //          </Document>
  //      </DocumentList>
  //  </InitUpload>

  //  Use this online tool to generate code from sample XML: 
  //  Generate Code to Create XML

  xmlToSign := TXml.Create;
  xmlToSign.Tag := 'InitUpload';
  xmlToSign.AddAttribute('xmlns','http://e-dokumenty.mf.gov.pl');
  xmlToSign.UpdateChildContent('DocumentType','JPK');
  xmlToSign.UpdateChildContent('Version','01.02.01.20160617');
  xmlToSign.UpdateAttrAt('EncryptionKey',True,'algorithm','RSA');
  xmlToSign.UpdateAttrAt('EncryptionKey',True,'encoding','Base64');
  xmlToSign.UpdateAttrAt('EncryptionKey',True,'mode','ECB');
  xmlToSign.UpdateAttrAt('EncryptionKey',True,'padding','PKCS#1');
  xmlToSign.UpdateChildContent('EncryptionKey','xxxx');
  xmlToSign.UpdateAttrAt('DocumentList|Document|FormCode',True,'schemaVersion','1-1');
  xmlToSign.UpdateAttrAt('DocumentList|Document|FormCode',True,'systemCode','JPK_VAT (3)');
  xmlToSign.UpdateChildContent('DocumentList|Document|FormCode','JPK_VAT');
  xmlToSign.UpdateChildContent('DocumentList|Document|FileName','JPK_VAT_3_v1-1_20181201.xml');
  xmlToSign.UpdateChildContent('DocumentList|Document|ContentLength','8736');
  xmlToSign.UpdateAttrAt('DocumentList|Document|HashValue',True,'algorithm','SHA-256');
  xmlToSign.UpdateAttrAt('DocumentList|Document|HashValue',True,'encoding','Base64');
  xmlToSign.UpdateChildContent('DocumentList|Document|HashValue','JEDI1pItwh6dj/Xx1uts/x61qnjZ4DLHpkZMhmf1oKQ=');
  xmlToSign.UpdateAttrAt('DocumentList|Document|FileSignatureList',True,'filesNumber','1');
  xmlToSign.UpdateAttrAt('DocumentList|Document|FileSignatureList|Packaging|SplitZip',True,'mode','zip');
  xmlToSign.UpdateAttrAt('DocumentList|Document|FileSignatureList|Packaging|SplitZip',True,'type','split');
  xmlToSign.UpdateAttrAt('DocumentList|Document|FileSignatureList|Encryption|AES',True,'block','16');
  xmlToSign.UpdateAttrAt('DocumentList|Document|FileSignatureList|Encryption|AES',True,'mode','CBC');
  xmlToSign.UpdateAttrAt('DocumentList|Document|FileSignatureList|Encryption|AES',True,'padding','PKCS#7');
  xmlToSign.UpdateAttrAt('DocumentList|Document|FileSignatureList|Encryption|AES',True,'size','256');
  xmlToSign.UpdateAttrAt('DocumentList|Document|FileSignatureList|Encryption|AES|IV',True,'bytes','16');
  xmlToSign.UpdateAttrAt('DocumentList|Document|FileSignatureList|Encryption|AES|IV',True,'encoding','Base64');
  xmlToSign.UpdateChildContent('DocumentList|Document|FileSignatureList|Encryption|AES|IV','z64oN9zXHt1+S3XACRSCYw==');
  xmlToSign.UpdateChildContent('DocumentList|Document|FileSignatureList|FileSignature|OrdinalNumber','1');
  xmlToSign.UpdateChildContent('DocumentList|Document|FileSignatureList|FileSignature|FileName','JPK_VAT_3_v1-1_20181201-000.xml.zip.aes');
  xmlToSign.UpdateChildContent('DocumentList|Document|FileSignatureList|FileSignature|ContentLength','16');
  xmlToSign.UpdateAttrAt('DocumentList|Document|FileSignatureList|FileSignature|HashValue',True,'algorithm','MD5');
  xmlToSign.UpdateAttrAt('DocumentList|Document|FileSignatureList|FileSignature|HashValue',True,'encoding','Base64');
  xmlToSign.UpdateChildContent('DocumentList|Document|FileSignatureList|FileSignature|HashValue','5MX0q1935fvMjLFV7E1yDw==');

  gen := TXmlDSigGen.Create;

  gen.SigLocation := 'InitUpload';
  gen.SigLocationMod := 0;
  gen.SigId := 'id-1234';
  gen.SigNamespacePrefix := 'ds';
  gen.SigNamespaceUri := 'http://www.w3.org/2000/09/xmldsig#';
  gen.SignedInfoCanonAlg := 'EXCL_C14N';
  gen.SignedInfoDigestMethod := 'sha256';

  //  Create an Object to be added to the Signature.

  //  <xades:QualifyingProperties Target="#id-1234" xmlns:xades="http://uri.etsi.org/01903/v1.3.2#">
  //      <xades:SignedProperties Id="xades-id-1234">
  //          <xades:SignedSignatureProperties>
  //              <xades:SigningTime>TO BE GENERATED BY CHILKAT</xades:SigningTime>
  //              <xades:SigningCertificate>
  //                  <xades:Cert>
  //                      <xades:CertDigest>
  //                          <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
  //                          <ds:DigestValue>TO BE GENERATED BY CHILKAT</ds:DigestValue>
  //                      </xades:CertDigest>
  //                      <xades:IssuerSerial>
  //                          <ds:X509IssuerName>TO BE GENERATED BY CHILKAT</ds:X509IssuerName>
  //                          <ds:X509SerialNumber>TO BE GENERATED BY CHILKAT</ds:X509SerialNumber>
  //                      </xades:IssuerSerial>
  //                  </xades:Cert>
  //              </xades:SigningCertificate>
  //          </xades:SignedSignatureProperties>
  //          <xades:SignedDataObjectProperties>
  //              <xades:DataObjectFormat ObjectReference="#r-id-1">
  //                  <xades:MimeType>text/xml</xades:MimeType>
  //              </xades:DataObjectFormat>
  //          </xades:SignedDataObjectProperties>
  //      </xades:SignedProperties>
  //  </xades:QualifyingProperties>

  object1 := TXml.Create;
  object1.Tag := 'xades:QualifyingProperties';
  object1.AddAttribute('xmlns:xades','http://uri.etsi.org/01903/v1.3.2#');
  object1.AddAttribute('Target','#id-1234');
  object1.UpdateAttrAt('xades:SignedProperties',True,'Id','xades-id-1234');
  //  Note: It may be that http://www.w3.org/2001/04/xmlenc#sha256 is needed in the following line instead of http://www.w3.org/2000/09/xmldsig#sha1
  object1.UpdateChildContent('xades:SignedProperties|xades:SignedSignatureProperties|xades:SigningTime','TO BE GENERATED BY CHILKAT');
  object1.UpdateAttrAt('xades:SignedProperties|xades:SignedSignatureProperties|xades:SigningCertificateV2|xades:Cert|xades:CertDigest|ds:DigestMethod',True,'Algorithm','http://www.w3.org/2000/09/xmldsig#sha1');
  object1.UpdateChildContent('xades:SignedProperties|xades:SignedSignatureProperties|xades:SigningCertificateV2|xades:Cert|xades:CertDigest|ds:DigestValue','TO BE GENERATED BY CHILKAT');
  object1.UpdateChildContent('xades:SignedProperties|xades:SignedSignatureProperties|xades:SigningCertificateV2|xades:Cert|xades:IssuerSerialV2','TO BE GENERATED BY CHILKAT');
  object1.UpdateAttrAt('xades:SignedProperties|xades:SignedDataObjectProperties|xades:DataObjectFormat',True,'ObjectReference','#r-id-1');
  object1.UpdateChildContent('xades:SignedProperties|xades:SignedDataObjectProperties|xades:DataObjectFormat|xades:MimeType','text/xml');

  gen.AddObject('',object1.GetXml(),'','');

  //  -------- Reference 1 --------
  //  Build the following Transforms fragment:

  //  <ds:Transforms>
  //      <ds:Transform Algorithm="http://www.w3.org/TR/1999/REC-xpath-19991116">
  //          <ds:XPath>not(ancestor-or-self::ds:Signature)</ds:XPath>
  //      </ds:Transform>
  //      <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
  //  </ds:Transforms>

  xml1 := TXml.Create;
  xml1.Tag := 'ds:Transforms';
  xml1.UpdateAttrAt('ds:Transform',True,'Algorithm','http://www.w3.org/TR/1999/REC-xpath-19991116');
  xml1.UpdateChildContent('ds:Transform|ds:XPath','not(ancestor-or-self::ds:Signature)');
  xml1.UpdateAttrAt('ds:Transform[1]',True,'Algorithm','http://www.w3.org/2001/10/xml-exc-c14n#');

  gen.AddSameDocRef2('','sha256',xml1,'');
  gen.SetRefIdAttr('','r-id-1');

  //  -------- Reference 2 --------
  //  Build the following Transforms fragment:

  //  <ds:Transforms>
  //      <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
  //  </ds:Transforms>

  xml2 := TXml.Create;
  xml2.Tag := 'ds:Transforms';
  xml2.UpdateAttrAt('ds:Transform',True,'Algorithm','http://www.w3.org/2001/10/xml-exc-c14n#');

  gen.AddObjectRef2('xades-id-1234','sha256',xml2,'http://uri.etsi.org/01903#SignedProperties');

  //  Provide a certificate + private key. (PFX password is test123)
  cert := TCert.Create;
  success := cert.LoadPfxFile('qa_data/pfx/cert_test123.pfx','test123');
  if (success <> True) then
    begin
      WriteLn(cert.LastErrorText);
      Exit;
    end;
  gen.SetX509Cert(cert,True);

  gen.KeyInfoType := 'X509Data';
  gen.X509Type := 'Certificate';

  //  Load XML to be signed...
  sbXml := TStringBuilder.Create;
  xmlToSign.GetXmlSb(sbXml);

  gen.Behaviors := 'IndentedSignature';

  //  Sign the XML...
  success := gen.CreateXmlDSigSb(sbXml);
  if (success <> True) then
    begin
      WriteLn(gen.LastErrorText);
      Exit;
    end;
  //  -----------------------------------------------

  //  Save the signed XML to a file.
  success := sbXml.WriteFile('qa_output/signedXml.xml','utf-8',False);

  WriteLn(sbXml.GetAsString());

  //  ----------------------------------------
  //  Verify the signatures we just produced...
  verifier := TXmlDSig.Create;
  success := verifier.LoadSignatureSb(sbXml);
  if (success <> True) then
    begin
      WriteLn(verifier.LastErrorText);
      Exit;
    end;

  numSigs := verifier.NumSignatures;
  verifyIdx := 0;
  while verifyIdx < numSigs do
    begin
      verifier.Selector := verifyIdx;
      verified := verifier.VerifySignature(True);
      if (verified <> True) then
        begin
          WriteLn(verifier.LastErrorText);
          Exit;
        end;
      verifyIdx := verifyIdx + 1;
    end;

  WriteLn('All signatures were successfully verified.');


  xmlToSign.Free;
  gen.Free;
  object1.Free;
  xml1.Free;
  xml2.Free;
  cert.Free;
  sbXml.Free;
  verifier.Free;

end;

// ---------------------------------------------------------------------------

begin

  try
    RunDemo;
  except
    on E: Exception do
      WriteLn('Unhandled exception: ', E.ClassName, ': ', E.Message);
  end;

  WriteLn;
  {$IFDEF MSWINDOWS}
  WriteLn('Press Enter to exit...');
  ReadLn;
  {$ENDIF}
end.