Skip to content

Commit d77d0fc

Browse files
authored
Merge branch 'master' into deprecate-originalxmlwithids
2 parents 063b976 + 73db72d commit d77d0fc

File tree

4 files changed

+158
-24
lines changed

4 files changed

+158
-24
lines changed

README.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -256,9 +256,8 @@ The `SignedXml` constructor provides an abstraction for sign and verify xml docu
256256
- `inclusiveNamespacesPrefixList` - string - default `null` - a list of namespace prefixes to include during canonicalization
257257
- `implicitTransforms` - string[] - default `[]` - a list of implicit transforms to use during verification
258258
- `keyInfoAttributes` - object - default `{}` - a hash of attributes and values `attrName: value` to add to the KeyInfo node
259-
- `getKeyInfoContent` - function - default `noop` - a function that returns the content of the KeyInfo node
260-
- `getCertFromKeyInfo` - function - default `SignedXml.getCertFromKeyInfo` - a function that returns the certificate from the `<KeyInfo />` node
261-
- `objects` - array - default `undefined` - an array of objects defining the content of the `<Object/>` nodes
259+
- `getKeyInfoContent` - function - default `SignedXml.getKeyInfoContent` - a function that returns the content of the KeyInfo node
260+
- `getCertFromKeyInfo` - function - default `noop` - a function that returns the certificate from the `<KeyInfo />` node
262261

263262
#### API
264263

src/signed-xml.ts

Lines changed: 1 addition & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1135,27 +1135,7 @@ export class SignedXml {
11351135
if (ref.isEmptyUri) {
11361136
targetUri = "";
11371137
} else {
1138-
let id: string | null = null;
1139-
if (this.idMode === "wssecurity") {
1140-
const attr = utils.findAttr(
1141-
node,
1142-
"Id",
1143-
"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd",
1144-
);
1145-
if (attr) {
1146-
id = attr.value;
1147-
}
1148-
} else {
1149-
for (const attr of this.idAttributes) {
1150-
id = node.getAttribute(attr);
1151-
if (id) {
1152-
break;
1153-
}
1154-
}
1155-
}
1156-
if (!id) {
1157-
throw new Error(`No ID attribute found on node for reference: ${ref.xpath}`);
1158-
}
1138+
const id = this.ensureHasId(node);
11591139
ref.uri = id;
11601140
targetUri = `#${id}`;
11611141
}

test/signature-object-tests.spec.ts

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,130 @@ describe("Valid signatures with ds:Object elements", function () {
366366
const { valid, errorMessage } = checkSignature(signedXml, doc);
367367
expect(valid, errorMessage).to.be.true;
368368
});
369+
370+
it("should create valid signature and generate Id attribute for ds:Object when not provided", function () {
371+
const xml = "<root></root>";
372+
const sig = new SignedXml({
373+
privateKey,
374+
canonicalizationAlgorithm: "http://www.w3.org/2001/10/xml-exc-c14n#",
375+
signatureAlgorithm: "http://www.w3.org/2000/09/xmldsig#rsa-sha1",
376+
objects: [
377+
{
378+
content: "<Data>Test data in Object element</Data>",
379+
},
380+
],
381+
});
382+
383+
sig.addReference({
384+
xpath: "//*[local-name(.)='Data']",
385+
digestAlgorithm: "http://www.w3.org/2000/09/xmldsig#sha1",
386+
transforms: [
387+
"http://www.w3.org/2000/09/xmldsig#enveloped-signature",
388+
"http://www.w3.org/2001/10/xml-exc-c14n#",
389+
],
390+
});
391+
392+
sig.computeSignature(xml, { prefix: "ds" });
393+
const signedXml = sig.getSignedXml();
394+
const doc = new xmldom.DOMParser().parseFromString(signedXml);
395+
396+
// Find the ds:Object/Data element and get the value of its Id attribute (ensuring it was generated)
397+
const dataEl = select1Ns("/root/ds:Signature/ds:Object/Data[@Id]", doc);
398+
isDomNode.assertIsElementNode(dataEl);
399+
const idValue = dataEl.getAttribute("Id");
400+
expect(idValue).to.be.a("string").that.is.not.empty;
401+
402+
// Verify that there is a Reference pointing to the generated Id
403+
const uri = `#${idValue}`;
404+
const refEl = select1Ns(`/root/ds:Signature/ds:SignedInfo/ds:Reference[@URI='${uri}']`, doc);
405+
isDomNode.assertIsElementNode(refEl);
406+
407+
// Verify that the signature is valid
408+
const { valid, errorMessage } = checkSignature(signedXml, doc);
409+
expect(valid, errorMessage).to.be.true;
410+
});
411+
});
412+
413+
describe("Should successfuly sign references to ds:KeyInfo elements", function () {
414+
it("should create valid signatures with references to ds:KeyInfo when the Id attribute is provided", function () {
415+
const xml = "<root><x /></root>";
416+
const sig = new SignedXml({
417+
privateKey,
418+
canonicalizationAlgorithm: "http://www.w3.org/2001/10/xml-exc-c14n#",
419+
signatureAlgorithm: "http://www.w3.org/2000/09/xmldsig#rsa-sha1",
420+
keyInfoAttributes: {
421+
Id: "key-info-1",
422+
},
423+
getKeyInfoContent: () => "<dummy></dummy>",
424+
});
425+
426+
sig.addReference({
427+
xpath: "//*[local-name(.)='KeyInfo']",
428+
digestAlgorithm: "http://www.w3.org/2000/09/xmldsig#sha1",
429+
transforms: [
430+
"http://www.w3.org/2000/09/xmldsig#enveloped-signature",
431+
"http://www.w3.org/2001/10/xml-exc-c14n#",
432+
],
433+
});
434+
435+
sig.computeSignature(xml);
436+
const signedXml = sig.getSignedXml();
437+
438+
const doc = new xmldom.DOMParser().parseFromString(signedXml);
439+
440+
// Verify that there is a Reference to KeyInfo
441+
const referenceEl = select1Ns(
442+
"/root/ds:Signature/ds:SignedInfo/ds:Reference[@URI='#key-info-1']",
443+
doc,
444+
);
445+
isDomNode.assertIsElementNode(referenceEl);
446+
447+
// Verify that the signature is valid
448+
const { valid, errorMessage } = checkSignature(signedXml, doc);
449+
expect(valid, errorMessage).to.be.true;
450+
});
451+
452+
it("should create valid signatures with references to ds:KeyInfo when the Id attribute is autogenerated", function () {
453+
const xml = "<root><x /></root>";
454+
const sig = new SignedXml({
455+
privateKey,
456+
canonicalizationAlgorithm: "http://www.w3.org/2001/10/xml-exc-c14n#",
457+
signatureAlgorithm: "http://www.w3.org/2000/09/xmldsig#rsa-sha1",
458+
getKeyInfoContent: () => "<dummy></dummy>",
459+
});
460+
461+
sig.addReference({
462+
xpath: "//*[local-name(.)='KeyInfo']",
463+
digestAlgorithm: "http://www.w3.org/2000/09/xmldsig#sha1",
464+
transforms: [
465+
"http://www.w3.org/2000/09/xmldsig#enveloped-signature",
466+
"http://www.w3.org/2001/10/xml-exc-c14n#",
467+
],
468+
});
469+
470+
sig.computeSignature(xml);
471+
const signedXml = sig.getSignedXml();
472+
473+
const doc = new xmldom.DOMParser().parseFromString(signedXml);
474+
475+
// Find the KeyInfo element and get the value of its Id attribute (ensuring it was generated)
476+
const keyInfoEl = select1Ns("/root/ds:Signature/ds:KeyInfo[@Id]", doc);
477+
isDomNode.assertIsElementNode(keyInfoEl);
478+
const idValue = keyInfoEl.getAttribute("Id");
479+
expect(idValue).to.be.a("string").that.is.not.empty;
480+
481+
// Find a Reference with URI=`#${idValue}`
482+
const uri = `#${idValue}`;
483+
const referenceEl = select1Ns(
484+
`/root/ds:Signature/ds:SignedInfo/ds:Reference[@URI='${uri}']`,
485+
doc,
486+
);
487+
isDomNode.assertIsElementNode(referenceEl);
488+
489+
// Verify that the signature is valid
490+
const { valid, errorMessage } = checkSignature(signedXml, doc);
491+
expect(valid, errorMessage).to.be.true;
492+
});
369493
});
370494

371495
describe("XAdES Object support in XML signatures", function () {

test/signature-unit-tests.spec.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1418,4 +1418,35 @@ describe("Signature unit tests", function () {
14181418
/the following xpath cannot be signed because it was not found/,
14191419
);
14201420
});
1421+
1422+
it("should sign references when the Id attribute is prefixed", () => {
1423+
const xml = '<root><x xmlns:ns="urn:example" ns:Id="unique-id"/></root>';
1424+
const sig = new SignedXml({
1425+
privateKey: fs.readFileSync("./test/static/client.pem"),
1426+
canonicalizationAlgorithm: "http://www.w3.org/2001/10/xml-exc-c14n#",
1427+
signatureAlgorithm: "http://www.w3.org/2000/09/xmldsig#rsa-sha1",
1428+
});
1429+
1430+
sig.addReference({
1431+
xpath: "//*[local-name(.)='x']",
1432+
digestAlgorithm: "http://www.w3.org/2000/09/xmldsig#sha1",
1433+
transforms: ["http://www.w3.org/2001/10/xml-exc-c14n#"],
1434+
});
1435+
1436+
sig.computeSignature(xml);
1437+
const signedXml = sig.getSignedXml();
1438+
1439+
const doc = new xmldom.DOMParser().parseFromString(signedXml);
1440+
const referenceElements = xpath.select("//*[local-name(.)='Reference']", doc);
1441+
isDomNode.assertIsArrayOfNodes(referenceElements);
1442+
expect(referenceElements.length, "Reference element should exist").to.equal(1);
1443+
1444+
const referenceElement = referenceElements[0];
1445+
isDomNode.assertIsElementNode(referenceElement);
1446+
1447+
const uriAttribute = referenceElement.getAttribute("URI");
1448+
expect(uriAttribute, "Reference element should have the correct URI attribute value").to.equal(
1449+
"#unique-id",
1450+
);
1451+
});
14211452
});

0 commit comments

Comments
 (0)