WSCOMUN  2.1.0
Web Services Comunes para PHP/GVHidra
WSSESoap.php
1 <?php
44 require_once 'XMLSecurityKey.php';
45 require_once 'XMLSecurityDSig.php';
46 require_once 'XMLSecEnc.php';
47 
48 
49 class WSSESoap
50 {
51  const WSSENS = 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd';
52  const WSUNS = 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd';
53  const WSUNAME = 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0';
54  const WSSEPFX = 'wsse';
55  const WSUPFX = 'wsu';
56  private $soapNS, $soapPFX;
57  private $soapDoc = null;
58  private $envelope = null;
59  private $SOAPXPath = null;
60  private $secNode = null;
61  public $signAllHeaders = false;
62  public $signBody = true;
63 
64 
65 
66  private function locateSecurityHeader($bMustUnderstand = true, $setActor = null)
67  {
68  if ($this->secNode == null)
69  {
70  $headers = $this->SOAPXPath->query ( '//wssoap:Envelope/wssoap:Header' );
71  $header = $headers->item ( 0 );
72  if (! $header)
73  {
74  $header = $this->soapDoc->createElementNS ( $this->soapNS, $this->soapPFX . ':Header' );
75  $this->envelope->insertBefore ( $header, $this->envelope->firstChild );
76  }
77  $secnodes = $this->SOAPXPath->query ( './wswsse:Security', $header );
78  $secnode = null;
79  foreach ( $secnodes as $node )
80  {
81  $actor = $node->getAttributeNS ( $this->soapNS, 'actor' );
82  if ($actor == $setActor)
83  {
84  $secnode = $node;
85  break;
86  }
87  }
88  if (! $secnode)
89  {
90  $secnode = $this->soapDoc->createElementNS ( self::WSSENS, self::WSSEPFX . ':Security' );
91  $header->appendChild ( $secnode );
92  if ($bMustUnderstand)
93  {
94  $secnode->setAttributeNS ( $this->soapNS, $this->soapPFX . ':mustUnderstand', '1' );
95  }
96  if (! empty ( $setActor ))
97  {
98  $ename = 'actor';
99  if ($this->soapNS == 'http://www.w3.org/2003/05/soap-envelope')
100  {
101  $ename = 'role';
102  }
103  $secnode->setAttributeNS ( $this->soapNS, $this->soapPFX . ':' . $ename, $setActor );
104  }
105  }
106  $this->secNode = $secnode;
107  }
108  return $this->secNode;
109  }
110 
111 
112  public function __construct($doc, $bMustUnderstand = true, $setActor = null)
113  {
114  $this->soapDoc = $doc;
115  $this->envelope = $doc->documentElement;
116  $this->soapNS = $this->envelope->namespaceURI;
117  $this->soapPFX = $this->envelope->prefix;
118  $this->SOAPXPath = new DOMXPath ( $doc );
119  $this->SOAPXPath->registerNamespace ( 'wssoap', $this->soapNS );
120  $this->SOAPXPath->registerNamespace ( 'wswsse', self::WSSENS );
121  $this->locateSecurityHeader ( $bMustUnderstand, $setActor );
122  }
123 
124 
125  public function addTimestamp($secondsToExpire = 3600)
126  {
127  /* Add the WSU timestamps */
128  $security = $this->locateSecurityHeader ();
129  $timestamp = $this->soapDoc->createElementNS ( self::WSUNS, self::WSUPFX . ':Timestamp' );
130  $security->insertBefore ( $timestamp, $security->firstChild );
131  $currentTime = time ();
132  $created = $this->soapDoc->createElementNS ( self::WSUNS, self::WSUPFX . ':Created', gmdate ( "Y-m-d\TH:i:s", $currentTime ) . 'Z' );
133  $timestamp->appendChild ( $created );
134  if (! is_null ( $secondsToExpire ))
135  {
136  $expire = $this->soapDoc->createElementNS ( self::WSUNS, self::WSUPFX . ':Expires', gmdate ( "Y-m-d\TH:i:s", $currentTime + $secondsToExpire ) . 'Z' );
137  $timestamp->appendChild ( $expire );
138  }
139  }
140 
141 
142  public function addUserToken($userName, $password = null, $passwordDigest = false)
143  {
144  if ($passwordDigest && empty ( $password ))
145  {
146  throw new Exception ( "Cannot calculate the digest without a password" );
147  }
148 
149  $security = $this->locateSecurityHeader ();
150  $token = $this->soapDoc->createElementNS ( self::WSSENS, self::WSSEPFX . ':UsernameToken' );
151  $security->insertBefore ( $token, $security->firstChild );
152  $username = $this->soapDoc->createElementNS ( self::WSSENS, self::WSSEPFX . ':Username' );
153  $usernameText = $this->soapDoc->createTextNode ( $userName );
154  $username->appendChild ( $usernameText );
155  $token->appendChild ( $username );
156 
157  /* Generate nonce - create a 256 bit session key to be used */
158  $objKey = new XMLSecurityKey ( XMLSecurityKey::AES256_CBC );
159  $nonce = $objKey->generateSessionKey ();
160  unset ( $objKey );
161  $createdate = gmdate ( "Y-m-d\TH:i:s" ) . 'Z';
162 
163  if ($password)
164  {
165  $passType = self::WSUNAME . '#PasswordText';
166  if ($passwordDigest)
167  {
168  $password = base64_encode ( sha1 ( $nonce . $createdate . $password, true ) );
169  $passType = self::WSUNAME . '#PasswordDigest';
170  }
171  $passwordNode = $this->soapDoc->createElementNS ( self::WSSENS, self::WSSEPFX . ':Password' );
172  $token->appendChild ( $passwordNode );
173  $passwordText = $this->soapDoc->createTextNode ( $password );
174  $passwordNode->appendChild ( $passwordText );
175  $passwordNode->setAttribute ( 'Type', $passType );
176  }
177  $nonceNode = $this->soapDoc->createElementNS ( self::WSSENS, self::WSSEPFX . ':Nonce', base64_encode ( $nonce ) );
178  $token->appendChild ( $nonceNode );
179  $created = $this->soapDoc->createElementNS ( self::WSUNS, self::WSUPFX . ':Created', $createdate );
180  $token->appendChild ( $created );
181  }
182 
183 
184  public function addBinaryToken($cert, $isPEMFormat = true, $isDSig = true)
185  {
186  $security = $this->locateSecurityHeader ();
187  $data = XMLSecurityDSig::get509XCert ( $cert, $isPEMFormat );
188  $token = $this->soapDoc->createElementNS ( self::WSSENS, self::WSSEPFX . ':BinarySecurityToken', $data );
189  $security->insertBefore ( $token, $security->firstChild );
190  $token->setAttribute ( 'EncodingType', 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary' );
191  $token->setAttributeNS ( self::WSUNS, self::WSUPFX . ':Id', XMLSecurityDSig::generate_GUID () );
192  $token->setAttribute ( 'ValueType', 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3' );
193 
194  return $token;
195  }
196 
197 
198  public function attachTokentoSig($token)
199  {
200  if (! ($token instanceof DOMElement))
201  {
202  throw new Exception ( 'Invalid parameter: BinarySecurityToken element expected' );
203  }
204  $objXMLSecDSig = new XMLSecurityDSig ();
205  if ($objDSig = $objXMLSecDSig->locateSignature ( $this->soapDoc ))
206  {
207  $tokenURI = '#' . $token->getAttributeNS ( self::WSUNS, "Id" );
208  $this->SOAPXPath->registerNamespace ( 'secdsig', XMLSecurityDSig::XMLDSIGNS );
209  $query = "./secdsig:KeyInfo";
210  $nodeset = $this->SOAPXPath->query ( $query, $objDSig );
211  $keyInfo = $nodeset->item ( 0 );
212  if (! $keyInfo)
213  {
214  $keyInfo = $objXMLSecDSig->createNewSignNode ( 'KeyInfo' );
215  $objDSig->appendChild ( $keyInfo );
216  }
217 
218  $tokenRef = $this->soapDoc->createElementNS ( self::WSSENS, self::WSSEPFX . ':SecurityTokenReference' );
219  $keyInfo->appendChild ( $tokenRef );
220  $reference = $this->soapDoc->createElementNS ( self::WSSENS, self::WSSEPFX . ':Reference' );
221  $reference->setAttribute ( 'ValueType', 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3' );
222  $reference->setAttribute ( "URI", $tokenURI );
223  $tokenRef->appendChild ( $reference );
224  }
225  else
226  {
227  throw new Exception ( 'Unable to locate digital signature' );
228  }
229  }
230 
231 
232  public function signSoapDoc($objKey, $options = null)
233  {
234  $objDSig = new XMLSecurityDSig ();
235  $objDSig->setCanonicalMethod ( XMLSecurityDSig::EXC_C14N );
236  $arNodes = array ();
237  foreach ( $this->secNode->childNodes as $node )
238  {
239  if ($node->nodeType == XML_ELEMENT_NODE)
240  {
241  $arNodes [] = $node;
242  }
243  }
244  if ($this->signAllHeaders)
245  {
246  foreach ( $this->secNode->parentNode->childNodes as $node )
247  {
248  if (($node->nodeType == XML_ELEMENT_NODE) && ($node->namespaceURI != self::WSSENS))
249  {
250  $arNodes [] = $node;
251  }
252  }
253  }
254  if ($this->signBody)
255  {
256  foreach ( $this->envelope->childNodes as $node )
257  {
258  if ($node->namespaceURI == $this->soapNS && $node->localName == 'Body')
259  {
260  $arNodes [] = $node;
261  break;
262  }
263  }
264  }
265 
266  $algorithm = XMLSecurityDSig::SHA1;
267  if (is_array ( $options ) && isset ( $options ["algorithm"] ))
268  {
269  $algorithm = $options ["algorithm"];
270  }
271 
272  $arOptions = array (
273  'prefix' => self::WSUPFX,
274  'prefix_ns' => self::WSUNS
275  );
276  $objDSig->addReferenceList ( $arNodes, $algorithm, null, $arOptions );
277  $objDSig->sign ( $objKey );
278  $insertTop = true;
279  if (is_array ( $options ) && isset ( $options ["insertBefore"] ))
280  {
281  $insertTop = ( bool ) $options ["insertBefore"];
282  }
283  $objDSig->appendSignature ( $this->secNode, $insertTop );
284  /* New suff */
285  if (is_array ( $options ))
286  {
287  if (! empty ( $options ["KeyInfo"] ))
288  {
289  if (! empty ( $options ["KeyInfo"] ["X509SubjectKeyIdentifier"] ))
290  {
291  $sigNode = $this->secNode->firstChild->nextSibling;
292  $objDoc = $sigNode->ownerDocument;
293  $keyInfo = $sigNode->ownerDocument->createElementNS ( XMLSecurityDSig::XMLDSIGNS, 'ds:KeyInfo' );
294  $sigNode->appendChild ( $keyInfo );
295  $tokenRef = $objDoc->createElementNS ( self::WSSENS, self::WSSEPFX . ':SecurityTokenReference' );
296  $keyInfo->appendChild ( $tokenRef );
297  $reference = $objDoc->createElementNS ( self::WSSENS, self::WSSEPFX . ':KeyIdentifier' );
298  $reference->setAttribute ( "ValueType", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509SubjectKeyIdentifier" );
299  $reference->setAttribute ( "EncodingType", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary" );
300  $tokenRef->appendChild ( $reference );
301  $x509 = openssl_x509_parse ( $objKey->getX509Certificate () );
302  $keyid = $x509 ["extensions"] ["subjectKeyIdentifier"];
303  $arkeyid = split ( ":", $keyid );
304  $data = "";
305  foreach ( $arkeyid as $hexchar )
306  {
307  $data .= chr ( hexdec ( $hexchar ) );
308  }
309  $dataNode = new DOMText ( base64_encode ( $data ) );
310  $reference->appendChild ( $dataNode );
311  }
312  }
313  }
314  }
315 
316 
317  public function addEncryptedKey($node, $key, $token, $options = null)
318  {
319  if (! $key->encKey)
320  {
321  return false;
322  }
323  $encKey = $key->encKey;
324  $security = $this->locateSecurityHeader ();
325  $doc = $security->ownerDocument;
326  if (! $doc->isSameNode ( $encKey->ownerDocument ))
327  {
328  $key->encKey = $security->ownerDocument->importNode ( $encKey, true );
329  $encKey = $key->encKey;
330  }
331  if (! empty ( $key->guid ))
332  {
333  return true;
334  }
335 
336  $lastToken = null;
337  $findTokens = $security->firstChild;
338  while ( $findTokens )
339  {
340  if ($findTokens->localName == 'BinarySecurityToken')
341  {
342  $lastToken = $findTokens;
343  }
344  $findTokens = $findTokens->nextSibling;
345  }
346  if ($lastToken)
347  {
348  $lastToken = $lastToken->nextSibling;
349  }
350  $security->insertBefore ( $encKey, $lastToken );
351  $key->guid = XMLSecurityDSig::generate_GUID ();
352  $encKey->setAttribute ( 'Id', $key->guid );
353  $encMethod = $encKey->firstChild;
354  while ( $encMethod && $encMethod->localName != 'EncryptionMethod' )
355  {
356  $encMethod = $encMethod->nextChild;
357  }
358  if ($encMethod)
359  {
360  $encMethod = $encMethod->nextSibling;
361  }
362  $objDoc = $encKey->ownerDocument;
363  $keyInfo = $objDoc->createElementNS ( 'http://www.w3.org/2000/09/xmldsig#', 'dsig:KeyInfo' );
364  $encKey->insertBefore ( $keyInfo, $encMethod );
365  $tokenRef = $objDoc->createElementNS ( self::WSSENS, self::WSSEPFX . ':SecurityTokenReference' );
366  $keyInfo->appendChild ( $tokenRef );
367  /* New suff */
368  if (is_array ( $options ))
369  {
370  if (! empty ( $options ["KeyInfo"] ))
371  {
372  if (! empty ( $options ["KeyInfo"] ["X509SubjectKeyIdentifier"] ))
373  {
374  $reference = $objDoc->createElementNS ( self::WSSENS, self::WSSEPFX . ':KeyIdentifier' );
375  $reference->setAttribute ( "ValueType", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509SubjectKeyIdentifier" );
376  $reference->setAttribute ( "EncodingType", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary" );
377  $tokenRef->appendChild ( $reference );
378  $x509 = openssl_x509_parse ( $token->getX509Certificate () );
379  $keyid = $x509 ["extensions"] ["subjectKeyIdentifier"];
380  $arkeyid = split ( ":", $keyid );
381  $data = "";
382  foreach ( $arkeyid as $hexchar )
383  {
384  $data .= chr ( hexdec ( $hexchar ) );
385  }
386  $dataNode = new DOMText ( base64_encode ( $data ) );
387  $reference->appendChild ( $dataNode );
388  return true;
389  }
390  }
391  }
392 
393  $tokenURI = '#' . $token->getAttributeNS ( self::WSUNS, "Id" );
394  $reference = $objDoc->createElementNS ( self::WSSENS, self::WSSEPFX . ':Reference' );
395  $reference->setAttribute ( "URI", $tokenURI );
396  $tokenRef->appendChild ( $reference );
397  return true;
398  }
399 
400 
401  public function AddReference($baseNode, $guid)
402  {
403  $refList = null;
404  $child = $baseNode->firstChild;
405  while ( $child )
406  {
407  if (($child->namespaceURI == XMLSecEnc::XMLENCNS) && ($child->localName == 'ReferenceList'))
408  {
409  $refList = $child;
410  break;
411  }
412  $child = $child->nextSibling;
413  }
414  $doc = $baseNode->ownerDocument;
415  if (is_null ( $refList ))
416  {
417  $refList = $doc->createElementNS ( XMLSecEnc::XMLENCNS, 'xenc:ReferenceList' );
418  $baseNode->appendChild ( $refList );
419  }
420  $dataref = $doc->createElementNS ( XMLSecEnc::XMLENCNS, 'xenc:DataReference' );
421  $refList->appendChild ( $dataref );
422  $dataref->setAttribute ( 'URI', '#' . $guid );
423  }
424 
425 
426  public function EncryptBody($siteKey, $objKey, $token)
427  {
428  $enc = new XMLSecEnc ();
429  foreach ( $this->envelope->childNodes as $node )
430  {
431  if ($node->namespaceURI == $this->soapNS && $node->localName == 'Body')
432  {
433  break;
434  }
435  }
436  $enc->setNode ( $node );
437  /* encrypt the symmetric key */
438  $enc->encryptKey ( $siteKey, $objKey, false );
439  $enc->type = XMLSecEnc::Content;
440  /* Using the symmetric key to actually encrypt the data */
441  $encNode = $enc->encryptNode ( $objKey );
443  $encNode->setAttribute ( 'Id', $guid );
444  $refNode = $encNode->firstChild;
445  while ( $refNode && $refNode->nodeType != XML_ELEMENT_NODE )
446  {
447  $refNode = $refNode->nextSibling;
448  }
449  if ($refNode)
450  {
451  $refNode = $refNode->nextSibling;
452  }
453  if ($this->addEncryptedKey ( $encNode, $enc, $token ))
454  {
455  $this->AddReference ( $enc->encKey, $guid );
456  }
457  }
458 
459 
460  public function encryptSoapDoc($siteKey, $objKey, $options = null, $encryptSignature = true)
461  {
462  $enc = new XMLSecEnc ();
463  $xpath = new DOMXPath ( $this->envelope->ownerDocument );
464  if ($encryptSignature == false)
465  {
466  $nodes = $xpath->query ( '//*[local-name()="Body"]' );
467  } else
468  {
469  $nodes = $xpath->query ( '//*[local-name()="Signature"] | //*[local-name()="Body"]' );
470  }
471 
472  foreach ( $nodes as $node )
473  {
474  $type = XMLSecEnc::Element;
475  $name = $node->localName;
476  if ($name == "Body")
477  {
478  $type = XMLSecEnc::Content;
479  }
480  $enc->addReference ( $name, $node, $type );
481  }
482  $enc->encryptReferences ( $objKey );
483 
484  $enc->encryptKey ( $siteKey, $objKey, false );
485 
486  $nodes = $xpath->query ( '//*[local-name()="Security"]' );
487  $signode = $nodes->item ( 0 );
488  $this->addEncryptedKey ( $signode, $enc, $siteKey, $options );
489  }
490 
491 
492  public function decryptSoapDoc($doc, $options)
493  {
494  $privKey = null;
495  $privKey_isFile = false;
496  $privKey_isCert = false;
497 
498  if (is_array ( $options ))
499  {
500  $privKey = (! empty ( $options ["keys"] ["private"] ["key"] ) ? $options ["keys"] ["private"] ["key"] : null);
501  $privKey_isFile = (! empty ( $options ["keys"] ["private"] ["isFile"] ) ? true : false);
502  $privKey_isCert = (! empty ( $options ["keys"] ["private"] ["isCert"] ) ? true : false);
503  }
504 
505  $objenc = new XMLSecEnc ();
506  $xpath = new DOMXPath ( $doc );
507  $envns = $doc->documentElement->namespaceURI;
508  $xpath->registerNamespace ( "soapns", $envns );
509  $xpath->registerNamespace ( "soapenc", "http://www.w3.org/2001/04/xmlenc#" );
510  $nodes = $xpath->query ( '/soapns:Envelope/soapns:Header/*[local-name()="Security"]/soapenc:EncryptedKey' );
511  $references = array ();
512  if ($node = $nodes->item ( 0 ))
513  {
514  $objenc = new XMLSecEnc ();
515  $objenc->setNode ( $node );
516  if (! $objKey = $objenc->locateKey ())
517  {
518  throw new Exception ( "Unable to locate algorithm for this Encrypted Key" );
519  }
520  $objKey->isEncrypted = true;
521  $objKey->encryptedCtx = $objenc;
522  XMLSecEnc::staticLocateKeyInfo ( $objKey, $node );
523  if ($objKey && $objKey->isEncrypted)
524  {
525  $objencKey = $objKey->encryptedCtx;
526  $objKey->loadKey ( $privKey, $privKey_isFile, $privKey_isCert );
527  $key = $objencKey->decryptKey ( $objKey );
528  $objKey->loadKey ( $key );
529  }
530  $refnodes = $xpath->query ( './soapenc:ReferenceList/soapenc:DataReference/@URI', $node );
531  foreach ( $refnodes as $reference )
532  {
533  $references [] = $reference->nodeValue;
534  }
535  }
536  foreach ( $references as $reference )
537  {
538  $arUrl = parse_url ( $reference );
539  $reference = $arUrl ['fragment'];
540  $query = '//*[@Id="' . $reference . '"]';
541  $nodes = $xpath->query ( $query );
542  $encData = $nodes->item ( 0 );
543  if ($algo = $xpath->evaluate ( "string(./soapenc:EncryptionMethod/@Algorithm)", $encData ))
544  {
545  $objKey = new XMLSecurityKey ( $algo );
546  $objKey->loadKey ( $key );
547  }
548  $objenc->setNode ( $encData );
549  $objenc->type = $encData->getAttribute ( "Type" );
550  $objenc->decryptNode ( $objKey, true );
551  }
552 
553  return true;
554  }
555 
556 
557  public function saveXML()
558  {
559  return $this->soapDoc->saveXML ();
560  }
561 
562 
563  public function save($file)
564  {
565  return $this->soapDoc->save ( $file );
566  }
567 }
static generate_GUID($prefix='pfx')
static staticLocateKeyInfo($objBaseKey=null, $node=null)
Definition: XMLSecEnc.php:462
static get509XCert($cert, $isPEMFormat=true)