It turns out the spaces matter even in the version you are submitting to ZATCA’s servers, not just when hashing.
ZATCA’s servers are hashing the SignedProperties you submit via the API as-is, they do not transform them to the spacing format that is required before hashing. Thus if you do not use the same spaces you will always get a hashing error returned by the server, even if your hash is actually correct.
In our call I suggested one of 2 changes to make spaces not matter:
As the original documentation used to say, canonicalize before hashing. This would require ZATCA to accept both the current and canonicalized format and may result in significant overhead and issues.
On ZATCA’s serverside, simply transform any signed properties block that an API client sends to ZATCA to ensure it has the same spaces. This matches the way we hash invoices, both the client and server format the content in the same way (canonicalization) and then the hash is generated and compared. The problem right now is that ZATCA’s server does not format the SignedProperties and expects the client to do it, but this behavior is not documented. Following this suggestion should retain backwards-compatability and make spaces not matter.
tl;dr version
For now just ensure any step you are using the SignedProperties block in 100% match ZATCA’s samples in terms of whitespace.
@obahareth-mrsool@MAl-tamimi
I’m encountering an issue where the invoice generated through the SDK produces the correct signing invoice hash. However, when I attempt to manually generate the same signing hash, I’m unable to match it. Can someone please help me in this regard.
If you faced any issues do not hesitate to reach out our support team via below mail mentioning all the detailed concerns to provide comprehensive support as usual.
The first line of the $signaturePart (the opening tag) had to start with exactly 36 spaces.
Every subsequent line had to be indented with exactly 4 spaces.
This fixed the hash mismatch and allowed the XML to produce the expected digest during signature generation.When generating hashes for digitally signed XML, every space and line break matters. XML canonicalization treats whitespace as part of the content, so make sure your formatting is 100% identical to the expected structure, both in content and indentation.
When I save my xml, i was adding 4 spaces ( just to make it clear for human )
and then i saw them putting the UBL:Extensions on the same line as the <Invoice…
I am also encountering the same issue. In my implementation, the SignedProperties tag is included within the XML. I would like to confirm whether the SignedProperties element embedded in the XML and the one used for generating the hash are identical. Could you please share the exact SignedProperties XML fragment for reference?
is canonicalisation required before generating Signed Properties Hash?
As you mentioned above ,
The first line of the $signaturePart (the opening tag) had to start with exactly 36 spaces.
Every subsequent line had to be indented with exactly 4 spaces.
i follow the same but still i am getting validation error.
{
“type”: “ERROR”,
“code”: “signed-properties-hashing”,
“category”: “CERTIFICATE_ERRORS”,
“message”: “Invalid signed properties hashing, SignedProperties with id=‘xadesSignedProperties’”,
“status”: “ERROR”
}
before <xades:SignedProperties tag i have 36 spaces in my final xml and if i create SignedProperties tag having 36 spaces will it work ? or its mandatory to have 32 spaces in both.
Many developers face the SignedProperties hashing error even when their XML structure looks perfectly correct.
The culprit is often not malformed XML, but rather inconsistent line endings across different operating systems.
Windows saves newlines as \r\n (carriage return + line feed).
Linux/macOS saves newlines as just \n (line feed).
When the SignedProperties block is generated on Windows, every line break is encoded as \r\n (two bytes instead of one). These extra \r characters change the byte sequence that gets hashed.
As a result, the computed digest on Windows doesn’t match the expected digest (usually calculated in an \n environment). This mismatch leads to signature validation errors—even though the XML looks identical in an editor.
In short, the hashing issue often comes down to newline inconsistencies, not XML formatting.
I have resolved this issue by adding 32 spaces before <xades:SignedProperties tag in both my final xml and the SignedProperties xml fragment before hashing.