Sample code for 30+ languages & platforms
Tcl Requires Chilkat v11.0.0+

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 Tcl Downloads

Tcl

load ./chilkat.dll

set success 0

#  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>

set xml [new_CkXml]

CkXml_put_Tag $xml "InitUpload"
CkXml_AddAttribute $xml "xmlns" "http://e-dokumenty.mf.gov.pl"
CkXml_UpdateChildContent $xml "DocumentType" "JPK"
CkXml_UpdateChildContent $xml "Version" "01.02.01.20160617"
CkXml_UpdateAttrAt $xml "EncryptionKey" 1 "algorithm" "RSA"
CkXml_UpdateAttrAt $xml "EncryptionKey" 1 "encoding" "Base64"
CkXml_UpdateAttrAt $xml "EncryptionKey" 1 "mode" "ECB"
CkXml_UpdateAttrAt $xml "EncryptionKey" 1 "padding" "PKCS#1"
CkXml_UpdateChildContent $xml "EncryptionKey" "TO BE DETERMINED"
CkXml_UpdateAttrAt $xml "DocumentList|Document|FormCode" 1 "schemaVersion" "1-1"
CkXml_UpdateAttrAt $xml "DocumentList|Document|FormCode" 1 "systemCode" "JPK_VAT (3)"
CkXml_UpdateChildContent $xml "DocumentList|Document|FormCode" "JPK_VAT"
CkXml_UpdateChildContent $xml "DocumentList|Document|FileName" "JPK_VAT_3_v1-1_20181201.xml"
CkXml_UpdateChildContent $xml "DocumentList|Document|ContentLength" "9999"
CkXml_UpdateAttrAt $xml "DocumentList|Document|HashValue" 1 "algorithm" "SHA-256"
CkXml_UpdateAttrAt $xml "DocumentList|Document|HashValue" 1 "encoding" "Base64"
CkXml_UpdateChildContent $xml "DocumentList|Document|HashValue" "TO BE DETERMINED"
CkXml_UpdateAttrAt $xml "DocumentList|Document|FileSignatureList" 1 "filesNumber" "1"
CkXml_UpdateAttrAt $xml "DocumentList|Document|FileSignatureList|Packaging|SplitZip" 1 "mode" "zip"
CkXml_UpdateAttrAt $xml "DocumentList|Document|FileSignatureList|Packaging|SplitZip" 1 "type" "split"
CkXml_UpdateAttrAt $xml "DocumentList|Document|FileSignatureList|Encryption|AES" 1 "block" "16"
CkXml_UpdateAttrAt $xml "DocumentList|Document|FileSignatureList|Encryption|AES" 1 "mode" "CBC"
CkXml_UpdateAttrAt $xml "DocumentList|Document|FileSignatureList|Encryption|AES" 1 "padding" "PKCS#7"
CkXml_UpdateAttrAt $xml "DocumentList|Document|FileSignatureList|Encryption|AES" 1 "size" "256"
CkXml_UpdateAttrAt $xml "DocumentList|Document|FileSignatureList|Encryption|AES|IV" 1 "bytes" "16"
CkXml_UpdateAttrAt $xml "DocumentList|Document|FileSignatureList|Encryption|AES|IV" 1 "encoding" "Base64"
CkXml_UpdateChildContent $xml "DocumentList|Document|FileSignatureList|Encryption|AES|IV" "TO BE DETERMINED"
CkXml_UpdateChildContent $xml "DocumentList|Document|FileSignatureList|FileSignature|OrdinalNumber" "1"
CkXml_UpdateChildContent $xml "DocumentList|Document|FileSignatureList|FileSignature|FileName" "JPK_VAT_3_v1-1_20181201-000.xml.zip.aes"
CkXml_UpdateChildContent $xml "DocumentList|Document|FileSignatureList|FileSignature|ContentLength" "9999"
CkXml_UpdateAttrAt $xml "DocumentList|Document|FileSignatureList|FileSignature|HashValue" 1 "algorithm" "MD5"
CkXml_UpdateAttrAt $xml "DocumentList|Document|FileSignatureList|FileSignature|HashValue" 1 "encoding" "Base64"
CkXml_UpdateChildContent $xml "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
set bdXml [new_CkBinData]

set success [CkBinData_LoadFile $bdXml "qa_data/xml_dsig/jpk_vat/JPK_VAT_3_v1-1_20181201-000.xml"]
if {$success != 1} then {
    puts "Failed to load XML file."
    delete_CkXml $xml
    delete_CkBinData $bdXml
    exit
}

CkXml_UpdateChildContentInt $xml "DocumentList|Document|ContentLength" [CkBinData_get_NumBytes $bdXml]

set crypt [new_CkCrypt2]

CkCrypt2_put_HashAlgorithm $crypt "sha256"
CkCrypt2_put_EncodingMode $crypt "base64"
CkXml_UpdateChildContent $xml "DocumentList|Document|HashValue" [CkCrypt2_hashBdENC $crypt $bdXml]

#  ------------------------------------------------------------
#  Step 2: Create a Zip archive containing the XML.
set zip [new_CkZip]

#  The filename we pass here doesn't matter because we won't actually be creating a .zip file.
CkZip_NewZip $zip "anything.zip"
CkZip_AddBd $zip "JPK_VAT_3_v1-1_20181201-000.xml" $bdXml
#  Write the .zip file to a BinData object.
set bdZip [new_CkBinData]

CkZip_WriteBd $zip $bdZip

#  ------------------------------------------------------------
#  Step 3: Generate a random 256-bit AES key (32-bytes)
set prng [new_CkPrng]

set bdAesKey [new_CkBinData]

CkPrng_GenRandomBd $prng 32 $bdAesKey
set ivBytes [CkPrng_genRandom $prng 16 "base64"]

#  Store the IV (base64 string) in the XML.
CkXml_UpdateChildContent $xml "DocumentList|Document|FileSignatureList|Encryption|AES|IV" $ivBytes

#  ------------------------------------------------------------
#  Step 4: AES encrypt our zip archive (the contents of bdZip)
CkCrypt2_put_CipherMode $crypt "cbc"
CkCrypt2_put_KeyLength $crypt 256
CkCrypt2_put_CryptAlgorithm $crypt "aes"
CkCrypt2_put_PaddingScheme $crypt 0
CkCrypt2_SetEncodedIV $crypt $ivBytes "base64"
CkCrypt2_SetEncodedKey $crypt [CkBinData_getEncoded $bdAesKey "base64"] "base64"
#  AES by definition has a block size of 16.
CkCrypt2_EncryptBd $crypt $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
set success [CkBinData_WriteFile $bdZip "qa_output/JPK_VAT_3_v1-1_20181201-000.xml.zip.aes"]
CkXml_UpdateChildContentInt $xml "DocumentList|Document|FileSignatureList|FileSignature|ContentLength" [CkBinData_get_NumBytes $bdZip]

#  ------------------------------------------------------------
#  Step 4: RSA Encrypt the AES key using the public key certificate provided by the Ministry of Finance
set cert [new_CkCert]

set success [CkCert_LoadFromFile $cert "qa_data/pem/mf_public_rsa.pem"]
if {$success == 0} then {
    puts [CkCert_lastErrorText $cert]
    delete_CkXml $xml
    delete_CkBinData $bdXml
    delete_CkCrypt2 $crypt
    delete_CkZip $zip
    delete_CkBinData $bdZip
    delete_CkPrng $prng
    delete_CkBinData $bdAesKey
    delete_CkCert $cert
    exit
}

set pubKey [new_CkPublicKey]

CkCert_GetPublicKey $cert $pubKey

set rsa [new_CkRsa]

CkRsa_UsePublicKey $rsa $pubKey

CkRsa_put_EncodingMode $rsa "base64"
CkRsa_put_LittleEndian $rsa 0
#  in-place RSA encrypt the contents of bdAesKey.
CkRsa_EncryptBd $rsa $bdAesKey 0
CkXml_UpdateChildContent $xml "EncryptionKey" [CkBinData_getEncoded $bdAesKey "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..)
CkCrypt2_put_HashAlgorithm $crypt "md5"
CkXml_UpdateChildContent $xml "DocumentList|Document|FileSignatureList|FileSignature|HashValue" [CkCrypt2_hashBdENC $crypt $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).
set finalXml [CkXml_getXml $xml]
puts "$finalXml"

CkXml_SaveXml $xml "qa_output/jpk_vat.xml"

puts "Finished."

delete_CkXml $xml
delete_CkBinData $bdXml
delete_CkCrypt2 $crypt
delete_CkZip $zip
delete_CkBinData $bdZip
delete_CkPrng $prng
delete_CkBinData $bdAesKey
delete_CkCert $cert
delete_CkPublicKey $pubKey
delete_CkRsa $rsa