Skip to content
Closed
Show file tree
Hide file tree
Changes from 3 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
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@ To sign xml documents:
- `transforms` - an array of [transform algorithms](#canonicalization-and-transformation-algorithms), the referenced element will be transformed for each value in the array
- `digestAlgorithm` - one of the supported [hashing algorithms](#hashing-algorithms)
- `computeSignature(xml, [options])` - compute the signature of the given xml where:
- `xml` - a string containing a xml document
- `xml` - a string containing an xml document or document object (like an [xmldom](https://github.com/xmldom/xmldom) document)
- `options` - an object with the following properties:
- `prefix` - adds this value as a prefix for the generated signature tags
- `attrs` - a hash of attributes and values `attrName: value` to add to the signature root node
Expand All @@ -287,7 +287,8 @@ To verify xml documents:

- `loadSignature(signatureXml)` - loads the signature where:
- `signatureXml` - a string or node object (like an [xmldom](https://github.com/xmldom/xmldom) node) containing the xml representation of the signature
- `checkSignature(xml)` - validates the given xml document and returns `true` if the validation was successful
- `checkSignature(xml)` - validates the given xml document and returns `true` if the validation was successful where:
- `xml` - a string or node object (like an [xmldom](https://github.com/xmldom/xmldom) node) containing the xml representation of the document

## Customizing Algorithms

Expand Down
30 changes: 14 additions & 16 deletions src/signed-xml.ts
Original file line number Diff line number Diff line change
Expand Up @@ -245,26 +245,25 @@ export class SignedXml {
* @returns `true` if the signature is valid
* @throws Error if no key info resolver is provided.
*/
checkSignature(xml: string): boolean;
checkSignature(xml: Document | string): boolean;
/**
* Validates the signature of the provided XML document synchronously using the configured key info provider.
*
* @param xml The XML document containing the signature to be validated.
* @param callback Callback function to handle the validation result asynchronously.
* @throws Error if the last parameter is provided and is not a function, or if no key info resolver is provided.
*/
checkSignature(xml: string, callback: (error: Error | null, isValid?: boolean) => void): void;
checkSignature(xml: Document | string, callback: (error: Error | null, isValid?: boolean) => void): void;
checkSignature(
xml: string,
xml: Document | string,
callback?: (error: Error | null, isValid?: boolean) => void,
): unknown {
if (callback != null && typeof callback !== "function") {
throw new Error("Last parameter must be a callback function");
}

this.signedXml = xml;

const doc = new xmldom.DOMParser().parseFromString(xml);
const doc = typeof xml === "string" ? new xmldom.DOMParser().parseFromString(xml) : xml;
this.signedXml = doc.toString();

// Reset the references as only references from our re-parsed signedInfo node can be trusted
this.references = [];
Expand Down Expand Up @@ -347,7 +346,7 @@ export class SignedXml {

// Check the signature verification to know whether to reset signature value or not.
const sigRes = signer.verifySignature(unverifiedSignedInfoCanon, key, this.signatureValue);
if (sigRes === true) {
if (sigRes) {
if (callback) {
callback(null, true);
} else {
Expand Down Expand Up @@ -845,11 +844,10 @@ export class SignedXml {
* Compute the signature of the given XML (using the already defined settings).
*
* @param xml The XML to compute the signature for.
* @param callback A callback function to handle the signature computation asynchronously.
* @returns void
* @throws TypeError If the xml can not be parsed.
*/
computeSignature(xml: string): void;
computeSignature(xml: Document | string): void;

/**
* Compute the signature of the given XML (using the already defined settings).
Expand All @@ -859,35 +857,35 @@ export class SignedXml {
* @returns void
* @throws TypeError If the xml can not be parsed.
*/
computeSignature(xml: string, callback: ErrorFirstCallback<SignedXml>): void;
computeSignature(xml: Document | string, callback: ErrorFirstCallback<SignedXml>): void;

/**
* Compute the signature of the given XML (using the already defined settings).
*
* @param xml The XML to compute the signature for.
* @param opts An object containing options for the signature computation.
* @param options An object containing options for the signature computation.
* @returns If no callback is provided, returns `this` (the instance of SignedXml).
* @throws TypeError If the xml can not be parsed, or Error if there were invalid options passed.
*/
computeSignature(xml: string, options: ComputeSignatureOptions): void;
computeSignature(xml: Document | string, options: ComputeSignatureOptions): void;

/**
* Compute the signature of the given XML (using the already defined settings).
*
* @param xml The XML to compute the signature for.
* @param opts An object containing options for the signature computation.
* @param options An object containing options for the signature computation.
* @param callback A callback function to handle the signature computation asynchronously.
* @returns void
* @throws TypeError If the xml can not be parsed, or Error if there were invalid options passed.
*/
computeSignature(
xml: string,
xml: Document | string,
options: ComputeSignatureOptions,
callback: ErrorFirstCallback<SignedXml>,
): void;

computeSignature(
xml: string,
xml: Document | string,
options?: ComputeSignatureOptions | ErrorFirstCallback<SignedXml>,
callbackParam?: ErrorFirstCallback<SignedXml>,
): void {
Expand All @@ -899,8 +897,8 @@ export class SignedXml {
callback = callbackParam as ErrorFirstCallback<SignedXml>;
options = (options ?? {}) as ComputeSignatureOptions;
}
const doc = typeof xml === "string" ? new xmldom.DOMParser().parseFromString(xml) : xml;

const doc = new xmldom.DOMParser().parseFromString(xml);
let xmlNsAttr = "xmlns";
const signatureAttrs: string[] = [];
let currentPrefix: string;
Expand Down
66 changes: 63 additions & 3 deletions test/signature-unit-tests.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -697,6 +697,66 @@ describe("Signature unit tests", function () {
expect(expected, "wrong signature format").to.equal(signedXml);
});

it("signer creates correct signature values when receiving a DOM", function () {
const xml = new xmldom.DOMParser().parseFromString('<root><x xmlns="ns" Id="_0"></x><y attr="value" Id="_1"></y><z><w Id="_2"></w></z></root>');
const sig = new SignedXml();
sig.privateKey = fs.readFileSync("./test/static/client.pem");

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.addReference({
xpath: "//*[local-name(.)='y']",
digestAlgorithm: "http://www.w3.org/2000/09/xmldsig#sha1",
transforms: ["http://www.w3.org/2001/10/xml-exc-c14n#"],
});
sig.addReference({
xpath: "//*[local-name(.)='w']",
digestAlgorithm: "http://www.w3.org/2000/09/xmldsig#sha1",
transforms: ["http://www.w3.org/2001/10/xml-exc-c14n#"],
});

sig.canonicalizationAlgorithm = "http://www.w3.org/2001/10/xml-exc-c14n#";
sig.signatureAlgorithm = "http://www.w3.org/2000/09/xmldsig#rsa-sha1";
sig.computeSignature(xml);
const signedXml = sig.getSignedXml();
const expected =
'<root><x xmlns="ns" Id="_0"/><y attr="value" Id="_1"/><z><w Id="_2"/></z>' +
'<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">' +
"<SignedInfo>" +
'<CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>' +
'<SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>' +
'<Reference URI="#_0">' +
"<Transforms>" +
'<Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/></Transforms>' +
'<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>' +
"<DigestValue>b5GCZ2xpP5T7tbLWBTkOl4CYupQ=</DigestValue>" +
"</Reference>" +
'<Reference URI="#_1">' +
"<Transforms>" +
'<Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>' +
"</Transforms>" +
'<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>' +
"<DigestValue>4Pq/sBri+AyOtxtSFsPSOyylyzk=</DigestValue>" +
"</Reference>" +
'<Reference URI="#_2">' +
"<Transforms>" +
'<Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>' +
"</Transforms>" +
'<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>' +
"<DigestValue>6I7SDu1iV2YOajTlf+iMLIBfLnE=</DigestValue>" +
"</Reference>" +
"</SignedInfo>" +
"<SignatureValue>NejzGB9MDUddKCt3GL2vJhEd5q6NBuhLdQc3W4bJI5q34hk7Hk6zBRoW3OliX+/f7Hpi9y0INYoqMSUfrsAVm3IuPzUETKlI6xiNZo07ULRj1DwxRo6cU66ar1EKUQLRuCZas795FjB8jvUI2lyhcax/00uMJ+Cjf4bwAQ+9gOQ=</SignatureValue>" +
"</Signature>" +
"</root>";

expect(expected, "wrong signature format").to.equal(signedXml);
});


it("signer creates correct signature values using async callback", function () {
class DummySignatureAlgorithm {
verifySignature = function () {
Expand Down Expand Up @@ -813,7 +873,7 @@ describe("Signature unit tests", function () {
return fs.readFileSync("./test/static/client.pem", "latin1");
};

const checkedSignature = sig.checkSignature(xml);
const checkedSignature = sig.checkSignature(toString ? doc.toString() : doc );
expect(checkedSignature).to.be.true;

/* eslint-disable-next-line deprecation/deprecation */
Expand Down Expand Up @@ -871,7 +931,6 @@ describe("Signature unit tests", function () {
const sig = new SignedXml({ idMode });
sig.publicCert = fs.readFileSync("./test/static/client_public.pem");
sig.loadSignature(node);

return sig;
}

Expand All @@ -895,8 +954,9 @@ describe("Signature unit tests", function () {
function throwsValidatingSignature(file: string, idMode?: "wssecurity") {
const xml = fs.readFileSync(file).toString();
const sig = loadSignature(xml, idMode);
const doc = new xmldom.DOMParser().parseFromString(xml);
expect(
() => sig.checkSignature(xml),
() => sig.checkSignature(doc),
"expected an error to be thrown because signatures couldn't be checked for validity",
).to.throw();
expect(sig.getSignedReferences().length).to.equal(0);
Expand Down
Loading