Nodejs Xpath parse error using xml-crypto

135 Views Asked by At

I am having trouble signing an xml using xml-crypto library. Here is my code:

  const fs = require("fs");
  const xmlCrypto = require("xml-crypto");
  const xpath = require("xpath");
  const dom = require("xmldom").DOMParser;

  var xml = "<book><title>Harry Potter</title></book>";
  var xmlDocument = new dom().parseFromString(xml, "text/xml");

  var node = xpath.select("//title", xmlDocument)[0];

  console.log(node.localName + ": " + node.firstChild.data);
  console.log("Node: " + node.toString());

  const privateKey = fs.readFileSync(
    "./certifications/universal-pk.pem",
    "utf-8"
  );

  console.log("privateKey", privateKey);

  const reference = {
    uri: "",
    transforms: ["canonicalize", "c14n"],
    digestAlgorithm: "http://www.w3.org/2001/04/xmlenc#sha256",
  };

  const sig = new xmlCrypto.SignedXml();
  sig.addReference(reference);
  sig.signingKey = privateKey;
  sig.computeSignature(node);

And the console log:

title: Harry Potter
Node: <title>Harry Potter</title>
privateKey -----BEGIN PRIVATE KEY-----
.......
-----END PRIVATE KEY-----

[xmldom error]  invalid doc source
@#[line:0,col:undefined]
\api\node_modules\xml-crypto\node_modules\xpath\xpath.js:1278
                    throw new Error("XPath parse error");
                          ^

Error: XPath parse error

I also tried the sample code from https://www.npmjs.com/package/xml-crypto

However, I also received the exact same error message.

1

There are 1 best solutions below

3
On BEST ANSWER

The problem is that you're not passing correct input to computeSignature method.

The docs say:

computeSignature(xml, [options]) - compute the signature of the given xml where:

xml - a string containing a xml document

https://www.npmjs.com/package/xml-crypto#signing-xml-documents

So, it takes an XML string, but node variable which you are sending here

sig.computeSignature(node);

is actually an xmldom object, hence the error.

So, send an XML string instead:

sig.computeSignature(node.toString());

Also, it seems you're using some older version, so when changed in accordance with the current docs, your code should look like this:

(note that you don't really need to use DOMParser, you can pass xml string and modify xpath selector in reference)

var sig = new xmlCrypto.SignedXml({ privateKey });
sig.addReference({
  xpath: "//*[local-name(.)='title']",
  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(node.toString()); // or just xml and modify xpath..