ILIAS  release_5-3 Revision v5.3.23-19-g915713cf615
XMLSecurityDSig.php
Go to the documentation of this file.
1<?php
3
4use DOMDocument;
5use DOMElement;
6use DOMNode;
7use DOMXPath;
8use Exception;
10
52{
53 const XMLDSIGNS = 'http://www.w3.org/2000/09/xmldsig#';
54 const SHA1 = 'http://www.w3.org/2000/09/xmldsig#sha1';
55 const SHA256 = 'http://www.w3.org/2001/04/xmlenc#sha256';
56 const SHA384 = 'http://www.w3.org/2001/04/xmldsig-more#sha384';
57 const SHA512 = 'http://www.w3.org/2001/04/xmlenc#sha512';
58 const RIPEMD160 = 'http://www.w3.org/2001/04/xmlenc#ripemd160';
59
60 const C14N = 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315';
61 const C14N_COMMENTS = 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments';
62 const EXC_C14N = 'http://www.w3.org/2001/10/xml-exc-c14n#';
63 const EXC_C14N_COMMENTS = 'http://www.w3.org/2001/10/xml-exc-c14n#WithComments';
64
65 const template = '<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
66 <ds:SignedInfo>
67 <ds:SignatureMethod />
68 </ds:SignedInfo>
69</ds:Signature>';
70
71 const BASE_TEMPLATE = '<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
72 <SignedInfo>
73 <SignatureMethod />
74 </SignedInfo>
75</Signature>';
76
78 public $sigNode = null;
79
81 public $idKeys = array();
82
84 public $idNS = array();
85
87 private $signedInfo = null;
88
90 private $xPathCtx = null;
91
93 private $canonicalMethod = null;
94
96 private $prefix = '';
97
99 private $searchpfx = 'secdsig';
100
105 private $validatedNodes = null;
106
110 public function __construct($prefix='ds')
111 {
113 if (! empty($prefix)) {
114 $this->prefix = $prefix.':';
115 $search = array("<S", "</S", "xmlns=");
116 $replace = array("<$prefix:S", "</$prefix:S", "xmlns:$prefix=");
117 $template = str_replace($search, $replace, $template);
118 }
119 $sigdoc = new DOMDocument();
120 $sigdoc->loadXML($template);
121 $this->sigNode = $sigdoc->documentElement;
122 }
123
127 private function resetXPathObj()
128 {
129 $this->xPathCtx = null;
130 }
131
137 private function getXPathObj()
138 {
139 if (empty($this->xPathCtx) && ! empty($this->sigNode)) {
140 $xpath = new DOMXPath($this->sigNode->ownerDocument);
141 $xpath->registerNamespace('secdsig', self::XMLDSIGNS);
142 $this->xPathCtx = $xpath;
143 }
144 return $this->xPathCtx;
145 }
146
154 public static function generateGUID($prefix='pfx')
155 {
156 $uuid = md5(uniqid(mt_rand(), true));
157 $guid = $prefix.substr($uuid, 0, 8)."-".
158 substr($uuid, 8, 4)."-".
159 substr($uuid, 12, 4)."-".
160 substr($uuid, 16, 4)."-".
161 substr($uuid, 20, 12);
162 return $guid;
163 }
164
174 public static function generate_GUID($prefix='pfx')
175 {
177 }
178
184 public function locateSignature($objDoc, $pos=0)
185 {
186 if ($objDoc instanceof DOMDocument) {
187 $doc = $objDoc;
188 } else {
189 $doc = $objDoc->ownerDocument;
190 }
191 if ($doc) {
192 $xpath = new DOMXPath($doc);
193 $xpath->registerNamespace('secdsig', self::XMLDSIGNS);
194 $query = ".//secdsig:Signature";
195 $nodeset = $xpath->query($query, $objDoc);
196 $this->sigNode = $nodeset->item($pos);
197 $query = "./secdsig:SignedInfo";
198 $nodeset = $xpath->query($query, $this->sigNode);
199 if ($nodeset->length > 1) {
200 throw new Exception("Invalid structure - Too many SignedInfo elements found");
201 }
202 return $this->sigNode;
203 }
204 return null;
205 }
206
212 public function createNewSignNode($name, $value=null)
213 {
214 $doc = $this->sigNode->ownerDocument;
215 if (! is_null($value)) {
216 $node = $doc->createElementNS(self::XMLDSIGNS, $this->prefix.$name, $value);
217 } else {
218 $node = $doc->createElementNS(self::XMLDSIGNS, $this->prefix.$name);
219 }
220 return $node;
221 }
222
227 public function setCanonicalMethod($method)
228 {
229 switch ($method) {
230 case 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315':
231 case 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments':
232 case 'http://www.w3.org/2001/10/xml-exc-c14n#':
233 case 'http://www.w3.org/2001/10/xml-exc-c14n#WithComments':
234 $this->canonicalMethod = $method;
235 break;
236 default:
237 throw new Exception('Invalid Canonical Method');
238 }
239 if ($xpath = $this->getXPathObj()) {
240 $query = './'.$this->searchpfx.':SignedInfo';
241 $nodeset = $xpath->query($query, $this->sigNode);
242 if ($sinfo = $nodeset->item(0)) {
243 $query = './'.$this->searchpfx.'CanonicalizationMethod';
244 $nodeset = $xpath->query($query, $sinfo);
245 if (! ($canonNode = $nodeset->item(0))) {
246 $canonNode = $this->createNewSignNode('CanonicalizationMethod');
247 $sinfo->insertBefore($canonNode, $sinfo->firstChild);
248 }
249 $canonNode->setAttribute('Algorithm', $this->canonicalMethod);
250 }
251 }
252 }
253
261 private function canonicalizeData($node, $canonicalmethod, $arXPath=null, $prefixList=null)
262 {
263 $exclusive = false;
264 $withComments = false;
265 switch ($canonicalmethod) {
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
282 if (is_null($arXPath) && ($node instanceof DOMNode) && ($node->ownerDocument !== null) && $node->isSameNode($node->ownerDocument->documentElement)) {
283 /* Check for any PI or comments as they would have been excluded */
284 $element = $node;
285 while ($refnode = $element->previousSibling) {
286 if ($refnode->nodeType == XML_PI_NODE || (($refnode->nodeType == XML_COMMENT_NODE) && $withComments)) {
287 break;
288 }
289 $element = $refnode;
290 }
291 if ($refnode == null) {
292 $node = $node->ownerDocument;
293 }
294 }
295
296 return $node->C14N($exclusive, $withComments, $arXPath, $prefixList);
297 }
298
302 public function canonicalizeSignedInfo()
303 {
304
305 $doc = $this->sigNode->ownerDocument;
306 $canonicalmethod = null;
307 if ($doc) {
308 $xpath = $this->getXPathObj();
309 $query = "./secdsig:SignedInfo";
310 $nodeset = $xpath->query($query, $this->sigNode);
311 if ($nodeset->length > 1) {
312 throw new Exception("Invalid structure - Too many SignedInfo elements found");
313 }
314 if ($signInfoNode = $nodeset->item(0)) {
315 $query = "./secdsig:CanonicalizationMethod";
316 $nodeset = $xpath->query($query, $signInfoNode);
317 if ($canonNode = $nodeset->item(0)) {
318 $canonicalmethod = $canonNode->getAttribute('Algorithm');
319 }
320 $this->signedInfo = $this->canonicalizeData($signInfoNode, $canonicalmethod);
321 return $this->signedInfo;
322 }
323 }
324 return null;
325 }
326
334 public function calculateDigest($digestAlgorithm, $data, $encode = true)
335 {
336 switch ($digestAlgorithm) {
337 case self::SHA1:
338 $alg = 'sha1';
339 break;
340 case self::SHA256:
341 $alg = 'sha256';
342 break;
343 case self::SHA384:
344 $alg = 'sha384';
345 break;
346 case self::SHA512:
347 $alg = 'sha512';
348 break;
349 case self::RIPEMD160:
350 $alg = 'ripemd160';
351 break;
352 default:
353 throw new Exception("Cannot validate digest: Unsupported Algorithm <$digestAlgorithm>");
354 }
355
356 $digest = hash($alg, $data, true);
357 if ($encode) {
358 $digest = base64_encode($digest);
359 }
360 return $digest;
361
362 }
363
369 public function validateDigest($refNode, $data)
370 {
371 $xpath = new DOMXPath($refNode->ownerDocument);
372 $xpath->registerNamespace('secdsig', self::XMLDSIGNS);
373 $query = 'string(./secdsig:DigestMethod/@Algorithm)';
374 $digestAlgorithm = $xpath->evaluate($query, $refNode);
375 $digValue = $this->calculateDigest($digestAlgorithm, $data, false);
376 $query = 'string(./secdsig:DigestValue)';
377 $digestValue = $xpath->evaluate($query, $refNode);
378 return ($digValue === base64_decode($digestValue));
379 }
380
387 public function processTransforms($refNode, $objData, $includeCommentNodes = true)
388 {
389 $data = $objData;
390 $xpath = new DOMXPath($refNode->ownerDocument);
391 $xpath->registerNamespace('secdsig', self::XMLDSIGNS);
392 $query = './secdsig:Transforms/secdsig:Transform';
393 $nodelist = $xpath->query($query, $refNode);
394 $canonicalMethod = 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315';
395 $arXPath = null;
396 $prefixList = null;
397 foreach ($nodelist AS $transform) {
398 $algorithm = $transform->getAttribute("Algorithm");
399 switch ($algorithm) {
400 case 'http://www.w3.org/2001/10/xml-exc-c14n#':
401 case 'http://www.w3.org/2001/10/xml-exc-c14n#WithComments':
402
403 if (!$includeCommentNodes) {
404 /* We remove comment nodes by forcing it to use a canonicalization
405 * without comments.
406 */
407 $canonicalMethod = 'http://www.w3.org/2001/10/xml-exc-c14n#';
408 } else {
409 $canonicalMethod = $algorithm;
410 }
411
412 $node = $transform->firstChild;
413 while ($node) {
414 if ($node->localName == 'InclusiveNamespaces') {
415 if ($pfx = $node->getAttribute('PrefixList')) {
416 $arpfx = array();
417 $pfxlist = explode(" ", $pfx);
418 foreach ($pfxlist AS $pfx) {
419 $val = trim($pfx);
420 if (! empty($val)) {
421 $arpfx[] = $val;
422 }
423 }
424 if (count($arpfx) > 0) {
425 $prefixList = $arpfx;
426 }
427 }
428 break;
429 }
430 $node = $node->nextSibling;
431 }
432 break;
433 case 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315':
434 case 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments':
435 if (!$includeCommentNodes) {
436 /* We remove comment nodes by forcing it to use a canonicalization
437 * without comments.
438 */
439 $canonicalMethod = 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315';
440 } else {
441 $canonicalMethod = $algorithm;
442 }
443
444 break;
445 case 'http://www.w3.org/TR/1999/REC-xpath-19991116':
446 $node = $transform->firstChild;
447 while ($node) {
448 if ($node->localName == 'XPath') {
449 $arXPath = array();
450 $arXPath['query'] = '(.//. | .//@* | .//namespace::*)['.$node->nodeValue.']';
451 $arXPath['namespaces'] = array();
452 $nslist = $xpath->query('./namespace::*', $node);
453 foreach ($nslist AS $nsnode) {
454 if ($nsnode->localName != "xml") {
455 $arXPath['namespaces'][$nsnode->localName] = $nsnode->nodeValue;
456 }
457 }
458 break;
459 }
460 $node = $node->nextSibling;
461 }
462 break;
463 }
464 }
465 if ($data instanceof DOMNode) {
466 $data = $this->canonicalizeData($objData, $canonicalMethod, $arXPath, $prefixList);
467 }
468 return $data;
469 }
470
475 public function processRefNode($refNode)
476 {
477 $dataObject = null;
478
479 /*
480 * Depending on the URI, we may not want to include comments in the result
481 * See: http://www.w3.org/TR/xmldsig-core/#sec-ReferenceProcessingModel
482 */
483 $includeCommentNodes = true;
484
485 if ($uri = $refNode->getAttribute("URI")) {
486 $arUrl = parse_url($uri);
487 if (empty($arUrl['path'])) {
488 if ($identifier = $arUrl['fragment']) {
489
490 /* This reference identifies a node with the given id by using
491 * a URI on the form "#identifier". This should not include comments.
492 */
493 $includeCommentNodes = false;
494
495 $xPath = new DOMXPath($refNode->ownerDocument);
496 if ($this->idNS && is_array($this->idNS)) {
497 foreach ($this->idNS as $nspf => $ns) {
498 $xPath->registerNamespace($nspf, $ns);
499 }
500 }
501 $iDlist = '@Id="'.XPath::filterAttrValue($identifier, XPath::DOUBLE_QUOTE).'"';
502 if (is_array($this->idKeys)) {
503 foreach ($this->idKeys as $idKey) {
504 $iDlist .= " or @".XPath::filterAttrName($idKey).'="'.
505 XPath::filterAttrValue($identifier, XPath::DOUBLE_QUOTE).'"';
506 }
507 }
508 $query = '//*['.$iDlist.']';
509 $dataObject = $xPath->query($query)->item(0);
510 } else {
511 $dataObject = $refNode->ownerDocument;
512 }
513 }
514 } else {
515 /* This reference identifies the root node with an empty URI. This should
516 * not include comments.
517 */
518 $includeCommentNodes = false;
519
520 $dataObject = $refNode->ownerDocument;
521 }
522 $data = $this->processTransforms($refNode, $dataObject, $includeCommentNodes);
523 if (!$this->validateDigest($refNode, $data)) {
524 return false;
525 }
526
527 if ($dataObject instanceof DOMNode) {
528 /* Add this node to the list of validated nodes. */
529 if (! empty($identifier)) {
530 $this->validatedNodes[$identifier] = $dataObject;
531 } else {
532 $this->validatedNodes[] = $dataObject;
533 }
534 }
535
536 return true;
537 }
538
543 public function getRefNodeID($refNode)
544 {
545 if ($uri = $refNode->getAttribute("URI")) {
546 $arUrl = parse_url($uri);
547 if (empty($arUrl['path'])) {
548 if ($identifier = $arUrl['fragment']) {
549 return $identifier;
550 }
551 }
552 }
553 return null;
554 }
555
560 public function getRefIDs()
561 {
562 $refids = array();
563
564 $xpath = $this->getXPathObj();
565 $query = "./secdsig:SignedInfo[1]/secdsig:Reference";
566 $nodeset = $xpath->query($query, $this->sigNode);
567 if ($nodeset->length == 0) {
568 throw new Exception("Reference nodes not found");
569 }
570 foreach ($nodeset AS $refNode) {
571 $refids[] = $this->getRefNodeID($refNode);
572 }
573 return $refids;
574 }
575
580 public function validateReference()
581 {
582 $docElem = $this->sigNode->ownerDocument->documentElement;
583 if (! $docElem->isSameNode($this->sigNode)) {
584 if ($this->sigNode->parentNode != null) {
585 $this->sigNode->parentNode->removeChild($this->sigNode);
586 }
587 }
588 $xpath = $this->getXPathObj();
589 $query = "./secdsig:SignedInfo[1]/secdsig:Reference";
590 $nodeset = $xpath->query($query, $this->sigNode);
591 if ($nodeset->length == 0) {
592 throw new Exception("Reference nodes not found");
593 }
594
595 /* Initialize/reset the list of validated nodes. */
596 $this->validatedNodes = array();
597
598 foreach ($nodeset AS $refNode) {
599 if (! $this->processRefNode($refNode)) {
600 /* Clear the list of validated nodes. */
601 $this->validatedNodes = null;
602 throw new Exception("Reference validation failed");
603 }
604 }
605 return true;
606 }
607
615 private function addRefInternal($sinfoNode, $node, $algorithm, $arTransforms=null, $options=null)
616 {
617 $prefix = null;
618 $prefix_ns = null;
619 $id_name = 'Id';
620 $overwrite_id = true;
621 $force_uri = false;
622
623 if (is_array($options)) {
624 $prefix = empty($options['prefix']) ? null : $options['prefix'];
625 $prefix_ns = empty($options['prefix_ns']) ? null : $options['prefix_ns'];
626 $id_name = empty($options['id_name']) ? 'Id' : $options['id_name'];
627 $overwrite_id = !isset($options['overwrite']) ? true : (bool) $options['overwrite'];
628 $force_uri = !isset($options['force_uri']) ? false : (bool) $options['force_uri'];
629 }
630
631 $attname = $id_name;
632 if (! empty($prefix)) {
633 $attname = $prefix.':'.$attname;
634 }
635
636 $refNode = $this->createNewSignNode('Reference');
637 $sinfoNode->appendChild($refNode);
638
639 if (! $node instanceof DOMDocument) {
640 $uri = null;
641 if (! $overwrite_id) {
642 $uri = $prefix_ns ? $node->getAttributeNS($prefix_ns, $id_name) : $node->getAttribute($id_name);
643 }
644 if (empty($uri)) {
645 $uri = self::generateGUID();
646 $node->setAttributeNS($prefix_ns, $attname, $uri);
647 }
648 $refNode->setAttribute("URI", '#'.$uri);
649 } elseif ($force_uri) {
650 $refNode->setAttribute("URI", '');
651 }
652
653 $transNodes = $this->createNewSignNode('Transforms');
654 $refNode->appendChild($transNodes);
655
656 if (is_array($arTransforms)) {
657 foreach ($arTransforms AS $transform) {
658 $transNode = $this->createNewSignNode('Transform');
659 $transNodes->appendChild($transNode);
660 if (is_array($transform) &&
661 (! empty($transform['http://www.w3.org/TR/1999/REC-xpath-19991116'])) &&
662 (! empty($transform['http://www.w3.org/TR/1999/REC-xpath-19991116']['query']))) {
663 $transNode->setAttribute('Algorithm', 'http://www.w3.org/TR/1999/REC-xpath-19991116');
664 $XPathNode = $this->createNewSignNode('XPath', $transform['http://www.w3.org/TR/1999/REC-xpath-19991116']['query']);
665 $transNode->appendChild($XPathNode);
666 if (! empty($transform['http://www.w3.org/TR/1999/REC-xpath-19991116']['namespaces'])) {
667 foreach ($transform['http://www.w3.org/TR/1999/REC-xpath-19991116']['namespaces'] AS $prefix => $namespace) {
668 $XPathNode->setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:$prefix", $namespace);
669 }
670 }
671 } else {
672 $transNode->setAttribute('Algorithm', $transform);
673 }
674 }
675 } elseif (! empty($this->canonicalMethod)) {
676 $transNode = $this->createNewSignNode('Transform');
677 $transNodes->appendChild($transNode);
678 $transNode->setAttribute('Algorithm', $this->canonicalMethod);
679 }
680
681 $canonicalData = $this->processTransforms($refNode, $node);
682 $digValue = $this->calculateDigest($algorithm, $canonicalData);
683
684 $digestMethod = $this->createNewSignNode('DigestMethod');
685 $refNode->appendChild($digestMethod);
686 $digestMethod->setAttribute('Algorithm', $algorithm);
687
688 $digestValue = $this->createNewSignNode('DigestValue', $digValue);
689 $refNode->appendChild($digestValue);
690 }
691
698 public function addReference($node, $algorithm, $arTransforms=null, $options=null)
699 {
700 if ($xpath = $this->getXPathObj()) {
701 $query = "./secdsig:SignedInfo";
702 $nodeset = $xpath->query($query, $this->sigNode);
703 if ($sInfo = $nodeset->item(0)) {
704 $this->addRefInternal($sInfo, $node, $algorithm, $arTransforms, $options);
705 }
706 }
707 }
708
715 public function addReferenceList($arNodes, $algorithm, $arTransforms=null, $options=null)
716 {
717 if ($xpath = $this->getXPathObj()) {
718 $query = "./secdsig:SignedInfo";
719 $nodeset = $xpath->query($query, $this->sigNode);
720 if ($sInfo = $nodeset->item(0)) {
721 foreach ($arNodes AS $node) {
722 $this->addRefInternal($sInfo, $node, $algorithm, $arTransforms, $options);
723 }
724 }
725 }
726 }
727
734 public function addObject($data, $mimetype=null, $encoding=null)
735 {
736 $objNode = $this->createNewSignNode('Object');
737 $this->sigNode->appendChild($objNode);
738 if (! empty($mimetype)) {
739 $objNode->setAttribute('MimeType', $mimetype);
740 }
741 if (! empty($encoding)) {
742 $objNode->setAttribute('Encoding', $encoding);
743 }
744
745 if ($data instanceof DOMElement) {
746 $newData = $this->sigNode->ownerDocument->importNode($data, true);
747 } else {
748 $newData = $this->sigNode->ownerDocument->createTextNode($data);
749 }
750 $objNode->appendChild($newData);
751
752 return $objNode;
753 }
754
759 public function locateKey($node=null)
760 {
761 if (empty($node)) {
762 $node = $this->sigNode;
763 }
764 if (! $node instanceof DOMNode) {
765 return null;
766 }
767 if ($doc = $node->ownerDocument) {
768 $xpath = new DOMXPath($doc);
769 $xpath->registerNamespace('secdsig', self::XMLDSIGNS);
770 $query = "string(./secdsig:SignedInfo/secdsig:SignatureMethod/@Algorithm)";
771 $algorithm = $xpath->evaluate($query, $node);
772 if ($algorithm) {
773 try {
774 $objKey = new XMLSecurityKey($algorithm, array('type' => 'public'));
775 } catch (Exception $e) {
776 return null;
777 }
778 return $objKey;
779 }
780 }
781 return null;
782 }
783
800 public function verify($objKey)
801 {
802 $doc = $this->sigNode->ownerDocument;
803 $xpath = new DOMXPath($doc);
804 $xpath->registerNamespace('secdsig', self::XMLDSIGNS);
805 $query = "string(./secdsig:SignatureValue)";
806 $sigValue = $xpath->evaluate($query, $this->sigNode);
807 if (empty($sigValue)) {
808 throw new Exception("Unable to locate SignatureValue");
809 }
810 return $objKey->verifySignature($this->signedInfo, base64_decode($sigValue));
811 }
812
818 public function signData($objKey, $data)
819 {
820 return $objKey->signData($data);
821 }
822
827 public function sign($objKey, $appendToNode = null)
828 {
829 // If we have a parent node append it now so C14N properly works
830 if ($appendToNode != null) {
831 $this->resetXPathObj();
832 $this->appendSignature($appendToNode);
833 $this->sigNode = $appendToNode->lastChild;
834 }
835 if ($xpath = $this->getXPathObj()) {
836 $query = "./secdsig:SignedInfo";
837 $nodeset = $xpath->query($query, $this->sigNode);
838 if ($sInfo = $nodeset->item(0)) {
839 $query = "./secdsig:SignatureMethod";
840 $nodeset = $xpath->query($query, $sInfo);
841 $sMethod = $nodeset->item(0);
842 $sMethod->setAttribute('Algorithm', $objKey->type);
843 $data = $this->canonicalizeData($sInfo, $this->canonicalMethod);
844 $sigValue = base64_encode($this->signData($objKey, $data));
845 $sigValueNode = $this->createNewSignNode('SignatureValue', $sigValue);
846 if ($infoSibling = $sInfo->nextSibling) {
847 $infoSibling->parentNode->insertBefore($sigValueNode, $infoSibling);
848 } else {
849 $this->sigNode->appendChild($sigValueNode);
850 }
851 }
852 }
853 }
854
855 public function appendCert()
856 {
857
858 }
859
864 public function appendKey($objKey, $parent=null)
865 {
866 $objKey->serializeKey($parent);
867 }
868
869
881 public function insertSignature($node, $beforeNode = null)
882 {
883
884 $document = $node->ownerDocument;
885 $signatureElement = $document->importNode($this->sigNode, true);
886
887 if ($beforeNode == null) {
888 return $node->insertBefore($signatureElement);
889 } else {
890 return $node->insertBefore($signatureElement, $beforeNode);
891 }
892 }
893
899 public function appendSignature($parentNode, $insertBefore = false)
900 {
901 $beforeNode = $insertBefore ? $parentNode->firstChild : null;
902 return $this->insertSignature($parentNode, $beforeNode);
903 }
904
910 public static function get509XCert($cert, $isPEMFormat=true)
911 {
912 $certs = self::staticGet509XCerts($cert, $isPEMFormat);
913 if (! empty($certs)) {
914 return $certs[0];
915 }
916 return '';
917 }
918
924 public static function staticGet509XCerts($certs, $isPEMFormat=true)
925 {
926 if ($isPEMFormat) {
927 $data = '';
928 $certlist = array();
929 $arCert = explode("\n", $certs);
930 $inData = false;
931 foreach ($arCert AS $curData) {
932 if (! $inData) {
933 if (strncmp($curData, '-----BEGIN CERTIFICATE', 22) == 0) {
934 $inData = true;
935 }
936 } else {
937 if (strncmp($curData, '-----END CERTIFICATE', 20) == 0) {
938 $inData = false;
939 $certlist[] = $data;
940 $data = '';
941 continue;
942 }
943 $data .= trim($curData);
944 }
945 }
946 return $certlist;
947 } else {
948 return array($certs);
949 }
950 }
951
961 public static function staticAdd509Cert($parentRef, $cert, $isPEMFormat=true, $isURL=false, $xpath=null, $options=null)
962 {
963 if ($isURL) {
964 $cert = file_get_contents($cert);
965 }
966 if (! $parentRef instanceof DOMElement) {
967 throw new Exception('Invalid parent Node parameter');
968 }
969 $baseDoc = $parentRef->ownerDocument;
970
971 if (empty($xpath)) {
972 $xpath = new DOMXPath($parentRef->ownerDocument);
973 $xpath->registerNamespace('secdsig', self::XMLDSIGNS);
974 }
975
976 $query = "./secdsig:KeyInfo";
977 $nodeset = $xpath->query($query, $parentRef);
978 $keyInfo = $nodeset->item(0);
979 $dsig_pfx = '';
980 if (! $keyInfo) {
981 $pfx = $parentRef->lookupPrefix(self::XMLDSIGNS);
982 if (! empty($pfx)) {
983 $dsig_pfx = $pfx.":";
984 }
985 $inserted = false;
986 $keyInfo = $baseDoc->createElementNS(self::XMLDSIGNS, $dsig_pfx.'KeyInfo');
987
988 $query = "./secdsig:Object";
989 $nodeset = $xpath->query($query, $parentRef);
990 if ($sObject = $nodeset->item(0)) {
991 $sObject->parentNode->insertBefore($keyInfo, $sObject);
992 $inserted = true;
993 }
994
995 if (! $inserted) {
996 $parentRef->appendChild($keyInfo);
997 }
998 } else {
999 $pfx = $keyInfo->lookupPrefix(self::XMLDSIGNS);
1000 if (! empty($pfx)) {
1001 $dsig_pfx = $pfx.":";
1002 }
1003 }
1004
1005 // Add all certs if there are more than one
1006 $certs = self::staticGet509XCerts($cert, $isPEMFormat);
1007
1008 // Attach X509 data node
1009 $x509DataNode = $baseDoc->createElementNS(self::XMLDSIGNS, $dsig_pfx.'X509Data');
1010 $keyInfo->appendChild($x509DataNode);
1011
1012 $issuerSerial = false;
1013 $subjectName = false;
1014 if (is_array($options)) {
1015 if (! empty($options['issuerSerial'])) {
1016 $issuerSerial = true;
1017 }
1018 if (! empty($options['subjectName'])) {
1019 $subjectName = true;
1020 }
1021 }
1022
1023 // Attach all certificate nodes and any additional data
1024 foreach ($certs as $X509Cert) {
1025 if ($issuerSerial || $subjectName) {
1026 if ($certData = openssl_x509_parse("-----BEGIN CERTIFICATE-----\n".chunk_split($X509Cert, 64, "\n")."-----END CERTIFICATE-----\n")) {
1027 if ($subjectName && ! empty($certData['subject'])) {
1028 if (is_array($certData['subject'])) {
1029 $parts = array();
1030 foreach ($certData['subject'] AS $key => $value) {
1031 if (is_array($value)) {
1032 foreach ($value as $valueElement) {
1033 array_unshift($parts, "$key=$valueElement");
1034 }
1035 } else {
1036 array_unshift($parts, "$key=$value");
1037 }
1038 }
1039 $subjectNameValue = implode(',', $parts);
1040 } else {
1041 $subjectNameValue = $certData['issuer'];
1042 }
1043 $x509SubjectNode = $baseDoc->createElementNS(self::XMLDSIGNS, $dsig_pfx.'X509SubjectName', $subjectNameValue);
1044 $x509DataNode->appendChild($x509SubjectNode);
1045 }
1046 if ($issuerSerial && ! empty($certData['issuer']) && ! empty($certData['serialNumber'])) {
1047 if (is_array($certData['issuer'])) {
1048 $parts = array();
1049 foreach ($certData['issuer'] AS $key => $value) {
1050 array_unshift($parts, "$key=$value");
1051 }
1052 $issuerName = implode(',', $parts);
1053 } else {
1054 $issuerName = $certData['issuer'];
1055 }
1056
1057 $x509IssuerNode = $baseDoc->createElementNS(self::XMLDSIGNS, $dsig_pfx.'X509IssuerSerial');
1058 $x509DataNode->appendChild($x509IssuerNode);
1059
1060 $x509Node = $baseDoc->createElementNS(self::XMLDSIGNS, $dsig_pfx.'X509IssuerName', $issuerName);
1061 $x509IssuerNode->appendChild($x509Node);
1062 $x509Node = $baseDoc->createElementNS(self::XMLDSIGNS, $dsig_pfx.'X509SerialNumber', $certData['serialNumber']);
1063 $x509IssuerNode->appendChild($x509Node);
1064 }
1065 }
1066
1067 }
1068 $x509CertNode = $baseDoc->createElementNS(self::XMLDSIGNS, $dsig_pfx.'X509Certificate', $X509Cert);
1069 $x509DataNode->appendChild($x509CertNode);
1070 }
1071 }
1072
1079 public function add509Cert($cert, $isPEMFormat=true, $isURL=false, $options=null)
1080 {
1081 if ($xpath = $this->getXPathObj()) {
1082 self::staticAdd509Cert($this->sigNode, $cert, $isPEMFormat, $isURL, $xpath, $options);
1083 }
1084 }
1085
1095 public function appendToKeyInfo($node)
1096 {
1097 $parentRef = $this->sigNode;
1098 $baseDoc = $parentRef->ownerDocument;
1099
1100 $xpath = $this->getXPathObj();
1101 if (empty($xpath)) {
1102 $xpath = new DOMXPath($parentRef->ownerDocument);
1103 $xpath->registerNamespace('secdsig', self::XMLDSIGNS);
1104 }
1105
1106 $query = "./secdsig:KeyInfo";
1107 $nodeset = $xpath->query($query, $parentRef);
1108 $keyInfo = $nodeset->item(0);
1109 if (! $keyInfo) {
1110 $dsig_pfx = '';
1111 $pfx = $parentRef->lookupPrefix(self::XMLDSIGNS);
1112 if (! empty($pfx)) {
1113 $dsig_pfx = $pfx.":";
1114 }
1115 $inserted = false;
1116 $keyInfo = $baseDoc->createElementNS(self::XMLDSIGNS, $dsig_pfx.'KeyInfo');
1117
1118 $query = "./secdsig:Object";
1119 $nodeset = $xpath->query($query, $parentRef);
1120 if ($sObject = $nodeset->item(0)) {
1121 $sObject->parentNode->insertBefore($keyInfo, $sObject);
1122 $inserted = true;
1123 }
1124
1125 if (! $inserted) {
1126 $parentRef->appendChild($keyInfo);
1127 }
1128 }
1129
1130 $keyInfo->appendChild($node);
1131
1132 return $keyInfo;
1133 }
1134
1146 public function getValidatedNodes()
1147 {
1148 return $this->validatedNodes;
1149 }
1150}
if(!isset( $_REQUEST[ 'ReturnTo'])) if(!isset($_REQUEST['AuthId'])) $options
Definition: as_login.php:20
An exception for terminatinating execution or to throw for unit testing.
static generateGUID($prefix='pfx')
Generate guid.
sign($objKey, $appendToNode=null)
addRefInternal($sinfoNode, $node, $algorithm, $arTransforms=null, $options=null)
addObject($data, $mimetype=null, $encoding=null)
getXPathObj()
Returns the XPathObj or null if xPathCtx is set and sigNode is empty.
canonicalizeData($node, $canonicalmethod, $arXPath=null, $prefixList=null)
appendToKeyInfo($node)
This function appends a node to the KeyInfo.
insertSignature($node, $beforeNode=null)
This function inserts the signature element.
static get509XCert($cert, $isPEMFormat=true)
addReferenceList($arNodes, $algorithm, $arTransforms=null, $options=null)
processTransforms($refNode, $objData, $includeCommentNodes=true)
resetXPathObj()
Reset the XPathObj to null.
static staticGet509XCerts($certs, $isPEMFormat=true)
appendSignature($parentNode, $insertBefore=false)
addReference($node, $algorithm, $arTransforms=null, $options=null)
static staticAdd509Cert($parentRef, $cert, $isPEMFormat=true, $isURL=false, $xpath=null, $options=null)
static generate_GUID($prefix='pfx')
Generate guid.
getValidatedNodes()
This function retrieves an associative array of the validated nodes.
verify($objKey)
Returns: Bool when verifying HMAC_SHA1; Int otherwise, with following meanings: 1 on succesful signat...
calculateDigest($digestAlgorithm, $data, $encode=true)
add509Cert($cert, $isPEMFormat=true, $isURL=false, $options=null)
$template
$key
Definition: croninfo.php:18
if($err=$client->getError()) $namespace
if($format !==null) $name
Definition: metadata.php:146
hash(StreamInterface $stream, $algo, $rawOutput=false)
Calculate a hash of a Stream.
Definition: functions.php:406
$query