Skip to content

Commit ca57699

Browse files
committed
refactor: rename XmlSigner to XmlDSigSigner, update examples and tests
1 parent ee0beef commit ca57699

11 files changed

+1804
-2020
lines changed

example/new-api-example.js

Lines changed: 0 additions & 606 deletions
This file was deleted.

example/xmldsig-signer-example.js

Lines changed: 406 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 306 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,306 @@
1+
const fs = require("fs");
2+
3+
// Try to import from local build first, fallback to published package
4+
let XmlSignerFactory, XmlDSigValidator, Algorithms;
5+
try {
6+
({ XmlSignerFactory, XmlDSigValidator, Algorithms } = require("../lib/index"));
7+
} catch (error) {
8+
({ XmlSignerFactory, XmlDSigValidator, Algorithms } = require("xml-crypto"));
9+
}
10+
11+
// Import the signer examples to get signed XML documents
12+
const {
13+
signWithXPathExample,
14+
signWithUriExample,
15+
signWithCustomIdAttributesExample,
16+
signWithWSSecurityModeExample,
17+
signWithAllAttributesExample,
18+
multipleSignaturesExample,
19+
} = require("./xmldsig-signer-example");
20+
21+
// Example: Standard validation
22+
function validateStandardExample(signedXml) {
23+
console.log("\n=== Standard Validation Example ===");
24+
25+
// Create a validator with configuration
26+
const validator = new XmlDSigValidator({
27+
publicCert: fs.readFileSync("./test/static/client_public.pem"),
28+
throwOnError: false, // Return errors in result instead of throwing
29+
maxTransforms: 5, // Allow up to 5 transforms per reference
30+
});
31+
32+
try {
33+
const result = validator.validate(signedXml);
34+
35+
console.log("Standard validation result:", result.valid);
36+
if (!result.valid) {
37+
console.log("Validation error:", result.error);
38+
}
39+
40+
// Get the signed content (only available after successful validation)
41+
if (result.valid && result.signedReferences) {
42+
console.log("Signed references count:", result.signedReferences.length);
43+
result.signedReferences.forEach((ref, index) => {
44+
console.log(`Signed reference ${index + 1} length:`, ref.length);
45+
});
46+
}
47+
} catch (error) {
48+
console.error("Standard validation failed:", error.message);
49+
}
50+
}
51+
52+
// Example: Validation with custom ID attributes
53+
function validateWithCustomIdAttributesExample(signedXml) {
54+
console.log("\n=== Custom ID Attributes Validation Example ===");
55+
56+
// Create a validator with custom ID attributes
57+
const validator = new XmlDSigValidator({
58+
publicCert: fs.readFileSync("./test/static/client_public.pem"),
59+
idAttributeQNames: ["customId", "id", "Id"], // Same order as used in signing
60+
throwOnError: false,
61+
maxTransforms: 3,
62+
});
63+
64+
try {
65+
const result = validator.validate(signedXml);
66+
console.log("Custom ID attributes validation result:", result.valid);
67+
if (result.valid && result.signedReferences) {
68+
console.log("Custom ID validation - signed references:", result.signedReferences.length);
69+
} else if (!result.valid) {
70+
console.log("Custom ID validation error:", result.error);
71+
}
72+
} catch (error) {
73+
console.error("Custom ID attributes validation failed:", error.message);
74+
}
75+
}
76+
77+
// Example: WS-Security mode validation
78+
function validateWSSecurityModeExample(signedXml) {
79+
console.log("\n=== WS-Security Mode Validation Example ===");
80+
81+
// Create a validator with WS-Security mode
82+
const validator = new XmlDSigValidator({
83+
publicCert: fs.readFileSync("./test/static/client_public.pem"),
84+
idAttributeQNames: ["wsu:Id"],
85+
namespaceMap: {
86+
wsu: "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd",
87+
},
88+
throwOnError: false,
89+
maxTransforms: 4,
90+
implicitTransforms: [Algorithms.transform.EXCLUSIVE_C14N], // Default transforms
91+
});
92+
93+
try {
94+
const result = validator.validate(signedXml);
95+
console.log("WS-Security mode validation result:", result.valid);
96+
if (result.valid && result.signedReferences) {
97+
console.log("WS-Security validation - signed references:", result.signedReferences.length);
98+
result.signedReferences.forEach((ref, index) => {
99+
console.log(`WS-Security reference ${index + 1} length:`, ref.length);
100+
});
101+
} else if (!result.valid) {
102+
console.log("WS-Security validation error:", result.error);
103+
}
104+
} catch (error) {
105+
console.error("WS-Security mode validation failed:", error.message);
106+
}
107+
}
108+
109+
// Example: Validation with getCertFromKeyInfo
110+
function validateWithKeyInfoCertExample(signedXml) {
111+
console.log("\n=== KeyInfo Certificate Extraction Validation Example ===");
112+
113+
// Create a validator with getCertFromKeyInfo function
114+
const validator = new XmlDSigValidator({
115+
getCertFromKeyInfo: (keyInfo) => {
116+
console.log("Extracting certificate from KeyInfo...");
117+
// In a real scenario, you would extract the certificate from KeyInfo
118+
// For this example, we'll just return the test certificate
119+
return fs.readFileSync("./test/static/client_public.pem", "utf8");
120+
},
121+
throwOnError: false,
122+
maxTransforms: 6,
123+
});
124+
125+
try {
126+
const result = validator.validate(signedXml);
127+
console.log("KeyInfo cert extraction validation result:", result.valid);
128+
if (result.valid && result.signedReferences) {
129+
console.log("KeyInfo validation - signed references:", result.signedReferences.length);
130+
} else if (!result.valid) {
131+
console.log("KeyInfo validation error:", result.error);
132+
}
133+
} catch (error) {
134+
console.error("KeyInfo cert extraction validation failed:", error.message);
135+
}
136+
}
137+
138+
// Example: Validation with certificate override
139+
function validateWithCertificateOverrideExample(signedXml) {
140+
console.log("\n=== Certificate Override Validation Example ===");
141+
142+
// Create a validator with a specific certificate
143+
const specificCert = fs.readFileSync("./test/static/client_public.pem");
144+
const validator = new XmlDSigValidator({
145+
publicCert: specificCert,
146+
throwOnError: false,
147+
});
148+
149+
try {
150+
const result = validator.validate(signedXml);
151+
console.log("Certificate override validation result:", result.valid);
152+
if (result.valid && result.signedReferences) {
153+
console.log("Certificate override - signed references:", result.signedReferences.length);
154+
} else if (!result.valid) {
155+
console.log("Certificate override validation error:", result.error);
156+
}
157+
} catch (error) {
158+
console.error("Certificate override validation failed:", error.message);
159+
}
160+
}
161+
162+
// Example: Reusable validator
163+
function validateReusableExample(signedXml) {
164+
console.log("\n=== Reusable Validator Example ===");
165+
166+
const validator = new XmlDSigValidator({
167+
publicCert: fs.readFileSync("./test/static/client_public.pem"),
168+
throwOnError: false,
169+
});
170+
171+
try {
172+
// First validation
173+
const result1 = validator.validate(signedXml);
174+
console.log("First validation result:", result1.valid);
175+
if (result1.valid && result1.signedReferences) {
176+
console.log("First validation - signed references count:", result1.signedReferences.length);
177+
}
178+
179+
// Second validation with same validator (demonstrating reusability)
180+
const result2 = validator.validate(signedXml);
181+
console.log("Second validation result:", result2.valid);
182+
if (result2.valid && result2.signedReferences) {
183+
console.log("Second validation - signed references count:", result2.signedReferences.length);
184+
}
185+
} catch (error) {
186+
console.error("Reusable validation failed:", error.message);
187+
}
188+
}
189+
190+
// Example: Multiple signatures validation
191+
function validateMultipleSignaturesExample() {
192+
console.log("\n=== Multiple Signatures Validation Example ===");
193+
194+
// Get XML with multiple signatures
195+
const signedTwice = multipleSignaturesExample();
196+
197+
const validator = new XmlDSigValidator({
198+
publicCert: fs.readFileSync("./test/static/client_public.pem"),
199+
});
200+
201+
// This will fail because multiple signatures are present
202+
const result1 = validator.validate(signedTwice);
203+
console.log("Validation without specifying signature node:", result1.valid);
204+
if (!result1.valid) {
205+
console.log("Error:", result1.error);
206+
}
207+
208+
// Validate a specific signature by passing the signature node
209+
const { DOMParser } = require("@xmldom/xmldom");
210+
const xpath = require("xpath");
211+
const doc = new DOMParser().parseFromString(signedTwice);
212+
const firstSignature = xpath.select1("//*[local-name(.)='Signature'][@Id='sig1']", doc);
213+
214+
const result2 = validator.validate(signedTwice, firstSignature);
215+
console.log("Validation with specific signature node:", result2.valid);
216+
}
217+
218+
// Example: Error handling
219+
function validateErrorHandlingExample() {
220+
console.log("\n=== Error Handling Example ===");
221+
222+
const validator = new XmlDSigValidator({
223+
publicCert: fs.readFileSync("./test/static/client_public.pem"),
224+
throwOnError: false, // Don't throw, return errors in result
225+
});
226+
227+
// Test with invalid XML
228+
const result1 = validator.validate("<invalid>xml");
229+
console.log("Invalid XML validation result:", result1.valid);
230+
if (!result1.valid) {
231+
console.log("Invalid XML error:", result1.error);
232+
}
233+
234+
// Test with XML without signature
235+
const result2 = validator.validate("<root><data>no signature</data></root>");
236+
console.log("No signature validation result:", result2.valid);
237+
if (!result2.valid) {
238+
console.log("No signature error:", result2.error);
239+
}
240+
241+
// Test with throwOnError: true
242+
const throwingValidator = new XmlDSigValidator({
243+
publicCert: fs.readFileSync("./test/static/client_public.pem"),
244+
throwOnError: true,
245+
});
246+
247+
try {
248+
throwingValidator.validate("<root><data>no signature</data></root>");
249+
} catch (error) {
250+
console.log("Caught thrown error:", error.message);
251+
}
252+
}
253+
254+
// Run the examples
255+
if (require.main === module) {
256+
console.log("🔐 XML Digital Signature Validator Examples\n");
257+
258+
// Generate signed XML documents from signer examples
259+
console.log("Generating signed XML documents for validation...\n");
260+
261+
const xpathSignedXml = signWithXPathExample();
262+
const uriSignedXml = signWithUriExample();
263+
const customIdSignedXml = signWithCustomIdAttributesExample();
264+
const wsSecuritySignedXml = signWithWSSecurityModeExample();
265+
const comprehensiveSignedXml = signWithAllAttributesExample();
266+
267+
// Validation examples
268+
if (xpathSignedXml) {
269+
validateStandardExample(xpathSignedXml);
270+
validateWithKeyInfoCertExample(xpathSignedXml);
271+
validateWithCertificateOverrideExample(xpathSignedXml);
272+
validateReusableExample(xpathSignedXml);
273+
}
274+
275+
if (uriSignedXml) {
276+
validateStandardExample(uriSignedXml);
277+
}
278+
279+
if (customIdSignedXml) {
280+
validateWithCustomIdAttributesExample(customIdSignedXml);
281+
}
282+
283+
if (wsSecuritySignedXml) {
284+
validateWSSecurityModeExample(wsSecuritySignedXml);
285+
}
286+
287+
if (comprehensiveSignedXml) {
288+
validateStandardExample(comprehensiveSignedXml);
289+
validateReusableExample(comprehensiveSignedXml);
290+
}
291+
292+
// Additional validation examples
293+
validateMultipleSignaturesExample();
294+
validateErrorHandlingExample();
295+
}
296+
297+
module.exports = {
298+
validateStandardExample,
299+
validateWithCustomIdAttributesExample,
300+
validateWSSecurityModeExample,
301+
validateWithKeyInfoCertExample,
302+
validateWithCertificateOverrideExample,
303+
validateReusableExample,
304+
validateMultipleSignaturesExample,
305+
validateErrorHandlingExample,
306+
};

src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ export {
44
ExclusiveCanonicalizationWithComments,
55
} from "./exclusive-canonicalization";
66
export { SignedXml } from "./signed-xml";
7-
export { XmlSigner, XmlSignerFactory } from "./xml-signer";
7+
export { XmlDSigSigner } from "./xmldsig-signer";
88
export { XmlDSigValidator } from "./xmldsig-validator";
99
export { Algorithms } from "./algorithms";
1010
export * from "./types";

src/utils.ts

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -378,16 +378,6 @@ function buildXPathLiteral(value: string): string {
378378
.join(", ")})`;
379379
}
380380

381-
export function buildIdXPath(idAttributes: string[], idValue: string): string {
382-
if (idAttributes.length === 0) {
383-
throw new Error("No ID attributes provided for XPath generation.");
384-
}
385-
386-
const idLiteral = buildXPathLiteral(idValue);
387-
const conditions = idAttributes.map((attr) => `local-name()=${buildXPathLiteral(attr)}`);
388-
return `//*[@*[${conditions.join(" or ")}]=${idLiteral}]`;
389-
}
390-
391381
export function splitQName(qName: string): [string | undefined, string] {
392382
if (qName.includes(":")) {
393383
const [prefix, localName] = qName.split(":", 2);
@@ -413,3 +403,25 @@ export function resolveQName(
413403
}
414404
return [prefix, localName, namespaceURI];
415405
}
406+
407+
export function buildIdXPathWithNamespaces(
408+
idAttributeQNames: string[],
409+
idValue: string,
410+
namespaceMap?: Record<string, string>,
411+
): string {
412+
if (idAttributeQNames.length === 0) {
413+
throw new Error("No ID attributes provided for XPath generation.");
414+
}
415+
const idLiteral = buildXPathLiteral(idValue);
416+
const conditions: string[] = [];
417+
for (const qName of idAttributeQNames) {
418+
const [, localName, namespaceURI] = resolveQName(qName, namespaceMap);
419+
if (namespaceURI) {
420+
conditions.push(`local-name()="${localName}" and namespace-uri()="${namespaceURI}"`);
421+
} else {
422+
conditions.push(`local-name()="${localName}"`);
423+
}
424+
}
425+
426+
return `//*[@*[${conditions.join(" or ")}]=${idLiteral}]`;
427+
}

0 commit comments

Comments
 (0)