Guest User

Untitled

a guest
Dec 4th, 2017
470
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PHP 99.49 KB | None | 0 0
  1. <?php  
  2. /**
  3.  * soap-wsse.php
  4.  *
  5.  * Copyright (c) 2010, Robert Richards <rrichards@ctindustries.net>.
  6.  * All rights reserved.
  7.  *
  8.  * Redistribution and use in source and binary forms, with or without
  9.  * modification, are permitted provided that the following conditions
  10.  * are met:
  11.  *
  12.  *   * Redistributions of source code must retain the above copyright
  13.  *     notice, this list of conditions and the following disclaimer.
  14.  *
  15.  *   * Redistributions in binary form must reproduce the above copyright
  16.  *     notice, this list of conditions and the following disclaimer in
  17.  *     the documentation and/or other materials provided with the
  18.  *     distribution.
  19.  *
  20.  *   * Neither the name of Robert Richards nor the names of his
  21.  *     contributors may be used to endorse or promote products derived
  22.  *     from this software without specific prior written permission.
  23.  *
  24.  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  25.  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  26.  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  27.  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  28.  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  29.  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  30.  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  31.  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  32.  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  33.  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
  34.  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  35.  * POSSIBILITY OF SUCH DAMAGE.
  36.  *
  37.  * @author     Robert Richards <rrichards@ctindustries.net>
  38.  * @copyright  2007-2010 Robert Richards <rrichards@ctindustries.net>
  39.  * @license    http://www.opensource.org/licenses/bsd-license.php  BSD License
  40.  * @version    1.1.0-dev
  41.  */
  42.  
  43. class WSSESoap {
  44.     const WSSENS = 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd';
  45.     const WSUNS = 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd';
  46.     const WSUNAME = 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0';
  47.     const WSSEPFX = 'wsse';
  48.     const WSUPFX = 'wsu';
  49.     private $soapNS, $soapPFX;
  50.     private $soapDoc = NULL;
  51.     private $envelope = NULL;
  52.     private $SOAPXPath = NULL;
  53.     private $secNode = NULL;
  54.     public $signAllHeaders = FALSE;
  55.      
  56.     private function locateSecurityHeader($bMustUnderstand = TRUE, $setActor = NULL) {
  57.         if ($this->secNode == NULL) {
  58.             $headers = $this->SOAPXPath->query('//wssoap:Envelope/wssoap:Header');
  59.             $header = $headers->item(0);
  60.             if (! $header) {
  61.                 $header = $this->soapDoc->createElementNS($this->soapNS, $this->soapPFX.':Header');
  62.                 $this->envelope->insertBefore($header, $this->envelope->firstChild);
  63.             }
  64.             $secnodes = $this->SOAPXPath->query('./wswsse:Security', $header);
  65.             $secnode = NULL;
  66.             foreach ($secnodes AS $node) {
  67.                 $actor = $node->getAttributeNS($this->soapNS, 'actor');
  68.                 if ($actor == $setActor) {
  69.                     $secnode = $node;
  70.                     break;
  71.                 }
  72.             }
  73.             if (! $secnode) {
  74.                 $secnode = $this->soapDoc->createElementNS(WSSESoap::WSSENS, WSSESoap::WSSEPFX.':Security');
  75.                 $header->appendChild($secnode);
  76.                 if ($bMustUnderstand) {
  77.                     $secnode->setAttributeNS($this->soapNS, $this->soapPFX.':mustUnderstand', '1');
  78.                 }
  79.                 if (! empty($setActor)) {
  80.                     $ename = 'actor';
  81.                     if ($this->soapNS == 'http://www.w3.org/2003/05/soap-envelope') {
  82.                         $ename = 'role';
  83.                     }
  84.                     $secnode->setAttributeNS($this->soapNS, $this->soapPFX.':'.$ename, $setActor);
  85.                 }
  86.             }
  87.             $this->secNode = $secnode;
  88.         }
  89.         return $this->secNode;
  90.     }
  91.  
  92.     public function __construct($doc, $bMustUnderstand = TRUE, $setActor=NULL) {
  93.         $this->soapDoc = $doc;
  94.         $this->envelope = $doc->documentElement;
  95.         $this->soapNS = $this->envelope->namespaceURI;
  96.         $this->soapPFX = $this->envelope->prefix;
  97.         $this->SOAPXPath = new DOMXPath($doc);
  98.         $this->SOAPXPath->registerNamespace('wssoap', $this->soapNS);
  99.         $this->SOAPXPath->registerNamespace('wswsse', WSSESoap::WSSENS);
  100.         $this->locateSecurityHeader($bMustUnderstand, $setActor);
  101.     }
  102.  
  103.     public function addTimestamp($secondsToExpire=3600) {
  104.         /* Add the WSU timestamps */
  105.         $security = $this->locateSecurityHeader();
  106.  
  107.         $timestamp = $this->soapDoc->createElementNS(WSSESoap::WSUNS, WSSESoap::WSUPFX.':Timestamp');
  108.         $security->insertBefore($timestamp, $security->firstChild);
  109.         $currentTime = time();
  110.         $created = $this->soapDoc->createElementNS(WSSESoap::WSUNS,  WSSESoap::WSUPFX.':Created', gmdate("Y-m-d\TH:i:s", $currentTime).'Z');
  111.         $timestamp->appendChild($created);
  112.         if (! is_null($secondsToExpire)) {
  113.             $expire = $this->soapDoc->createElementNS(WSSESoap::WSUNS,  WSSESoap::WSUPFX.':Expires', gmdate("Y-m-d\TH:i:s", $currentTime + $secondsToExpire).'Z');
  114.             $timestamp->appendChild($expire);
  115.         }
  116.     }
  117.  
  118.     public function addUserToken($userName, $password=NULL, $passwordDigest=FALSE) {
  119.         if ($passwordDigest && empty($password)) {
  120.             throw new Exception("Cannot calculate the digest without a password");
  121.         }
  122.          
  123.         $security = $this->locateSecurityHeader();
  124.  
  125.         $token = $this->soapDoc->createElementNS(WSSESoap::WSSENS, WSSESoap::WSSEPFX.':UsernameToken');
  126.         $security->insertBefore($token, $security->firstChild);
  127.  
  128.         $username = $this->soapDoc->createElementNS(WSSESoap::WSSENS,  WSSESoap::WSSEPFX.':Username', $userName);
  129.         $token->appendChild($username);
  130.          
  131.         /* Generate nonce - create a 256 bit session key to be used */
  132.         $objKey = new XMLSecurityKey(XMLSecurityKey::AES256_CBC);
  133.         $nonce = $objKey->generateSessionKey();
  134.         unset($objKey);
  135.         $createdate = gmdate("Y-m-d\TH:i:s").'Z';
  136.          
  137.         if ($password) {
  138.             $passType = WSSESoap::WSUNAME.'#PasswordText';
  139.             if ($passwordDigest) {
  140.                 $password = base64_encode(sha1($nonce.$createdate. $password, true));
  141.                 $passType = WSSESoap::WSUNAME.'#PasswordDigest';
  142.             }
  143.             $passwordNode = $this->soapDoc->createElementNS(WSSESoap::WSSENS,  WSSESoap::WSSEPFX.':Password', $password);
  144.             $token->appendChild($passwordNode);
  145.             $passwordNode->setAttribute('Type', $passType);
  146.         }
  147.  
  148.         $nonceNode = $this->soapDoc->createElementNS(WSSESoap::WSSENS,  WSSESoap::WSSEPFX.':Nonce', base64_encode($nonce));
  149.         $token->appendChild($nonceNode);
  150.  
  151.         $created = $this->soapDoc->createElementNS(WSSESoap::WSUNS,  WSSESoap::WSUPFX.':Created', $createdate);
  152.         $token->appendChild($created);
  153.     }
  154.  
  155.     public function addBinaryToken($cert, $isPEMFormat=TRUE, $isDSig=TRUE) {
  156.         $security = $this->locateSecurityHeader();
  157.         $data = XMLSecurityDSig::get509XCert($cert, $isPEMFormat);
  158.  
  159.         $token = $this->soapDoc->createElementNS(WSSESoap::WSSENS, WSSESoap::WSSEPFX.':BinarySecurityToken', $data);
  160.         $security->insertBefore($token, $security->firstChild);
  161.  
  162.         $token->setAttribute('EncodingType', 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary');
  163.         $token->setAttributeNS(WSSESoap::WSUNS, WSSESoap::WSUPFX.':Id', XMLSecurityDSig::generate_GUID());
  164.         $token->setAttribute('ValueType', 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3');
  165.          
  166.         return $token;
  167.     }
  168.      
  169.     public function attachTokentoSig($token) {
  170.         if (! ($token instanceof DOMElement)) {
  171.             throw new Exception('Invalid parameter: BinarySecurityToken element expected');
  172.         }
  173.         $objXMLSecDSig = new XMLSecurityDSig();
  174.         if ($objDSig = $objXMLSecDSig->locateSignature($this->soapDoc)) {
  175.             $tokenURI = '#'.$token->getAttributeNS(WSSESoap::WSUNS, "Id");
  176.             $this->SOAPXPath->registerNamespace('secdsig', XMLSecurityDSig::XMLDSIGNS);
  177.             $query = "./secdsig:KeyInfo";
  178.             $nodeset = $this->SOAPXPath->query($query, $objDSig);
  179.             $keyInfo = $nodeset->item(0);
  180.             if (! $keyInfo) {
  181.                 $keyInfo = $objXMLSecDSig->createNewSignNode('KeyInfo');
  182.                 $objDSig->appendChild($keyInfo);
  183.             }
  184.              
  185.             $tokenRef = $this->soapDoc->createElementNS(WSSESoap::WSSENS, WSSESoap::WSSEPFX.':SecurityTokenReference');
  186.             $keyInfo->appendChild($tokenRef);
  187.             $reference = $this->soapDoc->createElementNS(WSSESoap::WSSENS, WSSESoap::WSSEPFX.':Reference');
  188.             $reference->setAttribute("URI", $tokenURI);
  189.             $tokenRef->appendChild($reference);
  190.         } else {
  191.             throw new Exception('Unable to locate digital signature');
  192.         }
  193.     }
  194.  
  195.     public function signSoapDoc($objKey, $options = NULL) {
  196.         $objDSig = new XMLSecurityDSig();
  197.  
  198.         $objDSig->setCanonicalMethod(XMLSecurityDSig::EXC_C14N);
  199.  
  200.         $arNodes = array();
  201.         foreach ($this->secNode->childNodes AS $node) {
  202.             if ($node->nodeType == XML_ELEMENT_NODE) {
  203.                 $arNodes[] = $node;
  204.             }
  205.         }
  206.  
  207.         if ($this->signAllHeaders) {
  208.             foreach ($this->secNode->parentNode->childNodes AS $node) {
  209.                 if (($node->nodeType == XML_ELEMENT_NODE) &&  
  210.                 ($node->namespaceURI != WSSESoap::WSSENS)) {
  211.                     $arNodes[] = $node;
  212.                 }
  213.             }
  214.         }
  215.  
  216.         foreach ($this->envelope->childNodes AS $node) {
  217.             if ($node->namespaceURI == $this->soapNS && $node->localName == 'Body') {
  218.                 $arNodes[] = $node;
  219.                 break;
  220.             }
  221.         }
  222.        
  223.         $algorithm = XMLSecurityDSig::SHA1;
  224.         if (is_array($options) && isset($options["algorithm"])) {
  225.             $algorithm = $options["algorithm"];
  226.         }
  227.        
  228.         $arOptions = array('prefix'=>WSSESoap::WSUPFX, 'prefix_ns'=>WSSESoap::WSUNS);
  229.         $objDSig->addReferenceList($arNodes, $algorithm, NULL, $arOptions);
  230.  
  231.         $objDSig->sign($objKey);
  232.  
  233.         $insertTop = TRUE;
  234.         if (is_array($options) && isset($options["insertBefore"])) {
  235.             $insertTop = (bool)$options["insertBefore"];
  236.         }
  237.         $objDSig->appendSignature($this->secNode, $insertTop);
  238.  
  239. /* New suff */
  240.         if (is_array($options)) {
  241.             if (! empty($options["KeyInfo"]) ) {
  242.                 if (! empty($options["KeyInfo"]["X509SubjectKeyIdentifier"])) {
  243.                     $sigNode = $this->secNode->firstChild->nextSibling;
  244.                     $objDoc = $sigNode->ownerDocument;
  245.                     $keyInfo = $sigNode->ownerDocument->createElementNS(XMLSecurityDSig::XMLDSIGNS, 'ds:KeyInfo');
  246.                     $sigNode->appendChild($keyInfo);
  247.                     $tokenRef = $objDoc->createElementNS(WSSESoap::WSSENS, WSSESoap::WSSEPFX . ':SecurityTokenReference');
  248.                     $keyInfo->appendChild($tokenRef);
  249.                     $reference = $objDoc->createElementNS(WSSESoap::WSSENS, WSSESoap::WSSEPFX . ':KeyIdentifier');
  250.                     $reference->setAttribute("ValueType", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509SubjectKeyIdentifier");
  251.                     $reference->setAttribute("EncodingType", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary");
  252.                     $tokenRef->appendChild($reference);
  253.                     $x509 = openssl_x509_parse($objKey->getX509Certificate());
  254.                     $keyid = $x509["extensions"]["subjectKeyIdentifier"];
  255.                     $arkeyid = split(":", $keyid);
  256.                     $data = "";
  257.                     foreach ($arkeyid AS $hexchar) {
  258.                         $data .= chr(hexdec($hexchar));
  259.                     }
  260.                     $dataNode = new DOMText(base64_encode($data));
  261.                     $reference->appendChild($dataNode);
  262.                 }
  263.             }
  264.         }
  265.     }
  266.  
  267.     public function addEncryptedKey($node, $key, $token, $options = NULL) {
  268.         if (! $key->encKey) {
  269.             return FALSE;
  270.         }
  271.         $encKey = $key->encKey;
  272.         $security = $this->locateSecurityHeader();
  273.         $doc = $security->ownerDocument;
  274.         if (! $doc->isSameNode($encKey->ownerDocument)) {
  275.             $key->encKey = $security->ownerDocument->importNode($encKey, TRUE);
  276.             $encKey = $key->encKey;
  277.         }
  278.         if (! empty($key->guid)) {
  279.             return TRUE;
  280.         }
  281.          
  282.         $lastToken = NULL;
  283.         $findTokens = $security->firstChild;
  284.         while ($findTokens) {
  285.             if ($findTokens->localName == 'BinarySecurityToken') {
  286.                 $lastToken = $findTokens;
  287.             }
  288.             $findTokens = $findTokens->nextSibling;
  289.         }
  290.         if ($lastToken) {
  291.             $lastToken = $lastToken->nextSibling;
  292.         }
  293.  
  294.         $security->insertBefore($encKey, $lastToken);
  295.         $key->guid = XMLSecurityDSig::generate_GUID();
  296.         $encKey->setAttribute('Id', $key->guid);
  297.         $encMethod = $encKey->firstChild;
  298.         while ($encMethod && $encMethod->localName != 'EncryptionMethod') {
  299.             $encMethod = $encMethod->nextChild;
  300.         }
  301.         if ($encMethod) {
  302.             $encMethod = $encMethod->nextSibling;
  303.         }
  304.         $objDoc = $encKey->ownerDocument;
  305.         $keyInfo = $objDoc->createElementNS('http://www.w3.org/2000/09/xmldsig#', 'dsig:KeyInfo');
  306.         $encKey->insertBefore($keyInfo, $encMethod);
  307.         $tokenRef = $objDoc->createElementNS(WSSESoap::WSSENS, WSSESoap::WSSEPFX.':SecurityTokenReference');
  308.         $keyInfo->appendChild($tokenRef);
  309. /* New suff */
  310.         if (is_array($options)) {
  311.             if (! empty($options["KeyInfo"]) ) {
  312.                 if (! empty($options["KeyInfo"]["X509SubjectKeyIdentifier"])) {
  313.                     $reference = $objDoc->createElementNS(WSSESoap::WSSENS, WSSESoap::WSSEPFX . ':KeyIdentifier');
  314.                     $reference->setAttribute("ValueType", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509SubjectKeyIdentifier");
  315.                     $reference->setAttribute("EncodingType", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary");
  316.                     $tokenRef->appendChild($reference);
  317.                     $x509 = openssl_x509_parse($token->getX509Certificate());
  318.                     $keyid = $x509["extensions"]["subjectKeyIdentifier"];
  319.                     $arkeyid = split(":", $keyid);
  320.                     $data = "";
  321.                     foreach ($arkeyid AS $hexchar) {
  322.                         $data .= chr(hexdec($hexchar));
  323.                     }
  324.                     $dataNode = new DOMText(base64_encode($data));
  325.                     $reference->appendChild($dataNode);
  326.                     return TRUE;
  327.                 }
  328.             }
  329.         }
  330.        
  331.         $tokenURI = '#'.$token->getAttributeNS(WSSESoap::WSUNS, "Id");
  332.         $reference = $objDoc->createElementNS(WSSESoap::WSSENS, WSSESoap::WSSEPFX.':Reference');
  333.         $reference->setAttribute("URI", $tokenURI);
  334.         $tokenRef->appendChild($reference);
  335.  
  336.         return TRUE;
  337.     }
  338.  
  339.     public function AddReference($baseNode, $guid) {
  340.         $refList = NULL;
  341.         $child = $baseNode->firstChild;
  342.         while($child) {
  343.             if (($child->namespaceURI == XMLSecEnc::XMLENCNS) && ($child->localName == 'ReferenceList')) {
  344.                 $refList = $child;
  345.                 break;
  346.             }
  347.             $child = $child->nextSibling;
  348.         }
  349.         $doc = $baseNode->ownerDocument;
  350.         if (is_null($refList)) {
  351.             $refList = $doc->createElementNS(XMLSecEnc::XMLENCNS, 'xenc:ReferenceList');
  352.             $baseNode->appendChild($refList);
  353.         }
  354.         $dataref = $doc->createElementNS(XMLSecEnc::XMLENCNS, 'xenc:DataReference');
  355.         $refList->appendChild($dataref);
  356.         $dataref->setAttribute('URI', '#'.$guid);
  357.     }
  358.  
  359.     public function EncryptBody($siteKey, $objKey, $token) {
  360.  
  361.         $enc = new XMLSecEnc();
  362.         foreach ($this->envelope->childNodes AS $node) {
  363.             if ($node->namespaceURI == $this->soapNS && $node->localName == 'Body') {
  364.                 break;
  365.             }
  366.         }
  367.         $enc->setNode($node);
  368.         /* encrypt the symmetric key */
  369.         $enc->encryptKey($siteKey, $objKey, FALSE);
  370.  
  371.         $enc->type = XMLSecEnc::Content;
  372.         /* Using the symmetric key to actually encrypt the data */
  373.         $encNode = $enc->encryptNode($objKey);
  374.  
  375.         $guid = XMLSecurityDSig::generate_GUID();
  376.         $encNode->setAttribute('Id', $guid);
  377.  
  378.         $refNode = $encNode->firstChild;
  379.         while($refNode && $refNode->nodeType != XML_ELEMENT_NODE) {
  380.             $refNode = $refNode->nextSibling;
  381.         }
  382.         if ($refNode) {
  383.             $refNode = $refNode->nextSibling;
  384.         }
  385.         if ($this->addEncryptedKey($encNode, $enc, $token)) {
  386.             $this->AddReference($enc->encKey, $guid);
  387.         }
  388.     }
  389.      
  390.     public function encryptSoapDoc($siteKey, $objKey, $options=NULL, $encryptSignature=TRUE) {
  391.  
  392.         $enc = new XMLSecEnc();
  393.  
  394.         $xpath = new DOMXPath($this->envelope->ownerDocument);
  395.         if ($encryptSignature ==  FALSE) {
  396.             $nodes = $xpath->query('//*[local-name()="Body"]');
  397.         } else {
  398.             $nodes = $xpath->query('//*[local-name()="Signature"] | //*[local-name()="Body"]');
  399.         }
  400.        
  401.         foreach ($nodes AS $node) {
  402.             $type = XMLSecEnc::Element;
  403.             $name = $node->localName;
  404.             if ($name == "Body") {
  405.                 $type = XMLSecEnc::Content;
  406.             }
  407.             $enc->addReference($name, $node, $type);
  408.         }
  409.  
  410.         $enc->encryptReferences($objKey);
  411.        
  412.         $enc->encryptKey($siteKey, $objKey, false);
  413.        
  414.         $nodes = $xpath->query('//*[local-name()="Security"]');
  415.         $signode = $nodes->item(0);
  416.         $this->addEncryptedKey($signode, $enc, $siteKey, $options);
  417.     }
  418.    
  419.     public function decryptSoapDoc($doc, $options) {
  420.  
  421.         $privKey = NULL;
  422.         $privKey_isFile = FALSE;
  423.         $privKey_isCert = FALSE;
  424.        
  425.         if (is_array($options)) {
  426.             $privKey = (! empty($options["keys"]["private"]["key"]) ? $options["keys"]["private"]["key"] : NULL);
  427.             $privKey_isFile = (! empty($options["keys"]["private"]["isFile"]) ? TRUE : FALSE);
  428.             $privKey_isCert = (! empty($options["keys"]["private"]["isCert"])  ? TRUE : FALSE);
  429.         }
  430.        
  431.         $objenc = new XMLSecEnc();
  432.  
  433.         $xpath = new DOMXPath($doc);
  434.         $envns = $doc->documentElement->namespaceURI;
  435.         $xpath->registerNamespace("soapns", $envns);
  436.         $xpath->registerNamespace("soapenc", "http://www.w3.org/2001/04/xmlenc#");
  437.  
  438.         $nodes = $xpath->query('/soapns:Envelope/soapns:Header/*[local-name()="Security"]/soapenc:EncryptedKey');
  439.  
  440.         $references = array();
  441.         if ($node = $nodes->item(0)) {
  442.             $objenc = new XMLSecEnc();
  443.             $objenc->setNode($node);
  444.             if (! $objKey = $objenc->locateKey()) {
  445.                 throw new Exception("Unable to locate algorithm for this Encrypted Key");
  446.             }
  447.             $objKey->isEncrypted = TRUE;
  448.             $objKey->encryptedCtx = $objenc;
  449.             XMLSecEnc::staticLocateKeyInfo($objKey, $node);
  450.             if ($objKey && $objKey->isEncrypted) {
  451.                 $objencKey = $objKey->encryptedCtx;
  452.                 $objKey->loadKey($privKey, $privKey_isFile, $privKey_isCert);
  453.                 $key = $objencKey->decryptKey($objKey);
  454.                 $objKey->loadKey($key);
  455.             }
  456.  
  457.             $refnodes = $xpath->query('./soapenc:ReferenceList/soapenc:DataReference/@URI', $node);
  458.             foreach ($refnodes as $reference) {
  459.                 $references[] = $reference->nodeValue;
  460.             }
  461.         }
  462.  
  463.         foreach ($references AS $reference) {
  464.             $arUrl = parse_url($reference);
  465.             $reference = $arUrl['fragment'];
  466.             $query = '//*[@Id="'.$reference.'"]';
  467.             $nodes = $xpath->query($query);
  468.             $encData = $nodes->item(0);
  469.  
  470.             if ($algo = $xpath->evaluate("string(./soapenc:EncryptionMethod/@Algorithm)", $encData)) {
  471.                 $objKey = new XMLSecurityKey($algo);
  472.                 $objKey->loadKey($key);
  473.             }
  474.  
  475.             $objenc->setNode($encData);
  476.             $objenc->type = $encData->getAttribute("Type");
  477.             $decrypt = $objenc->decryptNode($objKey, TRUE);
  478.         }
  479.        
  480.         return TRUE;
  481.     }
  482.  
  483.     public function saveXML() {
  484.         return $this->soapDoc->saveXML();
  485.     }
  486.  
  487.     public function save($file) {
  488.         return $this->soapDoc->save($file);
  489.     }
  490. }
  491.  
  492.  
  493. /**
  494.  * soap-wsa.php
  495.  *
  496.  * Copyright (c) 2007, Robert Richards <rrichards@ctindustries.net>.
  497.  * All rights reserved.
  498.  *
  499.  * Redistribution and use in source and binary forms, with or without
  500.  * modification, are permitted provided that the following conditions
  501.  * are met:
  502.  *
  503.  *   * Redistributions of source code must retain the above copyright
  504.  *     notice, this list of conditions and the following disclaimer.
  505.  *
  506.  *   * Redistributions in binary form must reproduce the above copyright
  507.  *     notice, this list of conditions and the following disclaimer in
  508.  *     the documentation and/or other materials provided with the
  509.  *     distribution.
  510.  *
  511.  *   * Neither the name of Robert Richards nor the names of his
  512.  *     contributors may be used to endorse or promote products derived
  513.  *     from this software without specific prior written permission.
  514.  *
  515.  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  516.  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  517.  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  518.  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  519.  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  520.  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  521.  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  522.  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  523.  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  524.  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
  525.  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  526.  * POSSIBILITY OF SUCH DAMAGE.
  527.  *
  528.  * @author     Robert Richards <rrichards@ctindustries.net>
  529.  * @copyright  2007 Robert Richards <rrichards@ctindustries.net>
  530.  * @license    http://www.opensource.org/licenses/bsd-license.php  BSD License
  531.  * @version    1.0.0
  532.  */
  533.  
  534. class WSASoap {
  535.     const WSANS = 'http://schemas.xmlsoap.org/ws/2004/08/addressing';
  536.     const WSAPFX = 'wsa';
  537.     private $soapNS, $soapPFX;
  538.     private $soapDoc = NULL;
  539.     private $envelope = NULL;
  540.     private $SOAPXPath = NULL;
  541.     private $header = NULL;
  542.     private $messageID = NULL;
  543.      
  544.     private function locateHeader() {
  545.         if ($this->header == NULL) {
  546.             $headers = $this->SOAPXPath->query('//wssoap:Envelope/wssoap:Header');
  547.             $header = $headers->item(0);
  548.             if (! $header) {
  549.                 $header = $this->soapDoc->createElementNS($this->soapNS, $this->soapPFX.':Header');
  550.                 $this->envelope->insertBefore($header, $this->envelope->firstChild);
  551.             }
  552.             $this->header = $header;
  553.         }
  554.         return $this->header;
  555.     }
  556.  
  557.     public function __construct($doc) {
  558.         $this->soapDoc = $doc;
  559.         $this->envelope = $doc->documentElement;
  560.         $this->soapNS = $this->envelope->namespaceURI;
  561.         $this->soapPFX = $this->envelope->prefix;
  562.         $this->SOAPXPath = new DOMXPath($doc);
  563.         $this->SOAPXPath->registerNamespace('wssoap', $this->soapNS);
  564.         $this->SOAPXPath->registerNamespace('wswsa', WSASoap::WSANS);
  565.          
  566.         $this->envelope->setAttributeNS("http://www.w3.org/2000/xmlns/", 'xmlns:'.WSASoap::WSAPFX, WSASoap::WSANS);
  567.         $this->locateHeader();
  568.     }
  569.  
  570.     public function addAction($action) {
  571.         /* Add the WSA Action */
  572.         $header = $this->locateHeader();
  573.  
  574.         $nodeAction = $this->soapDoc->createElementNS(WSASoap::WSANS, WSASoap::WSAPFX.':Action', $action);
  575.         $header->appendChild($nodeAction);
  576.     }
  577.  
  578.     public function addTo($location) {
  579.         /* Add the WSA To */
  580.         $header = $this->locateHeader();
  581.  
  582.         $nodeTo = $this->soapDoc->createElementNS(WSASoap::WSANS, WSASoap::WSAPFX.':To', $location);
  583.         $header->appendChild($nodeTo);
  584.     }
  585.  
  586.     private function createID() {
  587.         $uuid = md5(uniqid(rand(), true));
  588.         $guid =  'uudi:'.substr($uuid,0,8)."-".
  589.                 substr($uuid,8,4)."-".
  590.                 substr($uuid,12,4)."-".
  591.                 substr($uuid,16,4)."-".
  592.                 substr($uuid,20,12);
  593.         return $guid;
  594.     }
  595.  
  596.     public function addMessageID($id=NULL) {
  597.         /* Add the WSA MessageID or return existing ID */
  598.         if (! is_null($this->messageID)) {
  599.             return $this->messageID;
  600.         }
  601.  
  602.         if (empty($id)) {
  603.             $id = $this->createID();
  604.         }
  605.  
  606.         $header = $this->locateHeader();
  607.  
  608.         $nodeID = $this->soapDoc->createElementNS(WSASoap::WSANS, WSASoap::WSAPFX.':MessageID', $id);
  609.         $header->appendChild($nodeID);
  610.         $this->messageID = $id;
  611.     }
  612.  
  613.     public function addReplyTo($address = NULL) {
  614.             /* Create Message ID is not already added - required for ReplyTo */
  615.             if (is_null($this->messageID)) {
  616.                 $this->addMessageID();
  617.             }
  618.             /* Add the WSA ReplyTo */
  619.             $header = $this->locateHeader();
  620.      
  621.             $nodeReply = $this->soapDoc->createElementNS(WSASoap::WSANS, WSASoap::WSAPFX.':ReplyTo');
  622.             $header->appendChild($nodeReply);
  623.              
  624.             if (empty($address)) {
  625.                 $address = 'http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous';
  626.             }
  627.             $nodeAddress = $this->soapDoc->createElementNS(WSASoap::WSANS, WSASoap::WSAPFX.':Address', $address);
  628.             $nodeReply->appendChild($nodeAddress);
  629.     }
  630.  
  631.     public function getDoc() {
  632.         return $this->soapDoc;
  633.     }
  634.      
  635.     public function saveXML() {
  636.         return $this->soapDoc->saveXML();
  637.     }
  638.  
  639.     public function save($file) {
  640.         return $this->soapDoc->save($file);
  641.     }
  642. }
  643.  
  644. /**
  645.  * xmlseclibs.php
  646.  *
  647.  * Copyright (c) 2007-2010, Robert Richards <rrichards@cdatazone.org>.
  648.  * All rights reserved.
  649.  *
  650.  * Redistribution and use in source and binary forms, with or without
  651.  * modification, are permitted provided that the following conditions
  652.  * are met:
  653.  *
  654.  *   * Redistributions of source code must retain the above copyright
  655.  *     notice, this list of conditions and the following disclaimer.
  656.  *
  657.  *   * Redistributions in binary form must reproduce the above copyright
  658.  *     notice, this list of conditions and the following disclaimer in
  659.  *     the documentation and/or other materials provided with the
  660.  *     distribution.
  661.  *
  662.  *   * Neither the name of Robert Richards nor the names of his
  663.  *     contributors may be used to endorse or promote products derived
  664.  *     from this software without specific prior written permission.
  665.  *
  666.  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  667.  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  668.  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  669.  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  670.  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  671.  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  672.  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  673.  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  674.  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  675.  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
  676.  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  677.  * POSSIBILITY OF SUCH DAMAGE.
  678.  *
  679.  * @author     Robert Richards <rrichards@cdatazone.org>
  680.  * @copyright  2007-2010 Robert Richards <rrichards@cdatazone.org>
  681.  * @license    http://www.opensource.org/licenses/bsd-license.php  BSD License
  682.  * @version    1.3.0-dev
  683.  */
  684.  
  685. /*
  686. Functions to generate simple cases of Exclusive Canonical XML - Callable function is C14NGeneral()
  687. i.e.: $canonical = C14NGeneral($domelement, TRUE);
  688. */
  689.  
  690. /* helper function */
  691. function sortAndAddAttrs($element, $arAtts) {
  692.    $newAtts = array();
  693.    foreach ($arAtts AS $attnode) {
  694.       $newAtts[$attnode->nodeName] = $attnode;
  695.    }
  696.    ksort($newAtts);
  697.    foreach ($newAtts as $attnode) {
  698.       $element->setAttribute($attnode->nodeName, $attnode->nodeValue);
  699.    }
  700. }
  701.  
  702. /* helper function */
  703. function canonical($tree, $element, $withcomments) {
  704.     if ($tree->nodeType != XML_DOCUMENT_NODE) {
  705.         $dom = $tree->ownerDocument;
  706.     } else {
  707.         $dom = $tree;
  708.     }
  709.     if ($element->nodeType != XML_ELEMENT_NODE) {
  710.         if ($element->nodeType == XML_DOCUMENT_NODE) {
  711.             foreach ($element->childNodes AS $node) {
  712.                 canonical($dom, $node, $withcomments);
  713.             }
  714.             return;
  715.         }
  716.         if ($element->nodeType == XML_COMMENT_NODE && ! $withcomments) {
  717.             return;
  718.         }
  719.         $tree->appendChild($dom->importNode($element, TRUE));
  720.         return;
  721.     }
  722.     $arNS = array();
  723.     if ($element->namespaceURI != "") {
  724.         if ($element->prefix == "") {
  725.             $elCopy = $dom->createElementNS($element->namespaceURI, $element->nodeName);
  726.         } else {
  727.             $prefix = $tree->lookupPrefix($element->namespaceURI);
  728.             if ($prefix == $element->prefix) {
  729.                 $elCopy = $dom->createElementNS($element->namespaceURI, $element->nodeName);
  730.             } else {
  731.                 $elCopy = $dom->createElement($element->nodeName);
  732.                 $arNS[$element->namespaceURI] = $element->prefix;
  733.             }
  734.         }
  735.     } else {
  736.         $elCopy = $dom->createElement($element->nodeName);
  737.     }
  738.     $tree->appendChild($elCopy);
  739.  
  740.     /* Create DOMXPath based on original document */
  741.     $xPath = new DOMXPath($element->ownerDocument);
  742.  
  743.     /* Get namespaced attributes */
  744.     $arAtts = $xPath->query('attribute::*[namespace-uri(.) != ""]', $element);
  745.  
  746.     /* Create an array with namespace URIs as keys, and sort them */
  747.     foreach ($arAtts AS $attnode) {
  748.         if (array_key_exists($attnode->namespaceURI, $arNS) &&
  749.             ($arNS[$attnode->namespaceURI] == $attnode->prefix)) {
  750.             continue;
  751.         }
  752.         $prefix = $tree->lookupPrefix($attnode->namespaceURI);
  753.         if ($prefix != $attnode->prefix) {
  754.            $arNS[$attnode->namespaceURI] = $attnode->prefix;
  755.         } else {
  756.             $arNS[$attnode->namespaceURI] = NULL;
  757.         }
  758.     }
  759.     if (count($arNS) > 0) {
  760.         asort($arNS);
  761.     }
  762.  
  763.     /* Add namespace nodes */
  764.     foreach ($arNS AS $namespaceURI=>$prefix) {
  765.         if ($prefix != NULL) {
  766.               $elCopy->setAttributeNS("http://www.w3.org/2000/xmlns/",
  767.                                "xmlns:".$prefix, $namespaceURI);
  768.         }
  769.     }
  770.     if (count($arNS) > 0) {
  771.         ksort($arNS);
  772.     }
  773.  
  774.     /* Get attributes not in a namespace, and then sort and add them */
  775.     $arAtts = $xPath->query('attribute::*[namespace-uri(.) = ""]', $element);
  776.     sortAndAddAttrs($elCopy, $arAtts);
  777.  
  778.     /* Loop through the URIs, and then sort and add attributes within that namespace */
  779.     foreach ($arNS as $nsURI=>$prefix) {
  780.        $arAtts = $xPath->query('attribute::*[namespace-uri(.) = "'.$nsURI.'"]', $element);
  781.        sortAndAddAttrs($elCopy, $arAtts);
  782.     }
  783.  
  784.     foreach ($element->childNodes AS $node) {
  785.         canonical($elCopy, $node, $withcomments);
  786.     }
  787. }
  788.  
  789. /*
  790. $element - DOMElement for which to produce the canonical version of
  791. $exclusive - boolean to indicate exclusive canonicalization (must pass TRUE)
  792. $withcomments - boolean indicating wether or not to include comments in canonicalized form
  793. */
  794. function C14NGeneral($element, $exclusive=FALSE, $withcomments=FALSE) {
  795.     /* IF PHP 5.2+ then use built in canonical functionality */
  796.     $php_version = explode('.', PHP_VERSION);
  797.     if (($php_version[0] > 5) || ($php_version[0] == 5 && $php_version[1] >= 2) ) {
  798.         return $element->C14N($exclusive, $withcomments);
  799.     }
  800.  
  801.     /* Must be element or document */
  802.     if (! $element instanceof DOMElement && ! $element instanceof DOMDocument) {
  803.         return NULL;
  804.     }
  805.     /* Currently only exclusive XML is supported */
  806.     if ($exclusive == FALSE) {
  807.         throw new Exception("Only exclusive canonicalization is supported in this version of PHP");
  808.     }
  809.  
  810.     $copyDoc = new DOMDocument();
  811.     canonical($copyDoc, $element, $withcomments);
  812.     return $copyDoc->saveXML($copyDoc->documentElement, LIBXML_NOEMPTYTAG);
  813. }
  814.  
  815. class XMLSecurityKey {
  816.     const TRIPLEDES_CBC = 'http://www.w3.org/2001/04/xmlenc#tripledes-cbc';
  817.     const AES128_CBC = 'http://www.w3.org/2001/04/xmlenc#aes128-cbc';
  818.     const AES192_CBC = 'http://www.w3.org/2001/04/xmlenc#aes192-cbc';
  819.     const AES256_CBC = 'http://www.w3.org/2001/04/xmlenc#aes256-cbc';
  820.     const RSA_1_5 = 'http://www.w3.org/2001/04/xmlenc#rsa-1_5';
  821.     const RSA_OAEP_MGF1P = 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p';
  822.     const DSA_SHA1 = 'http://www.w3.org/2000/09/xmldsig#dsa-sha1';
  823.     const RSA_SHA1 = 'http://www.w3.org/2000/09/xmldsig#rsa-sha1';
  824.     const RSA_SHA256 = 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256';
  825.  
  826.     private $cryptParams = array();
  827.     public $type = 0;
  828.     public $key = NULL;
  829.     public $passphrase = "";
  830.     public $iv = NULL;
  831.     public $name = NULL;
  832.     public $keyChain = NULL;
  833.     public $isEncrypted = FALSE;
  834.     public $encryptedCtx = NULL;
  835.     public $guid = NULL;
  836.  
  837.     /**
  838.      * This variable contains the certificate as a string if this key represents an X509-certificate.
  839.      * If this key doesn't represent a certificate, this will be NULL.
  840.      */
  841.     private $x509Certificate = NULL;
  842.  
  843.     /* This variable contains the certificate thunbprint if we have loaded an X509-certificate. */
  844.     private $X509Thumbprint = NULL;
  845.  
  846.     public function __construct($type, $params=NULL) {
  847.         srand();
  848.         switch ($type) {
  849.             case (XMLSecurityKey::TRIPLEDES_CBC):
  850.                 $this->cryptParams['library'] = 'mcrypt';
  851.                 $this->cryptParams['cipher'] = MCRYPT_TRIPLEDES;
  852.                 $this->cryptParams['mode'] = MCRYPT_MODE_CBC;
  853.                 $this->cryptParams['method'] = 'http://www.w3.org/2001/04/xmlenc#tripledes-cbc';
  854.                 break;
  855.             case (XMLSecurityKey::AES128_CBC):
  856.                 $this->cryptParams['library'] = 'mcrypt';
  857.                 $this->cryptParams['cipher'] = MCRYPT_RIJNDAEL_128;
  858.                 $this->cryptParams['mode'] = MCRYPT_MODE_CBC;
  859.                 $this->cryptParams['method'] = 'http://www.w3.org/2001/04/xmlenc#aes128-cbc';
  860.                 break;
  861.             case (XMLSecurityKey::AES192_CBC):
  862.                 $this->cryptParams['library'] = 'mcrypt';
  863.                 $this->cryptParams['cipher'] = MCRYPT_RIJNDAEL_128;
  864.                 $this->cryptParams['mode'] = MCRYPT_MODE_CBC;
  865.                 $this->cryptParams['method'] = 'http://www.w3.org/2001/04/xmlenc#aes192-cbc';
  866.                 break;
  867.             case (XMLSecurityKey::AES256_CBC):
  868.                 $this->cryptParams['library'] = 'mcrypt';
  869.                 $this->cryptParams['cipher'] = MCRYPT_RIJNDAEL_128;
  870.                 $this->cryptParams['mode'] = MCRYPT_MODE_CBC;
  871.                 $this->cryptParams['method'] = 'http://www.w3.org/2001/04/xmlenc#aes256-cbc';
  872.                 break;
  873.             case (XMLSecurityKey::RSA_1_5):
  874.                 $this->cryptParams['library'] = 'openssl';
  875.                 $this->cryptParams['padding'] = OPENSSL_PKCS1_PADDING;
  876.                 $this->cryptParams['method'] = 'http://www.w3.org/2001/04/xmlenc#rsa-1_5';
  877.                 if (is_array($params) && ! empty($params['type'])) {
  878.                     if ($params['type'] == 'public' || $params['type'] == 'private') {
  879.                         $this->cryptParams['type'] = $params['type'];
  880.                         break;
  881.                     }
  882.                 }
  883.                 throw new Exception('Certificate "type" (private/public) must be passed via parameters');
  884.                 return;
  885.             case (XMLSecurityKey::RSA_OAEP_MGF1P):
  886.                 $this->cryptParams['library'] = 'openssl';
  887.                 $this->cryptParams['padding'] = OPENSSL_PKCS1_OAEP_PADDING;
  888.                 $this->cryptParams['method'] = 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p';
  889.                 $this->cryptParams['hash'] = NULL;
  890.                 if (is_array($params) && ! empty($params['type'])) {
  891.                     if ($params['type'] == 'public' || $params['type'] == 'private') {
  892.                         $this->cryptParams['type'] = $params['type'];
  893.                         break;
  894.                     }
  895.                 }
  896.                 throw new Exception('Certificate "type" (private/public) must be passed via parameters');
  897.                 return;
  898.             case (XMLSecurityKey::RSA_SHA1):
  899.                 $this->cryptParams['library'] = 'openssl';
  900.                 $this->cryptParams['method'] = 'http://www.w3.org/2000/09/xmldsig#rsa-sha1';
  901.                 $this->cryptParams['padding'] = OPENSSL_PKCS1_PADDING;
  902.                 if (is_array($params) && ! empty($params['type'])) {
  903.                     if ($params['type'] == 'public' || $params['type'] == 'private') {
  904.                         $this->cryptParams['type'] = $params['type'];
  905.                         break;
  906.                     }
  907.                 }
  908.                 throw new Exception('Certificate "type" (private/public) must be passed via parameters');
  909.                 break;
  910.             case (XMLSecurityKey::RSA_SHA256):
  911.                 $this->cryptParams['library'] = 'openssl';
  912.                 $this->cryptParams['method'] = 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256';
  913.                 $this->cryptParams['padding'] = OPENSSL_PKCS1_PADDING;
  914.                 $this->cryptParams['digest'] = 'SHA256';
  915.                 if (is_array($params) && ! empty($params['type'])) {
  916.                     if ($params['type'] == 'public' || $params['type'] == 'private') {
  917.                         $this->cryptParams['type'] = $params['type'];
  918.                         break;
  919.                     }
  920.                 }
  921.                 throw new Exception('Certificate "type" (private/public) must be passed via parameters');
  922.                 break;
  923.             default:
  924.                 throw new Exception('Invalid Key Type');
  925.                 return;
  926.         }
  927.         $this->type = $type;
  928.     }
  929.  
  930.     public function generateSessionKey() {
  931.         $key = '';
  932.         if (! empty($this->cryptParams['cipher']) && ! empty($this->cryptParams['mode'])) {
  933.             $keysize = mcrypt_module_get_algo_key_size($this->cryptParams['cipher']);
  934.             /* Generating random key using iv generation routines */
  935.             if (($keysize > 0) && ($td = mcrypt_module_open(MCRYPT_RIJNDAEL_256, '',$this->cryptParams['mode'], ''))) {
  936.                 if ($this->cryptParams['cipher'] == MCRYPT_RIJNDAEL_128) {
  937.                     $keysize = 16;
  938.                     if ($this->type == XMLSecurityKey::AES256_CBC) {
  939.                         $keysize = 32;
  940.                     } elseif ($this->type == XMLSecurityKey::AES192_CBC) {
  941.                         $keysize = 24;
  942.                     }
  943.                 }
  944.                 while (strlen($key) < $keysize) {
  945.                     $key .= mcrypt_create_iv(mcrypt_enc_get_iv_size ($td),MCRYPT_RAND);
  946.                 }
  947.                 mcrypt_module_close($td);
  948.                 $key = substr($key, 0, $keysize);
  949.                 $this->key = $key;
  950.             }
  951.         }
  952.         return $key;
  953.     }
  954.  
  955.     public static function getRawThumbprint($cert) {
  956.  
  957.         $arCert = explode("\n", $cert);
  958.         $data = '';
  959.         $inData = FALSE;
  960.  
  961.         foreach ($arCert AS $curData) {
  962.             if (! $inData) {
  963.                 if (strncmp($curData, '-----BEGIN CERTIFICATE', 22) == 0) {
  964.                     $inData = TRUE;
  965.                 }
  966.             } else {
  967.                 if (strncmp($curData, '-----END CERTIFICATE', 20) == 0) {
  968.                     $inData = FALSE;
  969.                     break;
  970.                 }
  971.                 $data .= trim($curData);
  972.             }
  973.         }
  974.  
  975.         if (! empty($data)) {
  976.             return strtolower(sha1(base64_decode($data)));
  977.         }
  978.  
  979.         return NULL;
  980.     }
  981.  
  982.     public function loadKey($key, $isFile=FALSE, $isCert = FALSE) {
  983.         if ($isFile) {
  984.             $this->key = file_get_contents($key);
  985.         } else {
  986.             $this->key = $key;
  987.         }
  988.         if ($isCert) {
  989.             $this->key = openssl_x509_read($this->key);
  990.             openssl_x509_export($this->key, $str_cert);
  991.             $this->x509Certificate = $str_cert;
  992.             $this->key = $str_cert;
  993.         } else {
  994.             $this->x509Certificate = NULL;
  995.         }
  996.         if ($this->cryptParams['library'] == 'openssl') {
  997.             if ($this->cryptParams['type'] == 'public') {
  998.                 if ($isCert) {
  999.                     /* Load the thumbprint if this is an X509 certificate. */
  1000.                     $this->X509Thumbprint = self::getRawThumbprint($this->key);
  1001.                 }
  1002.                 $this->key = openssl_get_publickey($this->key);
  1003.             } else {
  1004.                 $this->key = openssl_get_privatekey($this->key, $this->passphrase);
  1005.             }
  1006.         } else if ($this->cryptParams['cipher'] == MCRYPT_RIJNDAEL_128) {
  1007.             /* Check key length */
  1008.             switch ($this->type) {
  1009.                 case (XMLSecurityKey::AES256_CBC):
  1010.                     if (strlen($this->key) < 25) {
  1011.                         throw new Exception('Key must contain at least 25 characters for this cipher');
  1012.                     }
  1013.                     break;
  1014.                 case (XMLSecurityKey::AES192_CBC):
  1015.                     if (strlen($this->key) < 17) {
  1016.                         throw new Exception('Key must contain at least 17 characters for this cipher');
  1017.                     }
  1018.                     break;
  1019.             }
  1020.         }
  1021.     }
  1022.  
  1023.     private function encryptMcrypt($data) {
  1024.         $td = mcrypt_module_open($this->cryptParams['cipher'], '', $this->cryptParams['mode'], '');
  1025.         $this->iv = mcrypt_create_iv (mcrypt_enc_get_iv_size($td), MCRYPT_RAND);
  1026.         mcrypt_generic_init($td, $this->key, $this->iv);
  1027.         if ($this->cryptParams['mode'] == MCRYPT_MODE_CBC) {
  1028.             $bs = mcrypt_enc_get_block_size($td);
  1029.             for ($datalen0=$datalen=strlen($data); (($datalen%$bs)!=($bs-1)); $datalen++)
  1030.                 $data.=chr(rand(1, 127));
  1031.             $data.=chr($datalen-$datalen0+1);
  1032.         }
  1033.         $encrypted_data = $this->iv.mcrypt_generic($td, $data);
  1034.         mcrypt_generic_deinit($td);
  1035.         mcrypt_module_close($td);
  1036.         return $encrypted_data;
  1037.     }
  1038.  
  1039.     private function decryptMcrypt($data) {
  1040.         $td = mcrypt_module_open($this->cryptParams['cipher'], '', $this->cryptParams['mode'], '');
  1041.         $iv_length = mcrypt_enc_get_iv_size($td);
  1042.  
  1043.         $this->iv = substr($data, 0, $iv_length);
  1044.         $data = substr($data, $iv_length);
  1045.  
  1046.         mcrypt_generic_init($td, $this->key, $this->iv);
  1047.         $decrypted_data = mdecrypt_generic($td, $data);
  1048.         mcrypt_generic_deinit($td);
  1049.         mcrypt_module_close($td);
  1050.         if ($this->cryptParams['mode'] == MCRYPT_MODE_CBC) {
  1051.             $dataLen = strlen($decrypted_data);
  1052.             $paddingLength = substr($decrypted_data, $dataLen - 1, 1);
  1053.             $decrypted_data = substr($decrypted_data, 0, $dataLen - ord($paddingLength));
  1054.         }
  1055.         return $decrypted_data;
  1056.     }
  1057.  
  1058.     private function encryptOpenSSL($data) {
  1059.         if ($this->cryptParams['type'] == 'public') {
  1060.             if (! openssl_public_encrypt($data, $encrypted_data, $this->key, $this->cryptParams['padding'])) {
  1061.                 throw new Exception('Failure encrypting Data');
  1062.                 return;
  1063.             }
  1064.         } else {
  1065.             if (! openssl_private_encrypt($data, $encrypted_data, $this->key, $this->cryptParams['padding'])) {
  1066.                 throw new Exception('Failure encrypting Data');
  1067.                 return;
  1068.             }
  1069.         }
  1070.         return $encrypted_data;
  1071.     }
  1072.  
  1073.     private function decryptOpenSSL($data) {
  1074.         if ($this->cryptParams['type'] == 'public') {
  1075.             if (! openssl_public_decrypt($data, $decrypted, $this->key, $this->cryptParams['padding'])) {
  1076.                 throw new Exception('Failure decrypting Data');
  1077.                 return;
  1078.             }
  1079.         } else {
  1080.             if (! openssl_private_decrypt($data, $decrypted, $this->key, $this->cryptParams['padding'])) {
  1081.                 throw new Exception('Failure decrypting Data');
  1082.                 return;
  1083.             }
  1084.         }
  1085.         return $decrypted;
  1086.     }
  1087.  
  1088.     private function signOpenSSL($data) {
  1089.         $algo = OPENSSL_ALGO_SHA1;
  1090.         if (! empty($this->cryptParams['digest'])) {
  1091.             $algo = $this->cryptParams['digest'];
  1092.         }
  1093.         if (! openssl_sign ($data, $signature, $this->key, $algo)) {
  1094.             throw new Exception('Failure Signing Data: ' . openssl_error_string() . ' - ' . $algo);
  1095.             return;
  1096.         }
  1097.         return $signature;
  1098.     }
  1099.  
  1100.     private function verifyOpenSSL($data, $signature) {
  1101.         $algo = OPENSSL_ALGO_SHA1;
  1102.         if (! empty($this->cryptParams['digest'])) {
  1103.             $algo = $this->cryptParams['digest'];
  1104.         }
  1105.         return openssl_verify ($data, $signature, $this->key, $algo);
  1106.     }
  1107.  
  1108.     public function encryptData($data) {
  1109.         switch ($this->cryptParams['library']) {
  1110.             case 'mcrypt':
  1111.                 return $this->encryptMcrypt($data);
  1112.                 break;
  1113.             case 'openssl':
  1114.                 return $this->encryptOpenSSL($data);
  1115.                 break;
  1116.         }
  1117.     }
  1118.  
  1119.     public function decryptData($data) {
  1120.         switch ($this->cryptParams['library']) {
  1121.             case 'mcrypt':
  1122.                 return $this->decryptMcrypt($data);
  1123.                 break;
  1124.             case 'openssl':
  1125.                 return $this->decryptOpenSSL($data);
  1126.                 break;
  1127.         }
  1128.     }
  1129.  
  1130.     public function signData($data) {
  1131.         switch ($this->cryptParams['library']) {
  1132.             case 'openssl':
  1133.                 return $this->signOpenSSL($data);
  1134.                 break;
  1135.         }
  1136.     }
  1137.  
  1138.     public function verifySignature($data, $signature) {
  1139.         switch ($this->cryptParams['library']) {
  1140.             case 'openssl':
  1141.                 return $this->verifyOpenSSL($data, $signature);
  1142.                 break;
  1143.         }
  1144.     }
  1145.  
  1146.     public function getAlgorith() {
  1147.         return $this->cryptParams['method'];
  1148.     }
  1149.  
  1150.     static function makeAsnSegment($type, $string) {
  1151.         switch ($type){
  1152.             case 0x02:
  1153.                 if (ord($string) > 0x7f)
  1154.                     $string = chr(0).$string;
  1155.                 break;
  1156.             case 0x03:
  1157.                 $string = chr(0).$string;
  1158.                 break;
  1159.         }
  1160.  
  1161.         $length = strlen($string);
  1162.  
  1163.         if ($length < 128){
  1164.            $output = sprintf("%c%c%s", $type, $length, $string);
  1165.         } else if ($length < 0x0100){
  1166.            $output = sprintf("%c%c%c%s", $type, 0x81, $length, $string);
  1167.         } else if ($length < 0x010000) {
  1168.            $output = sprintf("%c%c%c%c%s", $type, 0x82, $length/0x0100, $length%0x0100, $string);
  1169.         } else {
  1170.             $output = NULL;
  1171.         }
  1172.         return($output);
  1173.     }
  1174.  
  1175.     /* Modulus and Exponent must already be base64 decoded */
  1176.     static function convertRSA($modulus, $exponent) {
  1177.         /* make an ASN publicKeyInfo */
  1178.         $exponentEncoding = XMLSecurityKey::makeAsnSegment(0x02, $exponent);
  1179.         $modulusEncoding = XMLSecurityKey::makeAsnSegment(0x02, $modulus);
  1180.         $sequenceEncoding = XMLSecurityKey:: makeAsnSegment(0x30, $modulusEncoding.$exponentEncoding);
  1181.         $bitstringEncoding = XMLSecurityKey::makeAsnSegment(0x03, $sequenceEncoding);
  1182.         $rsaAlgorithmIdentifier = pack("H*", "300D06092A864886F70D0101010500");
  1183.         $publicKeyInfo = XMLSecurityKey::makeAsnSegment (0x30, $rsaAlgorithmIdentifier.$bitstringEncoding);
  1184.  
  1185.         /* encode the publicKeyInfo in base64 and add PEM brackets */
  1186.         $publicKeyInfoBase64 = base64_encode($publicKeyInfo);
  1187.         $encoding = "-----BEGIN PUBLIC KEY-----\n";
  1188.         $offset = 0;
  1189.         while ($segment=substr($publicKeyInfoBase64, $offset, 64)){
  1190.            $encoding = $encoding.$segment."\n";
  1191.            $offset += 64;
  1192.         }
  1193.         return $encoding."-----END PUBLIC KEY-----\n";
  1194.     }
  1195.  
  1196.     public function serializeKey($parent) {
  1197.  
  1198.     }
  1199.    
  1200.  
  1201.  
  1202.     /**
  1203.      * Retrieve the X509 certificate this key represents.
  1204.      *
  1205.      * Will return the X509 certificate in PEM-format if this key represents
  1206.      * an X509 certificate.
  1207.      *
  1208.      * @return  The X509 certificate or NULL if this key doesn't represent an X509-certificate.
  1209.      */
  1210.     public function getX509Certificate() {
  1211.         return $this->x509Certificate;
  1212.     }
  1213.  
  1214.     /* Get the thumbprint of this X509 certificate.
  1215.      *
  1216.      * Returns:
  1217.      *  The thumbprint as a lowercase 40-character hexadecimal number, or NULL
  1218.      *  if this isn't a X509 certificate.
  1219.      */
  1220.     public function getX509Thumbprint() {
  1221.         return $this->X509Thumbprint;
  1222.     }
  1223. }
  1224.  
  1225. class XMLSecurityDSig {
  1226.     const XMLDSIGNS = 'http://www.w3.org/2000/09/xmldsig#';
  1227.     const SHA1 = 'http://www.w3.org/2000/09/xmldsig#sha1';
  1228.     const SHA256 = 'http://www.w3.org/2001/04/xmlenc#sha256';
  1229.     const SHA512 = 'http://www.w3.org/2001/04/xmlenc#sha512';
  1230.     const RIPEMD160 = 'http://www.w3.org/2001/04/xmlenc#ripemd160';
  1231.  
  1232.     const C14N = 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315';
  1233.     const C14N_COMMENTS = 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments';
  1234.     const EXC_C14N = 'http://www.w3.org/2001/10/xml-exc-c14n#';
  1235.     const EXC_C14N_COMMENTS = 'http://www.w3.org/2001/10/xml-exc-c14n#WithComments';
  1236.  
  1237.     const template = '<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
  1238.  <ds:SignedInfo>
  1239.    <ds:SignatureMethod />
  1240.  </ds:SignedInfo>
  1241. </ds:Signature>';
  1242.  
  1243.     public $sigNode = NULL;
  1244.     public $idKeys = array();
  1245.     public $idNS = array();
  1246.     private $signedInfo = NULL;
  1247.     private $xPathCtx = NULL;
  1248.     private $canonicalMethod = NULL;
  1249.     private $prefix = 'ds';
  1250.     private $searchpfx = 'secdsig';
  1251.  
  1252.     /* This variable contains an associative array of validated nodes. */
  1253.     private $validatedNodes = NULL;
  1254.  
  1255.     public function __construct() {
  1256.         $sigdoc = new DOMDocument();
  1257.         $sigdoc->loadXML(XMLSecurityDSig::template);
  1258.         $this->sigNode = $sigdoc->documentElement;
  1259.     }
  1260.  
  1261.     private function resetXPathObj() {
  1262.         $this->xPathCtx = NULL;
  1263.     }
  1264.    
  1265.     private function getXPathObj() {
  1266.         if (empty($this->xPathCtx) && ! empty($this->sigNode)) {
  1267.             $xpath = new DOMXPath($this->sigNode->ownerDocument);
  1268.             $xpath->registerNamespace('secdsig', XMLSecurityDSig::XMLDSIGNS);
  1269.             $this->xPathCtx = $xpath;
  1270.         }
  1271.         return $this->xPathCtx;
  1272.     }
  1273.  
  1274.     static function generate_GUID($prefix='pfx') {
  1275.         $uuid = md5(uniqid(rand(), true));
  1276.         $guid =  $prefix.substr($uuid,0,8)."-".
  1277.                 substr($uuid,8,4)."-".
  1278.                 substr($uuid,12,4)."-".
  1279.                 substr($uuid,16,4)."-".
  1280.                 substr($uuid,20,12);
  1281.         return $guid;
  1282.     }
  1283.  
  1284.     public function locateSignature($objDoc) {
  1285.         if ($objDoc instanceof DOMDocument) {
  1286.             $doc = $objDoc;
  1287.         } else {
  1288.             $doc = $objDoc->ownerDocument;
  1289.         }
  1290.         if ($doc) {
  1291.             $xpath = new DOMXPath($doc);
  1292.             $xpath->registerNamespace('secdsig', XMLSecurityDSig::XMLDSIGNS);
  1293.             $query = ".//secdsig:Signature";
  1294.             $nodeset = $xpath->query($query, $objDoc);
  1295.             $this->sigNode = $nodeset->item(0);
  1296.             return $this->sigNode;
  1297.         }
  1298.         return NULL;
  1299.     }
  1300.  
  1301.     public function createNewSignNode($name, $value=NULL) {
  1302.         $doc = $this->sigNode->ownerDocument;
  1303.         if (! is_null($value)) {
  1304.             $node = $doc->createElementNS(XMLSecurityDSig::XMLDSIGNS, $this->prefix.':'.$name, $value);
  1305.         } else {
  1306.             $node = $doc->createElementNS(XMLSecurityDSig::XMLDSIGNS, $this->prefix.':'.$name);
  1307.         }
  1308.         return $node;
  1309.     }
  1310.  
  1311.     public function setCanonicalMethod($method) {
  1312.         switch ($method) {
  1313.             case 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315':
  1314.             case 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments':
  1315.             case 'http://www.w3.org/2001/10/xml-exc-c14n#':
  1316.             case 'http://www.w3.org/2001/10/xml-exc-c14n#WithComments':
  1317.                 $this->canonicalMethod = $method;
  1318.                 break;
  1319.             default:
  1320.                 throw new Exception('Invalid Canonical Method');
  1321.         }
  1322.         if ($xpath = $this->getXPathObj()) {
  1323.             $query = './'.$this->searchpfx.':SignedInfo';
  1324.             $nodeset = $xpath->query($query, $this->sigNode);
  1325.             if ($sinfo = $nodeset->item(0)) {
  1326.                 $query = './'.$this->searchpfx.'CanonicalizationMethod';
  1327.                 $nodeset = $xpath->query($query, $sinfo);
  1328.                 if (! ($canonNode = $nodeset->item(0))) {
  1329.                     $canonNode = $this->createNewSignNode('CanonicalizationMethod');
  1330.                     $sinfo->insertBefore($canonNode, $sinfo->firstChild);
  1331.                 }
  1332.                 $canonNode->setAttribute('Algorithm', $this->canonicalMethod);
  1333.             }
  1334.         }
  1335.     }
  1336.  
  1337.     private function canonicalizeData($node, $canonicalmethod, $arXPath=NULL, $prefixList=NULL) {
  1338.         $exclusive = FALSE;
  1339.         $withComments = FALSE;
  1340.         switch ($canonicalmethod) {
  1341.             case 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315':
  1342.                 $exclusive = FALSE;
  1343.                 $withComments = FALSE;
  1344.                 break;
  1345.             case 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments':
  1346.                 $withComments = TRUE;
  1347.                 break;
  1348.             case 'http://www.w3.org/2001/10/xml-exc-c14n#':
  1349.                 $exclusive = TRUE;
  1350.                 break;
  1351.             case 'http://www.w3.org/2001/10/xml-exc-c14n#WithComments':
  1352.                 $exclusive = TRUE;
  1353.                 $withComments = TRUE;
  1354.                 break;
  1355.         }
  1356. /* Support PHP versions < 5.2 not containing C14N methods in DOM extension */
  1357.         $php_version = explode('.', PHP_VERSION);
  1358.         if (($php_version[0] < 5) || ($php_version[0] == 5 && $php_version[1] < 2) ) {
  1359.             if (! is_null($arXPath)) {
  1360.                 throw new Exception("PHP 5.2.0 or higher is required to perform XPath Transformations");
  1361.             }
  1362.             return C14NGeneral($node, $exclusive, $withComments);
  1363.         }
  1364.         return $node->C14N($exclusive, $withComments, $arXPath, $prefixList);
  1365.     }
  1366.  
  1367.     public function canonicalizeSignedInfo() {
  1368.  
  1369.         $doc = $this->sigNode->ownerDocument;
  1370.         $canonicalmethod = NULL;
  1371.         if ($doc) {
  1372.             $xpath = $this->getXPathObj();
  1373.             $query = "./secdsig:SignedInfo";
  1374.             $nodeset = $xpath->query($query, $this->sigNode);
  1375.             if ($signInfoNode = $nodeset->item(0)) {
  1376.                 $query = "./secdsig:CanonicalizationMethod";
  1377.                 $nodeset = $xpath->query($query, $signInfoNode);
  1378.                 if ($canonNode = $nodeset->item(0)) {
  1379.                     $canonicalmethod = $canonNode->getAttribute('Algorithm');
  1380.                 }
  1381.                 $this->signedInfo = $this->canonicalizeData($signInfoNode, $canonicalmethod);
  1382.                 return $this->signedInfo;
  1383.             }
  1384.         }
  1385.         return NULL;
  1386.     }
  1387.  
  1388.     public function calculateDigest ($digestAlgorithm, $data) {
  1389.         switch ($digestAlgorithm) {
  1390.             case XMLSecurityDSig::SHA1:
  1391.                 $alg = 'sha1';
  1392.                 break;
  1393.             case XMLSecurityDSig::SHA256:
  1394.                 $alg = 'sha256';
  1395.                 break;
  1396.             case XMLSecurityDSig::SHA512:
  1397.                 $alg = 'sha512';
  1398.                 break;
  1399.             case XMLSecurityDSig::RIPEMD160:
  1400.                 $alg = 'ripemd160';
  1401.                 break;
  1402.             default:
  1403.                 throw new Exception("Cannot validate digest: Unsupported Algorith <$digestAlgorithm>");
  1404.         }
  1405.         if (function_exists('hash')) {
  1406.             return base64_encode(hash($alg, $data, TRUE));
  1407.         } elseif (function_exists('mhash')) {
  1408.             $alg = "MHASH_" . strtoupper($alg);
  1409.             return base64_encode(mhash(constant($alg), $data));
  1410.         } elseif ($alg === 'sha1') {
  1411.             return base64_encode(sha1($data, TRUE));
  1412.         } else {
  1413.             throw new Exception('xmlseclibs is unable to calculate a digest. Maybe you need the mhash library?');
  1414.         }
  1415.     }
  1416.  
  1417.     public function validateDigest($refNode, $data) {
  1418.         $xpath = new DOMXPath($refNode->ownerDocument);
  1419.         $xpath->registerNamespace('secdsig', XMLSecurityDSig::XMLDSIGNS);
  1420.         $query = 'string(./secdsig:DigestMethod/@Algorithm)';
  1421.         $digestAlgorithm = $xpath->evaluate($query, $refNode);
  1422.         $digValue = $this->calculateDigest($digestAlgorithm, $data);
  1423.         $query = 'string(./secdsig:DigestValue)';
  1424.         $digestValue = $xpath->evaluate($query, $refNode);
  1425.         return ($digValue == $digestValue);
  1426.     }
  1427.  
  1428.     public function processTransforms($refNode, $objData) {
  1429.         $data = $objData;
  1430.         $xpath = new DOMXPath($refNode->ownerDocument);
  1431.         $xpath->registerNamespace('secdsig', XMLSecurityDSig::XMLDSIGNS);
  1432.         $query = './secdsig:Transforms/secdsig:Transform';
  1433.         $nodelist = $xpath->query($query, $refNode);
  1434.         $canonicalMethod = 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315';
  1435.         $arXPath = NULL;
  1436.         $prefixList = NULL;
  1437.         foreach ($nodelist AS $transform) {
  1438.             $algorithm = $transform->getAttribute("Algorithm");
  1439.             switch ($algorithm) {
  1440.                 case 'http://www.w3.org/2001/10/xml-exc-c14n#':
  1441.                 case 'http://www.w3.org/2001/10/xml-exc-c14n#WithComments':
  1442.                     $node = $transform->firstChild;
  1443.                     while ($node) {
  1444.                         if ($node->localName == 'InclusiveNamespaces') {
  1445.                             if ($pfx = $node->getAttribute('PrefixList')) {
  1446.                                 $arpfx = array();
  1447.                                 $pfxlist = explode(" ", $pfx);
  1448.                                 foreach ($pfxlist AS $pfx) {
  1449.                                     $val = trim($pfx);
  1450.                                     if (! empty($val)) {
  1451.                                         $arpfx[] = $val;
  1452.                                     }
  1453.                                 }
  1454.                                 if (count($arpfx) > 0) {
  1455.                                     $prefixList = $arpfx;
  1456.                                 }
  1457.                             }
  1458.                             break;
  1459.                         }
  1460.                         $node = $node->nextSibling;
  1461.                     }
  1462.                 case 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315':
  1463.                 case 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments':
  1464.                     $canonicalMethod = $algorithm;
  1465.                     break;
  1466.                 case 'http://www.w3.org/TR/1999/REC-xpath-19991116':
  1467.                     $node = $transform->firstChild;
  1468.                     while ($node) {
  1469.                         if ($node->localName == 'XPath') {
  1470.                             $arXPath = array();
  1471.                             $arXPath['query'] = '(.//. | .//@* | .//namespace::*)['.$node->nodeValue.']';
  1472.                             $arXpath['namespaces'] = array();
  1473.                             $nslist = $xpath->query('./namespace::*', $node);
  1474.                             foreach ($nslist AS $nsnode) {
  1475.                                 if ($nsnode->localName != "xml") {
  1476.                                     $arXPath['namespaces'][$nsnode->localName] = $nsnode->nodeValue;
  1477.                                 }
  1478.                             }
  1479.                             break;
  1480.                         }
  1481.                         $node = $node->nextSibling;
  1482.                     }
  1483.                     break;
  1484.             }
  1485.         }
  1486.         if ($data instanceof DOMNode) {
  1487.             $data = $this->canonicalizeData($objData, $canonicalMethod, $arXPath, $prefixList);
  1488.         }
  1489.         return $data;
  1490.     }
  1491.  
  1492.     public function processRefNode($refNode) {
  1493.         $dataObject = NULL;
  1494.         if ($uri = $refNode->getAttribute("URI")) {
  1495.             $arUrl = parse_url($uri);
  1496.             if (empty($arUrl['path'])) {
  1497.                 if ($identifier = $arUrl['fragment']) {
  1498.                     $xPath = new DOMXPath($refNode->ownerDocument);
  1499.                     if ($this->idNS && is_array($this->idNS)) {
  1500.                         foreach ($this->idNS AS $nspf=>$ns) {
  1501.                             $xPath->registerNamespace($nspf, $ns);
  1502.                         }
  1503.                     }
  1504.                     $iDlist = '@Id="'.$identifier.'"';
  1505.                     if (is_array($this->idKeys)) {
  1506.                         foreach ($this->idKeys AS $idKey) {
  1507.                             $iDlist .= " or @$idKey='$identifier'";
  1508.                         }
  1509.                     }
  1510.                     $query = '//*['.$iDlist.']';
  1511.                     $dataObject = $xPath->query($query)->item(0);
  1512.                 } else {
  1513.                     $dataObject = $refNode->ownerDocument;
  1514.                 }
  1515.             } else {
  1516.                 $dataObject = file_get_contents($arUrl);
  1517.             }
  1518.         } else {
  1519.             $dataObject = $refNode->ownerDocument;
  1520.         }
  1521.         $data = $this->processTransforms($refNode, $dataObject);
  1522.         if (!$this->validateDigest($refNode, $data)) {
  1523.             return FALSE;
  1524.         }
  1525.  
  1526.         if ($dataObject instanceof DOMNode) {
  1527.             /* Add this node to the list of validated nodes. */
  1528.             if(! empty($identifier)) {
  1529.                 $this->validatedNodes[$identifier] = $dataObject;
  1530.             } else {
  1531.                 $this->validatedNodes[] = $dataObject;
  1532.             }
  1533.         }
  1534.  
  1535.         return TRUE;
  1536.     }
  1537.  
  1538.     public function getRefNodeID($refNode) {
  1539.         if ($uri = $refNode->getAttribute("URI")) {
  1540.             $arUrl = parse_url($uri);
  1541.             if (empty($arUrl['path'])) {
  1542.                 if ($identifier = $arUrl['fragment']) {
  1543.                     return $identifier;
  1544.                 }
  1545.             }
  1546.         }
  1547.         return null;
  1548.     }
  1549.  
  1550.     public function getRefIDs() {
  1551.         $refids = array();
  1552.         $doc = $this->sigNode->ownerDocument;
  1553.  
  1554.         $xpath = $this->getXPathObj();
  1555.         $query = "./secdsig:SignedInfo/secdsig:Reference";
  1556.         $nodeset = $xpath->query($query, $this->sigNode);
  1557.         if ($nodeset->length == 0) {
  1558.             throw new Exception("Reference nodes not found");
  1559.         }
  1560.         foreach ($nodeset AS $refNode) {
  1561.             $refids[] = $this->getRefNodeID($refNode);
  1562.         }
  1563.         return $refids;
  1564.     }
  1565.  
  1566.     public function validateReference() {
  1567.         $doc = $this->sigNode->ownerDocument;
  1568.         if (! $doc->isSameNode($this->sigNode)) {
  1569.             $this->sigNode->parentNode->removeChild($this->sigNode);
  1570.         }
  1571.         $xpath = $this->getXPathObj();
  1572.         $query = "./secdsig:SignedInfo/secdsig:Reference";
  1573.         $nodeset = $xpath->query($query, $this->sigNode);
  1574.         if ($nodeset->length == 0) {
  1575.             throw new Exception("Reference nodes not found");
  1576.         }
  1577.        
  1578.         /* Initialize/reset the list of validated nodes. */
  1579.         $this->validatedNodes = array();
  1580.        
  1581.         foreach ($nodeset AS $refNode) {
  1582.             if (! $this->processRefNode($refNode)) {
  1583.                 /* Clear the list of validated nodes. */
  1584.                 $this->validatedNodes = NULL;
  1585.                 throw new Exception("Reference validation failed");
  1586.             }
  1587.         }
  1588.         return TRUE;
  1589.     }
  1590.  
  1591.     private function addRefInternal($sinfoNode, $node, $algorithm, $arTransforms=NULL, $options=NULL) {
  1592.         $prefix = NULL;
  1593.         $prefix_ns = NULL;
  1594.         $id_name = 'Id';
  1595.         $overwrite_id  = TRUE;
  1596.         $force_uri = FALSE;
  1597.  
  1598.         if (is_array($options)) {
  1599.             $prefix = empty($options['prefix'])?NULL:$options['prefix'];
  1600.             $prefix_ns = empty($options['prefix_ns'])?NULL:$options['prefix_ns'];
  1601.             $id_name = empty($options['id_name'])?'Id':$options['id_name'];
  1602.             $overwrite_id = !isset($options['overwrite'])?TRUE:(bool)$options['overwrite'];
  1603.             $force_uri = !isset($options['force_uri'])?FALSE:(bool)$options['force_uri'];
  1604.         }
  1605.  
  1606.         $attname = $id_name;
  1607.         if (! empty($prefix)) {
  1608.             $attname = $prefix.':'.$attname;
  1609.         }
  1610.  
  1611.         $refNode = $this->createNewSignNode('Reference');
  1612.         $sinfoNode->appendChild($refNode);
  1613.  
  1614.         if (! $node instanceof DOMDocument) {
  1615.             $uri = NULL;
  1616.             if (! $overwrite_id) {
  1617.                 $uri = $node->getAttributeNS($prefix_ns, $attname);
  1618.             }
  1619.             if (empty($uri)) {
  1620.                 $uri = XMLSecurityDSig::generate_GUID();
  1621.                 $node->setAttributeNS($prefix_ns, $attname, $uri);
  1622.             }
  1623.             $refNode->setAttribute("URI", '#'.$uri);
  1624.         } elseif ($force_uri) {
  1625.             $refNode->setAttribute("URI", '');
  1626.         }
  1627.  
  1628.         $transNodes = $this->createNewSignNode('Transforms');
  1629.         $refNode->appendChild($transNodes);
  1630.  
  1631.         if (is_array($arTransforms)) {
  1632.             foreach ($arTransforms AS $transform) {
  1633.                 $transNode = $this->createNewSignNode('Transform');
  1634.                 $transNodes->appendChild($transNode);
  1635.                 if (is_array($transform) &&
  1636.                     (! empty($transform['http://www.w3.org/TR/1999/REC-xpath-19991116'])) &&
  1637.                     (! empty($transform['http://www.w3.org/TR/1999/REC-xpath-19991116']['query']))) {
  1638.                     $transNode->setAttribute('Algorithm', 'http://www.w3.org/TR/1999/REC-xpath-19991116');
  1639.                     $XPathNode = $this->createNewSignNode('XPath', $transform['http://www.w3.org/TR/1999/REC-xpath-19991116']['query']);
  1640.                     $transNode->appendChild($XPathNode);
  1641.                     if (! empty($transform['http://www.w3.org/TR/1999/REC-xpath-19991116']['namespaces'])) {
  1642.                         foreach ($transform['http://www.w3.org/TR/1999/REC-xpath-19991116']['namespaces'] AS $prefix => $namespace) {
  1643.                             $XPathNode->setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:$prefix", $namespace);
  1644.                         }
  1645.                     }
  1646.                 } else {
  1647.                     $transNode->setAttribute('Algorithm', $transform);
  1648.                 }
  1649.             }
  1650.         } elseif (! empty($this->canonicalMethod)) {
  1651.             $transNode = $this->createNewSignNode('Transform');
  1652.             $transNodes->appendChild($transNode);
  1653.             $transNode->setAttribute('Algorithm', $this->canonicalMethod);
  1654.         }
  1655.  
  1656.         $canonicalData = $this->processTransforms($refNode, $node);
  1657.         $digValue = $this->calculateDigest($algorithm, $canonicalData);
  1658.  
  1659.         $digestMethod = $this->createNewSignNode('DigestMethod');
  1660.         $refNode->appendChild($digestMethod);
  1661.         $digestMethod->setAttribute('Algorithm', $algorithm);
  1662.  
  1663.         $digestValue = $this->createNewSignNode('DigestValue', $digValue);
  1664.         $refNode->appendChild($digestValue);
  1665.     }
  1666.  
  1667.     public function addReference($node, $algorithm, $arTransforms=NULL, $options=NULL) {
  1668.         if ($xpath = $this->getXPathObj()) {
  1669.             $query = "./secdsig:SignedInfo";
  1670.             $nodeset = $xpath->query($query, $this->sigNode);
  1671.             if ($sInfo = $nodeset->item(0)) {
  1672.                 $this->addRefInternal($sInfo, $node, $algorithm, $arTransforms, $options);
  1673.             }
  1674.         }
  1675.     }
  1676.  
  1677.     public function addReferenceList($arNodes, $algorithm, $arTransforms=NULL, $options=NULL) {
  1678.         if ($xpath = $this->getXPathObj()) {
  1679.             $query = "./secdsig:SignedInfo";
  1680.             $nodeset = $xpath->query($query, $this->sigNode);
  1681.             if ($sInfo = $nodeset->item(0)) {
  1682.                 foreach ($arNodes AS $node) {
  1683.                     $this->addRefInternal($sInfo, $node, $algorithm, $arTransforms, $options);
  1684.                 }
  1685.             }
  1686.         }
  1687.     }
  1688.  
  1689.    public function addObject($data, $mimetype=NULL, $encoding=NULL) {
  1690.       $objNode = $this->createNewSignNode('Object');
  1691.       $this->sigNode->appendChild($objNode);
  1692.       if (! empty($mimetype)) {
  1693.          $objNode->setAtribute('MimeType', $mimetype);
  1694.       }
  1695.       if (! empty($encoding)) {
  1696.          $objNode->setAttribute('Encoding', $encoding);
  1697.       }
  1698.  
  1699.       if ($data instanceof DOMElement) {
  1700.          $newData = $this->sigNode->ownerDocument->importNode($data, TRUE);
  1701.       } else {
  1702.          $newData = $this->sigNode->ownerDocument->createTextNode($data);
  1703.       }
  1704.       $objNode->appendChild($newData);
  1705.  
  1706.       return $objNode;
  1707.    }
  1708.  
  1709.     public function locateKey($node=NULL) {
  1710.         if (empty($node)) {
  1711.             $node = $this->sigNode;
  1712.         }
  1713.         if (! $node instanceof DOMNode) {
  1714.             return NULL;
  1715.         }
  1716.         if ($doc = $node->ownerDocument) {
  1717.             $xpath = new DOMXPath($doc);
  1718.             $xpath->registerNamespace('secdsig', XMLSecurityDSig::XMLDSIGNS);
  1719.             $query = "string(./secdsig:SignedInfo/secdsig:SignatureMethod/@Algorithm)";
  1720.             $algorithm = $xpath->evaluate($query, $node);
  1721.             if ($algorithm) {
  1722.                 try {
  1723.                     $objKey = new XMLSecurityKey($algorithm, array('type'=>'public'));
  1724.                 } catch (Exception $e) {
  1725.                     return NULL;
  1726.                 }
  1727.                 return $objKey;
  1728.             }
  1729.         }
  1730.         return NULL;
  1731.     }
  1732.  
  1733.     public function verify($objKey) {
  1734.         $doc = $this->sigNode->ownerDocument;
  1735.         $xpath = new DOMXPath($doc);
  1736.         $xpath->registerNamespace('secdsig', XMLSecurityDSig::XMLDSIGNS);
  1737.         $query = "string(./secdsig:SignatureValue)";
  1738.         $sigValue = $xpath->evaluate($query, $this->sigNode);
  1739.         if (empty($sigValue)) {
  1740.             throw new Exception("Unable to locate SignatureValue");
  1741.         }
  1742.         return $objKey->verifySignature($this->signedInfo, base64_decode($sigValue));
  1743.     }
  1744.  
  1745.     public function signData($objKey, $data) {
  1746.         return $objKey->signData($data);
  1747.     }
  1748.  
  1749.     public function sign($objKey, $appendToNode = NULL) {
  1750.         // If we have a parent node append it now so C14N properly works
  1751.         if ($appendToNode != NULL) {
  1752.             $this->resetXPathObj();
  1753.             $this->appendSignature($appendToNode);
  1754.             $this->sigNode = $appendToNode->lastChild;
  1755.         }
  1756.         if ($xpath = $this->getXPathObj()) {
  1757.             $query = "./secdsig:SignedInfo";
  1758.             $nodeset = $xpath->query($query, $this->sigNode);
  1759.             if ($sInfo = $nodeset->item(0)) {
  1760.                 $query = "./secdsig:SignatureMethod";
  1761.                 $nodeset = $xpath->query($query, $sInfo);
  1762.                 $sMethod = $nodeset->item(0);
  1763.                 $sMethod->setAttribute('Algorithm', $objKey->type);
  1764.                 $data = $this->canonicalizeData($sInfo, $this->canonicalMethod);
  1765.                 $sigValue = base64_encode($this->signData($objKey, $data));
  1766.                 $sigValueNode = $this->createNewSignNode('SignatureValue', $sigValue);
  1767.                 if ($infoSibling = $sInfo->nextSibling) {
  1768.                     $infoSibling->parentNode->insertBefore($sigValueNode, $infoSibling);
  1769.                 } else {
  1770.                     $this->sigNode->appendChild($sigValueNode);
  1771.                 }
  1772.             }
  1773.         }
  1774.     }
  1775.  
  1776.     public function appendCert() {
  1777.  
  1778.     }
  1779.  
  1780.     public function appendKey($objKey, $parent=NULL) {
  1781.         $objKey->serializeKey($parent);
  1782.     }
  1783.  
  1784.  
  1785.     /**
  1786.      * This function inserts the signature element.
  1787.      *
  1788.      * The signature element will be appended to the element, unless $beforeNode is specified. If $beforeNode
  1789.      * is specified, the signature element will be inserted as the last element before $beforeNode.
  1790.      *
  1791.      * @param $node  The node the signature element should be inserted into.
  1792.      * @param $beforeNode  The node the signature element should be located before.
  1793.      */
  1794.     public function insertSignature($node, $beforeNode = NULL) {
  1795.  
  1796.         $document = $node->ownerDocument;
  1797.         $signatureElement = $document->importNode($this->sigNode, TRUE);
  1798.  
  1799.         if($beforeNode == NULL) {
  1800.             $node->insertBefore($signatureElement);
  1801.         } else {
  1802.             $node->insertBefore($signatureElement, $beforeNode);
  1803.         }
  1804.     }
  1805.  
  1806.     public function appendSignature($parentNode, $insertBefore = FALSE) {
  1807.         $beforeNode = $insertBefore ? $parentNode->firstChild : NULL;
  1808.         $this->insertSignature($parentNode, $beforeNode);
  1809.     }
  1810.  
  1811.     static function get509XCert($cert, $isPEMFormat=TRUE) {
  1812.         $certs = XMLSecurityDSig::staticGet509XCerts($cert, $isPEMFormat);
  1813.         if (! empty($certs)) {
  1814.             return $certs[0];
  1815.         }
  1816.         return '';
  1817.     }
  1818.  
  1819.     static function staticGet509XCerts($certs, $isPEMFormat=TRUE) {
  1820.         if ($isPEMFormat) {
  1821.             $data = '';
  1822.             $certlist = array();
  1823.             $arCert = explode("\n", $certs);
  1824.             $inData = FALSE;
  1825.             foreach ($arCert AS $curData) {
  1826.                 if (! $inData) {
  1827.                     if (strncmp($curData, '-----BEGIN CERTIFICATE', 22) == 0) {
  1828.                         $inData = TRUE;
  1829.                     }
  1830.                 } else {
  1831.                     if (strncmp($curData, '-----END CERTIFICATE', 20) == 0) {
  1832.                         $inData = FALSE;
  1833.                         $certlist[] = $data;
  1834.                         $data = '';
  1835.                         continue;
  1836.                     }
  1837.                     $data .= trim($curData);
  1838.                 }
  1839.             }
  1840.             return $certlist;
  1841.         } else {
  1842.             return array($certs);
  1843.         }
  1844.     }
  1845.  
  1846.     static function staticAdd509Cert($parentRef, $cert, $isPEMFormat=TRUE, $isURL=False, $xpath=NULL) {
  1847.           if ($isURL) {
  1848.             $cert = file_get_contents($cert);
  1849.           }
  1850.           if (! $parentRef instanceof DOMElement) {
  1851.             throw new Exception('Invalid parent Node parameter');
  1852.           }
  1853.           $baseDoc = $parentRef->ownerDocument;
  1854.  
  1855.           if (empty($xpath)) {
  1856.               $xpath = new DOMXPath($parentRef->ownerDocument);
  1857.               $xpath->registerNamespace('secdsig', XMLSecurityDSig::XMLDSIGNS);
  1858.           }
  1859.  
  1860.          $query = "./secdsig:KeyInfo";
  1861.          $nodeset = $xpath->query($query, $parentRef);
  1862.          $keyInfo = $nodeset->item(0);
  1863.          if (! $keyInfo) {
  1864.               $inserted = FALSE;
  1865.               $keyInfo = $baseDoc->createElementNS(XMLSecurityDSig::XMLDSIGNS, 'ds:KeyInfo');
  1866.  
  1867.                $query = "./secdsig:Object";
  1868.                $nodeset = $xpath->query($query, $parentRef);
  1869.                if ($sObject = $nodeset->item(0)) {
  1870.                     $sObject->parentNode->insertBefore($keyInfo, $sObject);
  1871.                     $inserted = TRUE;
  1872.                }
  1873.  
  1874.               if (! $inserted) {
  1875.                    $parentRef->appendChild($keyInfo);
  1876.               }
  1877.          }
  1878.  
  1879.          // Add all certs if there are more than one
  1880.          $certs = XMLSecurityDSig::staticGet509XCerts($cert, $isPEMFormat);
  1881.  
  1882.          // Atach X509 data node
  1883.          $x509DataNode = $baseDoc->createElementNS(XMLSecurityDSig::XMLDSIGNS, 'ds:X509Data');
  1884.          $keyInfo->appendChild($x509DataNode);
  1885.  
  1886.          // Atach all certificate nodes
  1887.          foreach ($certs as $X509Cert){
  1888.             $x509CertNode = $baseDoc->createElementNS(XMLSecurityDSig::XMLDSIGNS, 'ds:X509Certificate', $X509Cert);
  1889.          $x509DataNode->appendChild($x509CertNode);
  1890.          }
  1891.      }
  1892.  
  1893.     public function add509Cert($cert, $isPEMFormat=TRUE, $isURL=False) {
  1894.          if ($xpath = $this->getXPathObj()) {
  1895.             self::staticAdd509Cert($this->sigNode, $cert, $isPEMFormat, $isURL, $xpath);
  1896.          }
  1897.     }
  1898.    
  1899.     /* This function retrieves an associative array of the validated nodes.
  1900.      *
  1901.      * The array will contain the id of the referenced node as the key and the node itself
  1902.      * as the value.
  1903.      *
  1904.      * Returns:
  1905.      *  An associative array of validated nodes or NULL if no nodes have been validated.
  1906.      */
  1907.     public function getValidatedNodes() {
  1908.         return $this->validatedNodes;
  1909.     }
  1910. }
  1911.  
  1912. class XMLSecEnc {
  1913.     const template = "<xenc:EncryptedData xmlns:xenc='http://www.w3.org/2001/04/xmlenc#'>
  1914.   <xenc:CipherData>
  1915.      <xenc:CipherValue></xenc:CipherValue>
  1916.   </xenc:CipherData>
  1917. </xenc:EncryptedData>";
  1918.  
  1919.     const Element = 'http://www.w3.org/2001/04/xmlenc#Element';
  1920.     const Content = 'http://www.w3.org/2001/04/xmlenc#Content';
  1921.     const URI = 3;
  1922.     const XMLENCNS = 'http://www.w3.org/2001/04/xmlenc#';
  1923.  
  1924.     private $encdoc = NULL;
  1925.     private $rawNode = NULL;
  1926.     public $type = NULL;
  1927.     public $encKey = NULL;
  1928.     private $references = array();
  1929.  
  1930.     public function __construct() {
  1931.         $this->_resetTemplate();
  1932.     }
  1933.  
  1934.     private function _resetTemplate(){
  1935.         $this->encdoc = new DOMDocument();
  1936.         $this->encdoc->loadXML(XMLSecEnc::template);
  1937.     }
  1938.  
  1939.     public function addReference($name, $node, $type) {
  1940.         if (! $node instanceOf DOMNode) {
  1941.             throw new Exception('$node is not of type DOMNode');
  1942.         }
  1943.         $curencdoc = $this->encdoc;
  1944.         $this->_resetTemplate();
  1945.         $encdoc = $this->encdoc;
  1946.         $this->encdoc = $curencdoc;
  1947.         $refuri = XMLSecurityDSig::generate_GUID();
  1948.         $element = $encdoc->documentElement;
  1949.         $element->setAttribute("Id", $refuri);
  1950.         $this->references[$name] = array("node" => $node, "type" => $type, "encnode" => $encdoc, "refuri" => $refuri);
  1951.     }
  1952.  
  1953.     public function setNode($node) {
  1954.         $this->rawNode = $node;
  1955.     }
  1956.  
  1957.     public function encryptNode($objKey, $replace=TRUE) {
  1958.         $data = '';
  1959.         if (empty($this->rawNode)) {
  1960.             throw new Exception('Node to encrypt has not been set');
  1961.         }
  1962.         if (! $objKey instanceof XMLSecurityKey) {
  1963.             throw new Exception('Invalid Key');
  1964.         }
  1965.         $doc = $this->rawNode->ownerDocument;
  1966.         $xPath = new DOMXPath($this->encdoc);
  1967.         $objList = $xPath->query('/xenc:EncryptedData/xenc:CipherData/xenc:CipherValue');
  1968.         $cipherValue = $objList->item(0);
  1969.         if ($cipherValue == NULL) {
  1970.             throw new Exception('Error locating CipherValue element within template');
  1971.         }
  1972.         switch ($this->type) {
  1973.             case (XMLSecEnc::Element):
  1974.                 $data = $doc->saveXML($this->rawNode);
  1975.                 $this->encdoc->documentElement->setAttribute('Type', XMLSecEnc::Element);
  1976.                 break;
  1977.             case (XMLSecEnc::Content):
  1978.                 $children = $this->rawNode->childNodes;
  1979.                 foreach ($children AS $child) {
  1980.                     $data .= $doc->saveXML($child);
  1981.                 }
  1982.                 $this->encdoc->documentElement->setAttribute('Type', XMLSecEnc::Content);
  1983.                 break;
  1984.             default:
  1985.                 throw new Exception('Type is currently not supported');
  1986.                 return;
  1987.         }
  1988.  
  1989.         $encMethod = $this->encdoc->documentElement->appendChild($this->encdoc->createElementNS(XMLSecEnc::XMLENCNS, 'xenc:EncryptionMethod'));
  1990.         $encMethod->setAttribute('Algorithm', $objKey->getAlgorith());
  1991.         $cipherValue->parentNode->parentNode->insertBefore($encMethod, $cipherValue->parentNode);
  1992.  
  1993.         $strEncrypt = base64_encode($objKey->encryptData($data));
  1994.         $value = $this->encdoc->createTextNode($strEncrypt);
  1995.         $cipherValue->appendChild($value);
  1996.  
  1997.         if ($replace) {
  1998.             switch ($this->type) {
  1999.                 case (XMLSecEnc::Element):
  2000.                     if ($this->rawNode->nodeType == XML_DOCUMENT_NODE) {
  2001.                         return $this->encdoc;
  2002.                     }
  2003.                     $importEnc = $this->rawNode->ownerDocument->importNode($this->encdoc->documentElement, TRUE);
  2004.                     $this->rawNode->parentNode->replaceChild($importEnc, $this->rawNode);
  2005.                     return $importEnc;
  2006.                     break;
  2007.                 case (XMLSecEnc::Content):
  2008.                     $importEnc = $this->rawNode->ownerDocument->importNode($this->encdoc->documentElement, TRUE);
  2009.                     while($this->rawNode->firstChild) {
  2010.                         $this->rawNode->removeChild($this->rawNode->firstChild);
  2011.                     }
  2012.                     $this->rawNode->appendChild($importEnc);
  2013.                     return $importEnc;
  2014.                     break;
  2015.             }
  2016.         }
  2017.     }
  2018.  
  2019.     public function encryptReferences($objKey) {
  2020.         $curRawNode = $this->rawNode;
  2021.         $curType = $this->type;
  2022.         foreach ($this->references AS $name=>$reference) {
  2023.             $this->encdoc = $reference["encnode"];
  2024.             $this->rawNode = $reference["node"];
  2025.             $this->type = $reference["type"];
  2026.             try {
  2027.                 $encNode = $this->encryptNode($objKey);
  2028.                 $this->references[$name]["encnode"] = $encNode;
  2029.             } catch (Exception $e) {
  2030.                 $this->rawNode = $curRawNode;
  2031.                 $this->type = $curType;
  2032.                 throw $e;
  2033.             }
  2034.         }
  2035.         $this->rawNode = $curRawNode;
  2036.         $this->type = $curType;
  2037.     }
  2038.  
  2039.     public function decryptNode($objKey, $replace=TRUE) {
  2040.         $data = '';
  2041.         if (empty($this->rawNode)) {
  2042.             throw new Exception('Node to decrypt has not been set');
  2043.         }
  2044.         if (! $objKey instanceof XMLSecurityKey) {
  2045.             throw new Exception('Invalid Key');
  2046.         }
  2047.         $doc = $this->rawNode->ownerDocument;
  2048.         $xPath = new DOMXPath($doc);
  2049.         $xPath->registerNamespace('xmlencr', XMLSecEnc::XMLENCNS);
  2050.         /* Only handles embedded content right now and not a reference */
  2051.         $query = "./xmlencr:CipherData/xmlencr:CipherValue";
  2052.         $nodeset = $xPath->query($query, $this->rawNode);
  2053.  
  2054.         if ($node = $nodeset->item(0)) {
  2055.             $encryptedData = base64_decode($node->nodeValue);
  2056.             $decrypted = $objKey->decryptData($encryptedData);
  2057.             if ($replace) {
  2058.                 switch ($this->type) {
  2059.                     case (XMLSecEnc::Element):
  2060.                         $newdoc = new DOMDocument();
  2061.                         $newdoc->loadXML($decrypted);
  2062.                         if ($this->rawNode->nodeType == XML_DOCUMENT_NODE) {
  2063.                             return $newdoc;
  2064.                         }
  2065.                         $importEnc = $this->rawNode->ownerDocument->importNode($newdoc->documentElement, TRUE);
  2066.                         $this->rawNode->parentNode->replaceChild($importEnc, $this->rawNode);
  2067.                         return $importEnc;
  2068.                         break;
  2069.                     case (XMLSecEnc::Content):
  2070.                         if ($this->rawNode->nodeType == XML_DOCUMENT_NODE) {
  2071.                             $doc = $this->rawNode;
  2072.                         } else {
  2073.                             $doc = $this->rawNode->ownerDocument;
  2074.                         }
  2075.                         $newFrag = $doc->createDocumentFragment();
  2076.                         $newFrag->appendXML($decrypted);
  2077.                         $parent = $this->rawNode->parentNode;
  2078.                         $parent->replaceChild($newFrag, $this->rawNode);
  2079.                         return $parent;
  2080.                         break;
  2081.                     default:
  2082.                         return $decrypted;
  2083.                 }
  2084.             } else {
  2085.                 return $decrypted;
  2086.             }
  2087.         } else {
  2088.             throw new Exception("Cannot locate encrypted data");
  2089.         }
  2090.     }
  2091.  
  2092.     public function encryptKey($srcKey, $rawKey, $append=TRUE) {
  2093.         if ((! $srcKey instanceof XMLSecurityKey) || (! $rawKey instanceof XMLSecurityKey)) {
  2094.             throw new Exception('Invalid Key');
  2095.         }
  2096.         $strEncKey = base64_encode($srcKey->encryptData($rawKey->key));
  2097.         $root = $this->encdoc->documentElement;
  2098.         $encKey = $this->encdoc->createElementNS(XMLSecEnc::XMLENCNS, 'xenc:EncryptedKey');
  2099.         if ($append) {
  2100.             $keyInfo = $root->appendChild($this->encdoc->createElementNS('http://www.w3.org/2000/09/xmldsig#', 'dsig:KeyInfo'));
  2101.             $keyInfo->appendChild($encKey);
  2102.         } else {
  2103.             $this->encKey = $encKey;
  2104.         }
  2105.         $encMethod = $encKey->appendChild($this->encdoc->createElementNS(XMLSecEnc::XMLENCNS, 'xenc:EncryptionMethod'));
  2106.         $encMethod->setAttribute('Algorithm', $srcKey->getAlgorith());
  2107.         if (! empty($srcKey->name)) {
  2108.             $keyInfo = $encKey->appendChild($this->encdoc->createElementNS('http://www.w3.org/2000/09/xmldsig#', 'dsig:KeyInfo'));
  2109.             $keyInfo->appendChild($this->encdoc->createElementNS('http://www.w3.org/2000/09/xmldsig#', 'dsig:KeyName', $srcKey->name));
  2110.         }
  2111.         $cipherData = $encKey->appendChild($this->encdoc->createElementNS(XMLSecEnc::XMLENCNS, 'xenc:CipherData'));
  2112.         $cipherData->appendChild($this->encdoc->createElementNS(XMLSecEnc::XMLENCNS, 'xenc:CipherValue', $strEncKey));
  2113.         if (is_array($this->references) && count($this->references) > 0) {
  2114.            $refList =  $encKey->appendChild($this->encdoc->createElementNS(XMLSecEnc::XMLENCNS, 'xenc:ReferenceList'));
  2115.             foreach ($this->references AS $name=>$reference) {
  2116.                 $refuri = $reference["refuri"];
  2117.                 $dataRef = $refList->appendChild($this->encdoc->createElementNS(XMLSecEnc::XMLENCNS, 'xenc:DataReference'));
  2118.                 $dataRef->setAttribute("URI", '#' . $refuri);
  2119.             }
  2120.         }
  2121.         return;
  2122.     }
  2123.  
  2124.     public function decryptKey($encKey) {
  2125.         if (! $encKey->isEncrypted) {
  2126.             throw new Exception("Key is not Encrypted");
  2127.         }
  2128.         if (empty($encKey->key)) {
  2129.             throw new Exception("Key is missing data to perform the decryption");
  2130.         }
  2131.         return $this->decryptNode($encKey, FALSE);
  2132.     }
  2133.  
  2134.     public function locateEncryptedData($element) {
  2135.         if ($element instanceof DOMDocument) {
  2136.             $doc = $element;
  2137.         } else {
  2138.             $doc = $element->ownerDocument;
  2139.         }
  2140.         if ($doc) {
  2141.             $xpath = new DOMXPath($doc);
  2142.             $query = "//*[local-name()='EncryptedData' and namespace-uri()='".XMLSecEnc::XMLENCNS."']";
  2143.             $nodeset = $xpath->query($query);
  2144.             return $nodeset->item(0);
  2145.         }
  2146.         return NULL;
  2147.     }
  2148.  
  2149.     public function locateKey($node=NULL) {
  2150.         if (empty($node)) {
  2151.             $node = $this->rawNode;
  2152.         }
  2153.         if (! $node instanceof DOMNode) {
  2154.             return NULL;
  2155.         }
  2156.         if ($doc = $node->ownerDocument) {
  2157.             $xpath = new DOMXPath($doc);
  2158.             $xpath->registerNamespace('xmlsecenc', XMLSecEnc::XMLENCNS);
  2159.             $query = ".//xmlsecenc:EncryptionMethod";
  2160.             $nodeset = $xpath->query($query, $node);
  2161.             if ($encmeth = $nodeset->item(0)) {
  2162.                    $attrAlgorithm = $encmeth->getAttribute("Algorithm");
  2163.                 try {
  2164.                     $objKey = new XMLSecurityKey($attrAlgorithm, array('type'=>'private'));
  2165.                 } catch (Exception $e) {
  2166.                     return NULL;
  2167.                 }
  2168.                 return $objKey;
  2169.             }
  2170.         }
  2171.         return NULL;
  2172.     }
  2173.  
  2174.     static function staticLocateKeyInfo($objBaseKey=NULL, $node=NULL) {
  2175.         if (empty($node) || (! $node instanceof DOMNode)) {
  2176.             return NULL;
  2177.         }
  2178.         if ($doc = $node->ownerDocument) {
  2179.             $xpath = new DOMXPath($doc);
  2180.             $xpath->registerNamespace('xmlsecenc', XMLSecEnc::XMLENCNS);
  2181.             $xpath->registerNamespace('xmlsecdsig', XMLSecurityDSig::XMLDSIGNS);
  2182.             $query = "./xmlsecdsig:KeyInfo";
  2183.             $nodeset = $xpath->query($query, $node);
  2184.             if ($encmeth = $nodeset->item(0)) {
  2185.                 foreach ($encmeth->childNodes AS $child) {
  2186.                     switch ($child->localName) {
  2187.                         case 'KeyName':
  2188.                             if (! empty($objBaseKey)) {
  2189.                                 $objBaseKey->name = $child->nodeValue;
  2190.                             }
  2191.                             break;
  2192.                         case 'KeyValue':
  2193.                             foreach ($child->childNodes AS $keyval) {
  2194.                                 switch ($keyval->localName) {
  2195.                                     case 'DSAKeyValue':
  2196.                                         throw new Exception("DSAKeyValue currently not supported");
  2197.                                         break;
  2198.                                     case 'RSAKeyValue':
  2199.                                         $modulus = NULL;
  2200.                                         $exponent = NULL;
  2201.                                         if ($modulusNode = $keyval->getElementsByTagName('Modulus')->item(0)) {
  2202.                                             $modulus = base64_decode($modulusNode->nodeValue);
  2203.                                         }
  2204.                                         if ($exponentNode = $keyval->getElementsByTagName('Exponent')->item(0)) {
  2205.                                             $exponent = base64_decode($exponentNode->nodeValue);
  2206.                                         }
  2207.                                         if (empty($modulus) || empty($exponent)) {
  2208.                                             throw new Exception("Missing Modulus or Exponent");
  2209.                                         }
  2210.                                         $publicKey = XMLSecurityKey::convertRSA($modulus, $exponent);
  2211.                                         $objBaseKey->loadKey($publicKey);
  2212.                                         break;
  2213.                                 }
  2214.                             }
  2215.                             break;
  2216.                         case 'RetrievalMethod':
  2217.                             /* Not currently supported */
  2218.                             break;
  2219.                         case 'EncryptedKey':
  2220.                             $objenc = new XMLSecEnc();
  2221.                             $objenc->setNode($child);
  2222.                             if (! $objKey = $objenc->locateKey()) {
  2223.                                 throw new Exception("Unable to locate algorithm for this Encrypted Key");
  2224.                             }
  2225.                             $objKey->isEncrypted = TRUE;
  2226.                             $objKey->encryptedCtx = $objenc;
  2227.                             XMLSecEnc::staticLocateKeyInfo($objKey, $child);
  2228.                             return $objKey;
  2229.                             break;
  2230.                         case 'X509Data':
  2231.                             if ($x509certNodes = $child->getElementsByTagName('X509Certificate')) {
  2232.                                 if ($x509certNodes->length > 0) {
  2233.                                     $x509cert = $x509certNodes->item(0)->textContent;
  2234.                                     $x509cert = str_replace(array("\r", "\n"), "", $x509cert);
  2235.                                     $x509cert = "-----BEGIN CERTIFICATE-----\n".chunk_split($x509cert, 64, "\n")."-----END CERTIFICATE-----\n";
  2236.                                     $objBaseKey->loadKey($x509cert, FALSE, TRUE);
  2237.                                 }
  2238.                             }
  2239.                             break;
  2240.                     }
  2241.                 }
  2242.             }
  2243.             return $objBaseKey;
  2244.         }
  2245.         return NULL;
  2246.     }
  2247.  
  2248.     public function locateKeyInfo($objBaseKey=NULL, $node=NULL) {
  2249.         if (empty($node)) {
  2250.             $node = $this->rawNode;
  2251.         }
  2252.         return XMLSecEnc::staticLocateKeyInfo($objBaseKey, $node);
  2253.     }
  2254. }
  2255.  
  2256.  /*
  2257.  * soap-server-wsse.php
  2258.  *
  2259.  * Copyright (c) 2007, Robert Richards <rrichards@ctindustries.net>.
  2260.  * All rights reserved.
  2261.  *
  2262.  * Redistribution and use in source and binary forms, with or without
  2263.  * modification, are permitted provided that the following conditions
  2264.  * are met:
  2265.  *
  2266.  *   * Redistributions of source code must retain the above copyright
  2267.  *     notice, this list of conditions and the following disclaimer.
  2268.  *
  2269.  *   * Redistributions in binary form must reproduce the above copyright
  2270.  *     notice, this list of conditions and the following disclaimer in
  2271.  *     the documentation and/or other materials provided with the
  2272.  *     distribution.
  2273.  *
  2274.  *   * Neither the name of Robert Richards nor the names of his
  2275.  *     contributors may be used to endorse or promote products derived
  2276.  *     from this software without specific prior written permission.
  2277.  *
  2278.  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  2279.  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  2280.  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  2281.  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  2282.  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  2283.  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  2284.  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  2285.  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  2286.  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  2287.  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
  2288.  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  2289.  * POSSIBILITY OF SUCH DAMAGE.
  2290.  *
  2291.  * @author     Robert Richards <rrichards@ctindustries.net>
  2292.  * @copyright  2007 Robert Richards <rrichards@ctindustries.net>
  2293.  * @license    http://www.opensource.org/licenses/bsd-license.php  BSD License
  2294.  * @version    1.0.0
  2295.  */
  2296.  
  2297. class WSSESoapServer {
  2298.     const WSSENS = 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd';
  2299.     const WSSENS_2003 = 'http://schemas.xmlsoap.org/ws/2003/06/secext';
  2300.     const WSUNS = 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd';
  2301.     const WSSEPFX = 'wsse';
  2302.     const WSUPFX = 'wsu';
  2303.     private $soapNS, $soapPFX;
  2304.     private $soapDoc = NULL;
  2305.     private $envelope = NULL;
  2306.     private $SOAPXPath = NULL;
  2307.     private $secNode = NULL;
  2308.     public $signAllHeaders = FALSE;
  2309.  
  2310.     private function locateSecurityHeader($setActor=NULL) {
  2311.         $wsNamespace = NULL;
  2312.         if ($this->secNode == NULL) {
  2313.             $headers = $this->SOAPXPath->query('//wssoap:Envelope/wssoap:Header');
  2314.             if ($header = $headers->item(0)) {
  2315.                 $secnodes = $this->SOAPXPath->query('./*[local-name()="Security"]', $header);
  2316.                 $secnode = NULL;
  2317.                 foreach ($secnodes AS $node) {
  2318.                     $nsURI = $node->namespaceURI;
  2319.                     if (($nsURI == self::WSSENS) || ($nsURI == self::WSSENS_2003)) {
  2320.                         $actor = $node->getAttributeNS($this->soapNS, 'actor');
  2321.                         if (empty($actor) || ($actor == $setActor)) {
  2322.                             $secnode = $node;
  2323.                             $wsNamespace = $nsURI;
  2324.                             break;
  2325.                         }
  2326.                     }
  2327.                 }
  2328.             }
  2329.             $this->secNode = $secnode;
  2330.         }
  2331.         return $wsNamespace;
  2332.     }
  2333.  
  2334.     public function __construct($doc) {
  2335.         $this->soapDoc = $doc;
  2336.         $this->envelope = $doc->documentElement;
  2337.         $this->soapNS = $this->envelope->namespaceURI;
  2338.         $this->soapPFX = $this->envelope->prefix;
  2339.         $this->SOAPXPath = new DOMXPath($doc);
  2340.         $this->SOAPXPath->registerNamespace('wssoap', $this->soapNS);
  2341.         $this->SOAPXPath->registerNamespace('wswsu', WSSESoapServer::WSUNS);
  2342.         $wsNamespace = $this->locateSecurityHeader();
  2343.         if (! empty($wsNamespace)) {
  2344.             $this->SOAPXPath->registerNamespace('wswsse', $wsNamespace);
  2345.         }
  2346.     }
  2347.  
  2348.     public function processSignature($refNode) {
  2349.         $objXMLSecDSig = new XMLSecurityDSig();
  2350.         $objXMLSecDSig->idKeys[] = 'wswsu:Id';
  2351.         $objXMLSecDSig->idNS['wswsu'] = WSSESoapServer::WSUNS;
  2352.         $objXMLSecDSig->sigNode = $refNode;
  2353.  
  2354.         /* Canonicalize the signed info */
  2355.         $objXMLSecDSig->canonicalizeSignedInfo();
  2356.  
  2357.         $retVal = $objXMLSecDSig->validateReference();
  2358.  
  2359.         if (! $retVal) {
  2360.             throw new Exception("Validation Failed");
  2361.         }
  2362.  
  2363.         $key = NULL;
  2364.         $objKey = $objXMLSecDSig->locateKey();
  2365.  
  2366.         if ($objKey) {
  2367.             if ($objKeyInfo = XMLSecEnc::staticLocateKeyInfo($objKey, $refNode)) {
  2368.                 /* Handle any additional key processing such as encrypted keys here */
  2369.             }
  2370.         }
  2371.  
  2372.         if (empty($objKey)) {
  2373.             throw new Exception("Error loading key to handle Signature");
  2374.         }
  2375.         do {
  2376.             if (empty($objKey->key)) {
  2377.                 $this->SOAPXPath->registerNamespace('xmlsecdsig', XMLSecurityDSig::XMLDSIGNS);
  2378.                 $query = "./xmlsecdsig:KeyInfo/wswsse:SecurityTokenReference/wswsse:Reference";
  2379.                 $nodeset = $this->SOAPXPath->query($query, $refNode);
  2380.                 if ($encmeth = $nodeset->item(0)) {
  2381.                     if ($uri = $encmeth->getAttribute("URI")) {
  2382.                         $arUrl = parse_url($uri);
  2383.                         if (empty($arUrl['path']) && ($identifier = $arUrl['fragment'])) {
  2384.                             $query = '//wswsse:BinarySecurityToken[@wswsu:Id="'.$identifier.'"]';
  2385.                             $nodeset = $this->SOAPXPath->query($query);
  2386.                             if ($encmeth = $nodeset->item(0)) {
  2387.                                 $x509cert = $encmeth->textContent;
  2388.                                 $x509cert = str_replace(array("\r", "\n"), "", $x509cert);
  2389.                                 $x509cert = "-----BEGIN CERTIFICATE-----\n".chunk_split($x509cert, 64, "\n")."-----END CERTIFICATE-----\n";
  2390.                                 $objKey->loadKey($x509cert);
  2391.                                 break;
  2392.                             }
  2393.                         }
  2394.                     }
  2395.                 }
  2396.                 throw new Exception("Error loading key to handle Signature");
  2397.             }
  2398.         } while(0);
  2399.  
  2400.         if (! $objXMLSecDSig->verify($objKey)) {
  2401.             throw new Exception("Unable to validate Signature");
  2402.         }
  2403.  
  2404.         return TRUE;
  2405.     }
  2406.  
  2407.     public function process() {
  2408.         if (empty($this->secNode)) {
  2409.             return;
  2410.         }
  2411.         $node = $this->secNode->firstChild;
  2412.         while ($node) {
  2413.             $nextNode = $node->nextSibling;
  2414.             switch ($node->localName) {
  2415.                 case "Signature":
  2416.                     if ($this->processSignature($node)) {
  2417.                         if ($node->parentNode) {
  2418.                             $node->parentNode->removeChild($node);
  2419.                         }
  2420.                     } else {
  2421.                         /* throw fault */
  2422.                         return FALSE;
  2423.                     }
  2424.             }
  2425.             $node = $nextNode;
  2426.         }
  2427.         $this->secNode->parentNode->removeChild($this->secNode);
  2428.         $this->secNode = NULL;
  2429.         return TRUE;
  2430.     }
  2431.      
  2432.     public function saveXML() {
  2433.         return $this->soapDoc->saveXML();
  2434.     }
  2435.  
  2436.     public function save($file) {
  2437.         return $this->soapDoc->save($file);
  2438.     }
  2439. }
Add Comment
Please, Sign In to add comment