forked from friendica/friendica-addons
Add SAML addon.
This commit is contained in:
parent
f04493b5bb
commit
4b3b79c894
62 changed files with 16277 additions and 0 deletions
44
saml/vendor/robrichards/xmlseclibs/src/Utils/XPath.php
vendored
Normal file
44
saml/vendor/robrichards/xmlseclibs/src/Utils/XPath.php
vendored
Normal file
|
@ -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);
|
||||
}
|
||||
}
|
511
saml/vendor/robrichards/xmlseclibs/src/XMLSecEnc.php
vendored
Normal file
511
saml/vendor/robrichards/xmlseclibs/src/XMLSecEnc.php
vendored
Normal file
|
@ -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);
|
||||
}
|
||||
}
|
1162
saml/vendor/robrichards/xmlseclibs/src/XMLSecurityDSig.php
vendored
Normal file
1162
saml/vendor/robrichards/xmlseclibs/src/XMLSecurityDSig.php
vendored
Normal file
File diff suppressed because it is too large
Load diff
813
saml/vendor/robrichards/xmlseclibs/src/XMLSecurityKey.php
vendored
Normal file
813
saml/vendor/robrichards/xmlseclibs/src/XMLSecurityKey.php
vendored
Normal file
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue