WSCOMUN  2.1.0
Web Services Comunes para PHP/GVHidra
XMLSecEnc.php
1 <?php
42 class XMLSecEnc
43 {
44  const template = "<xenc:EncryptedData xmlns:xenc='http://www.w3.org/2001/04/xmlenc#'>
45  <xenc:CipherData>
46  <xenc:CipherValue></xenc:CipherValue>
47  </xenc:CipherData>
48 </xenc:EncryptedData>";
49  const Element = 'http://www.w3.org/2001/04/xmlenc#Element';
50  const Content = 'http://www.w3.org/2001/04/xmlenc#Content';
51  const URI = 3;
52  const XMLENCNS = 'http://www.w3.org/2001/04/xmlenc#';
53 
55  private $encdoc = null;
56 
58  private $rawNode = null;
59 
61  public $type = null;
62 
64  public $encKey = null;
65 
67  private $references = array ();
68 
69  public function __construct()
70  {
71  $this->_resetTemplate ();
72  }
73 
74  private function _resetTemplate()
75  {
76  $this->encdoc = new DOMDocument ();
77  $this->encdoc->loadXML ( self::template );
78  }
79 
87  public function addReference($name, $node, $type)
88  {
89  if (! $node instanceof DOMNode)
90  {
91  throw new Exception ( '$node is not of type DOMNode' );
92  }
93  $curencdoc = $this->encdoc;
94  $this->_resetTemplate ();
95  $encdoc = $this->encdoc;
96  $this->encdoc = $curencdoc;
97  $refuri = XMLSecurityDSig::generateGUID ();
98  $element = $encdoc->documentElement;
99  $element->setAttribute ( "Id", $refuri );
100  $this->references [$name] = array (
101  "node" => $node,
102  "type" => $type,
103  "encnode" => $encdoc,
104  "refuri" => $refuri
105  );
106  }
107 
112  public function setNode($node)
113  {
114  $this->rawNode = $node;
115  }
116 
128  public function encryptNode($objKey, $replace = true)
129  {
130  $data = '';
131  if (empty ( $this->rawNode ))
132  {
133  throw new Exception ( 'Node to encrypt has not been set' );
134  }
135  if (! $objKey instanceof XMLSecurityKey)
136  {
137  throw new Exception ( 'Invalid Key' );
138  }
139  $doc = $this->rawNode->ownerDocument;
140  $xPath = new DOMXPath ( $this->encdoc );
141  $objList = $xPath->query ( '/xenc:EncryptedData/xenc:CipherData/xenc:CipherValue' );
142  $cipherValue = $objList->item ( 0 );
143  if ($cipherValue == null)
144  {
145  throw new Exception ( 'Error locating CipherValue element within template' );
146  }
147  switch ($this->type)
148  {
149  case (self::Element) :
150  $data = $doc->saveXML ( $this->rawNode );
151  $this->encdoc->documentElement->setAttribute ( 'Type', self::Element );
152  break;
153  case (self::Content) :
154  $children = $this->rawNode->childNodes;
155  foreach ( $children as $child )
156  {
157  $data .= $doc->saveXML ( $child );
158  }
159  $this->encdoc->documentElement->setAttribute ( 'Type', self::Content );
160  break;
161  default :
162  throw new Exception ( 'Type is currently not supported' );
163  }
164  $encMethod = $this->encdoc->documentElement->appendChild ( $this->encdoc->createElementNS ( self::XMLENCNS, 'xenc:EncryptionMethod' ) );
165  $encMethod->setAttribute ( 'Algorithm', $objKey->getAlgorithm () );
166  $cipherValue->parentNode->parentNode->insertBefore ( $encMethod, $cipherValue->parentNode->parentNode->firstChild );
167  $strEncrypt = base64_encode ( $objKey->encryptData ( $data ) );
168  $value = $this->encdoc->createTextNode ( $strEncrypt );
169  $cipherValue->appendChild ( $value );
170  if ($replace)
171  {
172  switch ($this->type)
173  {
174  case (self::Element) :
175  if ($this->rawNode->nodeType == XML_DOCUMENT_NODE)
176  {
177  return $this->encdoc;
178  }
179  $importEnc = $this->rawNode->ownerDocument->importNode ( $this->encdoc->documentElement, true );
180  $this->rawNode->parentNode->replaceChild ( $importEnc, $this->rawNode );
181  return $importEnc;
182  case (self::Content) :
183  $importEnc = $this->rawNode->ownerDocument->importNode ( $this->encdoc->documentElement, true );
184  while ( $this->rawNode->firstChild )
185  {
186  $this->rawNode->removeChild ( $this->rawNode->firstChild );
187  }
188  $this->rawNode->appendChild ( $importEnc );
189  return $importEnc;
190  }
191  }
192  else
193  {
194  return $this->encdoc->documentElement;
195  }
196  }
197 
203  public function encryptReferences($objKey)
204  {
205  $curRawNode = $this->rawNode;
206  $curType = $this->type;
207  foreach ( $this->references as $name => $reference )
208  {
209  $this->encdoc = $reference ["encnode"];
210  $this->rawNode = $reference ["node"];
211  $this->type = $reference ["type"];
212  try
213  {
214  $encNode = $this->encryptNode ( $objKey );
215  $this->references [$name] ["encnode"] = $encNode;
216  }
217  catch ( Exception $e )
218  {
219  $this->rawNode = $curRawNode;
220  $this->type = $curType;
221  throw $e;
222  }
223  }
224  $this->rawNode = $curRawNode;
225  $this->type = $curType;
226  }
227 
234  public function getCipherValue()
235  {
236  if (empty ( $this->rawNode ))
237  {
238  throw new Exception ( 'Node to decrypt has not been set' );
239  }
240  $doc = $this->rawNode->ownerDocument;
241  $xPath = new DOMXPath ( $doc );
242  $xPath->registerNamespace ( 'xmlencr', self::XMLENCNS );
243  /* Only handles embedded content right now and not a reference */
244  $query = "./xmlencr:CipherData/xmlencr:CipherValue";
245  $nodeset = $xPath->query ( $query, $this->rawNode );
246  $node = $nodeset->item ( 0 );
247  if (! $node)
248  {
249  return null;
250  }
251  return base64_decode ( $node->nodeValue );
252  }
253 
269  public function decryptNode($objKey, $replace = true)
270  {
271  if (! $objKey instanceof XMLSecurityKey)
272  {
273  throw new Exception ( 'Invalid Key' );
274  }
275  $encryptedData = $this->getCipherValue ();
276  if ($encryptedData)
277  {
278  $decrypted = $objKey->decryptData ( $encryptedData );
279  if ($replace)
280  {
281  switch ($this->type)
282  {
283  case (self::Element) :
284  $newdoc = new DOMDocument ();
285  $newdoc->loadXML ( $decrypted );
286  if ($this->rawNode->nodeType == XML_DOCUMENT_NODE)
287  {
288  return $newdoc;
289  }
290  $importEnc = $this->rawNode->ownerDocument->importNode ( $newdoc->documentElement, true );
291  $this->rawNode->parentNode->replaceChild ( $importEnc, $this->rawNode );
292  return $importEnc;
293  case (self::Content) :
294  if ($this->rawNode->nodeType == XML_DOCUMENT_NODE)
295  {
296  $doc = $this->rawNode;
297  }
298  else
299  {
300  $doc = $this->rawNode->ownerDocument;
301  }
302  $newFrag = $doc->createDocumentFragment ();
303  $newFrag->appendXML ( $decrypted );
304  $parent = $this->rawNode->parentNode;
305  $parent->replaceChild ( $newFrag, $this->rawNode );
306  return $parent;
307  default :
308  return $decrypted;
309  }
310  }
311  else
312  {
313  return $decrypted;
314  }
315  }
316  else
317  {
318  throw new Exception ( "Cannot locate encrypted data" );
319  }
320  }
321 
330  public function encryptKey($srcKey, $rawKey, $append = true)
331  {
332  if ((! $srcKey instanceof XMLSecurityKey) || (! $rawKey instanceof XMLSecurityKey))
333  {
334  throw new Exception ( 'Invalid Key' );
335  }
336  $strEncKey = base64_encode ( $srcKey->encryptData ( $rawKey->key ) );
337  $root = $this->encdoc->documentElement;
338  $encKey = $this->encdoc->createElementNS ( self::XMLENCNS, 'xenc:EncryptedKey' );
339  if ($append)
340  {
341  $keyInfo = $root->insertBefore ( $this->encdoc->createElementNS ( 'http://www.w3.org/2000/09/xmldsig#', 'dsig:KeyInfo' ), $root->firstChild );
342  $keyInfo->appendChild ( $encKey );
343  }
344  else
345  {
346  $this->encKey = $encKey;
347  }
348  $encMethod = $encKey->appendChild ( $this->encdoc->createElementNS ( self::XMLENCNS, 'xenc:EncryptionMethod' ) );
349  $encMethod->setAttribute ( 'Algorithm', $srcKey->getAlgorith () );
350  if (! empty ( $srcKey->name ))
351  {
352  $keyInfo = $encKey->appendChild ( $this->encdoc->createElementNS ( 'http://www.w3.org/2000/09/xmldsig#', 'dsig:KeyInfo' ) );
353  $keyInfo->appendChild ( $this->encdoc->createElementNS ( 'http://www.w3.org/2000/09/xmldsig#', 'dsig:KeyName', $srcKey->name ) );
354  }
355  $cipherData = $encKey->appendChild ( $this->encdoc->createElementNS ( self::XMLENCNS, 'xenc:CipherData' ) );
356  $cipherData->appendChild ( $this->encdoc->createElementNS ( self::XMLENCNS, 'xenc:CipherValue', $strEncKey ) );
357  if (is_array ( $this->references ) && count ( $this->references ) > 0)
358  {
359  $refList = $encKey->appendChild ( $this->encdoc->createElementNS ( self::XMLENCNS, 'xenc:ReferenceList' ) );
360  foreach ( $this->references as $reference )
361  {
362  $refuri = $reference ["refuri"];
363  $dataRef = $refList->appendChild ( $this->encdoc->createElementNS ( self::XMLENCNS, 'xenc:DataReference' ) );
364  $dataRef->setAttribute ( "URI", '#' . $refuri );
365  }
366  }
367  return;
368  }
369 
376  public function decryptKey($encKey)
377  {
378  if (! $encKey->isEncrypted)
379  {
380  throw new Exception ( "Key is not Encrypted" );
381  }
382  if (empty ( $encKey->key ))
383  {
384  throw new Exception ( "Key is missing data to perform the decryption" );
385  }
386  return $this->decryptNode ( $encKey, false );
387  }
388 
394  public function locateEncryptedData($element)
395  {
396  if ($element instanceof DOMDocument)
397  {
398  $doc = $element;
399  }
400  else
401  {
402  $doc = $element->ownerDocument;
403  }
404  if ($doc)
405  {
406  $xpath = new DOMXPath ( $doc );
407  $query = "//*[local-name()='EncryptedData' and namespace-uri()='" . self::XMLENCNS . "']";
408  $nodeset = $xpath->query ( $query );
409  return $nodeset->item ( 0 );
410  }
411  return null;
412  }
413 
420  public function locateKey($node = null)
421  {
422  if (empty ( $node ))
423  {
424  $node = $this->rawNode;
425  }
426  if (! $node instanceof DOMNode)
427  {
428  return null;
429  }
430  if ($doc = $node->ownerDocument)
431  {
432  $xpath = new DOMXPath ( $doc );
433  $xpath->registerNamespace ( 'xmlsecenc', self::XMLENCNS );
434  $query = ".//xmlsecenc:EncryptionMethod";
435  $nodeset = $xpath->query ( $query, $node );
436  if ($encmeth = $nodeset->item ( 0 ))
437  {
438  $attrAlgorithm = $encmeth->getAttribute ( "Algorithm" );
439  try
440  {
441  $objKey = new XMLSecurityKey ( $attrAlgorithm, array (
442  'type' => 'private'
443  ) );
444  }
445  catch ( Exception $e )
446  {
447  return null;
448  }
449  return $objKey;
450  }
451  }
452  return null;
453  }
454 
462  public static function staticLocateKeyInfo($objBaseKey = null, $node = null)
463  {
464  if (empty ( $node ) || (! $node instanceof DOMNode))
465  {
466  return null;
467  }
468  $doc = $node->ownerDocument;
469  if (! $doc)
470  {
471  return null;
472  }
473  $xpath = new DOMXPath ( $doc );
474  $xpath->registerNamespace ( 'xmlsecenc', self::XMLENCNS );
475  $xpath->registerNamespace ( 'xmlsecdsig', XMLSecurityDSig::XMLDSIGNS );
476  $query = "./xmlsecdsig:KeyInfo";
477  $nodeset = $xpath->query ( $query, $node );
478  $encmeth = $nodeset->item ( 0 );
479  if (! $encmeth)
480  {
481  /* No KeyInfo in EncryptedData / EncryptedKey. */
482  return $objBaseKey;
483  }
484  foreach ( $encmeth->childNodes as $child )
485  {
486  switch ($child->localName)
487  {
488  case 'KeyName' :
489  if (! empty ( $objBaseKey ))
490  {
491  $objBaseKey->name = $child->nodeValue;
492  }
493  break;
494  case 'KeyValue' :
495  foreach ( $child->childNodes as $keyval )
496  {
497  switch ($keyval->localName)
498  {
499  case 'DSAKeyValue' :
500  throw new Exception ( "DSAKeyValue currently not supported" );
501  case 'RSAKeyValue' :
502  $modulus = null;
503  $exponent = null;
504  if ($modulusNode = $keyval->getElementsByTagName ( 'Modulus' )->item ( 0 ))
505  {
506  $modulus = base64_decode ( $modulusNode->nodeValue );
507  }
508  if ($exponentNode = $keyval->getElementsByTagName ( 'Exponent' )->item ( 0 ))
509  {
510  $exponent = base64_decode ( $exponentNode->nodeValue );
511  }
512  if (empty ( $modulus ) || empty ( $exponent ))
513  {
514  throw new Exception ( "Missing Modulus or Exponent" );
515  }
516  $publicKey = XMLSecurityKey::convertRSA ( $modulus, $exponent );
517  $objBaseKey->loadKey ( $publicKey );
518  break;
519  }
520  }
521  break;
522  case 'RetrievalMethod' :
523  $type = $child->getAttribute ( 'Type' );
524  if ($type !== 'http://www.w3.org/2001/04/xmlenc#EncryptedKey')
525  {
526  /* Unsupported key type. */
527  break;
528  }
529  $uri = $child->getAttribute ( 'URI' );
530  if ($uri [0] !== '#')
531  {
532  /* URI not a reference - unsupported. */
533  break;
534  }
535  $id = substr ( $uri, 1 );
536  $query = "//xmlsecenc:EncryptedKey[@Id='$id']";
537  $keyElement = $xpath->query ( $query )->item ( 0 );
538  if (! $keyElement)
539  {
540  throw new Exception ( "Unable to locate EncryptedKey with @Id='$id'." );
541  }
542  return XMLSecurityKey::fromEncryptedKeyElement ( $keyElement );
543  case 'EncryptedKey' :
544  return XMLSecurityKey::fromEncryptedKeyElement ( $child );
545  case 'X509Data' :
546  if ($x509certNodes = $child->getElementsByTagName ( 'X509Certificate' ))
547  {
548  if ($x509certNodes->length > 0)
549  {
550  $x509cert = $x509certNodes->item ( 0 )->textContent;
551  $x509cert = str_replace ( array (
552  "\r",
553  "\n",
554  " "
555  ), "", $x509cert );
556  $x509cert = "-----BEGIN CERTIFICATE-----\n" . chunk_split ( $x509cert, 64, "\n" ) . "-----END CERTIFICATE-----\n";
557  $objBaseKey->loadKey ( $x509cert, false, true );
558  }
559  }
560  break;
561  }
562  }
563  return $objBaseKey;
564  }
565 
572  public function locateKeyInfo($objBaseKey = null, $node = null)
573  {
574  if (empty ( $node ))
575  {
576  $node = $this->rawNode;
577  }
578  return self::staticLocateKeyInfo ( $objBaseKey, $node );
579  }
580 
581 }
static convertRSA($modulus, $exponent)
locateKeyInfo($objBaseKey=null, $node=null)
Definition: XMLSecEnc.php:572
encryptKey($srcKey, $rawKey, $append=true)
Definition: XMLSecEnc.php:330
encryptNode($objKey, $replace=true)
Definition: XMLSecEnc.php:128
setNode($node)
Definition: XMLSecEnc.php:112
decryptKey($encKey)
Definition: XMLSecEnc.php:376
encryptReferences($objKey)
Definition: XMLSecEnc.php:203
static staticLocateKeyInfo($objBaseKey=null, $node=null)
Definition: XMLSecEnc.php:462
decryptNode($objKey, $replace=true)
Definition: XMLSecEnc.php:269
getCipherValue()
Definition: XMLSecEnc.php:234
static fromEncryptedKeyElement(DOMElement $element)
addReference($name, $node, $type)
Definition: XMLSecEnc.php:87
static generateGUID($prefix='pfx')
locateKey($node=null)
Definition: XMLSecEnc.php:420
locateEncryptedData($element)
Definition: XMLSecEnc.php:394