0

Fixing "Incorrect reference digest value" in SOAP XML WS-Security with node-soap

Problem

I'm experiencing an intermittent issue with SOAP XML requests to an external service using the node-soap library (v1.1.10) and xml-crypto (v6.0.1). The server rejects some requests with the following error:

{
  "faultcode": "soapenv:Client",
  "faultstring": "Incorrect reference digest value",
  "detail": {
    "IFException": {
      "reasonCode": "0x00d30003",
      "reasonText": "Incorrect reference digest value"
    }
  }
}

After examining the logs, I discovered what I think is the root cause: duplicate IDs in the XML document. Specifically, both the Timestamp element and the Body element have the same ID (_1):

<Timestamp xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" Id="_1">
  <Created>2025-11-05T13:30:00Z</Created>
  <Expires>2025-11-05T13:40:00Z</Expires>
</Timestamp>

<!-- Later in the document -->
<soap:Body Id="_1">
  <!-- Body content -->
</soap:Body>

The signature then references this ID twice, with different digest values:

<Reference URI="#_1">
  <Transforms>
    <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
    <Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
  </Transforms>
  <DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
  <DigestValue>fSZ3uhjr1D+zGS4eh0yTiW56g3JKiOs+ozpkk0QOu2w=</DigestValue>
</Reference>
<Reference URI="#_1">
  <Transforms>
    <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
    <Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
  </Transforms>
  <DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
  <DigestValue>0G8pJmk3ObO7+IerCol9PtWuUllUzLkvZBiztv3jLBk=</DigestValue>
</Reference>

I've tried to fix this by adding a unique ID to the Body element:

const wsSecurityOptions = {
  signerOptions: {
    // Set a unique ID for the Body element to avoid reference conflicts
    attrs: {
      Id: `_body_${Date.now()}`,
    },
  },
};

But this didn't resolve the issue. The problem seems to be that the node-soap library is still generating duplicate IDs.

Detailed Analysis

Looking at the WSSecurityCert.js implementation in the node-soap library, I found that it creates a timestamp with a hardcoded ID of _1:

// From WSSecurityCert.js
if (this.hasTimeStamp) {
  timestampStr = `<Timestamp xmlns="${oasisBaseUri}/oasis-200401-wss-wssecurity-utility-1.0.xsd" Id="_1">` +
                 `<Created>${this.created}</Created>` +
                 `<Expires>${this.expires}</Expires>` +
                 `</Timestamp>`;
}

Meanwhile, when setting the Body ID, it's using the same ID or a dynamic one that might conflict:

// This is what I tried to customize
const wsSecurityOptions = {
  signerOptions: {
    attrs: {
      Id: `_body_${Date.now()}`, // Still conflicts sometimes
    },
  },
};

Potential Solutions

1. Modify the XML after generation but before sending

One approach is to modify the XML after it's generated but before it's sent to ensure unique IDs:

// Hook into the request event
client.on('request', (xml) => {
  // Replace the Body ID with a unique one that won't conflict with the Timestamp
  const modifiedXml = xml.replace(
    /<soap:Body Id="_1"/g, 
    '<soap:Body Id="_body"'
  );
  // Use the modified XML instead
  return modifiedXml;
});

2. Patch the WSSecurityCert class

A more robust solution would be to patch the WSSecurityCert class to use different IDs for the Timestamp and Body:

// Create a custom WSSecurityCert class
class CustomWSSecurityCert extends WSSecurityCert {
  postProcess(xml, envelopeKey) {
    // Override the timestamp ID
    this.timestampId = "_timestamp";
    
    // Generate timestamp with custom ID
    if (this.hasTimeStamp) {
      timestampStr = `<Timestamp xmlns="${oasisBaseUri}/oasis-200401-wss-wssecurity-utility-1.0.xsd" Id="${this.timestampId}">` +
                     `<Created>${this.created}</Created>` +
                     `<Expires>${this.expires}</Expires>` +
                     `</Timestamp>`;
    }
    
    // Continue with the rest of the original method
    // ...
    
    // Make sure references use the correct IDs
    // ...
  }
}

3. Use a different WS-Security implementation

If patching is too complex, consider using a different WS-Security implementation:

// Example using a different library
const wsSecurity = require('ws-security-custom');

const securityHandler = new wsSecurity.SecurityHandler({
  privateKey: fs.readFileSync('key.pem'),
  certificate: fs.readFileSync('cert.pem'),
  timestampId: '_timestamp',
  bodyId: '_body'
});

4. Modify the XML after signing but before sending

If the above solutions don't work, you could try a more direct approach by modifying the XML after it's signed but before it's sent:

// Hook into the request event
client.on('request', (xml) => {
  // First, check if we have duplicate IDs
  if ((xml.match(/Id="_1"/g) || []).length > 1) {
    // Replace the second occurrence (Body) with a different ID
    let count = 0;
    const modifiedXml = xml.replace(/Id="_1"/g, (match) => {
      count++;
      return count === 1 ? match : 'Id="_body"';
    });
    
    // Also update references in the signature
    // This is tricky and might require more complex XML parsing
    return modifiedXml;
  }
  return xml;
});

Questions

  1. Has anyone encountered this specific issue with duplicate IDs in node-soap WS-Security?
  2. Is there a cleaner way to ensure unique IDs for different XML elements when using node-soap?
  3. Are there any known patches or workarounds for this issue in the latest versions of node-soap?
  4. Would it be better to fork and modify the node-soap library, or handle this at the application level?

Any insights or solutions would be greatly appreciated!

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.