Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Included InclusiveNamespaces PrefixList for Reference/Signature and Fixed setAttributeNS Problem #218

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
128 changes: 87 additions & 41 deletions src/XMLSecurityDSig.php
Original file line number Diff line number Diff line change
Expand Up @@ -222,9 +222,10 @@ public function createNewSignNode($name, $value=null)

/**
* @param string $method
* @param string|array $include_ns
* @throws Exception
*/
public function setCanonicalMethod($method)
public function setCanonicalMethod($method, $include_ns=false)
{
switch ($method) {
case 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315':
Expand All @@ -245,6 +246,11 @@ public function setCanonicalMethod($method)
if (! ($canonNode = $nodeset->item(0))) {
$canonNode = $this->createNewSignNode('CanonicalizationMethod');
$sinfo->insertBefore($canonNode, $sinfo->firstChild);
if($include_ns !== false && ($method == self::EXC_C14N || $method == self::EXC_C14N_COMMENTS)) {
$includeNode = $this->sigNode->ownerDocument->createElementNS($method, 'ec:InclusiveNamespaces', null);
$includeNode->setAttribute('PrefixList', is_array($include_ns) ? implode(' ', $include_ns) : $include_ns);
$canonNode->appendChild($includeNode);
}
}
$canonNode->setAttribute('Algorithm', $this->canonicalMethod);
}
Expand All @@ -266,9 +272,11 @@ private function canonicalizeData($node, $canonicalmethod, $arXPath=null, $prefi
case 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315':
$exclusive = false;
$withComments = false;
$prefixList = null;
break;
case 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments':
$withComments = true;
$prefixList = null;
break;
case 'http://www.w3.org/2001/10/xml-exc-c14n#':
$exclusive = true;
Expand Down Expand Up @@ -314,22 +322,10 @@ public function canonicalizeSignedInfo()
if ($signInfoNode = $nodeset->item(0)) {
$query = "./secdsig:CanonicalizationMethod";
$nodeset = $xpath->query($query, $signInfoNode);
$prefixList = null;
if ($canonNode = $nodeset->item(0)) {
$canonicalmethod = $canonNode->getAttribute('Algorithm');
foreach ($canonNode->childNodes as $node)
{
if ($node->localName == 'InclusiveNamespaces') {
if ($pfx = $node->getAttribute('PrefixList')) {
$arpfx = array_filter(explode(' ', $pfx));
if (count($arpfx) > 0) {
$prefixList = array_merge($prefixList ? $prefixList : array(), $arpfx);
}
}
}
}
}
$this->signedInfo = $this->canonicalizeData($signInfoNode, $canonicalmethod, null, $prefixList);
$this->signedInfo = $this->canonicalizeData($signInfoNode, $canonicalmethod, null, $this->getInclusiveNamespaces($canonNode));
return $this->signedInfo;
}
}
Expand Down Expand Up @@ -391,6 +387,8 @@ public function validateDigest($refNode, $data)
}

/**
* Transforms the $objData according to the Algorithm and Inclusive Namespaces defined on the $refNode into it's Canonicalized form.
*
* @param $refNode
* @param DOMNode $objData
* @param bool $includeCommentNodes
Expand All @@ -405,7 +403,6 @@ public function processTransforms($refNode, $objData, $includeCommentNodes = tru
$nodelist = $xpath->query($query, $refNode);
$canonicalMethod = 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315';
$arXPath = null;
$prefixList = null;
foreach ($nodelist AS $transform) {
$algorithm = $transform->getAttribute("Algorithm");
switch ($algorithm) {
Expand All @@ -420,28 +417,7 @@ public function processTransforms($refNode, $objData, $includeCommentNodes = tru
} else {
$canonicalMethod = $algorithm;
}

$node = $transform->firstChild;
while ($node) {
if ($node->localName == 'InclusiveNamespaces') {
if ($pfx = $node->getAttribute('PrefixList')) {
$arpfx = array();
$pfxlist = explode(" ", $pfx);
foreach ($pfxlist AS $pfx) {
$val = trim($pfx);
if (! empty($val)) {
$arpfx[] = $val;
}
}
if (count($arpfx) > 0) {
$prefixList = $arpfx;
}
}
break;
}
$node = $node->nextSibling;
}
break;
break;
case 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315':
case 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments':
if (!$includeCommentNodes) {
Expand Down Expand Up @@ -475,7 +451,7 @@ public function processTransforms($refNode, $objData, $includeCommentNodes = tru
}
}
if ($data instanceof DOMNode) {
$data = $this->canonicalizeData($objData, $canonicalMethod, $arXPath, $prefixList);
$data = $this->canonicalizeData($objData, $canonicalMethod, $arXPath, $this->getInclusiveNamespaces($transform));
}
return $data;
}
Expand Down Expand Up @@ -631,13 +607,15 @@ private function addRefInternal($sinfoNode, $node, $algorithm, $arTransforms=nul
$id_name = 'Id';
$overwrite_id = true;
$force_uri = false;
$include_ns = false;

if (is_array($options)) {
$prefix = empty($options['prefix']) ? null : $options['prefix'];
$prefix_ns = empty($options['prefix_ns']) ? null : $options['prefix_ns'];
$id_name = empty($options['id_name']) ? 'Id' : $options['id_name'];
$overwrite_id = !isset($options['overwrite']) ? true : (bool) $options['overwrite'];
$force_uri = !isset($options['force_uri']) ? false : (bool) $options['force_uri'];
$include_ns = !isset($options['include_ns:'.$node->localName]) ? false : $options['include_ns:'.$node->localName];
}

$attname = $id_name;
Expand All @@ -655,7 +633,9 @@ private function addRefInternal($sinfoNode, $node, $algorithm, $arTransforms=nul
}
if (empty($uri)) {
$uri = self::generateGUID();
$node->setAttributeNS($prefix_ns, $attname, $uri);
$attrib = $node->ownerDocument->createAttributeNS($prefix_ns, $attname);
$attrib->value = $uri;
$node->setAttributeNodeNS($attrib);
}
$refNode->setAttribute("URI", '#'.$uri);
} elseif ($force_uri) {
Expand All @@ -677,7 +657,9 @@ private function addRefInternal($sinfoNode, $node, $algorithm, $arTransforms=nul
$transNode->appendChild($XPathNode);
if (! empty($transform['http://www.w3.org/TR/1999/REC-xpath-19991116']['namespaces'])) {
foreach ($transform['http://www.w3.org/TR/1999/REC-xpath-19991116']['namespaces'] AS $prefix => $namespace) {
$XPathNode->setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:$prefix", $namespace);
$attrib = $this->sigNode->ownerDocument->createAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:$prefix");
$attrib->value = $namespace;
$XPathNode->setAttributeNodeNS($attrib);
}
}
} else {
Expand All @@ -688,6 +670,12 @@ private function addRefInternal($sinfoNode, $node, $algorithm, $arTransforms=nul
$transNode = $this->createNewSignNode('Transform');
$transNodes->appendChild($transNode);
$transNode->setAttribute('Algorithm', $this->canonicalMethod);

if($include_ns !== false && ($this->canonicalMethod == self::EXC_C14N || $this->canonicalMethod == self::EXC_C14N_COMMENTS)) {
$includeNode = $this->sigNode->ownerDocument->createElementNS($this->canonicalMethod, 'ec:InclusiveNamespaces');
$includeNode->setAttribute('PrefixList', is_array($include_ns) ? implode(' ', $include_ns) : $include_ns);
$transNode->appendChild($includeNode);
}
}

$canonicalData = $this->processTransforms($refNode, $node);
Expand Down Expand Up @@ -719,10 +707,18 @@ public function addReference($node, $algorithm, $arTransforms=null, $options=nul
}

/**
* Adds Reference Nodes for all $arNodes to the SignedInfo Node.
*
* @param array $arNodes
* @param string $algorithm
* @param null|array $arTransforms
* @param null|array $options
* Takes Arguments: prefix => string ;
* prefix_ns => string ;
* id_name => string ;
* overwrite => boolean ;
* force_uri => boolean ;
* include_ns:[localName of Node] => string | array
*/
public function addReferenceList($arNodes, $algorithm, $arTransforms=null, $options=null)
{
Expand Down Expand Up @@ -839,6 +835,7 @@ public function signData($objKey, $data)
public function sign($objKey, $appendToNode = null)
{
// If we have a parent node append it now so C14N properly works
$this->sigNode = $this->rebuildNode($this->sigNode);
if ($appendToNode != null) {
$this->resetXPathObj();
$this->appendSignature($appendToNode);
Expand All @@ -852,7 +849,10 @@ public function sign($objKey, $appendToNode = null)
$nodeset = $xpath->query($query, $sInfo);
$sMethod = $nodeset->item(0);
$sMethod->setAttribute('Algorithm', $objKey->type);
$data = $this->canonicalizeData($sInfo, $this->canonicalMethod);
$query = "./secdsig:CanonicalizationMethod";
$nodeset = $xpath->query($query, $sInfo);
$canonNode = $nodeset->item(0);
$data = $this->canonicalizeData($sInfo, $this->canonicalMethod, null, $this->getInclusiveNamespaces($canonNode));
$sigValue = base64_encode($this->signData($objKey, $data));
$sigValueNode = $this->createNewSignNode('SignatureValue', $sigValue);
if ($infoSibling = $sInfo->nextSibling) {
Expand All @@ -863,6 +863,52 @@ public function sign($objKey, $appendToNode = null)
}
}
}


/**
* Collects all Namespaces from the InclusiveNamespaces Element in the $C14NNode into an array.
* If there is no InclusiveNamespaces Element it returns null.
*
* @param DOMElement $canonNode
* @return null|array
*/
private function getInclusiveNamespaces($C14NNode) {
if(isset($C14NNode) && $C14NNode->hasChildNodes()) {
foreach($C14NNode->childNodes as $node) {
if($node->localName == 'InclusiveNamespaces' && $prefixList = $node->getAttribute('PrefixList')) {
$prefixList = array_filter(explode(' ', $prefixList));
if(count($prefixList) > 0) {
return $prefixList;
}
}
}
}
return null;
}


/**
* Rebuild the inserted $node to include all Namespaces down from the SubTree to the Root!
* This Action should be envoked if there is a new Element with a differing Namespace appended to another Element that has already been appended to the Refering $node and a Canonicalization is needed!
*
*
* @param DOMElement $node
* @return null|DOMElement
*/
private function rebuildNode($node) {
if(isset($node) && $node->hasChildNodes()) {
$childNodes = array();
foreach($node->childNodes as $child) {
$childNodes[] = $child;
}
foreach($childNodes as $child) {
$node->removeChild($child);
$node->appendChild($this->rebuildNode($child));
}
}
return $node;
}


public function appendCert()
{
Expand Down