WSCOMUN  2.0.0
Web Services Comunes para PHP/GVHidra
XMLSecurityDSig.php
1 <?php
43 {
44  const XMLDSIGNS = 'http://www.w3.org/2000/09/xmldsig#';
45  const SHA1 = 'http://www.w3.org/2000/09/xmldsig#sha1';
46  const SHA256 = 'http://www.w3.org/2001/04/xmlenc#sha256';
47  const SHA384 = 'http://www.w3.org/2001/04/xmldsig-more#sha384';
48  const SHA512 = 'http://www.w3.org/2001/04/xmlenc#sha512';
49  const RIPEMD160 = 'http://www.w3.org/2001/04/xmlenc#ripemd160';
50  const C14N = 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315';
51  const C14N_COMMENTS = 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments';
52  const EXC_C14N = 'http://www.w3.org/2001/10/xml-exc-c14n#';
53  const EXC_C14N_COMMENTS = 'http://www.w3.org/2001/10/xml-exc-c14n#WithComments';
54  const template = '<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
55  <ds:SignedInfo>
56  <ds:SignatureMethod />
57  </ds:SignedInfo>
58 </ds:Signature>';
59  const BASE_TEMPLATE = '<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
60  <SignedInfo>
61  <SignatureMethod />
62  </SignedInfo>
63 </Signature>';
65  public $sigNode = null;
67  public $idKeys = array ();
69  public $idNS = array ();
71  private $signedInfo = null;
73  private $xPathCtx = null;
75  private $canonicalMethod = null;
77  private $prefix = '';
79  private $searchpfx = 'secdsig';
85  private $validatedNodes = null;
86 
91  public function __construct($prefix = 'ds')
92  {
93  $template = self::BASE_TEMPLATE;
94  if (! empty ( $prefix ))
95  {
96  $this->prefix = $prefix . ':';
97  $search = array (
98  "<S",
99  "</S",
100  "xmlns="
101  );
102  $replace = array (
103  "<$prefix:S",
104  "</$prefix:S",
105  "xmlns:$prefix="
106  );
107  $template = str_replace ( $search, $replace, $template );
108  }
109  $sigdoc = new DOMDocument ();
110  $sigdoc->loadXML ( $template );
111  $this->sigNode = $sigdoc->documentElement;
112  }
113 
117  private function resetXPathObj()
118  {
119  $this->xPathCtx = null;
120  }
121 
127  private function getXPathObj()
128  {
129  if (empty ( $this->xPathCtx ) && ! empty ( $this->sigNode ))
130  {
131  $xpath = new DOMXPath ( $this->sigNode->ownerDocument );
132  $xpath->registerNamespace ( 'secdsig', self::XMLDSIGNS );
133  $this->xPathCtx = $xpath;
134  }
135  return $this->xPathCtx;
136  }
137 
146  public static function generateGUID($prefix = 'pfx')
147  {
148  $uuid = md5 ( uniqid ( mt_rand (), true ) );
149  $guid = $prefix . substr ( $uuid, 0, 8 ) . "-" . substr ( $uuid, 8, 4 ) . "-" . substr ( $uuid, 12, 4 ) . "-" . substr ( $uuid, 16, 4 ) . "-" . substr ( $uuid, 20, 12 );
150  return $guid;
151  }
152 
163  public static function generate_GUID($prefix = 'pfx')
164  {
165  return self::generateGUID ( $prefix );
166  }
167 
174  public function locateSignature($objDoc, $pos = 0)
175  {
176  if ($objDoc instanceof DOMDocument)
177  {
178  $doc = $objDoc;
179  }
180  else
181  {
182  $doc = $objDoc->ownerDocument;
183  }
184  if ($doc)
185  {
186  $xpath = new DOMXPath ( $doc );
187  $xpath->registerNamespace ( 'secdsig', self::XMLDSIGNS );
188  $query = ".//secdsig:Signature";
189  $nodeset = $xpath->query ( $query, $objDoc );
190  $this->sigNode = $nodeset->item ( $pos );
191  return $this->sigNode;
192  }
193  return null;
194  }
195 
202  public function createNewSignNode($name, $value = null)
203  {
204  $doc = $this->sigNode->ownerDocument;
205  if (! is_null ( $value ))
206  {
207  $node = $doc->createElementNS ( self::XMLDSIGNS, $this->prefix . $name, $value );
208  }
209  else
210  {
211  $node = $doc->createElementNS ( self::XMLDSIGNS, $this->prefix . $name );
212  }
213  return $node;
214  }
215 
221  public function setCanonicalMethod($method)
222  {
223  switch ($method)
224  {
225  case 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315' :
226  case 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments' :
227  case 'http://www.w3.org/2001/10/xml-exc-c14n#' :
228  case 'http://www.w3.org/2001/10/xml-exc-c14n#WithComments' :
229  $this->canonicalMethod = $method;
230  break;
231  default :
232  throw new Exception ( 'Invalid Canonical Method' );
233  }
234  if ($xpath = $this->getXPathObj ())
235  {
236  $query = './' . $this->searchpfx . ':SignedInfo';
237  $nodeset = $xpath->query ( $query, $this->sigNode );
238  if ($sinfo = $nodeset->item ( 0 ))
239  {
240  $query = './' . $this->searchpfx . 'CanonicalizationMethod';
241  $nodeset = $xpath->query ( $query, $sinfo );
242  if (! ($canonNode = $nodeset->item ( 0 )))
243  {
244  $canonNode = $this->createNewSignNode ( 'CanonicalizationMethod' );
245  $sinfo->insertBefore ( $canonNode, $sinfo->firstChild );
246  }
247  $canonNode->setAttribute ( 'Algorithm', $this->canonicalMethod );
248  }
249  }
250  }
251 
260  private function canonicalizeData($node, $canonicalmethod, $arXPath = null, $prefixList = null)
261  {
262  $exclusive = false;
263  $withComments = false;
264  switch ($canonicalmethod)
265  {
266  case 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315' :
267  $exclusive = false;
268  $withComments = false;
269  break;
270  case 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments' :
271  $withComments = true;
272  break;
273  case 'http://www.w3.org/2001/10/xml-exc-c14n#' :
274  $exclusive = true;
275  break;
276  case 'http://www.w3.org/2001/10/xml-exc-c14n#WithComments' :
277  $exclusive = true;
278  $withComments = true;
279  break;
280  }
281  if (is_null ( $arXPath ) && ($node instanceof DOMNode) && ($node->ownerDocument !== null) && $node->isSameNode ( $node->ownerDocument->documentElement ))
282  {
283  /* Check for any PI or comments as they would have been excluded */
284  $element = $node;
285  while ( $refnode = $element->previousSibling )
286  {
287  if ($refnode->nodeType == XML_PI_NODE || (($refnode->nodeType == XML_COMMENT_NODE) && $withComments))
288  {
289  break;
290  }
291  $element = $refnode;
292  }
293  if ($refnode == null)
294  {
295  $node = $node->ownerDocument;
296  }
297  }
298  return $node->C14N ( $exclusive, $withComments, $arXPath, $prefixList );
299  }
300 
305  public function canonicalizeSignedInfo()
306  {
307  $doc = $this->sigNode->ownerDocument;
308  $canonicalmethod = null;
309  if ($doc)
310  {
311  $xpath = $this->getXPathObj ();
312  $query = "./secdsig:SignedInfo";
313  $nodeset = $xpath->query ( $query, $this->sigNode );
314  if ($signInfoNode = $nodeset->item ( 0 ))
315  {
316  $query = "./secdsig:CanonicalizationMethod";
317  $nodeset = $xpath->query ( $query, $signInfoNode );
318  if ($canonNode = $nodeset->item ( 0 ))
319  {
320  $canonicalmethod = $canonNode->getAttribute ( 'Algorithm' );
321  }
322  $this->signedInfo = $this->canonicalizeData ( $signInfoNode, $canonicalmethod );
323  return $this->signedInfo;
324  }
325  }
326  return null;
327  }
328 
329 
338  public function calculateDigest($digestAlgorithm, $data, $encode = true)
339  {
340  switch ($digestAlgorithm)
341  {
342  case self::SHA1 :
343  $alg = 'sha1';
344  break;
345  case self::SHA256 :
346  $alg = 'sha256';
347  break;
348  case self::SHA384 :
349  $alg = 'sha384';
350  break;
351  case self::SHA512 :
352  $alg = 'sha512';
353  break;
354  case self::RIPEMD160 :
355  $alg = 'ripemd160';
356  break;
357  default :
358  throw new Exception ( "Cannot validate digest: Unsupported Algorithm <$digestAlgorithm>" );
359  }
360  $digest = hash ( $alg, $data, true );
361  if ($encode)
362  {
363  $digest = base64_encode ( $digest );
364  }
365  return $digest;
366  }
367 
375  public function validateDigest($refNode, $data)
376  {
377  $xpath = new DOMXPath ( $refNode->ownerDocument );
378  $xpath->registerNamespace ( 'secdsig', self::XMLDSIGNS );
379  $query = 'string(./secdsig:DigestMethod/@Algorithm)';
380  $digestAlgorithm = $xpath->evaluate ( $query, $refNode );
381  $digValue = $this->calculateDigest ( $digestAlgorithm, $data, false );
382  $query = 'string(./secdsig:DigestValue)';
383  $digestValue = $xpath->evaluate ( $query, $refNode );
384  return ($digValue == base64_decode ( $digestValue ));
385  }
386 
395  public function processTransforms($refNode, $objData, $includeCommentNodes = true)
396  {
397  $data = $objData;
398  $xpath = new DOMXPath ( $refNode->ownerDocument );
399  $xpath->registerNamespace ( 'secdsig', self::XMLDSIGNS );
400  $query = './secdsig:Transforms/secdsig:Transform';
401  $nodelist = $xpath->query ( $query, $refNode );
402  $canonicalMethod = 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315';
403  $arXPath = null;
404  $prefixList = null;
405  foreach ( $nodelist as $transform )
406  {
407  $algorithm = $transform->getAttribute ( "Algorithm" );
408  switch ($algorithm)
409  {
410  case 'http://www.w3.org/2001/10/xml-exc-c14n#' :
411  case 'http://www.w3.org/2001/10/xml-exc-c14n#WithComments' :
412  if (! $includeCommentNodes)
413  {
414  /*
415  * We remove comment nodes by forcing it to use a canonicalization
416  * without comments.
417  */
418  $canonicalMethod = 'http://www.w3.org/2001/10/xml-exc-c14n#';
419  }
420  else
421  {
422  $canonicalMethod = $algorithm;
423  }
424  $node = $transform->firstChild;
425  while ( $node )
426  {
427  if ($node->localName == 'InclusiveNamespaces')
428  {
429  if ($pfx = $node->getAttribute ( 'PrefixList' ))
430  {
431  $arpfx = array ();
432  $pfxlist = explode ( " ", $pfx );
433  foreach ( $pfxlist as $pfx )
434  {
435  $val = trim ( $pfx );
436  if (! empty ( $val ))
437  {
438  $arpfx [] = $val;
439  }
440  }
441  if (count ( $arpfx ) > 0)
442  {
443  $prefixList = $arpfx;
444  }
445  }
446  break;
447  }
448  $node = $node->nextSibling;
449  }
450  break;
451  case 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315' :
452  case 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments' :
453  if (! $includeCommentNodes)
454  {
455  /*
456  * We remove comment nodes by forcing it to use a canonicalization
457  * without comments.
458  */
459  $canonicalMethod = 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315';
460  }
461  else
462  {
463  $canonicalMethod = $algorithm;
464  }
465  break;
466  case 'http://www.w3.org/TR/1999/REC-xpath-19991116' :
467  $node = $transform->firstChild;
468  while ( $node )
469  {
470  if ($node->localName == 'XPath')
471  {
472  $arXPath = array (
473  'query'=> '(.//. | .//@* | .//namespace::*)[' . $node->nodeValue . ']',
474  'namespaces' => array()
475  );
476  $nslist = $xpath->query ( './namespace::*', $node );
477  foreach ( $nslist as $nsnode )
478  {
479  if ($nsnode->localName != "xml")
480  {
481  $arXPath ['namespaces'] [$nsnode->localName] = $nsnode->nodeValue;
482  }
483  }
484  break;
485  }
486  $node = $node->nextSibling;
487  }
488  break;
489  }
490  }
491  if ($data instanceof DOMNode)
492  {
493  $data = $this->canonicalizeData ( $objData, $canonicalMethod, $arXPath, $prefixList );
494  }
495  return $data;
496  }
497 
503  public function processRefNode($refNode)
504  {
505  $dataObject = null;
506  /*
507  * Depending on the URI, we may not want to include comments in the result
508  * See: http://www.w3.org/TR/xmldsig-core/#sec-ReferenceProcessingModel
509  */
510  $includeCommentNodes = true;
511  if ($uri = $refNode->getAttribute ( "URI" ))
512  {
513  $arUrl = parse_url ( $uri );
514  if (empty ( $arUrl ['path'] ))
515  {
516  if ($identifier = $arUrl ['fragment'])
517  {
518  /*
519  * This reference identifies a node with the given id by using
520  * a URI on the form "#identifier". This should not include comments.
521  */
522  $includeCommentNodes = false;
523  $xPath = new DOMXPath ( $refNode->ownerDocument );
524  if ($this->idNS && is_array ( $this->idNS ))
525  {
526  foreach ( $this->idNS as $nspf => $ns )
527  {
528  $xPath->registerNamespace ( $nspf, $ns );
529  }
530  }
531  $iDlist = '@Id="' . $identifier . '"';
532  if (is_array ( $this->idKeys ))
533  {
534  foreach ( $this->idKeys as $idKey )
535  {
536  $iDlist .= " or @$idKey='$identifier'";
537  }
538  }
539  $query = '//*[' . $iDlist . ']';
540  $dataObject = $xPath->query ( $query )->item ( 0 );
541  }
542  else
543  {
544  $dataObject = $refNode->ownerDocument;
545  }
546  }
547  else
548  {
549  $dataObject = file_get_contents ( $arUrl );
550  }
551  }
552  else
553  {
554  /*
555  * This reference identifies the root node with an empty URI. This should
556  * not include comments.
557  */
558  $includeCommentNodes = false;
559  $dataObject = $refNode->ownerDocument;
560  }
561  $data = $this->processTransforms ( $refNode, $dataObject, $includeCommentNodes );
562  if (! $this->validateDigest ( $refNode, $data ))
563  {
564  return false;
565  }
566  if ($dataObject instanceof DOMNode)
567  {
568  /* Add this node to the list of validated nodes. */
569  if (! empty ( $identifier ))
570  {
571  $this->validatedNodes [$identifier] = $dataObject;
572  }
573  else
574  {
575  $this->validatedNodes [] = $dataObject;
576  }
577  }
578  return true;
579  }
580 
586  public function getRefNodeID($refNode)
587  {
588  if ($uri = $refNode->getAttribute ( "URI" ))
589  {
590  $arUrl = parse_url ( $uri );
591  if (empty ( $arUrl ['path'] ))
592  {
593  if ($identifier = $arUrl ['fragment'])
594  {
595  return $identifier;
596  }
597  }
598  }
599  return null;
600  }
601 
607  public function getRefIDs()
608  {
609  $refids = array ();
610  $xpath = $this->getXPathObj ();
611  $query = "./secdsig:SignedInfo/secdsig:Reference";
612  $nodeset = $xpath->query ( $query, $this->sigNode );
613  if ($nodeset->length == 0)
614  {
615  throw new Exception ( "Reference nodes not found" );
616  }
617  foreach ( $nodeset as $refNode )
618  {
619  $refids [] = $this->getRefNodeID ( $refNode );
620  }
621  return $refids;
622  }
623 
629  public function validateReference()
630  {
631  $docElem = $this->sigNode->ownerDocument->documentElement;
632  if (! $docElem->isSameNode ( $this->sigNode ))
633  {
634  $this->sigNode->parentNode->removeChild ( $this->sigNode );
635  }
636  $xpath = $this->getXPathObj ();
637  $query = "./secdsig:SignedInfo/secdsig:Reference";
638  $nodeset = $xpath->query ( $query, $this->sigNode );
639  if ($nodeset->length == 0)
640  {
641  throw new Exception ( "Reference nodes not found" );
642  }
643  /* Initialize/reset the list of validated nodes. */
644  $this->validatedNodes = array ();
645  foreach ( $nodeset as $refNode )
646  {
647  if (! $this->processRefNode ( $refNode ))
648  {
649  /* Clear the list of validated nodes. */
650  $this->validatedNodes = null;
651  throw new Exception ( "Reference validation failed" );
652  }
653  }
654  return true;
655  }
656 
665  private function addRefInternal($sinfoNode, $node, $algorithm, $arTransforms = null, $options = null)
666  {
667  $prefix = null;
668  $prefix_ns = null;
669  $id_name = 'Id';
670  $overwrite_id = true;
671  $force_uri = false;
672  if (is_array ( $options ))
673  {
674  $prefix = empty ( $options ['prefix'] ) ? null : $options ['prefix'];
675  $prefix_ns = empty ( $options ['prefix_ns'] ) ? null : $options ['prefix_ns'];
676  $id_name = empty ( $options ['id_name'] ) ? 'Id' : $options ['id_name'];
677  $overwrite_id = ! isset ( $options ['overwrite'] ) ? true : ( bool ) $options ['overwrite'];
678  $force_uri = ! isset ( $options ['force_uri'] ) ? false : ( bool ) $options ['force_uri'];
679  }
680  $attname = $id_name;
681  if (! empty ( $prefix ))
682  {
683  $attname = $prefix . ':' . $attname;
684  }
685  $refNode = $this->createNewSignNode ( 'Reference' );
686  $sinfoNode->appendChild ( $refNode );
687  if (! $node instanceof DOMDocument)
688  {
689  $uri = null;
690  if (! $overwrite_id)
691  {
692  $uri = $prefix_ns ? $node->getAttributeNS ( $prefix_ns, $id_name ) : $node->getAttribute ( $id_name );
693  }
694  if (empty ( $uri ))
695  {
696  $uri = self::generateGUID ();
697  $node->setAttributeNS ( $prefix_ns, $attname, $uri );
698  }
699  $refNode->setAttribute ( "URI", '#' . $uri );
700  }
701  elseif ($force_uri)
702  {
703  $refNode->setAttribute ( "URI", '' );
704  }
705  $transNodes = $this->createNewSignNode ( 'Transforms' );
706  $refNode->appendChild ( $transNodes );
707  if (is_array ( $arTransforms ))
708  {
709  foreach ( $arTransforms as $transform )
710  {
711  $transNode = $this->createNewSignNode ( 'Transform' );
712  $transNodes->appendChild ( $transNode );
713  if (is_array ( $transform ) && (! empty ( $transform ['http://www.w3.org/TR/1999/REC-xpath-19991116'] )) && (! empty ( $transform ['http://www.w3.org/TR/1999/REC-xpath-19991116'] ['query'] )))
714  {
715  $transNode->setAttribute ( 'Algorithm', 'http://www.w3.org/TR/1999/REC-xpath-19991116' );
716  $XPathNode = $this->createNewSignNode ( 'XPath', $transform ['http://www.w3.org/TR/1999/REC-xpath-19991116'] ['query'] );
717  $transNode->appendChild ( $XPathNode );
718  if (! empty ( $transform ['http://www.w3.org/TR/1999/REC-xpath-19991116'] ['namespaces'] ))
719  {
720  foreach ( $transform ['http://www.w3.org/TR/1999/REC-xpath-19991116'] ['namespaces'] as $prefix => $namespace )
721  {
722  $XPathNode->setAttributeNS ( "http://www.w3.org/2000/xmlns/", "xmlns:$prefix", $namespace );
723  }
724  }
725  }
726  else
727  {
728  $transNode->setAttribute ( 'Algorithm', $transform );
729  }
730  }
731  }
732  elseif (! empty ( $this->canonicalMethod ))
733  {
734  $transNode = $this->createNewSignNode ( 'Transform' );
735  $transNodes->appendChild ( $transNode );
736  $transNode->setAttribute ( 'Algorithm', $this->canonicalMethod );
737  }
738  $canonicalData = $this->processTransforms ( $refNode, $node );
739  $digValue = $this->calculateDigest ( $algorithm, $canonicalData );
740  $digestMethod = $this->createNewSignNode ( 'DigestMethod' );
741  $refNode->appendChild ( $digestMethod );
742  $digestMethod->setAttribute ( 'Algorithm', $algorithm );
743  $digestValue = $this->createNewSignNode ( 'DigestValue', $digValue );
744  $refNode->appendChild ( $digestValue );
745  }
746 
754  public function addReference($node, $algorithm, $arTransforms = null, $options = null)
755  {
756  if ($xpath = $this->getXPathObj ())
757  {
758  $query = "./secdsig:SignedInfo";
759  $nodeset = $xpath->query ( $query, $this->sigNode );
760  if ($sInfo = $nodeset->item ( 0 ))
761  {
762  $this->addRefInternal ( $sInfo, $node, $algorithm, $arTransforms, $options );
763  }
764  }
765  }
766 
774  public function addReferenceList($arNodes, $algorithm, $arTransforms = null, $options = null)
775  {
776  if ($xpath = $this->getXPathObj ())
777  {
778  $query = "./secdsig:SignedInfo";
779  $nodeset = $xpath->query ( $query, $this->sigNode );
780  if ($sInfo = $nodeset->item ( 0 ))
781  {
782  foreach ( $arNodes as $node )
783  {
784  $this->addRefInternal ( $sInfo, $node, $algorithm, $arTransforms, $options );
785  }
786  }
787  }
788  }
789 
797  public function addObject($data, $mimetype = null, $encoding = null)
798  {
799  $objNode = $this->createNewSignNode ( 'Object' );
800  $this->sigNode->appendChild ( $objNode );
801  if (! empty ( $mimetype ))
802  {
803  $objNode->setAttribute ( 'MimeType', $mimetype );
804  }
805  if (! empty ( $encoding ))
806  {
807  $objNode->setAttribute ( 'Encoding', $encoding );
808  }
809  if ($data instanceof DOMElement)
810  {
811  $newData = $this->sigNode->ownerDocument->importNode ( $data, true );
812  }
813  else
814  {
815  $newData = $this->sigNode->ownerDocument->createTextNode ( $data );
816  }
817  $objNode->appendChild ( $newData );
818  return $objNode;
819  }
820 
826  public function locateKey($node = null)
827  {
828  if (empty ( $node ))
829  {
830  $node = $this->sigNode;
831  }
832  if (! $node instanceof DOMNode)
833  {
834  return null;
835  }
836  if ($doc = $node->ownerDocument)
837  {
838  $xpath = new DOMXPath ( $doc );
839  $xpath->registerNamespace ( 'secdsig', self::XMLDSIGNS );
840  $query = "string(./secdsig:SignedInfo/secdsig:SignatureMethod/@Algorithm)";
841  $algorithm = $xpath->evaluate ( $query, $node );
842  if ($algorithm)
843  {
844  try
845  {
846  $objKey = new XMLSecurityKey ( $algorithm, array (
847  'type' => 'public'
848  ) );
849  }
850  catch ( Exception $e )
851  {
852  return null;
853  }
854  return $objKey;
855  }
856  }
857  return null;
858  }
859 
866  public function verify($objKey)
867  {
868  $doc = $this->sigNode->ownerDocument;
869  $xpath = new DOMXPath ( $doc );
870  $xpath->registerNamespace ( 'secdsig', self::XMLDSIGNS );
871  $query = "string(./secdsig:SignatureValue)";
872  $sigValue = $xpath->evaluate ( $query, $this->sigNode );
873  if (empty ( $sigValue ))
874  {
875  throw new Exception ( "Unable to locate SignatureValue" );
876  }
877  return $objKey->verifySignature ( $this->signedInfo, base64_decode ( $sigValue ) );
878  }
879 
886  public function signData($objKey, $data)
887  {
888  return $objKey->signData ( $data );
889  }
890 
896  public function sign($objKey, $appendToNode = null)
897  {
898  // If we have a parent node append it now so C14N properly works
899  if ($appendToNode != null)
900  {
901  $this->resetXPathObj ();
902  $this->appendSignature ( $appendToNode );
903  $this->sigNode = $appendToNode->lastChild;
904  }
905  if ($xpath = $this->getXPathObj ())
906  {
907  $query = "./secdsig:SignedInfo";
908  $nodeset = $xpath->query ( $query, $this->sigNode );
909  if ($sInfo = $nodeset->item ( 0 ))
910  {
911  $query = "./secdsig:SignatureMethod";
912  $nodeset = $xpath->query ( $query, $sInfo );
913  $sMethod = $nodeset->item ( 0 );
914  $sMethod->setAttribute ( 'Algorithm', $objKey->type );
915  $data = $this->canonicalizeData ( $sInfo, $this->canonicalMethod );
916  $sigValue = base64_encode ( $this->signData ( $objKey, $data ) );
917  $sigValueNode = $this->createNewSignNode ( 'SignatureValue', $sigValue );
918  if ($infoSibling = $sInfo->nextSibling)
919  {
920  $infoSibling->parentNode->insertBefore ( $sigValueNode, $infoSibling );
921  }
922  else
923  {
924  $this->sigNode->appendChild ( $sigValueNode );
925  }
926  }
927  }
928  }
929 
930  public function appendCert()
931  {
932  }
933 
939  public function appendKey($objKey, $parent = null)
940  {
941  $objKey->serializeKey ( $parent );
942  }
943 
957  public function insertSignature($node, $beforeNode = null)
958  {
959  $document = $node->ownerDocument;
960  $signatureElement = $document->importNode ( $this->sigNode, true );
961  if ($beforeNode == null)
962  {
963  return $node->insertBefore ( $signatureElement );
964  }
965  else
966  {
967  return $node->insertBefore ( $signatureElement, $beforeNode );
968  }
969  }
970 
977  public function appendSignature($parentNode, $insertBefore = false)
978  {
979  $beforeNode = $insertBefore ? $parentNode->firstChild : null;
980  return $this->insertSignature ( $parentNode, $beforeNode );
981  }
982 
989  public static function get509XCert($cert, $isPEMFormat = true)
990  {
991  $certs = self::staticGet509XCerts ( $cert, $isPEMFormat );
992  if (! empty ( $certs ))
993  {
994  return $certs [0];
995  }
996  return '';
997  }
998 
1005  public static function staticGet509XCerts($certs, $isPEMFormat = true)
1006  {
1007  if ($isPEMFormat)
1008  {
1009  $data = '';
1010  $certlist = array ();
1011  $arCert = explode ( "\n", $certs );
1012  $inData = false;
1013  foreach ( $arCert as $curData )
1014  {
1015  if (! $inData)
1016  {
1017  if (strncmp ( $curData, '-----BEGIN CERTIFICATE', 22 ) == 0)
1018  {
1019  $inData = true;
1020  }
1021  }
1022  else
1023  {
1024  if (strncmp ( $curData, '-----END CERTIFICATE', 20 ) == 0)
1025  {
1026  $inData = false;
1027  $certlist [] = $data;
1028  $data = '';
1029  continue;
1030  }
1031  $data .= trim ( $curData );
1032  }
1033  }
1034  return $certlist;
1035  }
1036  else
1037  {
1038  return array (
1039  $certs
1040  );
1041  }
1042  }
1043 
1054  public static function staticAdd509Cert($parentRef, $cert, $isPEMFormat = true, $isURL = false, $xpath = null, $options = null)
1055  {
1056  if ($isURL)
1057  {
1058  $cert = file_get_contents ( $cert );
1059  }
1060  if (! $parentRef instanceof DOMElement)
1061  {
1062  throw new Exception ( 'Invalid parent Node parameter' );
1063  }
1064  $baseDoc = $parentRef->ownerDocument;
1065  if (empty ( $xpath ))
1066  {
1067  $xpath = new DOMXPath ( $parentRef->ownerDocument );
1068  $xpath->registerNamespace ( 'secdsig', self::XMLDSIGNS );
1069  }
1070  $query = "./secdsig:KeyInfo";
1071  $nodeset = $xpath->query ( $query, $parentRef );
1072  $keyInfo = $nodeset->item ( 0 );
1073  $dsig_pfx = '';
1074  if (! $keyInfo)
1075  {
1076  $pfx = $parentRef->lookupPrefix ( self::XMLDSIGNS );
1077  if (! empty ( $pfx ))
1078  {
1079  $dsig_pfx = $pfx . ":";
1080  }
1081  $inserted = false;
1082  $keyInfo = $baseDoc->createElementNS ( self::XMLDSIGNS, $dsig_pfx . 'KeyInfo' );
1083  $query = "./secdsig:Object";
1084  $nodeset = $xpath->query ( $query, $parentRef );
1085  if ($sObject = $nodeset->item ( 0 ))
1086  {
1087  $sObject->parentNode->insertBefore ( $keyInfo, $sObject );
1088  $inserted = true;
1089  }
1090  if (! $inserted)
1091  {
1092  $parentRef->appendChild ( $keyInfo );
1093  }
1094  }
1095  else
1096  {
1097  $pfx = $keyInfo->lookupPrefix ( self::XMLDSIGNS );
1098  if (! empty ( $pfx ))
1099  {
1100  $dsig_pfx = $pfx . ":";
1101  }
1102  }
1103  // Add all certs if there are more than one
1104  $certs = self::staticGet509XCerts ( $cert, $isPEMFormat );
1105  // Attach X509 data node
1106  $x509DataNode = $baseDoc->createElementNS ( self::XMLDSIGNS, $dsig_pfx . 'X509Data' );
1107  $keyInfo->appendChild ( $x509DataNode );
1108  $issuerSerial = false;
1109  $subjectName = false;
1110  if (is_array ( $options ))
1111  {
1112  if (! empty ( $options ['issuerSerial'] ))
1113  {
1114  $issuerSerial = true;
1115  }
1116  if (! empty ( $options ['subjectName'] ))
1117  {
1118  $subjectName = true;
1119  }
1120  }
1121  // Attach all certificate nodes and any additional data
1122  foreach ( $certs as $X509Cert )
1123  {
1124  if ($issuerSerial || $subjectName)
1125  {
1126  if ($certData = openssl_x509_parse ( "-----BEGIN CERTIFICATE-----\n" . chunk_split ( $X509Cert, 64, "\n" ) . "-----END CERTIFICATE-----\n" ))
1127  {
1128  if ($subjectName && ! empty ( $certData ['subject'] ))
1129  {
1130  if (is_array ( $certData ['subject'] ))
1131  {
1132  $parts = array ();
1133  foreach ( $certData ['subject'] as $key => $value )
1134  {
1135  if (is_array ( $value ))
1136  {
1137  foreach ( $value as $valueElement )
1138  {
1139  array_unshift ( $parts, "$key=$valueElement" );
1140  }
1141  }
1142  else
1143  {
1144  array_unshift ( $parts, "$key=$value" );
1145  }
1146  }
1147  $subjectNameValue = implode ( ',', $parts );
1148  }
1149  else
1150  {
1151  $subjectNameValue = $certData ['issuer'];
1152  }
1153  $x509SubjectNode = $baseDoc->createElementNS ( self::XMLDSIGNS, $dsig_pfx . 'X509SubjectName', $subjectNameValue );
1154  $x509DataNode->appendChild ( $x509SubjectNode );
1155  }
1156  if ($issuerSerial && ! empty ( $certData ['issuer'] ) && ! empty ( $certData ['serialNumber'] ))
1157  {
1158  if (is_array ( $certData ['issuer'] ))
1159  {
1160  $parts = array ();
1161  foreach ( $certData ['issuer'] as $key => $value )
1162  {
1163  array_unshift ( $parts, "$key=$value" );
1164  }
1165  $issuerName = implode ( ',', $parts );
1166  }
1167  else
1168  {
1169  $issuerName = $certData ['issuer'];
1170  }
1171  $x509IssuerNode = $baseDoc->createElementNS ( self::XMLDSIGNS, $dsig_pfx . 'X509IssuerSerial' );
1172  $x509DataNode->appendChild ( $x509IssuerNode );
1173  $x509Node = $baseDoc->createElementNS ( self::XMLDSIGNS, $dsig_pfx . 'X509IssuerName', $issuerName );
1174  $x509IssuerNode->appendChild ( $x509Node );
1175  $x509Node = $baseDoc->createElementNS ( self::XMLDSIGNS, $dsig_pfx . 'X509SerialNumber', $certData ['serialNumber'] );
1176  $x509IssuerNode->appendChild ( $x509Node );
1177  }
1178  }
1179  }
1180  $x509CertNode = $baseDoc->createElementNS ( self::XMLDSIGNS, $dsig_pfx . 'X509Certificate', $X509Cert );
1181  $x509DataNode->appendChild ( $x509CertNode );
1182  }
1183  }
1184 
1192  public function add509Cert($cert, $isPEMFormat = true, $isURL = false, $options = null)
1193  {
1194  if ($xpath = $this->getXPathObj ())
1195  {
1196  self::staticAdd509Cert ( $this->sigNode, $cert, $isPEMFormat, $isURL, $xpath, $options );
1197  }
1198  }
1199 
1210  public function appendToKeyInfo($node)
1211  {
1212  $parentRef = $this->sigNode;
1213  $baseDoc = $parentRef->ownerDocument;
1214  $xpath = $this->getXPathObj ();
1215  if (empty ( $xpath ))
1216  {
1217  $xpath = new DOMXPath ( $parentRef->ownerDocument );
1218  $xpath->registerNamespace ( 'secdsig', self::XMLDSIGNS );
1219  }
1220  $query = "./secdsig:KeyInfo";
1221  $nodeset = $xpath->query ( $query, $parentRef );
1222  $keyInfo = $nodeset->item ( 0 );
1223  if (! $keyInfo)
1224  {
1225  $dsig_pfx = '';
1226  $pfx = $parentRef->lookupPrefix ( self::XMLDSIGNS );
1227  if (! empty ( $pfx ))
1228  {
1229  $dsig_pfx = $pfx . ":";
1230  }
1231  $inserted = false;
1232  $keyInfo = $baseDoc->createElementNS ( self::XMLDSIGNS, $dsig_pfx . 'KeyInfo' );
1233  $query = "./secdsig:Object";
1234  $nodeset = $xpath->query ( $query, $parentRef );
1235  if ($sObject = $nodeset->item ( 0 ))
1236  {
1237  $sObject->parentNode->insertBefore ( $keyInfo, $sObject );
1238  $inserted = true;
1239  }
1240  if (! $inserted)
1241  {
1242  $parentRef->appendChild ( $keyInfo );
1243  }
1244  }
1245  $keyInfo->appendChild ( $node );
1246  return $keyInfo;
1247  }
1248 
1260  public function getValidatedNodes()
1261  {
1262  return $this->validatedNodes;
1263  }
1264 
1265 }
addReferenceList($arNodes, $algorithm, $arTransforms=null, $options=null)
getRefNodeID($refNode)
static staticAdd509Cert($parentRef, $cert, $isPEMFormat=true, $isURL=false, $xpath=null, $options=null)
__construct($prefix= 'ds')
addReference($node, $algorithm, $arTransforms=null, $options=null)
locateSignature($objDoc, $pos=0)
processTransforms($refNode, $objData, $includeCommentNodes=true)
sign($objKey, $appendToNode=null)
signData($objKey, $data)
appendSignature($parentNode, $insertBefore=false)
static generate_GUID($prefix= 'pfx')
validateDigest($refNode, $data)
insertSignature($node, $beforeNode=null)
appendKey($objKey, $parent=null)
setCanonicalMethod($method)
calculateDigest($digestAlgorithm, $data, $encode=true)
static staticGet509XCerts($certs, $isPEMFormat=true)
processRefNode($refNode)
add509Cert($cert, $isPEMFormat=true, $isURL=false, $options=null)
createNewSignNode($name, $value=null)
locateKey($node=null)
static generateGUID($prefix= 'pfx')
addObject($data, $mimetype=null, $encoding=null)
static get509XCert($cert, $isPEMFormat=true)