44 require_once
'XMLSecurityKey.php';
45 require_once
'XMLSecurityDSig.php';
46 require_once
'XMLSecEnc.php';
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';
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;
66 private function locateSecurityHeader($bMustUnderstand =
true, $setActor = null)
68 if ($this->secNode == null)
70 $headers = $this->SOAPXPath->query (
'//wssoap:Envelope/wssoap:Header' );
71 $header = $headers->item ( 0 );
74 $header = $this->soapDoc->createElementNS ( $this->soapNS, $this->soapPFX .
':Header' );
75 $this->envelope->insertBefore ( $header, $this->envelope->firstChild );
77 $secnodes = $this->SOAPXPath->query (
'./wswsse:Security', $header );
79 foreach ( $secnodes as $node )
81 $actor = $node->getAttributeNS ( $this->soapNS,
'actor' );
82 if ($actor == $setActor)
90 $secnode = $this->soapDoc->createElementNS ( self::WSSENS, self::WSSEPFX .
':Security' );
91 $header->appendChild ( $secnode );
94 $secnode->setAttributeNS ( $this->soapNS, $this->soapPFX .
':mustUnderstand',
'1' );
96 if (! empty ( $setActor ))
99 if ($this->soapNS ==
'http://www.w3.org/2003/05/soap-envelope')
103 $secnode->setAttributeNS ( $this->soapNS, $this->soapPFX .
':' . $ename, $setActor );
106 $this->secNode = $secnode;
108 return $this->secNode;
112 public function __construct($doc, $bMustUnderstand =
true, $setActor = null)
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 );
125 public function addTimestamp($secondsToExpire = 3600)
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 ))
136 $expire = $this->soapDoc->createElementNS ( self::WSUNS, self::WSUPFX .
':Expires', gmdate (
"Y-m-d\TH:i:s", $currentTime + $secondsToExpire ) .
'Z' );
137 $timestamp->appendChild ( $expire );
142 public function addUserToken($userName, $password = null, $passwordDigest =
false)
144 if ($passwordDigest && empty ( $password ))
146 throw new Exception (
"Cannot calculate the digest without a password" );
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 );
159 $nonce = $objKey->generateSessionKey ();
161 $createdate = gmdate (
"Y-m-d\TH:i:s" ) .
'Z';
165 $passType = self::WSUNAME .
'#PasswordText';
168 $password = base64_encode ( sha1 ( $nonce . $createdate . $password,
true ) );
169 $passType = self::WSUNAME .
'#PasswordDigest';
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 );
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 );
184 public function addBinaryToken($cert, $isPEMFormat =
true, $isDSig =
true)
186 $security = $this->locateSecurityHeader ();
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' );
192 $token->setAttribute (
'ValueType',
'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3' );
198 public function attachTokentoSig($token)
200 if (! ($token instanceof DOMElement))
202 throw new Exception (
'Invalid parameter: BinarySecurityToken element expected' );
205 if ($objDSig = $objXMLSecDSig->locateSignature ( $this->soapDoc ))
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 );
214 $keyInfo = $objXMLSecDSig->createNewSignNode (
'KeyInfo' );
215 $objDSig->appendChild ( $keyInfo );
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 );
227 throw new Exception (
'Unable to locate digital signature' );
232 public function signSoapDoc($objKey, $options = null)
235 $objDSig->setCanonicalMethod ( XMLSecurityDSig::EXC_C14N );
237 foreach ( $this->secNode->childNodes as $node )
239 if ($node->nodeType == XML_ELEMENT_NODE)
244 if ($this->signAllHeaders)
246 foreach ( $this->secNode->parentNode->childNodes as $node )
248 if (($node->nodeType == XML_ELEMENT_NODE) && ($node->namespaceURI != self::WSSENS))
256 foreach ( $this->envelope->childNodes as $node )
258 if ($node->namespaceURI == $this->soapNS && $node->localName ==
'Body')
266 $algorithm = XMLSecurityDSig::SHA1;
267 if (is_array ( $options ) && isset ( $options [
"algorithm"] ))
269 $algorithm = $options [
"algorithm"];
273 'prefix' => self::WSUPFX,
274 'prefix_ns' => self::WSUNS
276 $objDSig->addReferenceList ( $arNodes, $algorithm, null, $arOptions );
277 $objDSig->sign ( $objKey );
279 if (is_array ( $options ) && isset ( $options [
"insertBefore"] ))
281 $insertTop = ( bool ) $options [
"insertBefore"];
283 $objDSig->appendSignature ( $this->secNode, $insertTop );
285 if (is_array ( $options ))
287 if (! empty ( $options [
"KeyInfo"] ))
289 if (! empty ( $options [
"KeyInfo"] [
"X509SubjectKeyIdentifier"] ))
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 );
305 foreach ( $arkeyid as $hexchar )
307 $data .= chr ( hexdec ( $hexchar ) );
309 $dataNode =
new DOMText ( base64_encode ( $data ) );
310 $reference->appendChild ( $dataNode );
317 public function addEncryptedKey($node, $key, $token, $options = null)
323 $encKey = $key->encKey;
324 $security = $this->locateSecurityHeader ();
325 $doc = $security->ownerDocument;
326 if (! $doc->isSameNode ( $encKey->ownerDocument ))
328 $key->encKey = $security->ownerDocument->importNode ( $encKey,
true );
329 $encKey = $key->encKey;
331 if (! empty ( $key->guid ))
337 $findTokens = $security->firstChild;
338 while ( $findTokens )
340 if ($findTokens->localName ==
'BinarySecurityToken')
342 $lastToken = $findTokens;
344 $findTokens = $findTokens->nextSibling;
348 $lastToken = $lastToken->nextSibling;
350 $security->insertBefore ( $encKey, $lastToken );
352 $encKey->setAttribute (
'Id', $key->guid );
353 $encMethod = $encKey->firstChild;
354 while ( $encMethod && $encMethod->localName !=
'EncryptionMethod' )
356 $encMethod = $encMethod->nextChild;
360 $encMethod = $encMethod->nextSibling;
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 );
368 if (is_array ( $options ))
370 if (! empty ( $options [
"KeyInfo"] ))
372 if (! empty ( $options [
"KeyInfo"] [
"X509SubjectKeyIdentifier"] ))
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 );
382 foreach ( $arkeyid as $hexchar )
384 $data .= chr ( hexdec ( $hexchar ) );
386 $dataNode =
new DOMText ( base64_encode ( $data ) );
387 $reference->appendChild ( $dataNode );
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 );
401 public function AddReference($baseNode, $guid)
404 $child = $baseNode->firstChild;
407 if (($child->namespaceURI == XMLSecEnc::XMLENCNS) && ($child->localName ==
'ReferenceList'))
412 $child = $child->nextSibling;
414 $doc = $baseNode->ownerDocument;
415 if (is_null ( $refList ))
417 $refList = $doc->createElementNS ( XMLSecEnc::XMLENCNS,
'xenc:ReferenceList' );
418 $baseNode->appendChild ( $refList );
420 $dataref = $doc->createElementNS ( XMLSecEnc::XMLENCNS,
'xenc:DataReference' );
421 $refList->appendChild ( $dataref );
422 $dataref->setAttribute (
'URI',
'#' . $guid );
426 public function EncryptBody($siteKey, $objKey, $token)
429 foreach ( $this->envelope->childNodes as $node )
431 if ($node->namespaceURI == $this->soapNS && $node->localName ==
'Body')
436 $enc->setNode ( $node );
438 $enc->encryptKey ( $siteKey, $objKey,
false );
439 $enc->type = XMLSecEnc::Content;
441 $encNode = $enc->encryptNode ( $objKey );
443 $encNode->setAttribute (
'Id', $guid );
444 $refNode = $encNode->firstChild;
445 while ( $refNode && $refNode->nodeType != XML_ELEMENT_NODE )
447 $refNode = $refNode->nextSibling;
451 $refNode = $refNode->nextSibling;
453 if ($this->addEncryptedKey ( $encNode, $enc, $token ))
455 $this->AddReference ( $enc->encKey, $guid );
460 public function encryptSoapDoc($siteKey, $objKey, $options = null, $encryptSignature =
true)
463 $xpath =
new DOMXPath ( $this->envelope->ownerDocument );
464 if ($encryptSignature ==
false)
466 $nodes = $xpath->query (
'//*[local-name()="Body"]' );
469 $nodes = $xpath->query (
'//*[local-name()="Signature"] | //*[local-name()="Body"]' );
472 foreach ( $nodes as $node )
474 $type = XMLSecEnc::Element;
475 $name = $node->localName;
478 $type = XMLSecEnc::Content;
480 $enc->addReference ( $name, $node, $type );
482 $enc->encryptReferences ( $objKey );
484 $enc->encryptKey ( $siteKey, $objKey,
false );
486 $nodes = $xpath->query (
'//*[local-name()="Security"]' );
487 $signode = $nodes->item ( 0 );
488 $this->addEncryptedKey ( $signode, $enc, $siteKey, $options );
492 public function decryptSoapDoc($doc, $options)
495 $privKey_isFile =
false;
496 $privKey_isCert =
false;
498 if (is_array ( $options ))
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);
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 ))
515 $objenc->setNode ( $node );
516 if (! $objKey = $objenc->locateKey ())
518 throw new Exception (
"Unable to locate algorithm for this Encrypted Key" );
520 $objKey->isEncrypted =
true;
521 $objKey->encryptedCtx = $objenc;
523 if ($objKey && $objKey->isEncrypted)
525 $objencKey = $objKey->encryptedCtx;
526 $objKey->loadKey ( $privKey, $privKey_isFile, $privKey_isCert );
527 $key = $objencKey->decryptKey ( $objKey );
528 $objKey->loadKey ( $key );
530 $refnodes = $xpath->query (
'./soapenc:ReferenceList/soapenc:DataReference/@URI', $node );
531 foreach ( $refnodes as $reference )
533 $references [] = $reference->nodeValue;
536 foreach ( $references as $reference )
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 ))
546 $objKey->loadKey ( $key );
548 $objenc->setNode ( $encData );
549 $objenc->type = $encData->getAttribute (
"Type" );
550 $objenc->decryptNode ( $objKey,
true );
557 public function saveXML()
559 return $this->soapDoc->saveXML ();
563 public function save($file)
565 return $this->soapDoc->save ( $file );
static generate_GUID($prefix='pfx')
static staticLocateKeyInfo($objBaseKey=null, $node=null)
static get509XCert($cert, $isPEMFormat=true)