Sample code for 30+ languages & platforms
Pascal (Lazarus/Delphi)

Create JPK VAT metadata XML

See more RSA Examples

Demonstrates how to create the JPK VAT metadata XML (InitUpload) that will be signed using XADES.

Chilkat Pascal (Lazarus/Delphi) Downloads

Pascal (Lazarus/Delphi)
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.PublicKey,
  Chilkat.Zip,
  Chilkat.Prng,
  Chilkat.BinData,
  Chilkat.Rsa,
  Chilkat.Cert,
  Chilkat.Xml,
  Chilkat.Crypt2;

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

procedure RunDemo;
var
  success: Boolean;
  xml: TXml;
  bdXml: TBinData;
  crypt: TCrypt2;
  zip: TZip;
  bdZip: TBinData;
  prng: TPrng;
  bdAesKey: TBinData;
  ivBytes: string;
  cert: TCert;
  pubKey: TPublicKey;
  rsa: TRsa;
  finalXml: string;

begin
  success := False;

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

  //  First build an InitUpload XML template

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

  //  <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">F9EhKFec...uWqAWUIg==</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">JFDI1pItwh6dj/Xe1uts/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">5NX0q1935fvMjLFV7E1yDw==</HashValue>
  //                  </FileSignature>
  //              </FileSignatureList>
  //          </Document>
  //      </DocumentList>
  //  </InitUpload>

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

  //  ------------------------------------------------------------
  //  Step 1: Load our JPK_VAT XML and update the DocumentList|Document|HashValue
  //  and DocumentList|Document|ContentLength
  bdXml := TBinData.Create;
  success := bdXml.LoadFile('qa_data/xml_dsig/jpk_vat/JPK_VAT_3_v1-1_20181201-000.xml');
  if (success <> True) then
    begin
      WriteLn('Failed to load XML file.');
      Exit;
    end;

  xml.UpdateChildContentInt('DocumentList|Document|ContentLength',bdXml.NumBytes);

  crypt := TCrypt2.Create;
  crypt.HashAlgorithm := 'sha256';
  crypt.EncodingMode := 'base64';
  xml.UpdateChildContent('DocumentList|Document|HashValue',crypt.HashBdENC(bdXml));

  //  ------------------------------------------------------------
  //  Step 2: Create a Zip archive containing the XML.
  zip := TZip.Create;
  //  The filename we pass here doesn't matter because we won't actually be creating a .zip file.
  zip.NewZip('anything.zip');
  zip.AddBd('JPK_VAT_3_v1-1_20181201-000.xml',bdXml);
  //  Write the .zip file to a BinData object.
  bdZip := TBinData.Create;
  zip.WriteBd(bdZip);

  //  ------------------------------------------------------------
  //  Step 3: Generate a random 256-bit AES key (32-bytes)
  prng := TPrng.Create;
  bdAesKey := TBinData.Create;
  prng.GenRandomBd(32,bdAesKey);
  ivBytes := prng.GenRandom(16,'base64');

  //  Store the IV (base64 string) in the XML.
  xml.UpdateChildContent('DocumentList|Document|FileSignatureList|Encryption|AES|IV',ivBytes);

  //  ------------------------------------------------------------
  //  Step 4: AES encrypt our zip archive (the contents of bdZip)
  crypt.CipherMode := 'cbc';
  crypt.KeyLength := 256;
  crypt.CryptAlgorithm := 'aes';
  crypt.PaddingScheme := 0;
  crypt.SetEncodedIV(ivBytes,'base64');
  crypt.SetEncodedKey(bdAesKey.GetEncoded('base64'),'base64');
  //  AES by definition has a block size of 16.
  crypt.EncryptBd(bdZip);

  //  bdZip now contains the AES encrypted data. 
  //  Note: This is NOT the same as a zip where the contents are AES encrypted.
  //  In that case, we have an unencrypted zip structure with AES encrypted files within.
  //  In our case, the entire zip file image is encrypted.

  //  Save the bdZip to a file.  This is what will get sent to e-dokumenty.mf.gov.pl
  success := bdZip.WriteFile('qa_output/JPK_VAT_3_v1-1_20181201-000.xml.zip.aes');
  xml.UpdateChildContentInt('DocumentList|Document|FileSignatureList|FileSignature|ContentLength',bdZip.NumBytes);

  //  ------------------------------------------------------------
  //  Step 4: RSA Encrypt the AES key using the public key certificate provided by the Ministry of Finance
  cert := TCert.Create;
  success := cert.LoadFromFile('qa_data/pem/mf_public_rsa.pem');
  if (success = False) then
    begin
      WriteLn(cert.LastErrorText);
      Exit;
    end;

  pubKey := TPublicKey.Create;
  cert.GetPublicKey(pubKey);

  rsa := TRsa.Create;
  rsa.UsePublicKey(pubKey);

  rsa.EncodingMode := 'base64';
  rsa.LittleEndian := False;
  //  in-place RSA encrypt the contents of bdAesKey.
  rsa.EncryptBd(bdAesKey,False);
  xml.UpdateChildContent('EncryptionKey',bdAesKey.GetEncoded('base64'));

  //  Step 5: We forgot to get the MD5 hash of the AES encrypted zip.
  //  (I'm assuming we need the MD5 of the encrypted zip as opposed to the MD5 of the pre-encrypted zip..)
  crypt.HashAlgorithm := 'md5';
  xml.UpdateChildContent('DocumentList|Document|FileSignatureList|FileSignature|HashValue',crypt.HashBdENC(bdZip));

  //  At this point, the XML is prepared and the AES encrypted image of the zip file is written
  //  to a file (and also in bdZip).
  finalXml := xml.GetXml();
  WriteLn(finalXml);

  xml.SaveXml('qa_output/jpk_vat.xml');

  WriteLn('Finished.');


  xml.Free;
  bdXml.Free;
  crypt.Free;
  zip.Free;
  bdZip.Free;
  prng.Free;
  bdAesKey.Free;
  cert.Free;
  pubKey.Free;
  rsa.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.