Fixing IRBM’s DS322 Error: Digest Mismatch in Signed XML Invoice #ds322 #irbxml


SEO Title: Fixing IRBM DS322 Digest Error in Signed UBL XML Invoices #ds322 #irbxml
Focus Keyphrase: IRB DS322 error
Meta Description: Learn how to fix the IRBM DS322 digest mismatch error during e-invoice submission in Laravel. Solve C14N issues and sign your UBL Invoice element correctly.


🧾 What is DS322?

DS322 is a digest mismatch error from IRBM’s MyInvois API. It occurs when your submitted XML contains a <ds:DigestValue> that doesn’t match the value IRBM computes on their side.

IRBM compares the SHA-256 digest of your canonicalized <Invoice> element (after transforms) to the value inside your <ds:SignedInfo> block. If they don’t match — you get DS322.


❓ Why does DS322 happen?

Here’s what causes it:

CauseExplanation
❌ Wrong canonicalizationYou didn’t use Exclusive C14N correctly on <Invoice>
❌ Wrong node referenceYou signed a child node (e.g. <InvoiceLine>) instead of the full <Invoice>
❌ Signature block placementThe signature is outside the expected structure or added before canonicalizing
❌ Enveloped-signature transform missingIRBM expects this to remove the signature during digesting
❌ You removed required UBLExtensionsIRBM expects the <ext:UBLExtensions> to remain part of the digest

🕒 When do you get this error?

You’ll receive DS322 immediately after submitting a signed UBL XML invoice to:

POST https://preprod-api.myinvois.hasil.gov.my/api/v1.0/invoices

Response example:

{
  "errorCode": "DS322",
  "message": "Digest mismatch",
  "propertyPath": "Invoice.UBLExtensions.SignatureInformation.Signature"
}

👨‍💻 Who is affected?

This error hits:

  • Developers using xmlseclibs or manual DOM signing in Laravel
  • Integrators converting IRBM’s sample XMLs manually
  • Teams copy-pasting signature logic from other UBL projects (like Peppol) without adjusting to IRBM rules
See also  Day 3: Fixing DS322 Digest Mismatch in IRB Signature #ds322 #xmlcanonicalization

🌐 Where is the problem?

It lies in this block (inside <ds:SignedInfo>):

<Reference URI="">
  <Transforms>
    <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
  </Transforms>
  <DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
  <DigestValue>[calculated-hash]</DigestValue>
</Reference>

The digest value here must match the canonicalized version of your <Invoice> element (excluding the signature itself via the enveloped transform).


🛠 How to Fix DS322 in Laravel (Step-by-Step)

✅ 1. Use xmlseclibs with proper transform

$objDSig = new \RobRichards\XMLSecLibs\XMLSecurityDSig();
$objDSig->setCanonicalMethod(XMLSecurityDSig::EXC_C14N);

// This signs the entire document (root node), minus the signature block
$objDSig->addReference(
    $doc, // full DOMDocument
    XMLSecurityDSig::SHA256,
    ['http://www.w3.org/2000/09/xmldsig#enveloped-signature'],
    ['uri' => '']
);

✅ 2. Sign the document after building the entire <Invoice>

Make sure all these are already inserted into the DOM before signing:

  • <ext:UBLExtensions> with <ds:Signature> placeholder
  • All cbc:* fields like UBLVersionID, InvoiceTypeCode, etc.
  • Invoice lines, tax totals, monetary totals
$unsignedXml = $doc->saveXML(); // after everything is added
$signedXml = $signatureService->sign($unsignedXml); // do not sign partial DOM

✅ 3. Use Exclusive C14N for digesting

IRBM uses exclusive canonicalization, so your signature must use:

<CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>

And internally:

$canonicalized = $doc->C14N(true, false);
$digest = base64_encode(hash('sha256', $canonicalized, true));

✅ 4. Keep UBLExtensions inside the digest

This is critical: do not remove <ext:UBLExtensions> before digesting. The signature block inside <ext:ExtensionContent> will be removed by the enveloped-signature transform, but the container itself must remain.


✅ 5. Confirm reference URI is empty string

You must sign the root element:

<Reference URI="">

This means “sign the whole document (root element) after removing the signature node.”


🧪 Debug Tip: Log Your Digest Locally

In Laravel:

$canonical = $doc->C14N(true, false);
$digest = base64_encode(hash('sha256', $canonical, true));
Log::debug('Canonical digest for Invoice', ['digest' => $digest]);

Compare this to the <DigestValue> inside your <SignedInfo> block.

See also  Day 3: Fixing DS322 Digest Mismatch in IRB Signature #ds322 #xmlcanonicalization

✅ Final Checklist to Fix DS322

✅ What to Check✅ Fix Strategy
Reference URISet to "" (empty string)
Canonicalization methodUse http://www.w3.org/2001/10/xml-exc-c14n#
Signature block locationInside <ext:UBLExtensions>
Signature timingSign after generating full XML structure
Transforms in <Reference>Add enveloped-signature
<ext:UBLExtensions> included in digestYes — only <ds:Signature> is removed

✅ Summary

DS322 = digest mismatch on the main Invoice element.

Fix it by:

  • Using exclusive C14N
  • Applying the enveloped-signature transform
  • Signing the entire Invoice node (with UBLExtensions)
  • Canonicalizing only after the full XML is assembled

Tags:
#IRBIntegration #DS322 #UBLSignature #XMLDigest #LaravelMyInvois #DigitalSigning

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.