xml-crypto-next 7.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ (The MIT License)
2
+
3
+ Copyright (c) Yaron Naveh <yaronn01@gmail.com>
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ 'Software'), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,592 @@
1
+ # xml-crypto
2
+
3
+ ![Build Status](https://github.com/node-saml/xml-crypto/workflows/Test%20Status/badge.svg)
4
+ [![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat-square)](https://github.com/prettier/prettier)
5
+ [![codecov](https://codecov.io/gh/node-saml/xml-crypto/branch/master/graph/badge.svg?token=PQWCMBWBFB)](https://codecov.io/gh/node-saml/xml-crypto)
6
+ [![DeepScan grade](https://deepscan.io/api/teams/17569/projects/30525/branches/981134/badge/grade.svg)](https://deepscan.io/dashboard#view=project&tid=17569&pid=30525&bid=981134)
7
+
8
+ [![NPM](https://nodei.co/npm/xml-crypto.png?downloads=true&downloadRank=true&stars=true)](https://nodei.co/npm/xml-crypto)
9
+
10
+ ## Sponsors
11
+
12
+ ![workos](https://github.com/workos.png?size=30) [workos](https://github.com/workos)
13
+
14
+ ![stytchauth](https://github.com/stytchauth.png?size=30) [stytchauth](https://github.com/stytchauth)
15
+
16
+ ## Upgrading
17
+
18
+ The `.getReferences()` AND the `.references` APIs are deprecated.
19
+ Please do not attempt to access them. The content in them should be treated as unsigned.
20
+
21
+ Instead, we strongly encourage users to migrate to the `.getSignedReferences()` API. See the [Verifying XML document](#verifying-xml-documents) section
22
+ We understand that this may take a lot of efforts to migrate, feel free to ask for help.
23
+ This will help prevent future XML signature wrapping attacks.
24
+
25
+ ## Supported Algorithms
26
+
27
+ ### Canonicalization and Transformation Algorithms
28
+
29
+ - Canonicalization <http://www.w3.org/TR/2001/REC-xml-c14n-20010315>
30
+ - Canonicalization with comments <http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments>
31
+ - Exclusive Canonicalization <http://www.w3.org/2001/10/xml-exc-c14n#>
32
+ - Exclusive Canonicalization with comments <http://www.w3.org/2001/10/xml-exc-c14n#WithComments>
33
+ - Enveloped Signature transform <http://www.w3.org/2000/09/xmldsig#enveloped-signature>
34
+
35
+ ### Hashing Algorithms
36
+
37
+ - SHA1 digests <http://www.w3.org/2000/09/xmldsig#sha1>
38
+ - SHA256 digests <http://www.w3.org/2001/04/xmlenc#sha256>
39
+ - SHA512 digests <http://www.w3.org/2001/04/xmlenc#sha512>
40
+
41
+ ### Signature Algorithms
42
+
43
+ - RSA-SHA1 <http://www.w3.org/2000/09/xmldsig#rsa-sha1>
44
+ - RSA-SHA256 <http://www.w3.org/2001/04/xmldsig-more#rsa-sha256>
45
+ - RSA-SHA256 with MGF1 <http://www.w3.org/2007/05/xmldsig-more#sha256-rsa-MGF1>
46
+ - RSA-SHA512 <http://www.w3.org/2001/04/xmldsig-more#rsa-sha512>
47
+
48
+ HMAC-SHA1 is also available but it is disabled by default
49
+
50
+ - HMAC-SHA1 <http://www.w3.org/2000/09/xmldsig#hmac-sha1>
51
+
52
+ to enable HMAC-SHA1, call `enableHMAC()` on your instance of `SignedXml`.
53
+
54
+ This will enable HMAC and disable digital signature algorithms. Due to key
55
+ confusion issues, it is risky to have both HMAC-based and public key digital
56
+ signature algorithms enabled at same time.
57
+
58
+ [You are able to extend xml-crypto with custom algorithms.](#customizing-algorithms)
59
+
60
+ ## Signing Xml documents
61
+
62
+ When signing a xml document you can pass the following options to the `SignedXml` constructor to customize the signature process:
63
+
64
+ - `privateKey` - **[required]** a `Buffer` or pem encoded `String` containing your private key
65
+ - `publicCert` - **[optional]** a `Buffer` or pem encoded `String` containing your public key
66
+ - `signatureAlgorithm` - **[required]** one of the supported [signature algorithms](#signature-algorithms). Ex: `sign.signatureAlgorithm = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"`
67
+ - `canonicalizationAlgorithm` - **[required]** one of the supported [canonicalization algorithms](#canonicalization-and-transformation-algorithms). Ex: `sign.canonicalizationAlgorithm = "http://www.w3.org/2001/10/xml-exc-c14n#WithComments"`
68
+
69
+ Use this code:
70
+
71
+ ```javascript
72
+ var SignedXml = require("xml-crypto").SignedXml,
73
+ fs = require("fs");
74
+
75
+ var xml = "<library>" + "<book>" + "<name>Harry Potter</name>" + "</book>" + "</library>";
76
+
77
+ var sig = new SignedXml({ privateKey: fs.readFileSync("client.pem") });
78
+ sig.addReference({
79
+ xpath: "//*[local-name(.)='book']",
80
+ digestAlgorithm: "http://www.w3.org/2000/09/xmldsig#sha1",
81
+ transforms: ["http://www.w3.org/2001/10/xml-exc-c14n#"],
82
+ });
83
+ sig.canonicalizationAlgorithm = "http://www.w3.org/2001/10/xml-exc-c14n#";
84
+ sig.signatureAlgorithm = "http://www.w3.org/2000/09/xmldsig#rsa-sha1";
85
+ sig.computeSignature(xml);
86
+ fs.writeFileSync("signed.xml", sig.getSignedXml());
87
+ ```
88
+
89
+ The result will be:
90
+
91
+ ```xml
92
+ <library>
93
+ <book Id="_0">
94
+ <name>Harry Potter</name>
95
+ </book>
96
+ <Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
97
+ <SignedInfo>
98
+ <CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
99
+ <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" />
100
+ <Reference URI="#_0">
101
+ <Transforms>
102
+ <Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
103
+ </Transforms>
104
+ <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
105
+ <DigestValue>cdiS43aFDQMnb3X8yaIUej3+z9Q=</DigestValue>
106
+ </Reference>
107
+ </SignedInfo>
108
+ <SignatureValue>vhWzpQyIYuncHUZV9W...[long base64 removed]...</SignatureValue>
109
+ </Signature>
110
+ </library>
111
+ ```
112
+
113
+ Note:
114
+
115
+ If you set the `publicCert` and the `getKeyInfoContent` properties, a `<KeyInfo></KeyInfo>` element with the public certificate will be generated in the signature:
116
+
117
+ ```xml
118
+ <Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
119
+ <SignedInfo>
120
+ ...[signature info removed]...
121
+ </SignedInfo>
122
+ <SignatureValue>vhWzpQyIYuncHUZV9W...[long base64 removed]...</SignatureValue>
123
+ <KeyInfo>
124
+ <X509Data>
125
+ <X509Certificate>MIIGYjCCBJagACCBN...[long base64 removed]...</X509Certificate>
126
+ </X509Data>
127
+ </KeyInfo>
128
+ </Signature>
129
+ ```
130
+
131
+ For `getKeyInfoContent`, a default implementation `SignedXml.getKeyInfoContent` is available.
132
+
133
+ To customize this see [customizing algorithms](#customizing-algorithms) for an example.
134
+
135
+ ## Verifying Xml documents
136
+
137
+ When verifying a xml document you can pass the following options to the `SignedXml` constructor to customize the verify process:
138
+
139
+ - `publicCert` - **[optional]** your certificate as a string, a string of multiple certs in PEM format, or a Buffer
140
+ - `privateKey` - **[optional]** your private key as a string or a Buffer - used for verifying symmetrical signatures (HMAC)
141
+
142
+ The certificate that will be used to check the signature will first be determined by calling `this.getCertFromKeyInfo()`, which function you can customize as you see fit. If that returns `null`, then `publicCert` is used. If that is `null`, then `privateKey` is used (for symmetrical signing applications).
143
+
144
+ Example:
145
+
146
+ ```javascript
147
+ new SignedXml({
148
+ publicCert: client_public_pem,
149
+ getCertFromKeyInfo: () => null,
150
+ });
151
+ ```
152
+
153
+ You can use any dom parser you want in your code (or none, depending on your usage). This sample uses [xmldom](https://github.com/xmldom/xmldom), so you should install it first:
154
+
155
+ ```shell
156
+ npm install @xmldom/xmldom
157
+ ```
158
+
159
+ Example:
160
+
161
+ ```javascript
162
+ var select = require("xml-crypto").xpath,
163
+ dom = require("@xmldom/xmldom").DOMParser,
164
+ SignedXml = require("xml-crypto").SignedXml,
165
+ fs = require("fs");
166
+
167
+ var xml = fs.readFileSync("signed.xml").toString();
168
+ var doc = new dom().parseFromString(xml);
169
+
170
+ // DO NOT attempt to parse whatever data object you have here in `doc`
171
+ // and then use it to verify the signature. This can lead to security issues.
172
+ // i.e. BAD: parseAssertion(doc),
173
+ // good: see below
174
+
175
+ var signature = select(
176
+ doc,
177
+ "//*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']",
178
+ )[0];
179
+ var sig = new SignedXml({ publicCert: fs.readFileSync("client_public.pem") });
180
+ sig.loadSignature(signature);
181
+ try {
182
+ var res = sig.checkSignature(xml);
183
+ } catch (ex) {
184
+ console.log(ex);
185
+ }
186
+ ```
187
+
188
+ In order to protect from some attacks we must check the content we want to use is the one that has been signed:
189
+
190
+ ```javascript
191
+ if (!res) {
192
+ throw "Invalid Signature";
193
+ }
194
+ // good: The XML Signature has been verified, meaning some subset of XML is verified.
195
+ var signedBytes = sig.getSignedReferences();
196
+
197
+ var authenticatedDoc = new dom().parseFromString(signedBytes[0]); // Take the first signed reference
198
+ // It is now safe to load SAML, obtain the assertion XML, or do whatever else is needed.
199
+ // Be sure to only use authenticated data.
200
+ let signedAssertionNode = extractAssertion(authenticatedDoc);
201
+ let parsedAssertion = parseAssertion(signedAssertionNode);
202
+
203
+ return parsedAssertion; // This the correctly verified signed Assertion
204
+
205
+ // BAD example: DO not use the .getReferences() API.
206
+ ```
207
+
208
+ Note:
209
+
210
+ The xml-crypto api requires you to supply it separately the xml signature ("&lt;Signature&gt;...&lt;/Signature&gt;", in loadSignature) and the signed xml (in checkSignature). The signed xml may or may not contain the signature in it, but you are still required to supply the signature separately.
211
+
212
+ ### Caring for Implicit transform
213
+
214
+ If you fail to verify signed XML, then one possible cause is that there are some hidden implicit transforms(#).
215
+ (#) Normalizing XML document to be verified. i.e. remove extra space within a tag, sorting attributes, importing namespace declared in ancestor nodes, etc.
216
+
217
+ The reason for these implicit transform might come from [complex xml signature specification](https://www.w3.org/TR/2002/REC-xmldsig-core-20020212),
218
+ which makes XML developers confused and then leads to incorrect implementation for signing XML document.
219
+
220
+ If you keep failing verification, it is worth trying to guess such a hidden transform and specify it to the option as below:
221
+
222
+ ```javascript
223
+ var options = {
224
+ implicitTransforms: ["http://www.w3.org/TR/2001/REC-xml-c14n-20010315"],
225
+ publicCert: fs.readFileSync("client_public.pem"),
226
+ };
227
+ var sig = new SignedXml(options);
228
+ sig.loadSignature(signature);
229
+ var res = sig.checkSignature(xml);
230
+ ```
231
+
232
+ You might find it difficult to guess such transforms, but there are typical transforms you can try.
233
+
234
+ - <http://www.w3.org/TR/2001/REC-xml-c14n-20010315>
235
+ - <http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments>
236
+ - <http://www.w3.org/2001/10/xml-exc-c14n#>
237
+ - <http://www.w3.org/2001/10/xml-exc-c14n#WithComments>
238
+
239
+ ## API
240
+
241
+ ### xpath
242
+
243
+ See [xpath.js](https://github.com/yaronn/xpath.js) for usage. Note that this is actually using
244
+ [another library](https://github.com/goto100/xpath) as the underlying implementation.
245
+
246
+ ### SignedXml
247
+
248
+ The `SignedXml` constructor provides an abstraction for sign and verify xml documents. The object is constructed using `new SignedXml(options?: SignedXmlOptions)` where the possible options are:
249
+
250
+ - `idMode` - default `null` - if the value of `wssecurity` is passed it will create/validate id's with the ws-security namespace.
251
+ - `idAttribute` - string - default `Id` or `ID` or `id` - the name of the attribute that contains the id of the element
252
+ - `privateKey` - string or Buffer - default `null` - the private key to use for signing
253
+ - `publicCert` - string or Buffer - default `null` - the public certificate to use for verifying
254
+ - `signatureAlgorithm` - string - the signature algorithm to use
255
+ - `canonicalizationAlgorithm` - string - default `undefined` - the canonicalization algorithm to use
256
+ - `inclusiveNamespacesPrefixList` - string - default `null` - a list of namespace prefixes to include during canonicalization
257
+ - `implicitTransforms` - string[] - default `[]` - a list of implicit transforms to use during verification
258
+ - `keyInfoAttributes` - object - default `{}` - a hash of attributes and values `attrName: value` to add to the KeyInfo node
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
261
+
262
+ #### API
263
+
264
+ A `SignedXml` object provides the following methods:
265
+
266
+ To sign xml documents:
267
+
268
+ - `addReference({ xpath, transforms, digestAlgorithm, id, type })` - adds a reference to a xml element where:
269
+ - `xpath` - a string containing a XPath expression referencing a xml element
270
+ - `transforms` - an array of [transform algorithms](#canonicalization-and-transformation-algorithms), the referenced element will be transformed for each value in the array
271
+ - `digestAlgorithm` - one of the supported [hashing algorithms](#hashing-algorithms)
272
+ - `id` - an optional `Id` attribute to add to the reference element
273
+ - `type` - the optional `Type` attribute to add to the reference element (represented as a URI)
274
+ - `computeSignature(xml, [options])` - compute the signature of the given xml where:
275
+ - `xml` - a string containing a xml document
276
+ - `options` - an object with the following properties:
277
+ - `prefix` - adds this value as a prefix for the generated signature tags
278
+ - `attrs` - a hash of attributes and values `attrName: value` to add to the signature root node
279
+ - `location` - customize the location of the signature, pass an object with a `reference` key which should contain a XPath expression to a reference node, an `action` key which should contain one of the following values: `append`, `prepend`, `before`, `after`
280
+ - `existingPrefixes` - A hash of prefixes and namespaces `prefix: namespace` that shouldn't be in the signature because they already exist in the xml
281
+ - `getSignedXml()` - returns the original xml document with the signature in it, **must be called only after `computeSignature`**
282
+ - `getSignatureXml()` - returns just the signature part, **must be called only after `computeSignature`**
283
+ - `getOriginalXmlWithIds()` - returns the original xml with Id attributes added on relevant elements (required for validation), **must be called only after `computeSignature`**
284
+
285
+ To verify xml documents:
286
+
287
+ - `loadSignature(signatureXml)` - loads the signature where:
288
+ - `signatureXml` - a string or node object (like an [xmldom](https://github.com/xmldom/xmldom) node) containing the xml representation of the signature
289
+ - `checkSignature(xml)` - validates the given xml document and returns `true` if the validation was successful
290
+
291
+ ## Customizing Algorithms
292
+
293
+ The following sample shows how to sign a message using custom algorithms.
294
+
295
+ First import some modules:
296
+
297
+ ```javascript
298
+ var SignedXml = require("xml-crypto").SignedXml,
299
+ fs = require("fs");
300
+ ```
301
+
302
+ Now define the extension point you want to implement. You can choose one or more.
303
+
304
+ To determine the inclusion and contents of a `<KeyInfo />` element, the function
305
+ `this.getKeyInfoContent()` is called. There is a default implementation of this. If you wish to change
306
+ this implementation, provide your own function assigned to the property `this.getKeyInfoContent`. If you prefer to use the default implementation, assign `SignedXml.getKeyInfoContent` to `this.getKeyInfoContent` If
307
+ there are no attributes and no contents to the `<KeyInfo />` element, it won't be included in the
308
+ generated XML.
309
+
310
+ To specify custom attributes on `<KeyInfo />`, add the properties to the `.keyInfoAttributes` property.
311
+
312
+ A custom hash algorithm is used to calculate digests. Implement it if you want a hash other than the built-in methods.
313
+
314
+ ```javascript
315
+ function MyDigest() {
316
+ this.getHash = function (xml) {
317
+ return "the base64 hash representation of the given xml string";
318
+ };
319
+
320
+ this.getAlgorithmName = function () {
321
+ return "http://myDigestAlgorithm";
322
+ };
323
+ }
324
+ ```
325
+
326
+ A custom signing algorithm.
327
+
328
+ ```javascript
329
+ function MySignatureAlgorithm() {
330
+ /*sign the given SignedInfo using the key. return base64 signature value*/
331
+ this.getSignature = function (signedInfo, privateKey) {
332
+ return "signature of signedInfo as base64...";
333
+ };
334
+
335
+ this.getAlgorithmName = function () {
336
+ return "http://mySigningAlgorithm";
337
+ };
338
+ }
339
+ ```
340
+
341
+ Custom transformation algorithm.
342
+
343
+ ```javascript
344
+ function MyTransformation() {
345
+ /*given a node (from the xmldom module) return its canonical representation (as string)*/
346
+ this.process = function (node) {
347
+ //you should apply your transformation before returning
348
+ return node.toString();
349
+ };
350
+
351
+ this.getAlgorithmName = function () {
352
+ return "http://myTransformation";
353
+ };
354
+ }
355
+ ```
356
+
357
+ Custom canonicalization is actually the same as custom transformation. It is applied on the SignedInfo rather than on references.
358
+
359
+ ```javascript
360
+ function MyCanonicalization() {
361
+ /*given a node (from the xmldom module) return its canonical representation (as string)*/
362
+ this.process = function (node) {
363
+ //you should apply your transformation before returning
364
+ return "< x/>";
365
+ };
366
+
367
+ this.getAlgorithmName = function () {
368
+ return "http://myCanonicalization";
369
+ };
370
+ }
371
+ ```
372
+
373
+ Now you need to register the new algorithms:
374
+
375
+ ```javascript
376
+ /*register all the custom algorithms*/
377
+
378
+ signedXml.CanonicalizationAlgorithms["http://MyTransformation"] = MyTransformation;
379
+ signedXml.CanonicalizationAlgorithms["http://MyCanonicalization"] = MyCanonicalization;
380
+ signedXml.HashAlgorithms["http://myDigestAlgorithm"] = MyDigest;
381
+ signedXml.SignatureAlgorithms["http://mySigningAlgorithm"] = MySignatureAlgorithm;
382
+ ```
383
+
384
+ Now do the signing. Note how we configure the signature to use the above algorithms:
385
+
386
+ ```javascript
387
+ function signXml(xml, xpath, key, dest) {
388
+ var options = {
389
+ publicCert: fs.readFileSync("my_public_cert.pem", "latin1"),
390
+ privateKey: fs.readFileSync(key),
391
+ /*configure the signature object to use the custom algorithms*/
392
+ signatureAlgorithm: "http://mySignatureAlgorithm",
393
+ canonicalizationAlgorithm: "http://MyCanonicalization",
394
+ };
395
+
396
+ var sig = new SignedXml(options);
397
+
398
+ sig.addReference({
399
+ xpath: "//*[local-name(.)='x']",
400
+ transforms: ["http://MyTransformation"],
401
+ digestAlgorithm: "http://myDigestAlgorithm",
402
+ });
403
+
404
+ sig.addReference({
405
+ xpath,
406
+ transforms: ["http://MyTransformation"],
407
+ digestAlgorithm: "http://myDigestAlgorithm",
408
+ });
409
+ sig.canonicalizationAlgorithm = "http://www.w3.org/2001/10/xml-exc-c14n#";
410
+ sig.signatureAlgorithm = "http://www.w3.org/2000/09/xmldsig#rsa-sha1";
411
+ sig.computeSignature(xml);
412
+ fs.writeFileSync(dest, sig.getSignedXml());
413
+ }
414
+
415
+ var xml = "<library>" + "<book>" + "<name>Harry Potter</name>" + "</book>";
416
+ ("</library>");
417
+
418
+ signXml(xml, "//*[local-name(.)='book']", "client.pem", "result.xml");
419
+ ```
420
+
421
+ You can always look at the actual code as a sample.
422
+
423
+ ## Asynchronous signing and verification
424
+
425
+ If the private key is not stored locally, and you wish to use a signing server or Hardware Security Module (HSM) to sign documents, you can create a custom signing algorithm that uses an asynchronous callback.
426
+
427
+ ```javascript
428
+ function AsyncSignatureAlgorithm() {
429
+ this.getSignature = function (signedInfo, privateKey, callback) {
430
+ var signer = crypto.createSign("RSA-SHA1");
431
+ signer.update(signedInfo);
432
+ var res = signer.sign(privateKey, "base64");
433
+ //Do some asynchronous things here
434
+ callback(null, res);
435
+ };
436
+ this.getAlgorithmName = function () {
437
+ return "http://www.w3.org/2000/09/xmldsig#rsa-sha1";
438
+ };
439
+ }
440
+
441
+ var sig = new SignedXml({ signatureAlgorithm: "http://asyncSignatureAlgorithm" });
442
+ sig.SignatureAlgorithms["http://asyncSignatureAlgorithm"] = AsyncSignatureAlgorithm;
443
+ sig.signatureAlgorithm = "http://asyncSignatureAlgorithm";
444
+ sig.canonicalizationAlgorithm = "http://www.w3.org/2001/10/xml-exc-c14n#";
445
+ sig.computeSignature(xml, opts, function (err) {
446
+ var signedResponse = sig.getSignedXml();
447
+ });
448
+ ```
449
+
450
+ The function `sig.checkSignature` may also use a callback if asynchronous verification is needed.
451
+
452
+ ## X.509 / Key formats
453
+
454
+ Xml-Crypto internally relies on node's crypto module. This means pem encoded certificates are supported. So to sign an xml use key.pem that looks like this (only the beginning of the key content is shown):
455
+
456
+ ```text
457
+ -----BEGIN PRIVATE KEY-----
458
+ MIICdwIBADANBgkqhkiG9w0...
459
+ -----END PRIVATE KEY-----
460
+ ```
461
+
462
+ And for verification use key_public.pem:
463
+
464
+ ```text
465
+ -----BEGIN CERTIFICATE-----
466
+ MIIBxDCCAW6gAwIBAgIQxUSX...
467
+ -----END CERTIFICATE-----
468
+ ```
469
+
470
+ ### Converting .pfx certificates to pem
471
+
472
+ If you have .pfx certificates you can convert them to .pem using [openssl](http://www.openssl.org/):
473
+
474
+ ```shell
475
+ openssl pkcs12 -in c:\certs\yourcert.pfx -out c:\certs\cag.pem
476
+ ```
477
+
478
+ Then you could use the result as is for the purpose of signing. For the purpose of validation open the resulting .pem with a text editor and copy from -----BEGIN CERTIFICATE----- to -----END CERTIFICATE----- (including) to a new text file and save it as .pem.
479
+
480
+ ## Examples
481
+
482
+ ### how to sign a root node (_coming soon_)
483
+
484
+ ### how to add a prefix for the signature
485
+
486
+ Use the `prefix` option when calling `computeSignature` to add a prefix to the signature.
487
+
488
+ ```javascript
489
+ var SignedXml = require("xml-crypto").SignedXml,
490
+ fs = require("fs");
491
+
492
+ var xml = "<library>" + "<book>" + "<name>Harry Potter</name>" + "</book>" + "</library>";
493
+
494
+ var sig = new SignedXml({ privateKey: fs.readFileSync("client.pem") });
495
+ sig.addReference({
496
+ xpath: "//*[local-name(.)='book']",
497
+ digestAlgorithm: "http://www.w3.org/2000/09/xmldsig#sha1",
498
+ transforms: ["http://www.w3.org/2001/10/xml-exc-c14n#"],
499
+ });
500
+ sig.canonicalizationAlgorithm = "http://www.w3.org/2001/10/xml-exc-c14n#";
501
+ sig.signatureAlgorithm = "http://www.w3.org/2000/09/xmldsig#rsa-sha1";
502
+ sig.computeSignature(xml, {
503
+ prefix: "ds",
504
+ });
505
+ ```
506
+
507
+ ### how to specify the location of the signature
508
+
509
+ Use the `location` option when calling `computeSignature` to move the signature around.
510
+ Set `action` to one of the following:
511
+
512
+ - append(default) - append to the end of the xml document
513
+ - prepend - prepend to the xml document
514
+ - before - prepend to a specific node (use the `referenceNode` property)
515
+ - after - append to specific node (use the `referenceNode` property)
516
+
517
+ ```javascript
518
+ const SignedXml = require("xml-crypto").SignedXml;
519
+ const fs = require("fs");
520
+
521
+ const xml = "<library>" + "<book>" + "<name>Harry Potter</name>" + "</book>" + "</library>";
522
+
523
+ const sig = new SignedXml({ privateKey: fs.readFileSync("client.pem") });
524
+ sig.addReference({
525
+ xpath: "//*[local-name(.)='book']",
526
+ digestAlgorithm: "http://www.w3.org/2000/09/xmldsig#sha1",
527
+ transforms: ["http://www.w3.org/2001/10/xml-exc-c14n#"],
528
+ });
529
+ sig.canonicalizationAlgorithm = "http://www.w3.org/2001/10/xml-exc-c14n#";
530
+ sig.signatureAlgorithm = "http://www.w3.org/2000/09/xmldsig#rsa-sha1";
531
+ sig.computeSignature(xml, {
532
+ location: { reference: "//*[local-name(.)='book']", action: "after" }, // This will place the signature after the book element
533
+ });
534
+ ```
535
+
536
+ ### How to add custom Objects to the signature
537
+
538
+ Use the `objects` option when creating a SignedXml instance to add custom Objects to the signature.
539
+
540
+ ```javascript
541
+ const SignedXml = require("xml-crypto").SignedXml;
542
+ const fs = require("fs");
543
+
544
+ const xml = "<library>" + "<book>" + "<name>Harry Potter</name>" + "</book>" + "</library>";
545
+
546
+ const sig = new SignedXml({
547
+ privateKey: fs.readFileSync("client.pem"),
548
+ canonicalizationAlgorithm: "http://www.w3.org/2001/10/xml-exc-c14n#",
549
+ signatureAlgorithm: "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256",
550
+ objects: [
551
+ {
552
+ content: "<TestObject>Test data in Object</TestObject>",
553
+ attributes: {
554
+ Id: "Object1",
555
+ MimeType: "text/xml",
556
+ },
557
+ },
558
+ ],
559
+ });
560
+
561
+ // Add a reference to the Object element
562
+ sig.addReference({
563
+ xpath: "//*[@Id='Object1']",
564
+ digestAlgorithm: "http://www.w3.org/2001/04/xmlenc#sha256",
565
+ transforms: ["http://www.w3.org/2001/10/xml-exc-c14n#"],
566
+ });
567
+
568
+ sig.computeSignature(xml);
569
+ fs.writeFileSync("signed.xml", sig.getSignedXml());
570
+ ```
571
+
572
+ ### more examples (_coming soon_)
573
+
574
+ ## Development
575
+
576
+ The testing framework we use is [Mocha](https://github.com/mochajs/mocha) with [Chai](https://github.com/chaijs/chai) as the assertion framework.
577
+
578
+ To run tests use:
579
+
580
+ ```shell
581
+ npm test
582
+ ```
583
+
584
+ ## Sponsors
585
+
586
+ ![Short-io logo](https://github.com/Short-io.png?size=30) [Short-io](https://github.com/Short-io)
587
+
588
+ ![RideAmigosCorp logo](https://github.com/RideAmigosCorp.png?size=30) [RideAmigosCorp](https://github.com/RideAmigosCorp)
589
+
590
+ ## License
591
+
592
+ This project is licensed under the [MIT License](http://opensource.org/licenses/MIT). See the [LICENSE](LICENSE) file for more info.
@@ -0,0 +1,39 @@
1
+ import type { CanonicalizationOrTransformationAlgorithm, CanonicalizationOrTransformationAlgorithmProcessOptions, NamespacePrefix, RenderedNamespace } from "./types";
2
+ export declare class C14nCanonicalization implements CanonicalizationOrTransformationAlgorithm {
3
+ protected includeComments: boolean;
4
+ constructor();
5
+ attrCompare(a: any, b: any): 1 | 0 | -1;
6
+ nsCompare(a: any, b: any): any;
7
+ renderAttrs(node: any): string;
8
+ /**
9
+ * Create the string of all namespace declarations that should appear on this element
10
+ *
11
+ * @param node The node we now render
12
+ * @param prefixesInScope The prefixes defined on this node parents which are a part of the output set
13
+ * @param defaultNs The current default namespace
14
+ * @param defaultNsForPrefix
15
+ * @param ancestorNamespaces Import ancestor namespaces if it is specified
16
+ * @api private
17
+ */
18
+ renderNs(node: Element, prefixesInScope: string[], defaultNs: string, defaultNsForPrefix: string, ancestorNamespaces: NamespacePrefix[]): RenderedNamespace;
19
+ /**
20
+ * @param node Node
21
+ */
22
+ processInner(node: any, prefixesInScope: any, defaultNs: any, defaultNsForPrefix: any, ancestorNamespaces: any): string;
23
+ renderComment(node: Comment): string;
24
+ /**
25
+ * Perform canonicalization of the given node
26
+ *
27
+ * @param node
28
+ * @api public
29
+ */
30
+ process(node: Node, options: CanonicalizationOrTransformationAlgorithmProcessOptions): string;
31
+ getAlgorithmName(): string;
32
+ }
33
+ /**
34
+ * Add c14n#WithComments here (very simple subclass)
35
+ */
36
+ export declare class C14nCanonicalizationWithComments extends C14nCanonicalization {
37
+ constructor();
38
+ getAlgorithmName(): string;
39
+ }