diff --git a/examples/username-sign.php b/examples/username-sign.php index d249e37..52674f2 100644 --- a/examples/username-sign.php +++ b/examples/username-sign.php @@ -10,15 +10,39 @@ class MySoap extends SoapClient { - private $_username; - private $_password; - private $_digest; - - public function addUserToken($username, $password, $digest = false) + /** @var string $username */ + private $username; + + /** @var null|string $password */ + private $password; + + /** @var bool $digest */ + private $digest; + + /** @var bool $addNonce */ + private $addNonce; + + /** @var bool $addCreated */ + private $addCreated; + + /** + * addUserToken + * + * @param string $username + * @param null|string $password + * @param bool $digest + * @param bool $addNonce + * @param bool $addCreated + * + * @return void + */ + public function addUserToken($username, $password = null, $digest = false, $addNonce = true, $addCreated = true) { - $this->_username = $username; - $this->_password = $password; - $this->_digest = $digest; + $this->username = $username; + $this->password = $password; + $this->digest = $digest; + $this->addNonce = $addNonce; + $this->addCreated = $addCreated; } public function __doRequest($request, $location, $saction, $version, $one_way = 0) @@ -32,7 +56,7 @@ public function __doRequest($request, $location, $saction, $version, $one_way = $objWSSE->signAllHeaders = true; $objWSSE->addTimestamp(); - $objWSSE->addUserToken($this->_username, $this->_password, $this->_digest); + $objWSSE->addUserToken($this->username, $this->password, $this->digest, $this->addNonce, $this->addCreated); /* create new XMLSec Key using RSA SHA-1 and type is private key */ $objKey = new XMLSecurityKey(XMLSecurityKey::RSA_SHA1, array('type' => 'private')); diff --git a/src/WSASoap.php b/src/WSASoap.php index c6890e7..ca51936 100644 --- a/src/WSASoap.php +++ b/src/WSASoap.php @@ -67,7 +67,7 @@ private function locateHeader() $headers = $this->SOAPXPath->query('//wssoap:Envelope/wssoap:Header'); $header = $headers->item(0); if (!$header) { - $header = $this->soapDoc->createElementNS($this->soapNS, $this->soapPFX.':Header'); + $header = $this->soapDoc->createElementNS($this->soapNS, $this->soapPFX . ':Header'); $this->envelope->insertBefore($header, $this->envelope->firstChild); } $this->header = $header; @@ -87,7 +87,7 @@ public function __construct($doc, $ns = self::WSANS) $this->SOAPXPath->registerNamespace('wssoap', $this->soapNS); $this->SOAPXPath->registerNamespace('wswsa', $this->ns); - $this->envelope->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:'.self::WSAPFX, $this->ns); + $this->envelope->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:' . self::WSAPFX, $this->ns); $this->locateHeader(); } @@ -96,7 +96,7 @@ public function addAction($action) /* Add the WSA Action */ $header = $this->locateHeader(); - $nodeAction = $this->soapDoc->createElementNS($this->ns, self::WSAPFX.':Action', $action); + $nodeAction = $this->soapDoc->createElementNS($this->ns, self::WSAPFX . ':Action', $action); $header->appendChild($nodeAction); } @@ -105,22 +105,22 @@ public function addFrom($location = null) /* Add the WSA From */ $header = $this->locateHeader(); - $nodeFrom = $this->soapDoc->createElementNS($this->ns, self::WSAPFX.':From'); + $nodeFrom = $this->soapDoc->createElementNS($this->ns, self::WSAPFX . ':From'); $header->appendChild($nodeFrom); if (empty($location)) { $location = 'http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous'; } - $nodeAddress = $this->soapDoc->createElementNS($this->ns, self::WSAPFX.':Address', $location); + $nodeAddress = $this->soapDoc->createElementNS($this->ns, self::WSAPFX . ':Address', $location); $nodeFrom->appendChild($nodeAddress); } - + public function addTo($location) { /* Add the WSA To */ $header = $this->locateHeader(); - $nodeTo = $this->soapDoc->createElementNS($this->ns, self::WSAPFX.':To', $location); + $nodeTo = $this->soapDoc->createElementNS($this->ns, self::WSAPFX . ':To', $location); $header->appendChild($nodeTo); } @@ -137,7 +137,7 @@ public function addMessageID($id = null) $header = $this->locateHeader(); - $nodeID = $this->soapDoc->createElementNS($this->ns, self::WSAPFX.':MessageID', $id); + $nodeID = $this->soapDoc->createElementNS($this->ns, self::WSAPFX . ':MessageID', $id); $header->appendChild($nodeID); $this->messageID = $id; } @@ -151,13 +151,13 @@ public function addReplyTo($address = null) /* Add the WSA ReplyTo */ $header = $this->locateHeader(); - $nodeReply = $this->soapDoc->createElementNS($this->ns, self::WSAPFX.':ReplyTo'); + $nodeReply = $this->soapDoc->createElementNS($this->ns, self::WSAPFX . ':ReplyTo'); $header->appendChild($nodeReply); if (empty($address)) { $address = 'http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous'; } - $nodeAddress = $this->soapDoc->createElementNS($this->ns, self::WSAPFX.':Address', $address); + $nodeAddress = $this->soapDoc->createElementNS($this->ns, self::WSAPFX . ':Address', $address); $nodeReply->appendChild($nodeAddress); } diff --git a/src/WSSESoap.php b/src/WSSESoap.php index 91bf288..a48d89b 100644 --- a/src/WSSESoap.php +++ b/src/WSSESoap.php @@ -2,6 +2,7 @@ namespace RobRichards\WsePhp; +use DOMDocument; use DOMElement; use DOMText; use DOMXPath; @@ -58,21 +59,46 @@ class WSSESoap const WSUNAME = 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0'; const WSSEPFX = 'wsse'; const WSUPFX = 'wsu'; - protected $soapNS, $soapPFX; + + /** @var string $soapNS */ + protected $soapNS; + + /** @var string $soapPFX */ + protected $soapPFX; + + /** @var null|DOMDocument $soapDoc */ protected $soapDoc = null; + + /** @var null|DOMElement $envelope */ protected $envelope = null; + + /** @var null|DOMXPath $SOAPXPath */ protected $SOAPXPath = null; + + /** @var null|DOMElement $secNode */ protected $secNode = null; + + /** @var bool $signAllHeaders */ public $signAllHeaders = false; + + /** @var bool $signBody */ public $signBody = true; + /** + * locateSecurityHeader + * + * @param bool $bMustUnderstand + * @param null|string $setActor + * + * @return DOMElement + */ private function locateSecurityHeader($bMustUnderstand = true, $setActor = null) { if ($this->secNode == null) { $headers = $this->SOAPXPath->query('//wssoap:Envelope/wssoap:Header'); $header = $headers->item(0); if (!$header) { - $header = $this->soapDoc->createElementNS($this->soapNS, $this->soapPFX.':Header'); + $header = $this->soapDoc->createElementNS($this->soapNS, $this->soapPFX . ':Header'); $this->envelope->insertBefore($header, $this->envelope->firstChild); } $secnodes = $this->SOAPXPath->query('./wswsse:Security', $header); @@ -85,17 +111,17 @@ private function locateSecurityHeader($bMustUnderstand = true, $setActor = null) } } if (!$secnode) { - $secnode = $this->soapDoc->createElementNS(self::WSSENS, self::WSSEPFX.':Security'); + $secnode = $this->soapDoc->createElementNS(self::WSSENS, self::WSSEPFX . ':Security'); $header->appendChild($secnode); if ($bMustUnderstand) { - $secnode->setAttributeNS($this->soapNS, $this->soapPFX.':mustUnderstand', '1'); + $secnode->setAttributeNS($this->soapNS, $this->soapPFX . ':mustUnderstand', '1'); } if (!empty($setActor)) { $ename = 'actor'; if ($this->soapNS == 'http://www.w3.org/2003/05/soap-envelope') { $ename = 'role'; } - $secnode->setAttributeNS($this->soapNS, $this->soapPFX.':'.$ename, $setActor); + $secnode->setAttributeNS($this->soapNS, $this->soapPFX . ':' . $ename, $setActor); } } $this->secNode = $secnode; @@ -104,6 +130,15 @@ private function locateSecurityHeader($bMustUnderstand = true, $setActor = null) return $this->secNode; } + /** + * constructor + * + * @param \DOMDocument $doc + * @param bool $bMustUnderstand + * @param null| $setActor + * + * @return void + */ public function __construct($doc, $bMustUnderstand = true, $setActor = null) { $this->soapDoc = $doc; @@ -116,23 +151,41 @@ public function __construct($doc, $bMustUnderstand = true, $setActor = null) $this->locateSecurityHeader($bMustUnderstand, $setActor); } + /** + * add Timestamp to Security header + * + * @param int $secondsToExpire + * + * @return void + */ public function addTimestamp($secondsToExpire = 3600) { /* Add the WSU timestamps */ $security = $this->locateSecurityHeader(); - $timestamp = $this->soapDoc->createElementNS(self::WSUNS, self::WSUPFX.':Timestamp'); + $timestamp = $this->soapDoc->createElementNS(self::WSUNS, self::WSUPFX . ':Timestamp'); $security->insertBefore($timestamp, $security->firstChild); $currentTime = time(); - $created = $this->soapDoc->createElementNS(self::WSUNS, self::WSUPFX.':Created', gmdate("Y-m-d\TH:i:s", $currentTime).'Z'); + $created = $this->soapDoc->createElementNS(self::WSUNS, self::WSUPFX . ':Created', gmdate("Y-m-d\TH:i:s", $currentTime) . 'Z'); $timestamp->appendChild($created); if (!is_null($secondsToExpire)) { - $expire = $this->soapDoc->createElementNS(self::WSUNS, self::WSUPFX.':Expires', gmdate("Y-m-d\TH:i:s", $currentTime + $secondsToExpire).'Z'); + $expire = $this->soapDoc->createElementNS(self::WSUNS, self::WSUPFX . ':Expires', gmdate("Y-m-d\TH:i:s", $currentTime + $secondsToExpire) . 'Z'); $timestamp->appendChild($expire); } } - public function addUserToken($userName, $password = null, $passwordDigest = false) + /** + * add UsernameToken to Security header + * + * @param string $userName + * @param string $password + * @param bool $passwordDigest + * @param bool $addNonce add Nonce item + * @param bool $addCreated add Created item + * + * @return void + */ + public function addUserToken($userName, $password = null, $passwordDigest = false, $addNonce = true, $addCreated = true) { if ($passwordDigest && empty($password)) { throw new Exception('Cannot calculate the digest without a password'); @@ -140,10 +193,10 @@ public function addUserToken($userName, $password = null, $passwordDigest = fals $security = $this->locateSecurityHeader(); - $token = $this->soapDoc->createElementNS(self::WSSENS, self::WSSEPFX.':UsernameToken'); + $token = $this->soapDoc->createElementNS(self::WSSENS, self::WSSEPFX . ':UsernameToken'); $security->insertBefore($token, $security->firstChild); - $username = $this->soapDoc->createElementNS(self::WSSENS, self::WSSEPFX.':Username'); + $username = $this->soapDoc->createElementNS(self::WSSENS, self::WSSEPFX . ':Username'); $usernameText = $this->soapDoc->createTextNode($userName); $username->appendChild($usernameText); $token->appendChild($username); @@ -152,44 +205,64 @@ public function addUserToken($userName, $password = null, $passwordDigest = fals $objKey = new XMLSecurityKey(XMLSecurityKey::AES256_CBC); $nonce = $objKey->generateSessionKey(); unset($objKey); - $createdate = gmdate("Y-m-d\TH:i:s").'Z'; + $createdate = gmdate("Y-m-d\TH:i:s") . 'Z'; if ($password) { - $passType = self::WSUNAME.'#PasswordText'; + $passType = self::WSUNAME . '#PasswordText'; if ($passwordDigest) { - $password = base64_encode(sha1($nonce.$createdate.$password, true)); - $passType = self::WSUNAME.'#PasswordDigest'; + $password = base64_encode(sha1($nonce . $createdate . $password, true)); + $passType = self::WSUNAME . '#PasswordDigest'; } - $passwordNode = $this->soapDoc->createElementNS(self::WSSENS, self::WSSEPFX.':Password'); + $passwordNode = $this->soapDoc->createElementNS(self::WSSENS, self::WSSEPFX . ':Password'); $token->appendChild($passwordNode); $passwordText = $this->soapDoc->createTextNode($password); $passwordNode->appendChild($passwordText); $passwordNode->setAttribute('Type', $passType); } - $nonceNode = $this->soapDoc->createElementNS(self::WSSENS, self::WSSEPFX.':Nonce', base64_encode($nonce)); - $nonceNode->setAttribute('EncodingType', "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary"); - $token->appendChild($nonceNode); + if ($addNonce) { + $nonceNode = $this->soapDoc->createElementNS(self::WSSENS, self::WSSEPFX . ':Nonce', base64_encode($nonce)); + $nonceNode->setAttribute('EncodingType', "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary"); + $token->appendChild($nonceNode); + } - $created = $this->soapDoc->createElementNS(self::WSUNS, self::WSUPFX.':Created', $createdate); - $token->appendChild($created); + if ($addCreated) { + $created = $this->soapDoc->createElementNS(self::WSUNS, self::WSUPFX . ':Created', $createdate); + $token->appendChild($created); + } } + /** + * add BinaryToken to Security header + * + * @param mixed $cert + * @param bool $isPEMFormat + * @param bool $isDSig + * + * @return DOMElement|false + */ public function addBinaryToken($cert, $isPEMFormat = true, $isDSig = true) { $security = $this->locateSecurityHeader(); $data = XMLSecurityDSig::get509XCert($cert, $isPEMFormat); - $token = $this->soapDoc->createElementNS(self::WSSENS, self::WSSEPFX.':BinarySecurityToken', $data); + $token = $this->soapDoc->createElementNS(self::WSSENS, self::WSSEPFX . ':BinarySecurityToken', $data); $security->insertBefore($token, $security->firstChild); $token->setAttribute('EncodingType', 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary'); - $token->setAttributeNS(self::WSUNS, self::WSUPFX.':Id', XMLSecurityDSig::generateGUID()); + $token->setAttributeNS(self::WSUNS, self::WSUPFX . ':Id', XMLSecurityDSig::generateGUID()); $token->setAttribute('ValueType', 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3'); return $token; } + /** + * attachTokentoSig + * + * @param DOMElement $token + * + * @return void + */ public function attachTokentoSig($token) { if (!($token instanceof DOMElement)) { @@ -197,7 +270,7 @@ public function attachTokentoSig($token) } $objXMLSecDSig = new XMLSecurityDSig(); if ($objDSig = $objXMLSecDSig->locateSignature($this->soapDoc)) { - $tokenURI = '#'.$token->getAttributeNS(self::WSUNS, 'Id'); + $tokenURI = '#' . $token->getAttributeNS(self::WSUNS, 'Id'); $this->SOAPXPath->registerNamespace('secdsig', XMLSecurityDSig::XMLDSIGNS); $query = './secdsig:KeyInfo'; $nodeset = $this->SOAPXPath->query($query, $objDSig); @@ -207,9 +280,9 @@ public function attachTokentoSig($token) $objDSig->appendChild($keyInfo); } - $tokenRef = $this->soapDoc->createElementNS(self::WSSENS, self::WSSEPFX.':SecurityTokenReference'); + $tokenRef = $this->soapDoc->createElementNS(self::WSSENS, self::WSSEPFX . ':SecurityTokenReference'); $keyInfo->appendChild($tokenRef); - $reference = $this->soapDoc->createElementNS(self::WSSENS, self::WSSEPFX.':Reference'); + $reference = $this->soapDoc->createElementNS(self::WSSENS, self::WSSEPFX . ':Reference'); $reference->setAttribute('ValueType', 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3'); $reference->setAttribute('URI', $tokenURI); $tokenRef->appendChild($reference); @@ -218,6 +291,14 @@ public function attachTokentoSig($token) } } + /** + * signSoapDoc + * + * @param XMLSecurityKey $objKey + * @param null|array $options + * + * @return void + */ public function signSoapDoc($objKey, $options = null) { $objDSig = new XMLSecurityDSig(); @@ -227,14 +308,14 @@ public function signSoapDoc($objKey, $options = null) $arNodes = array(); -// $options['signSpecificHeaders'] = [ -// WSSESoap::WSUNS => [ -// 'Timestamp' => true -// ], -// WSASoap::WSANS_2005 => [ -// 'To' => true -// ] -// ]; + // $options['signSpecificHeaders'] = [ + // WSSESoap::WSUNS => [ + // 'Timestamp' => true + // ], + // WSASoap::WSANS_2005 => [ + // 'To' => true + // ] + // ]; $signSpecificHeaders = (isset($options['signSpecificHeaders']) ? $options['signSpecificHeaders'] : null); foreach ($this->secNode->childNodes as $node) { @@ -301,9 +382,9 @@ public function signSoapDoc($objKey, $options = null) $objDoc = $sigNode->ownerDocument; $keyInfo = $sigNode->ownerDocument->createElementNS(XMLSecurityDSig::XMLDSIGNS, 'ds:KeyInfo'); $sigNode->appendChild($keyInfo); - $tokenRef = $objDoc->createElementNS(self::WSSENS, self::WSSEPFX.':SecurityTokenReference'); + $tokenRef = $objDoc->createElementNS(self::WSSENS, self::WSSEPFX . ':SecurityTokenReference'); $keyInfo->appendChild($tokenRef); - $reference = $objDoc->createElementNS(self::WSSENS, self::WSSEPFX.':KeyIdentifier'); + $reference = $objDoc->createElementNS(self::WSSENS, self::WSSEPFX . ':KeyIdentifier'); $reference->setAttribute('ValueType', 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509SubjectKeyIdentifier'); $reference->setAttribute('EncodingType', 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary'); $tokenRef->appendChild($reference); @@ -362,13 +443,13 @@ public function addEncryptedKey($node, $key, $token, $options = null) $objDoc = $encKey->ownerDocument; $keyInfo = $objDoc->createElementNS('http://www.w3.org/2000/09/xmldsig#', 'dsig:KeyInfo'); $encKey->insertBefore($keyInfo, $encMethod); - $tokenRef = $objDoc->createElementNS(self::WSSENS, self::WSSEPFX.':SecurityTokenReference'); + $tokenRef = $objDoc->createElementNS(self::WSSENS, self::WSSEPFX . ':SecurityTokenReference'); $keyInfo->appendChild($tokenRef); /* New suff */ if (is_array($options)) { if (!empty($options['KeyInfo'])) { if (!empty($options['KeyInfo']['X509SubjectKeyIdentifier'])) { - $reference = $objDoc->createElementNS(self::WSSENS, self::WSSEPFX.':KeyIdentifier'); + $reference = $objDoc->createElementNS(self::WSSENS, self::WSSEPFX . ':KeyIdentifier'); $reference->setAttribute('ValueType', 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509SubjectKeyIdentifier'); $reference->setAttribute('EncodingType', 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary'); $tokenRef->appendChild($reference); @@ -387,14 +468,22 @@ public function addEncryptedKey($node, $key, $token, $options = null) } } - $tokenURI = '#'.$token->getAttributeNS(self::WSUNS, 'Id'); - $reference = $objDoc->createElementNS(self::WSSENS, self::WSSEPFX.':Reference'); + $tokenURI = '#' . $token->getAttributeNS(self::WSUNS, 'Id'); + $reference = $objDoc->createElementNS(self::WSSENS, self::WSSEPFX . ':Reference'); $reference->setAttribute('URI', $tokenURI); $tokenRef->appendChild($reference); return true; } + /** + * AddReference + * + * @param DOMElement $baseNode + * @param string $guid + * + * @return void + */ public function AddReference($baseNode, $guid) { $refList = null; @@ -413,7 +502,7 @@ public function AddReference($baseNode, $guid) } $dataref = $doc->createElementNS(XMLSecEnc::XMLENCNS, 'xenc:DataReference'); $refList->appendChild($dataref); - $dataref->setAttribute('URI', '#'.$guid); + $dataref->setAttribute('URI', '#' . $guid); } public function EncryptBody($siteKey, $objKey, $token) @@ -526,7 +615,7 @@ public function decryptSoapDoc($doc, $options) foreach ($references as $reference) { $arUrl = parse_url($reference); $reference = $arUrl['fragment']; - $query = '//*[@Id="'.$reference.'"]'; + $query = '//*[@Id="' . $reference . '"]'; $nodes = $xpath->query($query); $encData = $nodes->item(0); @@ -543,11 +632,23 @@ public function decryptSoapDoc($doc, $options) return true; } + /** + * saveXML + * + * @return string + */ public function saveXML() { return $this->soapDoc->saveXML(); } + /** + * save + * + * @param string $file + * + * @return int|false + */ public function save($file) { return $this->soapDoc->save($file); diff --git a/src/WSSESoapServer.php b/src/WSSESoapServer.php index 5e2e66f..b5e814b 100644 --- a/src/WSSESoapServer.php +++ b/src/WSSESoapServer.php @@ -140,12 +140,12 @@ public function processSignature($refNode) if ($uri = $encmeth->getAttribute('URI')) { $arUrl = parse_url($uri); if (empty($arUrl['path']) && ($identifier = $arUrl['fragment'])) { - $query = '//wswsse:BinarySecurityToken[@wswsu:Id="'.$identifier.'"]'; + $query = '//wswsse:BinarySecurityToken[@wswsu:Id="' . $identifier . '"]'; $nodeset = $this->SOAPXPath->query($query); if ($encmeth = $nodeset->item(0)) { $x509cert = $encmeth->textContent; $x509cert = str_replace(array("\r", "\n"), '', $x509cert); - $x509cert = "-----BEGIN CERTIFICATE-----\n".chunk_split($x509cert, 64, "\n")."-----END CERTIFICATE-----\n"; + $x509cert = "-----BEGIN CERTIFICATE-----\n" . chunk_split($x509cert, 64, "\n") . "-----END CERTIFICATE-----\n"; $objKey->loadKey($x509cert); break; }