wes4m / zatca-xml-js

An implementation of Saudi Arabia ZATCA's E-Invoicing requirements, processes, and standards in TypeScript.
MIT License
67 stars 55 forks source link

Invalid signed properties hashing #44

Open tawwfik opened 10 months ago

tawwfik commented 10 months ago

Error Invalid signed properties hashing, Signed Properties with id=\'xadesSignedProperties\' during onboarding

fuadhasni commented 10 months ago

Can you please share context, what type of invoice, which environment and if possible generated xml

Also, steps you’ve taken to sign your invoice?

On Tue, Aug 22, 2023 at 2:52 AM, tawfik @.***> wrote:

Error Invalid signed properties hashing, Signed Properties with id='xadesSignedProperties' during onboarding

— Reply to this email directly, view it on GitHub https://github.com/wes4m/zatca-xml-js/issues/44, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABPK2CMNLEHLDPCBAGQCGWTXWPKAFANCNFSM6AAAAAA3Y7QC4Q . You are receiving this because you are subscribed to this thread.Message ID: @.***>

tawwfik commented 10 months ago

` $template = "

SET_SIGN_TIMESTAMP SET_CERTIFICATE_HASH SET_CERTIFICATE_ISSUER SET_CERTIFICATE_SERIAL_NUMBER "; $template = str_replace('SET_CERTIFICATE_HASH', $cert_info['hash'], $template); $template = str_replace('SET_SIGN_TIMESTAMP', $time, $template); $template = str_replace('SET_CERTIFICATE_SERIAL_NUMBER', $cert_info['serial_number'], $template); $template = str_replace('SET_CERTIFICATE_ISSUER', $cert_info['issuer'], $template); $hash=hash('sha256',$template); $signed_properties_hash = base64_encode($hash);` these properties invalid hash the template is invalid this response from zatca api `{ "type" => "ERROR", "code" => "signed-properties-hashing", "category" => "CERTIFICATE_ERRORS", "message" => "Invalid signed properties hashing, SignedProperties with id='xadesSignedProperties'", "status" => "ERROR" }`
fawadsaboor commented 7 months ago

@tawwfik Did you able to fix that issue?

ktnasar commented 7 months ago

i have same issue can anybody help on it

thaifanisalla commented 7 months ago

I have same issue in simulation mode

"errorMessages":[{"type":"ERROR","code":"GENERAL","category":"BUSINESS_RULES","message":"Unable to execute Business Rules validation ->BR-KSA-19","status":"ERROR"},{"type":"ERROR","code":"signed-properties-hashing","category":"CERTIFICATE_ERRORS","message":"Invalid signed properties hashing, SignedProperties with id='xadesSignedProperties'","status":"ERROR"}],"status":"ERROR"},"reportingStatus":"NOT_REPORTED","clearanceStatus":null,"qrSellertStatus":null,"qrBuyertStatus":null
tawwfik commented 7 months ago

Hi guys. I fixed the problem by register Namespace to Invoice Xml file. after that get the properties from invoice file. this code laravel php solve problem.

                   $xml = new DOMDocument("1.0", "utf-8");
                     $xml->loadXML($invoice)// invoice file after populate the properties;
                   //use domPath to register this namespace
                   $xpath = new DOMXPath($xml);
                   //  register  namespace
                   $xpath->registerNamespace('default-ns', "urn:oasis:names:specification:ubl:schema:xsd:Invoice-2");
                   $xpath->registerNamespace('sig', "urn:oasis:names:specification:ubl:schema:xsd:CommonSignatureComponents-                                      
                   2");
                   $xpath->registerNamespace('sac',                    
                   "urn:oasis:names:specification:ubl:schema:xsd:SignatureAggregateComponents-2");
                   $xpath->registerNamespace('sbc', "urn:oasis:names:specification:ubl:schema:xsd:SignatureBasicComponents-2");
                    $xpath->registerNamespace('ds', "http://www.w3.org/2000/09/xmldsig#");
                   $xpath->registerNamespace('xades', "http://uri.etsi.org/01903/v1.3.2#");
                   // path of SignedProperties
                   $SignedProperties = "//default-ns:Invoice/ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sig:UBLDocumentSignatures/sac:SignatureInformation/ds:Signature/ds:Object/xades:QualifyingProperties/xades:SignedProperties";
                   // get SignedProperties by path query
                   $SignedPropertiesValue = $xpath->query($SignedProperties);
                    // convert SignedProperties node to c14n standerd.
                   $canonicalizationInvoiceXML = $SignedPropertiesValue[0]->C14N(\true);
                   // replace tag to rquired in zatca.
                   $canonicalizationInvoiceXML = str_replace('></ds:DigestMethod>', '/>', $canonicalizationInvoiceXML);
                   // hash SignedProperties
                   $signed_properties_hash = base64_encode(hash('sha256', $canonicalizationInvoiceXML));
Yottskry commented 4 months ago

Sorry to resurrect an old thread, but I'm having the same issue. Could you show exactly what the SignedProperties section looks like just before you hash it, please? I'm still getting "Invalid signed properties hashing" every time.

ajaybalachandran commented 3 months ago

@Yottskry @fuadhasni @tawwfik @fawadsaboor @thaifanisalla

Issue 1: Inconsistency in E-Invoicing Statistics

After successfully integrating and following the official documentation provided by ZATCA, I have encountered an inconsistency in the e-invoicing statistics displayed on the Fatoora portal's simulation section. Despite receiving a "status":"PASS", "reportingStatus": "REPORTED" for submitted simplified invoices via the API endpoint "https://gw-fatoora.zatca.gov.sa/e-invoicing/simulation/invoices/reporting/single" and despite receiving a "status": "PASS", "clearanceStatus": "CLEARED" for standard invoices via "https://gw-fatoora.zatca.gov.sa/e-invoicing/simulation/invoices/clearance/single", the total count of submitted documents does not reflect accurately on the portal. This issue persists even after multiple checks over several days. Also, if I submitted and got a successful response from the API as the document is accepted and while checking on the Fatoora statistics page sometimes it is counted in rejected documents. Why is this mismatch happening? If I received REPORTED, CLEARED status with no error or warning messages, can I confirm that my document is submitted and accepted by ZATCA successfully? Please clarify this.

Issue 2: Lack of Clarity on Tax Amount Calculation

Additionally, I seek clarification on the calculation of the total tax amount payable to ZATCA for the submitted and accepted invoice documents. As there is no provision to retrieve this information from the ZATCA side, could you please confirm whether we need to calculate this amount internally based on the submitted invoices? For instance, if five invoices are successfully submitted and accepted, how do we determine the total tax amount owed to ZATCA?

Could someone please clarify these doubts?

ajaybalachandran commented 3 months ago

@tawwfik I followed steps to obtain Production CSID in CORE API's from the official documentation and I got issuer name as given below <ds:X509IssuerName>CN=PRZEINVOICESCA4-CA, DC=extgazt, DC=gov, DC=local</ds:X509IssuerName> is this correct issuer name?

owncommander commented 3 months ago

hello i'am facing the same problem with the same error message : "Invalid signed properties hashing, SignedProperties with id='xadesSignedProperties" but i'am using .NET DLL from zatca , (Zatca.EInvoice.SDK.dll + Zatca.EInvoice.SDK.Contracts.dll) everything we need in these dll , hashing and signing invoice , This happen when calling API for invoice Complaince in Fatoora Simulation Portal in Developer Portal never happen this error can any one help me with that ? And I thank you in advance

Yottskry commented 3 months ago

hello i'am facing the same problem with the same error message : "Invalid signed properties hashing, SignedProperties with id='xadesSignedProperties" but i'am using .NET DLL from zatca , (Zatca.EInvoice.SDK.dll + Zatca.EInvoice.SDK.Contracts.dll) everything we need in these dll , hashing and signing invoice , This happen when calling API for invoice Complaince in Fatoora Simulation Portal in Developer Portal never happen this error can any one help me with that ? And I thank you in advance

I also used the .net SDK and eventually got this working. The problem is that the XML has to be formatted exactly as Zatca expects because they also perform the hash at their end, so if we don't make the same manipulations they make they will get a different hash to us.

The steps I followed were:

  1. Sign the document using the SDK. Then obtain the xades:QualifyingProperties node using XPath
  2. Then get the InnerXML property as a string. You'll need to do string manipulation rather than relying on XML objects because you need to guarantee the order of attributes. We'll call this XmlString for now.
  3. Remove all xades:QualifyingProperties' children. This has the side effect of also removing its attributes, so you'll need to add in the Target property again (signode is the node found at 2):
    signode.RemoveAll();
    var xatt = xmldoc.CreateAttribute("Target");
    xatt.Value = "signature";
    signode.Attributes.Append(xatt);
  4. Ensure the attributes for XmlString's elements are in the right order. xmlns:ds="http://www.w3.org/2000/09/xmldsig#" must come first:
                  <xades:SignedProperties xmlns:xades="http://uri.etsi.org/01903/v1.3.2#" Id="xadesSignedProperties">
                    <xades:SignedSignatureProperties>
                      <xades:SigningTime>2024-04-16T09:50:23</xades:SigningTime>
                      <xades:SigningCertificate>
                        <xades:Cert>
                          <xades:CertDigest>
                            <ds:DigestMethod xmlns:ds="http://www.w3.org/2000/09/xmldsig#" Algorithm="http://www.w3.org/2001/04/xmlenc#sha256" />
                            <ds:DigestValue xmlns:ds="http://www.w3.org/2000/09/xmldsig#">YmJmMDdjZWQ0ZThkNTM3MjU2YTlhOGIwNTM4ZjBjMWQwYzMzNDJkZmNhZTBhOTUxNDE5NWFjM2RhZDg5NDczMQ==</ds:DigestValue>
                          </xades:CertDigest>
                          <xades:IssuerSerial>
                            <ds:X509IssuerName xmlns:ds="http://www.w3.org/2000/09/xmldsig#">CN=eInvoicing</ds:X509IssuerName>
                            <ds:X509SerialNumber xmlns:ds="http://www.w3.org/2000/09/xmldsig#">1712586866985</ds:X509SerialNumber>
                          </xades:IssuerSerial>
                        </xades:Cert>
                      </xades:SigningCertificate>
                    </xades:SignedSignatureProperties>
                  </xades:SignedProperties>
  1. Remove the space before the slash at the ends of any empty tags. i.e. replace: " \>" with "\>"
  2. Trim any newlines, carriage returns, or white space from the very beginning and end of XmlString (not from the individual lines, but from the string as a whole)
  3. Finally, replace any \r\n combinations with just \n. Lines should end with only newline.
  4. Now you'll need to hash XmlString, but you'll need to do it manually:
    
    string finalstr;

// Hash the signature block. using(var myhash = SHA256.Create()) { var hashval = myhash.ComputeHash(Encoding.UTF8.GetBytes(XmlString)); StringBuilder builder = new StringBuilder(); for (int i = 0; i < hashval.Length; i++) { builder.Append(hashval[i].ToString("x2")); } finalstr = Convert.ToBase64String(Encoding.UTF8.GetBytes(builder.ToString())); }

10. Replace the existing hash value with the one created in step 9:

xpathstr = "//ns:Invoice/ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sig:UBLDocumentSignatures/sac:SignatureInformation/ds:Signature/ds:SignedInfo/ds:Reference[position()=2]/ds:DigestValue"; signode = xmldoc.DocumentElement.SelectSingleNode(xpathstr, nsmgr); signode.InnerText = finalstr;


One last thing is to remove the extra namespaces from the signature block or it won't validate the document correctly at Zatca, and to replace the entire signature block (removed in step 3) with our signature block. docstr is the entire document as a string (xmldoc.OuterXml):

chararray = new char[] { '\r', '\n' }; XmlString = XmlString.TrimStart(chararray); XmlString = XmlString.Replace(" xmlns:ds=\"http://www.w3.org/2000/09/xmldsig#\"", ""); docstr = docstr.Replace("</xades:QualifyingProperties>", "\n" + signodestr + "</xades:QualifyingProperties>");



It's a lot of hoops to jump through, and it took me about a week of trial and error to get here. I'm not saying this is the only way of doing this or even the best way, but this is what worked for me. I hope it at least puts you on the right track.
owncommander commented 2 months ago

hello i'am facing the same problem with the same error message : "Invalid signed properties hashing, SignedProperties with id='xadesSignedProperties" but i'am using .NET DLL from zatca , (Zatca.EInvoice.SDK.dll + Zatca.EInvoice.SDK.Contracts.dll) everything we need in these dll , hashing and signing invoice , This happen when calling API for invoice Complaince in Fatoora Simulation Portal in Developer Portal never happen this error can any one help me with that ? And I thank you in advance

I also used the .net SDK and eventually got this working. The problem is that the XML has to be formatted exactly as Zatca expects because they also perform the hash at their end, so if we don't make the same manipulations they make they will get a different hash to us.

The steps I followed were:

  1. Sign the document using the SDK. Then obtain the xades:QualifyingProperties node using XPath
  2. Then get the InnerXML property as a string. You'll need to do string manipulation rather than relying on XML objects because you need to guarantee the order of attributes. We'll call this XmlString for now.
  3. Remove all xades:QualifyingProperties' children. This has the side effect of also removing its attributes, so you'll need to add in the Target property again (signode is the node found at 2):
signode.RemoveAll();
var xatt = xmldoc.CreateAttribute("Target");
xatt.Value = "signature";
signode.Attributes.Append(xatt);
  1. Ensure the attributes for XmlString's elements are in the right order. xmlns:ds="http://www.w3.org/2000/09/xmldsig#" must come first:
                  <xades:SignedProperties xmlns:xades="http://uri.etsi.org/01903/v1.3.2#" Id="xadesSignedProperties">
                    <xades:SignedSignatureProperties>
                      <xades:SigningTime>2024-04-16T09:50:23</xades:SigningTime>
                      <xades:SigningCertificate>
                        <xades:Cert>
                          <xades:CertDigest>
                            <ds:DigestMethod xmlns:ds="http://www.w3.org/2000/09/xmldsig#" Algorithm="http://www.w3.org/2001/04/xmlenc#sha256" />
                            <ds:DigestValue xmlns:ds="http://www.w3.org/2000/09/xmldsig#">YmJmMDdjZWQ0ZThkNTM3MjU2YTlhOGIwNTM4ZjBjMWQwYzMzNDJkZmNhZTBhOTUxNDE5NWFjM2RhZDg5NDczMQ==</ds:DigestValue>
                          </xades:CertDigest>
                          <xades:IssuerSerial>
                            <ds:X509IssuerName xmlns:ds="http://www.w3.org/2000/09/xmldsig#">CN=eInvoicing</ds:X509IssuerName>
                            <ds:X509SerialNumber xmlns:ds="http://www.w3.org/2000/09/xmldsig#">1712586866985</ds:X509SerialNumber>
                          </xades:IssuerSerial>
                        </xades:Cert>
                      </xades:SigningCertificate>
                    </xades:SignedSignatureProperties>
                  </xades:SignedProperties>
  1. Remove the space before the slash at the ends of any empty tags. i.e. replace: " \>" with "\>"
  2. Trim any newlines, carriage returns, or white space from the very beginning and end of XmlString (not from the individual lines, but from the string as a whole)
  3. Finally, replace any \r\n combinations with just \n. Lines should end with only newline.
  4. Now you'll need to hash XmlString, but you'll need to do it manually:
string finalstr;

// Hash the signature block.
using(var myhash = SHA256.Create())
{
  var hashval = myhash.ComputeHash(Encoding.UTF8.GetBytes(XmlString));
  StringBuilder builder = new StringBuilder();
  for (int i = 0; i < hashval.Length; i++)
  {
    builder.Append(hashval[i].ToString("x2"));
  }
  finalstr = Convert.ToBase64String(Encoding.UTF8.GetBytes(builder.ToString()));
}
  1. Replace the existing hash value with the one created in step 9:
xpathstr = "//ns:Invoice/ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sig:UBLDocumentSignatures/sac:SignatureInformation/ds:Signature/ds:SignedInfo/ds:Reference[position()=2]/ds:DigestValue";
signode = xmldoc.DocumentElement.SelectSingleNode(xpathstr, nsmgr);
signode.InnerText = finalstr;

One last thing is to remove the extra namespaces from the signature block or it won't validate the document correctly at Zatca, and to replace the entire signature block (removed in step 3) with our signature block. docstr is the entire document as a string (xmldoc.OuterXml):

chararray = new char[] { '\r', '\n' };
XmlString = XmlString.TrimStart(chararray);
XmlString = XmlString.Replace(" xmlns:ds=\"http://www.w3.org/2000/09/xmldsig#\"", "");
docstr = docstr.Replace("</xades:QualifyingProperties>", "\n" + signodestr + "</xades:QualifyingProperties>");

It's a lot of hoops to jump through, and it took me about a week of trial and error to get here. I'm not saying this is the only way of doing this or even the best way, but this is what worked for me. I hope it at least puts you on the right track.

First of all, I would like to thank you very much for the explanation you provided , Second : i found the soluation in another DLL files named "SDKNETFrameWorkLib.dll" , this dll has also Sign and hash Method , and i replaced with the others in "Zatca.EInvoice.SDK.dll" , and it working very well these methos are "EInvoiceSigningLogic" and "HashingValidator" , i expect these DLL's doing what you just explaining .... thanks alot

Yottskry commented 2 months ago

First of all, I would like to thank you very much for the explanation you provided , Second : i found the soluation in another DLL files named "SDKNETFrameWorkLib.dll" , this dll has also Sign and hash Method , and i replaced with the others in "Zatca.EInvoice.SDK.dll" , and it working very well these methos are "EInvoiceSigningLogic" and "HashingValidator" , i expect these DLL's doing what you just explaining .... thanks alot

That's useful to know. We do have those DLL's but they were from an earlier version of the SDK. Perhaps they broke something in the newer versions? I'll have a look at them though, thanks.

tawwfik commented 2 months ago

this code for SignedProperties issue

it's work with me with php language

        $time = $this->dateFormat(new \DateTime());
        $template_xml = str_replace('SET_CERTIFICATE_HASH', $cert_info['hash'], $template_xml);
        $template_xml = str_replace('SET_SIGN_TIMESTAMP', $time, $template_xml);
        $template_xml = str_replace('SET_CERTIFICATE_SERIAL_NUMBER', $cert_info['serial_number'], $template_xml);
        $template_xml = str_replace('SET_CERTIFICATE_ISSUER', $cert_info['issuer'], $template_xml);
        $invoice = str_replace('<xades:SignedProperties/>', ($template_xml), $invoice);
        $invoice = str_replace('<?xml version="1.0"?>', '<?xml version="1.0" encoding="UTF-8"?>', $invoice);
        $xml = new DOMDocument("1.0", "utf-8");
        $xml->loadXML($invoice);
        $xpath = new DOMXPath($xml);
        $xpath->registerNamespace('default-ns', "urn:oasis:names:specification:ubl:schema:xsd:Invoice-2");
        $SignedProperties = "//default-ns:Invoice/ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sig:UBLDocumentSignatures/sac:SignatureInformation/ds:Signature/ds:Object/xades:QualifyingProperties/xades:SignedProperties";
        $xpath->registerNamespace('sig', "urn:oasis:names:specification:ubl:schema:xsd:CommonSignatureComponents-2");
        $xpath->registerNamespace('sac', "urn:oasis:names:specification:ubl:schema:xsd:SignatureAggregateComponents-2");
        $xpath->registerNamespace('sbc', "urn:oasis:names:specification:ubl:schema:xsd:SignatureBasicComponents-2");
        $xpath->registerNamespace('ds', "http://www.w3.org/2000/09/xmldsig#");
        $xpath->registerNamespace('xades', "http://uri.etsi.org/01903/v1.3.2#");
        $SignedPropertiesValue = $xpath->query($SignedProperties);
        $canonicalizationInvoiceXML = $SignedPropertiesValue[0]->C14N(\true);
        $canonicalizationInvoiceXML = str_replace('></ds:DigestMethod>', '/>', $canonicalizationInvoiceXML);
        $signed_properties_hash = base64_encode(hash('sha256', $canonicalizationInvoiceXML));
        return [
            'signed_properties_hash' => $signed_properties_hash,
            'invoice' => $invoice,
        ];

$invoice is full invoice without SignedProperties tag $template_xml is SignedProperties tag without data after I add data to SignedProperties add SignedProperties to full invoice
use xpath to get SignedProperties from full invoice not work if you not use xpath you shoud use c14n when get SignedProperties by xpath

tawwfik commented 2 months ago

@tawwfik I followed steps to obtain Production CSID in CORE API's from the official documentation and I got issuer name as given below <ds:X509IssuerName>CN=PRZEINVOICESCA4-CA, DC=extgazt, DC=gov, DC=local</ds:X509IssuerName> is this correct issuer name?

@ajaybalachandran yes is correct

Wagdi-Noman commented 2 months ago

this code for SignedProperties issue

it's work with me with php language

        $time = $this->dateFormat(new \DateTime());
        $template_xml = str_replace('SET_CERTIFICATE_HASH', $cert_info['hash'], $template_xml);
        $template_xml = str_replace('SET_SIGN_TIMESTAMP', $time, $template_xml);
        $template_xml = str_replace('SET_CERTIFICATE_SERIAL_NUMBER', $cert_info['serial_number'], $template_xml);
        $template_xml = str_replace('SET_CERTIFICATE_ISSUER', $cert_info['issuer'], $template_xml);
        $invoice = str_replace('<xades:SignedProperties/>', ($template_xml), $invoice);
        $invoice = str_replace('<?xml version="1.0"?>', '<?xml version="1.0" encoding="UTF-8"?>', $invoice);
        $xml = new DOMDocument("1.0", "utf-8");
        $xml->loadXML($invoice);
        $xpath = new DOMXPath($xml);
        $xpath->registerNamespace('default-ns', "urn:oasis:names:specification:ubl:schema:xsd:Invoice-2");
        $SignedProperties = "//default-ns:Invoice/ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sig:UBLDocumentSignatures/sac:SignatureInformation/ds:Signature/ds:Object/xades:QualifyingProperties/xades:SignedProperties";
        $xpath->registerNamespace('sig', "urn:oasis:names:specification:ubl:schema:xsd:CommonSignatureComponents-2");
        $xpath->registerNamespace('sac', "urn:oasis:names:specification:ubl:schema:xsd:SignatureAggregateComponents-2");
        $xpath->registerNamespace('sbc', "urn:oasis:names:specification:ubl:schema:xsd:SignatureBasicComponents-2");
        $xpath->registerNamespace('ds', "http://www.w3.org/2000/09/xmldsig#");
        $xpath->registerNamespace('xades', "http://uri.etsi.org/01903/v1.3.2#");
        $SignedPropertiesValue = $xpath->query($SignedProperties);
        $canonicalizationInvoiceXML = $SignedPropertiesValue[0]->C14N(\true);
        $canonicalizationInvoiceXML = str_replace('></ds:DigestMethod>', '/>', $canonicalizationInvoiceXML);
        $signed_properties_hash = base64_encode(hash('sha256', $canonicalizationInvoiceXML));
        return [
            'signed_properties_hash' => $signed_properties_hash,
            'invoice' => $invoice,
        ];

@tawwfik Can you please provide more context so I can use this code in my project

tawwfik commented 2 months ago

this code for SignedProperties issue it's work with me with php language

        $time = $this->dateFormat(new \DateTime());
        $template_xml = str_replace('SET_CERTIFICATE_HASH', $cert_info['hash'], $template_xml);
        $template_xml = str_replace('SET_SIGN_TIMESTAMP', $time, $template_xml);
        $template_xml = str_replace('SET_CERTIFICATE_SERIAL_NUMBER', $cert_info['serial_number'], $template_xml);
        $template_xml = str_replace('SET_CERTIFICATE_ISSUER', $cert_info['issuer'], $template_xml);
        $invoice = str_replace('<xades:SignedProperties/>', ($template_xml), $invoice);
        $invoice = str_replace('<?xml version="1.0"?>', '<?xml version="1.0" encoding="UTF-8"?>', $invoice);
        $xml = new DOMDocument("1.0", "utf-8");
        $xml->loadXML($invoice);
        $xpath = new DOMXPath($xml);
        $xpath->registerNamespace('default-ns', "urn:oasis:names:specification:ubl:schema:xsd:Invoice-2");
        $SignedProperties = "//default-ns:Invoice/ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sig:UBLDocumentSignatures/sac:SignatureInformation/ds:Signature/ds:Object/xades:QualifyingProperties/xades:SignedProperties";
        $xpath->registerNamespace('sig', "urn:oasis:names:specification:ubl:schema:xsd:CommonSignatureComponents-2");
        $xpath->registerNamespace('sac', "urn:oasis:names:specification:ubl:schema:xsd:SignatureAggregateComponents-2");
        $xpath->registerNamespace('sbc', "urn:oasis:names:specification:ubl:schema:xsd:SignatureBasicComponents-2");
        $xpath->registerNamespace('ds', "http://www.w3.org/2000/09/xmldsig#");
        $xpath->registerNamespace('xades', "http://uri.etsi.org/01903/v1.3.2#");
        $SignedPropertiesValue = $xpath->query($SignedProperties);
        $canonicalizationInvoiceXML = $SignedPropertiesValue[0]->C14N(\true);
        $canonicalizationInvoiceXML = str_replace('></ds:DigestMethod>', '/>', $canonicalizationInvoiceXML);
        $signed_properties_hash = base64_encode(hash('sha256', $canonicalizationInvoiceXML));
        return [
            'signed_properties_hash' => $signed_properties_hash,
            'invoice' => $invoice,
        ];

@tawwfik Can you please provide more context so I can use this code in my project

  • What is the contents of the $invoice variable ? is it a full invoice including the UBL extension?.
  • what is the type of $template_xml . thank you in advance افدي الشنب

$invoice is full invoice without SignedPropertiestag $template_xml is SignedProperties tag without data after I add data to SignedProperties add SignedProperties to full invoice
use xpath to get SignedProperties from full invoice not work if you not use xpath you shoud use c14n when get SignedPropertiesby xpath

Wagdi-Noman commented 2 months ago

@tawwfik full invoice including the UBL Extension only without the `SignedProperties'! thank you.

aafi53 commented 1 month ago

Hey , i'm facing this issue , anybody can help me ,code":"invalid-invoice-hash","category":"INVOICE_HASHING_ERRORS","message":"The invoice hash API body does not match the (calculated) Hash of the XML (PHP)

owncommander commented 1 month ago

i face this issue before , the reason was i didn't encode the xml to UTF8 before converting to base64 , because i'am using Arabic Letters

aafi53 commented 1 month ago

i face this issue before , the reason was i didn't encode the xml to UTF8 before converting to base64 , because i'am using Arabic Letters

can you share me some code example, because hash created from my side is not same like created by sdk..

owncommander commented 1 month ago

i face this issue before , the reason was i didn't encode the xml to UTF8 before converting to base64 , because i'am using Arabic Letters

can you share me some code example, because hash created from my side is not same like created by sdk..

after Signing the XML for invoice ... generate hash by using this : HashingValidator ihash = new HashingValidator(); Result hr_g = new Result(); Result hr_v = new Result(); XmlDocument SignedXML = new XmlDocument(); invFileName = vvatno + "" + vdate.Replace("-", "") + "T" + vtime.Replace(":", "") + "_" + vinvno; SignedXMLPath = File.ReadAllText(AppDomain.CurrentDomain.BaseDirectory + @"\Invoices\Signed\" + invFileName + ".xml"); SignedXML.LoadXml(SignedXMLPath);

            hr_g = ihash.GenerateEInvoiceHashing(SignedXMLPath);
            hr_v = ihash.ValidateEInvoiceHashing(SignedXMLPath);
            if (hr_v.IsValid)
            {
                ih = hr_g.ResultedValue;
            }

and i saved the uuid and hash into out system database .... then when report or clear the invoice to zatca i used this :: string i = File.ReadAllText(AppDomain.CurrentDomain.BaseDirectory + @"\Invoices\Signed\" + invFileName + ".xml"); // For read all xml file into string variable string invb64 = Convert.ToBase64String(Encoding.UTF8.GetBytes(i)); // to Encode all invoice to UTF8 and the convert it to base64


i hope that can help you

aafi53 commented 1 month ago

i face this issue before , the reason was i didn't encode the xml to UTF8 before converting to base64 , because i'am using Arabic Letters

can you share me some code example, because hash created from my side is not same like created by sdk..

after Signing the XML for invoice ... generate hash by using this : HashingValidator ihash = new HashingValidator(); Result hr_g = new Result(); Result hr_v = new Result(); XmlDocument SignedXML = new XmlDocument(); invFileName = vvatno + "" + vdate.Replace("-", "") + "T" + vtime.Replace(":", "") + "_" + vinvno; SignedXMLPath = File.ReadAllText(AppDomain.CurrentDomain.BaseDirectory + @"\Invoices\Signed" + invFileName + ".xml"); SignedXML.LoadXml(SignedXMLPath);

            hr_g = ihash.GenerateEInvoiceHashing(SignedXMLPath);
            hr_v = ihash.ValidateEInvoiceHashing(SignedXMLPath);
            if (hr_v.IsValid)
            {
                ih = hr_g.ResultedValue;
            }

and i saved the uuid and hash into out system database .... then when report or clear the invoice to zatca i used this :: string i = File.ReadAllText(AppDomain.CurrentDomain.BaseDirectory + @"\Invoices\Signed" + invFileName + ".xml"); // For read all xml file into string variable string invb64 = Convert.ToBase64String(Encoding.UTF8.GetBytes(i)); // to Encode all invoice to UTF8 and the convert it to base64

i hope that can help you

thank you, but still i'm getting same error , do you have any php code example in which you generated hash of invoice from xml

owncommander commented 1 month ago

sorry , but i'am using c#

aafi53 commented 1 month ago

Hi guys. I fixed the problem by register Namespace to Invoice Xml file. after that get the properties from invoice file. this code laravel php solve problem.

                   $xml = new DOMDocument("1.0", "utf-8");
                     $xml->loadXML($invoice)// invoice file after populate the properties;
                   //use domPath to register this namespace
                   $xpath = new DOMXPath($xml);
                   //  register  namespace
                   $xpath->registerNamespace('default-ns', "urn:oasis:names:specification:ubl:schema:xsd:Invoice-2");
                   $xpath->registerNamespace('sig', "urn:oasis:names:specification:ubl:schema:xsd:CommonSignatureComponents-                                      
                   2");
                   $xpath->registerNamespace('sac',                    
                   "urn:oasis:names:specification:ubl:schema:xsd:SignatureAggregateComponents-2");
                   $xpath->registerNamespace('sbc', "urn:oasis:names:specification:ubl:schema:xsd:SignatureBasicComponents-2");
                    $xpath->registerNamespace('ds', "http://www.w3.org/2000/09/xmldsig#");
                   $xpath->registerNamespace('xades', "http://uri.etsi.org/01903/v1.3.2#");
                   // path of SignedProperties
                   $SignedProperties = "//default-ns:Invoice/ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sig:UBLDocumentSignatures/sac:SignatureInformation/ds:Signature/ds:Object/xades:QualifyingProperties/xades:SignedProperties";
                   // get SignedProperties by path query
                   $SignedPropertiesValue = $xpath->query($SignedProperties);
                    // convert SignedProperties node to c14n standerd.
                   $canonicalizationInvoiceXML = $SignedPropertiesValue[0]->C14N(\true);
                   // replace tag to rquired in zatca.
                   $canonicalizationInvoiceXML = str_replace('></ds:DigestMethod>', '/>', $canonicalizationInvoiceXML);
                   // hash SignedProperties
                   $signed_properties_hash = base64_encode(hash('sha256', $canonicalizationInvoiceXML));

Bro how you generated InvoiceHash in php ? or3rT7i9zH/YB9b2TkIGT9w1ysISfcckyHpGMSGRVyA= i created invoiceHash by sdk drYi8GYkrOhTk2CzX6MMOUIOtgCz4qJuTaE+FfpZ0lE= this i generated by Php code, when i pass First Hash generated By SDK i didn't get error, but second Invoice Hash shows error, so i get error my error in InvoiceHash, Can you help me in this Regards..

Faheemmcfc commented 1 month ago

I am trying to pass Simplified Invoice (B2C) for compliance. But it's giving me following errors:

Invalid certificate hashing
Invalid signed properties hashing, SignedProperties with id='xadesSignedProperties'

I am using the same procedure for Standard Invoice(B2B) for compliance and it's working very fine. I am using .Net SDK for implementation. Validation of the xml with ZATCA SDK is successful. But when passing the invoice to Compliance Invoice API producing above errors. Only difference in Simplified and Standard invoice is that, simplified invoice have 'InvoiceTypeCode = 020000', but in Standard Invoice it is 'InvoiceTypeCode=010000'.

Trying to get a solution for a long time.

Please help. Thank you..

owncommander commented 1 month ago

I am trying to pass Simplified Invoice (B2C) for compliance. But it's giving me following errors:

Invalid certificate hashing
Invalid signed properties hashing, SignedProperties with id='xadesSignedProperties'

I am using the same procedure for Standard Invoice(B2B) for compliance and it's working very fine. I am using .Net SDK for implementation. Validation of the xml with ZATCA SDK is successful. But when passing the invoice to Compliance Invoice API producing above errors. Only difference in Simplified and Standard invoice is that, simplified invoice have 'InvoiceTypeCode = 020000', but in Standard Invoice it is 'InvoiceTypeCode=010000'.

Trying to get a solution for a long time.

Please help. Thank you..

i was facing these errors before .... the soluation i did is using Old Zatca dll because i'am using .NET like you .... this dll called "SDKNETFrameWorkLib.dll" ... i used this dll for Signing the invoice and "Zatca.EInvoice.SDK.dll" for QR Generating . another note : for complaince using (CCSID) (responce from zatca when send CSR) and for reporting using (PCSID) (when send token+secret+request id from csid) this is link for the old dll : https://www.mediafire.com/file/b7j52okvygc8v3p/SDKNETFrameWorkLib.dll/file i hope that can help best wishes

Faheemmcfc commented 1 month ago

I am trying to pass Simplified Invoice (B2C) for compliance. But it's giving me following errors:

Invalid certificate hashing
Invalid signed properties hashing, SignedProperties with id='xadesSignedProperties'

I am using the same procedure for Standard Invoice(B2B) for compliance and it's working very fine. I am using .Net SDK for implementation. Validation of the xml with ZATCA SDK is successful. But when passing the invoice to Compliance Invoice API producing above errors. Only difference in Simplified and Standard invoice is that, simplified invoice have 'InvoiceTypeCode = 020000', but in Standard Invoice it is 'InvoiceTypeCode=010000'. Trying to get a solution for a long time. Please help. Thank you..

i was facing these errors before .... the soluation i did is using Old Zatca dll because i'am using .NET like you .... this dll called "SDKNETFrameWorkLib.dll" ... i used this dll for Signing the invoice and "Zatca.EInvoice.SDK.dll" for QR Generating . another note : for complaince using (CCSID) (responce from zatca when send CSR) and for reporting using (PCSID) (when send token+secret+request id from csid) this is link for the old dll : https://www.mediafire.com/file/b7j52okvygc8v3p/SDKNETFrameWorkLib.dll/file i hope that can help best wishes

Tried this dll, but hashing function is not working. Code is below: var _IHashingValidator = new SDKNETFrameWorkLib.BLL.HashingValidator(); Result objResult = new Result(); objResult = _IHashingValidator.GenerateEInvoiceHashing(location);

I think this dll doen't have that function. How you are doing the hashing of the xml? Can you help. Thank you..

owncommander commented 1 month ago

this for hashing : SDKNETFrameWorkLib.BLL.HashingValidator ihash = new SDKNETFrameWorkLib.BLL.HashingValidator(); SDKNETFrameWorkLib.GeneralLogic.Result hr_g = new SDKNETFrameWorkLib.GeneralLogic.Result(); SDKNETFrameWorkLib.GeneralLogic.Result hr_v = new SDKNETFrameWorkLib.GeneralLogic.Result(); XmlDocument SignedXML = new XmlDocument(); invFileName = vvatno + "" + vdate.Replace("-", "") + "T" + vtime.Replace(":", "") + "_" + vinvno; SignedXMLPath = File.ReadAllText(AppDomain.CurrentDomain.BaseDirectory + @"\Invoices\Signed\" + invFileName + ".xml"); SignedXML.LoadXml(SignedXMLPath);

            hr_g = ihash.GenerateEInvoiceHashing(SignedXMLPath);
            hr_v = ihash.ValidateEInvoiceHashing(SignedXMLPath);
            if (hr_v.IsValid)
            {
                //if (flag == 2) { Console.WriteLine("Invoice Hashing Successfully ....."); }
                ih = hr_g.ResultedValue;
            }
        }
        catch (Exception ErrorHasing)
        {
            File.WriteAllText(AppDomain.CurrentDomain.BaseDirectory + @"\Errors\Err_Hash_" + invFileName + ".txt", ErrorHasing.Message);
        }
Faheemmcfc commented 1 month ago

this for hashing : SDKNETFrameWorkLib.BLL.HashingValidator ihash = new SDKNETFrameWorkLib.BLL.HashingValidator(); SDKNETFrameWorkLib.GeneralLogic.Result hr_g = new SDKNETFrameWorkLib.GeneralLogic.Result(); SDKNETFrameWorkLib.GeneralLogic.Result hr_v = new SDKNETFrameWorkLib.GeneralLogic.Result(); XmlDocument SignedXML = new XmlDocument(); invFileName = vvatno + "" + vdate.Replace("-", "") + "T" + vtime.Replace(":", "") + "_" + vinvno; SignedXMLPath = File.ReadAllText(AppDomain.CurrentDomain.BaseDirectory + @"\Invoices\Signed" + invFileName + ".xml"); SignedXML.LoadXml(SignedXMLPath);

            hr_g = ihash.GenerateEInvoiceHashing(SignedXMLPath);
            hr_v = ihash.ValidateEInvoiceHashing(SignedXMLPath);
            if (hr_v.IsValid)
            {
                //if (flag == 2) { Console.WriteLine("Invoice Hashing Successfully ....."); }
                ih = hr_g.ResultedValue;
            }
        }
        catch (Exception ErrorHasing)
        {
            File.WriteAllText(AppDomain.CurrentDomain.BaseDirectory + @"\Errors\Err_Hash_" + invFileName + ".txt", ErrorHasing.Message);
        }

Tried your solution, but couldn't get it right. Hash is not valid after using your method. can I hash the xml without making it as a string.

glow2590 commented 2 weeks ago

Hi guys. I fixed the problem by register Namespace to Invoice Xml file. after that get the properties from invoice file. this code laravel php solve problem.

                   $xml = new DOMDocument("1.0", "utf-8");
                     $xml->loadXML($invoice)// invoice file after populate the properties;
                   //use domPath to register this namespace
                   $xpath = new DOMXPath($xml);
                   //  register  namespace
                   $xpath->registerNamespace('default-ns', "urn:oasis:names:specification:ubl:schema:xsd:Invoice-2");
                   $xpath->registerNamespace('sig', "urn:oasis:names:specification:ubl:schema:xsd:CommonSignatureComponents-                                      
                   2");
                   $xpath->registerNamespace('sac',                    
                   "urn:oasis:names:specification:ubl:schema:xsd:SignatureAggregateComponents-2");
                   $xpath->registerNamespace('sbc', "urn:oasis:names:specification:ubl:schema:xsd:SignatureBasicComponents-2");
                    $xpath->registerNamespace('ds', "http://www.w3.org/2000/09/xmldsig#");
                   $xpath->registerNamespace('xades', "http://uri.etsi.org/01903/v1.3.2#");
                   // path of SignedProperties
                   $SignedProperties = "//default-ns:Invoice/ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sig:UBLDocumentSignatures/sac:SignatureInformation/ds:Signature/ds:Object/xades:QualifyingProperties/xades:SignedProperties";
                   // get SignedProperties by path query
                   $SignedPropertiesValue = $xpath->query($SignedProperties);
                    // convert SignedProperties node to c14n standerd.
                   $canonicalizationInvoiceXML = $SignedPropertiesValue[0]->C14N(\true);
                   // replace tag to rquired in zatca.
                   $canonicalizationInvoiceXML = str_replace('></ds:DigestMethod>', '/>', $canonicalizationInvoiceXML);
                   // hash SignedProperties
                   $signed_properties_hash = base64_encode(hash('sha256', $canonicalizationInvoiceXML));

Thank you so much mate <3

mranonyms022 commented 1 week ago

Hi guys. I fixed the problem by register Namespace to Invoice Xml file. after that get the properties from invoice file. this code laravel php solve problem.

                   $xml = new DOMDocument("1.0", "utf-8");
                     $xml->loadXML($invoice)// invoice file after populate the properties;
                   //use domPath to register this namespace
                   $xpath = new DOMXPath($xml);
                   //  register  namespace
                   $xpath->registerNamespace('default-ns', "urn:oasis:names:specification:ubl:schema:xsd:Invoice-2");
                   $xpath->registerNamespace('sig', "urn:oasis:names:specification:ubl:schema:xsd:CommonSignatureComponents-                                      
                   2");
                   $xpath->registerNamespace('sac',                    
                   "urn:oasis:names:specification:ubl:schema:xsd:SignatureAggregateComponents-2");
                   $xpath->registerNamespace('sbc', "urn:oasis:names:specification:ubl:schema:xsd:SignatureBasicComponents-2");
                    $xpath->registerNamespace('ds', "http://www.w3.org/2000/09/xmldsig#");
                   $xpath->registerNamespace('xades', "http://uri.etsi.org/01903/v1.3.2#");
                   // path of SignedProperties
                   $SignedProperties = "//default-ns:Invoice/ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sig:UBLDocumentSignatures/sac:SignatureInformation/ds:Signature/ds:Object/xades:QualifyingProperties/xades:SignedProperties";
                   // get SignedProperties by path query
                   $SignedPropertiesValue = $xpath->query($SignedProperties);
                    // convert SignedProperties node to c14n standerd.
                   $canonicalizationInvoiceXML = $SignedPropertiesValue[0]->C14N(\true);
                   // replace tag to rquired in zatca.
                   $canonicalizationInvoiceXML = str_replace('></ds:DigestMethod>', '/>', $canonicalizationInvoiceXML);
                   // hash SignedProperties
                   $signed_properties_hash = base64_encode(hash('sha256', $canonicalizationInvoiceXML));

Thank you so much mate <3

Will you please provide me some support regarding this I am facing the same Issue I did everything as per the document but I am still getting invoice hashing error.Please help me out

errorMessages": array:1 [ 0 => {#1506 +"type": "ERROR" +"code": "invalid-invoice-hash" +"category": "INVOICE_HASHING_ERRORS" +"message": "The invoice hash API body does not match the (calculated) Hash of the XML" +"status": "ERROR" } ] +"status": "ERROR" }

I am getting this error in laravel