Socket
Socket
Sign inDemoInstall

xml-crypto

Package Overview
Dependencies
Maintainers
5
Versions
80
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

xml-crypto - npm Package Compare versions

Comparing version 3.0.1 to 3.1.0

.eslintignore

10

index.js

@@ -1,6 +0,6 @@

var select = require('xpath').select
var select = require("xpath").select;
module.exports = require('./lib/signed-xml')
module.exports.xpath = function(node, xpath) {
return select(xpath, node)
}
module.exports = require("./lib/signed-xml");
module.exports.xpath = function (node, xpath) {
return select(xpath, node);
};
/* jshint laxcomma: true */
var utils = require('./utils');
var utils = require("./utils");

@@ -8,31 +8,40 @@ exports.C14nCanonicalization = C14nCanonicalization;

function C14nCanonicalization() {
this.includeComments = false;
};
this.includeComments = false;
}
C14nCanonicalization.prototype.attrCompare = function(a,b) {
if (!a.namespaceURI && b.namespaceURI) { return -1; }
if (!b.namespaceURI && a.namespaceURI) { return 1; }
C14nCanonicalization.prototype.attrCompare = function (a, b) {
if (!a.namespaceURI && b.namespaceURI) {
return -1;
}
if (!b.namespaceURI && a.namespaceURI) {
return 1;
}
var left = a.namespaceURI + a.localName
var right = b.namespaceURI + b.localName
var left = a.namespaceURI + a.localName;
var right = b.namespaceURI + b.localName;
if (left===right) return 0
else if (left<right) return -1
else return 1
if (left === right) return 0;
else if (left < right) return -1;
else return 1;
};
C14nCanonicalization.prototype.nsCompare = function(a,b) {
C14nCanonicalization.prototype.nsCompare = function (a, b) {
var attr1 = a.prefix;
var attr2 = b.prefix;
if (attr1 == attr2) { return 0; }
if (attr1 == attr2) {
return 0;
}
return attr1.localeCompare(attr2);
};
C14nCanonicalization.prototype.renderAttrs = function(node, defaultNS) {
var a, i, attr
, res = []
, attrListToRender = [];
C14nCanonicalization.prototype.renderAttrs = function (node, defaultNS) {
var a,
i,
attr,
res = [],
attrListToRender = [];
if (node.nodeType===8) { return this.renderComment(node); }
if (node.nodeType === 8) {
return this.renderComment(node);
}

@@ -43,3 +52,5 @@ if (node.attributes) {

//ignore namespace definition attributes
if (attr.name.indexOf("xmlns") === 0) { continue; }
if (attr.name.indexOf("xmlns") === 0) {
continue;
}
attrListToRender.push(attr);

@@ -52,3 +63,5 @@ }

for (a in attrListToRender) {
if (!attrListToRender.hasOwnProperty(a)) { continue; }
if (!attrListToRender.hasOwnProperty(a)) {
continue;
}

@@ -62,3 +75,2 @@ attr = attrListToRender[a];

/**

@@ -76,21 +88,32 @@ * Create the string of all namespace declarations that should appear on this element

*/
C14nCanonicalization.prototype.renderNs = function(node, prefixesInScope, defaultNs, defaultNsForPrefix, ancestorNamespaces) {
var a, i, p, attr
, res = []
, newDefaultNs = defaultNs
, nsListToRender = []
, currNs = node.namespaceURI || "";
C14nCanonicalization.prototype.renderNs = function (
node,
prefixesInScope,
defaultNs,
defaultNsForPrefix,
ancestorNamespaces
) {
var a,
i,
p,
attr,
res = [],
newDefaultNs = defaultNs,
nsListToRender = [],
currNs = node.namespaceURI || "";
//handle the namespaceof the node itself
if (node.prefix) {
if (prefixesInScope.indexOf(node.prefix)==-1) {
nsListToRender.push({"prefix": node.prefix, "namespaceURI": node.namespaceURI || defaultNsForPrefix[node.prefix]});
if (prefixesInScope.indexOf(node.prefix) == -1) {
nsListToRender.push({
prefix: node.prefix,
namespaceURI: node.namespaceURI || defaultNsForPrefix[node.prefix],
});
prefixesInScope.push(node.prefix);
}
} else if (defaultNs != currNs) {
//new default ns
newDefaultNs = node.namespaceURI;
res.push(' xmlns="', newDefaultNs, '"');
}
else if (defaultNs!=currNs) {
//new default ns
newDefaultNs = node.namespaceURI;
res.push(' xmlns="', newDefaultNs, '"');
}

@@ -105,3 +128,3 @@ //handle the attributes namespace

if (attr.prefix === "xmlns" && prefixesInScope.indexOf(attr.localName) === -1) {
nsListToRender.push({"prefix": attr.localName, "namespaceURI": attr.value});
nsListToRender.push({ prefix: attr.localName, namespaceURI: attr.value });
prefixesInScope.push(attr.localName);

@@ -112,4 +135,9 @@ }

//the prefix is not defined already
if (attr.prefix && prefixesInScope.indexOf(attr.prefix)==-1 && attr.prefix!="xmlns" && attr.prefix!="xml") {
nsListToRender.push({"prefix": attr.prefix, "namespaceURI": attr.namespaceURI});
if (
attr.prefix &&
prefixesInScope.indexOf(attr.prefix) == -1 &&
attr.prefix != "xmlns" &&
attr.prefix != "xml"
) {
nsListToRender.push({ prefix: attr.prefix, namespaceURI: attr.namespaceURI });
prefixesInScope.push(attr.prefix);

@@ -119,17 +147,18 @@ }

}
if(Array.isArray(ancestorNamespaces) && ancestorNamespaces.length > 0){
if (Array.isArray(ancestorNamespaces) && ancestorNamespaces.length > 0) {
// Remove namespaces which are already present in nsListToRender
for(var p1 in ancestorNamespaces){
if(!ancestorNamespaces.hasOwnProperty(p1)) continue;
for (var p1 in ancestorNamespaces) {
if (!ancestorNamespaces.hasOwnProperty(p1)) continue;
var alreadyListed = false;
for(var p2 in nsListToRender){
if(nsListToRender[p2].prefix === ancestorNamespaces[p1].prefix
&& nsListToRender[p2].namespaceURI === ancestorNamespaces[p1].namespaceURI)
{
for (var p2 in nsListToRender) {
if (
nsListToRender[p2].prefix === ancestorNamespaces[p1].prefix &&
nsListToRender[p2].namespaceURI === ancestorNamespaces[p1].namespaceURI
) {
alreadyListed = true;
}
}
if(!alreadyListed){
if (!alreadyListed) {
nsListToRender.push(ancestorNamespaces[p1]);

@@ -144,3 +173,5 @@ }

for (a in nsListToRender) {
if (!nsListToRender.hasOwnProperty(a)) { continue; }
if (!nsListToRender.hasOwnProperty(a)) {
continue;
}

@@ -151,17 +182,29 @@ p = nsListToRender[a];

return {"rendered": res.join(""), "newDefaultNs": newDefaultNs};
return { rendered: res.join(""), newDefaultNs: newDefaultNs };
};
C14nCanonicalization.prototype.processInner = function(node, prefixesInScope, defaultNs, defaultNsForPrefix, ancestorNamespaces) {
C14nCanonicalization.prototype.processInner = function (
node,
prefixesInScope,
defaultNs,
defaultNsForPrefix,
ancestorNamespaces
) {
if (node.nodeType === 8) {
return this.renderComment(node);
}
if (node.data) {
return utils.encodeSpecialCharactersInText(node.data);
}
if (node.nodeType === 8) { return this.renderComment(node); }
if (node.data) { return utils.encodeSpecialCharactersInText(node.data); }
var i,
pfxCopy,
ns = this.renderNs(node, prefixesInScope, defaultNs, defaultNsForPrefix, ancestorNamespaces),
res = ["<", node.tagName, ns.rendered, this.renderAttrs(node, ns.newDefaultNs), ">"];
var i, pfxCopy
, ns = this.renderNs(node, prefixesInScope, defaultNs, defaultNsForPrefix, ancestorNamespaces)
, res = ["<", node.tagName, ns.rendered, this.renderAttrs(node, ns.newDefaultNs), ">"];
for (i = 0; i < node.childNodes.length; ++i) {
pfxCopy = prefixesInScope.slice(0);
res.push(this.processInner(node.childNodes[i], pfxCopy, ns.newDefaultNs, defaultNsForPrefix, []));
res.push(
this.processInner(node.childNodes[i], pfxCopy, ns.newDefaultNs, defaultNsForPrefix, [])
);
}

@@ -175,12 +218,13 @@

C14nCanonicalization.prototype.renderComment = function (node) {
if (!this.includeComments) {
return "";
}
if (!this.includeComments) { return ""; }
var isOutsideDocument = node.ownerDocument === node.parentNode,
isBeforeDocument = null,
isAfterDocument = null;
var isOutsideDocument = (node.ownerDocument === node.parentNode),
isBeforeDocument = null,
isAfterDocument = null;
if (isOutsideDocument) {
var nextNode = node,
previousNode = node;
previousNode = node;

@@ -206,3 +250,9 @@ while (nextNode !== null) {

return (isAfterDocument ? "\n" : "") + "<!--" + utils.encodeSpecialCharactersInText(node.data) + "-->" + (isBeforeDocument ? "\n" : "");
return (
(isAfterDocument ? "\n" : "") +
"<!--" +
utils.encodeSpecialCharactersInText(node.data) +
"-->" +
(isBeforeDocument ? "\n" : "")
);
};

@@ -217,3 +267,3 @@

*/
C14nCanonicalization.prototype.process = function(node, options) {
C14nCanonicalization.prototype.process = function (node, options) {
options = options || {};

@@ -229,7 +279,13 @@ var defaultNs = options.defaultNs || "";

var res = this.processInner(node, prefixesInScope, defaultNs, defaultNsForPrefix, ancestorNamespaces);
var res = this.processInner(
node,
prefixesInScope,
defaultNs,
defaultNsForPrefix,
ancestorNamespaces
);
return res;
};
C14nCanonicalization.prototype.getAlgorithmName = function() {
C14nCanonicalization.prototype.getAlgorithmName = function () {
return "http://www.w3.org/TR/2001/REC-xml-c14n-20010315";

@@ -242,5 +298,5 @@ };

function C14nCanonicalizationWithComments() {
C14nCanonicalization.call(this);
this.includeComments = true;
};
C14nCanonicalization.call(this);
this.includeComments = true;
}

@@ -251,4 +307,4 @@ C14nCanonicalizationWithComments.prototype = Object.create(C14nCanonicalization.prototype);

C14nCanonicalizationWithComments.prototype.getAlgorithmName = function() {
C14nCanonicalizationWithComments.prototype.getAlgorithmName = function () {
return "http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments";
};

@@ -1,8 +0,7 @@

var xpath = require('xpath');
var utils = require('./utils');
var xpath = require("xpath");
var utils = require("./utils");
exports.EnvelopedSignature = EnvelopedSignature;
function EnvelopedSignature() {
}
function EnvelopedSignature() {}

@@ -12,3 +11,6 @@ EnvelopedSignature.prototype.process = function (node, options) {

// leave this for the moment...
var signature = xpath.select("./*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']", node)[0];
var signature = xpath.select(
"./*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']",
node
)[0];
if (signature) signature.parentNode.removeChild(signature);

@@ -18,10 +20,19 @@ return node;

var signatureNode = options.signatureNode;
var expectedSignatureValue = utils.findFirst(signatureNode, ".//*[local-name(.)='SignatureValue']/text()").data;
var signatures = xpath.select(".//*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']", node);
var expectedSignatureValue = utils.findFirst(
signatureNode,
".//*[local-name(.)='SignatureValue']/text()"
).data;
var signatures = xpath.select(
".//*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']",
node
);
for (var h in signatures) {
if (!signatures.hasOwnProperty(h)) continue;
var signature = signatures[h];
var signatureValue = utils.findFirst(signature, ".//*[local-name(.)='SignatureValue']/text()").data;
var nodeSignature = signatures[h];
var signatureValue = utils.findFirst(
nodeSignature,
".//*[local-name(.)='SignatureValue']/text()"
).data;
if (expectedSignatureValue === signatureValue) {
signature.parentNode.removeChild(signature);
nodeSignature.parentNode.removeChild(nodeSignature);
}

@@ -28,0 +39,0 @@ }

/* jshint laxcomma: true */
var utils = require('./utils');
var utils = require("./utils");

@@ -8,31 +8,40 @@ exports.ExclusiveCanonicalization = ExclusiveCanonicalization;

function ExclusiveCanonicalization() {
this.includeComments = false;
};
this.includeComments = false;
}
ExclusiveCanonicalization.prototype.attrCompare = function(a,b) {
if (!a.namespaceURI && b.namespaceURI) { return -1; }
if (!b.namespaceURI && a.namespaceURI) { return 1; }
ExclusiveCanonicalization.prototype.attrCompare = function (a, b) {
if (!a.namespaceURI && b.namespaceURI) {
return -1;
}
if (!b.namespaceURI && a.namespaceURI) {
return 1;
}
var left = a.namespaceURI + a.localName
var right = b.namespaceURI + b.localName
var left = a.namespaceURI + a.localName;
var right = b.namespaceURI + b.localName;
if (left===right) return 0
else if (left<right) return -1
else return 1
if (left === right) return 0;
else if (left < right) return -1;
else return 1;
};
ExclusiveCanonicalization.prototype.nsCompare = function(a,b) {
ExclusiveCanonicalization.prototype.nsCompare = function (a, b) {
var attr1 = a.prefix;
var attr2 = b.prefix;
if (attr1 == attr2) { return 0; }
if (attr1 == attr2) {
return 0;
}
return attr1.localeCompare(attr2);
};
ExclusiveCanonicalization.prototype.renderAttrs = function(node, defaultNS) {
var a, i, attr
, res = []
, attrListToRender = [];
ExclusiveCanonicalization.prototype.renderAttrs = function (node, defaultNS) {
var a,
i,
attr,
res = [],
attrListToRender = [];
if (node.nodeType===8) { return this.renderComment(node); }
if (node.nodeType === 8) {
return this.renderComment(node);
}

@@ -43,3 +52,5 @@ if (node.attributes) {

//ignore namespace definition attributes
if (attr.name.indexOf("xmlns") === 0) { continue; }
if (attr.name.indexOf("xmlns") === 0) {
continue;
}
attrListToRender.push(attr);

@@ -52,3 +63,5 @@ }

for (a in attrListToRender) {
if (!attrListToRender.hasOwnProperty(a)) { continue; }
if (!attrListToRender.hasOwnProperty(a)) {
continue;
}

@@ -62,4 +75,3 @@ attr = attrListToRender[a];

function isPrefixInScope(prefixesInScope, prefix, namespaceURI)
{
function isPrefixInScope(prefixesInScope, prefix, namespaceURI) {
var ret = false;

@@ -70,3 +82,3 @@ prefixesInScope.forEach(function (pf) {

}
})
});

@@ -86,21 +98,41 @@ return ret;

*/
ExclusiveCanonicalization.prototype.renderNs = function(node, prefixesInScope, defaultNs, defaultNsForPrefix, inclusiveNamespacesPrefixList) {
var a, i, p, attr
, res = []
, newDefaultNs = defaultNs
, nsListToRender = []
, currNs = node.namespaceURI || "";
ExclusiveCanonicalization.prototype.renderNs = function (
node,
prefixesInScope,
defaultNs,
defaultNsForPrefix,
inclusiveNamespacesPrefixList
) {
var a,
i,
p,
attr,
res = [],
newDefaultNs = defaultNs,
nsListToRender = [],
currNs = node.namespaceURI || "";
//handle the namespaceof the node itself
if (node.prefix) {
if (!isPrefixInScope(prefixesInScope, node.prefix, node.namespaceURI || defaultNsForPrefix[node.prefix])) {
nsListToRender.push({"prefix": node.prefix, "namespaceURI": node.namespaceURI || defaultNsForPrefix[node.prefix]});
prefixesInScope.push({"prefix": node.prefix, "namespaceURI": node.namespaceURI || defaultNsForPrefix[node.prefix]});
if (
!isPrefixInScope(
prefixesInScope,
node.prefix,
node.namespaceURI || defaultNsForPrefix[node.prefix]
)
) {
nsListToRender.push({
prefix: node.prefix,
namespaceURI: node.namespaceURI || defaultNsForPrefix[node.prefix],
});
prefixesInScope.push({
prefix: node.prefix,
namespaceURI: node.namespaceURI || defaultNsForPrefix[node.prefix],
});
}
} else if (defaultNs != currNs) {
//new default ns
newDefaultNs = node.namespaceURI;
res.push(' xmlns="', newDefaultNs, '"');
}
else if (defaultNs!=currNs) {
//new default ns
newDefaultNs = node.namespaceURI;
res.push(' xmlns="', newDefaultNs, '"');
}

@@ -114,5 +146,9 @@ //handle the attributes namespace

//the prefix is not defined already
if (attr.prefix && !isPrefixInScope(prefixesInScope, attr.localName, attr.value) && inclusiveNamespacesPrefixList.indexOf(attr.localName) >= 0) {
nsListToRender.push({"prefix": attr.localName, "namespaceURI": attr.value});
prefixesInScope.push({"prefix": attr.localName, "namespaceURI": attr.value});
if (
attr.prefix &&
!isPrefixInScope(prefixesInScope, attr.localName, attr.value) &&
inclusiveNamespacesPrefixList.indexOf(attr.localName) >= 0
) {
nsListToRender.push({ prefix: attr.localName, namespaceURI: attr.value });
prefixesInScope.push({ prefix: attr.localName, namespaceURI: attr.value });
}

@@ -122,5 +158,10 @@

//the prefix is not defined already
if (attr.prefix && !isPrefixInScope(prefixesInScope, attr.prefix, attr.namespaceURI) && attr.prefix!="xmlns" && attr.prefix!="xml") {
nsListToRender.push({"prefix": attr.prefix, "namespaceURI": attr.namespaceURI});
prefixesInScope.push({"prefix": attr.prefix, "namespaceURI": attr.namespaceURI});
if (
attr.prefix &&
!isPrefixInScope(prefixesInScope, attr.prefix, attr.namespaceURI) &&
attr.prefix != "xmlns" &&
attr.prefix != "xml"
) {
nsListToRender.push({ prefix: attr.prefix, namespaceURI: attr.namespaceURI });
prefixesInScope.push({ prefix: attr.prefix, namespaceURI: attr.namespaceURI });
}

@@ -134,3 +175,5 @@ }

for (a in nsListToRender) {
if (!nsListToRender.hasOwnProperty(a)) { continue; }
if (!nsListToRender.hasOwnProperty(a)) {
continue;
}

@@ -141,17 +184,41 @@ p = nsListToRender[a];

return {"rendered": res.join(""), "newDefaultNs": newDefaultNs};
return { rendered: res.join(""), newDefaultNs: newDefaultNs };
};
ExclusiveCanonicalization.prototype.processInner = function(node, prefixesInScope, defaultNs, defaultNsForPrefix, inclusiveNamespacesPrefixList) {
ExclusiveCanonicalization.prototype.processInner = function (
node,
prefixesInScope,
defaultNs,
defaultNsForPrefix,
inclusiveNamespacesPrefixList
) {
if (node.nodeType === 8) {
return this.renderComment(node);
}
if (node.data) {
return utils.encodeSpecialCharactersInText(node.data);
}
if (node.nodeType === 8) { return this.renderComment(node); }
if (node.data) { return utils.encodeSpecialCharactersInText(node.data); }
var i,
pfxCopy,
ns = this.renderNs(
node,
prefixesInScope,
defaultNs,
defaultNsForPrefix,
inclusiveNamespacesPrefixList
),
res = ["<", node.tagName, ns.rendered, this.renderAttrs(node, ns.newDefaultNs), ">"];
var i, pfxCopy
, ns = this.renderNs(node, prefixesInScope, defaultNs, defaultNsForPrefix, inclusiveNamespacesPrefixList)
, res = ["<", node.tagName, ns.rendered, this.renderAttrs(node, ns.newDefaultNs), ">"];
for (i = 0; i < node.childNodes.length; ++i) {
pfxCopy = prefixesInScope.slice(0);
res.push(this.processInner(node.childNodes[i], pfxCopy, ns.newDefaultNs, defaultNsForPrefix, inclusiveNamespacesPrefixList));
res.push(
this.processInner(
node.childNodes[i],
pfxCopy,
ns.newDefaultNs,
defaultNsForPrefix,
inclusiveNamespacesPrefixList
)
);
}

@@ -165,12 +232,13 @@

ExclusiveCanonicalization.prototype.renderComment = function (node) {
if (!this.includeComments) {
return "";
}
if (!this.includeComments) { return ""; }
var isOutsideDocument = node.ownerDocument === node.parentNode,
isBeforeDocument = null,
isAfterDocument = null;
var isOutsideDocument = (node.ownerDocument === node.parentNode),
isBeforeDocument = null,
isAfterDocument = null;
if (isOutsideDocument) {
var nextNode = node,
previousNode = node;
previousNode = node;

@@ -196,3 +264,9 @@ while (nextNode !== null) {

return (isAfterDocument ? "\n" : "") + "<!--" + utils.encodeSpecialCharactersInText(node.data) + "-->" + (isBeforeDocument ? "\n" : "");
return (
(isAfterDocument ? "\n" : "") +
"<!--" +
utils.encodeSpecialCharactersInText(node.data) +
"-->" +
(isBeforeDocument ? "\n" : "")
);
};

@@ -207,3 +281,3 @@

*/
ExclusiveCanonicalization.prototype.process = function(node, options) {
ExclusiveCanonicalization.prototype.process = function (node, options) {
options = options || {};

@@ -213,19 +287,23 @@ var inclusiveNamespacesPrefixList = options.inclusiveNamespacesPrefixList || [];

var defaultNsForPrefix = options.defaultNsForPrefix || {};
if (!(inclusiveNamespacesPrefixList instanceof Array)) { inclusiveNamespacesPrefixList = inclusiveNamespacesPrefixList.split(' '); }
if (!(inclusiveNamespacesPrefixList instanceof Array)) {
inclusiveNamespacesPrefixList = inclusiveNamespacesPrefixList.split(" ");
}
var ancestorNamespaces = options.ancestorNamespaces || [];
/**
* If the inclusiveNamespacesPrefixList has not been explicitly provided then look it up in CanonicalizationMethod/InclusiveNamespaces
*/
*/
if (inclusiveNamespacesPrefixList.length == 0) {
var CanonicalizationMethod = utils.findChilds(node, "CanonicalizationMethod")
var CanonicalizationMethod = utils.findChilds(node, "CanonicalizationMethod");
if (CanonicalizationMethod.length != 0) {
var inclusiveNamespaces = utils.findChilds(CanonicalizationMethod[0], "InclusiveNamespaces")
var inclusiveNamespaces = utils.findChilds(CanonicalizationMethod[0], "InclusiveNamespaces");
if (inclusiveNamespaces.length != 0) {
inclusiveNamespacesPrefixList = inclusiveNamespaces[0].getAttribute('PrefixList').split(" ");
inclusiveNamespacesPrefixList = inclusiveNamespaces[0]
.getAttribute("PrefixList")
.split(" ");
}
}
}
/**

@@ -235,19 +313,32 @@ * If you have a PrefixList then use it and the ancestors to add the necessary namespaces

if (inclusiveNamespacesPrefixList) {
var prefixList = inclusiveNamespacesPrefixList instanceof Array ? inclusiveNamespacesPrefixList : inclusiveNamespacesPrefixList.split(' ');
var prefixList =
inclusiveNamespacesPrefixList instanceof Array
? inclusiveNamespacesPrefixList
: inclusiveNamespacesPrefixList.split(" ");
prefixList.forEach(function (prefix) {
if (ancestorNamespaces) {
if (ancestorNamespaces) {
ancestorNamespaces.forEach(function (ancestorNamespace) {
if (prefix == ancestorNamespace.prefix) {
node.setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:' + prefix, ancestorNamespace.namespaceURI);
node.setAttributeNS(
"http://www.w3.org/2000/xmlns/",
"xmlns:" + prefix,
ancestorNamespace.namespaceURI
);
}
})
});
}
})
});
}
var res = this.processInner(node, [], defaultNs, defaultNsForPrefix, inclusiveNamespacesPrefixList);
var res = this.processInner(
node,
[],
defaultNs,
defaultNsForPrefix,
inclusiveNamespacesPrefixList
);
return res;
};
ExclusiveCanonicalization.prototype.getAlgorithmName = function() {
ExclusiveCanonicalization.prototype.getAlgorithmName = function () {
return "http://www.w3.org/2001/10/xml-exc-c14n#";

@@ -260,12 +351,14 @@ };

function ExclusiveCanonicalizationWithComments() {
ExclusiveCanonicalization.call(this);
this.includeComments = true;
};
ExclusiveCanonicalization.call(this);
this.includeComments = true;
}
ExclusiveCanonicalizationWithComments.prototype = Object.create(ExclusiveCanonicalization.prototype);
ExclusiveCanonicalizationWithComments.prototype = Object.create(
ExclusiveCanonicalization.prototype
);
ExclusiveCanonicalizationWithComments.prototype.constructor = ExclusiveCanonicalizationWithComments;
ExclusiveCanonicalizationWithComments.prototype.getAlgorithmName = function() {
ExclusiveCanonicalizationWithComments.prototype.getAlgorithmName = function () {
return "http://www.w3.org/2001/10/xml-exc-c14n#WithComments";
};

@@ -1,32 +0,16 @@

var xpath = require('xpath')
, Dom = require('@xmldom/xmldom').DOMParser
, utils = require('./utils')
, c14n = require('./c14n-canonicalization')
, execC14n = require('./exclusive-canonicalization')
, EnvelopedSignature = require('./enveloped-signature').EnvelopedSignature
, crypto = require('crypto')
, fs = require('fs')
var xpath = require("xpath"),
Dom = require("@xmldom/xmldom").DOMParser,
utils = require("./utils"),
c14n = require("./c14n-canonicalization"),
execC14n = require("./exclusive-canonicalization"),
EnvelopedSignature = require("./enveloped-signature").EnvelopedSignature,
StringKeyInfo = require("./string-key-info"),
FileKeyInfo = require("./file-key-info"),
crypto = require("crypto");
exports.SignedXml = SignedXml
exports.FileKeyInfo = FileKeyInfo
exports.SignedXml = SignedXml;
exports.StringKeyInfo = StringKeyInfo;
exports.FileKeyInfo = FileKeyInfo;
/**
* A key info provider implementation
*
*/
function FileKeyInfo(file) {
this.file = file
this.getKeyInfo = function(key, prefix) {
prefix = prefix || ''
prefix = prefix ? prefix + ':' : prefix
return "<" + prefix + "X509Data></" + prefix + "X509Data>"
}
this.getKey = function(keyInfo) {
return fs.readFileSync(this.file)
}
}
/**
* Hash algorithm implementation

@@ -36,44 +20,40 @@ *

function SHA1() {
this.getHash = function (xml) {
var shasum = crypto.createHash("sha1");
shasum.update(xml, "utf8");
var res = shasum.digest("base64");
return res;
};
this.getHash = function(xml) {
var shasum = crypto.createHash('sha1')
shasum.update(xml, 'utf8')
var res = shasum.digest('base64')
return res
}
this.getAlgorithmName = function() {
return "http://www.w3.org/2000/09/xmldsig#sha1"
}
this.getAlgorithmName = function () {
return "http://www.w3.org/2000/09/xmldsig#sha1";
};
}
function SHA256() {
this.getHash = function (xml) {
var shasum = crypto.createHash("sha256");
shasum.update(xml, "utf8");
var res = shasum.digest("base64");
return res;
};
this.getHash = function(xml) {
var shasum = crypto.createHash('sha256')
shasum.update(xml, 'utf8')
var res = shasum.digest('base64')
return res
}
this.getAlgorithmName = function() {
return "http://www.w3.org/2001/04/xmlenc#sha256"
}
this.getAlgorithmName = function () {
return "http://www.w3.org/2001/04/xmlenc#sha256";
};
}
function SHA512() {
this.getHash = function (xml) {
var shasum = crypto.createHash("sha512");
shasum.update(xml, "utf8");
var res = shasum.digest("base64");
return res;
};
this.getHash = function(xml) {
var shasum = crypto.createHash('sha512')
shasum.update(xml, 'utf8')
var res = shasum.digest('base64')
return res
}
this.getAlgorithmName = function() {
return "http://www.w3.org/2001/04/xmlenc#sha512"
}
this.getAlgorithmName = function () {
return "http://www.w3.org/2001/04/xmlenc#sha512";
};
}
/**

@@ -84,34 +64,31 @@ * Signature algorithm implementation

function RSASHA1() {
/**
* Sign the given string using the given key
*
*/
this.getSignature = function(signedInfo, signingKey, callback) {
var signer = crypto.createSign("RSA-SHA1")
signer.update(signedInfo)
var res = signer.sign(signingKey, 'base64')
if (callback) callback(null, res)
return res
}
* Sign the given string using the given key
*
*/
this.getSignature = function (signedInfo, signingKey, callback) {
var signer = crypto.createSign("RSA-SHA1");
signer.update(signedInfo);
var res = signer.sign(signingKey, "base64");
if (callback) callback(null, res);
return res;
};
/**
* Verify the given signature of the given string using key
*
*/
this.verifySignature = function(str, key, signatureValue, callback) {
var verifier = crypto.createVerify("RSA-SHA1")
verifier.update(str)
var res = verifier.verify(key, signatureValue, 'base64')
if (callback) callback(null, res)
return res
}
* Verify the given signature of the given string using key
*
*/
this.verifySignature = function (str, key, signatureValue, callback) {
var verifier = crypto.createVerify("RSA-SHA1");
verifier.update(str);
var res = verifier.verify(key, signatureValue, "base64");
if (callback) callback(null, res);
return res;
};
this.getAlgorithmName = function() {
return "http://www.w3.org/2000/09/xmldsig#rsa-sha1"
}
this.getAlgorithmName = function () {
return "http://www.w3.org/2000/09/xmldsig#rsa-sha1";
};
}
/**

@@ -122,31 +99,29 @@ * Signature algorithm implementation

function RSASHA256() {
/**
* Sign the given string using the given key
*
*/
this.getSignature = function(signedInfo, signingKey, callback) {
var signer = crypto.createSign("RSA-SHA256")
signer.update(signedInfo)
var res = signer.sign(signingKey, 'base64')
if (callback) callback(null, res)
return res
}
* Sign the given string using the given key
*
*/
this.getSignature = function (signedInfo, signingKey, callback) {
var signer = crypto.createSign("RSA-SHA256");
signer.update(signedInfo);
var res = signer.sign(signingKey, "base64");
if (callback) callback(null, res);
return res;
};
/**
* Verify the given signature of the given string using key
*
*/
this.verifySignature = function(str, key, signatureValue, callback) {
var verifier = crypto.createVerify("RSA-SHA256")
verifier.update(str)
var res = verifier.verify(key, signatureValue, 'base64')
if (callback) callback(null, res)
return res
}
* Verify the given signature of the given string using key
*
*/
this.verifySignature = function (str, key, signatureValue, callback) {
var verifier = crypto.createVerify("RSA-SHA256");
verifier.update(str);
var res = verifier.verify(key, signatureValue, "base64");
if (callback) callback(null, res);
return res;
};
this.getAlgorithmName = function() {
return "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"
}
this.getAlgorithmName = function () {
return "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256";
};
}

@@ -159,54 +134,51 @@

function RSASHA512() {
/**
* Sign the given string using the given key
*
*/
this.getSignature = function(signedInfo, signingKey, callback) {
var signer = crypto.createSign("RSA-SHA512")
signer.update(signedInfo)
var res = signer.sign(signingKey, 'base64')
if (callback) callback(null, res)
return res
}
* Sign the given string using the given key
*
*/
this.getSignature = function (signedInfo, signingKey, callback) {
var signer = crypto.createSign("RSA-SHA512");
signer.update(signedInfo);
var res = signer.sign(signingKey, "base64");
if (callback) callback(null, res);
return res;
};
/**
* Verify the given signature of the given string using key
*
*/
this.verifySignature = function(str, key, signatureValue, callback) {
var verifier = crypto.createVerify("RSA-SHA512")
verifier.update(str)
var res = verifier.verify(key, signatureValue, 'base64')
if (callback) callback(null, res)
return res
}
* Verify the given signature of the given string using key
*
*/
this.verifySignature = function (str, key, signatureValue, callback) {
var verifier = crypto.createVerify("RSA-SHA512");
verifier.update(str);
var res = verifier.verify(key, signatureValue, "base64");
if (callback) callback(null, res);
return res;
};
this.getAlgorithmName = function() {
return "http://www.w3.org/2001/04/xmldsig-more#rsa-sha512"
}
this.getAlgorithmName = function () {
return "http://www.w3.org/2001/04/xmldsig-more#rsa-sha512";
};
}
function HMACSHA1() {
this.verifySignature = function(str, key, signatureValue) {
var verifier = crypto.createHmac("SHA1", key);
verifier.update(str);
var res = verifier.digest('base64');
return res === signatureValue;
};
this.verifySignature = function (str, key, signatureValue) {
var verifier = crypto.createHmac("SHA1", key);
verifier.update(str);
var res = verifier.digest("base64");
return res === signatureValue;
};
this.getAlgorithmName = function() {
return "http://www.w3.org/2000/09/xmldsig#hmac-sha1";
};
this.getAlgorithmName = function () {
return "http://www.w3.org/2000/09/xmldsig#hmac-sha1";
};
this.getSignature = function(signedInfo, signingKey) {
var verifier = crypto.createHmac("SHA1", signingKey);
verifier.update(signedInfo);
var res = verifier.digest('base64');
return res;
};
this.getSignature = function (signedInfo, signingKey) {
var verifier = crypto.createHmac("SHA1", signingKey);
verifier.update(signedInfo);
var res = verifier.digest("base64");
return res;
};
}
/**

@@ -221,16 +193,16 @@ * Extract ancestor namespaces in order to import it to root of document subset

*/
function findAncestorNs(doc, docSubsetXpath, namespaceResolver){
function findAncestorNs(doc, docSubsetXpath, namespaceResolver) {
var docSubset = xpath.selectWithResolver(docSubsetXpath, doc, namespaceResolver);
if(!Array.isArray(docSubset) || docSubset.length < 1){
if (!Array.isArray(docSubset) || docSubset.length < 1) {
return [];
}
// Remove duplicate on ancestor namespace
var ancestorNs = collectAncestorNamespaces(docSubset[0]);
var ancestorNsWithoutDuplicate = [];
for(var i=0;i<ancestorNs.length;i++){
for (var i = 0; i < ancestorNs.length; i++) {
var notOnTheList = true;
for(var v in ancestorNsWithoutDuplicate){
if(ancestorNsWithoutDuplicate[v].prefix === ancestorNs[i].prefix){
for (var v in ancestorNsWithoutDuplicate) {
if (ancestorNsWithoutDuplicate[v].prefix === ancestorNs[i].prefix) {
notOnTheList = false;

@@ -240,18 +212,18 @@ break;

}
if(notOnTheList){
if (notOnTheList) {
ancestorNsWithoutDuplicate.push(ancestorNs[i]);
}
}
// Remove namespaces which are already declared in the subset with the same prefix
var returningNs = [];
var subsetAttributes = docSubset[0].attributes;
for(var j=0;j<ancestorNsWithoutDuplicate.length;j++){
for (var j = 0; j < ancestorNsWithoutDuplicate.length; j++) {
var isUnique = true;
for(var k=0;k<subsetAttributes.length;k++){
for (var k = 0; k < subsetAttributes.length; k++) {
var nodeName = subsetAttributes[k].nodeName;
if(nodeName.search(/^xmlns:/) === -1) continue;
if (nodeName.search(/^xmlns:/) === -1) continue;
var prefix = nodeName.replace(/^xmlns:/, "");
if(ancestorNsWithoutDuplicate[j].prefix === prefix){
if (ancestorNsWithoutDuplicate[j].prefix === prefix) {
isUnique = false;

@@ -261,33 +233,34 @@ break;

}
if(isUnique){
if (isUnique) {
returningNs.push(ancestorNsWithoutDuplicate[j]);
}
}
return returningNs;
}
function collectAncestorNamespaces(node, nsArray){
if(!nsArray){
function collectAncestorNamespaces(node, nsArray) {
if (!nsArray) {
nsArray = [];
}
var parent = node.parentNode;
if(!parent){
if (!parent) {
return nsArray;
}
if(parent.attributes && parent.attributes.length > 0){
for(var i=0;i<parent.attributes.length;i++){
if (parent.attributes && parent.attributes.length > 0) {
for (var i = 0; i < parent.attributes.length; i++) {
var attr = parent.attributes[i];
if(attr && attr.nodeName && attr.nodeName.search(/^xmlns:/) !== -1){
nsArray.push({prefix: attr.nodeName.replace(/^xmlns:/, ""), namespaceURI: attr.nodeValue})
if (attr && attr.nodeName && attr.nodeName.search(/^xmlns:/) !== -1) {
nsArray.push({
prefix: attr.nodeName.replace(/^xmlns:/, ""),
namespaceURI: attr.nodeValue,
});
}
}
}
return collectAncestorNamespaces(parent, nsArray);

@@ -297,24 +270,28 @@ }

/**
* Xml signature implementation
*
* @param {string} idMode. Value of "wssecurity" will create/validate id's with the ws-security namespace
* @param {object} options. Initial configurations
*/
* Xml signature implementation
*
* @param {string} idMode. Value of "wssecurity" will create/validate id's with the ws-security namespace
* @param {object} options. Initial configurations
*/
function SignedXml(idMode, options) {
this.options = options || {};
this.idMode = idMode
this.references = []
this.id = 0
this.signingKey = null
this.signatureAlgorithm = this.options.signatureAlgorithm || "http://www.w3.org/2000/09/xmldsig#rsa-sha1";
this.keyInfoProvider = null
this.canonicalizationAlgorithm = this.options.canonicalizationAlgorithm || "http://www.w3.org/2001/10/xml-exc-c14n#"
this.signedXml = ""
this.signatureXml = ""
this.signatureNode = null
this.signatureValue = ""
this.originalXmlWithIds = ""
this.validationErrors = []
this.keyInfo = null
this.idAttributes = [ 'Id', 'ID', 'id' ];
this.idMode = idMode;
this.references = [];
this.id = 0;
this.signingKey = null;
this.signingCert = null;
this.signatureAlgorithm =
this.options.signatureAlgorithm || "http://www.w3.org/2000/09/xmldsig#rsa-sha1";
this.keyInfoProvider = null;
this.canonicalizationAlgorithm =
this.options.canonicalizationAlgorithm || "http://www.w3.org/2001/10/xml-exc-c14n#";
this.inclusiveNamespacesPrefixList = this.options.inclusiveNamespacesPrefixList || "";
this.signedXml = "";
this.signatureXml = "";
this.signatureNode = null;
this.signatureValue = "";
this.originalXmlWithIds = "";
this.validationErrors = [];
this.keyInfo = null;
this.idAttributes = ["Id", "ID", "id"];
if (this.options.idAttribute) this.idAttributes.splice(0, 0, this.options.idAttribute);

@@ -325,22 +302,24 @@ this.implicitTransforms = this.options.implicitTransforms || [];

SignedXml.CanonicalizationAlgorithms = {
'http://www.w3.org/TR/2001/REC-xml-c14n-20010315': c14n.C14nCanonicalization,
'http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments': c14n.C14nCanonicalizationWithComments,
'http://www.w3.org/2001/10/xml-exc-c14n#': execC14n.ExclusiveCanonicalization,
'http://www.w3.org/2001/10/xml-exc-c14n#WithComments': execC14n.ExclusiveCanonicalizationWithComments,
'http://www.w3.org/2000/09/xmldsig#enveloped-signature': EnvelopedSignature
}
"http://www.w3.org/TR/2001/REC-xml-c14n-20010315": c14n.C14nCanonicalization,
"http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments":
c14n.C14nCanonicalizationWithComments,
"http://www.w3.org/2001/10/xml-exc-c14n#": execC14n.ExclusiveCanonicalization,
"http://www.w3.org/2001/10/xml-exc-c14n#WithComments":
execC14n.ExclusiveCanonicalizationWithComments,
"http://www.w3.org/2000/09/xmldsig#enveloped-signature": EnvelopedSignature,
};
SignedXml.HashAlgorithms = {
'http://www.w3.org/2000/09/xmldsig#sha1': SHA1,
'http://www.w3.org/2001/04/xmlenc#sha256': SHA256,
'http://www.w3.org/2001/04/xmlenc#sha512': SHA512
}
"http://www.w3.org/2000/09/xmldsig#sha1": SHA1,
"http://www.w3.org/2001/04/xmlenc#sha256": SHA256,
"http://www.w3.org/2001/04/xmlenc#sha512": SHA512,
};
SignedXml.SignatureAlgorithms = {
'http://www.w3.org/2000/09/xmldsig#rsa-sha1': RSASHA1,
'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256': RSASHA256,
'http://www.w3.org/2001/04/xmldsig-more#rsa-sha512': RSASHA512,
"http://www.w3.org/2000/09/xmldsig#rsa-sha1": RSASHA1,
"http://www.w3.org/2001/04/xmldsig-more#rsa-sha256": RSASHA256,
"http://www.w3.org/2001/04/xmldsig-more#rsa-sha512": RSASHA512,
// Disabled by default due to key confusion concerns.
// 'http://www.w3.org/2000/09/xmldsig#hmac-sha1': HMACSHA1
}
};

@@ -354,8 +333,8 @@ /**

SignedXml.SignatureAlgorithms = {
'http://www.w3.org/2000/09/xmldsig#hmac-sha1': HMACSHA1
}
}
"http://www.w3.org/2000/09/xmldsig#hmac-sha1": HMACSHA1,
};
};
SignedXml.defaultNsForPrefix = {
ds: 'http://www.w3.org/2000/09/xmldsig#'
ds: "http://www.w3.org/2000/09/xmldsig#",
};

@@ -365,32 +344,32 @@

SignedXml.prototype.checkSignature = function(xml, callback) {
if (callback != null && typeof callback !== 'function') {
throw new Error("Last paramater must be a callback function")
SignedXml.prototype.checkSignature = function (xml, callback) {
if (callback != null && typeof callback !== "function") {
throw new Error("Last parameter must be a callback function");
}
this.validationErrors = []
this.signedXml = xml
this.validationErrors = [];
this.signedXml = xml;
if (!this.keyInfoProvider) {
var err = new Error("cannot validate signature since no key info resolver was provided")
var err = new Error("cannot validate signature since no key info resolver was provided");
if (!callback) {
throw err
throw err;
} else {
callback(err)
return
callback(err);
return;
}
}
this.signingKey = this.keyInfoProvider.getKey(this.keyInfo)
this.signingKey = this.keyInfoProvider.getKey(this.keyInfo);
if (!this.signingKey) {
var err = new Error("key info provider could not resolve key info " + this.keyInfo)
var err2 = new Error("key info provider could not resolve key info " + this.keyInfo);
if (!callback) {
throw err
throw err2;
} else {
callback(err)
return
callback(err2);
return;
}
}
var doc = new Dom().parseFromString(xml)
var doc = new Dom().parseFromString(xml);

@@ -401,4 +380,4 @@ if (!this.validateReferences(doc)) {

} else {
callback(new Error('Could not validate references'))
return
callback(new Error("Could not validate references"));
return;
}

@@ -408,29 +387,32 @@ }

if (!callback) {
//Syncronous flow
// Synchronous flow
if (!this.validateSignatureValue(doc)) {
return false
return false;
}
return true
return true;
} else {
//Asyncronous flow
// Asynchronous flow
this.validateSignatureValue(doc, function (err, isValidSignature) {
if (err) {
this.validationErrors.push("invalid signature: the signature value " +
this.signatureValue + " is incorrect")
callback(err)
this.validationErrors.push(
"invalid signature: the signature value " + this.signatureValue + " is incorrect"
);
callback(err);
} else {
callback(null, isValidSignature)
callback(null, isValidSignature);
}
})
});
}
}
};
SignedXml.prototype.getCanonSignedInfoXml = function(doc) {
var signedInfo = utils.findChilds(this.signatureNode, "SignedInfo")
if (signedInfo.length==0) throw new Error("could not find SignedInfo element in the message")
if(this.canonicalizationAlgorithm === "http://www.w3.org/TR/2001/REC-xml-c14n-20010315"
|| this.canonicalizationAlgorithm === "http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments")
{
if(!doc || typeof(doc) !== "object"){
SignedXml.prototype.getCanonSignedInfoXml = function (doc) {
var signedInfo = utils.findChilds(this.signatureNode, "SignedInfo");
if (signedInfo.length == 0) throw new Error("could not find SignedInfo element in the message");
if (
this.canonicalizationAlgorithm === "http://www.w3.org/TR/2001/REC-xml-c14n-20010315" ||
this.canonicalizationAlgorithm ===
"http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments"
) {
if (!doc || typeof doc !== "object") {
throw new Error(

@@ -441,3 +423,3 @@ "When canonicalization method is non-exclusive, whole xml dom must be provided as an argument"

}
/**

@@ -448,15 +430,15 @@ * Search for ancestor namespaces before canonicalization.

ancestorNamespaces = findAncestorNs(doc, "//*[local-name()='SignedInfo']");
var c14nOptions = {
ancestorNamespaces: ancestorNamespaces
ancestorNamespaces: ancestorNamespaces,
};
return this.getCanonXml([this.canonicalizationAlgorithm], signedInfo[0], c14nOptions)
}
return this.getCanonXml([this.canonicalizationAlgorithm], signedInfo[0], c14nOptions);
};
SignedXml.prototype.getCanonReferenceXml = function(doc, ref, node) {
SignedXml.prototype.getCanonReferenceXml = function (doc, ref, node) {
/**
* Search for ancestor namespaces before canonicalization.
*/
if(Array.isArray(ref.transforms)){
ref.ancestorNamespaces = findAncestorNs(doc, ref.xpath, this.namespaceResolver)
if (Array.isArray(ref.transforms)) {
ref.ancestorNamespaces = findAncestorNs(doc, ref.xpath, this.namespaceResolver);
}

@@ -466,59 +448,58 @@

inclusiveNamespacesPrefixList: ref.inclusiveNamespacesPrefixList,
ancestorNamespaces: ref.ancestorNamespaces
}
ancestorNamespaces: ref.ancestorNamespaces,
};
return this.getCanonXml(ref.transforms, node, c14nOptions)
}
return this.getCanonXml(ref.transforms, node, c14nOptions);
};
SignedXml.prototype.validateSignatureValue = function(doc, callback) {
var signedInfoCanon = this.getCanonSignedInfoXml(doc)
var signer = this.findSignatureAlgorithm(this.signatureAlgorithm)
var res = signer.verifySignature(signedInfoCanon, this.signingKey, this.signatureValue, callback)
if (!res && !callback) this.validationErrors.push("invalid signature: the signature value " +
this.signatureValue + " is incorrect")
return res
}
SignedXml.prototype.validateSignatureValue = function (doc, callback) {
var signedInfoCanon = this.getCanonSignedInfoXml(doc);
var signer = this.findSignatureAlgorithm(this.signatureAlgorithm);
var res = signer.verifySignature(signedInfoCanon, this.signingKey, this.signatureValue, callback);
if (!res && !callback)
this.validationErrors.push(
"invalid signature: the signature value " + this.signatureValue + " is incorrect"
);
return res;
};
SignedXml.prototype.calculateSignatureValue = function(doc, callback) {
var signedInfoCanon = this.getCanonSignedInfoXml(doc)
var signer = this.findSignatureAlgorithm(this.signatureAlgorithm)
this.signatureValue = signer.getSignature(signedInfoCanon, this.signingKey, callback)
}
SignedXml.prototype.calculateSignatureValue = function (doc, callback) {
var signedInfoCanon = this.getCanonSignedInfoXml(doc);
var signer = this.findSignatureAlgorithm(this.signatureAlgorithm);
this.signatureValue = signer.getSignature(signedInfoCanon, this.signingKey, callback);
};
SignedXml.prototype.findSignatureAlgorithm = function(name) {
var algo = SignedXml.SignatureAlgorithms[name]
if (algo) return new algo()
SignedXml.prototype.findSignatureAlgorithm = function (name) {
var algo = SignedXml.SignatureAlgorithms[name];
if (algo) return new algo();
else throw new Error("signature algorithm '" + name + "' is not supported");
}
};
SignedXml.prototype.findCanonicalizationAlgorithm = function(name) {
var algo = SignedXml.CanonicalizationAlgorithms[name]
if (algo) return new algo()
SignedXml.prototype.findCanonicalizationAlgorithm = function (name) {
var algo = SignedXml.CanonicalizationAlgorithms[name];
if (algo) return new algo();
else throw new Error("canonicalization algorithm '" + name + "' is not supported");
}
};
SignedXml.prototype.findHashAlgorithm = function(name) {
var algo = SignedXml.HashAlgorithms[name]
if (algo) return new algo()
SignedXml.prototype.findHashAlgorithm = function (name) {
var algo = SignedXml.HashAlgorithms[name];
if (algo) return new algo();
else throw new Error("hash algorithm '" + name + "' is not supported");
}
};
SignedXml.prototype.validateReferences = function(doc) {
SignedXml.prototype.validateReferences = function (doc) {
for (var r in this.references) {
if (!this.references.hasOwnProperty(r)) continue;
var ref = this.references[r]
var ref = this.references[r];
var uri = ref.uri[0]=="#" ? ref.uri.substring(1) : ref.uri
var uri = ref.uri[0] == "#" ? ref.uri.substring(1) : ref.uri;
var elem = [];
if (uri=="") {
elem = xpath.select("//*", doc)
}
else if (uri.indexOf("'") != -1) {
if (uri == "") {
elem = xpath.select("//*", doc);
} else if (uri.indexOf("'") != -1) {
// xpath injection
throw new Error("Cannot validate a uri with quotes inside it");
}
else {
} else {
var elemXpath;

@@ -528,4 +509,5 @@ var num_elements_for_id = 0;

if (!this.idAttributes.hasOwnProperty(index)) continue;
var tmp_elemXpath = "//*[@*[local-name(.)='" + this.idAttributes[index] + "']='" + uri + "']";
var tmp_elem = xpath.select(tmp_elemXpath, doc)
var tmp_elemXpath =
"//*[@*[local-name(.)='" + this.idAttributes[index] + "']='" + uri + "']";
var tmp_elem = xpath.select(tmp_elemXpath, doc);
num_elements_for_id += tmp_elem.length;

@@ -538,5 +520,7 @@ if (tmp_elem.length > 0) {

if (num_elements_for_id > 1) {
throw new Error('Cannot validate a document which contains multiple elements with the ' +
'same value for the ID / Id / Id attributes, in order to prevent ' +
'signature wrapping attack.');
throw new Error(
"Cannot validate a document which contains multiple elements with the " +
"same value for the ID / Id / Id attributes, in order to prevent " +
"signature wrapping attack."
);
}

@@ -547,23 +531,31 @@

if (elem.length==0) {
this.validationErrors.push("invalid signature: the signature refernces an element with uri "+
ref.uri + " but could not find such element in the xml")
return false
if (elem.length == 0) {
this.validationErrors.push(
"invalid signature: the signature references an element with uri " +
ref.uri +
" but could not find such element in the xml"
);
return false;
}
var canonXml = this.getCanonReferenceXml(doc, ref, elem[0])
var hash = this.findHashAlgorithm(ref.digestAlgorithm)
var digest = hash.getHash(canonXml)
var canonXml = this.getCanonReferenceXml(doc, ref, elem[0]);
var hash = this.findHashAlgorithm(ref.digestAlgorithm);
var digest = hash.getHash(canonXml);
if (!validateDigestValue(digest, ref.digestValue)) {
this.validationErrors.push("invalid signature: for uri " + ref.uri +
" calculated digest is " + digest +
" but the xml to validate supplies digest " + ref.digestValue)
this.validationErrors.push(
"invalid signature: for uri " +
ref.uri +
" calculated digest is " +
digest +
" but the xml to validate supplies digest " +
ref.digestValue
);
return false
return false;
}
}
return true
}
return true;
};

@@ -576,11 +568,11 @@ function validateDigestValue(digest, expectedDigest) {

if (+majorVersion >= 6) {
buffer = Buffer.from(digest, 'base64');
expectedBuffer = Buffer.from(expectedDigest, 'base64');
buffer = Buffer.from(digest, "base64");
expectedBuffer = Buffer.from(expectedDigest, "base64");
} else {
// Compatibility with Node < 5.10.0
buffer = new Buffer(digest, 'base64');
expectedBuffer = new Buffer(expectedDigest, 'base64');
buffer = new Buffer(digest, "base64");
expectedBuffer = new Buffer(expectedDigest, "base64");
}
if (typeof buffer.equals === 'function') {
if (typeof buffer.equals === "function") {
return buffer.equals(expectedBuffer);

@@ -603,4 +595,4 @@ }

SignedXml.prototype.loadSignature = function(signatureNode) {
if (typeof signatureNode === 'string') {
SignedXml.prototype.loadSignature = function (signatureNode) {
if (typeof signatureNode === "string") {
this.signatureNode = signatureNode = new Dom().parseFromString(signatureNode);

@@ -613,12 +605,21 @@ } else {

var nodes = xpath.select(".//*[local-name(.)='CanonicalizationMethod']/@Algorithm", signatureNode)
if (nodes.length==0) throw new Error("could not find CanonicalizationMethod/@Algorithm element")
this.canonicalizationAlgorithm = nodes[0].value
var nodes = xpath.select(
".//*[local-name(.)='CanonicalizationMethod']/@Algorithm",
signatureNode
);
if (nodes.length == 0)
throw new Error("could not find CanonicalizationMethod/@Algorithm element");
this.canonicalizationAlgorithm = nodes[0].value;
this.signatureAlgorithm =
utils.findFirst(signatureNode, ".//*[local-name(.)='SignatureMethod']/@Algorithm").value
this.signatureAlgorithm = utils.findFirst(
signatureNode,
".//*[local-name(.)='SignatureMethod']/@Algorithm"
).value;
this.references = []
var references = xpath.select(".//*[local-name(.)='SignedInfo']/*[local-name(.)='Reference']", signatureNode)
if (references.length == 0) throw new Error("could not find any Reference elements")
this.references = [];
var references = xpath.select(
".//*[local-name(.)='SignedInfo']/*[local-name(.)='Reference']",
signatureNode
);
if (references.length == 0) throw new Error("could not find any Reference elements");

@@ -628,10 +629,11 @@ for (var i in references) {

this.loadReference(references[i])
this.loadReference(references[i]);
}
this.signatureValue =
utils.findFirst(signatureNode, ".//*[local-name(.)='SignatureValue']/text()").data.replace(/\r?\n/g, '')
this.signatureValue = utils
.findFirst(signatureNode, ".//*[local-name(.)='SignatureValue']/text()")
.data.replace(/\r?\n/g, "");
this.keyInfo = xpath.select(".//*[local-name(.)='KeyInfo']", signatureNode)
}
this.keyInfo = xpath.select(".//*[local-name(.)='KeyInfo']", signatureNode);
};

@@ -642,40 +644,43 @@ /**

*/
SignedXml.prototype.loadReference = function(ref) {
var nodes = utils.findChilds(ref, "DigestMethod")
if (nodes.length==0) throw new Error("could not find DigestMethod in reference " + ref.toString())
var digestAlgoNode = nodes[0]
SignedXml.prototype.loadReference = function (ref) {
var nodes = utils.findChilds(ref, "DigestMethod");
if (nodes.length == 0)
throw new Error("could not find DigestMethod in reference " + ref.toString());
var digestAlgoNode = nodes[0];
var attr = utils.findAttr(digestAlgoNode, "Algorithm")
if (!attr) throw new Error("could not find Algorithm attribute in node " + digestAlgoNode.toString())
var digestAlgo = attr.value
var attr = utils.findAttr(digestAlgoNode, "Algorithm");
if (!attr)
throw new Error("could not find Algorithm attribute in node " + digestAlgoNode.toString());
var digestAlgo = attr.value;
nodes = utils.findChilds(ref, "DigestValue")
if (nodes.length==0) throw new Error("could not find DigestValue node in reference " + ref.toString())
if (nodes[0].childNodes.length==0 || !nodes[0].firstChild.data)
{
throw new Error("could not find the value of DigestValue in " + nodes[0].toString())
nodes = utils.findChilds(ref, "DigestValue");
if (nodes.length == 0)
throw new Error("could not find DigestValue node in reference " + ref.toString());
if (nodes[0].childNodes.length == 0 || !nodes[0].firstChild.data) {
throw new Error("could not find the value of DigestValue in " + nodes[0].toString());
}
var digestValue = nodes[0].firstChild.data
var digestValue = nodes[0].firstChild.data;
var transforms = []
var transforms = [];
var inclusiveNamespacesPrefixList;
nodes = utils.findChilds(ref, "Transforms")
if (nodes.length!=0) {
var transformsNode = nodes[0]
var transformsAll = utils.findChilds(transformsNode, "Transform")
nodes = utils.findChilds(ref, "Transforms");
if (nodes.length != 0) {
var transformsNode = nodes[0];
var transformsAll = utils.findChilds(transformsNode, "Transform");
for (var t in transformsAll) {
if (!transformsAll.hasOwnProperty(t)) continue;
var trans = transformsAll[t]
transforms.push(utils.findAttr(trans, "Algorithm").value)
var trans = transformsAll[t];
transforms.push(utils.findAttr(trans, "Algorithm").value);
}
var inclusiveNamespaces = utils.findChilds(trans, "InclusiveNamespaces")
var inclusiveNamespaces = utils.findChilds(trans, "InclusiveNamespaces");
if (inclusiveNamespaces.length > 0) {
//Should really only be one prefix list, but maybe there's some circumstances where more than one to lets handle it
for (var i = 0; i<inclusiveNamespaces.length; i++) {
for (var i = 0; i < inclusiveNamespaces.length; i++) {
if (inclusiveNamespacesPrefixList) {
inclusiveNamespacesPrefixList = inclusiveNamespacesPrefixList + " " + inclusiveNamespaces[i].getAttribute('PrefixList');
inclusiveNamespacesPrefixList =
inclusiveNamespacesPrefixList + " " + inclusiveNamespaces[i].getAttribute("PrefixList");
} else {
inclusiveNamespacesPrefixList = inclusiveNamespaces[i].getAttribute('PrefixList');
inclusiveNamespacesPrefixList = inclusiveNamespaces[i].getAttribute("PrefixList");
}

@@ -686,39 +691,59 @@ }

var hasImplicitTransforms = (Array.isArray(this.implicitTransforms) && this.implicitTransforms.length > 0);
if(hasImplicitTransforms){
this.implicitTransforms.forEach(function(t){
var hasImplicitTransforms =
Array.isArray(this.implicitTransforms) && this.implicitTransforms.length > 0;
if (hasImplicitTransforms) {
this.implicitTransforms.forEach(function (t) {
transforms.push(t);
});
}
/**
* DigestMethods take an octet stream rather than a node set. If the output of the last transform is a node set, we
* need to canonicalize the node set to an octet stream using non-exclusive canonicalization. If there are no
* transforms, we need to canonicalize because URI dereferencing for a same-document reference will return a node-set.
* See:
* https://www.w3.org/TR/xmldsig-core1/#sec-DigestMethod
* https://www.w3.org/TR/xmldsig-core1/#sec-ReferenceProcessingModel
* https://www.w3.org/TR/xmldsig-core1/#sec-Same-Document
*/
if (transforms.length === 0 || transforms[transforms.length - 1] === "http://www.w3.org/2000/09/xmldsig#enveloped-signature") {
transforms.push("http://www.w3.org/TR/2001/REC-xml-c14n-20010315")
/**
* DigestMethods take an octet stream rather than a node set. If the output of the last transform is a node set, we
* need to canonicalize the node set to an octet stream using non-exclusive canonicalization. If there are no
* transforms, we need to canonicalize because URI dereferencing for a same-document reference will return a node-set.
* See:
* https://www.w3.org/TR/xmldsig-core1/#sec-DigestMethod
* https://www.w3.org/TR/xmldsig-core1/#sec-ReferenceProcessingModel
* https://www.w3.org/TR/xmldsig-core1/#sec-Same-Document
*/
if (
transforms.length === 0 ||
transforms[transforms.length - 1] === "http://www.w3.org/2000/09/xmldsig#enveloped-signature"
) {
transforms.push("http://www.w3.org/TR/2001/REC-xml-c14n-20010315");
}
this.addReference(null, transforms, digestAlgo, utils.findAttr(ref, "URI").value, digestValue, inclusiveNamespacesPrefixList, false)
}
this.addReference(
null,
transforms,
digestAlgo,
utils.findAttr(ref, "URI").value,
digestValue,
inclusiveNamespacesPrefixList,
false
);
};
SignedXml.prototype.addReference = function(xpath, transforms, digestAlgorithm, uri, digestValue, inclusiveNamespacesPrefixList, isEmptyUri) {
SignedXml.prototype.addReference = function (
xpath,
transforms,
digestAlgorithm,
uri,
digestValue,
inclusiveNamespacesPrefixList,
isEmptyUri
) {
this.references.push({
"xpath": xpath,
"transforms": transforms ? transforms : ["http://www.w3.org/2001/10/xml-exc-c14n#"] ,
"digestAlgorithm": digestAlgorithm ? digestAlgorithm : "http://www.w3.org/2000/09/xmldsig#sha1",
"uri": uri,
"digestValue": digestValue,
"inclusiveNamespacesPrefixList": inclusiveNamespacesPrefixList,
"isEmptyUri": isEmptyUri
xpath: xpath,
transforms: transforms ? transforms : ["http://www.w3.org/2001/10/xml-exc-c14n#"],
digestAlgorithm: digestAlgorithm ? digestAlgorithm : "http://www.w3.org/2000/09/xmldsig#sha1",
uri: uri,
digestValue: digestValue,
inclusiveNamespacesPrefixList: inclusiveNamespacesPrefixList,
isEmptyUri: isEmptyUri,
});
}
};
/**
* Compute the signature of the given xml (usign the already defined settings)
* Compute the signature of the given xml (using the already defined settings)
*

@@ -737,18 +762,18 @@ * Options:

*/
SignedXml.prototype.computeSignature = function(xml, opts, callback) {
if (typeof opts === 'function' && callback == null) {
callback = opts
SignedXml.prototype.computeSignature = function (xml, opts, callback) {
if (typeof opts === "function" && callback == null) {
callback = opts;
}
if (callback != null && typeof callback !== 'function') {
throw new Error("Last paramater must be a callback function")
if (callback != null && typeof callback !== "function") {
throw new Error("Last parameter must be a callback function");
}
var doc = new Dom().parseFromString(xml),
xmlNsAttr = "xmlns",
signatureAttrs = [],
location,
attrs,
prefix,
currentPrefix;
xmlNsAttr = "xmlns",
signatureAttrs = [],
location,
attrs,
prefix,
currentPrefix;

@@ -764,6 +789,6 @@ var validActions = ["append", "prepend", "before", "after"];

this.namespaceResolver = {
lookupNamespaceURI: function(prefix) {
lookupNamespaceURI: function (prefix) {
return existingPrefixes[prefix];
}
}
},
};

@@ -776,9 +801,13 @@ // defaults to the root node

if (validActions.indexOf(location.action) === -1) {
var err = new Error("location.action option has an invalid action: " + location.action +
", must be any of the following values: " + validActions.join(", "));
var err = new Error(
"location.action option has an invalid action: " +
location.action +
", must be any of the following values: " +
validActions.join(", ")
);
if (!callback) {
throw err;
} else {
callback(err, null)
return
callback(err, null);
return;
}

@@ -795,5 +824,5 @@ }

Object.keys(attrs).forEach(function(name) {
Object.keys(attrs).forEach(function (name) {
if (name !== "xmlns" && name !== xmlNsAttr) {
signatureAttrs.push(name + "=\"" + attrs[name] + "\"");
signatureAttrs.push(name + '="' + attrs[name] + '"');
}

@@ -803,15 +832,15 @@ });

// add the xml namespace attribute
signatureAttrs.push(xmlNsAttr + "=\"http://www.w3.org/2000/09/xmldsig#\"");
signatureAttrs.push(xmlNsAttr + '="http://www.w3.org/2000/09/xmldsig#"');
var signatureXml = "<" + currentPrefix + "Signature " + signatureAttrs.join(" ") + ">"
var signatureXml = "<" + currentPrefix + "Signature " + signatureAttrs.join(" ") + ">";
signatureXml += this.createSignedInfo(doc, prefix);
signatureXml += this.getKeyInfo(prefix)
signatureXml += "</" + currentPrefix + "Signature>"
signatureXml += this.getKeyInfo(prefix);
signatureXml += "</" + currentPrefix + "Signature>";
this.originalXmlWithIds = doc.toString()
this.originalXmlWithIds = doc.toString();
var existingPrefixesString = ""
Object.keys(existingPrefixes).forEach(function(key) {
existingPrefixesString += "xmlns:" + key + '="' + existingPrefixes[key] + '" '
var existingPrefixesString = "";
Object.keys(existingPrefixes).forEach(function (key) {
existingPrefixesString += "xmlns:" + key + '="' + existingPrefixes[key] + '" ';
});

@@ -821,5 +850,5 @@

// This only works if the prefix and namespace match with those in te xml
var dummySignatureWrapper = "<Dummy " + existingPrefixesString + ">" + signatureXml + "</Dummy>"
var xml = new Dom().parseFromString(dummySignatureWrapper)
var signatureDoc = xml.documentElement.firstChild;
var dummySignatureWrapper = "<Dummy " + existingPrefixesString + ">" + signatureXml + "</Dummy>";
var nodeXml = new Dom().parseFromString(dummySignatureWrapper);
var signatureDoc = nodeXml.documentElement.firstChild;

@@ -829,8 +858,10 @@ var referenceNode = xpath.select(location.reference, doc);

if (!referenceNode || referenceNode.length === 0) {
var err = new Error("the following xpath cannot be used because it was not found: " + location.reference);
var err2 = new Error(
"the following xpath cannot be used because it was not found: " + location.reference
);
if (!callback) {
throw err
throw err2;
} else {
callback(err, null)
return
callback(err2, null);
return;
}

@@ -851,11 +882,11 @@ }

this.signatureNode = signatureDoc
var signedInfoNode = utils.findChilds(this.signatureNode, "SignedInfo")
this.signatureNode = signatureDoc;
var signedInfoNode = utils.findChilds(this.signatureNode, "SignedInfo");
if (signedInfoNode.length == 0) {
var err = new Error("could not find SignedInfo element in the message")
var err3 = new Error("could not find SignedInfo element in the message");
if (!callback) {
throw err
throw err3;
} else {
callback(err)
return
callback(err3);
return;
}

@@ -867,37 +898,43 @@ }

//Synchronous flow
this.calculateSignatureValue(doc)
signatureDoc.insertBefore(this.createSignature(prefix), signedInfoNode.nextSibling)
this.signatureXml = signatureDoc.toString()
this.signedXml = doc.toString()
this.calculateSignatureValue(doc);
signatureDoc.insertBefore(this.createSignature(prefix), signedInfoNode.nextSibling);
this.signatureXml = signatureDoc.toString();
this.signedXml = doc.toString();
} else {
var self = this
var self = this;
//Asynchronous flow
this.calculateSignatureValue(doc, function(err, signature) {
this.calculateSignatureValue(doc, function (err, signature) {
if (err) {
callback(err)
callback(err);
} else {
self.signatureValue = signature
signatureDoc.insertBefore(self.createSignature(prefix), signedInfoNode.nextSibling)
self.signatureXml = signatureDoc.toString()
self.signedXml = doc.toString()
callback(null, self)
self.signatureValue = signature;
signatureDoc.insertBefore(self.createSignature(prefix), signedInfoNode.nextSibling);
self.signatureXml = signatureDoc.toString();
self.signedXml = doc.toString();
callback(null, self);
}
})
});
}
}
};
SignedXml.prototype.getKeyInfo = function(prefix) {
var res = ""
var currentPrefix
SignedXml.prototype.getKeyInfo = function (prefix) {
var res = "";
var currentPrefix;
currentPrefix = prefix || ''
currentPrefix = currentPrefix ? currentPrefix + ':' : currentPrefix
currentPrefix = prefix || "";
currentPrefix = currentPrefix ? currentPrefix + ":" : currentPrefix;
if (this.keyInfoProvider) {
res += "<" + currentPrefix + "KeyInfo>"
res += this.keyInfoProvider.getKeyInfo(this.signingKey, prefix)
res += "</" + currentPrefix + "KeyInfo>"
var keyInfoAttrs = "";
if (this.keyInfoProvider.attrs) {
Object.keys(this.keyInfoProvider.attrs).forEach((name) => {
keyInfoAttrs += " " + name + '="' + this.keyInfoProvider.attrs[name] + '"';
});
}
res += "<" + currentPrefix + "KeyInfo" + keyInfoAttrs + ">";
res += this.keyInfoProvider.getKeyInfo(this.signingCert || this.signingKey, prefix);
res += "</" + currentPrefix + "KeyInfo>";
}
return res
}
return res;
};

@@ -908,17 +945,18 @@ /**

*/
SignedXml.prototype.createReferences = function(doc, prefix) {
SignedXml.prototype.createReferences = function (doc, prefix) {
var res = "";
var res = ""
prefix = prefix || "";
prefix = prefix ? prefix + ":" : prefix;
prefix = prefix || ''
prefix = prefix ? prefix + ':' : prefix
for (var n in this.references) {
if (!this.references.hasOwnProperty(n)) continue;
var ref = this.references[n]
, nodes = xpath.selectWithResolver(ref.xpath, doc, this.namespaceResolver)
var ref = this.references[n],
nodes = xpath.selectWithResolver(ref.xpath, doc, this.namespaceResolver);
if (nodes.length==0) {
throw new Error('the following xpath cannot be signed because it was not found: ' + ref.xpath)
if (nodes.length == 0) {
throw new Error(
"the following xpath cannot be signed because it was not found: " + ref.xpath
);
}

@@ -929,34 +967,60 @@

var node = nodes[h]
var node = nodes[h];
if (ref.isEmptyUri) {
res += "<" + prefix + "Reference URI=\"\">"
}
else {
res += "<" + prefix + 'Reference URI="">';
} else {
var id = this.ensureHasId(node);
ref.uri = id
res += "<" + prefix + "Reference URI=\"#" + id + "\">"
ref.uri = id;
res += "<" + prefix + 'Reference URI="#' + id + '">';
}
res += "<" + prefix + "Transforms>"
res += "<" + prefix + "Transforms>";
for (var t in ref.transforms) {
if (!ref.transforms.hasOwnProperty(t)) continue;
var trans = ref.transforms[t]
var transform = this.findCanonicalizationAlgorithm(trans)
res += "<" + prefix + "Transform Algorithm=\"" + transform.getAlgorithmName() + "\" />"
var trans = ref.transforms[t];
var transform = this.findCanonicalizationAlgorithm(trans);
res += "<" + prefix + 'Transform Algorithm="' + transform.getAlgorithmName() + '"';
if (ref.inclusiveNamespacesPrefixList) {
res += ">";
res +=
'<InclusiveNamespaces PrefixList="' +
ref.inclusiveNamespacesPrefixList +
'" xmlns="' +
transform.getAlgorithmName() +
'"/>';
res += "</" + prefix + "Transform>";
} else {
res += " />";
}
}
var canonXml = this.getCanonReferenceXml(doc, ref, node)
var canonXml = this.getCanonReferenceXml(doc, ref, node);
var digestAlgorithm = this.findHashAlgorithm(ref.digestAlgorithm)
res += "</" + prefix + "Transforms>"+
"<" + prefix + "DigestMethod Algorithm=\"" + digestAlgorithm.getAlgorithmName() + "\" />"+
"<" + prefix + "DigestValue>" + digestAlgorithm.getHash(canonXml) + "</" + prefix + "DigestValue>"+
"</" + prefix + "Reference>"
var digestAlgorithm = this.findHashAlgorithm(ref.digestAlgorithm);
res +=
"</" +
prefix +
"Transforms>" +
"<" +
prefix +
'DigestMethod Algorithm="' +
digestAlgorithm.getAlgorithmName() +
'" />' +
"<" +
prefix +
"DigestValue>" +
digestAlgorithm.getHash(canonXml) +
"</" +
prefix +
"DigestValue>" +
"</" +
prefix +
"Reference>";
}
}
return res
}
return res;
};
SignedXml.prototype.getCanonXml = function(transforms, node, options) {
SignedXml.prototype.getCanonXml = function (transforms, node, options) {
options = options || {};

@@ -966,3 +1030,3 @@ options.defaultNsForPrefix = options.defaultNsForPrefix || SignedXml.defaultNsForPrefix;

var canonXml = node.cloneNode(true) // Deep clone
var canonXml = node.cloneNode(true); // Deep clone

@@ -972,7 +1036,7 @@ for (var t in transforms) {

var transform = this.findCanonicalizationAlgorithm(transforms[t])
var transform = this.findCanonicalizationAlgorithm(transforms[t]);
canonXml = transform.process(canonXml, options);
//TODO: currently transform.process may return either Node or String value (enveloped transformation returns Node, exclusive-canonicalization returns String).
//This eitehr needs to be more explicit in the API, or all should return the same.
//exclusive-canonicalization returns String since it builds the Xml by hand. If it had used xmldom it would inccorectly minimize empty tags
//This either needs to be more explicit in the API, or all should return the same.
//exclusive-canonicalization returns String since it builds the Xml by hand. If it had used xmldom it would incorrectly minimize empty tags
//to <x/> instead of <x></x> and also incorrectly handle some delicate line break issues.

@@ -983,4 +1047,4 @@ //enveloped transformation returns Node since if it would return String consider this case:

}
return canonXml.toString()
}
return canonXml.toString();
};

@@ -991,11 +1055,12 @@ /**

*/
SignedXml.prototype.ensureHasId = function(node) {
var attr
SignedXml.prototype.ensureHasId = function (node) {
var attr;
if (this.idMode=="wssecurity") {
attr = utils.findAttr(node,
if (this.idMode == "wssecurity") {
attr = utils.findAttr(
node,
"Id",
"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd")
}
else {
"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
);
} else {
for (var index in this.idAttributes) {

@@ -1009,21 +1074,24 @@ if (!this.idAttributes.hasOwnProperty(index)) continue;

if (attr) return attr.value
if (attr) return attr.value;
//add the attribute
var id = "_" + this.id++
var id = "_" + this.id++;
if (this.idMode=="wssecurity") {
node.setAttributeNS("http://www.w3.org/2000/xmlns/",
if (this.idMode == "wssecurity") {
node.setAttributeNS(
"http://www.w3.org/2000/xmlns/",
"xmlns:wsu",
"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd")
node.setAttributeNS("http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd",
"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
);
node.setAttributeNS(
"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd",
"wsu:Id",
id)
id
);
} else {
node.setAttribute("Id", id);
}
else {
node.setAttribute("Id", id)
}
return id
}
return id;
};

@@ -1034,18 +1102,31 @@ /**

*/
SignedXml.prototype.createSignedInfo = function(doc, prefix) {
var transform = this.findCanonicalizationAlgorithm(this.canonicalizationAlgorithm)
var algo = this.findSignatureAlgorithm(this.signatureAlgorithm)
var currentPrefix
SignedXml.prototype.createSignedInfo = function (doc, prefix) {
var transform = this.findCanonicalizationAlgorithm(this.canonicalizationAlgorithm);
var algo = this.findSignatureAlgorithm(this.signatureAlgorithm);
var currentPrefix;
currentPrefix = prefix || ''
currentPrefix = currentPrefix ? currentPrefix + ':' : currentPrefix
currentPrefix = prefix || "";
currentPrefix = currentPrefix ? currentPrefix + ":" : currentPrefix;
var res = "<" + currentPrefix + "SignedInfo>"
res += "<" + currentPrefix + "CanonicalizationMethod Algorithm=\"" + transform.getAlgorithmName() + "\" />" +
"<" + currentPrefix + "SignatureMethod Algorithm=\"" + algo.getAlgorithmName() + "\" />"
var res = "<" + currentPrefix + "SignedInfo>";
res +=
"<" + currentPrefix + 'CanonicalizationMethod Algorithm="' + transform.getAlgorithmName() + '"';
if (this.inclusiveNamespacesPrefixList) {
res += ">";
res +=
'<InclusiveNamespaces PrefixList="' +
this.inclusiveNamespacesPrefixList +
'" xmlns="' +
transform.getAlgorithmName() +
'"/>';
res += "</" + currentPrefix + "CanonicalizationMethod>";
} else {
res += " />";
}
res += "<" + currentPrefix + 'SignatureMethod Algorithm="' + algo.getAlgorithmName() + '" />';
res += this.createReferences(doc, prefix)
res += "</" + currentPrefix + "SignedInfo>"
return res
}
res += this.createReferences(doc, prefix);
res += "</" + currentPrefix + "SignedInfo>";
return res;
};

@@ -1056,34 +1137,41 @@ /**

*/
SignedXml.prototype.createSignature = function(prefix) {
var xmlNsAttr = 'xmlns'
SignedXml.prototype.createSignature = function (prefix) {
var xmlNsAttr = "xmlns";
if (prefix) {
xmlNsAttr += ':' + prefix;
prefix += ':';
xmlNsAttr += ":" + prefix;
prefix += ":";
} else {
prefix = '';
prefix = "";
}
var signatureValueXml = "<" + prefix + "SignatureValue>" + this.signatureValue + "</" + prefix + "SignatureValue>"
var signatureValueXml =
"<" + prefix + "SignatureValue>" + this.signatureValue + "</" + prefix + "SignatureValue>";
//the canonicalization requires to get a valid xml node.
//we need to wrap the info in a dummy signature since it contains the default namespace.
var dummySignatureWrapper = "<" + prefix + "Signature " + xmlNsAttr + "=\"http://www.w3.org/2000/09/xmldsig#\">" +
signatureValueXml +
"</" + prefix + "Signature>"
var dummySignatureWrapper =
"<" +
prefix +
"Signature " +
xmlNsAttr +
'="http://www.w3.org/2000/09/xmldsig#">' +
signatureValueXml +
"</" +
prefix +
"Signature>";
var doc = new Dom().parseFromString(dummySignatureWrapper)
var doc = new Dom().parseFromString(dummySignatureWrapper);
return doc.documentElement.firstChild;
}
};
SignedXml.prototype.getSignatureXml = function () {
return this.signatureXml;
};
SignedXml.prototype.getSignatureXml = function() {
return this.signatureXml
}
SignedXml.prototype.getOriginalXmlWithIds = function () {
return this.originalXmlWithIds;
};
SignedXml.prototype.getOriginalXmlWithIds = function() {
return this.originalXmlWithIds
}
SignedXml.prototype.getSignedXml = function() {
return this.signedXml
}
SignedXml.prototype.getSignedXml = function () {
return this.signedXml;
};

@@ -1,18 +0,21 @@

var select = require('xpath').select
var select = require("xpath").select;
function findAttr(node, localName, namespace) {
for (var i = 0; i<node.attributes.length; i++) {
var attr = node.attributes[i]
for (var i = 0; i < node.attributes.length; i++) {
var attr = node.attributes[i];
if (attrEqualsExplicitly(attr, localName, namespace) || attrEqualsImplicitly(attr, localName, namespace, node)) {
return attr
}
if (
attrEqualsExplicitly(attr, localName, namespace) ||
attrEqualsImplicitly(attr, localName, namespace, node)
) {
return attr;
}
}
return null
return null;
}
function findFirst(doc, xpath) {
var nodes = select(xpath, doc)
if (nodes.length==0) throw "could not find xpath " + xpath
return nodes[0]
function findFirst(doc, xpath) {
var nodes = select(xpath, doc);
if (nodes.length == 0) throw "could not find xpath " + xpath;
return nodes[0];
}

@@ -22,61 +25,61 @@

node = node.documentElement || node;
var res = []
for (var i = 0; i<node.childNodes.length; i++) {
var child = node.childNodes[i]
if (child.localName==localName && (child.namespaceURI==namespace || !namespace)) {
res.push(child)
var res = [];
for (var i = 0; i < node.childNodes.length; i++) {
var child = node.childNodes[i];
if (child.localName == localName && (child.namespaceURI == namespace || !namespace)) {
res.push(child);
}
}
return res
return res;
}
function attrEqualsExplicitly(attr, localName, namespace) {
return attr.localName==localName && (attr.namespaceURI==namespace || !namespace)
return attr.localName == localName && (attr.namespaceURI == namespace || !namespace);
}
function attrEqualsImplicitly(attr, localName, namespace, node) {
return attr.localName==localName && ((!attr.namespaceURI && node.namespaceURI==namespace) || !namespace)
return (
attr.localName == localName &&
((!attr.namespaceURI && node.namespaceURI == namespace) || !namespace)
);
}
var xml_special_to_encoded_attribute = {
'&': '&amp;',
'<': '&lt;',
'"': '&quot;',
'\r': '&#xD;',
'\n': '&#xA;',
'\t': '&#x9;'
}
"&": "&amp;",
"<": "&lt;",
'"': "&quot;",
"\r": "&#xD;",
"\n": "&#xA;",
"\t": "&#x9;",
};
var xml_special_to_encoded_text = {
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'\r': '&#xD;'
}
"&": "&amp;",
"<": "&lt;",
">": "&gt;",
"\r": "&#xD;",
};
function encodeSpecialCharactersInAttribute(attributeValue){
return attributeValue
.replace(/([&<"\r\n\t])/g, function(str, item){
// Special character normalization. See:
// - https://www.w3.org/TR/xml-c14n#ProcessingModel (Attribute Nodes)
// - https://www.w3.org/TR/xml-c14n#Example-Chars
return xml_special_to_encoded_attribute[item]
})
function encodeSpecialCharactersInAttribute(attributeValue) {
return attributeValue.replace(/([&<"\r\n\t])/g, function (str, item) {
// Special character normalization. See:
// - https://www.w3.org/TR/xml-c14n#ProcessingModel (Attribute Nodes)
// - https://www.w3.org/TR/xml-c14n#Example-Chars
return xml_special_to_encoded_attribute[item];
});
}
function encodeSpecialCharactersInText(text){
return text
.replace(/([&<>\r])/g, function(str, item){
// Special character normalization. See:
// - https://www.w3.org/TR/xml-c14n#ProcessingModel (Text Nodes)
// - https://www.w3.org/TR/xml-c14n#Example-Chars
return xml_special_to_encoded_text[item]
})
function encodeSpecialCharactersInText(text) {
return text.replace(/([&<>\r])/g, function (str, item) {
// Special character normalization. See:
// - https://www.w3.org/TR/xml-c14n#ProcessingModel (Text Nodes)
// - https://www.w3.org/TR/xml-c14n#Example-Chars
return xml_special_to_encoded_text[item];
});
}
exports.findAttr = findAttr
exports.findChilds = findChilds
exports.encodeSpecialCharactersInAttribute = encodeSpecialCharactersInAttribute
exports.encodeSpecialCharactersInText = encodeSpecialCharactersInText
exports.findFirst = findFirst
exports.findAttr = findAttr;
exports.findChilds = findChilds;
exports.encodeSpecialCharactersInAttribute = encodeSpecialCharactersInAttribute;
exports.encodeSpecialCharactersInText = encodeSpecialCharactersInText;
exports.findFirst = findFirst;
{
"name": "xml-crypto",
"version": "3.0.1",
"version": "3.1.0",
"private": false,
"description": "Xml digital signature and encryption library for Node.js",
"engines": {
"node": ">=0.4.0"
"keywords": [
"xml",
"digital signature",
"xml encryption",
"x.509 certificate"
],
"repository": {
"type": "git",
"url": "https://github.com/node-saml/xml-crypto.git"
},
"license": "MIT",
"author": "Yaron Naveh <yaronn01@gmail.com> (http://webservices20.blogspot.com/)",
"contributors": [
"LoneRifle <LoneRifle@users.noreply.github.com>"
"LoneRifle <LoneRifle@users.noreply.github.com>",
"Chris Barth <chrisjbarth@hotmail.com>"
],
"dependencies": {
"@xmldom/xmldom": "^0.8.5",
"xpath": "0.0.32"
},
"devDependencies": {
"ejs": "3.1.7",
"nodeunit": "^0.11.3"
},
"repository": {
"type": "git",
"url": "https://github.com/yaronn/xml-crypto.git"
},
"main": "./index.js",
"types": "./index.d.ts",
"directories": {
"lib": "./lib"
},
"keywords": [
"xml",
"digital signature",
"xml encryption",
"x.509 certificate"
],
"license": "MIT",
"scripts": {
"changelog": "gren changelog --override --generate --head master",
"lint": "eslint --ext .js \"**/*.js\" --cache && npm run prettier-check",
"lint:fix": "eslint --ext .js --fix \"**/*.js\" && npm run prettier-format",
"prettier-check": "prettier --config .prettierrc.json --check .",
"prettier-format": "prettier --config .prettierrc.json --write .",
"prerelease": "git clean -xfd && npm ci && npm test",
"release": "release-it",
"test": "nodeunit ./test/canonicalization-unit-tests.js ./test/c14nWithComments-unit-tests.js ./test/signature-unit-tests.js ./test/saml-response-test.js ./test/signature-integration-tests.js ./test/document-test.js ./test/wsfed-metadata-test.js ./test/hmac-tests.js ./test/c14n-non-exclusive-unit-test.js"
},
"dependencies": {
"@xmldom/xmldom": "0.8.7",
"xpath": "0.0.32"
},
"devDependencies": {
"@cjbarth/github-release-notes": "^4.0.0",
"@prettier/plugin-xml": "^2.2.0",
"ejs": "3.1.9",
"eslint": "^8.42.0",
"eslint-config-prettier": "^8.8.0",
"nodeunit": "^0.11.3",
"prettier": "^2.8.8",
"prettier-plugin-packagejson": "^2.4.3",
"release-it": "^15.11.0"
},
"engines": {
"node": ">=4.0.0"
}
}

@@ -1,4 +0,4 @@

## xml-crypto
# xml-crypto
![Build](https://github.com/yaronn/xml-crypto/actions/workflows/ci.yml/badge.svg)
![Build](https://github.com/node-saml/xml-crypto/actions/workflows/ci.yml/badge.svg)
[![Gitpod Ready-to-Code](https://img.shields.io/badge/Gitpod-Ready--to--Code-blue?logo=gitpod)](https://gitpod.io/from-referrer/)

@@ -11,5 +11,8 @@

## Install
Install with [npm](http://github.com/isaacs/npm):
npm install xml-crypto
```shell
npm install xml-crypto
```

@@ -22,27 +25,30 @@ A pre requisite it to have [openssl](http://www.openssl.org/) installed and its /bin to be on the system path. I used version 1.0.1c but it should work on older versions too.

* Canonicalization http://www.w3.org/TR/2001/REC-xml-c14n-20010315
* Canonicalization with comments http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments
* Exclusive Canonicalization http://www.w3.org/2001/10/xml-exc-c14n#
* Exclusive Canonicalization with comments http://www.w3.org/2001/10/xml-exc-c14n#WithComments
* Enveloped Signature transform http://www.w3.org/2000/09/xmldsig#enveloped-signature
- Canonicalization http://www.w3.org/TR/2001/REC-xml-c14n-20010315
- Canonicalization with comments http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments
- Exclusive Canonicalization http://www.w3.org/2001/10/xml-exc-c14n#
- Exclusive Canonicalization with comments http://www.w3.org/2001/10/xml-exc-c14n#WithComments
- Enveloped Signature transform http://www.w3.org/2000/09/xmldsig#enveloped-signature
### Hashing Algorithms
* SHA1 digests http://www.w3.org/2000/09/xmldsig#sha1
* SHA256 digests http://www.w3.org/2001/04/xmlenc#sha256
* SHA512 digests http://www.w3.org/2001/04/xmlenc#sha512
- SHA1 digests http://www.w3.org/2000/09/xmldsig#sha1
- SHA256 digests http://www.w3.org/2001/04/xmlenc#sha256
- SHA512 digests http://www.w3.org/2001/04/xmlenc#sha512
### Signature Algorithms
* RSA-SHA1 http://www.w3.org/2000/09/xmldsig#rsa-sha1
* RSA-SHA256 http://www.w3.org/2001/04/xmldsig-more#rsa-sha256
* RSA-SHA512 http://www.w3.org/2001/04/xmldsig-more#rsa-sha512
- RSA-SHA1 http://www.w3.org/2000/09/xmldsig#rsa-sha1
- RSA-SHA256 http://www.w3.org/2001/04/xmldsig-more#rsa-sha256
- RSA-SHA512 http://www.w3.org/2001/04/xmldsig-more#rsa-sha512
HMAC-SHA1 is also available but it is disabled by default
* HMAC-SHA1 http://www.w3.org/2000/09/xmldsig#hmac-sha1
- HMAC-SHA1 http://www.w3.org/2000/09/xmldsig#hmac-sha1
to enable HMAC-SHA1, do:
```javascript
require( 'xml-crypto' ).SignedXml.enableHMAC();
require("xml-crypto").SignedXml.enableHMAC();
```
This will enable HMAC and disable digital signature algorithms. Due to key

@@ -54,11 +60,10 @@ confusion issues, it is risky to have both HMAC-based and public key digital

*Canonicalization/Transformation Algorithm:* Exclusive Canonicalization http://www.w3.org/2001/10/xml-exc-c14n#
_Canonicalization/Transformation Algorithm:_ Exclusive Canonicalization http://www.w3.org/2001/10/xml-exc-c14n#
*Hashing Algorithm:* SHA1 digest http://www.w3.org/2000/09/xmldsig#sha1
_Hashing Algorithm:_ SHA1 digest http://www.w3.org/2000/09/xmldsig#sha1
*Signature Algorithm:* RSA-SHA1 http://www.w3.org/2000/09/xmldsig#rsa-sha1
_Signature Algorithm:_ RSA-SHA1 http://www.w3.org/2000/09/xmldsig#rsa-sha1
[You are able to extend xml-crypto with custom algorithms.](#customizing-algorithms)
## Signing Xml documents

@@ -75,45 +80,39 @@

`````javascript
var SignedXml = require('xml-crypto').SignedXml
, fs = require('fs')
```javascript
var SignedXml = require("xml-crypto").SignedXml,
fs = require("fs");
var xml = "<library>" +
"<book>" +
"<name>Harry Potter</name>" +
"</book>" +
"</library>"
var xml = "<library>" + "<book>" + "<name>Harry Potter</name>" + "</book>" + "</library>";
var sig = new SignedXml()
sig.addReference("//*[local-name(.)='book']")
sig.signingKey = fs.readFileSync("client.pem")
sig.computeSignature(xml)
fs.writeFileSync("signed.xml", sig.getSignedXml())
var sig = new SignedXml();
sig.addReference("//*[local-name(.)='book']");
sig.signingKey = fs.readFileSync("client.pem");
sig.computeSignature(xml);
fs.writeFileSync("signed.xml", sig.getSignedXml());
```
`````
The result will be:
```xml
<library>
<book Id="_0">
<name>Harry Potter</name>
</book>
<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>cdiS43aFDQMnb3X8yaIUej3+z9Q=</DigestValue>
</Reference>
</SignedInfo>
<SignatureValue>vhWzpQyIYuncHUZV9W...[long base64 removed]...</SignatureValue>
</Signature>
</library>
```
`````xml
<library>
<book Id="_0">
<name>Harry Potter</name>
</book>
<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>cdiS43aFDQMnb3X8yaIUej3+z9Q=</DigestValue>
</Reference>
</SignedInfo>
<SignatureValue>vhWzpQyIYuncHUZV9W...[long base64 removed]...</SignatureValue>
</Signature>
</library>
`````
Note:

@@ -131,23 +130,28 @@

npm install xmldom
```shell
npm install xmldom
```
Example:
`````javascript
var select = require('xml-crypto').xpath
, dom = require('@xmldom/xmldom').DOMParser
, SignedXml = require('xml-crypto').SignedXml
, FileKeyInfo = require('xml-crypto').FileKeyInfo
, fs = require('fs')
```javascript
var select = require("xml-crypto").xpath,
dom = require("@xmldom/xmldom").DOMParser,
SignedXml = require("xml-crypto").SignedXml,
FileKeyInfo = require("xml-crypto").FileKeyInfo,
fs = require("fs");
var xml = fs.readFileSync("signed.xml").toString()
var doc = new dom().parseFromString(xml)
var xml = fs.readFileSync("signed.xml").toString();
var doc = new dom().parseFromString(xml);
var signature = select(doc, "//*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']")[0]
var sig = new SignedXml()
sig.keyInfoProvider = new FileKeyInfo("client_public.pem")
sig.loadSignature(signature)
var res = sig.checkSignature(xml)
if (!res) console.log(sig.validationErrors)
`````
var signature = select(
doc,
"//*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']"
)[0];
var sig = new SignedXml();
sig.keyInfoProvider = new FileKeyInfo("client_public.pem");
sig.loadSignature(signature);
var res = sig.checkSignature(xml);
if (!res) console.log(sig.validationErrors);
```

@@ -157,10 +161,11 @@ if the verification process fails `sig.validationErrors` will have the errors.

In order to protect from some attacks we must check the content we want to use is the one that has been signed:
`````javascript
var elem = select(doc, "/xpath_to_interesting_element");
var uri = sig.references[0].uri; // might not be 0 - depending on the document you verify
var id = (uri[0] === '#') ? uri.substring(1) : uri;
if (elem.getAttribute('ID') != id && elem.getAttribute('Id') != id && elem.getAttribute('id') != id)
throw new Error('the interesting element was not the one verified by the signature')
`````
```javascript
var elem = select(doc, "/xpath_to_interesting_element");
var uri = sig.references[0].uri; // might not be 0 - depending on the document you verify
var id = uri[0] === "#" ? uri.substring(1) : uri;
if (elem.getAttribute("ID") != id && elem.getAttribute("Id") != id && elem.getAttribute("id") != id)
throw new Error("the interesting element was not the one verified by the signature");
```
Note:

@@ -170,4 +175,4 @@

### Caring for Implicit transform
### Caring for Implicit transform
If you fail to verify signed XML, then one possible cause is that there are some hidden implicit transforms(#).

@@ -182,7 +187,7 @@ (#) Normalizing XML document to be verified. i.e. remove extra space within a tag, sorting attributes, importing namespace declared in ancestor nodes, etc.

```javascript
var option = {implicitTransforms: ["http://www.w3.org/TR/2001/REC-xml-c14n-20010315"]}
var sig = new SignedXml(null, option)
sig.keyInfoProvider = new FileKeyInfo("client_public.pem")
sig.loadSignature(signature)
var res = sig.checkSignature(xml)
var option = { implicitTransforms: ["http://www.w3.org/TR/2001/REC-xml-c14n-20010315"] };
var sig = new SignedXml(null, option);
sig.keyInfoProvider = new FileKeyInfo("client_public.pem");
sig.loadSignature(signature);
var res = sig.checkSignature(xml);
```

@@ -210,3 +215,3 @@

*API*
#### API

@@ -218,12 +223,12 @@ A `SignedXml` object provides the following methods:

- `addReference(xpath, [transforms], [digestAlgorithm])` - adds a reference to a xml element where:
- `xpath` - a string containing a XPath expression referencing a xml element
- `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)
- `xpath` - a string containing a XPath expression referencing a xml element
- `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
- `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
- `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`
- `existingPrefixes` - A hash of prefixes and namespaces `prefix: namespace` that shouldn't be in the signature because they already exist in the xml
- `xml` - a string containing a xml 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
- `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`
- `existingPrefixes` - A hash of prefixes and namespaces `prefix: namespace` that shouldn't be in the signature because they already exist in the xml
- `getSignedXml()` - returns the original xml document with the signature in it, **must be called only after `computeSignature`**

@@ -236,7 +241,6 @@ - `getSignatureXml()` - returns just the signature part, **must be called only after `computeSignature`**

- `loadSignature(signatureXml)` - loads the signature where:
- `signatureXml` - a string or node object (like an [xml-dom](https://github.com/jindw/xmldom) node) containing the xml representation of the signature
- `signatureXml` - a string or node object (like an [xml-dom](https://github.com/jindw/xmldom) node) containing the xml representation of the signature
- `checkSignature(xml)` - validates the given xml document and returns true if the validation was successful, `sig.validationErrors` will have the validation errors if any, where:
- `xml` - a string containing a xml document
- `xml` - a string containing a xml document
### FileKeyInfo

@@ -250,4 +254,4 @@

## Customizing Algorithms
## Customizing Algorithms
The following sample shows how to sign a message using custom algorithms.

@@ -257,8 +261,7 @@

`````javascript
var SignedXml = require('xml-crypto').SignedXml
, fs = require('fs')
`````
```javascript
var SignedXml = require("xml-crypto").SignedXml,
fs = require("fs");
```
Now define the extension point you want to implement. You can choose one or more.

@@ -268,123 +271,116 @@

Implement it if you want to create a signature with a KeyInfo section, or you want to read your key in a different way then the default file read option.
`````javascript
/**/
function MyKeyInfo() {
this.getKeyInfo = function(key, prefix) {
prefix = prefix || ''
prefix = prefix ? prefix + ':' : prefix
return "<" + prefix + "X509Data></" + prefix + "X509Data>"
}
this.getKey = function(keyInfo) {
//you can use the keyInfo parameter to extract the key in any way you want
return fs.readFileSync("key.pem")
}
}
`````
```javascript
function MyKeyInfo() {
this.getKeyInfo = function (key, prefix) {
prefix = prefix || "";
prefix = prefix ? prefix + ":" : prefix;
return "<" + prefix + "X509Data></" + prefix + "X509Data>";
};
this.getKey = function (keyInfo) {
//you can use the keyInfo parameter to extract the key in any way you want
return fs.readFileSync("key.pem");
};
}
```
A custom hash algorithm is used to calculate digests. Implement it if you want a hash other than the default SHA1.
`````javascript
function MyDigest() {
```javascript
function MyDigest() {
this.getHash = function (xml) {
return "the base64 hash representation of the given xml string";
};
this.getAlgorithmName = function () {
return "http://myDigestAlgorithm";
};
}
```
this.getHash = function(xml) {
return "the base64 hash representation of the given xml string"
}
this.getAlgorithmName = function() {
return "http://myDigestAlgorithm"
}
}
`````
A custom signing algorithm. The default is RSA-SHA1
`````javascript
function MySignatureAlgorithm() {
/*sign the given SignedInfo using the key. return base64 signature value*/
this.getSignature = function(signedInfo, signingKey) {
return "signature of signedInfo as base64..."
}
```javascript
function MySignatureAlgorithm() {
/*sign the given SignedInfo using the key. return base64 signature value*/
this.getSignature = function (signedInfo, signingKey) {
return "signature of signedInfo as base64...";
};
this.getAlgorithmName = function() {
return "http://mySigningAlgorithm"
}
this.getAlgorithmName = function () {
return "http://mySigningAlgorithm";
};
}
```
}
`````
Custom transformation algorithm. The default is exclusive canonicalization.
`````javascript
function MyTransformation() {
```javascript
function MyTransformation() {
/*given a node (from the xmldom module) return its canonical representation (as string)*/
this.process = function (node) {
//you should apply your transformation before returning
return node.toString();
};
/*given a node (from the xmldom module) return its canonical representation (as string)*/
this.process = function(node) {
//you should apply your transformation before returning
return node.toString()
}
this.getAlgorithmName = function () {
return "http://myTransformation";
};
}
```
this.getAlgorithmName = function() {
return "http://myTransformation"
}
}
`````
Custom canonicalization is actually the same as custom transformation. It is applied on the SignedInfo rather than on references.
`````javascript
function MyCanonicalization() {
```javascript
function MyCanonicalization() {
/*given a node (from the xmldom module) return its canonical representation (as string)*/
this.process = function (node) {
//you should apply your transformation before returning
return "< x/>";
};
/*given a node (from the xmldom module) return its canonical representation (as string)*/
this.process = function(node) {
//you should apply your transformation before returning
return "< x/>"
}
this.getAlgorithmName = function () {
return "http://myCanonicalization";
};
}
```
this.getAlgorithmName = function() {
return "http://myCanonicalization"
}
}
`````
Now you need to register the new algorithms:
`````javascript
/*register all the custom algorithms*/
```javascript
/*register all the custom algorithms*/
SignedXml.CanonicalizationAlgorithms["http://MyTransformation"] = MyTransformation
SignedXml.CanonicalizationAlgorithms["http://MyCanonicalization"] = MyCanonicalization
SignedXml.HashAlgorithms["http://myDigestAlgorithm"] = MyDigest
SignedXml.SignatureAlgorithms["http://mySigningAlgorithm"] = MySignatureAlgorithm
`````
SignedXml.CanonicalizationAlgorithms["http://MyTransformation"] = MyTransformation;
SignedXml.CanonicalizationAlgorithms["http://MyCanonicalization"] = MyCanonicalization;
SignedXml.HashAlgorithms["http://myDigestAlgorithm"] = MyDigest;
SignedXml.SignatureAlgorithms["http://mySigningAlgorithm"] = MySignatureAlgorithm;
```
Now do the signing. Note how we configure the signature to use the above algorithms:
`````javascript
function signXml(xml, xpath, key, dest)
{
var sig = new SignedXml()
```javascript
function signXml(xml, xpath, key, dest) {
var sig = new SignedXml();
/*configure the signature object to use the custom algorithms*/
sig.signatureAlgorithm = "http://mySignatureAlgorithm"
sig.keyInfoProvider = new MyKeyInfo()
sig.canonicalizationAlgorithm = "http://MyCanonicalization"
sig.addReference("//*[local-name(.)='x']", ["http://MyTransformation"], "http://myDigestAlgorithm")
/*configure the signature object to use the custom algorithms*/
sig.signatureAlgorithm = "http://mySignatureAlgorithm";
sig.keyInfoProvider = new MyKeyInfo();
sig.canonicalizationAlgorithm = "http://MyCanonicalization";
sig.addReference(
"//*[local-name(.)='x']",
["http://MyTransformation"],
"http://myDigestAlgorithm"
);
sig.signingKey = fs.readFileSync(key)
sig.addReference(xpath)
sig.computeSignature(xml)
fs.writeFileSync(dest, sig.getSignedXml())
}
sig.signingKey = fs.readFileSync(key);
sig.addReference(xpath);
sig.computeSignature(xml);
fs.writeFileSync(dest, sig.getSignedXml());
}
var xml = "<library>" +
"<book>" +
"<name>Harry Potter</name>" +
"</book>"
"</library>"
var xml = "<library>" + "<book>" + "<name>Harry Potter</name>" + "</book>";
("</library>");
signXml(xml,
"//*[local-name(.)='book']",
"client.pem",
"result.xml")
`````
signXml(xml, "//*[local-name(.)='book']", "client.pem", "result.xml");
```

@@ -397,23 +393,23 @@ You can always look at the actual code as a sample (or drop me a [mail](mailto:yaronn01@gmail.com)).

`````javascript
function AsyncSignatureAlgorithm() {
this.getSignature = function (signedInfo, signingKey, callback) {
var signer = crypto.createSign("RSA-SHA1")
signer.update(signedInfo)
var res = signer.sign(signingKey, 'base64')
//Do some asynchronous things here
callback(null, res)
}
this.getAlgorithmName = function () {
return "http://www.w3.org/2000/09/xmldsig#rsa-sha1"
}
}
```javascript
function AsyncSignatureAlgorithm() {
this.getSignature = function (signedInfo, signingKey, callback) {
var signer = crypto.createSign("RSA-SHA1");
signer.update(signedInfo);
var res = signer.sign(signingKey, "base64");
//Do some asynchronous things here
callback(null, res);
};
this.getAlgorithmName = function () {
return "http://www.w3.org/2000/09/xmldsig#rsa-sha1";
};
}
SignedXml.SignatureAlgorithms["http://asyncSignatureAlgorithm"] = AsyncSignatureAlgorithm
var sig = new SignedXml()
sig.signatureAlgorithm = "http://asyncSignatureAlgorithm"
sig.computeSignature(xml, opts, function(err){
var signedResponse = sig.getSignedXml()
})
`````
SignedXml.SignatureAlgorithms["http://asyncSignatureAlgorithm"] = AsyncSignatureAlgorithm;
var sig = new SignedXml();
sig.signatureAlgorithm = "http://asyncSignatureAlgorithm";
sig.computeSignature(xml, opts, function (err) {
var signedResponse = sig.getSignedXml();
});
```

@@ -423,49 +419,56 @@ The function `sig.checkSignature` may also use a callback if asynchronous verification is needed.

## X.509 / Key formats
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 begining of the key content is shown):
-----BEGIN PRIVATE KEY-----
MIICdwIBADANBgkqhkiG9w0...
-----END PRIVATE KEY-----
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):
```text
-----BEGIN PRIVATE KEY-----
MIICdwIBADANBgkqhkiG9w0...
-----END PRIVATE KEY-----
```
And for verification use key_public.pem:
-----BEGIN CERTIFICATE-----
MIIBxDCCAW6gAwIBAgIQxUSX...
-----END CERTIFICATE-----
```text
-----BEGIN CERTIFICATE-----
MIIBxDCCAW6gAwIBAgIQxUSX...
-----END CERTIFICATE-----
```
**Converting .pfx certificates to pem**
### Converting .pfx certificates to pem
If you have .pfx certificates you can convert them to .pem using [openssl](http://www.openssl.org/):
openssl pkcs12 -in c:\certs\yourcert.pfx -out c:\certs\cag.pem
```shell
openssl pkcs12 -in c:\certs\yourcert.pfx -out c:\certs\cag.pem
```
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.
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.
## Examples
- [how to sign a root node](#) *coming soon*
### how to sign a root node (_coming soon_)
###how to add a prefix for the signature###
Use the `prefix` option when calling `computeSignature` to add a prefix to the signature.
`````javascript
var SignedXml = require('xml-crypto').SignedXml
, fs = require('fs');
### how to add a prefix for the signature
var xml = "<library>" +
"<book>" +
"<name>Harry Potter</name>" +
"</book>" +
"</library>";
Use the `prefix` option when calling `computeSignature` to add a prefix to the signature.
```javascript
var SignedXml = require("xml-crypto").SignedXml,
fs = require("fs");
var xml = "<library>" + "<book>" + "<name>Harry Potter</name>" + "</book>" + "</library>";
var sig = new SignedXml();
sig.addReference("//*[local-name(.)='book']");
sig.signingKey = fs.readFileSync("client.pem");
sig.computeSignature(xml,{
prefix: 'ds'
sig.computeSignature(xml, {
prefix: "ds",
});
`````
```
###how to specify the location of the signature###
Use the `location` option when calling `computeSignature` to move the signature around.
Set `action` to one of the following:
### how to specify the location of the signature
Use the `location` option when calling `computeSignature` to move the signature around.
Set `action` to one of the following:
- append(default) - append to the end of the xml document

@@ -476,11 +479,7 @@ - prepend - prepend to the xml document

`````javascript
var SignedXml = require('xml-crypto').SignedXml
, fs = require('fs');
```javascript
var SignedXml = require("xml-crypto").SignedXml,
fs = require("fs");
var xml = "<library>" +
"<book>" +
"<name>Harry Potter</name>" +
"</book>" +
"</library>";
var xml = "<library>" + "<book>" + "<name>Harry Potter</name>" + "</book>" + "</library>";

@@ -490,18 +489,21 @@ var sig = new SignedXml();

sig.signingKey = fs.readFileSync("client.pem");
sig.computeSignature(xml,{
location: { reference: "//*[local-name(.)='book']", action: "after" } //This will place the signature after the book element
sig.computeSignature(xml, {
location: { reference: "//*[local-name(.)='book']", action: "after" }, //This will place the signature after the book element
});
```
`````
*more examples coming soon*
### more examples (_coming soon_)
## Development
The test framework is [nodeunit](https://github.com/caolan/nodeunit). To run tests use:
$> npm test
```shell
npm test
```
## More information
Visit my [blog](http://webservices20.blogspot.com/) or my [twitter](http://twitter.com/#!/YaronNaveh)
[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/yaronn/xml-crypto/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

@@ -508,0 +510,0 @@

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc