/trunk/plugins/viathinksoft/publicPages/100_whois/whois/xml/vendor/robrichards/xmlseclibs/src/Utils/XPath.php |
---|
0,0 → 1,44 |
<?php |
namespace RobRichards\XMLSecLibs\Utils; |
class XPath |
{ |
const ALPHANUMERIC = '\w\d'; |
const NUMERIC = '\d'; |
const LETTERS = '\w'; |
const EXTENDED_ALPHANUMERIC = '\w\d\s\-_:\.'; |
const SINGLE_QUOTE = '\''; |
const DOUBLE_QUOTE = '"'; |
const ALL_QUOTES = '[\'"]'; |
/** |
* Filter an attribute value for save inclusion in an XPath query. |
* |
* @param string $value The value to filter. |
* @param string $quotes The quotes used to delimit the value in the XPath query. |
* |
* @return string The filtered attribute value. |
*/ |
public static function filterAttrValue($value, $quotes = self::ALL_QUOTES) |
{ |
return preg_replace('#'.$quotes.'#', '', $value); |
} |
/** |
* Filter an attribute name for save inclusion in an XPath query. |
* |
* @param string $name The attribute name to filter. |
* @param mixed $allow The set of characters to allow. Can be one of the constants provided by this class, or a |
* custom regex excluding the '#' character (used as delimiter). |
* |
* @return string The filtered attribute name. |
*/ |
public static function filterAttrName($name, $allow = self::EXTENDED_ALPHANUMERIC) |
{ |
return preg_replace('#[^'.$allow.']#', '', $name); |
} |
} |
/trunk/plugins/viathinksoft/publicPages/100_whois/whois/xml/vendor/robrichards/xmlseclibs/src/XMLSecEnc.php |
---|
0,0 → 1,511 |
<?php |
namespace RobRichards\XMLSecLibs; |
use DOMDocument; |
use DOMElement; |
use DOMNode; |
use DOMXPath; |
use Exception; |
use RobRichards\XMLSecLibs\Utils\XPath as XPath; |
/** |
* xmlseclibs.php |
* |
* Copyright (c) 2007-2020, Robert Richards <rrichards@cdatazone.org>. |
* All rights reserved. |
* |
* Redistribution and use in source and binary forms, with or without |
* modification, are permitted provided that the following conditions |
* are met: |
* |
* * Redistributions of source code must retain the above copyright |
* notice, this list of conditions and the following disclaimer. |
* |
* * Redistributions in binary form must reproduce the above copyright |
* notice, this list of conditions and the following disclaimer in |
* the documentation and/or other materials provided with the |
* distribution. |
* |
* * Neither the name of Robert Richards nor the names of his |
* contributors may be used to endorse or promote products derived |
* from this software without specific prior written permission. |
* |
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
* POSSIBILITY OF SUCH DAMAGE. |
* |
* @author Robert Richards <rrichards@cdatazone.org> |
* @copyright 2007-2020 Robert Richards <rrichards@cdatazone.org> |
* @license http://www.opensource.org/licenses/bsd-license.php BSD License |
*/ |
class XMLSecEnc |
{ |
const template = "<xenc:EncryptedData xmlns:xenc='http://www.w3.org/2001/04/xmlenc#'> |
<xenc:CipherData> |
<xenc:CipherValue></xenc:CipherValue> |
</xenc:CipherData> |
</xenc:EncryptedData>"; |
const Element = 'http://www.w3.org/2001/04/xmlenc#Element'; |
const Content = 'http://www.w3.org/2001/04/xmlenc#Content'; |
const URI = 3; |
const XMLENCNS = 'http://www.w3.org/2001/04/xmlenc#'; |
/** @var null|DOMDocument */ |
private $encdoc = null; |
/** @var null|DOMNode */ |
private $rawNode = null; |
/** @var null|string */ |
public $type = null; |
/** @var null|DOMElement */ |
public $encKey = null; |
/** @var array */ |
private $references = array(); |
public function __construct() |
{ |
$this->_resetTemplate(); |
} |
private function _resetTemplate() |
{ |
$this->encdoc = new DOMDocument(); |
$this->encdoc->loadXML(self::template); |
} |
/** |
* @param string $name |
* @param DOMNode $node |
* @param string $type |
* @throws Exception |
*/ |
public function addReference($name, $node, $type) |
{ |
if (! $node instanceOf DOMNode) { |
throw new Exception('$node is not of type DOMNode'); |
} |
$curencdoc = $this->encdoc; |
$this->_resetTemplate(); |
$encdoc = $this->encdoc; |
$this->encdoc = $curencdoc; |
$refuri = XMLSecurityDSig::generateGUID(); |
$element = $encdoc->documentElement; |
$element->setAttribute("Id", $refuri); |
$this->references[$name] = array("node" => $node, "type" => $type, "encnode" => $encdoc, "refuri" => $refuri); |
} |
/** |
* @param DOMNode $node |
*/ |
public function setNode($node) |
{ |
$this->rawNode = $node; |
} |
/** |
* Encrypt the selected node with the given key. |
* |
* @param XMLSecurityKey $objKey The encryption key and algorithm. |
* @param bool $replace Whether the encrypted node should be replaced in the original tree. Default is true. |
* @throws Exception |
* |
* @return DOMElement The <xenc:EncryptedData>-element. |
*/ |
public function encryptNode($objKey, $replace = true) |
{ |
$data = ''; |
if (empty($this->rawNode)) { |
throw new Exception('Node to encrypt has not been set'); |
} |
if (! $objKey instanceof XMLSecurityKey) { |
throw new Exception('Invalid Key'); |
} |
$doc = $this->rawNode->ownerDocument; |
$xPath = new DOMXPath($this->encdoc); |
$objList = $xPath->query('/xenc:EncryptedData/xenc:CipherData/xenc:CipherValue'); |
$cipherValue = $objList->item(0); |
if ($cipherValue == null) { |
throw new Exception('Error locating CipherValue element within template'); |
} |
switch ($this->type) { |
case (self::Element): |
$data = $doc->saveXML($this->rawNode); |
$this->encdoc->documentElement->setAttribute('Type', self::Element); |
break; |
case (self::Content): |
$children = $this->rawNode->childNodes; |
foreach ($children AS $child) { |
$data .= $doc->saveXML($child); |
} |
$this->encdoc->documentElement->setAttribute('Type', self::Content); |
break; |
default: |
throw new Exception('Type is currently not supported'); |
} |
$encMethod = $this->encdoc->documentElement->appendChild($this->encdoc->createElementNS(self::XMLENCNS, 'xenc:EncryptionMethod')); |
$encMethod->setAttribute('Algorithm', $objKey->getAlgorithm()); |
$cipherValue->parentNode->parentNode->insertBefore($encMethod, $cipherValue->parentNode->parentNode->firstChild); |
$strEncrypt = base64_encode($objKey->encryptData($data)); |
$value = $this->encdoc->createTextNode($strEncrypt); |
$cipherValue->appendChild($value); |
if ($replace) { |
switch ($this->type) { |
case (self::Element): |
if ($this->rawNode->nodeType == XML_DOCUMENT_NODE) { |
return $this->encdoc; |
} |
$importEnc = $this->rawNode->ownerDocument->importNode($this->encdoc->documentElement, true); |
$this->rawNode->parentNode->replaceChild($importEnc, $this->rawNode); |
return $importEnc; |
case (self::Content): |
$importEnc = $this->rawNode->ownerDocument->importNode($this->encdoc->documentElement, true); |
while ($this->rawNode->firstChild) { |
$this->rawNode->removeChild($this->rawNode->firstChild); |
} |
$this->rawNode->appendChild($importEnc); |
return $importEnc; |
} |
} else { |
return $this->encdoc->documentElement; |
} |
} |
/** |
* @param XMLSecurityKey $objKey |
* @throws Exception |
*/ |
public function encryptReferences($objKey) |
{ |
$curRawNode = $this->rawNode; |
$curType = $this->type; |
foreach ($this->references AS $name => $reference) { |
$this->encdoc = $reference["encnode"]; |
$this->rawNode = $reference["node"]; |
$this->type = $reference["type"]; |
try { |
$encNode = $this->encryptNode($objKey); |
$this->references[$name]["encnode"] = $encNode; |
} catch (Exception $e) { |
$this->rawNode = $curRawNode; |
$this->type = $curType; |
throw $e; |
} |
} |
$this->rawNode = $curRawNode; |
$this->type = $curType; |
} |
/** |
* Retrieve the CipherValue text from this encrypted node. |
* |
* @throws Exception |
* @return string|null The Ciphervalue text, or null if no CipherValue is found. |
*/ |
public function getCipherValue() |
{ |
if (empty($this->rawNode)) { |
throw new Exception('Node to decrypt has not been set'); |
} |
$doc = $this->rawNode->ownerDocument; |
$xPath = new DOMXPath($doc); |
$xPath->registerNamespace('xmlencr', self::XMLENCNS); |
/* Only handles embedded content right now and not a reference */ |
$query = "./xmlencr:CipherData/xmlencr:CipherValue"; |
$nodeset = $xPath->query($query, $this->rawNode); |
$node = $nodeset->item(0); |
if (!$node) { |
return null; |
} |
return base64_decode($node->nodeValue); |
} |
/** |
* Decrypt this encrypted node. |
* |
* The behaviour of this function depends on the value of $replace. |
* If $replace is false, we will return the decrypted data as a string. |
* If $replace is true, we will insert the decrypted element(s) into the |
* document, and return the decrypted element(s). |
* |
* @param XMLSecurityKey $objKey The decryption key that should be used when decrypting the node. |
* @param boolean $replace Whether we should replace the encrypted node in the XML document with the decrypted data. The default is true. |
* |
* @return string|DOMElement The decrypted data. |
*/ |
public function decryptNode($objKey, $replace=true) |
{ |
if (! $objKey instanceof XMLSecurityKey) { |
throw new Exception('Invalid Key'); |
} |
$encryptedData = $this->getCipherValue(); |
if ($encryptedData) { |
$decrypted = $objKey->decryptData($encryptedData); |
if ($replace) { |
switch ($this->type) { |
case (self::Element): |
$newdoc = new DOMDocument(); |
$newdoc->loadXML($decrypted); |
if ($this->rawNode->nodeType == XML_DOCUMENT_NODE) { |
return $newdoc; |
} |
$importEnc = $this->rawNode->ownerDocument->importNode($newdoc->documentElement, true); |
$this->rawNode->parentNode->replaceChild($importEnc, $this->rawNode); |
return $importEnc; |
case (self::Content): |
if ($this->rawNode->nodeType == XML_DOCUMENT_NODE) { |
$doc = $this->rawNode; |
} else { |
$doc = $this->rawNode->ownerDocument; |
} |
$newFrag = $doc->createDocumentFragment(); |
$newFrag->appendXML($decrypted); |
$parent = $this->rawNode->parentNode; |
$parent->replaceChild($newFrag, $this->rawNode); |
return $parent; |
default: |
return $decrypted; |
} |
} else { |
return $decrypted; |
} |
} else { |
throw new Exception("Cannot locate encrypted data"); |
} |
} |
/** |
* Encrypt the XMLSecurityKey |
* |
* @param XMLSecurityKey $srcKey |
* @param XMLSecurityKey $rawKey |
* @param bool $append |
* @throws Exception |
*/ |
public function encryptKey($srcKey, $rawKey, $append=true) |
{ |
if ((! $srcKey instanceof XMLSecurityKey) || (! $rawKey instanceof XMLSecurityKey)) { |
throw new Exception('Invalid Key'); |
} |
$strEncKey = base64_encode($srcKey->encryptData($rawKey->key)); |
$root = $this->encdoc->documentElement; |
$encKey = $this->encdoc->createElementNS(self::XMLENCNS, 'xenc:EncryptedKey'); |
if ($append) { |
$keyInfo = $root->insertBefore($this->encdoc->createElementNS('http://www.w3.org/2000/09/xmldsig#', 'dsig:KeyInfo'), $root->firstChild); |
$keyInfo->appendChild($encKey); |
} else { |
$this->encKey = $encKey; |
} |
$encMethod = $encKey->appendChild($this->encdoc->createElementNS(self::XMLENCNS, 'xenc:EncryptionMethod')); |
$encMethod->setAttribute('Algorithm', $srcKey->getAlgorith()); |
if (! empty($srcKey->name)) { |
$keyInfo = $encKey->appendChild($this->encdoc->createElementNS('http://www.w3.org/2000/09/xmldsig#', 'dsig:KeyInfo')); |
$keyInfo->appendChild($this->encdoc->createElementNS('http://www.w3.org/2000/09/xmldsig#', 'dsig:KeyName', $srcKey->name)); |
} |
$cipherData = $encKey->appendChild($this->encdoc->createElementNS(self::XMLENCNS, 'xenc:CipherData')); |
$cipherData->appendChild($this->encdoc->createElementNS(self::XMLENCNS, 'xenc:CipherValue', $strEncKey)); |
if (is_array($this->references) && count($this->references) > 0) { |
$refList = $encKey->appendChild($this->encdoc->createElementNS(self::XMLENCNS, 'xenc:ReferenceList')); |
foreach ($this->references AS $name => $reference) { |
$refuri = $reference["refuri"]; |
$dataRef = $refList->appendChild($this->encdoc->createElementNS(self::XMLENCNS, 'xenc:DataReference')); |
$dataRef->setAttribute("URI", '#' . $refuri); |
} |
} |
return; |
} |
/** |
* @param XMLSecurityKey $encKey |
* @return DOMElement|string |
* @throws Exception |
*/ |
public function decryptKey($encKey) |
{ |
if (! $encKey->isEncrypted) { |
throw new Exception("Key is not Encrypted"); |
} |
if (empty($encKey->key)) { |
throw new Exception("Key is missing data to perform the decryption"); |
} |
return $this->decryptNode($encKey, false); |
} |
/** |
* @param DOMDocument $element |
* @return DOMNode|null |
*/ |
public function locateEncryptedData($element) |
{ |
if ($element instanceof DOMDocument) { |
$doc = $element; |
} else { |
$doc = $element->ownerDocument; |
} |
if ($doc) { |
$xpath = new DOMXPath($doc); |
$query = "//*[local-name()='EncryptedData' and namespace-uri()='".self::XMLENCNS."']"; |
$nodeset = $xpath->query($query); |
return $nodeset->item(0); |
} |
return null; |
} |
/** |
* Returns the key from the DOM |
* @param null|DOMNode $node |
* @return null|XMLSecurityKey |
*/ |
public function locateKey($node=null) |
{ |
if (empty($node)) { |
$node = $this->rawNode; |
} |
if (! $node instanceof DOMNode) { |
return null; |
} |
if ($doc = $node->ownerDocument) { |
$xpath = new DOMXPath($doc); |
$xpath->registerNamespace('xmlsecenc', self::XMLENCNS); |
$query = ".//xmlsecenc:EncryptionMethod"; |
$nodeset = $xpath->query($query, $node); |
if ($encmeth = $nodeset->item(0)) { |
$attrAlgorithm = $encmeth->getAttribute("Algorithm"); |
try { |
$objKey = new XMLSecurityKey($attrAlgorithm, array('type' => 'private')); |
} catch (Exception $e) { |
return null; |
} |
return $objKey; |
} |
} |
return null; |
} |
/** |
* @param null|XMLSecurityKey $objBaseKey |
* @param null|DOMNode $node |
* @return null|XMLSecurityKey |
* @throws Exception |
*/ |
public static function staticLocateKeyInfo($objBaseKey=null, $node=null) |
{ |
if (empty($node) || (! $node instanceof DOMNode)) { |
return null; |
} |
$doc = $node->ownerDocument; |
if (!$doc) { |
return null; |
} |
$xpath = new DOMXPath($doc); |
$xpath->registerNamespace('xmlsecenc', self::XMLENCNS); |
$xpath->registerNamespace('xmlsecdsig', XMLSecurityDSig::XMLDSIGNS); |
$query = "./xmlsecdsig:KeyInfo"; |
$nodeset = $xpath->query($query, $node); |
$encmeth = $nodeset->item(0); |
if (!$encmeth) { |
/* No KeyInfo in EncryptedData / EncryptedKey. */ |
return $objBaseKey; |
} |
foreach ($encmeth->childNodes AS $child) { |
switch ($child->localName) { |
case 'KeyName': |
if (! empty($objBaseKey)) { |
$objBaseKey->name = $child->nodeValue; |
} |
break; |
case 'KeyValue': |
foreach ($child->childNodes AS $keyval) { |
switch ($keyval->localName) { |
case 'DSAKeyValue': |
throw new Exception("DSAKeyValue currently not supported"); |
case 'RSAKeyValue': |
$modulus = null; |
$exponent = null; |
if ($modulusNode = $keyval->getElementsByTagName('Modulus')->item(0)) { |
$modulus = base64_decode($modulusNode->nodeValue); |
} |
if ($exponentNode = $keyval->getElementsByTagName('Exponent')->item(0)) { |
$exponent = base64_decode($exponentNode->nodeValue); |
} |
if (empty($modulus) || empty($exponent)) { |
throw new Exception("Missing Modulus or Exponent"); |
} |
$publicKey = XMLSecurityKey::convertRSA($modulus, $exponent); |
$objBaseKey->loadKey($publicKey); |
break; |
} |
} |
break; |
case 'RetrievalMethod': |
$type = $child->getAttribute('Type'); |
if ($type !== 'http://www.w3.org/2001/04/xmlenc#EncryptedKey') { |
/* Unsupported key type. */ |
break; |
} |
$uri = $child->getAttribute('URI'); |
if ($uri[0] !== '#') { |
/* URI not a reference - unsupported. */ |
break; |
} |
$id = substr($uri, 1); |
$query = '//xmlsecenc:EncryptedKey[@Id="'.XPath::filterAttrValue($id, XPath::DOUBLE_QUOTE).'"]'; |
$keyElement = $xpath->query($query)->item(0); |
if (!$keyElement) { |
throw new Exception("Unable to locate EncryptedKey with @Id='$id'."); |
} |
return XMLSecurityKey::fromEncryptedKeyElement($keyElement); |
case 'EncryptedKey': |
return XMLSecurityKey::fromEncryptedKeyElement($child); |
case 'X509Data': |
if ($x509certNodes = $child->getElementsByTagName('X509Certificate')) { |
if ($x509certNodes->length > 0) { |
$x509cert = $x509certNodes->item(0)->textContent; |
$x509cert = str_replace(array("\r", "\n", " "), "", $x509cert); |
$x509cert = "-----BEGIN CERTIFICATE-----\n".chunk_split($x509cert, 64, "\n")."-----END CERTIFICATE-----\n"; |
$objBaseKey->loadKey($x509cert, false, true); |
} |
} |
break; |
} |
} |
return $objBaseKey; |
} |
/** |
* @param null|XMLSecurityKey $objBaseKey |
* @param null|DOMNode $node |
* @return null|XMLSecurityKey |
*/ |
public function locateKeyInfo($objBaseKey=null, $node=null) |
{ |
if (empty($node)) { |
$node = $this->rawNode; |
} |
return self::staticLocateKeyInfo($objBaseKey, $node); |
} |
} |
/trunk/plugins/viathinksoft/publicPages/100_whois/whois/xml/vendor/robrichards/xmlseclibs/src/XMLSecurityDSig.php |
---|
0,0 → 1,1162 |
<?php |
namespace RobRichards\XMLSecLibs; |
use DOMDocument; |
use DOMElement; |
use DOMNode; |
use DOMXPath; |
use Exception; |
use RobRichards\XMLSecLibs\Utils\XPath as XPath; |
/** |
* xmlseclibs.php |
* |
* Copyright (c) 2007-2020, Robert Richards <rrichards@cdatazone.org>. |
* All rights reserved. |
* |
* Redistribution and use in source and binary forms, with or without |
* modification, are permitted provided that the following conditions |
* are met: |
* |
* * Redistributions of source code must retain the above copyright |
* notice, this list of conditions and the following disclaimer. |
* |
* * Redistributions in binary form must reproduce the above copyright |
* notice, this list of conditions and the following disclaimer in |
* the documentation and/or other materials provided with the |
* distribution. |
* |
* * Neither the name of Robert Richards nor the names of his |
* contributors may be used to endorse or promote products derived |
* from this software without specific prior written permission. |
* |
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
* POSSIBILITY OF SUCH DAMAGE. |
* |
* @author Robert Richards <rrichards@cdatazone.org> |
* @copyright 2007-2020 Robert Richards <rrichards@cdatazone.org> |
* @license http://www.opensource.org/licenses/bsd-license.php BSD License |
*/ |
class XMLSecurityDSig |
{ |
const XMLDSIGNS = 'http://www.w3.org/2000/09/xmldsig#'; |
const SHA1 = 'http://www.w3.org/2000/09/xmldsig#sha1'; |
const SHA256 = 'http://www.w3.org/2001/04/xmlenc#sha256'; |
const SHA384 = 'http://www.w3.org/2001/04/xmldsig-more#sha384'; |
const SHA512 = 'http://www.w3.org/2001/04/xmlenc#sha512'; |
const RIPEMD160 = 'http://www.w3.org/2001/04/xmlenc#ripemd160'; |
const C14N = 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315'; |
const C14N_COMMENTS = 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments'; |
const EXC_C14N = 'http://www.w3.org/2001/10/xml-exc-c14n#'; |
const EXC_C14N_COMMENTS = 'http://www.w3.org/2001/10/xml-exc-c14n#WithComments'; |
const template = '<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#"> |
<ds:SignedInfo> |
<ds:SignatureMethod /> |
</ds:SignedInfo> |
</ds:Signature>'; |
const BASE_TEMPLATE = '<Signature xmlns="http://www.w3.org/2000/09/xmldsig#"> |
<SignedInfo> |
<SignatureMethod /> |
</SignedInfo> |
</Signature>'; |
/** @var DOMElement|null */ |
public $sigNode = null; |
/** @var array */ |
public $idKeys = array(); |
/** @var array */ |
public $idNS = array(); |
/** @var string|null */ |
private $signedInfo = null; |
/** @var DomXPath|null */ |
private $xPathCtx = null; |
/** @var string|null */ |
private $canonicalMethod = null; |
/** @var string */ |
private $prefix = ''; |
/** @var string */ |
private $searchpfx = 'secdsig'; |
/** |
* This variable contains an associative array of validated nodes. |
* @var array|null |
*/ |
private $validatedNodes = null; |
/** |
* @param string $prefix |
*/ |
public function __construct($prefix='ds') |
{ |
$template = self::BASE_TEMPLATE; |
if (! empty($prefix)) { |
$this->prefix = $prefix.':'; |
$search = array("<S", "</S", "xmlns="); |
$replace = array("<$prefix:S", "</$prefix:S", "xmlns:$prefix="); |
$template = str_replace($search, $replace, $template); |
} |
$sigdoc = new DOMDocument(); |
$sigdoc->loadXML($template); |
$this->sigNode = $sigdoc->documentElement; |
} |
/** |
* Reset the XPathObj to null |
*/ |
private function resetXPathObj() |
{ |
$this->xPathCtx = null; |
} |
/** |
* Returns the XPathObj or null if xPathCtx is set and sigNode is empty. |
* |
* @return DOMXPath|null |
*/ |
private function getXPathObj() |
{ |
if (empty($this->xPathCtx) && ! empty($this->sigNode)) { |
$xpath = new DOMXPath($this->sigNode->ownerDocument); |
$xpath->registerNamespace('secdsig', self::XMLDSIGNS); |
$this->xPathCtx = $xpath; |
} |
return $this->xPathCtx; |
} |
/** |
* Generate guid |
* |
* @param string $prefix Prefix to use for guid. defaults to pfx |
* |
* @return string The generated guid |
*/ |
public static function generateGUID($prefix='pfx') |
{ |
$uuid = md5(uniqid(mt_rand(), true)); |
$guid = $prefix.substr($uuid, 0, 8)."-". |
substr($uuid, 8, 4)."-". |
substr($uuid, 12, 4)."-". |
substr($uuid, 16, 4)."-". |
substr($uuid, 20, 12); |
return $guid; |
} |
/** |
* Generate guid |
* |
* @param string $prefix Prefix to use for guid. defaults to pfx |
* |
* @return string The generated guid |
* |
* @deprecated Method deprecated in Release 1.4.1 |
*/ |
public static function generate_GUID($prefix='pfx') |
{ |
return self::generateGUID($prefix); |
} |
/** |
* @param DOMDocument $objDoc |
* @param int $pos |
* @return DOMNode|null |
*/ |
public function locateSignature($objDoc, $pos=0) |
{ |
if ($objDoc instanceof DOMDocument) { |
$doc = $objDoc; |
} else { |
$doc = $objDoc->ownerDocument; |
} |
if ($doc) { |
$xpath = new DOMXPath($doc); |
$xpath->registerNamespace('secdsig', self::XMLDSIGNS); |
$query = ".//secdsig:Signature"; |
$nodeset = $xpath->query($query, $objDoc); |
$this->sigNode = $nodeset->item($pos); |
$query = "./secdsig:SignedInfo"; |
$nodeset = $xpath->query($query, $this->sigNode); |
if ($nodeset->length > 1) { |
throw new Exception("Invalid structure - Too many SignedInfo elements found"); |
} |
return $this->sigNode; |
} |
return null; |
} |
/** |
* @param string $name |
* @param null|string $value |
* @return DOMElement |
*/ |
public function createNewSignNode($name, $value=null) |
{ |
$doc = $this->sigNode->ownerDocument; |
if (! is_null($value)) { |
$node = $doc->createElementNS(self::XMLDSIGNS, $this->prefix.$name, $value); |
} else { |
$node = $doc->createElementNS(self::XMLDSIGNS, $this->prefix.$name); |
} |
return $node; |
} |
/** |
* @param string $method |
* @throws Exception |
*/ |
public function setCanonicalMethod($method) |
{ |
switch ($method) { |
case 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315': |
case 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments': |
case 'http://www.w3.org/2001/10/xml-exc-c14n#': |
case 'http://www.w3.org/2001/10/xml-exc-c14n#WithComments': |
$this->canonicalMethod = $method; |
break; |
default: |
throw new Exception('Invalid Canonical Method'); |
} |
if ($xpath = $this->getXPathObj()) { |
$query = './'.$this->searchpfx.':SignedInfo'; |
$nodeset = $xpath->query($query, $this->sigNode); |
if ($sinfo = $nodeset->item(0)) { |
$query = './'.$this->searchpfx.'CanonicalizationMethod'; |
$nodeset = $xpath->query($query, $sinfo); |
if (! ($canonNode = $nodeset->item(0))) { |
$canonNode = $this->createNewSignNode('CanonicalizationMethod'); |
$sinfo->insertBefore($canonNode, $sinfo->firstChild); |
} |
$canonNode->setAttribute('Algorithm', $this->canonicalMethod); |
} |
} |
} |
/** |
* @param DOMNode $node |
* @param string $canonicalmethod |
* @param null|array $arXPath |
* @param null|array $prefixList |
* @return string |
*/ |
private function canonicalizeData($node, $canonicalmethod, $arXPath=null, $prefixList=null) |
{ |
$exclusive = false; |
$withComments = false; |
switch ($canonicalmethod) { |
case 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315': |
$exclusive = false; |
$withComments = false; |
break; |
case 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments': |
$withComments = true; |
break; |
case 'http://www.w3.org/2001/10/xml-exc-c14n#': |
$exclusive = true; |
break; |
case 'http://www.w3.org/2001/10/xml-exc-c14n#WithComments': |
$exclusive = true; |
$withComments = true; |
break; |
} |
if (is_null($arXPath) && ($node instanceof DOMNode) && ($node->ownerDocument !== null) && $node->isSameNode($node->ownerDocument->documentElement)) { |
/* Check for any PI or comments as they would have been excluded */ |
$element = $node; |
while ($refnode = $element->previousSibling) { |
if ($refnode->nodeType == XML_PI_NODE || (($refnode->nodeType == XML_COMMENT_NODE) && $withComments)) { |
break; |
} |
$element = $refnode; |
} |
if ($refnode == null) { |
$node = $node->ownerDocument; |
} |
} |
return $node->C14N($exclusive, $withComments, $arXPath, $prefixList); |
} |
/** |
* @return null|string |
*/ |
public function canonicalizeSignedInfo() |
{ |
$doc = $this->sigNode->ownerDocument; |
$canonicalmethod = null; |
if ($doc) { |
$xpath = $this->getXPathObj(); |
$query = "./secdsig:SignedInfo"; |
$nodeset = $xpath->query($query, $this->sigNode); |
if ($nodeset->length > 1) { |
throw new Exception("Invalid structure - Too many SignedInfo elements found"); |
} |
if ($signInfoNode = $nodeset->item(0)) { |
$query = "./secdsig:CanonicalizationMethod"; |
$nodeset = $xpath->query($query, $signInfoNode); |
$prefixList = null; |
if ($canonNode = $nodeset->item(0)) { |
$canonicalmethod = $canonNode->getAttribute('Algorithm'); |
foreach ($canonNode->childNodes as $node) |
{ |
if ($node->localName == 'InclusiveNamespaces') { |
if ($pfx = $node->getAttribute('PrefixList')) { |
$arpfx = array_filter(explode(' ', $pfx)); |
if (count($arpfx) > 0) { |
$prefixList = array_merge($prefixList ? $prefixList : array(), $arpfx); |
} |
} |
} |
} |
} |
$this->signedInfo = $this->canonicalizeData($signInfoNode, $canonicalmethod, null, $prefixList); |
return $this->signedInfo; |
} |
} |
return null; |
} |
/** |
* @param string $digestAlgorithm |
* @param string $data |
* @param bool $encode |
* @return string |
* @throws Exception |
*/ |
public function calculateDigest($digestAlgorithm, $data, $encode = true) |
{ |
switch ($digestAlgorithm) { |
case self::SHA1: |
$alg = 'sha1'; |
break; |
case self::SHA256: |
$alg = 'sha256'; |
break; |
case self::SHA384: |
$alg = 'sha384'; |
break; |
case self::SHA512: |
$alg = 'sha512'; |
break; |
case self::RIPEMD160: |
$alg = 'ripemd160'; |
break; |
default: |
throw new Exception("Cannot validate digest: Unsupported Algorithm <$digestAlgorithm>"); |
} |
$digest = hash($alg, $data, true); |
if ($encode) { |
$digest = base64_encode($digest); |
} |
return $digest; |
} |
/** |
* @param $refNode |
* @param string $data |
* @return bool |
*/ |
public function validateDigest($refNode, $data) |
{ |
$xpath = new DOMXPath($refNode->ownerDocument); |
$xpath->registerNamespace('secdsig', self::XMLDSIGNS); |
$query = 'string(./secdsig:DigestMethod/@Algorithm)'; |
$digestAlgorithm = $xpath->evaluate($query, $refNode); |
$digValue = $this->calculateDigest($digestAlgorithm, $data, false); |
$query = 'string(./secdsig:DigestValue)'; |
$digestValue = $xpath->evaluate($query, $refNode); |
return ($digValue === base64_decode($digestValue)); |
} |
/** |
* @param $refNode |
* @param DOMNode $objData |
* @param bool $includeCommentNodes |
* @return string |
*/ |
public function processTransforms($refNode, $objData, $includeCommentNodes = true) |
{ |
$data = $objData; |
$xpath = new DOMXPath($refNode->ownerDocument); |
$xpath->registerNamespace('secdsig', self::XMLDSIGNS); |
$query = './secdsig:Transforms/secdsig:Transform'; |
$nodelist = $xpath->query($query, $refNode); |
$canonicalMethod = 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315'; |
$arXPath = null; |
$prefixList = null; |
foreach ($nodelist AS $transform) { |
$algorithm = $transform->getAttribute("Algorithm"); |
switch ($algorithm) { |
case 'http://www.w3.org/2001/10/xml-exc-c14n#': |
case 'http://www.w3.org/2001/10/xml-exc-c14n#WithComments': |
if (!$includeCommentNodes) { |
/* We remove comment nodes by forcing it to use a canonicalization |
* without comments. |
*/ |
$canonicalMethod = 'http://www.w3.org/2001/10/xml-exc-c14n#'; |
} else { |
$canonicalMethod = $algorithm; |
} |
$node = $transform->firstChild; |
while ($node) { |
if ($node->localName == 'InclusiveNamespaces') { |
if ($pfx = $node->getAttribute('PrefixList')) { |
$arpfx = array(); |
$pfxlist = explode(" ", $pfx); |
foreach ($pfxlist AS $pfx) { |
$val = trim($pfx); |
if (! empty($val)) { |
$arpfx[] = $val; |
} |
} |
if (count($arpfx) > 0) { |
$prefixList = $arpfx; |
} |
} |
break; |
} |
$node = $node->nextSibling; |
} |
break; |
case 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315': |
case 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments': |
if (!$includeCommentNodes) { |
/* We remove comment nodes by forcing it to use a canonicalization |
* without comments. |
*/ |
$canonicalMethod = 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315'; |
} else { |
$canonicalMethod = $algorithm; |
} |
break; |
case 'http://www.w3.org/TR/1999/REC-xpath-19991116': |
$node = $transform->firstChild; |
while ($node) { |
if ($node->localName == 'XPath') { |
$arXPath = array(); |
$arXPath['query'] = '(.//. | .//@* | .//namespace::*)['.$node->nodeValue.']'; |
$arXPath['namespaces'] = array(); |
$nslist = $xpath->query('./namespace::*', $node); |
foreach ($nslist AS $nsnode) { |
if ($nsnode->localName != "xml") { |
$arXPath['namespaces'][$nsnode->localName] = $nsnode->nodeValue; |
} |
} |
break; |
} |
$node = $node->nextSibling; |
} |
break; |
} |
} |
if ($data instanceof DOMNode) { |
$data = $this->canonicalizeData($objData, $canonicalMethod, $arXPath, $prefixList); |
} |
return $data; |
} |
/** |
* @param DOMNode $refNode |
* @return bool |
*/ |
public function processRefNode($refNode) |
{ |
$dataObject = null; |
/* |
* Depending on the URI, we may not want to include comments in the result |
* See: http://www.w3.org/TR/xmldsig-core/#sec-ReferenceProcessingModel |
*/ |
$includeCommentNodes = true; |
if ($uri = $refNode->getAttribute("URI")) { |
$arUrl = parse_url($uri); |
if (empty($arUrl['path'])) { |
if ($identifier = $arUrl['fragment']) { |
/* This reference identifies a node with the given id by using |
* a URI on the form "#identifier". This should not include comments. |
*/ |
$includeCommentNodes = false; |
$xPath = new DOMXPath($refNode->ownerDocument); |
if ($this->idNS && is_array($this->idNS)) { |
foreach ($this->idNS as $nspf => $ns) { |
$xPath->registerNamespace($nspf, $ns); |
} |
} |
$iDlist = '@Id="'.XPath::filterAttrValue($identifier, XPath::DOUBLE_QUOTE).'"'; |
if (is_array($this->idKeys)) { |
foreach ($this->idKeys as $idKey) { |
$iDlist .= " or @".XPath::filterAttrName($idKey).'="'. |
XPath::filterAttrValue($identifier, XPath::DOUBLE_QUOTE).'"'; |
} |
} |
$query = '//*['.$iDlist.']'; |
$dataObject = $xPath->query($query)->item(0); |
} else { |
$dataObject = $refNode->ownerDocument; |
} |
} |
} else { |
/* This reference identifies the root node with an empty URI. This should |
* not include comments. |
*/ |
$includeCommentNodes = false; |
$dataObject = $refNode->ownerDocument; |
} |
$data = $this->processTransforms($refNode, $dataObject, $includeCommentNodes); |
if (!$this->validateDigest($refNode, $data)) { |
return false; |
} |
if ($dataObject instanceof DOMNode) { |
/* Add this node to the list of validated nodes. */ |
if (! empty($identifier)) { |
$this->validatedNodes[$identifier] = $dataObject; |
} else { |
$this->validatedNodes[] = $dataObject; |
} |
} |
return true; |
} |
/** |
* @param DOMNode $refNode |
* @return null |
*/ |
public function getRefNodeID($refNode) |
{ |
if ($uri = $refNode->getAttribute("URI")) { |
$arUrl = parse_url($uri); |
if (empty($arUrl['path'])) { |
if ($identifier = $arUrl['fragment']) { |
return $identifier; |
} |
} |
} |
return null; |
} |
/** |
* @return array |
* @throws Exception |
*/ |
public function getRefIDs() |
{ |
$refids = array(); |
$xpath = $this->getXPathObj(); |
$query = "./secdsig:SignedInfo[1]/secdsig:Reference"; |
$nodeset = $xpath->query($query, $this->sigNode); |
if ($nodeset->length == 0) { |
throw new Exception("Reference nodes not found"); |
} |
foreach ($nodeset AS $refNode) { |
$refids[] = $this->getRefNodeID($refNode); |
} |
return $refids; |
} |
/** |
* @return bool |
* @throws Exception |
*/ |
public function validateReference() |
{ |
$docElem = $this->sigNode->ownerDocument->documentElement; |
if (! $docElem->isSameNode($this->sigNode)) { |
if ($this->sigNode->parentNode != null) { |
$this->sigNode->parentNode->removeChild($this->sigNode); |
} |
} |
$xpath = $this->getXPathObj(); |
$query = "./secdsig:SignedInfo[1]/secdsig:Reference"; |
$nodeset = $xpath->query($query, $this->sigNode); |
if ($nodeset->length == 0) { |
throw new Exception("Reference nodes not found"); |
} |
/* Initialize/reset the list of validated nodes. */ |
$this->validatedNodes = array(); |
foreach ($nodeset AS $refNode) { |
if (! $this->processRefNode($refNode)) { |
/* Clear the list of validated nodes. */ |
$this->validatedNodes = null; |
throw new Exception("Reference validation failed"); |
} |
} |
return true; |
} |
/** |
* @param DOMNode $sinfoNode |
* @param DOMDocument $node |
* @param string $algorithm |
* @param null|array $arTransforms |
* @param null|array $options |
*/ |
private function addRefInternal($sinfoNode, $node, $algorithm, $arTransforms=null, $options=null) |
{ |
$prefix = null; |
$prefix_ns = null; |
$id_name = 'Id'; |
$overwrite_id = true; |
$force_uri = false; |
if (is_array($options)) { |
$prefix = empty($options['prefix']) ? null : $options['prefix']; |
$prefix_ns = empty($options['prefix_ns']) ? null : $options['prefix_ns']; |
$id_name = empty($options['id_name']) ? 'Id' : $options['id_name']; |
$overwrite_id = !isset($options['overwrite']) ? true : (bool) $options['overwrite']; |
$force_uri = !isset($options['force_uri']) ? false : (bool) $options['force_uri']; |
} |
$attname = $id_name; |
if (! empty($prefix)) { |
$attname = $prefix.':'.$attname; |
} |
$refNode = $this->createNewSignNode('Reference'); |
$sinfoNode->appendChild($refNode); |
if (! $node instanceof DOMDocument) { |
$uri = null; |
if (! $overwrite_id) { |
$uri = $prefix_ns ? $node->getAttributeNS($prefix_ns, $id_name) : $node->getAttribute($id_name); |
} |
if (empty($uri)) { |
$uri = self::generateGUID(); |
$node->setAttributeNS($prefix_ns, $attname, $uri); |
} |
$refNode->setAttribute("URI", '#'.$uri); |
} elseif ($force_uri) { |
$refNode->setAttribute("URI", ''); |
} |
$transNodes = $this->createNewSignNode('Transforms'); |
$refNode->appendChild($transNodes); |
if (is_array($arTransforms)) { |
foreach ($arTransforms AS $transform) { |
$transNode = $this->createNewSignNode('Transform'); |
$transNodes->appendChild($transNode); |
if (is_array($transform) && |
(! empty($transform['http://www.w3.org/TR/1999/REC-xpath-19991116'])) && |
(! empty($transform['http://www.w3.org/TR/1999/REC-xpath-19991116']['query']))) { |
$transNode->setAttribute('Algorithm', 'http://www.w3.org/TR/1999/REC-xpath-19991116'); |
$XPathNode = $this->createNewSignNode('XPath', $transform['http://www.w3.org/TR/1999/REC-xpath-19991116']['query']); |
$transNode->appendChild($XPathNode); |
if (! empty($transform['http://www.w3.org/TR/1999/REC-xpath-19991116']['namespaces'])) { |
foreach ($transform['http://www.w3.org/TR/1999/REC-xpath-19991116']['namespaces'] AS $prefix => $namespace) { |
$XPathNode->setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:$prefix", $namespace); |
} |
} |
} else { |
$transNode->setAttribute('Algorithm', $transform); |
} |
} |
} elseif (! empty($this->canonicalMethod)) { |
$transNode = $this->createNewSignNode('Transform'); |
$transNodes->appendChild($transNode); |
$transNode->setAttribute('Algorithm', $this->canonicalMethod); |
} |
$canonicalData = $this->processTransforms($refNode, $node); |
$digValue = $this->calculateDigest($algorithm, $canonicalData); |
$digestMethod = $this->createNewSignNode('DigestMethod'); |
$refNode->appendChild($digestMethod); |
$digestMethod->setAttribute('Algorithm', $algorithm); |
$digestValue = $this->createNewSignNode('DigestValue', $digValue); |
$refNode->appendChild($digestValue); |
} |
/** |
* @param DOMDocument $node |
* @param string $algorithm |
* @param null|array $arTransforms |
* @param null|array $options |
*/ |
public function addReference($node, $algorithm, $arTransforms=null, $options=null) |
{ |
if ($xpath = $this->getXPathObj()) { |
$query = "./secdsig:SignedInfo"; |
$nodeset = $xpath->query($query, $this->sigNode); |
if ($sInfo = $nodeset->item(0)) { |
$this->addRefInternal($sInfo, $node, $algorithm, $arTransforms, $options); |
} |
} |
} |
/** |
* @param array $arNodes |
* @param string $algorithm |
* @param null|array $arTransforms |
* @param null|array $options |
*/ |
public function addReferenceList($arNodes, $algorithm, $arTransforms=null, $options=null) |
{ |
if ($xpath = $this->getXPathObj()) { |
$query = "./secdsig:SignedInfo"; |
$nodeset = $xpath->query($query, $this->sigNode); |
if ($sInfo = $nodeset->item(0)) { |
foreach ($arNodes AS $node) { |
$this->addRefInternal($sInfo, $node, $algorithm, $arTransforms, $options); |
} |
} |
} |
} |
/** |
* @param DOMElement|string $data |
* @param null|string $mimetype |
* @param null|string $encoding |
* @return DOMElement |
*/ |
public function addObject($data, $mimetype=null, $encoding=null) |
{ |
$objNode = $this->createNewSignNode('Object'); |
$this->sigNode->appendChild($objNode); |
if (! empty($mimetype)) { |
$objNode->setAttribute('MimeType', $mimetype); |
} |
if (! empty($encoding)) { |
$objNode->setAttribute('Encoding', $encoding); |
} |
if ($data instanceof DOMElement) { |
$newData = $this->sigNode->ownerDocument->importNode($data, true); |
} else { |
$newData = $this->sigNode->ownerDocument->createTextNode($data); |
} |
$objNode->appendChild($newData); |
return $objNode; |
} |
/** |
* @param null|DOMNode $node |
* @return null|XMLSecurityKey |
*/ |
public function locateKey($node=null) |
{ |
if (empty($node)) { |
$node = $this->sigNode; |
} |
if (! $node instanceof DOMNode) { |
return null; |
} |
if ($doc = $node->ownerDocument) { |
$xpath = new DOMXPath($doc); |
$xpath->registerNamespace('secdsig', self::XMLDSIGNS); |
$query = "string(./secdsig:SignedInfo/secdsig:SignatureMethod/@Algorithm)"; |
$algorithm = $xpath->evaluate($query, $node); |
if ($algorithm) { |
try { |
$objKey = new XMLSecurityKey($algorithm, array('type' => 'public')); |
} catch (Exception $e) { |
return null; |
} |
return $objKey; |
} |
} |
return null; |
} |
/** |
* Returns: |
* Bool when verifying HMAC_SHA1; |
* Int otherwise, with following meanings: |
* 1 on succesful signature verification, |
* 0 when signature verification failed, |
* -1 if an error occurred during processing. |
* |
* NOTE: be very careful when checking the int return value, because in |
* PHP, -1 will be cast to True when in boolean context. Always check the |
* return value in a strictly typed way, e.g. "$obj->verify(...) === 1". |
* |
* @param XMLSecurityKey $objKey |
* @return bool|int |
* @throws Exception |
*/ |
public function verify($objKey) |
{ |
$doc = $this->sigNode->ownerDocument; |
$xpath = new DOMXPath($doc); |
$xpath->registerNamespace('secdsig', self::XMLDSIGNS); |
$query = "string(./secdsig:SignatureValue)"; |
$sigValue = $xpath->evaluate($query, $this->sigNode); |
if (empty($sigValue)) { |
throw new Exception("Unable to locate SignatureValue"); |
} |
return $objKey->verifySignature($this->signedInfo, base64_decode($sigValue)); |
} |
/** |
* @param XMLSecurityKey $objKey |
* @param string $data |
* @return mixed|string |
*/ |
public function signData($objKey, $data) |
{ |
return $objKey->signData($data); |
} |
/** |
* @param XMLSecurityKey $objKey |
* @param null|DOMNode $appendToNode |
*/ |
public function sign($objKey, $appendToNode = null) |
{ |
// If we have a parent node append it now so C14N properly works |
if ($appendToNode != null) { |
$this->resetXPathObj(); |
$this->appendSignature($appendToNode); |
$this->sigNode = $appendToNode->lastChild; |
} |
if ($xpath = $this->getXPathObj()) { |
$query = "./secdsig:SignedInfo"; |
$nodeset = $xpath->query($query, $this->sigNode); |
if ($sInfo = $nodeset->item(0)) { |
$query = "./secdsig:SignatureMethod"; |
$nodeset = $xpath->query($query, $sInfo); |
$sMethod = $nodeset->item(0); |
$sMethod->setAttribute('Algorithm', $objKey->type); |
$data = $this->canonicalizeData($sInfo, $this->canonicalMethod); |
$sigValue = base64_encode($this->signData($objKey, $data)); |
$sigValueNode = $this->createNewSignNode('SignatureValue', $sigValue); |
if ($infoSibling = $sInfo->nextSibling) { |
$infoSibling->parentNode->insertBefore($sigValueNode, $infoSibling); |
} else { |
$this->sigNode->appendChild($sigValueNode); |
} |
} |
} |
} |
public function appendCert() |
{ |
} |
/** |
* @param XMLSecurityKey $objKey |
* @param null|DOMNode $parent |
*/ |
public function appendKey($objKey, $parent=null) |
{ |
$objKey->serializeKey($parent); |
} |
/** |
* This function inserts the signature element. |
* |
* The signature element will be appended to the element, unless $beforeNode is specified. If $beforeNode |
* is specified, the signature element will be inserted as the last element before $beforeNode. |
* |
* @param DOMNode $node The node the signature element should be inserted into. |
* @param DOMNode $beforeNode The node the signature element should be located before. |
* |
* @return DOMNode The signature element node |
*/ |
public function insertSignature($node, $beforeNode = null) |
{ |
$document = $node->ownerDocument; |
$signatureElement = $document->importNode($this->sigNode, true); |
if ($beforeNode == null) { |
return $node->insertBefore($signatureElement); |
} else { |
return $node->insertBefore($signatureElement, $beforeNode); |
} |
} |
/** |
* @param DOMNode $parentNode |
* @param bool $insertBefore |
* @return DOMNode |
*/ |
public function appendSignature($parentNode, $insertBefore = false) |
{ |
$beforeNode = $insertBefore ? $parentNode->firstChild : null; |
return $this->insertSignature($parentNode, $beforeNode); |
} |
/** |
* @param string $cert |
* @param bool $isPEMFormat |
* @return string |
*/ |
public static function get509XCert($cert, $isPEMFormat=true) |
{ |
$certs = self::staticGet509XCerts($cert, $isPEMFormat); |
if (! empty($certs)) { |
return $certs[0]; |
} |
return ''; |
} |
/** |
* @param string $certs |
* @param bool $isPEMFormat |
* @return array |
*/ |
public static function staticGet509XCerts($certs, $isPEMFormat=true) |
{ |
if ($isPEMFormat) { |
$data = ''; |
$certlist = array(); |
$arCert = explode("\n", $certs); |
$inData = false; |
foreach ($arCert AS $curData) { |
if (! $inData) { |
if (strncmp($curData, '-----BEGIN CERTIFICATE', 22) == 0) { |
$inData = true; |
} |
} else { |
if (strncmp($curData, '-----END CERTIFICATE', 20) == 0) { |
$inData = false; |
$certlist[] = $data; |
$data = ''; |
continue; |
} |
$data .= trim($curData); |
} |
} |
return $certlist; |
} else { |
return array($certs); |
} |
} |
/** |
* @param DOMElement $parentRef |
* @param string $cert |
* @param bool $isPEMFormat |
* @param bool $isURL |
* @param null|DOMXPath $xpath |
* @param null|array $options |
* @throws Exception |
*/ |
public static function staticAdd509Cert($parentRef, $cert, $isPEMFormat=true, $isURL=false, $xpath=null, $options=null) |
{ |
if ($isURL) { |
$cert = file_get_contents($cert); |
} |
if (! $parentRef instanceof DOMElement) { |
throw new Exception('Invalid parent Node parameter'); |
} |
$baseDoc = $parentRef->ownerDocument; |
if (empty($xpath)) { |
$xpath = new DOMXPath($parentRef->ownerDocument); |
$xpath->registerNamespace('secdsig', self::XMLDSIGNS); |
} |
$query = "./secdsig:KeyInfo"; |
$nodeset = $xpath->query($query, $parentRef); |
$keyInfo = $nodeset->item(0); |
$dsig_pfx = ''; |
if (! $keyInfo) { |
$pfx = $parentRef->lookupPrefix(self::XMLDSIGNS); |
if (! empty($pfx)) { |
$dsig_pfx = $pfx.":"; |
} |
$inserted = false; |
$keyInfo = $baseDoc->createElementNS(self::XMLDSIGNS, $dsig_pfx.'KeyInfo'); |
$query = "./secdsig:Object"; |
$nodeset = $xpath->query($query, $parentRef); |
if ($sObject = $nodeset->item(0)) { |
$sObject->parentNode->insertBefore($keyInfo, $sObject); |
$inserted = true; |
} |
if (! $inserted) { |
$parentRef->appendChild($keyInfo); |
} |
} else { |
$pfx = $keyInfo->lookupPrefix(self::XMLDSIGNS); |
if (! empty($pfx)) { |
$dsig_pfx = $pfx.":"; |
} |
} |
// Add all certs if there are more than one |
$certs = self::staticGet509XCerts($cert, $isPEMFormat); |
// Attach X509 data node |
$x509DataNode = $baseDoc->createElementNS(self::XMLDSIGNS, $dsig_pfx.'X509Data'); |
$keyInfo->appendChild($x509DataNode); |
$issuerSerial = false; |
$subjectName = false; |
if (is_array($options)) { |
if (! empty($options['issuerSerial'])) { |
$issuerSerial = true; |
} |
if (! empty($options['subjectName'])) { |
$subjectName = true; |
} |
} |
// Attach all certificate nodes and any additional data |
foreach ($certs as $X509Cert) { |
if ($issuerSerial || $subjectName) { |
if ($certData = openssl_x509_parse("-----BEGIN CERTIFICATE-----\n".chunk_split($X509Cert, 64, "\n")."-----END CERTIFICATE-----\n")) { |
if ($subjectName && ! empty($certData['subject'])) { |
if (is_array($certData['subject'])) { |
$parts = array(); |
foreach ($certData['subject'] AS $key => $value) { |
if (is_array($value)) { |
foreach ($value as $valueElement) { |
array_unshift($parts, "$key=$valueElement"); |
} |
} else { |
array_unshift($parts, "$key=$value"); |
} |
} |
$subjectNameValue = implode(',', $parts); |
} else { |
$subjectNameValue = $certData['issuer']; |
} |
$x509SubjectNode = $baseDoc->createElementNS(self::XMLDSIGNS, $dsig_pfx.'X509SubjectName', $subjectNameValue); |
$x509DataNode->appendChild($x509SubjectNode); |
} |
if ($issuerSerial && ! empty($certData['issuer']) && ! empty($certData['serialNumber'])) { |
if (is_array($certData['issuer'])) { |
$parts = array(); |
foreach ($certData['issuer'] AS $key => $value) { |
array_unshift($parts, "$key=$value"); |
} |
$issuerName = implode(',', $parts); |
} else { |
$issuerName = $certData['issuer']; |
} |
$x509IssuerNode = $baseDoc->createElementNS(self::XMLDSIGNS, $dsig_pfx.'X509IssuerSerial'); |
$x509DataNode->appendChild($x509IssuerNode); |
$x509Node = $baseDoc->createElementNS(self::XMLDSIGNS, $dsig_pfx.'X509IssuerName', $issuerName); |
$x509IssuerNode->appendChild($x509Node); |
$x509Node = $baseDoc->createElementNS(self::XMLDSIGNS, $dsig_pfx.'X509SerialNumber', $certData['serialNumber']); |
$x509IssuerNode->appendChild($x509Node); |
} |
} |
} |
$x509CertNode = $baseDoc->createElementNS(self::XMLDSIGNS, $dsig_pfx.'X509Certificate', $X509Cert); |
$x509DataNode->appendChild($x509CertNode); |
} |
} |
/** |
* @param string $cert |
* @param bool $isPEMFormat |
* @param bool $isURL |
* @param null|array $options |
*/ |
public function add509Cert($cert, $isPEMFormat=true, $isURL=false, $options=null) |
{ |
if ($xpath = $this->getXPathObj()) { |
self::staticAdd509Cert($this->sigNode, $cert, $isPEMFormat, $isURL, $xpath, $options); |
} |
} |
/** |
* This function appends a node to the KeyInfo. |
* |
* The KeyInfo element will be created if one does not exist in the document. |
* |
* @param DOMNode $node The node to append to the KeyInfo. |
* |
* @return DOMNode The KeyInfo element node |
*/ |
public function appendToKeyInfo($node) |
{ |
$parentRef = $this->sigNode; |
$baseDoc = $parentRef->ownerDocument; |
$xpath = $this->getXPathObj(); |
if (empty($xpath)) { |
$xpath = new DOMXPath($parentRef->ownerDocument); |
$xpath->registerNamespace('secdsig', self::XMLDSIGNS); |
} |
$query = "./secdsig:KeyInfo"; |
$nodeset = $xpath->query($query, $parentRef); |
$keyInfo = $nodeset->item(0); |
if (! $keyInfo) { |
$dsig_pfx = ''; |
$pfx = $parentRef->lookupPrefix(self::XMLDSIGNS); |
if (! empty($pfx)) { |
$dsig_pfx = $pfx.":"; |
} |
$inserted = false; |
$keyInfo = $baseDoc->createElementNS(self::XMLDSIGNS, $dsig_pfx.'KeyInfo'); |
$query = "./secdsig:Object"; |
$nodeset = $xpath->query($query, $parentRef); |
if ($sObject = $nodeset->item(0)) { |
$sObject->parentNode->insertBefore($keyInfo, $sObject); |
$inserted = true; |
} |
if (! $inserted) { |
$parentRef->appendChild($keyInfo); |
} |
} |
$keyInfo->appendChild($node); |
return $keyInfo; |
} |
/** |
* This function retrieves an associative array of the validated nodes. |
* |
* The array will contain the id of the referenced node as the key and the node itself |
* as the value. |
* |
* Returns: |
* An associative array of validated nodes or null if no nodes have been validated. |
* |
* @return array Associative array of validated nodes |
*/ |
public function getValidatedNodes() |
{ |
return $this->validatedNodes; |
} |
} |
/trunk/plugins/viathinksoft/publicPages/100_whois/whois/xml/vendor/robrichards/xmlseclibs/src/XMLSecurityKey.php |
---|
0,0 → 1,813 |
<?php |
namespace RobRichards\XMLSecLibs; |
use DOMElement; |
use Exception; |
/** |
* xmlseclibs.php |
* |
* Copyright (c) 2007-2020, Robert Richards <rrichards@cdatazone.org>. |
* All rights reserved. |
* |
* Redistribution and use in source and binary forms, with or without |
* modification, are permitted provided that the following conditions |
* are met: |
* |
* * Redistributions of source code must retain the above copyright |
* notice, this list of conditions and the following disclaimer. |
* |
* * Redistributions in binary form must reproduce the above copyright |
* notice, this list of conditions and the following disclaimer in |
* the documentation and/or other materials provided with the |
* distribution. |
* |
* * Neither the name of Robert Richards nor the names of his |
* contributors may be used to endorse or promote products derived |
* from this software without specific prior written permission. |
* |
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
* POSSIBILITY OF SUCH DAMAGE. |
* |
* @author Robert Richards <rrichards@cdatazone.org> |
* @copyright 2007-2020 Robert Richards <rrichards@cdatazone.org> |
* @license http://www.opensource.org/licenses/bsd-license.php BSD License |
*/ |
class XMLSecurityKey |
{ |
const TRIPLEDES_CBC = 'http://www.w3.org/2001/04/xmlenc#tripledes-cbc'; |
const AES128_CBC = 'http://www.w3.org/2001/04/xmlenc#aes128-cbc'; |
const AES192_CBC = 'http://www.w3.org/2001/04/xmlenc#aes192-cbc'; |
const AES256_CBC = 'http://www.w3.org/2001/04/xmlenc#aes256-cbc'; |
const AES128_GCM = 'http://www.w3.org/2009/xmlenc11#aes128-gcm'; |
const AES192_GCM = 'http://www.w3.org/2009/xmlenc11#aes192-gcm'; |
const AES256_GCM = 'http://www.w3.org/2009/xmlenc11#aes256-gcm'; |
const RSA_1_5 = 'http://www.w3.org/2001/04/xmlenc#rsa-1_5'; |
const RSA_OAEP_MGF1P = 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p'; |
const RSA_OAEP = 'http://www.w3.org/2009/xmlenc11#rsa-oaep'; |
const DSA_SHA1 = 'http://www.w3.org/2000/09/xmldsig#dsa-sha1'; |
const RSA_SHA1 = 'http://www.w3.org/2000/09/xmldsig#rsa-sha1'; |
const RSA_SHA256 = 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256'; |
const RSA_SHA384 = 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha384'; |
const RSA_SHA512 = 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha512'; |
const HMAC_SHA1 = 'http://www.w3.org/2000/09/xmldsig#hmac-sha1'; |
const AUTHTAG_LENGTH = 16; |
/** @var array */ |
private $cryptParams = array(); |
/** @var int|string */ |
public $type = 0; |
/** @var mixed|null */ |
public $key = null; |
/** @var string */ |
public $passphrase = ""; |
/** @var string|null */ |
public $iv = null; |
/** @var string|null */ |
public $name = null; |
/** @var mixed|null */ |
public $keyChain = null; |
/** @var bool */ |
public $isEncrypted = false; |
/** @var XMLSecEnc|null */ |
public $encryptedCtx = null; |
/** @var mixed|null */ |
public $guid = null; |
/** |
* This variable contains the certificate as a string if this key represents an X509-certificate. |
* If this key doesn't represent a certificate, this will be null. |
* @var string|null |
*/ |
private $x509Certificate = null; |
/** |
* This variable contains the certificate thumbprint if we have loaded an X509-certificate. |
* @var string|null |
*/ |
private $X509Thumbprint = null; |
/** |
* @param string $type |
* @param null|array $params |
* @throws Exception |
*/ |
public function __construct($type, $params=null) |
{ |
switch ($type) { |
case (self::TRIPLEDES_CBC): |
$this->cryptParams['library'] = 'openssl'; |
$this->cryptParams['cipher'] = 'des-ede3-cbc'; |
$this->cryptParams['type'] = 'symmetric'; |
$this->cryptParams['method'] = 'http://www.w3.org/2001/04/xmlenc#tripledes-cbc'; |
$this->cryptParams['keysize'] = 24; |
$this->cryptParams['blocksize'] = 8; |
break; |
case (self::AES128_CBC): |
$this->cryptParams['library'] = 'openssl'; |
$this->cryptParams['cipher'] = 'aes-128-cbc'; |
$this->cryptParams['type'] = 'symmetric'; |
$this->cryptParams['method'] = 'http://www.w3.org/2001/04/xmlenc#aes128-cbc'; |
$this->cryptParams['keysize'] = 16; |
$this->cryptParams['blocksize'] = 16; |
break; |
case (self::AES192_CBC): |
$this->cryptParams['library'] = 'openssl'; |
$this->cryptParams['cipher'] = 'aes-192-cbc'; |
$this->cryptParams['type'] = 'symmetric'; |
$this->cryptParams['method'] = 'http://www.w3.org/2001/04/xmlenc#aes192-cbc'; |
$this->cryptParams['keysize'] = 24; |
$this->cryptParams['blocksize'] = 16; |
break; |
case (self::AES256_CBC): |
$this->cryptParams['library'] = 'openssl'; |
$this->cryptParams['cipher'] = 'aes-256-cbc'; |
$this->cryptParams['type'] = 'symmetric'; |
$this->cryptParams['method'] = 'http://www.w3.org/2001/04/xmlenc#aes256-cbc'; |
$this->cryptParams['keysize'] = 32; |
$this->cryptParams['blocksize'] = 16; |
break; |
case (self::AES128_GCM): |
$this->cryptParams['library'] = 'openssl'; |
$this->cryptParams['cipher'] = 'aes-128-gcm'; |
$this->cryptParams['type'] = 'symmetric'; |
$this->cryptParams['method'] = 'http://www.w3.org/2009/xmlenc11#aes128-gcm'; |
$this->cryptParams['keysize'] = 16; |
$this->cryptParams['blocksize'] = 16; |
break; |
case (self::AES192_GCM): |
$this->cryptParams['library'] = 'openssl'; |
$this->cryptParams['cipher'] = 'aes-192-gcm'; |
$this->cryptParams['type'] = 'symmetric'; |
$this->cryptParams['method'] = 'http://www.w3.org/2009/xmlenc11#aes192-gcm'; |
$this->cryptParams['keysize'] = 24; |
$this->cryptParams['blocksize'] = 16; |
break; |
case (self::AES256_GCM): |
$this->cryptParams['library'] = 'openssl'; |
$this->cryptParams['cipher'] = 'aes-256-gcm'; |
$this->cryptParams['type'] = 'symmetric'; |
$this->cryptParams['method'] = 'http://www.w3.org/2009/xmlenc11#aes256-gcm'; |
$this->cryptParams['keysize'] = 32; |
$this->cryptParams['blocksize'] = 16; |
break; |
case (self::RSA_1_5): |
$this->cryptParams['library'] = 'openssl'; |
$this->cryptParams['padding'] = OPENSSL_PKCS1_PADDING; |
$this->cryptParams['method'] = 'http://www.w3.org/2001/04/xmlenc#rsa-1_5'; |
if (is_array($params) && ! empty($params['type'])) { |
if ($params['type'] == 'public' || $params['type'] == 'private') { |
$this->cryptParams['type'] = $params['type']; |
break; |
} |
} |
throw new Exception('Certificate "type" (private/public) must be passed via parameters'); |
case (self::RSA_OAEP_MGF1P): |
$this->cryptParams['library'] = 'openssl'; |
$this->cryptParams['padding'] = OPENSSL_PKCS1_OAEP_PADDING; |
$this->cryptParams['method'] = 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p'; |
$this->cryptParams['hash'] = null; |
if (is_array($params) && ! empty($params['type'])) { |
if ($params['type'] == 'public' || $params['type'] == 'private') { |
$this->cryptParams['type'] = $params['type']; |
break; |
} |
} |
throw new Exception('Certificate "type" (private/public) must be passed via parameters'); |
case (self::RSA_OAEP): |
$this->cryptParams['library'] = 'openssl'; |
$this->cryptParams['padding'] = OPENSSL_PKCS1_OAEP_PADDING; |
$this->cryptParams['method'] = 'http://www.w3.org/2009/xmlenc11#rsa-oaep'; |
$this->cryptParams['hash'] = 'http://www.w3.org/2009/xmlenc11#mgf1sha1'; |
if (is_array($params) && ! empty($params['type'])) { |
if ($params['type'] == 'public' || $params['type'] == 'private') { |
$this->cryptParams['type'] = $params['type']; |
break; |
} |
} |
throw new Exception('Certificate "type" (private/public) must be passed via parameters'); |
case (self::RSA_SHA1): |
$this->cryptParams['library'] = 'openssl'; |
$this->cryptParams['method'] = 'http://www.w3.org/2000/09/xmldsig#rsa-sha1'; |
$this->cryptParams['padding'] = OPENSSL_PKCS1_PADDING; |
if (is_array($params) && ! empty($params['type'])) { |
if ($params['type'] == 'public' || $params['type'] == 'private') { |
$this->cryptParams['type'] = $params['type']; |
break; |
} |
} |
throw new Exception('Certificate "type" (private/public) must be passed via parameters'); |
case (self::RSA_SHA256): |
$this->cryptParams['library'] = 'openssl'; |
$this->cryptParams['method'] = 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256'; |
$this->cryptParams['padding'] = OPENSSL_PKCS1_PADDING; |
$this->cryptParams['digest'] = 'SHA256'; |
if (is_array($params) && ! empty($params['type'])) { |
if ($params['type'] == 'public' || $params['type'] == 'private') { |
$this->cryptParams['type'] = $params['type']; |
break; |
} |
} |
throw new Exception('Certificate "type" (private/public) must be passed via parameters'); |
case (self::RSA_SHA384): |
$this->cryptParams['library'] = 'openssl'; |
$this->cryptParams['method'] = 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha384'; |
$this->cryptParams['padding'] = OPENSSL_PKCS1_PADDING; |
$this->cryptParams['digest'] = 'SHA384'; |
if (is_array($params) && ! empty($params['type'])) { |
if ($params['type'] == 'public' || $params['type'] == 'private') { |
$this->cryptParams['type'] = $params['type']; |
break; |
} |
} |
throw new Exception('Certificate "type" (private/public) must be passed via parameters'); |
case (self::RSA_SHA512): |
$this->cryptParams['library'] = 'openssl'; |
$this->cryptParams['method'] = 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha512'; |
$this->cryptParams['padding'] = OPENSSL_PKCS1_PADDING; |
$this->cryptParams['digest'] = 'SHA512'; |
if (is_array($params) && ! empty($params['type'])) { |
if ($params['type'] == 'public' || $params['type'] == 'private') { |
$this->cryptParams['type'] = $params['type']; |
break; |
} |
} |
throw new Exception('Certificate "type" (private/public) must be passed via parameters'); |
case (self::HMAC_SHA1): |
$this->cryptParams['library'] = $type; |
$this->cryptParams['method'] = 'http://www.w3.org/2000/09/xmldsig#hmac-sha1'; |
break; |
default: |
throw new Exception('Invalid Key Type'); |
} |
$this->type = $type; |
} |
/** |
* Retrieve the key size for the symmetric encryption algorithm.. |
* |
* If the key size is unknown, or this isn't a symmetric encryption algorithm, |
* null is returned. |
* |
* @return int|null The number of bytes in the key. |
*/ |
public function getSymmetricKeySize() |
{ |
if (! isset($this->cryptParams['keysize'])) { |
return null; |
} |
return $this->cryptParams['keysize']; |
} |
/** |
* Generates a session key using the openssl-extension. |
* In case of using DES3-CBC the key is checked for a proper parity bits set. |
* @return string |
* @throws Exception |
*/ |
public function generateSessionKey() |
{ |
if (!isset($this->cryptParams['keysize'])) { |
throw new Exception('Unknown key size for type "' . $this->type . '".'); |
} |
$keysize = $this->cryptParams['keysize']; |
$key = openssl_random_pseudo_bytes($keysize); |
if ($this->type === self::TRIPLEDES_CBC) { |
/* Make sure that the generated key has the proper parity bits set. |
* Mcrypt doesn't care about the parity bits, but others may care. |
*/ |
for ($i = 0; $i < strlen($key); $i++) { |
$byte = ord($key[$i]) & 0xfe; |
$parity = 1; |
for ($j = 1; $j < 8; $j++) { |
$parity ^= ($byte >> $j) & 1; |
} |
$byte |= $parity; |
$key[$i] = chr($byte); |
} |
} |
$this->key = $key; |
return $key; |
} |
/** |
* Get the raw thumbprint of a certificate |
* |
* @param string $cert |
* @return null|string |
*/ |
public static function getRawThumbprint($cert) |
{ |
$arCert = explode("\n", $cert); |
$data = ''; |
$inData = false; |
foreach ($arCert AS $curData) { |
if (! $inData) { |
if (strncmp($curData, '-----BEGIN CERTIFICATE', 22) == 0) { |
$inData = true; |
} |
} else { |
if (strncmp($curData, '-----END CERTIFICATE', 20) == 0) { |
break; |
} |
$data .= trim($curData); |
} |
} |
if (! empty($data)) { |
return strtolower(sha1(base64_decode($data))); |
} |
return null; |
} |
/** |
* Loads the given key, or - with isFile set true - the key from the keyfile. |
* |
* @param string $key |
* @param bool $isFile |
* @param bool $isCert |
* @throws Exception |
*/ |
public function loadKey($key, $isFile=false, $isCert = false) |
{ |
if ($isFile) { |
$this->key = file_get_contents($key); |
} else { |
$this->key = $key; |
} |
if ($isCert) { |
$this->key = openssl_x509_read($this->key); |
openssl_x509_export($this->key, $str_cert); |
$this->x509Certificate = $str_cert; |
$this->key = $str_cert; |
} else { |
$this->x509Certificate = null; |
} |
if ($this->cryptParams['library'] == 'openssl') { |
switch ($this->cryptParams['type']) { |
case 'public': |
if ($isCert) { |
/* Load the thumbprint if this is an X509 certificate. */ |
$this->X509Thumbprint = self::getRawThumbprint($this->key); |
} |
$this->key = openssl_get_publickey($this->key); |
if (! $this->key) { |
throw new Exception('Unable to extract public key'); |
} |
break; |
case 'private': |
$this->key = openssl_get_privatekey($this->key, $this->passphrase); |
break; |
case'symmetric': |
if (strlen($this->key) < $this->cryptParams['keysize']) { |
throw new Exception('Key must contain at least '.$this->cryptParams['keysize'].' characters for this cipher, contains '.strlen($this->key)); |
} |
break; |
default: |
throw new Exception('Unknown type'); |
} |
} |
} |
/** |
* ISO 10126 Padding |
* |
* @param string $data |
* @param integer $blockSize |
* @throws Exception |
* @return string |
*/ |
private function padISO10126($data, $blockSize) |
{ |
if ($blockSize > 256) { |
throw new Exception('Block size higher than 256 not allowed'); |
} |
$padChr = $blockSize - (strlen($data) % $blockSize); |
$pattern = chr($padChr); |
return $data . str_repeat($pattern, $padChr); |
} |
/** |
* Remove ISO 10126 Padding |
* |
* @param string $data |
* @return string |
*/ |
private function unpadISO10126($data) |
{ |
$padChr = substr($data, -1); |
$padLen = ord($padChr); |
return substr($data, 0, -$padLen); |
} |
/** |
* Encrypts the given data (string) using the openssl-extension |
* |
* @param string $data |
* @return string |
*/ |
private function encryptSymmetric($data) |
{ |
$this->iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length($this->cryptParams['cipher'])); |
$authTag = null; |
if(in_array($this->cryptParams['cipher'], ['aes-128-gcm', 'aes-192-gcm', 'aes-256-gcm'])) { |
if (version_compare(PHP_VERSION, '7.1.0') < 0) { |
throw new Exception('PHP 7.1.0 is required to use AES GCM algorithms'); |
} |
$authTag = openssl_random_pseudo_bytes(self::AUTHTAG_LENGTH); |
$encrypted = openssl_encrypt($data, $this->cryptParams['cipher'], $this->key, OPENSSL_RAW_DATA, $this->iv, $authTag); |
} else { |
$data = $this->padISO10126($data, $this->cryptParams['blocksize']); |
$encrypted = openssl_encrypt($data, $this->cryptParams['cipher'], $this->key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $this->iv); |
} |
if (false === $encrypted) { |
throw new Exception('Failure encrypting Data (openssl symmetric) - ' . openssl_error_string()); |
} |
return $this->iv . $encrypted . $authTag; |
} |
/** |
* Decrypts the given data (string) using the openssl-extension |
* |
* @param string $data |
* @return string |
*/ |
private function decryptSymmetric($data) |
{ |
$iv_length = openssl_cipher_iv_length($this->cryptParams['cipher']); |
$this->iv = substr($data, 0, $iv_length); |
$data = substr($data, $iv_length); |
$authTag = null; |
if(in_array($this->cryptParams['cipher'], ['aes-128-gcm', 'aes-192-gcm', 'aes-256-gcm'])) { |
if (version_compare(PHP_VERSION, '7.1.0') < 0) { |
throw new Exception('PHP 7.1.0 is required to use AES GCM algorithms'); |
} |
// obtain and remove the authentication tag |
$offset = 0 - self::AUTHTAG_LENGTH; |
$authTag = substr($data, $offset); |
$data = substr($data, 0, $offset); |
$decrypted = openssl_decrypt($data, $this->cryptParams['cipher'], $this->key, OPENSSL_RAW_DATA, $this->iv, $authTag); |
} else { |
$decrypted = openssl_decrypt($data, $this->cryptParams['cipher'], $this->key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $this->iv); |
} |
if (false === $decrypted) { |
throw new Exception('Failure decrypting Data (openssl symmetric) - ' . openssl_error_string()); |
} |
return null !== $authTag ? $decrypted : $this->unpadISO10126($decrypted); |
} |
/** |
* Encrypts the given public data (string) using the openssl-extension |
* |
* @param string $data |
* @return string |
* @throws Exception |
*/ |
private function encryptPublic($data) |
{ |
if (! openssl_public_encrypt($data, $encrypted, $this->key, $this->cryptParams['padding'])) { |
throw new Exception('Failure encrypting Data (openssl public) - ' . openssl_error_string()); |
} |
return $encrypted; |
} |
/** |
* Decrypts the given public data (string) using the openssl-extension |
* |
* @param string $data |
* @return string |
* @throws Exception |
*/ |
private function decryptPublic($data) |
{ |
if (! openssl_public_decrypt($data, $decrypted, $this->key, $this->cryptParams['padding'])) { |
throw new Exception('Failure decrypting Data (openssl public) - ' . openssl_error_string()); |
} |
return $decrypted; |
} |
/** |
* Encrypts the given private data (string) using the openssl-extension |
* |
* @param string $data |
* @return string |
* @throws Exception |
*/ |
private function encryptPrivate($data) |
{ |
if (! openssl_private_encrypt($data, $encrypted, $this->key, $this->cryptParams['padding'])) { |
throw new Exception('Failure encrypting Data (openssl private) - ' . openssl_error_string()); |
} |
return $encrypted; |
} |
/** |
* Decrypts the given private data (string) using the openssl-extension |
* |
* @param string $data |
* @return string |
* @throws Exception |
*/ |
private function decryptPrivate($data) |
{ |
if (! openssl_private_decrypt($data, $decrypted, $this->key, $this->cryptParams['padding'])) { |
throw new Exception('Failure decrypting Data (openssl private) - ' . openssl_error_string()); |
} |
return $decrypted; |
} |
/** |
* Signs the given data (string) using the openssl-extension |
* |
* @param string $data |
* @return string |
* @throws Exception |
*/ |
private function signOpenSSL($data) |
{ |
$algo = OPENSSL_ALGO_SHA1; |
if (! empty($this->cryptParams['digest'])) { |
$algo = $this->cryptParams['digest']; |
} |
if (! openssl_sign($data, $signature, $this->key, $algo)) { |
throw new Exception('Failure Signing Data: ' . openssl_error_string() . ' - ' . $algo); |
} |
return $signature; |
} |
/** |
* Verifies the given data (string) belonging to the given signature using the openssl-extension |
* |
* Returns: |
* 1 on succesful signature verification, |
* 0 when signature verification failed, |
* -1 if an error occurred during processing. |
* |
* NOTE: be very careful when checking the return value, because in PHP, |
* -1 will be cast to True when in boolean context. So always check the |
* return value in a strictly typed way, e.g. "$obj->verify(...) === 1". |
* |
* @param string $data |
* @param string $signature |
* @return int |
*/ |
private function verifyOpenSSL($data, $signature) |
{ |
$algo = OPENSSL_ALGO_SHA1; |
if (! empty($this->cryptParams['digest'])) { |
$algo = $this->cryptParams['digest']; |
} |
return openssl_verify($data, $signature, $this->key, $algo); |
} |
/** |
* Encrypts the given data (string) using the regarding php-extension, depending on the library assigned to algorithm in the contructor. |
* |
* @param string $data |
* @return mixed|string |
*/ |
public function encryptData($data) |
{ |
if ($this->cryptParams['library'] === 'openssl') { |
switch ($this->cryptParams['type']) { |
case 'symmetric': |
return $this->encryptSymmetric($data); |
case 'public': |
return $this->encryptPublic($data); |
case 'private': |
return $this->encryptPrivate($data); |
} |
} |
} |
/** |
* Decrypts the given data (string) using the regarding php-extension, depending on the library assigned to algorithm in the contructor. |
* |
* @param string $data |
* @return mixed|string |
*/ |
public function decryptData($data) |
{ |
if ($this->cryptParams['library'] === 'openssl') { |
switch ($this->cryptParams['type']) { |
case 'symmetric': |
return $this->decryptSymmetric($data); |
case 'public': |
return $this->decryptPublic($data); |
case 'private': |
return $this->decryptPrivate($data); |
} |
} |
} |
/** |
* Signs the data (string) using the extension assigned to the type in the constructor. |
* |
* @param string $data |
* @return mixed|string |
*/ |
public function signData($data) |
{ |
switch ($this->cryptParams['library']) { |
case 'openssl': |
return $this->signOpenSSL($data); |
case (self::HMAC_SHA1): |
return hash_hmac("sha1", $data, $this->key, true); |
} |
} |
/** |
* Verifies the data (string) against the given signature using the extension assigned to the type in the constructor. |
* |
* Returns in case of openSSL: |
* 1 on succesful signature verification, |
* 0 when signature verification failed, |
* -1 if an error occurred during processing. |
* |
* NOTE: be very careful when checking the return value, because in PHP, |
* -1 will be cast to True when in boolean context. So always check the |
* return value in a strictly typed way, e.g. "$obj->verify(...) === 1". |
* |
* @param string $data |
* @param string $signature |
* @return bool|int |
*/ |
public function verifySignature($data, $signature) |
{ |
switch ($this->cryptParams['library']) { |
case 'openssl': |
return $this->verifyOpenSSL($data, $signature); |
case (self::HMAC_SHA1): |
$expectedSignature = hash_hmac("sha1", $data, $this->key, true); |
return strcmp($signature, $expectedSignature) == 0; |
} |
} |
/** |
* @deprecated |
* @see getAlgorithm() |
* @return mixed |
*/ |
public function getAlgorith() |
{ |
return $this->getAlgorithm(); |
} |
/** |
* @return mixed |
*/ |
public function getAlgorithm() |
{ |
return $this->cryptParams['method']; |
} |
/** |
* |
* @param int $type |
* @param string $string |
* @return null|string |
*/ |
public static function makeAsnSegment($type, $string) |
{ |
switch ($type) { |
case 0x02: |
if (ord($string) > 0x7f) |
$string = chr(0).$string; |
break; |
case 0x03: |
$string = chr(0).$string; |
break; |
} |
$length = strlen($string); |
if ($length < 128) { |
$output = sprintf("%c%c%s", $type, $length, $string); |
} else if ($length < 0x0100) { |
$output = sprintf("%c%c%c%s", $type, 0x81, $length, $string); |
} else if ($length < 0x010000) { |
$output = sprintf("%c%c%c%c%s", $type, 0x82, $length / 0x0100, $length % 0x0100, $string); |
} else { |
$output = null; |
} |
return $output; |
} |
/** |
* |
* Hint: Modulus and Exponent must already be base64 decoded |
* @param string $modulus |
* @param string $exponent |
* @return string |
*/ |
public static function convertRSA($modulus, $exponent) |
{ |
/* make an ASN publicKeyInfo */ |
$exponentEncoding = self::makeAsnSegment(0x02, $exponent); |
$modulusEncoding = self::makeAsnSegment(0x02, $modulus); |
$sequenceEncoding = self::makeAsnSegment(0x30, $modulusEncoding.$exponentEncoding); |
$bitstringEncoding = self::makeAsnSegment(0x03, $sequenceEncoding); |
$rsaAlgorithmIdentifier = pack("H*", "300D06092A864886F70D0101010500"); |
$publicKeyInfo = self::makeAsnSegment(0x30, $rsaAlgorithmIdentifier.$bitstringEncoding); |
/* encode the publicKeyInfo in base64 and add PEM brackets */ |
$publicKeyInfoBase64 = base64_encode($publicKeyInfo); |
$encoding = "-----BEGIN PUBLIC KEY-----\n"; |
$offset = 0; |
while ($segment = substr($publicKeyInfoBase64, $offset, 64)) { |
$encoding = $encoding.$segment."\n"; |
$offset += 64; |
} |
return $encoding."-----END PUBLIC KEY-----\n"; |
} |
/** |
* @param mixed $parent |
*/ |
public function serializeKey($parent) |
{ |
} |
/** |
* Retrieve the X509 certificate this key represents. |
* |
* Will return the X509 certificate in PEM-format if this key represents |
* an X509 certificate. |
* |
* @return string The X509 certificate or null if this key doesn't represent an X509-certificate. |
*/ |
public function getX509Certificate() |
{ |
return $this->x509Certificate; |
} |
/** |
* Get the thumbprint of this X509 certificate. |
* |
* Returns: |
* The thumbprint as a lowercase 40-character hexadecimal number, or null |
* if this isn't a X509 certificate. |
* |
* @return string Lowercase 40-character hexadecimal number of thumbprint |
*/ |
public function getX509Thumbprint() |
{ |
return $this->X509Thumbprint; |
} |
/** |
* Create key from an EncryptedKey-element. |
* |
* @param DOMElement $element The EncryptedKey-element. |
* @throws Exception |
* |
* @return XMLSecurityKey The new key. |
*/ |
public static function fromEncryptedKeyElement(DOMElement $element) |
{ |
$objenc = new XMLSecEnc(); |
$objenc->setNode($element); |
if (! $objKey = $objenc->locateKey()) { |
throw new Exception("Unable to locate algorithm for this Encrypted Key"); |
} |
$objKey->isEncrypted = true; |
$objKey->encryptedCtx = $objenc; |
XMLSecEnc::staticLocateKeyInfo($objKey, $element); |
return $objKey; |
} |
} |