Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 1 addition & 21 deletions src/signed-xml.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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}`;
}
Expand Down
124 changes: 124 additions & 0 deletions test/signature-object-tests.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 = "<root></root>";
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: "<Data>Test data in Object element</Data>",
},
],
});

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 = "<root><x /></root>";
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: () => "<dummy></dummy>",
});

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 = "<root><x /></root>";
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: () => "<dummy></dummy>",
});

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 () {
Expand Down
31 changes: 31 additions & 0 deletions test/signature-unit-tests.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 = '<root><x xmlns:ns="urn:example" ns:Id="unique-id"/></root>';
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",
);
});
});