Delphi DLL
Delphi DLL
Create XAdES for Malaysia E-Invoice
See more Malaysia MyInvois Examples
This example signs XML to create the required XAdES for Malaysia E-Invoice as described at the following web pages:
https://sdk.myinvois.hasil.gov.my/signature/
https://sdk.myinvois.hasil.gov.my/signature-creation/
Note: This example requires Chilkat 10.0.0 or later.
Chilkat Delphi DLL Downloads
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, StringBuilder, XmlDSigGen, Xml, XmlDSig, Cert;
...
procedure TForm1.Button1Click(Sender: TObject);
var
success: Boolean;
xmlToSign: HCkXml;
gen: HCkXmlDSigGen;
object1: HCkXml;
xml1: HCkXml;
cert: HCkCert;
sbXml: HCkStringBuilder;
verifier: HCkXmlDSig;
numSigs: Integer;
verifyIdx: Integer;
verified: Boolean;
begin
success := False;
success := True;
// Load XML such as the following to be signed:
// <?xml version="1.0" encoding="utf-8"?>
// <Invoice xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2" xmlns:cac="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2" xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2" xmlns:ext="urn:oasis:names:specification:ubl:schema:xsd:CommonExtensionComponents-2">
// <ext:UBLExtensions>
// <ext:UBLExtension>
// <ext:ExtensionURI>urn:oasis:names:specification:ubl:dsig:enveloped:xades</ext:ExtensionURI>
// <ext:ExtensionContent>
// <sig:UBLDocumentSignatures xmlns:sac="urn:oasis:names:specification:ubl:schema:xsd:SignatureAggregateComponents-2" xmlns:sbc="urn:oasis:names:specification:ubl:schema:xsd:SignatureBasicComponents-2" xmlns:sig="urn:oasis:names:specification:ubl:schema:xsd:CommonSignatureComponents-2">
// <sac:SignatureInformation>
// <cbc:ID>urn:oasis:names:specification:ubl:signature:1</cbc:ID>
// <sbc:ReferencedSignatureID>urn:oasis:names:specification:ubl:signature:Invoice</sbc:ReferencedSignatureID>
// </sac:SignatureInformation>
// </sig:UBLDocumentSignatures>
// </ext:ExtensionContent>
// </ext:UBLExtension>
// </ext:UBLExtensions>
// <cbc:ID>IV00000105</cbc:ID>
// <cbc:IssueDate>2024-07-06</cbc:IssueDate>
// <cbc:IssueTime>15:06:00Z</cbc:IssueTime>
// <cbc:InvoiceTypeCode listVersionID="1.1">1</cbc:InvoiceTypeCode>
// <cbc:DocumentCurrencyCode>MYR</cbc:DocumentCurrencyCode>
// <cac:BillingReference>
// <cac:AdditionalDocumentReference>
// <cbc:ID>IV00000105</cbc:ID>
// </cac:AdditionalDocumentReference>
// </cac:BillingReference>
// <cac:Signature>
// <cbc:ID>urn:oasis:names:specification:ubl:signature:Invoice</cbc:ID>
// <cbc:SignatureMethod>urn:oasis:names:specification:ubl:dsig:enveloped:xades</cbc:SignatureMethod>
// </cac:Signature>
// <cac:AccountingSupplierParty>
// <cac:Party>
// <cbc:IndustryClassificationCode name="Other information technology service activities n.e.c.">62099</cbc:IndustryClassificationCode>
// <cac:PartyIdentification>
// <cbc:ID schemeID="TIN">C99999999999</cbc:ID>
// </cac:PartyIdentification>
// <cac:PartyIdentification>
// <cbc:ID schemeID="BRN">200801099999</cbc:ID>
// </cac:PartyIdentification>
// <cac:PostalAddress>
// <cbc:CityName>Cheras</cbc:CityName>
// <cbc:PostalZone>56000</cbc:PostalZone>
// <cbc:CountrySubentityCode>14</cbc:CountrySubentityCode>
// <cac:AddressLine>
// <cbc:Line>A-3, 11, Jalan 2/199a,</cbc:Line>
// </cac:AddressLine>
// <cac:AddressLine>
// <cbc:Line>Cheras, 56000 Cheras</cbc:Line>
// </cac:AddressLine>
// <cac:AddressLine>
// <cbc:Line>Wilayah Persekutuan Kuala Lumpur</cbc:Line>
// </cac:AddressLine>
// <cac:Country>
// <cbc:IdentificationCode listAgencyID="6" listID="ISO3166-1">MYS</cbc:IdentificationCode>
// </cac:Country>
// </cac:PostalAddress>
// <cac:PartyLegalEntity>
// <cbc:RegistrationName>ABC SYSTEMS SERVICES</cbc:RegistrationName>
// </cac:PartyLegalEntity>
// <cac:Contact>
// <cbc:Telephone>019-626 9999</cbc:Telephone>
// <cbc:ElectronicMail>Info@abc.com.my</cbc:ElectronicMail>
// </cac:Contact>
// </cac:Party>
// </cac:AccountingSupplierParty>
// <cac:AccountingCustomerParty>
// <cac:Party>
// <cac:PartyIdentification>
// <cbc:ID schemeID="TIN">C99999999999</cbc:ID>
// </cac:PartyIdentification>
// <cac:PartyIdentification>
// <cbc:ID schemeID="BRN">200801099999</cbc:ID>
// </cac:PartyIdentification>
// <cac:PostalAddress>
// <cbc:CityName>Cheras</cbc:CityName>
// <cbc:PostalZone>56000</cbc:PostalZone>
// <cbc:CountrySubentityCode>14</cbc:CountrySubentityCode>
// <cac:AddressLine>
// <cbc:Line>A-3, 11, Jalan 2/199a,</cbc:Line>
// </cac:AddressLine>
// <cac:AddressLine>
// <cbc:Line>Cheras, 56000 Cheras</cbc:Line>
// </cac:AddressLine>
// <cac:AddressLine>
// <cbc:Line>Cheras, 56000 Cheras</cbc:Line>
// </cac:AddressLine>
// <cac:Country>
// <cbc:IdentificationCode listAgencyID="6" listID="ISO3166-1">MYS</cbc:IdentificationCode>
// </cac:Country>
// </cac:PostalAddress>
// <cac:PartyLegalEntity>
// <cbc:RegistrationName>ABC SYSTEMS SERVICES</cbc:RegistrationName>
// </cac:PartyLegalEntity>
// <cac:Contact>
// <cbc:Telephone>019-626 9999</cbc:Telephone>
// <cbc:ElectronicMail>Info@abc.com.my</cbc:ElectronicMail>
// </cac:Contact>
// </cac:Party>
// </cac:AccountingCustomerParty>
// <cac:TaxTotal>
// <cbc:TaxAmount currencyID="MYR">0</cbc:TaxAmount>
// <cac:TaxSubtotal>
// <cbc:TaxableAmount currencyID="MYR">40</cbc:TaxableAmount>
// <cbc:TaxAmount currencyID="MYR">0</cbc:TaxAmount>
// <cac:TaxCategory>
// <cbc:ID>6</cbc:ID>
// <cbc:Percent>0</cbc:Percent>
// <cac:TaxScheme>
// <cbc:ID schemeAgencyID="6" schemeID="UN/ECE 5153">OTH</cbc:ID>
// </cac:TaxScheme>
// </cac:TaxCategory>
// </cac:TaxSubtotal>
// </cac:TaxTotal>
// <cac:LegalMonetaryTotal>
// <cbc:LineExtensionAmount currencyID="MYR">40</cbc:LineExtensionAmount>
// <cbc:TaxExclusiveAmount currencyID="MYR">40</cbc:TaxExclusiveAmount>
// <cbc:TaxInclusiveAmount currencyID="MYR">49</cbc:TaxInclusiveAmount>
// <cbc:AllowanceTotalAmount currencyID="MYR">0</cbc:AllowanceTotalAmount>
// <cbc:PayableAmount currencyID="MYR">49</cbc:PayableAmount>
// </cac:LegalMonetaryTotal>
// <cac:InvoiceLine>
// <cbc:ID>1</cbc:ID>
// <cbc:InvoicedQuantity unitCode="H87">2</cbc:InvoicedQuantity>
// <cbc:LineExtensionAmount currencyID="MYR">40</cbc:LineExtensionAmount>
// <cac:TaxTotal>
// <cbc:TaxAmount currencyID="MYR">0</cbc:TaxAmount>
// <cac:TaxSubtotal>
// <cbc:TaxableAmount currencyID="MYR">40</cbc:TaxableAmount>
// <cbc:TaxAmount currencyID="MYR">0</cbc:TaxAmount>
// <cac:TaxCategory>
// <cbc:ID>6</cbc:ID>
// <cbc:Percent>0</cbc:Percent>
// <cac:TaxScheme>
// <cbc:ID schemeAgencyID="6" schemeID="UN/ECE 5153">OTH</cbc:ID>
// </cac:TaxScheme>
// </cac:TaxCategory>
// </cac:TaxSubtotal>
// </cac:TaxTotal>
// <cac:Item>
// <cbc:Description>Computer Monitor 24 inch</cbc:Description>
// <cac:CommodityClassification>
// <cbc:ItemClassificationCode listID="CLASS">3</cbc:ItemClassificationCode>
// </cac:CommodityClassification>
// </cac:Item>
// <cac:Price>
// <cbc:PriceAmount currencyID="MYR">20</cbc:PriceAmount>
// </cac:Price>
// <cac:ItemPriceExtension>
// <cbc:Amount currencyID="MYR">40</cbc:Amount>
// </cac:ItemPriceExtension>
// </cac:InvoiceLine>
// </Invoice>
xmlToSign := CkXml_Create();
success := CkXml_LoadXmlFile(xmlToSign,'qa_data/xml/myinvois_notYetSigned.xml');
if (success = False) then
begin
Memo1.Lines.Add('Failed to load the XML to be signed.');
Exit;
end;
gen := CkXmlDSigGen_Create();
// ------------------------------------------------------------------------------------------------
// This behavior is required for the peculiarities of the MyInvois implementation.
// It requires Chilkat 10.0.0 or later.
CkXmlDSigGen_putBehaviors(gen,'MyInvois');
// ------------------------------------------------------------------------------------------------
CkXmlDSigGen_putSigLocation(gen,'Invoice|ext:UBLExtensions|ext:UBLExtension|ext:ExtensionContent|sig:UBLDocumentSignatures|sac:SignatureInformation');
CkXmlDSigGen_putSigLocationMod(gen,0);
CkXmlDSigGen_putSigId(gen,'signature');
CkXmlDSigGen_putSigNamespacePrefix(gen,'ds');
CkXmlDSigGen_putSigNamespaceUri(gen,'http://www.w3.org/2000/09/xmldsig#');
CkXmlDSigGen_putSignedInfoCanonAlg(gen,'C14N_11');
CkXmlDSigGen_putSignedInfoDigestMethod(gen,'sha256');
// Create an Object to be added to the Signature.
object1 := CkXml_Create();
CkXml_putTag(object1,'xades:QualifyingProperties');
CkXml_AddAttribute(object1,'xmlns:xades','http://uri.etsi.org/01903/v1.3.2#');
CkXml_AddAttribute(object1,'Target','signature');
CkXml_UpdateAttrAt(object1,'xades:SignedProperties',True,'Id','id-xades-signed-props');
CkXml_UpdateChildContent(object1,'xades:SignedProperties|xades:SignedSignatureProperties|xades:SigningTime','TO BE GENERATED BY CHILKAT');
CkXml_UpdateAttrAt(object1,'xades:SignedProperties|xades:SignedSignatureProperties|xades:SigningCertificate|xades:Cert|xades:CertDigest|ds:DigestMethod',True,'Algorithm','http://www.w3.org/2001/04/xmlenc#sha256');
CkXml_UpdateChildContent(object1,'xades:SignedProperties|xades:SignedSignatureProperties|xades:SigningCertificate|xades:Cert|xades:CertDigest|ds:DigestValue','TO BE GENERATED BY CHILKAT');
CkXml_UpdateChildContent(object1,'xades:SignedProperties|xades:SignedSignatureProperties|xades:SigningCertificate|xades:Cert|xades:IssuerSerial|ds:X509IssuerName','TO BE GENERATED BY CHILKAT');
CkXml_UpdateChildContent(object1,'xades:SignedProperties|xades:SignedSignatureProperties|xades:SigningCertificate|xades:Cert|xades:IssuerSerial|ds:X509SerialNumber','TO BE GENERATED BY CHILKAT');
CkXmlDSigGen_AddObject(gen,'',CkXml__getXml(object1),'','');
// -------- Reference 1 --------
xml1 := CkXml_Create();
CkXml_putTag(xml1,'ds:Transforms');
CkXml_UpdateAttrAt(xml1,'ds:Transform',True,'Algorithm','http://www.w3.org/TR/1999/REC-xpath-19991116');
CkXml_UpdateChildContent(xml1,'ds:Transform|ds:XPath','not(//ancestor-or-self::ext:UBLExtensions)');
CkXml_UpdateAttrAt(xml1,'ds:Transform[1]',True,'Algorithm','http://www.w3.org/TR/1999/REC-xpath-19991116');
CkXml_UpdateChildContent(xml1,'ds:Transform[1]|ds:XPath','not(//ancestor-or-self::cac:Signature)');
CkXml_UpdateAttrAt(xml1,'ds:Transform[2]',True,'Algorithm','http://www.w3.org/2006/12/xml-c14n11');
CkXmlDSigGen_AddSameDocRef2(gen,'','sha256',xml1,'');
CkXmlDSigGen_SetRefIdAttr(gen,'','id-doc-signed-data');
// -------- Reference 2 --------
CkXmlDSigGen_AddObjectRef(gen,'id-xades-signed-props','sha256','','','http://www.w3.org/2000/09/xmldsig#SignatureProperties');
// Provide a certificate + private key. (PFX password is test123)
cert := CkCert_Create();
success := CkCert_LoadPfxFile(cert,'qa_data/pfx/cert_test123.pfx','test123');
if (success <> True) then
begin
Memo1.Lines.Add(CkCert__lastErrorText(cert));
Exit;
end;
CkXmlDSigGen_SetX509Cert(gen,cert,True);
CkXmlDSigGen_putKeyInfoType(gen,'X509Data');
CkXmlDSigGen_putX509Type(gen,'Certificate');
// Load XML to be signed...
sbXml := CkStringBuilder_Create();
CkXml_putEmitCompact(xmlToSign,True);
CkXml_GetXmlSb(xmlToSign,sbXml);
// Sign the XML...
success := CkXmlDSigGen_CreateXmlDSigSb(gen,sbXml);
if (success <> True) then
begin
Memo1.Lines.Add(CkXmlDSigGen__lastErrorText(gen));
Exit;
end;
// -----------------------------------------------
// Save the signed XML to a file.
success := CkStringBuilder_WriteFile(sbXml,'c:/temp/qa_output/signedXml.xml','utf-8',False);
Memo1.Lines.Add(CkStringBuilder__getAsString(sbXml));
// ----------------------------------------
// Verify the signatures we just produced...
verifier := CkXmlDSig_Create();
success := CkXmlDSig_LoadSignatureSb(verifier,sbXml);
if (success <> True) then
begin
Memo1.Lines.Add(CkXmlDSig__lastErrorText(verifier));
Exit;
end;
// ----------------------------------------
// Make sure to indicate that we are verifying a MyInvois signature
// This is because MyInvois does things in a non-standard way..
CkXmlDSig_putUncommonOptions(verifier,'MyInvois');
// ----------------------------------------
numSigs := CkXmlDSig_getNumSignatures(verifier);
verifyIdx := 0;
while verifyIdx < numSigs do
begin
CkXmlDSig_putSelector(verifier,verifyIdx);
verified := CkXmlDSig_VerifySignature(verifier,True);
if (verified <> True) then
begin
Memo1.Lines.Add(CkXmlDSig__lastErrorText(verifier));
Exit;
end;
verifyIdx := verifyIdx + 1;
end;
Memo1.Lines.Add('All signatures were successfully verified.');
CkXml_Dispose(xmlToSign);
CkXmlDSigGen_Dispose(gen);
CkXml_Dispose(object1);
CkXml_Dispose(xml1);
CkCert_Dispose(cert);
CkStringBuilder_Dispose(sbXml);
CkXmlDSig_Dispose(verifier);
end;