diff --git a/src/signed-xml.ts b/src/signed-xml.ts
index 50a370f1..663d3d0e 100644
--- a/src/signed-xml.ts
+++ b/src/signed-xml.ts
@@ -1135,27 +1135,7 @@ export class SignedXml {
if (ref.isEmptyUri) {
targetUri = "";
} else {
- let id: string | null = null;
- if (this.idMode === "wssecurity") {
- const attr = utils.findAttr(
- node,
- "Id",
- "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd",
- );
- if (attr) {
- id = attr.value;
- }
- } else {
- for (const attr of this.idAttributes) {
- id = node.getAttribute(attr);
- if (id) {
- break;
- }
- }
- }
- if (!id) {
- throw new Error(`No ID attribute found on node for reference: ${ref.xpath}`);
- }
+ const id = this.ensureHasId(node);
ref.uri = id;
targetUri = `#${id}`;
}
diff --git a/test/signature-object-tests.spec.ts b/test/signature-object-tests.spec.ts
index 59b70118..75126c23 100644
--- a/test/signature-object-tests.spec.ts
+++ b/test/signature-object-tests.spec.ts
@@ -366,6 +366,130 @@ describe("Valid signatures with ds:Object elements", function () {
const { valid, errorMessage } = checkSignature(signedXml, doc);
expect(valid, errorMessage).to.be.true;
});
+
+ it("should create valid signature and generate Id attribute for ds:Object when not provided", function () {
+ const xml = "";
+ const sig = new SignedXml({
+ privateKey,
+ canonicalizationAlgorithm: "http://www.w3.org/2001/10/xml-exc-c14n#",
+ signatureAlgorithm: "http://www.w3.org/2000/09/xmldsig#rsa-sha1",
+ objects: [
+ {
+ content: "Test data in Object element",
+ },
+ ],
+ });
+
+ sig.addReference({
+ xpath: "//*[local-name(.)='Data']",
+ digestAlgorithm: "http://www.w3.org/2000/09/xmldsig#sha1",
+ transforms: [
+ "http://www.w3.org/2000/09/xmldsig#enveloped-signature",
+ "http://www.w3.org/2001/10/xml-exc-c14n#",
+ ],
+ });
+
+ sig.computeSignature(xml, { prefix: "ds" });
+ const signedXml = sig.getSignedXml();
+ const doc = new xmldom.DOMParser().parseFromString(signedXml);
+
+ // Find the ds:Object/Data element and get the value of its Id attribute (ensuring it was generated)
+ const dataEl = select1Ns("/root/ds:Signature/ds:Object/Data[@Id]", doc);
+ isDomNode.assertIsElementNode(dataEl);
+ const idValue = dataEl.getAttribute("Id");
+ expect(idValue).to.be.a("string").that.is.not.empty;
+
+ // Verify that there is a Reference pointing to the generated Id
+ const uri = `#${idValue}`;
+ const refEl = select1Ns(`/root/ds:Signature/ds:SignedInfo/ds:Reference[@URI='${uri}']`, doc);
+ isDomNode.assertIsElementNode(refEl);
+
+ // Verify that the signature is valid
+ const { valid, errorMessage } = checkSignature(signedXml, doc);
+ expect(valid, errorMessage).to.be.true;
+ });
+});
+
+describe("Should successfuly sign references to ds:KeyInfo elements", function () {
+ it("should create valid signatures with references to ds:KeyInfo when the Id attribute is provided", function () {
+ const xml = "";
+ const sig = new SignedXml({
+ privateKey,
+ canonicalizationAlgorithm: "http://www.w3.org/2001/10/xml-exc-c14n#",
+ signatureAlgorithm: "http://www.w3.org/2000/09/xmldsig#rsa-sha1",
+ keyInfoAttributes: {
+ Id: "key-info-1",
+ },
+ getKeyInfoContent: () => "",
+ });
+
+ sig.addReference({
+ xpath: "//*[local-name(.)='KeyInfo']",
+ digestAlgorithm: "http://www.w3.org/2000/09/xmldsig#sha1",
+ transforms: [
+ "http://www.w3.org/2000/09/xmldsig#enveloped-signature",
+ "http://www.w3.org/2001/10/xml-exc-c14n#",
+ ],
+ });
+
+ sig.computeSignature(xml);
+ const signedXml = sig.getSignedXml();
+
+ const doc = new xmldom.DOMParser().parseFromString(signedXml);
+
+ // Verify that there is a Reference to KeyInfo
+ const referenceEl = select1Ns(
+ "/root/ds:Signature/ds:SignedInfo/ds:Reference[@URI='#key-info-1']",
+ doc,
+ );
+ isDomNode.assertIsElementNode(referenceEl);
+
+ // Verify that the signature is valid
+ const { valid, errorMessage } = checkSignature(signedXml, doc);
+ expect(valid, errorMessage).to.be.true;
+ });
+
+ it("should create valid signatures with references to ds:KeyInfo when the Id attribute is autogenerated", function () {
+ const xml = "";
+ const sig = new SignedXml({
+ privateKey,
+ canonicalizationAlgorithm: "http://www.w3.org/2001/10/xml-exc-c14n#",
+ signatureAlgorithm: "http://www.w3.org/2000/09/xmldsig#rsa-sha1",
+ getKeyInfoContent: () => "",
+ });
+
+ sig.addReference({
+ xpath: "//*[local-name(.)='KeyInfo']",
+ digestAlgorithm: "http://www.w3.org/2000/09/xmldsig#sha1",
+ transforms: [
+ "http://www.w3.org/2000/09/xmldsig#enveloped-signature",
+ "http://www.w3.org/2001/10/xml-exc-c14n#",
+ ],
+ });
+
+ sig.computeSignature(xml);
+ const signedXml = sig.getSignedXml();
+
+ const doc = new xmldom.DOMParser().parseFromString(signedXml);
+
+ // Find the KeyInfo element and get the value of its Id attribute (ensuring it was generated)
+ const keyInfoEl = select1Ns("/root/ds:Signature/ds:KeyInfo[@Id]", doc);
+ isDomNode.assertIsElementNode(keyInfoEl);
+ const idValue = keyInfoEl.getAttribute("Id");
+ expect(idValue).to.be.a("string").that.is.not.empty;
+
+ // Find a Reference with URI=`#${idValue}`
+ const uri = `#${idValue}`;
+ const referenceEl = select1Ns(
+ `/root/ds:Signature/ds:SignedInfo/ds:Reference[@URI='${uri}']`,
+ doc,
+ );
+ isDomNode.assertIsElementNode(referenceEl);
+
+ // Verify that the signature is valid
+ const { valid, errorMessage } = checkSignature(signedXml, doc);
+ expect(valid, errorMessage).to.be.true;
+ });
});
describe("XAdES Object support in XML signatures", function () {
diff --git a/test/signature-unit-tests.spec.ts b/test/signature-unit-tests.spec.ts
index deeffa9a..c0dcf136 100644
--- a/test/signature-unit-tests.spec.ts
+++ b/test/signature-unit-tests.spec.ts
@@ -1403,4 +1403,35 @@ describe("Signature unit tests", function () {
/the following xpath cannot be signed because it was not found/,
);
});
+
+ it("should sign references when the Id attribute is prefixed", () => {
+ const xml = '';
+ const sig = new SignedXml({
+ privateKey: fs.readFileSync("./test/static/client.pem"),
+ canonicalizationAlgorithm: "http://www.w3.org/2001/10/xml-exc-c14n#",
+ signatureAlgorithm: "http://www.w3.org/2000/09/xmldsig#rsa-sha1",
+ });
+
+ sig.addReference({
+ xpath: "//*[local-name(.)='x']",
+ digestAlgorithm: "http://www.w3.org/2000/09/xmldsig#sha1",
+ transforms: ["http://www.w3.org/2001/10/xml-exc-c14n#"],
+ });
+
+ sig.computeSignature(xml);
+ const signedXml = sig.getSignedXml();
+
+ const doc = new xmldom.DOMParser().parseFromString(signedXml);
+ const referenceElements = xpath.select("//*[local-name(.)='Reference']", doc);
+ isDomNode.assertIsArrayOfNodes(referenceElements);
+ expect(referenceElements.length, "Reference element should exist").to.equal(1);
+
+ const referenceElement = referenceElements[0];
+ isDomNode.assertIsElementNode(referenceElement);
+
+ const uriAttribute = referenceElement.getAttribute("URI");
+ expect(uriAttribute, "Reference element should have the correct URI attribute value").to.equal(
+ "#unique-id",
+ );
+ });
});