Sample code for 30+ languages & platforms
Lazarus Pascal

XML-DSig Add Reference with Transforms Specified Explicitly

Demonstrates how to use the new AddSameDocRef2 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;
  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;

  //  Create the following XML to be signed:

  //  <doc>
  //      <s id="s1">Some text...</s>
  //      <p>Some text...</p>
  //      <p class="note">A note...</p>
  //  </doc>

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

  xmlToSign := TXml.Create;
  xmlToSign.Tag := 'doc';
  xmlToSign.UpdateAttrAt('s',True,'id','s1');
  xmlToSign.UpdateChildContent('s','Some text...');
  xmlToSign.UpdateChildContent('p','Some text...');
  xmlToSign.UpdateAttrAt('p[1]',True,'class','note');
  xmlToSign.UpdateChildContent('p[1]','A note...');

  WriteLn(xmlToSign.GetXml());

  gen := TXmlDSigGen.Create;

  gen.SigLocation := 'doc';
  gen.SigLocationMod := 0;
  gen.SigId := 'Signature-78f29839-06af-448f-b479-ca46457fab1b-Signature';
  gen.SigNamespacePrefix := 'ds';
  gen.SigNamespaceUri := 'http://www.w3.org/2000/09/xmldsig#';
  gen.SigValueId := 'Signature-78f29839-06af-448f-b479-ca46457fab1b-SignatureValue';
  gen.SignedInfoCanonAlg := 'C14N';
  gen.SignedInfoDigestMethod := 'sha1';

  //  Set the KeyInfoId before adding references..
  gen.KeyInfoId := 'Signature-78f29839-06af-448f-b479-ca46457fab1b-KeyInfo';

  //  The following XML to be added as an Object to the Signature

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

  //  <xades:QualifyingProperties Id="Signature-78f29839-06af-448f-b479-ca46457fab1b-QualifyingProperties" Target="#Signature-78f29839-06af-448f-b479-ca46457fab1b-Signature" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:xades="http://uri.etsi.org/01903/v1.3.2#">
  //      <xades:SignedProperties Id="Signature-78f29839-06af-448f-b479-ca46457fab1b-SignedProperties">
  //          <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="#Reference-24eb6003-d41c-442c-a731-d4c58f94790b">
  //                  <xades:Description/>
  //                  <xades:ObjectIdentifier>
  //                      <xades:Identifier Qualifier="OIDAsURN">urn:oid:1.2.840.10003.5.109.10</xades:Identifier>
  //                      <xades:Description/>
  //                  </xades:ObjectIdentifier>
  //                  <xades:MimeType>text/xml</xades:MimeType>
  //                  <xades:Encoding/>
  //              </xades:DataObjectFormat>
  //          </xades:SignedDataObjectProperties>
  //      </xades:SignedProperties>
  //  </xades:QualifyingProperties>

  object1 := TXml.Create;
  object1.Tag := 'xades:QualifyingProperties';
  object1.AddAttribute('Id','Signature-78f29839-06af-448f-b479-ca46457fab1b-QualifyingProperties');
  object1.AddAttribute('Target','#Signature-78f29839-06af-448f-b479-ca46457fab1b-Signature');
  object1.AddAttribute('xmlns:ds','http://www.w3.org/2000/09/xmldsig#');
  object1.AddAttribute('xmlns:xades','http://uri.etsi.org/01903/v1.3.2#');
  object1.UpdateAttrAt('xades:SignedProperties',True,'Id','Signature-78f29839-06af-448f-b479-ca46457fab1b-SignedProperties');
  object1.UpdateChildContent('xades:SignedProperties|xades:SignedSignatureProperties|xades:SigningTime','TO BE GENERATED BY CHILKAT');
  //  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.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','#Reference-24eb6003-d41c-442c-a731-d4c58f94790b');
  object1.UpdateChildContent('xades:SignedProperties|xades:SignedDataObjectProperties|xades:DataObjectFormat|xades:Description','');
  object1.UpdateAttrAt('xades:SignedProperties|xades:SignedDataObjectProperties|xades:DataObjectFormat|xades:ObjectIdentifier|xades:Identifier',True,'Qualifier','OIDAsURN');
  object1.UpdateChildContent('xades:SignedProperties|xades:SignedDataObjectProperties|xades:DataObjectFormat|xades:ObjectIdentifier|xades:Identifier','urn:oid:1.2.840.10003.5.109.10');
  object1.UpdateChildContent('xades:SignedProperties|xades:SignedDataObjectProperties|xades:DataObjectFormat|xades:ObjectIdentifier|xades:Description','');
  object1.UpdateChildContent('xades:SignedProperties|xades:SignedDataObjectProperties|xades:DataObjectFormat|xades:MimeType','text/xml');
  object1.UpdateChildContent('xades:SignedProperties|xades:SignedDataObjectProperties|xades:DataObjectFormat|xades:Encoding','');

  WriteLn(object1.GetXml());

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

  //  -------- Reference 1 --------

  //  Create the following Transforms fragment:

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

  //  <ds:Transforms>
  //      <ds:Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
  //      <ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
  //      <ds:Transform Algorithm="http://www.w3.org/TR/1999/REC-xpath-19991116">
  //          <ds:XPath xmlns:ds="http://www.w3.org/2000/09/xmldsig#">not(ancestor-or-self::ds:Signature)</ds:XPath>
  //      </ds:Transform>
  //  </ds:Transforms>

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

  //  This is the "Transforms" XML fragment passed to AddSameDocRef2.
  WriteLn(xml1.GetXml());

  gen.AddSameDocRef2('','sha1',xml1,'');

  gen.SetRefIdAttr('','Reference-24eb6003-d41c-442c-a731-d4c58f94790b');

  //  -------- Reference 2 --------
  gen.AddObjectRef('Signature-78f29839-06af-448f-b479-ca46457fab1b-SignedProperties','sha1','','','http://uri.etsi.org/01903#SignedProperties');

  //  -------- Reference 3 --------
  gen.AddSameDocRef('Signature-78f29839-06af-448f-b479-ca46457fab1b-KeyInfo','sha1','','','');

  //  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+KeyValue';
  gen.X509Type := 'CertChain';

  //  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;
  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.