Skip to content

Commit

Permalink
Merge pull request #179 from bazzadp/xml_enc_c14#_inclusivenamespace_…
Browse files Browse the repository at this point in the history
…fixes

* Handle enveloped transformations with exclusive canonicalization better

* Add unit test for enveloped transformation with inclusivenamespaces

* Add test case static file for SOAP-UI style message with transforms with
inclusive namespaces

* Refactor inclusive namespaces to happen in canonicalization code

* Only use the CanonicalizationMethod/InclusiveNameSpace if one was not
explicitly provided

* Update lib/signed-xml.js

* aFix logic for multiple PrefixList + fix some old whitespace issues
  • Loading branch information
LoneRifle authored Mar 23, 2019
2 parents 3516a57 + 9e1c6ea commit dcba12d
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 62 deletions.
31 changes: 31 additions & 0 deletions lib/exclusive-canonicalization.js
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,37 @@ ExclusiveCanonicalization.prototype.process = function(node, options) {
var defaultNsForPrefix = options.defaultNsForPrefix || {};
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")
if (CanonicalizationMethod.length != 0) {
var inclusiveNamespaces = utils.findChilds(CanonicalizationMethod[0], "InclusiveNamespaces")
if (inclusiveNamespaces.length != 0) {
inclusiveNamespacesPrefixList = inclusiveNamespaces[0].getAttribute('PrefixList').split(" ");
}
}
}

/**
* 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(' ');
prefixList.forEach(function (prefix) {
if (ancestorNamespaces) {
ancestorNamespaces.forEach(function (ancestorNamespace) {
if (prefix == ancestorNamespace.prefix) {
node.setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:' + prefix, ancestorNamespace.namespaceURI);
}
})
}
})
}

var res = this.processInner(node, [], defaultNs, defaultNsForPrefix, inclusiveNamespacesPrefixList);
return res;
};
Expand Down
80 changes: 24 additions & 56 deletions lib/signed-xml.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ function FileKeyInfo(file) {
this.file = file

this.getKeyInfo = function(key, prefix) {
prefix = prefix || ''
prefix = prefix ? prefix + ':' : prefix
prefix = prefix || ''
prefix = prefix ? prefix + ':' : prefix
return "<" + prefix + "X509Data></" + prefix + "X509Data>"
}

Expand Down Expand Up @@ -361,21 +361,20 @@ SignedXml.prototype.validateSignatureValue = function(doc) {
var signedInfo = utils.findChilds(this.signatureNode, "SignedInfo")
if (signedInfo.length==0) throw new Error("could not find SignedInfo element in the message")

/**
* When canonicalization algorithm is non-exclusive, search for ancestor namespaces
* before validating signature.
*/
var ancestorNamespaces = [];
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("When canonicalization method is non-exclusive, whole xml dom must be provided as an argument");
}

ancestorNamespaces = findAncestorNs(doc, "//*[local-name()='SignedInfo']");
}

/**
* Search for ancestor namespaces before validating signature.
*/
var ancestorNamespaces = [];
ancestorNamespaces = findAncestorNs(doc, "//*[local-name()='SignedInfo']");

var c14nOptions = {
ancestorNamespaces: ancestorNamespaces
};
Expand Down Expand Up @@ -449,59 +448,21 @@ SignedXml.prototype.validateReferences = function(doc) {
}

/**
* When canonicalization algorithm is non-exclusive, search for ancestor namespaces
* before validating references.
* Search for ancestor namespaces before validating references.
*/
if(Array.isArray(ref.transforms)){
var hasNonExcC14nTransform = false;
for(var t in ref.transforms){
if(!ref.transforms.hasOwnProperty(t)) continue;

if(ref.transforms[t] === "http://www.w3.org/TR/2001/REC-xml-c14n-20010315"
|| ref.transforms[t] === "http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments")
{
hasNonExcC14nTransform = true;
break;
}
}

if(hasNonExcC14nTransform){
ref.ancestorNamespaces = findAncestorNs(doc, elemXpath);
}
if(Array.isArray(ref.transforms)){
ref.ancestorNamespaces = findAncestorNs(doc, elemXpath);
}

var c14nOptions = {
inclusiveNamespacesPrefixList: ref.inclusiveNamespacesPrefixList,
ancestorNamespaces: ref.ancestorNamespaces
};

var canonXml = this.getCanonXml(ref.transforms, elem[0], c14nOptions);

var hash = this.findHashAlgorithm(ref.digestAlgorithm)
var digest = hash.getHash(canonXml)

if (!validateDigestValue(digest, ref.digestValue)) {
if (ref.inclusiveNamespacesPrefixList) {
// fallback: apply InclusiveNamespaces workaround (https://github.com/yaronn/xml-crypto/issues/72)
var prefixList = ref.inclusiveNamespacesPrefixList instanceof Array ? ref.inclusiveNamespacesPrefixList : ref.inclusiveNamespacesPrefixList.split(' ');
var supported_definitions = {
'xs': 'http://www.w3.org/2001/XMLSchema',
'xsi': 'http://www.w3.org/2001/XMLSchema-instance',
'saml': 'urn:oasis:names:tc:SAML:2.0:assertion'
}

prefixList.forEach(function (prefix) {
if (supported_definitions[prefix]) {
elem[0].setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:' + prefix, supported_definitions[prefix]);
}
});

canonXml = this.getCanonXml(ref.transforms, elem[0], { inclusiveNamespacesPrefixList: ref.inclusiveNamespacesPrefixList });
digest = hash.getHash(canonXml);
if (digest === ref.digestValue) {
return true;
}
}
}

if (!validateDigestValue(digest, ref.digestValue)) {
this.validationErrors.push("invalid signature: for uri " + ref.uri +
Expand Down Expand Up @@ -612,9 +573,16 @@ SignedXml.prototype.loadReference = function(ref) {
transforms.push(utils.findAttr(trans, "Algorithm").value)
}

var inclusiveNamespaces = xpath.select("//*[local-name(.)='InclusiveNamespaces']", transformsNode);
var inclusiveNamespaces = utils.findChilds(trans, "InclusiveNamespaces")
if (inclusiveNamespaces.length > 0) {
inclusiveNamespacesPrefixList = inclusiveNamespaces[0].getAttribute('PrefixList');
//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++) {
if (inclusiveNamespacesPrefixList) {
inclusiveNamespacesPrefixList = inclusiveNamespacesPrefixList + " " + inclusiveNamespaces[i].getAttribute('PrefixList');
} else {
inclusiveNamespacesPrefixList = inclusiveNamespaces[i].getAttribute('PrefixList');
}
}
}
}

Expand Down Expand Up @@ -916,10 +884,10 @@ SignedXml.prototype.createSignature = function(signedInfo, prefix) {
var xmlNsAttr = 'xmlns'

if (prefix) {
xmlNsAttr += ':' + prefix;
prefix += ':';
xmlNsAttr += ':' + prefix;
prefix += ':';
} else {
prefix = '';
prefix = '';
}

//the canonicalization requires to get a valid xml node.
Expand Down
13 changes: 7 additions & 6 deletions test/signature-unit-tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -474,19 +474,20 @@ module.exports = {
},

"correctly loads signature": function(test) {
passLoadSignature(test, "./test/static/valid_signature.xml");
passLoadSignature(test, "./test/static/valid_signature.xml", true);
passLoadSignature(test, "./test/static/valid_signature_with_root_level_sig_namespace.xml");
passLoadSignature(test, "./test/static/valid_signature.xml")
passLoadSignature(test, "./test/static/valid_signature.xml", true)
passLoadSignature(test, "./test/static/valid_signature_with_root_level_sig_namespace.xml")
test.done()
},

"verify valid signature": function(test) {
passValidSignature(test, "./test/static/valid_signature.xml")
passValidSignature(test, "./test/static/valid_signature_with_lowercase_id_attribute.xml");
passValidSignature(test, "./test/static/valid_signature_with_lowercase_id_attribute.xml")
passValidSignature(test, "./test/static/valid_signature wsu.xml", "wssecurity")
passValidSignature(test, "./test/static/valid_signature_with_reference_keyInfo.xml")
passValidSignature(test, "./test/static/valid_signature_with_whitespace_in_digestvalue.xml")
passValidSignature(test, "./test/static/valid_signature_utf8.xml")
passValidSignature(test, "./test/static/valid_signature_with_unused_prefixes.xml")
test.done()
},

Expand Down Expand Up @@ -639,8 +640,8 @@ function failInvalidSignature(test, file, mode) {
function verifySignature(xml, mode) {

var doc = new dom().parseFromString(xml)
var node = select("/*/*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']", doc)[0]

var node = select("//*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']", doc)[0]
var sig = new SignedXml(mode)
sig.keyInfoProvider = new FileKeyInfo("./test/static/client_public.pem")
sig.loadSignature(node)
Expand Down
1 change: 1 addition & 0 deletions test/static/valid_signature_with_unused_prefixes.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<soapenv:Envelope xmlns:eg="http://www.example.com" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"><soapenv:Header xmlns:wsa="http://www.w3.org/2005/08/addressing"><wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"><ds:Signature Id="SIG-14" xmlns:ds="http://www.w3.org/2000/09/xmldsig#"><ds:SignedInfo><ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"><ec:InclusiveNamespaces PrefixList="wsa eg soapenv" xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#"/></ds:CanonicalizationMethod><ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/><ds:Reference URI="#id-9"><ds:Transforms><ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"><ec:InclusiveNamespaces PrefixList="eg" xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#"/></ds:Transform></ds:Transforms><ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/><ds:DigestValue>GAEFzMhRbXD8lZeTd5GwSqkB73Q=</ds:DigestValue></ds:Reference><ds:Reference URI="#id-10"><ds:Transforms><ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"><ec:InclusiveNamespaces PrefixList="eg soapenv" xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#"/></ds:Transform></ds:Transforms><ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/><ds:DigestValue>c2bqLRa0Q+/P7+vuXZHO8/iiac8=</ds:DigestValue></ds:Reference><ds:Reference URI="#id-11"><ds:Transforms><ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"><ec:InclusiveNamespaces PrefixList="eg soapenv" xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#"/></ds:Transform></ds:Transforms><ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/><ds:DigestValue>95WoCzf9J63UgdLCj/05PYaJIAw=</ds:DigestValue></ds:Reference><ds:Reference URI="#TS-8"><ds:Transforms><ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"><ec:InclusiveNamespaces PrefixList="wsse wsa eg soapenv" xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#"/></ds:Transform></ds:Transforms><ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/><ds:DigestValue>pa04vRHbAdvfr6Re5wgx2qFQepE=</ds:DigestValue></ds:Reference><ds:Reference URI="#id-12"><ds:Transforms><ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"><ec:InclusiveNamespaces PrefixList="eg soapenv" xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#"/></ds:Transform></ds:Transforms><ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/><ds:DigestValue>YYNZUGNHN8uRGzMqPVC2n5XODP8=</ds:DigestValue></ds:Reference><ds:Reference URI="#id-13"><ds:Transforms><ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"><ec:InclusiveNamespaces PrefixList="eg soapenv" xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#"/></ds:Transform></ds:Transforms><ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/><ds:DigestValue>GvVowT6jH/4fExGYfFzDbzhSwTs=</ds:DigestValue></ds:Reference></ds:SignedInfo><ds:SignatureValue>F8Lm5aUouA5FtxQ1krQ3unE9NFh0QSl+QscEyTcK3FrIpWCB195Z7cbmYa9RsiYMdr2wTHHmou/+wrjpk9pZFJq+b0tpHKCpfj6B302Rexb5f+cDpUjBB/NGb11qaUiM65keVIWzmYnHC0iCxsCaG3lwHMELNr7GxNun1U7LzzI=</ds:SignatureValue><ds:KeyInfo Id="KI-7E559E62741B121FC215529264679845"><wsse:SecurityTokenReference wsu:Id="STR-7E559E62741B121FC215529264679846"><wsse:KeyIdentifier EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary" ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3">MIIBxDCCAW6gAwIBAgIQxUSXFzWJYYtOZnmmuOMKkjANBgkqhkiG9w0BAQQFADAWMRQwEgYDVQQDEwtSb290IEFnZW5jeTAeFw0wMzA3MDgxODQ3NTlaFw0zOTEyMzEyMzU5NTlaMB8xHTAbBgNVBAMTFFdTRTJRdWlja1N0YXJ0Q2xpZW50MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC+L6aB9x928noY4+0QBsXnxkQE4quJl7c3PUPdVu7k9A02hRG481XIfWhrDY5i7OEB7KGW7qFJotLLeMec/UkKUwCgv3VvJrs2nE9xO3SSWIdNzADukYh+Cxt+FUU6tUkDeqg7dqwivOXhuOTRyOI3HqbWTbumaLdc8jufz2LhaQIDAQABo0swSTBHBgNVHQEEQDA+gBAS5AktBh0dTwCNYSHcFmRjoRgwFjEUMBIGA1UEAxMLUm9vdCBBZ2VuY3mCEAY3bACqAGSKEc+41KpcNfQwDQYJKoZIhvcNAQEEBQADQQAfIbnMPVYkNNfX1tG1F+qfLhHwJdfDUZuPyRPucWF5qkh6sSdWVBY5sT/txBnVJGziyO8DPYdu2fPMER8ajJfl</wsse:KeyIdentifier></wsse:SecurityTokenReference></ds:KeyInfo></ds:Signature><wsu:Timestamp wsu:Id="TS-8"><wsu:Created>2019-03-18T16:27:47.984Z</wsu:Created><wsu:Expires>2019-03-19T04:27:47.984Z</wsu:Expires></wsu:Timestamp></wsse:Security><wsa:Action wsu:Id="id-13" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">http://www.example.com/testAction</wsa:Action><wsa:From wsu:Id="id-12" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"><wsa:Address>client</wsa:Address></wsa:From><wsa:MessageID wsu:Id="id-11" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">uuid:c519e597-0570-4d35-92e5-0df733a17cc1</wsa:MessageID><wsa:To wsu:Id="id-10" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">test</wsa:To></soapenv:Header><soapenv:Body wsu:Id="id-9" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"><eg:node1>testMessage</eg:node1></soapenv:Body></soapenv:Envelope>

0 comments on commit dcba12d

Please sign in to comment.