Skip to content

Commit c3e31a0

Browse files
committed
feat: add certificate truststore validation, certificate expiration, refactor XmlDSigValidator interface, reuse SignedXml
1 parent 414ec7c commit c3e31a0

12 files changed

+708
-106
lines changed

example/xmldsig-validator-example.js

Lines changed: 43 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,13 @@ function validateStandardExample(signedXml) {
2424

2525
// Create a validator with configuration
2626
const validator = new XmlDSigValidator({
27-
publicCert: fs.readFileSync("./test/static/client_public.pem"),
27+
keySelector: {
28+
publicCert: fs.readFileSync("./test/static/client_public.pem"),
29+
},
2830
throwOnError: false, // Return errors in result instead of throwing
29-
maxTransforms: 5, // Allow up to 5 transforms per reference
31+
security: {
32+
maxTransforms: 5, // Allow up to 5 transforms per reference
33+
},
3034
});
3135

3236
try {
@@ -55,10 +59,14 @@ function validateWithCustomIdAttributesExample(signedXml) {
5559

5660
// Create a validator with custom ID attributes
5761
const validator = new XmlDSigValidator({
58-
publicCert: fs.readFileSync("./test/static/client_public.pem"),
62+
keySelector: {
63+
publicCert: fs.readFileSync("./test/static/client_public.pem"),
64+
},
5965
idAttributeQNames: ["customId", "id", "Id"], // Same order as used in signing
6066
throwOnError: false,
61-
maxTransforms: 3,
67+
security: {
68+
maxTransforms: 3,
69+
},
6270
});
6371

6472
try {
@@ -80,13 +88,17 @@ function validateWSSecurityModeExample(signedXml) {
8088

8189
// Create a validator with WS-Security mode
8290
const validator = new XmlDSigValidator({
83-
publicCert: fs.readFileSync("./test/static/client_public.pem"),
91+
keySelector: {
92+
publicCert: fs.readFileSync("./test/static/client_public.pem"),
93+
},
8494
idAttributeQNames: ["wsu:Id"],
8595
namespaceMap: {
8696
wsu: "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd",
8797
},
8898
throwOnError: false,
89-
maxTransforms: 4,
99+
security: {
100+
maxTransforms: 4,
101+
},
90102
implicitTransforms: [Algorithms.transform.EXCLUSIVE_C14N], // Default transforms
91103
});
92104

@@ -112,14 +124,18 @@ function validateWithKeyInfoCertExample(signedXml) {
112124

113125
// Create a validator with getCertFromKeyInfo function
114126
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");
127+
keySelector: {
128+
getCertFromKeyInfo: (keyInfo) => {
129+
console.log("Extracting certificate from KeyInfo...");
130+
// In a real scenario, you would extract the certificate from KeyInfo
131+
// For this example, we'll just return the test certificate
132+
return fs.readFileSync("./test/static/client_public.pem", "utf8");
133+
},
120134
},
121135
throwOnError: false,
122-
maxTransforms: 6,
136+
security: {
137+
maxTransforms: 6,
138+
},
123139
});
124140

125141
try {
@@ -142,7 +158,9 @@ function validateWithCertificateOverrideExample(signedXml) {
142158
// Create a validator with a specific certificate
143159
const specificCert = fs.readFileSync("./test/static/client_public.pem");
144160
const validator = new XmlDSigValidator({
145-
publicCert: specificCert,
161+
keySelector: {
162+
publicCert: specificCert,
163+
},
146164
throwOnError: false,
147165
});
148166

@@ -164,7 +182,9 @@ function validateReusableExample(signedXml) {
164182
console.log("\n=== Reusable Validator Example ===");
165183

166184
const validator = new XmlDSigValidator({
167-
publicCert: fs.readFileSync("./test/static/client_public.pem"),
185+
keySelector: {
186+
publicCert: fs.readFileSync("./test/static/client_public.pem"),
187+
},
168188
throwOnError: false,
169189
});
170190

@@ -195,7 +215,9 @@ function validateMultipleSignaturesExample() {
195215
const signedTwice = multipleSignaturesExample();
196216

197217
const validator = new XmlDSigValidator({
198-
publicCert: fs.readFileSync("./test/static/client_public.pem"),
218+
keySelector: {
219+
publicCert: fs.readFileSync("./test/static/client_public.pem"),
220+
},
199221
});
200222

201223
// This will fail because multiple signatures are present
@@ -220,7 +242,9 @@ function validateErrorHandlingExample() {
220242
console.log("\n=== Error Handling Example ===");
221243

222244
const validator = new XmlDSigValidator({
223-
publicCert: fs.readFileSync("./test/static/client_public.pem"),
245+
keySelector: {
246+
publicCert: fs.readFileSync("./test/static/client_public.pem"),
247+
},
224248
throwOnError: false, // Don't throw, return errors in result
225249
});
226250

@@ -240,7 +264,9 @@ function validateErrorHandlingExample() {
240264

241265
// Test with throwOnError: true
242266
const throwingValidator = new XmlDSigValidator({
243-
publicCert: fs.readFileSync("./test/static/client_public.pem"),
267+
keySelector: {
268+
publicCert: fs.readFileSync("./test/static/client_public.pem"),
269+
},
244270
throwOnError: true,
245271
});
246272

src/signed-xml.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ export class SignedXml {
5555
throw new Error("Not implemented");
5656
},
5757
};
58+
private maxTransforms: number | null;
5859
implicitTransforms: ReadonlyArray<CanonicalizationOrTransformAlgorithmType> = [];
5960
keyInfoAttributes: { [attrName: string]: string } = {};
6061
getKeyInfoContent = SignedXml.getKeyInfoContent;
@@ -146,6 +147,7 @@ export class SignedXml {
146147
signatureAlgorithm,
147148
canonicalizationAlgorithm,
148149
inclusiveNamespacesPrefixList,
150+
maxTransforms,
149151
implicitTransforms,
150152
keyInfoAttributes,
151153
getKeyInfoContent,
@@ -170,6 +172,7 @@ export class SignedXml {
170172
} else if (utils.isArrayHasLength(inclusiveNamespacesPrefixList)) {
171173
this.inclusiveNamespacesPrefixList = inclusiveNamespacesPrefixList;
172174
}
175+
this.maxTransforms = maxTransforms ?? null;
173176
this.implicitTransforms = implicitTransforms ?? this.implicitTransforms;
174177
this.keyInfoAttributes = keyInfoAttributes ?? this.keyInfoAttributes;
175178
this.getKeyInfoContent = getKeyInfoContent ?? this.getKeyInfoContent;
@@ -830,6 +833,14 @@ export class SignedXml {
830833
? refNode.getAttribute("URI") || undefined
831834
: undefined;
832835

836+
if (this.maxTransforms) {
837+
if (transforms.length > this.maxTransforms) {
838+
throw new Error(
839+
`Number of transforms (${transforms.length}) exceeds the maximum allowed (${this.maxTransforms})`,
840+
);
841+
}
842+
}
843+
833844
this.addReference({
834845
transforms,
835846
digestAlgorithm: digestAlgo,

src/types.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ export interface ObjectAttributes {
4242
[key: string]: string | undefined;
4343
}
4444

45+
export type KeySelectorFunction = (keyInfo?: Node | null) => string | null;
46+
4547
/**
4648
* Options for the SignedXml constructor.
4749
*/
@@ -55,10 +57,11 @@ export interface SignedXmlOptions {
5557
signatureAlgorithm?: SignatureAlgorithmType;
5658
canonicalizationAlgorithm?: CanonicalizationAlgorithmType;
5759
inclusiveNamespacesPrefixList?: string | string[];
60+
maxTransforms?: number | null;
5861
implicitTransforms?: ReadonlyArray<CanonicalizationOrTransformAlgorithmType>;
5962
keyInfoAttributes?: Record<string, string>;
6063
getKeyInfoContent?(args?: GetKeyInfoContentArgs): string | null;
61-
getCertFromKeyInfo?(keyInfo?: Node | null): string | null;
64+
getCertFromKeyInfo?: KeySelectorFunction;
6265
objects?: Array<{ content: string; attributes?: ObjectAttributes }>;
6366
}
6467

0 commit comments

Comments
 (0)