Sync changes v29.0.0 from IceHrmPro (https://icehrm.com/purchase-icehrmpro)
This commit is contained in:
1354
core/lib/saml2/Assertion.php
Normal file
1354
core/lib/saml2/Assertion.php
Normal file
File diff suppressed because it is too large
Load Diff
185
core/lib/saml2/MetadataReader.php
Normal file
185
core/lib/saml2/MetadataReader.php
Normal file
@@ -0,0 +1,185 @@
|
||||
<?php
|
||||
|
||||
include_once 'Utilities.php';
|
||||
class IDPMetadataReader{
|
||||
|
||||
private $identityProviders;
|
||||
private $serviceProviders;
|
||||
|
||||
public function __construct(DOMNode $xml = NULL){
|
||||
|
||||
$this->identityProviders = array();
|
||||
$this->serviceProviders = array();
|
||||
|
||||
$entitiesDescriptor = Utilities::xpQuery($xml, './saml_metadata:EntitiesDescriptor');
|
||||
|
||||
if(!empty($entitiesDescriptor))
|
||||
$entityDescriptors = Utilities::xpQuery($entitiesDescriptor[0], './saml_metadata:EntityDescriptor');
|
||||
else
|
||||
$entityDescriptors = Utilities::xpQuery($xml, './saml_metadata:EntityDescriptor');
|
||||
|
||||
foreach ($entityDescriptors as $entityDescriptor) {
|
||||
$idpSSODescriptor = Utilities::xpQuery($entityDescriptor, './saml_metadata:IDPSSODescriptor');
|
||||
|
||||
if(isset($idpSSODescriptor) && !empty($idpSSODescriptor)){
|
||||
array_push($this->identityProviders,new IdentityProviders($entityDescriptor));
|
||||
}
|
||||
//TODO: add sp descriptor
|
||||
}
|
||||
}
|
||||
|
||||
public function getIdentityProviders(){
|
||||
return $this->identityProviders;
|
||||
}
|
||||
|
||||
public function getServiceProviders(){
|
||||
return $this->serviceProviders;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class IdentityProviders{
|
||||
|
||||
private $idpName;
|
||||
private $entityID;
|
||||
private $loginDetails;
|
||||
private $logoutDetails;
|
||||
private $signingCertificate;
|
||||
private $encryptionCertificate;
|
||||
private $signedRequest;
|
||||
|
||||
public function __construct(DOMElement $xml = NULL){
|
||||
|
||||
$this->idpName = '';
|
||||
$this->loginDetails = array();
|
||||
$this->logoutDetails = array();
|
||||
$this->signingCertificate = array();
|
||||
$this->encryptionCertificate = array();
|
||||
|
||||
if ($xml->hasAttribute('entityID')) {
|
||||
$this->entityID = $xml->getAttribute('entityID');
|
||||
}
|
||||
|
||||
if($xml->hasAttribute('WantAuthnRequestsSigned')){
|
||||
$this->signedRequest = $xml->getAttribute('WantAuthnRequestsSigned');
|
||||
}
|
||||
|
||||
$idpSSODescriptor = Utilities::xpQuery($xml, './saml_metadata:IDPSSODescriptor');
|
||||
|
||||
if (count($idpSSODescriptor) > 1) {
|
||||
throw new Exception('More than one <IDPSSODescriptor> in <EntityDescriptor>.');
|
||||
} elseif (empty($idpSSODescriptor)) {
|
||||
throw new Exception('Missing required <IDPSSODescriptor> in <EntityDescriptor>.');
|
||||
}
|
||||
$idpSSODescriptorEL = $idpSSODescriptor[0];
|
||||
|
||||
$info = Utilities::xpQuery($xml, './saml_metadata:Extensions');
|
||||
|
||||
if($info)
|
||||
$this->parseInfo($idpSSODescriptorEL);
|
||||
$this->parseSSOService($idpSSODescriptorEL);
|
||||
$this->parseSLOService($idpSSODescriptorEL);
|
||||
$this->parsex509Certificate($idpSSODescriptorEL);
|
||||
|
||||
}
|
||||
|
||||
private function parseInfo($xml){
|
||||
$displayNames = Utilities::xpQuery($xml, './mdui:UIInfo/mdui:DisplayName');
|
||||
foreach ($displayNames as $name) {
|
||||
if($name->hasAttribute('xml:lang') && $name->getAttribute('xml:lang')=="en"){
|
||||
$this->idpName = $name->textContent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function parseSSOService($xml){
|
||||
$ssoServices = Utilities::xpQuery($xml, './saml_metadata:SingleSignOnService');
|
||||
foreach ($ssoServices as $ssoService) {
|
||||
$binding = str_replace("urn:oasis:names:tc:SAML:2.0:bindings:","",$ssoService->getAttribute('Binding'));
|
||||
$this->loginDetails = array_merge(
|
||||
$this->loginDetails,
|
||||
array($binding => $ssoService->getAttribute('Location'))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private function parseSLOService($xml){
|
||||
$sloServices = Utilities::xpQuery($xml, './saml_metadata:SingleLogoutService');
|
||||
foreach ($sloServices as $sloService) {
|
||||
$binding = str_replace("urn:oasis:names:tc:SAML:2.0:bindings:","",$sloService->getAttribute('Binding'));
|
||||
$this->logoutDetails = array_merge(
|
||||
$this->logoutDetails,
|
||||
array($binding => $sloService->getAttribute('Location'))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private function parsex509Certificate($xml){
|
||||
foreach ( Utilities::xpQuery($xml, './saml_metadata:KeyDescriptor') as $KeyDescriptorNode ) {
|
||||
if($KeyDescriptorNode->hasAttribute('use')){
|
||||
if($KeyDescriptorNode->getAttribute('use')=='encryption'){
|
||||
$this->parseEncryptionCertificate($KeyDescriptorNode);
|
||||
}else{
|
||||
$this->parseSigningCertificate($KeyDescriptorNode);
|
||||
}
|
||||
}else{
|
||||
$this->parseSigningCertificate($KeyDescriptorNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function parseSigningCertificate($xml){
|
||||
$certNode = Utilities::xpQuery($xml, './ds:KeyInfo/ds:X509Data/ds:X509Certificate');
|
||||
$certData = trim($certNode[0]->textContent);
|
||||
$certData = str_replace(array ( "\r", "\n", "\t", ' '), '', $certData);
|
||||
if(!empty($certNode))
|
||||
array_push($this->signingCertificate, Utilities::sanitize_certificate( $certData ));
|
||||
}
|
||||
|
||||
|
||||
private function parseEncryptionCertificate($xml){
|
||||
$certNode = Utilities::xpQuery($xml, './ds:KeyInfo/ds:X509Data/ds:X509Certificate');
|
||||
$certData = trim($certNode[0]->textContent);
|
||||
$certData = str_replace(array ( "\r", "\n", "\t", ' '), '', $certData);
|
||||
if(!empty($certNode))
|
||||
array_push($this->encryptionCertificate, $certData);
|
||||
}
|
||||
|
||||
public function getIdpName(){
|
||||
return "";
|
||||
}
|
||||
|
||||
public function getEntityID(){
|
||||
return $this->entityID;
|
||||
}
|
||||
|
||||
public function getLoginURL($binding){
|
||||
return $this->loginDetails[$binding];
|
||||
}
|
||||
|
||||
public function getLogoutURL($binding){
|
||||
return $this->logoutDetails[$binding];
|
||||
}
|
||||
|
||||
public function getLoginDetails(){
|
||||
return $this->loginDetails;
|
||||
}
|
||||
|
||||
public function getLogoutDetails(){
|
||||
return $this->logoutDetails;
|
||||
}
|
||||
|
||||
public function getSigningCertificate(){
|
||||
return $this->signingCertificate;
|
||||
}
|
||||
|
||||
public function getEncryptionCertificate(){
|
||||
return $this->encryptionCertificate[0];
|
||||
}
|
||||
|
||||
public function isRequestSigned(){
|
||||
return $this->signedRequest;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
33
core/lib/saml2/MoSAMLBasicEnum.php
Normal file
33
core/lib/saml2/MoSAMLBasicEnum.php
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
abstract class MoSAMLBasicEnum {
|
||||
private static $constCacheArray = NULL;
|
||||
|
||||
public static function getConstants() {
|
||||
if (self::$constCacheArray == NULL) {
|
||||
self::$constCacheArray = array();
|
||||
}
|
||||
$calledClass = get_called_class();
|
||||
if (!array_key_exists($calledClass, self::$constCacheArray)) {
|
||||
$reflect = new ReflectionClass($calledClass);
|
||||
self::$constCacheArray[$calledClass] = $reflect->getConstants();
|
||||
}
|
||||
return self::$constCacheArray[$calledClass];
|
||||
}
|
||||
|
||||
public static function isValidName($name, $strict = false) {
|
||||
$constants = self::getConstants();
|
||||
|
||||
if ($strict) {
|
||||
return array_key_exists($name, $constants);
|
||||
}
|
||||
|
||||
$keys = array_map('strtolower', array_keys($constants));
|
||||
return in_array(strtolower($name), $keys);
|
||||
}
|
||||
|
||||
public static function isValidValue($value, $strict = true) {
|
||||
$values = array_values(self::getConstants());
|
||||
return in_array($value, $values, $strict);
|
||||
}
|
||||
}
|
||||
109
core/lib/saml2/MoSAMLPointer.php
Normal file
109
core/lib/saml2/MoSAMLPointer.php
Normal file
@@ -0,0 +1,109 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: HP
|
||||
* Date: 9/11/2018
|
||||
* Time: 9:48 AM
|
||||
*/
|
||||
|
||||
class MoSAMLPointer
|
||||
{
|
||||
|
||||
private $content,$anchor_id,$edge,$align,$active,$pointer_name;
|
||||
|
||||
function __construct($header,$body,$anchor_id,$edge,$align,$active,$prefix){
|
||||
|
||||
$this->content = '<h3>'.$header.'</h3>';
|
||||
$this->content .= '<p id="'.$prefix.'" style="font-size: initial;">' .$body . '</p>';
|
||||
$this-> anchor_id = $anchor_id;
|
||||
$this->edge = $edge;
|
||||
$this->align = $align;
|
||||
$this->active = $active;
|
||||
$this->pointer_name = 'miniorange_admin_pointer_'.$prefix;
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
function return_array(){
|
||||
return array(
|
||||
// The content needs to point to what we created above in the $new_pointer_content variable
|
||||
'content' => $this->content,
|
||||
|
||||
// In order for the custom pointer to appear in the right location we need to specify the ID
|
||||
// of the element we want it to appear next to
|
||||
'anchor_id' => $this->anchor_id,
|
||||
|
||||
// On what edge do we want the pointer to appear. Options are 'top', 'left', 'right', 'bottom'
|
||||
'edge' => $this->edge,
|
||||
|
||||
// How do we want out custom pointer to align to the element it is attached to. Options are
|
||||
// 'left', 'right', 'center'
|
||||
'align' => $this->align,
|
||||
|
||||
// This is how we tell the pointer to be dismissed or not. Make sure that the 'new_items'
|
||||
// string matches the string at the beginning of the array item
|
||||
'active' => $this->active
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getContent()
|
||||
{
|
||||
return $this->content;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $content
|
||||
*/
|
||||
public function setContent($content)
|
||||
{
|
||||
$this->content = $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getAnchorId()
|
||||
{
|
||||
return $this->anchor_id;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getEdge()
|
||||
{
|
||||
return $this->edge;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getActive()
|
||||
{
|
||||
return $this->active;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $active
|
||||
*/
|
||||
public function setActive($active)
|
||||
{
|
||||
$this->active = $active;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getPointerName()
|
||||
{
|
||||
return $this->pointer_name;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
51
core/lib/saml2/PointersManager.php
Normal file
51
core/lib/saml2/PointersManager.php
Normal file
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
class MoSAMLPointersManager {
|
||||
|
||||
private $pfile;
|
||||
private $version;
|
||||
private $prefix;
|
||||
private $pointers = array();
|
||||
|
||||
public function __construct( $file, $version, $prefix ) {
|
||||
$this->pfile = file_exists( $file ) ? $file : FALSE;
|
||||
$this->version = str_replace( '.', '_', $version );
|
||||
$this->prefix = $prefix;
|
||||
}
|
||||
|
||||
public function parse() {
|
||||
if ( empty( $this->pfile ) ) return;
|
||||
$pointers = (array) require_once $this->pfile;
|
||||
if ( empty($pointers) ) return;
|
||||
foreach ( $pointers as $i => $pointer ) {
|
||||
if(is_array($pointer)){
|
||||
$pointer['id'] = "{$this->prefix}{$this->version}_{$i}";
|
||||
$this->pointers[$pointer['id']] = (object) $pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function filter( $page ) {
|
||||
if ( empty( $this->pointers ) ) return array();
|
||||
$uid = get_current_user_id();
|
||||
$no = explode( ',', (string) get_user_meta( $uid, 'dismissed_wp_pointers', TRUE ) );
|
||||
$active_ids = array_diff( array_keys( $this->pointers ), $no );
|
||||
$good = array();
|
||||
foreach( $this->pointers as $i => $pointer ) {
|
||||
if (
|
||||
in_array( $i, $active_ids, TRUE ) // is active
|
||||
&& isset( $pointer->where ) // has where
|
||||
&& in_array( $page, (array) $pointer->where, TRUE ) // current page is in where
|
||||
) {
|
||||
$good[] = $pointer;
|
||||
}
|
||||
}
|
||||
$count = count( $good );
|
||||
if ( $good === 0 ) return array();
|
||||
foreach( array_values( $good ) as $i => $pointer ) {
|
||||
$good[$i]->next = $i+1 < $count ? $good[$i+1]->id : '';
|
||||
}
|
||||
|
||||
return $good;
|
||||
}
|
||||
}
|
||||
114
core/lib/saml2/Response.php
Normal file
114
core/lib/saml2/Response.php
Normal file
@@ -0,0 +1,114 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of miniOrange SAML plugin.
|
||||
*
|
||||
* miniOrange SAML plugin is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* miniOrange SAML plugin is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with miniOrange SAML plugin. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
include 'Assertion.php';
|
||||
|
||||
/**
|
||||
* Class for SAML2 Response messages.
|
||||
*
|
||||
*/
|
||||
class SAML2_Response
|
||||
{
|
||||
/**
|
||||
* The assertions in this response.
|
||||
*/
|
||||
private $assertions;
|
||||
|
||||
/**
|
||||
* The destination URL in this response.
|
||||
*/
|
||||
private $destination;
|
||||
|
||||
private $certificates;
|
||||
private $signatureData;
|
||||
|
||||
/**
|
||||
* Constructor for SAML 2 response messages.
|
||||
*
|
||||
* @param DOMElement|NULL $xml The input message.
|
||||
*/
|
||||
public function __construct(DOMElement $xml = NULL)
|
||||
{
|
||||
//parent::__construct('Response', $xml);
|
||||
|
||||
$this->assertions = array();
|
||||
$this->certificates = array();
|
||||
|
||||
if ($xml === NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
$sig = Utilities::validateElement($xml);
|
||||
if ($sig !== FALSE) {
|
||||
$this->certificates = $sig['Certificates'];
|
||||
$this->signatureData = $sig;
|
||||
}
|
||||
|
||||
/* set the destination from saml response */
|
||||
if ($xml->hasAttribute('Destination')) {
|
||||
$this->destination = $xml->getAttribute('Destination');
|
||||
}
|
||||
|
||||
for ($node = $xml->firstChild; $node !== NULL; $node = $node->nextSibling) {
|
||||
if ($node->namespaceURI !== 'urn:oasis:names:tc:SAML:2.0:assertion') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($node->localName === 'Assertion' || $node->localName === 'EncryptedAssertion') {
|
||||
$this->assertions[] = new SAML2_Assertion($node);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the assertions in this response.
|
||||
*
|
||||
* @return SAML2_Assertion[]|SAML2_EncryptedAssertion[]
|
||||
*/
|
||||
public function getAssertions()
|
||||
{
|
||||
return $this->assertions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the assertions that should be included in this response.
|
||||
*
|
||||
* @param SAML2_Assertion[]|SAML2_EncryptedAssertion[] The assertions.
|
||||
*/
|
||||
public function setAssertions(array $assertions)
|
||||
{
|
||||
$this->assertions = $assertions;
|
||||
}
|
||||
|
||||
public function getDestination()
|
||||
{
|
||||
return $this->destination;
|
||||
}
|
||||
|
||||
|
||||
public function getCertificates()
|
||||
{
|
||||
return $this->certificates;
|
||||
}
|
||||
|
||||
public function getSignatureData()
|
||||
{
|
||||
return $this->signatureData;
|
||||
}
|
||||
}
|
||||
511
core/lib/saml2/SAML2Core/MoXMLSecEnc.php
Normal file
511
core/lib/saml2/SAML2Core/MoXMLSecEnc.php
Normal file
@@ -0,0 +1,511 @@
|
||||
<?php
|
||||
namespace RobRichards\XMLSecLibs;
|
||||
|
||||
use DOMDocument;
|
||||
use DOMElement;
|
||||
use DOMNode;
|
||||
use DOMXPath;
|
||||
use Exception;
|
||||
use RobRichards\XMLSecLibs\Utils\MoXPath 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 MoXMLSecEnc
|
||||
{
|
||||
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 = MoXMLSecurityDSig::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 MoXMLSecurityKey $objKey The encryption key and algorithm.
|
||||
* @param bool $replace Whether the encrypted node should be replaced in the original tree. Default is true.
|
||||
* @return DOMElement The <xenc:EncryptedData>-element.
|
||||
*@throws Exception
|
||||
*
|
||||
*/
|
||||
public function encryptNode($objKey, $replace = true)
|
||||
{
|
||||
$data = '';
|
||||
if (empty($this->rawNode)) {
|
||||
throw new Exception('Node to encrypt has not been set');
|
||||
}
|
||||
if (! $objKey instanceof MoXMLSecurityKey) {
|
||||
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 MoXMLSecurityKey $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 MoXMLSecurityKey $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 MoXMLSecurityKey) {
|
||||
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 MoXMLSecurityKey $srcKey
|
||||
* @param MoXMLSecurityKey $rawKey
|
||||
* @param bool $append
|
||||
* @throws Exception
|
||||
*/
|
||||
public function encryptKey($srcKey, $rawKey, $append=true)
|
||||
{
|
||||
if ((! $srcKey instanceof MoXMLSecurityKey) || (! $rawKey instanceof MoXMLSecurityKey)) {
|
||||
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 MoXMLSecurityKey $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|MoXMLSecurityKey
|
||||
*/
|
||||
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 MoXMLSecurityKey($attrAlgorithm, array('type' => 'private'));
|
||||
} catch (Exception $e) {
|
||||
return null;
|
||||
}
|
||||
return $objKey;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param null|MoXMLSecurityKey $objBaseKey
|
||||
* @param null|DOMNode $node
|
||||
* @return null|MoXMLSecurityKey
|
||||
* @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', MoXMLSecurityDSig::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 = MoXMLSecurityKey::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="'.MoXPath::filterAttrValue($id, MoXPath::DOUBLE_QUOTE).'"]';
|
||||
$keyElement = $xpath->query($query)->item(0);
|
||||
if (!$keyElement) {
|
||||
throw new Exception("Unable to locate EncryptedKey with @Id='$id'.");
|
||||
}
|
||||
|
||||
return MoXMLSecurityKey::fromEncryptedKeyElement($keyElement);
|
||||
case 'EncryptedKey':
|
||||
return MoXMLSecurityKey::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|MoXMLSecurityKey $objBaseKey
|
||||
* @param null|DOMNode $node
|
||||
* @return null|MoXMLSecurityKey
|
||||
*/
|
||||
public function locateKeyInfo($objBaseKey=null, $node=null)
|
||||
{
|
||||
if (empty($node)) {
|
||||
$node = $this->rawNode;
|
||||
}
|
||||
return self::staticLocateKeyInfo($objBaseKey, $node);
|
||||
}
|
||||
}
|
||||
1162
core/lib/saml2/SAML2Core/MoXMLSecurityDSig.php
Normal file
1162
core/lib/saml2/SAML2Core/MoXMLSecurityDSig.php
Normal file
File diff suppressed because it is too large
Load Diff
800
core/lib/saml2/SAML2Core/MoXMLSecurityKey.php
Normal file
800
core/lib/saml2/SAML2Core/MoXMLSecurityKey.php
Normal file
@@ -0,0 +1,800 @@
|
||||
<?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 MoXMLSecurityKey
|
||||
{
|
||||
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 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 MoXMLSecEnc|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'] = 32;
|
||||
$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'] = 32;
|
||||
$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_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 25 characters for this cipher');
|
||||
}
|
||||
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.
|
||||
* @return MoXMLSecurityKey The new key.
|
||||
* @throws Exception
|
||||
*
|
||||
*/
|
||||
public static function fromEncryptedKeyElement(DOMElement $element)
|
||||
{
|
||||
|
||||
$objenc = new MoXMLSecEnc();
|
||||
$objenc->setNode($element);
|
||||
if (! $objKey = $objenc->locateKey()) {
|
||||
throw new Exception("Unable to locate algorithm for this Encrypted Key");
|
||||
}
|
||||
$objKey->isEncrypted = true;
|
||||
$objKey->encryptedCtx = $objenc;
|
||||
MoXMLSecEnc::staticLocateKeyInfo($objKey, $element);
|
||||
return $objKey;
|
||||
}
|
||||
|
||||
}
|
||||
44
core/lib/saml2/SAML2Core/Utils/MoXPath.php
Normal file
44
core/lib/saml2/SAML2Core/Utils/MoXPath.php
Normal file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace RobRichards\XMLSecLibs\Utils;
|
||||
|
||||
class MoXPath
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
830
core/lib/saml2/Utilities.php
Normal file
830
core/lib/saml2/Utilities.php
Normal file
@@ -0,0 +1,830 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of miniOrange SAML plugin.
|
||||
*
|
||||
* miniOrange SAML plugin is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* miniOrange SAML plugin is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with miniOrange SAML plugin. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
include_once 'xmlseclibs.php';
|
||||
use \RobRichards\XMLSecLibs\MoXMLSecurityKey;
|
||||
use \RobRichards\XMLSecLibs\MoXMLSecurityDSig;
|
||||
use \RobRichards\XMLSecLibs\MoXMLSecEnc;
|
||||
|
||||
class Utilities {
|
||||
|
||||
public static function generateID() {
|
||||
return '_' . self::stringToHex(self::generateRandomBytes(21));
|
||||
}
|
||||
|
||||
public static function stringToHex($bytes) {
|
||||
$ret = '';
|
||||
for($i = 0; $i < strlen($bytes); $i++) {
|
||||
$ret .= sprintf('%02x', ord($bytes[$i]));
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
public static function generateRandomBytes($length, $fallback = TRUE) {
|
||||
|
||||
return openssl_random_pseudo_bytes($length);
|
||||
}
|
||||
|
||||
public static function createAuthnRequest($acsUrl, $issuer, $force_authn = 'false') {
|
||||
$requestXmlStr = '<?xml version="1.0" encoding="UTF-8"?>' .
|
||||
'<samlp:AuthnRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" ID="' . self::generateID() .
|
||||
'" Version="2.0" IssueInstant="' . self::generateTimestamp() . '"';
|
||||
if( $force_authn == 'true') {
|
||||
$requestXmlStr .= ' ForceAuthn="true"';
|
||||
}
|
||||
$requestXmlStr .= ' ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" AssertionConsumerServiceURL="' . $acsUrl .
|
||||
'" ><saml:Issuer xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">' . $issuer . '</saml:Issuer></samlp:AuthnRequest>';
|
||||
$deflatedStr = gzdeflate($requestXmlStr);
|
||||
$base64EncodedStr = base64_encode($deflatedStr);
|
||||
$urlEncoded = urlencode($base64EncodedStr);
|
||||
update_option('MO_SAML_REQUEST',$base64EncodedStr);
|
||||
|
||||
return $urlEncoded;
|
||||
}
|
||||
|
||||
public static function createSAMLRequest($acsUrl, $issuer, $destination, $force_authn = 'false') {
|
||||
|
||||
$requestXmlStr = '<?xml version="1.0" encoding="UTF-8"?>' .
|
||||
'<samlp:AuthnRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns="urn:oasis:names:tc:SAML:2.0:assertion" ID="' . self::generateID() .
|
||||
'" Version="2.0" IssueInstant="' . self::generateTimestamp() . '"';
|
||||
if( $force_authn == 'true') {
|
||||
$requestXmlStr .= ' ForceAuthn="true"';
|
||||
}
|
||||
$requestXmlStr .= ' ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" AssertionConsumerServiceURL="' . $acsUrl .
|
||||
'" Destination="' . htmlspecialchars($destination) . '"><saml:Issuer xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">' . $issuer . '</saml:Issuer><samlp:NameIDPolicy AllowCreate="true" Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"
|
||||
/></samlp:AuthnRequest>';
|
||||
$samlRequest = base64_encode($requestXmlStr);
|
||||
update_option('MO_SAML_REQUEST',$samlRequest);
|
||||
return $requestXmlStr;
|
||||
}
|
||||
|
||||
public static function generateTimestamp($instant = NULL) {
|
||||
if($instant === NULL) {
|
||||
$instant = time();
|
||||
}
|
||||
return gmdate('Y-m-d\TH:i:s\Z', $instant);
|
||||
}
|
||||
|
||||
public static function xpQuery(DOMNode $node, $query)
|
||||
{
|
||||
|
||||
static $xpCache = NULL;
|
||||
|
||||
if ($node instanceof DOMDocument) {
|
||||
$doc = $node;
|
||||
} else {
|
||||
$doc = $node->ownerDocument;
|
||||
}
|
||||
|
||||
if ($xpCache === NULL || !$xpCache->document->isSameNode($doc)) {
|
||||
$xpCache = new DOMXPath($doc);
|
||||
$xpCache->registerNamespace('soap-env', 'http://schemas.xmlsoap.org/soap/envelope/');
|
||||
$xpCache->registerNamespace('saml_protocol', 'urn:oasis:names:tc:SAML:2.0:protocol');
|
||||
$xpCache->registerNamespace('saml_assertion', 'urn:oasis:names:tc:SAML:2.0:assertion');
|
||||
$xpCache->registerNamespace('saml_metadata', 'urn:oasis:names:tc:SAML:2.0:metadata');
|
||||
$xpCache->registerNamespace('ds', 'http://www.w3.org/2000/09/xmldsig#');
|
||||
$xpCache->registerNamespace('xenc', 'http://www.w3.org/2001/04/xmlenc#');
|
||||
}
|
||||
|
||||
$results = $xpCache->query($query, $node);
|
||||
$ret = array();
|
||||
for ($i = 0; $i < $results->length; $i++) {
|
||||
$ret[$i] = $results->item($i);
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
public static function parseNameId(DOMElement $xml)
|
||||
{
|
||||
$ret = array('Value' => trim($xml->textContent));
|
||||
|
||||
foreach (array('NameQualifier', 'SPNameQualifier', 'Format') as $attr) {
|
||||
if ($xml->hasAttribute($attr)) {
|
||||
$ret[$attr] = $xml->getAttribute($attr);
|
||||
}
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
public static function xsDateTimeToTimestamp($time)
|
||||
{
|
||||
$matches = array();
|
||||
|
||||
// We use a very strict regex to parse the timestamp.
|
||||
$regex = '/^(\\d\\d\\d\\d)-(\\d\\d)-(\\d\\d)T(\\d\\d):(\\d\\d):(\\d\\d)(?:\\.\\d+)?Z$/D';
|
||||
if (preg_match($regex, $time, $matches) == 0) {
|
||||
echo sprintf("Invalid SAML2 timestamp passed to xsDateTimeToTimestamp: ". htmlspecialchars($time));
|
||||
exit;
|
||||
}
|
||||
|
||||
// Extract the different components of the time from the matches in the regex.
|
||||
// intval will ignore leading zeroes in the string.
|
||||
$year = intval($matches[1]);
|
||||
$month = intval($matches[2]);
|
||||
$day = intval($matches[3]);
|
||||
$hour = intval($matches[4]);
|
||||
$minute = intval($matches[5]);
|
||||
$second = intval($matches[6]);
|
||||
|
||||
// We use gmmktime because the timestamp will always be given
|
||||
//in UTC.
|
||||
$ts = gmmktime($hour, $minute, $second, $month, $day, $year);
|
||||
|
||||
return $ts;
|
||||
}
|
||||
|
||||
public static function extractStrings(DOMElement $parent, $namespaceURI, $localName)
|
||||
{
|
||||
|
||||
|
||||
|
||||
$ret = array();
|
||||
for ($node = $parent->firstChild; $node !== NULL; $node = $node->nextSibling) {
|
||||
if ($node->namespaceURI !== $namespaceURI || $node->localName !== $localName) {
|
||||
continue;
|
||||
}
|
||||
$ret[] = trim($node->textContent);
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
public static function validateElement(DOMElement $root)
|
||||
{
|
||||
//$data = $root->ownerDocument->saveXML($root);
|
||||
//echo htmlspecialchars($data);
|
||||
|
||||
/* Create an XML security object. */
|
||||
$objXMLSecDSig = new MoXMLSecurityDSig();
|
||||
|
||||
/* Both SAML messages and SAML assertions use the 'ID' attribute. */
|
||||
$objXMLSecDSig->idKeys[] = 'ID';
|
||||
|
||||
|
||||
/* Locate the XMLDSig Signature element to be used. */
|
||||
$signatureElement = self::xpQuery($root, './ds:Signature');
|
||||
//print_r($signatureElement);
|
||||
|
||||
if (count($signatureElement) === 0) {
|
||||
/* We don't have a signature element to validate. */
|
||||
return FALSE;
|
||||
} elseif (count($signatureElement) > 1) {
|
||||
echo sprintf("XMLSec: more than one signature element in root.");
|
||||
exit;
|
||||
}/* elseif ((in_array('Response', $signatureElement) && $ocurrence['Response'] > 1) ||
|
||||
(in_array('Assertion', $signatureElement) && $ocurrence['Assertion'] > 1) ||
|
||||
!in_array('Response', $signatureElement) && !in_array('Assertion', $signatureElement)
|
||||
) {
|
||||
return false;
|
||||
} */
|
||||
|
||||
$signatureElement = $signatureElement[0];
|
||||
$objXMLSecDSig->sigNode = $signatureElement;
|
||||
|
||||
/* Canonicalize the XMLDSig SignedInfo element in the message. */
|
||||
$objXMLSecDSig->canonicalizeSignedInfo();
|
||||
|
||||
/* Validate referenced xml nodes. */
|
||||
if (!$objXMLSecDSig->validateReference()) {
|
||||
echo sprintf("XMLSec: digest validation failed");
|
||||
exit;
|
||||
}
|
||||
|
||||
/* Check that $root is one of the signed nodes. */
|
||||
$rootSigned = FALSE;
|
||||
/** @var DOMNode $signedNode */
|
||||
foreach ($objXMLSecDSig->getValidatedNodes() as $signedNode) {
|
||||
if ($signedNode->isSameNode($root)) {
|
||||
$rootSigned = TRUE;
|
||||
break;
|
||||
} elseif ($root->parentNode instanceof DOMDocument && $signedNode->isSameNode($root->ownerDocument)) {
|
||||
/* $root is the root element of a signed document. */
|
||||
$rootSigned = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$rootSigned) {
|
||||
echo sprintf("XMLSec: The root element is not signed.");
|
||||
exit;
|
||||
}
|
||||
|
||||
/* Now we extract all available X509 certificates in the signature element. */
|
||||
$certificates = array();
|
||||
foreach (self::xpQuery($signatureElement, './ds:KeyInfo/ds:X509Data/ds:X509Certificate') as $certNode) {
|
||||
$certData = trim($certNode->textContent);
|
||||
$certData = str_replace(array("\r", "\n", "\t", ' '), '', $certData);
|
||||
$certificates[] = $certData;
|
||||
//echo "CertDate: " . $certData . "<br />";
|
||||
}
|
||||
|
||||
$ret = array(
|
||||
'Signature' => $objXMLSecDSig,
|
||||
'Certificates' => $certificates,
|
||||
);
|
||||
|
||||
//echo "Signature validated";
|
||||
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static function validateSignature(array $info, MoXMLSecurityKey $key)
|
||||
{
|
||||
|
||||
|
||||
/** @var MoXMLSecurityDSig $objXMLSecDSig */
|
||||
$objXMLSecDSig = $info['Signature'];
|
||||
|
||||
$sigMethod = self::xpQuery($objXMLSecDSig->sigNode, './ds:SignedInfo/ds:SignatureMethod');
|
||||
if (empty($sigMethod)) {
|
||||
echo sprintf('Missing SignatureMethod element');
|
||||
exit();
|
||||
}
|
||||
$sigMethod = $sigMethod[0];
|
||||
if (!$sigMethod->hasAttribute('Algorithm')) {
|
||||
echo sprintf('Missing Algorithm-attribute on SignatureMethod element.');
|
||||
exit;
|
||||
}
|
||||
$algo = $sigMethod->getAttribute('Algorithm');
|
||||
|
||||
if ($key->type === MoXMLSecurityKey::RSA_SHA1 && $algo !== $key->type) {
|
||||
$key = self::castKey($key, $algo);
|
||||
}
|
||||
|
||||
/* Check the signature. */
|
||||
if (! $objXMLSecDSig->verify($key)) {
|
||||
echo sprintf('Unable to validate Signature');
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
public static function castKey(MoXMLSecurityKey $key, $algorithm, $type = 'public')
|
||||
{
|
||||
|
||||
// do nothing if algorithm is already the type of the key
|
||||
if ($key->type === $algorithm) {
|
||||
return $key;
|
||||
}
|
||||
|
||||
$keyInfo = openssl_pkey_get_details($key->key);
|
||||
if ($keyInfo === FALSE) {
|
||||
echo sprintf('Unable to get key details from XMLSecurityKey.');
|
||||
exit;
|
||||
}
|
||||
if (!isset($keyInfo['key'])) {
|
||||
echo sprintf('Missing key in public key details.');
|
||||
exit;
|
||||
}
|
||||
|
||||
$newKey = new MoXMLSecurityKey($algorithm, array('type'=>$type));
|
||||
$newKey->loadKey($keyInfo['key']);
|
||||
|
||||
return $newKey;
|
||||
}
|
||||
|
||||
public static function processResponse($currentURL, $certFingerprint, $signatureData,
|
||||
SAML2_Response $response, $certNumber,$relayState) {
|
||||
|
||||
$assertion = current($response->getAssertions());
|
||||
|
||||
$notBefore = $assertion->getNotBefore();
|
||||
if ($notBefore !== NULL && $notBefore > time() + 60) {
|
||||
error_log('Received an assertion that is valid in the future. Check clock synchronization on IdP and SP.');
|
||||
return false;
|
||||
}
|
||||
|
||||
$notOnOrAfter = $assertion->getNotOnOrAfter();
|
||||
if ($notOnOrAfter !== NULL && $notOnOrAfter <= time() - 60) {
|
||||
error_log('Received an assertion that has expired. Check clock synchronization on IdP and SP.');
|
||||
return false;
|
||||
}
|
||||
|
||||
$sessionNotOnOrAfter = $assertion->getSessionNotOnOrAfter();
|
||||
if ($sessionNotOnOrAfter !== NULL && $sessionNotOnOrAfter <= time() - 60) {
|
||||
error_log('Received an assertion with a session that has expired. Check clock synchronization on IdP and SP.');
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Validate Response-element destination. */
|
||||
$msgDestination = $response->getDestination();
|
||||
if(substr($msgDestination, -1) == '/') {
|
||||
$msgDestination = substr($msgDestination, 0, -1);
|
||||
}
|
||||
if(substr($currentURL, -1) == '/') {
|
||||
$currentURL = substr($currentURL, 0, -1);
|
||||
}
|
||||
|
||||
if ($msgDestination !== NULL && $msgDestination !== $currentURL) {
|
||||
echo sprintf('Destination in response doesn\'t match the current URL. Destination is "' .
|
||||
htmlspecialchars($msgDestination) . '", current URL is "' . htmlspecialchars($currentURL) . '".');
|
||||
exit;
|
||||
}
|
||||
|
||||
$responseSigned = self::checkSign($certFingerprint, $signatureData, $certNumber,$relayState);
|
||||
|
||||
if (!$responseSigned) {
|
||||
error_log('SAML: Responses is not signed');
|
||||
}
|
||||
|
||||
/* Returning boolean $responseSigned */
|
||||
return $responseSigned;
|
||||
}
|
||||
|
||||
|
||||
public static function checkSign($certFingerprint, $signatureData, $certNumber, $relayState) {
|
||||
$certificates = $signatureData['Certificates'];
|
||||
error_log('SAML: certificate count = '.count($certificates));
|
||||
if (count($certificates) === 0) {
|
||||
$storedCerts = maybe_unserialize(get_option('saml_x509_certificate'));
|
||||
$pemCert = $storedCerts[$certNumber];
|
||||
}else{
|
||||
$fpArray = array();
|
||||
$fpArray[] = $certFingerprint;
|
||||
$pemCert = self::findCertificate($fpArray, $certificates, $relayState);
|
||||
if($pemCert==false)
|
||||
return false;
|
||||
}
|
||||
|
||||
$lastException = NULL;
|
||||
|
||||
$key = new MoXMLSecurityKey(MoXMLSecurityKey::RSA_SHA1, array('type'=>'public'));
|
||||
$key->loadKey($pemCert);
|
||||
|
||||
try {
|
||||
/*
|
||||
* Make sure that we have a valid signature
|
||||
*/
|
||||
self::validateSignature($signatureData, $key);
|
||||
return TRUE;
|
||||
} catch (Exception $e) {
|
||||
$lastException = $e;
|
||||
}
|
||||
|
||||
|
||||
/* We were unable to validate the signature with any of our keys. */
|
||||
if ($lastException !== NULL) {
|
||||
throw $lastException;
|
||||
} else {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
public static function validateIssuerAndAudience($samlResponse, $spEntityId, $issuerToValidateAgainst, $relayState) {
|
||||
$issuer = current($samlResponse->getAssertions())->getIssuer();
|
||||
$assertion = current($samlResponse->getAssertions());
|
||||
$audiences = $assertion->getValidAudiences();
|
||||
if(strcmp($issuerToValidateAgainst, $issuer) === 0) {
|
||||
if(!empty($audiences)) {
|
||||
if(in_array($spEntityId, $audiences, TRUE)) {
|
||||
return TRUE;
|
||||
} else {
|
||||
if($relayState=='testValidate'){
|
||||
$Error_message=mo_saml_options_error_constants::Error_invalid_audience;
|
||||
$Cause_message = mo_saml_options_error_constants::Cause_invalid_audience;
|
||||
echo '<div style="font-family:Calibri;padding:0 3%;">';
|
||||
echo '<div style="color: #a94442;background-color: #f2dede;padding: 15px;margin-bottom: 20px;text-align:center;border:1px solid #E6B3B2;font-size:18pt;"> ' . __('ERROR','miniorange-saml-20-single-sign-on') . '</div>
|
||||
<div style="color: #a94442;font-size:14pt; margin-bottom:20px;"><p><strong>' . __('Error','miniorange-saml-20-single-sign-on') . ': </strong>'.$Error_message.'</p>
|
||||
|
||||
<p><strong>' . __('Possible Cause','miniorange-saml-20-single-sign-on'). ': </strong>'.$Cause_message.'</p>
|
||||
<p>' . __('Expected one of the Audiences to be','miniorange-saml-20-single-sign-on'). ': '.$spEntityId.'<p>
|
||||
</div>';
|
||||
mo_saml_download_logs($Error_message,$Cause_message);
|
||||
exit;
|
||||
}
|
||||
else
|
||||
{
|
||||
wp_die(__("We could not sign you in. Please contact your administrator",'miniorange-saml-20-single-sign-on'),"Error: Invalid Audience URI");
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if($relayState=='testValidate'){
|
||||
|
||||
$Error_message=mo_saml_options_error_constants::Error_issuer_not_verfied;
|
||||
$Cause_message = mo_saml_options_error_constants::Cause_issuer_not_verfied;
|
||||
update_option('mo_saml_required_issuer',$issuer);
|
||||
echo '<div style="font-family:Calibri;padding:0 3%;">';
|
||||
echo '<div style="color: #a94442;background-color: #f2dede;padding: 15px;margin-bottom: 20px;text-align:center;border:1px solid #E6B3B2;font-size:18pt;">' . __('ERROR','miniorange-saml-20-single-sign-on') . '</div>
|
||||
<div style="color: #a94442;font-size:14pt; margin-bottom:20px;text-align: justify"><p><strong>' . __('Error','miniorange-saml-20-single-sign-on'). ':'.$Error_message.' </strong></p>
|
||||
|
||||
<p><strong>' . __('Possible Cause','miniorange-saml-20-single-sign-on') . ':'.$Cause_message.' </strong></p>
|
||||
<div>
|
||||
<ol style="text-align: center">
|
||||
<form method="post" action="" name="mo_fix_entityid" id="mo_fix_certificate">';
|
||||
wp_nonce_field('mo_fix_entity_id');
|
||||
echo '<input type="hidden" name="option" value="mo_fix_entity_id" />
|
||||
<input type="submit" class="miniorange-button" style="width: 55%" value="' . __('Fix Issue','miniorange-saml-20-single-sign-on' ) .'">
|
||||
</form>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
</div>';
|
||||
|
||||
mo_saml_download_logs($Error_message,$Cause_message);
|
||||
exit;
|
||||
}
|
||||
else
|
||||
{
|
||||
wp_die(__("We could not sign you in. Please contact your administrator",'miniorange-saml-20-single-sign-on'),"Error: Issuer cannot be verified");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static function findCertificate(array $certFingerprints, array $certificates, $relayState) {
|
||||
|
||||
$candidates = array();
|
||||
|
||||
//foreach ($certificates as $cert) {
|
||||
$fp = strtolower(sha1(base64_decode($certificates[0])));
|
||||
if (!in_array($fp, $certFingerprints, TRUE)) {
|
||||
$candidates[] = $fp;
|
||||
return false;
|
||||
//continue;
|
||||
}
|
||||
|
||||
/* We have found a matching fingerprint. */
|
||||
$pem = "-----BEGIN CERTIFICATE-----\n" .
|
||||
chunk_split($certificates[0], 64) .
|
||||
"-----END CERTIFICATE-----\n";
|
||||
|
||||
return $pem;
|
||||
// }
|
||||
|
||||
// if($relayState=='testValidate'){
|
||||
// $pem = "-----BEGIN CERTIFICATE-----<br>" .
|
||||
// chunk_split($cert, 64) .
|
||||
// "<br>-----END CERTIFICATE-----";
|
||||
|
||||
// echo '<div style="font-family:Calibri;padding:0 3%;">';
|
||||
// echo '<div style="color: #a94442;background-color: #f2dede;padding: 15px;margin-bottom: 20px;text-align:center;border:1px solid #E6B3B2;font-size:18pt;"> ERROR</div>
|
||||
// <div style="color: #a94442;font-size:14pt; margin-bottom:20px;"><p><strong>Error: </strong>Unable to find a certificate matching the configured fingerprint.</p>
|
||||
// <p>Please contact your administrator and report the following error:</p>
|
||||
// <p><strong>Possible Cause: </strong>Content of \'X.509 Certificate\' field in Service Provider Settings is incorrect. Please replace it with certificate given below.</p>
|
||||
// <p><strong>Certificate found in SAML Response: </strong><br><br>'.$pem.'</p>
|
||||
// </div>
|
||||
// <div style="margin:3%;display:block;text-align:center;">
|
||||
// <form action="index.php">
|
||||
// <div style="margin:3%;display:block;text-align:center;"><input style="padding:1%;width:100px;background: #0091CD none repeat scroll 0% 0%;cursor: pointer;font-size:15px;border-width: 1px;border-style: solid;border-radius: 3px;white-space: nowrap;box-sizing: border-box;border-color: #0073AA;box-shadow: 0px 1px 0px rgba(120, 200, 230, 0.6) inset;color: #FFF;"type="button" value="Done" onClick="self.close();"></div>';
|
||||
|
||||
// exit;
|
||||
// }
|
||||
// else{
|
||||
// wp_die("We could not sign you in. Please contact your administrator","Error: Invalid Certificate");
|
||||
// }
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypt an encrypted element.
|
||||
*
|
||||
* This is an internal helper function.
|
||||
*
|
||||
* @param DOMElement $encryptedData The encrypted data.
|
||||
* @param MoXMLSecurityKey $inputKey The decryption key.
|
||||
* @param array &$blacklist Blacklisted decryption algorithms.
|
||||
* @return DOMElement The decrypted element.
|
||||
* @throws Exception
|
||||
*/
|
||||
private static function doDecryptElement(DOMElement $encryptedData, MoXMLSecurityKey $inputKey, array &$blacklist)
|
||||
{
|
||||
$enc = new MoXMLSecEnc();
|
||||
$enc->setNode($encryptedData);
|
||||
|
||||
$enc->type = $encryptedData->getAttribute("Type");
|
||||
$symmetricKey = $enc->locateKey($encryptedData);
|
||||
if (!$symmetricKey) {
|
||||
echo sprintf(__('Could not locate key algorithm in encrypted data.','miniorange-saml-20-single-sign-on'));
|
||||
exit;
|
||||
}
|
||||
|
||||
$symmetricKeyInfo = $enc->locateKeyInfo($symmetricKey);
|
||||
if (!$symmetricKeyInfo) {
|
||||
echo sprintf(__('Could not locate <dsig:KeyInfo> for the encrypted key.','miniorange-saml-20-single-sign-on'));
|
||||
exit;
|
||||
}
|
||||
$inputKeyAlgo = $inputKey->getAlgorith();
|
||||
if ($symmetricKeyInfo->isEncrypted) {
|
||||
$symKeyInfoAlgo = $symmetricKeyInfo->getAlgorith();
|
||||
if (in_array($symKeyInfoAlgo, $blacklist, TRUE)) {
|
||||
echo sprintf('Algorithm disabled: ' . var_export($symKeyInfoAlgo, TRUE));
|
||||
exit;
|
||||
}
|
||||
if ($symKeyInfoAlgo === MoXMLSecurityKey::RSA_OAEP_MGF1P && $inputKeyAlgo === MoXMLSecurityKey::RSA_1_5) {
|
||||
/*
|
||||
* The RSA key formats are equal, so loading an RSA_1_5 key
|
||||
* into an RSA_OAEP_MGF1P key can be done without problems.
|
||||
* We therefore pretend that the input key is an
|
||||
* RSA_OAEP_MGF1P key.
|
||||
*/
|
||||
$inputKeyAlgo = MoXMLSecurityKey::RSA_OAEP_MGF1P;
|
||||
}
|
||||
/* Make sure that the input key format is the same as the one used to encrypt the key. */
|
||||
if ($inputKeyAlgo !== $symKeyInfoAlgo) {
|
||||
echo sprintf( 'Algorithm mismatch between input key and key used to encrypt ' .
|
||||
' the symmetric key for the message. Key was: ' .
|
||||
var_export($inputKeyAlgo, TRUE) . '; message was: ' .
|
||||
var_export($symKeyInfoAlgo, TRUE));
|
||||
exit;
|
||||
}
|
||||
/** @var MoXMLSecEnc $encKey */
|
||||
$encKey = $symmetricKeyInfo->encryptedCtx;
|
||||
$symmetricKeyInfo->key = $inputKey->key;
|
||||
$keySize = $symmetricKey->getSymmetricKeySize();
|
||||
if ($keySize === NULL) {
|
||||
/* To protect against "key oracle" attacks, we need to be able to create a
|
||||
* symmetric key, and for that we need to know the key size.
|
||||
*/
|
||||
echo sprintf('Unknown key size for encryption algorithm: ' . var_export($symmetricKey->type, TRUE));
|
||||
exit;
|
||||
}
|
||||
try {
|
||||
$key = $encKey->decryptKey($symmetricKeyInfo);
|
||||
if (strlen($key) != $keySize) {
|
||||
echo sprintf('Unexpected key size (' . strlen($key) * 8 . 'bits) for encryption algorithm: ' .
|
||||
var_export($symmetricKey->type, TRUE));
|
||||
exit;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
/* We failed to decrypt this key. Log it, and substitute a "random" key. */
|
||||
|
||||
/* Create a replacement key, so that it looks like we fail in the same way as if the key was correctly padded. */
|
||||
/* We base the symmetric key on the encrypted key and private key, so that we always behave the
|
||||
* same way for a given input key.
|
||||
*/
|
||||
$encryptedKey = $encKey->getCipherValue();
|
||||
$pkey = openssl_pkey_get_details($symmetricKeyInfo->key);
|
||||
$pkey = sha1(serialize($pkey), TRUE);
|
||||
$key = sha1($encryptedKey . $pkey, TRUE);
|
||||
/* Make sure that the key has the correct length. */
|
||||
if (strlen($key) > $keySize) {
|
||||
$key = substr($key, 0, $keySize);
|
||||
} elseif (strlen($key) < $keySize) {
|
||||
$key = str_pad($key, $keySize);
|
||||
}
|
||||
}
|
||||
$symmetricKey->loadkey($key);
|
||||
} else {
|
||||
$symKeyAlgo = $symmetricKey->getAlgorith();
|
||||
/* Make sure that the input key has the correct format. */
|
||||
if ($inputKeyAlgo !== $symKeyAlgo) {
|
||||
echo sprintf( 'Algorithm mismatch between input key and key in message. ' .
|
||||
'Key was: ' . var_export($inputKeyAlgo, TRUE) . '; message was: ' .
|
||||
var_export($symKeyAlgo, TRUE));
|
||||
exit;
|
||||
}
|
||||
$symmetricKey = $inputKey;
|
||||
}
|
||||
$algorithm = $symmetricKey->getAlgorith();
|
||||
if (in_array($algorithm, $blacklist, TRUE)) {
|
||||
echo sprintf('Algorithm disabled: ' . var_export($algorithm, TRUE));
|
||||
exit;
|
||||
}
|
||||
/** @var string $decrypted */
|
||||
$decrypted = $enc->decryptNode($symmetricKey, FALSE);
|
||||
/*
|
||||
* This is a workaround for the case where only a subset of the XML
|
||||
* tree was serialized for encryption. In that case, we may miss the
|
||||
* namespaces needed to parse the XML.
|
||||
*/
|
||||
$xml = '<root xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" '.
|
||||
'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">' .
|
||||
$decrypted .
|
||||
'</root>';
|
||||
$newDoc = new DOMDocument();
|
||||
if (!@$newDoc->loadXML($xml)) {
|
||||
echo sprintf('Failed to parse decrypted XML. Maybe the wrong sharedkey was used?');
|
||||
throw new Exception('Failed to parse decrypted XML. Maybe the wrong sharedkey was used?');
|
||||
}
|
||||
$decryptedElement = $newDoc->firstChild->firstChild;
|
||||
if ($decryptedElement === NULL) {
|
||||
echo sprintf('Missing encrypted element.');
|
||||
throw new Exception('Missing encrypted element.');
|
||||
}
|
||||
|
||||
if (!($decryptedElement instanceof DOMElement)) {
|
||||
echo sprintf('Decrypted element was not actually a DOMElement.');
|
||||
}
|
||||
|
||||
return $decryptedElement;
|
||||
}
|
||||
/**
|
||||
* Decrypt an encrypted element.
|
||||
*
|
||||
* @param DOMElement $encryptedData The encrypted data.
|
||||
* @param MoXMLSecurityKey $inputKey The decryption key.
|
||||
* @param array $blacklist Blacklisted decryption algorithms.
|
||||
* @return DOMElement The decrypted element.
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function decryptElement(DOMElement $encryptedData, MoXMLSecurityKey $inputKey, array $blacklist = array(), MoXMLSecurityKey $alternateKey = NULL)
|
||||
{
|
||||
try {
|
||||
return self::doDecryptElement($encryptedData, $inputKey, $blacklist);
|
||||
} catch (Exception $e) {
|
||||
//Try with alternate key
|
||||
try {
|
||||
return self::doDecryptElement($encryptedData, $alternateKey, $blacklist);
|
||||
} catch(Exception $t) {
|
||||
|
||||
}
|
||||
/*
|
||||
* Something went wrong during decryption, but for security
|
||||
* reasons we cannot tell the user what failed.
|
||||
*/
|
||||
//print_r($e->getMessage());
|
||||
echo sprintf('Failed to decrypt XML element.');
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the metadata of the SP based on the settings
|
||||
*
|
||||
* @param string $sp The SP data
|
||||
* @param string $authnsign authnRequestsSigned attribute
|
||||
* @param string $wsign wantAssertionsSigned attribute
|
||||
* @param DateTime $validUntil Metadata's valid time
|
||||
* @param Timestamp $cacheDuration Duration of the cache in seconds
|
||||
* @param array $contacts Contacts info
|
||||
* @param array $organization Organization ingo
|
||||
*
|
||||
* @return string SAML Metadata XML
|
||||
*/
|
||||
public static function metadata_builder($siteUrl)
|
||||
{
|
||||
$xml = new DOMDocument();
|
||||
$url = plugins_url().'/miniorange-saml-20-single-sign-on/sp-metadata.xml';
|
||||
|
||||
$xml->load($url);
|
||||
|
||||
$xpath = new DOMXPath($xml);
|
||||
$elements = $xpath->query('//md:EntityDescriptor[@entityID="http://{path-to-your-site}/wp-content/plugins/miniorange-saml-20-single-sign-on/"]');
|
||||
|
||||
if ($elements->length >= 1) {
|
||||
$element = $elements->item(0);
|
||||
$element->setAttribute('entityID', $siteUrl.'/wp-content/plugins/miniorange-saml-20-single-sign-on/');
|
||||
}
|
||||
|
||||
$elements = $xpath->query('//md:AssertionConsumerService[@Location="http://{path-to-your-site}"]');
|
||||
if ($elements->length >= 1) {
|
||||
$element = $elements->item(0);
|
||||
$element->setAttribute('Location', $siteUrl.'/');
|
||||
}
|
||||
|
||||
//re-save
|
||||
$xml->save(plugins_url()."/miniorange-saml-20-single-sign-on/sp-metadata.xml");
|
||||
}
|
||||
|
||||
public static function get_mapped_groups($saml_params, $saml_groups)
|
||||
{
|
||||
$groups = array();
|
||||
|
||||
if (!empty($saml_groups)) {
|
||||
$saml_mapped_groups = array();
|
||||
$i=1;
|
||||
while ($i < 10) {
|
||||
$saml_mapped_groups_value = $saml_params->get('group'.$i.'_map');
|
||||
|
||||
$saml_mapped_groups[$i] = explode(';', $saml_mapped_groups_value);
|
||||
$i++;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($saml_groups as $saml_group) {
|
||||
if (!empty($saml_group)) {
|
||||
$i = 0;
|
||||
$found = false;
|
||||
|
||||
while ($i < 9 && !$found) {
|
||||
if (!empty($saml_mapped_groups[$i]) && in_array($saml_group, $saml_mapped_groups[$i], TRUE)) {
|
||||
$groups[] = $saml_params->get('group'.$i);
|
||||
$found = true;
|
||||
}
|
||||
$i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return array_unique($groups);
|
||||
}
|
||||
|
||||
|
||||
public static function getEncryptionAlgorithm($method){
|
||||
switch($method){
|
||||
case 'http://www.w3.org/2001/04/xmlenc#tripledes-cbc':
|
||||
return MoXMLSecurityKey::TRIPLEDES_CBC;
|
||||
break;
|
||||
|
||||
case 'http://www.w3.org/2001/04/xmlenc#aes128-cbc':
|
||||
return MoXMLSecurityKey::AES128_CBC;
|
||||
|
||||
case 'http://www.w3.org/2001/04/xmlenc#aes192-cbc':
|
||||
return MoXMLSecurityKey::AES192_CBC;
|
||||
break;
|
||||
|
||||
case 'http://www.w3.org/2001/04/xmlenc#aes256-cbc':
|
||||
return MoXMLSecurityKey::AES256_CBC;
|
||||
break;
|
||||
|
||||
case 'http://www.w3.org/2001/04/xmlenc#rsa-1_5':
|
||||
return MoXMLSecurityKey::RSA_1_5;
|
||||
break;
|
||||
|
||||
case 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p':
|
||||
return MoXMLSecurityKey::RSA_OAEP_MGF1P;
|
||||
break;
|
||||
|
||||
case 'http://www.w3.org/2000/09/xmldsig#dsa-sha1':
|
||||
return MoXMLSecurityKey::DSA_SHA1;
|
||||
break;
|
||||
|
||||
case 'http://www.w3.org/2000/09/xmldsig#rsa-sha1':
|
||||
return MoXMLSecurityKey::RSA_SHA1;
|
||||
break;
|
||||
|
||||
case 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256':
|
||||
return MoXMLSecurityKey::RSA_SHA256;
|
||||
break;
|
||||
|
||||
case 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha384':
|
||||
return MoXMLSecurityKey::RSA_SHA384;
|
||||
break;
|
||||
|
||||
case 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha512':
|
||||
return MoXMLSecurityKey::RSA_SHA512;
|
||||
break;
|
||||
|
||||
default:
|
||||
echo sprintf('Invalid Encryption Method: '. htmlspecialchars($method));
|
||||
exit;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public static function sanitize_certificate( $certificate ) {
|
||||
$certificate = preg_replace("/[\r\n]+/", "", $certificate);
|
||||
$certificate = str_replace( "-", "", $certificate );
|
||||
$certificate = str_replace( "BEGIN CERTIFICATE", "", $certificate );
|
||||
$certificate = str_replace( "END CERTIFICATE", "", $certificate );
|
||||
$certificate = str_replace( " ", "", $certificate );
|
||||
$certificate = chunk_split($certificate, 64, "\r\n");
|
||||
$certificate = "-----BEGIN CERTIFICATE-----\r\n" . $certificate . "-----END CERTIFICATE-----";
|
||||
return $certificate;
|
||||
}
|
||||
|
||||
public static function desanitize_certificate( $certificate ) {
|
||||
$certificate = preg_replace("/[\r\n]+/", "", $certificate);
|
||||
//$certificate = str_replace( "-", "", $certificate );
|
||||
$certificate = str_replace( "-----BEGIN CERTIFICATE-----", "", $certificate );
|
||||
$certificate = str_replace( "-----END CERTIFICATE-----", "", $certificate );
|
||||
$certificate = str_replace( " ", "", $certificate );
|
||||
//$certificate = chunk_split($certificate, 64, "\r\n");
|
||||
//$certificate = "-----BEGIN CERTIFICATE-----\r\n" . $certificate . "-----END CERTIFICATE-----";
|
||||
return $certificate;
|
||||
}
|
||||
|
||||
public static function mo_saml_wp_remote_post($url, $args = array()){
|
||||
$response = wp_remote_post($url, $args);
|
||||
if(!is_wp_error($response)){
|
||||
return $response;
|
||||
} else {
|
||||
$show_message = new saml_mo_login();
|
||||
update_option('mo_saml_message', __('Unable to connect to the Internet. Please try again.','miniorange-saml-20-single-sign-on'));
|
||||
$show_message->mo_saml_show_error_message();
|
||||
}
|
||||
}
|
||||
|
||||
public static function mo_saml_wp_remote_get($url, $args = array()){
|
||||
$response = wp_remote_get($url, $args);
|
||||
if(!is_wp_error($response)){
|
||||
return $response;
|
||||
} else {
|
||||
$show_message = new saml_mo_login();
|
||||
update_option('mo_saml_message', __('Unable to connect to the Internet. Please try again.','miniorange-saml-20-single-sign-on'));
|
||||
$show_message->mo_saml_show_error_message();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
?>
|
||||
44
core/lib/saml2/encryption.php
Normal file
44
core/lib/saml2/encryption.php
Normal file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
/**
|
||||
* @package miniOrange
|
||||
* @author miniOrange Security Software Pvt. Ltd.
|
||||
* @license GNU/GPLv3
|
||||
* @copyright Copyright 2015 miniOrange. All Rights Reserved.
|
||||
*
|
||||
*
|
||||
* This file is part of miniOrange SAML plugin.
|
||||
*/
|
||||
|
||||
class AESEncryption {
|
||||
/**
|
||||
* @param string $data - the key=value pairs separated with &
|
||||
* @return string
|
||||
*/
|
||||
public static function encrypt_data($data, $key) {
|
||||
$key = openssl_digest($key, 'sha256');
|
||||
$method = 'AES-128-ECB';
|
||||
$ivSize = openssl_cipher_iv_length($method);
|
||||
$iv = openssl_random_pseudo_bytes($ivSize);
|
||||
$strCrypt = openssl_encrypt ($data, $method, $key,OPENSSL_RAW_DATA||OPENSSL_ZERO_PADDING, $iv);
|
||||
return base64_encode($iv.$strCrypt);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $data - crypt response from Sagepay
|
||||
* @return string
|
||||
*/
|
||||
public static function decrypt_data($data, $key) {
|
||||
$strIn = base64_decode($data);
|
||||
$key = openssl_digest($key, 'sha256');
|
||||
$method = 'AES-128-ECB';
|
||||
$ivSize = openssl_cipher_iv_length($method);
|
||||
$iv = substr($strIn,0,$ivSize);
|
||||
$data = substr($strIn,$ivSize);
|
||||
$clear = openssl_decrypt ($data, $method, $key, OPENSSL_RAW_DATA||OPENSSL_ZERO_PADDING, $iv);
|
||||
|
||||
return $clear;
|
||||
}
|
||||
|
||||
}
|
||||
?>
|
||||
640
core/lib/saml2/mo-saml-options-enum.php
Normal file
640
core/lib/saml2/mo-saml-options-enum.php
Normal file
@@ -0,0 +1,640 @@
|
||||
<?php
|
||||
include "MoSAMLBasicEnum.php";
|
||||
class mo_saml_options_enum_sso_loginMoSAML extends MoSAMLBasicEnum {
|
||||
const Relay_state = 'mo_saml_relay_state';
|
||||
const Redirect_Idp = 'mo_saml_registered_only_access';
|
||||
const Force_authentication = 'mo_saml_force_authentication';
|
||||
const Enable_access_RSS = 'mo_saml_enable_rss_access';
|
||||
const Auto_redirect = 'mo_saml_enable_login_redirect';
|
||||
}
|
||||
|
||||
class mo_saml_options_enum_identity_providerMoSAML extends MoSAMLBasicEnum{
|
||||
const Broker_service ='mo_saml_enable_cloud_broker';
|
||||
const SP_Base_Url='mo_saml_sp_base_url';
|
||||
const SP_Entity_ID = 'mo_saml_sp_entity_id';
|
||||
|
||||
}
|
||||
|
||||
class mo_saml_options_tab_names extends MoSAMLBasicEnum{
|
||||
|
||||
const Service_provider_settings = 'service-provider-setup';
|
||||
const Identity_provider_settting = 'identity-provider-setup';
|
||||
const Redirection_sso_links = 'redirection-sso-links';
|
||||
const Entire_plugin_tour = 'entire-plugin-tour';
|
||||
const Attribute_role_mapping = 'attribute-role-mapping';
|
||||
|
||||
}
|
||||
|
||||
class mo_saml_options_enum_pointersMoSAML extends MoSAMLBasicEnum{
|
||||
public static
|
||||
$DEFAULT = array(
|
||||
'custom_admin_pointers4_8_52_default-miniorange-sp-metadata-url',
|
||||
'custom_admin_pointers4_8_52_default-miniorange-select-your-idp',
|
||||
'custom_admin_pointers4_8_52_default-miniorange-upload-metadata',
|
||||
'custom_admin_pointers4_8_52_default-miniorange-test-configuration',
|
||||
'custom_admin_pointers4_8_52_default-miniorange-attribute-mapping',
|
||||
'custom_admin_pointers4_8_52_default-miniorange-role-mapping',
|
||||
'custom_admin_pointers4_8_52_default-minorange-use-widget',
|
||||
'custom_admin_pointers4_8_52_default-miniorange-addons',
|
||||
'custom_admin_pointers4_8_52_default-miniorange-support-pointer'
|
||||
);
|
||||
public static $DEFAULT_SKIP = array(
|
||||
'custom_admin_pointers4_8_52_default-miniorange-sp-metadata-url',
|
||||
'custom_admin_pointers4_8_52_default-miniorange-select-your-idp',
|
||||
'custom_admin_pointers4_8_52_default-miniorange-upload-metadata',
|
||||
'custom_admin_pointers4_8_52_default-miniorange-test-configuration',
|
||||
'custom_admin_pointers4_8_52_default-miniorange-attribute-mapping',
|
||||
'custom_admin_pointers4_8_52_default-miniorange-role-mapping',
|
||||
'custom_admin_pointers4_8_52_default-minorange-use-widget',
|
||||
'custom_admin_pointers4_8_52_default-miniorange-addons',
|
||||
);
|
||||
public static $SERVICE_PROVIDER = array(
|
||||
'custom_admin_pointers4_8_52_miniorange-select-your-idp',
|
||||
'custom_admin_pointers4_8_52_miniorange-upload-metadata',
|
||||
'custom_admin_pointers4_8_52_miniorange-test-configuration',
|
||||
'custom_admin_pointers4_8_52_miniorange-import-config',
|
||||
'custom_admin_pointers4_8_52_export-import-config',
|
||||
'custom_admin_pointers4_8_52_configure-service-restart-tour');
|
||||
public static $IDENTITY_PROVIDER = array(
|
||||
'custom_admin_pointers4_8_52_metadata_manual',
|
||||
'custom_admin_pointers4_8_52_miniorange-sp-metadata-url',
|
||||
'custom_admin_pointers4_8_52_identity-provider-restart-tour'
|
||||
);
|
||||
public static $ATTRIBUTE_MAPPING = array(
|
||||
'custom_admin_pointers4_8_52_miniorange-attribute-mapping',
|
||||
'custom_admin_pointers4_8_52_miniorange-role-mapping',
|
||||
'custom_admin_pointers4_8_52_attribute-mapping-restart-tour');
|
||||
public static $REDIRECTION_LINK = array(
|
||||
'custom_admin_pointers4_8_52_minorange-use-widget',
|
||||
'custom_admin_pointers4_8_52_miniorange-auto-redirect',
|
||||
'custom_admin_pointers4_8_52_miniorange-auto-redirect-login-page',
|
||||
'custom_admin_pointers4_8_52_miniorange-short-code',
|
||||
'custom_admin_pointers4_8_52_miniorange-redirection-sso-restart-tour'
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
|
||||
class mo_saml_options_enum_service_providerMoSAML extends MoSAMLBasicEnum{
|
||||
const Identity_name ='saml_identity_name';
|
||||
const Login_binding_type='saml_login_binding_type';
|
||||
const Login_URL = 'saml_login_url';
|
||||
const Logout_binding_type = 'saml_logout_binding_type';
|
||||
const Logout_URL = 'saml_logout_url';
|
||||
const Issuer = 'saml_issuer';
|
||||
const X509_certificate = 'saml_x509_certificate';
|
||||
const Request_signed = 'saml_request_signed';
|
||||
const Guide_name = 'saml_identity_provider_guide_name';
|
||||
const Is_encoding_enabled = 'mo_saml_encoding_enabled';
|
||||
|
||||
|
||||
}
|
||||
|
||||
class mo_saml_options_test_configuration extends MoSAMLBasicEnum{
|
||||
const SAML_REQUEST = 'MO_SAML_REQUEST';
|
||||
const SAML_RESPONSE = 'MO_SAML_RESPONSE';
|
||||
const TEST_CONFIG_ERROR_LOG = 'MO_SAML_TEST';
|
||||
}
|
||||
|
||||
class mo_saml_options_enum_attribute_mappingMoSAML extends MoSAMLBasicEnum{
|
||||
const Attribute_Username ='saml_am_username';
|
||||
const Attribute_Email = 'saml_am_email';
|
||||
const Attribute_First_name ='saml_am_first_name';
|
||||
const Attribute_Last_name = 'saml_am_last_name';
|
||||
const Attribute_Group_name ='saml_am_group_name';
|
||||
const Attribute_Custom_mapping = 'mo_saml_custom_attrs_mapping';
|
||||
const Attribute_Account_matcher = 'saml_am_account_matcher';
|
||||
|
||||
}
|
||||
|
||||
class mo_saml_options_enum_role_mappingMoSAML extends MoSAMLBasicEnum{
|
||||
const Role_do_not_auto_create_users = 'mo_saml_dont_create_user_if_role_not_mapped';
|
||||
const Role_do_not_assign_role_unlisted = 'saml_am_dont_allow_unlisted_user_role';
|
||||
const Role_do_not_update_existing_user = 'saml_am_dont_update_existing_user_role';
|
||||
const Role_default_role ='saml_am_default_user_role';
|
||||
}
|
||||
|
||||
|
||||
class mo_saml_options_error_constants extends MoSAMLBasicEnum{
|
||||
const Error_no_certificate = "Unable to find a certificate .";
|
||||
const Cause_no_certificate = "No signature found in SAML Response or Assertion. Please sign at least one of them.";
|
||||
const Error_wrong_certificate = "Unable to find a certificate matching the configured fingerprint.";
|
||||
const Cause_wrong_certificate = "X.509 Certificate field in plugin does not match the certificate found in SAML Response.";
|
||||
const Error_invalid_audience = "Invalid Audience URI.";
|
||||
const Cause_invalid_audience = "The value of 'Audience URI' field on Identity Provider's side is incorrect";
|
||||
const Error_issuer_not_verfied = "Issuer cannot be verified.";
|
||||
const Cause_issuer_not_verfied = "IdP Entity ID configured and the one found in SAML Response do not match";
|
||||
}
|
||||
|
||||
|
||||
class mo_saml_options_plugin_constants extends MoSAMLBasicEnum{
|
||||
const CMS_Name = "WP";
|
||||
const Application_Name = "WP miniOrange SAML 2.0 SSO Plugin";
|
||||
const Application_type = "SAML";
|
||||
const Version = "4.9.05";
|
||||
const HOSTNAME = "https://login.xecurify.com";
|
||||
}
|
||||
|
||||
class mo_saml_options_plugin_idp extends MoSAMLBasicEnum{
|
||||
public static $IDP_GUIDES = array(
|
||||
"Azure AD" => "azure-ad",
|
||||
"Azure B2C" => "azure-b2c",
|
||||
"ADFS" => "adfs",
|
||||
"Okta" => "okta",
|
||||
"SalesForce" => "salesforce",
|
||||
"Google Apps" => "google-apps",
|
||||
"OneLogin" => "onelogin",
|
||||
"MiniOrange" => "miniorange",
|
||||
"Keycloak" => "jboss-keycloak",
|
||||
"AbsorbLMS" => "absorb-lms",
|
||||
"Degreed" => "degreed",
|
||||
"JumpCloud" => "jumpcloud",
|
||||
"PingFederate" => "pingfederate",
|
||||
"PingOne" => "pingone",
|
||||
"Centrify" => "centrify",
|
||||
"Oracle" => "oracle-enterprise-manager",
|
||||
"Bitium" => "bitium",
|
||||
"Shibboleth 2" => "shibboleth2",
|
||||
"Shibboleth 3" => "shibboleth3",
|
||||
"Gluu Server" => "gluu-server",
|
||||
"SimpleSAMLphp" => "simplesaml",
|
||||
"OpenAM" => "openam",
|
||||
"Authanvil"=>"authanvil",
|
||||
"Auth0"=>"auth0",
|
||||
"CA Identity"=>"ca-identity",
|
||||
"WSO2"=>"wso2",
|
||||
"RSA SecureID"=>"rsa-secureid",
|
||||
"Custom IDP"=>"custom-idp"
|
||||
);
|
||||
}
|
||||
|
||||
class mo_saml_options_plugin_idp_videos extends MoSAMLBasicEnum{
|
||||
public static $IDP_VIDEOS = array(
|
||||
"azure-ad"=> "eHen4aiflFU",
|
||||
"azure-b2c"=> "",
|
||||
"adfs"=> "rLBHbRbrY5E",
|
||||
"okta"=> "YHE8iYojUqM",
|
||||
"salesforce"=> "LRQrmgr255Q",
|
||||
"google-apps"=> "5BwzEjgZiu4",
|
||||
"onelogin"=> "_Hsot_RG9YY",
|
||||
"miniorange"=> "eamf9s6JpbA",
|
||||
"jboss-keycloak"=> "Io6x1fTNWHI",
|
||||
"absorb-lms"=> "",
|
||||
"degreed"=> "",
|
||||
"jumpcloud"=> "",
|
||||
"pingfederate"=> "",
|
||||
"pingone"=> "",
|
||||
"centrify"=> "",
|
||||
"oracle-enterprise-manager"=> "",
|
||||
"bitium"=> "",
|
||||
"shibboleth2"=> "",
|
||||
"shibboleth3"=> "",
|
||||
"gluu-server"=> "",
|
||||
"simplesaml"=> "",
|
||||
"openam"=> "",
|
||||
"authanvil"=> "",
|
||||
"auth0"=> "54pz6m5h9mk",
|
||||
"ca-identity" => "",
|
||||
"wso2" => "",
|
||||
"rsa-secureid" => "",
|
||||
"custom-idp" => "gilfhNFYsgc"
|
||||
);
|
||||
}
|
||||
|
||||
class mo_saml_options_addons extends MoSAMLBasicEnum{
|
||||
|
||||
public static $ADDON_URL = array(
|
||||
|
||||
'scim' => 'https://plugins.miniorange.com/wordpress-user-provisioning',
|
||||
'page_restriction' => 'https://plugins.miniorange.com/wordpress-page-restriction',
|
||||
'file_prevention' => 'https://plugins.miniorange.com/wordpress-media-restriction',
|
||||
'ssologin' => 'https://plugins.miniorange.com/wordpress-sso-login-audit',
|
||||
'buddypress' => 'https://plugins.miniorange.com/wordpress-buddypress-integrator',
|
||||
'learndash' => 'https://plugins.miniorange.com/wordpress-learndash-integrator',
|
||||
'attribute_based_redirection' => 'https://plugins.miniorange.com/wordpress-attribute-based-redirection-restriction',
|
||||
'ssosession' => 'https://plugins.miniorange.com/sso-session-management',
|
||||
'fsso' => 'https://plugins.miniorange.com/incommon-federation-single-sign-on-sso',
|
||||
'paid_mem_pro' => 'https://plugins.miniorange.com/paid-membership-pro-integrator',
|
||||
'memberpress' => 'https://plugins.miniorange.com/wordpress-memberpress-integrator',
|
||||
'wp_members' => 'https://plugins.miniorange.com/wordpress-members-integrator',
|
||||
'woocommerce' => 'https://plugins.miniorange.com/wordpress-woocommerce-integrator',
|
||||
'guest_login' => 'https://plugins.miniorange.com/guest-user-login',
|
||||
'profile_picture_add_on' => 'https://plugins.miniorange.com/wordpress-profile-picture-map'
|
||||
|
||||
);
|
||||
|
||||
public static $WP_ADDON_URL = array(
|
||||
'page-restriction' => 'https://wordpress.org/plugins/page-and-post-restriction/embed/',
|
||||
'scim-user-sync'=> 'https://wordpress.org/plugins/scim-user-provisioning/embed/'
|
||||
);
|
||||
|
||||
public static $ADDON_TITLE = array(
|
||||
|
||||
'scim' => 'SCIM User Provisioning',
|
||||
'page_restriction' => 'Page and Post Restriction',
|
||||
'file_prevention' => 'Prevent File Access',
|
||||
'ssologin' => 'SSO Login Audit',
|
||||
'buddypress' => 'BuddyPress Integrator',
|
||||
'learndash' => 'Learndash Integrator',
|
||||
'attribute_based_redirection' => 'Attribute Based Redirection',
|
||||
'ssosession' => 'SSO Session Management',
|
||||
'fsso' => 'Federation Single Sign-On',
|
||||
'memberpress' => 'MemberPress Integrator',
|
||||
'wp_members' => 'WP-Members Integrator',
|
||||
'woocommerce' => 'WooCommerce Integrator',
|
||||
'guest_login' => 'Guest Login',
|
||||
'profile_picture_add_on' => 'Profile Picture Add-on',
|
||||
'paid_mem_pro' => 'PaidMembership Pro Integrator'
|
||||
);
|
||||
|
||||
public static $RECOMMENDED_ADDONS_PATH = array(
|
||||
|
||||
"learndash" => "sfwd-lms/sfwd_lms.php",
|
||||
"buddypress" => "buddypress/bp-loader.php",
|
||||
"paid_mem_pro" => "paid-memberships-pro/paid-memberships-pro.php",
|
||||
"memberpress" => "memberpress/memberpress.php",
|
||||
"wp_members" => "wp-members/wp-members.php",
|
||||
"woocommerce" => "woocommerce/woocommerce.php"
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
class mo_saml_license_plans extends MoSAMLBasicEnum {
|
||||
|
||||
public static $license_plans = array (
|
||||
'standard' => 'WP SAML SSO Standard Plan',
|
||||
'premium' => 'WP SAML SSO Premium Plan',
|
||||
'enterprise' => 'WP SAML SSO Enterprise Plan',
|
||||
'enterprise-multiple-idp' => 'WP SAML SSO Enterprise Multiple-IDP Plan',
|
||||
'all-inclusive' => 'WP SAML SSO All Inclusive Plan',
|
||||
'premium-multisite' => 'WP SAML SSO Premium Multisite Plan',
|
||||
'enterprise-multisite' => 'WP SAML SSO Enterprise Multisite Plan',
|
||||
'all-inclusive-multisite' => 'WP SAML SSO All Inclusive Multisite Plan',
|
||||
'help' => 'Not Sure'
|
||||
);
|
||||
|
||||
public static $license_plans_slug = array (
|
||||
'standard' => '16.0.2@16.0.2',
|
||||
'premium' => '12.0.2@12.0.2',
|
||||
'enterprise' => '12.0.2@12.0.2',
|
||||
'enterprise-multiple-idp' => '25.0.1@25.0.1',
|
||||
'all-inclusive' => '25.0.1@25.0.1',
|
||||
);
|
||||
}
|
||||
|
||||
class mo_saml_time_zones extends MoSAMLBasicEnum {
|
||||
|
||||
public static $time_zones = array(
|
||||
"(GMT-11:00) Niue Time" => "Pacific/Niue",
|
||||
"(GMT-11:00) Samoa Standard Time" => "Pacific/Pago_Pago",
|
||||
"(GMT-10:00) Cook Islands Standard Time" => "Pacific/Rarotonga",
|
||||
"(GMT-10:00) Hawaii-Aleutian Standard Time" => "Pacific/Honolulu",
|
||||
"(GMT-10:00) Tahiti Time" => "Pacific/Tahiti",
|
||||
"(GMT-09:30) Marquesas Time" => "Pacific/Marquesas",
|
||||
"(GMT-09:00) Gambier Time" => "Pacific/Gambier",
|
||||
"(GMT-09:00) Hawaii-Aleutian Time (Adak)" => "America/Adak",
|
||||
"(GMT-08:00) Alaska Time - Anchorage" => "America/Anchorage",
|
||||
"(GMT-08:00) Alaska Time - Juneau" => "America/Juneau",
|
||||
"(GMT-08:00) Alaska Time - Metlakatla" => "America/Metlakatla",
|
||||
"(GMT-08:00) Alaska Time - Nome" => "America/Nome",
|
||||
"(GMT-08:00) Alaska Time - Sitka" => "America/Sitka",
|
||||
"(GMT-08:00) Alaska Time - Yakutat" => "America/Yakutat",
|
||||
"(GMT-08:00) Pitcairn Time" => "Pacific/Pitcairn",
|
||||
"(GMT-07:00) Mexican Pacific Standard Time" => "America/Hermosillo",
|
||||
"(GMT-07:00) Mountain Standard Time - Creston" => "America/Creston",
|
||||
"(GMT-07:00) Mountain Standard Time - Dawson" => "America/Dawson",
|
||||
"(GMT-07:00) Mountain Standard Time - Dawson Creek" => "America/Dawson_Creek",
|
||||
"(GMT-07:00) Mountain Standard Time - Fort Nelson" => "America/Fort_Nelson",
|
||||
"(GMT-07:00) Mountain Standard Time - Phoenix" => "America/Phoenix",
|
||||
"(GMT-07:00) Mountain Standard Time - Whitehorse" => "America/Whitehorse",
|
||||
"(GMT-07:00) Pacific Time - Los Angeles" => "America/Los_Angeles",
|
||||
"(GMT-07:00) Pacific Time - Tijuana" => "America/Tijuana",
|
||||
"(GMT-07:00) Pacific Time - Vancouver" => "America/Vancouver",
|
||||
"(GMT-06:00) Central Standard Time - Belize" => "America/Belize",
|
||||
"(GMT-06:00) Central Standard Time - Costa Rica" => "America/Costa_Rica",
|
||||
"(GMT-06:00) Central Standard Time - El Salvador" => "America/El_Salvador",
|
||||
"(GMT-06:00) Central Standard Time - Guatemala" => "America/Guatemala",
|
||||
"(GMT-06:00) Central Standard Time - Managua" => "America/Managua",
|
||||
"(GMT-06:00) Central Standard Time - Regina" => "America/Regina",
|
||||
"(GMT-06:00) Central Standard Time - Swift Current" => "America/Swift_Current",
|
||||
"(GMT-06:00) Central Standard Time - Tegucigalpa" => "America/Tegucigalpa",
|
||||
"(GMT-06:00) Easter Island Time" => "Pacific/Easter",
|
||||
"(GMT-06:00) Galapagos Time" => "Pacific/Galapagos",
|
||||
"(GMT-06:00) Mexican Pacific Time - Chihuahua" => "America/Chihuahua",
|
||||
"(GMT-06:00) Mexican Pacific Time - Mazatlan" => "America/Mazatlan",
|
||||
"(GMT-06:00) Mountain Time - Boise" => "America/Boise",
|
||||
"(GMT-06:00) Mountain Time - Cambridge Bay" => "America/Cambridge_Bay",
|
||||
"(GMT-06:00) Mountain Time - Denver" => "America/Denver",
|
||||
"(GMT-06:00) Mountain Time - Edmonton" => "America/Edmonton",
|
||||
"(GMT-06:00) Mountain Time - Inuvik" => "America/Inuvik",
|
||||
"(GMT-06:00) Mountain Time - Ojinaga" => "America/Ojinaga",
|
||||
"(GMT-06:00) Mountain Time - Yellowknife" => "America/Yellowknife",
|
||||
"(GMT-05:00) Acre Standard Time - Eirunepe" => "America/Eirunepe",
|
||||
"(GMT-05:00) Acre Standard Time - Rio Branco" => "America/Rio_Branco",
|
||||
"(GMT-05:00) Central Time - Bahia Banderas" => "America/Bahia_Banderas",
|
||||
"(GMT-05:00) Central Time - Beulah, North Dakota" => "America/North_Dakota/Beulah",
|
||||
"(GMT-05:00) Central Time - Center, North Dakota" => "America/North_Dakota/Center",
|
||||
"(GMT-05:00) Central Time - Chicago" => "America/Chicago",
|
||||
"(GMT-05:00) Central Time - Knox, Indiana" => "America/Indiana/Knox",
|
||||
"(GMT-05:00) Central Time - Matamoros" => "America/Matamoros",
|
||||
"(GMT-05:00) Central Time - Menominee" => "America/Menominee",
|
||||
"(GMT-05:00) Central Time - Merida" => "America/Merida",
|
||||
"(GMT-05:00) Central Time - Mexico City" => "America/Mexico_City",
|
||||
"(GMT-05:00) Central Time - Monterrey" => "America/Monterrey",
|
||||
"(GMT-05:00) Central Time - New Salem, North Dakota" => "America/North_Dakota/New_Salem",
|
||||
"(GMT-05:00) Central Time - Rainy River" => "America/Rainy_River",
|
||||
"(GMT-05:00) Central Time - Rankin Inlet" => "America/Rankin_Inlet",
|
||||
"(GMT-05:00) Central Time - Resolute" => "America/Resolute",
|
||||
"(GMT-05:00) Central Time - Tell City, Indiana" => "America/Indiana/Tell_City",
|
||||
"(GMT-05:00) Central Time - Winnipeg" => "America/Winnipeg",
|
||||
"(GMT-05:00) Colombia Standard Time" => "America/Bogota",
|
||||
"(GMT-05:00) Eastern Standard Time - Atikokan" => "America/Atikokan",
|
||||
"(GMT-05:00) Eastern Standard Time - Cancun" => "America/Cancun",
|
||||
"(GMT-05:00) Eastern Standard Time - Jamaica" => "America/Jamaica",
|
||||
"(GMT-05:00) Eastern Standard Time - Panama" => "America/Panama",
|
||||
"(GMT-05:00) Ecuador Time" => "America/Guayaquil",
|
||||
"(GMT-05:00) Peru Standard Time" => "America/Lima",
|
||||
"(GMT-04:00) Amazon Standard Time - Boa Vista" => "America/Boa_Vista",
|
||||
"(GMT-04:00) Amazon Standard Time - Campo Grande" => "America/Campo_Grande",
|
||||
"(GMT-04:00) Amazon Standard Time - Cuiaba" => "America/Cuiaba",
|
||||
"(GMT-04:00) Amazon Standard Time - Manaus" => "America/Manaus",
|
||||
"(GMT-04:00) Amazon Standard Time - Porto Velho" => "America/Porto_Velho",
|
||||
"(GMT-04:00) Atlantic Standard Time - Barbados" => "America/Barbados",
|
||||
"(GMT-04:00) Atlantic Standard Time - Blanc-Sablon" => "America/Blanc-Sablon",
|
||||
"(GMT-04:00) Atlantic Standard Time - Curaçao" => "America/Curacao",
|
||||
"(GMT-04:00) Atlantic Standard Time - Martinique" => "America/Martinique",
|
||||
"(GMT-04:00) Atlantic Standard Time - Port of Spain" => "America/Port_of_Spain",
|
||||
"(GMT-04:00) Atlantic Standard Time - Puerto Rico" => "America/Puerto_Rico",
|
||||
"(GMT-04:00) Atlantic Standard Time - Santo Domingo" => "America/Santo_Domingo",
|
||||
"(GMT-04:00) Bolivia Time" => "America/La_Paz",
|
||||
"(GMT-04:00) Chile Time" => "America/Santiago",
|
||||
"(GMT-04:00) Cuba Time" => "America/Havana",
|
||||
"(GMT-04:00) Eastern Time - Detroit" => "America/Detroit",
|
||||
"(GMT-04:00) Eastern Time - Grand Turk" => "America/Grand_Turk",
|
||||
"(GMT-04:00) Eastern Time - Indianapolis" => "America/Indiana/Indianapolis",
|
||||
"(GMT-04:00) Eastern Time - Iqaluit" => "America/Iqaluit",
|
||||
"(GMT-04:00) Eastern Time - Louisville" => "America/Kentucky/Louisville",
|
||||
"(GMT-04:00) Eastern Time - Marengo, Indiana" => "America/Indiana/Marengo",
|
||||
"(GMT-04:00) Eastern Time - Monticello, Kentucky" => "America/Kentucky/Monticello",
|
||||
"(GMT-04:00) Eastern Time - Nassau" => "America/Nassau",
|
||||
"(GMT-04:00) Eastern Time - New York" => "America/New_York",
|
||||
"(GMT-04:00) Eastern Time - Nipigon" => "America/Nipigon",
|
||||
"(GMT-04:00) Eastern Time - Pangnirtung" => "America/Pangnirtung",
|
||||
"(GMT-04:00) Eastern Time - Petersburg, Indiana" => "America/Indiana/Petersburg",
|
||||
"(GMT-04:00) Eastern Time - Port-au-Prince" => "America/Port-au-Prince",
|
||||
"(GMT-04:00) Eastern Time - Thunder Bay" => "America/Thunder_Bay",
|
||||
"(GMT-04:00) Eastern Time - Toronto" => "America/Toronto",
|
||||
"(GMT-04:00) Eastern Time - Vevay, Indiana" => "America/Indiana/Vevay",
|
||||
"(GMT-04:00) Eastern Time - Vincennes, Indiana" => "America/Indiana/Vincennes",
|
||||
"(GMT-04:00) Eastern Time - Winamac, Indiana" => "America/Indiana/Winamac",
|
||||
"(GMT-04:00) Guyana Time" => "America/Guyana",
|
||||
"(GMT-04:00) Paraguay Time" => "America/Asuncion",
|
||||
"(GMT-04:00) Venezuela Time" => "America/Caracas",
|
||||
"(GMT-03:00) Argentina Standard Time - Buenos Aires" => "America/Argentina/Buenos_Aires",
|
||||
"(GMT-03:00) Argentina Standard Time - Catamarca" => "America/Argentina/Catamarca",
|
||||
"(GMT-03:00) Argentina Standard Time - Cordoba" => "America/Argentina/Cordoba",
|
||||
"(GMT-03:00) Argentina Standard Time - Jujuy" => "America/Argentina/Jujuy",
|
||||
"(GMT-03:00) Argentina Standard Time - La Rioja" => "America/Argentina/La_Rioja",
|
||||
"(GMT-03:00) Argentina Standard Time - Mendoza" => "America/Argentina/Mendoza",
|
||||
"(GMT-03:00) Argentina Standard Time - Rio Gallegos" => "America/Argentina/Rio_Gallegos",
|
||||
"(GMT-03:00) Argentina Standard Time - Salta" => "America/Argentina/Salta",
|
||||
"(GMT-03:00) Argentina Standard Time - San Juan" => "America/Argentina/San_Juan",
|
||||
"(GMT-03:00) Argentina Standard Time - San Luis" => "America/Argentina/San_Luis",
|
||||
"(GMT-03:00) Argentina Standard Time - Tucuman" => "America/Argentina/Tucuman",
|
||||
"(GMT-03:00) Argentina Standard Time - Ushuaia" => "America/Argentina/Ushuaia",
|
||||
"(GMT-03:00) Atlantic Time - Bermuda" => "Atlantic/Bermuda",
|
||||
"(GMT-03:00) Atlantic Time - Glace Bay" => "America/Glace_Bay",
|
||||
"(GMT-03:00) Atlantic Time - Goose Bay" => "America/Goose_Bay",
|
||||
"(GMT-03:00) Atlantic Time - Halifax" => "America/Halifax",
|
||||
"(GMT-03:00) Atlantic Time - Moncton" => "America/Moncton",
|
||||
"(GMT-03:00) Atlantic Time - Thule" => "America/Thule",
|
||||
"(GMT-03:00) Brasilia Standard Time - Araguaina" => "America/Araguaina",
|
||||
"(GMT-03:00) Brasilia Standard Time - Bahia" => "America/Bahia",
|
||||
"(GMT-03:00) Brasilia Standard Time - Belem" => "America/Belem",
|
||||
"(GMT-03:00) Brasilia Standard Time - Fortaleza" => "America/Fortaleza",
|
||||
"(GMT-03:00) Brasilia Standard Time - Maceio" => "America/Maceio",
|
||||
"(GMT-03:00) Brasilia Standard Time - Recife" => "America/Recife",
|
||||
"(GMT-03:00) Brasilia Standard Time - Santarem" => "America/Santarem",
|
||||
"(GMT-03:00) Brasilia Standard Time - Sao Paulo" => "America/Sao_Paulo",
|
||||
"(GMT-03:00) Chile Time" => "America/Santiago",
|
||||
"(GMT-03:00) Falkland Islands Standard Time" => "Atlantic/Stanley",
|
||||
"(GMT-03:00) French Guiana Time" => "America/Cayenne",
|
||||
"(GMT-03:00) Palmer Time" => "Antarctica/Palmer",
|
||||
"(GMT-03:00) Punta Arenas Time" => "America/Punta_Arenas",
|
||||
"(GMT-03:00) Rothera Time" => "Antarctica/Rothera",
|
||||
"(GMT-03:00) Suriname Time" => "America/Paramaribo",
|
||||
"(GMT-03:00) Uruguay Standard Time" => "America/Montevideo",
|
||||
"(GMT-02:30) Newfoundland Time" => "America/St_Johns",
|
||||
"(GMT-02:00) Fernando de Noronha Standard Time" => "America/Noronha",
|
||||
"(GMT-02:00) South Georgia Time" => "Atlantic/South_Georgia",
|
||||
"(GMT-02:00) St. Pierre & Miquelon Time" => "America/Miquelon",
|
||||
"(GMT-02:00) West Greenland Time" => "America/Nuuk",
|
||||
"(GMT-01:00) Cape Verde Standard Time" => "Atlantic/Cape_Verde",
|
||||
"(GMT+00:00) Azores Time" => "Atlantic/Azores",
|
||||
"(GMT+00:00) Coordinated Universal Time" => "UTC",
|
||||
"(GMT+00:00) East Greenland Time" => "America/Scoresbysund",
|
||||
"(GMT+00:00) Greenwich Mean Time" => "Etc/GMT",
|
||||
"(GMT+00:00) Greenwich Mean Time - Abidjan" => "Africa/Abidjan",
|
||||
"(GMT+00:00) Greenwich Mean Time - Accra" => "Africa/Accra",
|
||||
"(GMT+00:00) Greenwich Mean Time - Bissau" => "Africa/Bissau",
|
||||
"(GMT+00:00) Greenwich Mean Time - Danmarkshavn" => "America/Danmarkshavn",
|
||||
"(GMT+00:00) Greenwich Mean Time - Monrovia" => "Africa/Monrovia",
|
||||
"(GMT+00:00) Greenwich Mean Time - Reykjavik" => "Atlantic/Reykjavik",
|
||||
"(GMT+00:00) Greenwich Mean Time - São Tomé" => "Africa/Sao_Tome",
|
||||
"(GMT+01:00) Central European Standard Time - Algiers" => "Africa/Algiers",
|
||||
"(GMT+01:00) Central European Standard Time - Tunis" => "Africa/Tunis",
|
||||
"(GMT+01:00) Ireland Time" => "Europe/Dublin",
|
||||
"(GMT+01:00) Morocco Time" => "Africa/Casablanca",
|
||||
"(GMT+01:00) United Kingdom Time" => "Europe/London",
|
||||
"(GMT+01:00) West Africa Standard Time - Lagos" => "Africa/Lagos",
|
||||
"(GMT+01:00) West Africa Standard Time - Ndjamena" => "Africa/Ndjamena",
|
||||
"(GMT+01:00) Western European Time - Canary" => "Atlantic/Canary",
|
||||
"(GMT+01:00) Western European Time - Faroe" => "Atlantic/Faroe",
|
||||
"(GMT+01:00) Western European Time - Lisbon" => "Europe/Lisbon",
|
||||
"(GMT+01:00) Western European Time - Madeira" => "Atlantic/Madeira",
|
||||
"(GMT+01:00) Western Sahara Time" => "Africa/El_Aaiun",
|
||||
"(GMT+02:00) Central Africa Time - Khartoum" => "Africa/Khartoum",
|
||||
"(GMT+02:00) Central Africa Time - Maputo" => "Africa/Maputo",
|
||||
"(GMT+02:00) Central Africa Time - Windhoek" => "Africa/Windhoek",
|
||||
"(GMT+02:00) Central European Time - Amsterdam" => "Europe/Amsterdam",
|
||||
"(GMT+02:00) Central European Time - Andorra" => "Europe/Andorra",
|
||||
"(GMT+02:00) Central European Time - Belgrade" => "Europe/Belgrade",
|
||||
"(GMT+02:00) Central European Time - Berlin" => "Europe/Berlin",
|
||||
"(GMT+02:00) Central European Time - Brussels" => "Europe/Brussels",
|
||||
"(GMT+02:00) Central European Time - Budapest" => "Europe/Budapest",
|
||||
"(GMT+02:00) Central European Time - Ceuta" => "Africa/Ceuta",
|
||||
"(GMT+02:00) Central European Time - Copenhagen" => "Europe/Copenhagen",
|
||||
"(GMT+02:00) Central European Time - Gibraltar" => "Europe/Gibraltar",
|
||||
"(GMT+02:00) Central European Time - Luxembourg" => "Europe/Luxembourg",
|
||||
"(GMT+02:00) Central European Time - Madrid" => "Europe/Madrid",
|
||||
"(GMT+02:00) Central European Time - Malta" => "Europe/Malta",
|
||||
"(GMT+02:00) Central European Time - Monaco" => "Europe/Monaco",
|
||||
"(GMT+02:00) Central European Time - Oslo" => "Europe/Oslo",
|
||||
"(GMT+02:00) Central European Time - Paris" => "Europe/Paris",
|
||||
"(GMT+02:00) Central European Time - Prague" => "Europe/Prague",
|
||||
"(GMT+02:00) Central European Time - Rome" => "Europe/Rome",
|
||||
"(GMT+02:00) Central European Time - Stockholm" => "Europe/Stockholm",
|
||||
"(GMT+02:00) Central European Time - Tirane" => "Europe/Tirane",
|
||||
"(GMT+02:00) Central European Time - Vienna" => "Europe/Vienna",
|
||||
"(GMT+02:00) Central European Time - Warsaw" => "Europe/Warsaw",
|
||||
"(GMT+02:00) Central European Time - Zurich" => "Europe/Zurich",
|
||||
"(GMT+02:00) Eastern European Standard Time - Cairo" => "Africa/Cairo",
|
||||
"(GMT+02:00) Eastern European Standard Time - Kaliningrad" => "Europe/Kaliningrad",
|
||||
"(GMT+02:00) Eastern European Standard Time - Tripoli" => "Africa/Tripoli",
|
||||
"(GMT+02:00) South Africa Standard Time" => "Africa/Johannesburg",
|
||||
"(GMT+02:00) Troll Time" => "Antarctica/Troll",
|
||||
"(GMT+03:00) Arabian Standard Time - Baghdad" => "Asia/Baghdad",
|
||||
"(GMT+03:00) Arabian Standard Time - Qatar" => "Asia/Qatar",
|
||||
"(GMT+03:00) Arabian Standard Time - Riyadh" => "Asia/Riyadh",
|
||||
"(GMT+03:00) East Africa Time - Juba" => "Africa/Juba",
|
||||
"(GMT+03:00) East Africa Time - Nairobi" => "Africa/Nairobi",
|
||||
"(GMT+03:00) Eastern European Time - Amman" => "Asia/Amman",
|
||||
"(GMT+03:00) Eastern European Time - Athens" => "Europe/Athens",
|
||||
"(GMT+03:00) Eastern European Time - Beirut" => "Asia/Beirut",
|
||||
"(GMT+03:00) Eastern European Time - Bucharest" => "Europe/Bucharest",
|
||||
"(GMT+03:00) Eastern European Time - Chisinau" => "Europe/Chisinau",
|
||||
"(GMT+03:00) Eastern European Time - Damascus" => "Asia/Damascus",
|
||||
"(GMT+03:00) Eastern European Time - Gaza" => "Asia/Gaza",
|
||||
"(GMT+03:00) Eastern European Time - Hebron" => "Asia/Hebron",
|
||||
"(GMT+03:00) Eastern European Time - Helsinki" => "Europe/Helsinki",
|
||||
"(GMT+03:00) Eastern European Time - Kiev" => "Europe/Kiev",
|
||||
"(GMT+03:00) Eastern European Time - Nicosia" => "Asia/Nicosia",
|
||||
"(GMT+03:00) Eastern European Time - Riga" => "Europe/Riga",
|
||||
"(GMT+03:00) Eastern European Time - Sofia" => "Europe/Sofia",
|
||||
"(GMT+03:00) Eastern European Time - Tallinn" => "Europe/Tallinn",
|
||||
"(GMT+03:00) Eastern European Time - Uzhhorod" => "Europe/Uzhgorod",
|
||||
"(GMT+03:00) Eastern European Time - Vilnius" => "Europe/Vilnius",
|
||||
"(GMT+03:00) Eastern European Time - Zaporozhye" => "Europe/Zaporozhye",
|
||||
"(GMT+03:00) Famagusta Time" => "Asia/Famagusta",
|
||||
"(GMT+03:00) Israel Time" => "Asia/Jerusalem",
|
||||
"(GMT+03:00) Kirov Time" => "Europe/Kirov",
|
||||
"(GMT+03:00) Moscow Standard Time - Minsk" => "Europe/Minsk",
|
||||
"(GMT+03:00) Moscow Standard Time - Moscow" => "Europe/Moscow",
|
||||
"(GMT+03:00) Moscow Standard Time - Simferopol" => "Europe/Simferopol",
|
||||
"(GMT+03:00) Syowa Time" => "Antarctica/Syowa",
|
||||
"(GMT+03:00) Turkey Time" => "Europe/Istanbul",
|
||||
"(GMT+04:00) Armenia Standard Time" => "Asia/Yerevan",
|
||||
"(GMT+04:00) Astrakhan Time" => "Europe/Astrakhan",
|
||||
"(GMT+04:00) Azerbaijan Standard Time" => "Asia/Baku",
|
||||
"(GMT+04:00) Georgia Standard Time" => "Asia/Tbilisi",
|
||||
"(GMT+04:00) Gulf Standard Time" => "Asia/Dubai",
|
||||
"(GMT+04:00) Mauritius Standard Time" => "Indian/Mauritius",
|
||||
"(GMT+04:00) Réunion Time" => "Indian/Reunion",
|
||||
"(GMT+04:00) Samara Standard Time" => "Europe/Samara",
|
||||
"(GMT+04:00) Saratov Time" => "Europe/Saratov",
|
||||
"(GMT+04:00) Seychelles Time" => "Indian/Mahe",
|
||||
"(GMT+04:00) Ulyanovsk Time" => "Europe/Ulyanovsk",
|
||||
"(GMT+04:00) Volgograd Standard Time" => "Europe/Volgograd",
|
||||
"(GMT+04:30) Afghanistan Time" => "Asia/Kabul",
|
||||
"(GMT+04:30) Iran Time" => "Asia/Tehran",
|
||||
"(GMT+05:00) French Southern & Antarctic Time" => "Indian/Kerguelen",
|
||||
"(GMT+05:00) Maldives Time" => "Indian/Maldives",
|
||||
"(GMT+05:00) Mawson Time" => "Antarctica/Mawson",
|
||||
"(GMT+05:00) Pakistan Standard Time" => "Asia/Karachi",
|
||||
"(GMT+05:00) Tajikistan Time" => "Asia/Dushanbe",
|
||||
"(GMT+05:00) Turkmenistan Standard Time" => "Asia/Ashgabat",
|
||||
"(GMT+05:00) Uzbekistan Standard Time - Samarkand" => "Asia/Samarkand",
|
||||
"(GMT+05:00) Uzbekistan Standard Time - Tashkent" => "Asia/Tashkent",
|
||||
"(GMT+05:00) West Kazakhstan Time - Aqtau" => "Asia/Aqtau",
|
||||
"(GMT+05:00) West Kazakhstan Time - Aqtobe" => "Asia/Aqtobe",
|
||||
"(GMT+05:00) West Kazakhstan Time - Atyrau" => "Asia/Atyrau",
|
||||
"(GMT+05:00) West Kazakhstan Time - Oral" => "Asia/Oral",
|
||||
"(GMT+05:00) West Kazakhstan Time - Qyzylorda" => "Asia/Qyzylorda",
|
||||
"(GMT+05:00) Yekaterinburg Standard Time" => "Asia/Yekaterinburg",
|
||||
"(GMT+05:30) Indian Standard Time - Colombo" => "Asia/Colombo",
|
||||
"(GMT+05:30) Indian Standard Time - Kolkata" => "Asia/Kolkata",
|
||||
"(GMT+05:45) Nepal Time" => "Asia/Kathmandu",
|
||||
"(GMT+06:00) Bangladesh Standard Time" => "Asia/Dhaka",
|
||||
"(GMT+06:00) Bhutan Time" => "Asia/Thimphu",
|
||||
"(GMT+06:00) East Kazakhstan Time - Almaty" => "Asia/Almaty",
|
||||
"(GMT+06:00) East Kazakhstan Time - Kostanay" => "Asia/Qostanay",
|
||||
"(GMT+06:00) Indian Ocean Time" => "Indian/Chagos",
|
||||
"(GMT+06:00) Kyrgyzstan Time" => "Asia/Bishkek",
|
||||
"(GMT+06:00) Omsk Standard Time" => "Asia/Omsk",
|
||||
"(GMT+06:00) Urumqi Time" => "Asia/Urumqi",
|
||||
"(GMT+06:00) Vostok Time" => "Antarctica/Vostok",
|
||||
"(GMT+06:30) Cocos Islands Time" => "Indian/Cocos",
|
||||
"(GMT+06:30) Myanmar Time" => "Asia/Yangon",
|
||||
"(GMT+07:00) Barnaul Time" => "Asia/Barnaul",
|
||||
"(GMT+07:00) Christmas Island Time" => "Indian/Christmas",
|
||||
"(GMT+07:00) Davis Time" => "Antarctica/Davis",
|
||||
"(GMT+07:00) Hovd Standard Time" => "Asia/Hovd",
|
||||
"(GMT+07:00) Indochina Time - Bangkok" => "Asia/Bangkok",
|
||||
"(GMT+07:00) Indochina Time - Ho Chi Minh City" => "Asia/Ho_Chi_Minh",
|
||||
"(GMT+07:00) Krasnoyarsk Standard Time - Krasnoyarsk" => "Asia/Krasnoyarsk",
|
||||
"(GMT+07:00) Krasnoyarsk Standard Time - Novokuznetsk" => "Asia/Novokuznetsk",
|
||||
"(GMT+07:00) Novosibirsk Standard Time" => "Asia/Novosibirsk",
|
||||
"(GMT+07:00) Tomsk Time" => "Asia/Tomsk",
|
||||
"(GMT+07:00) Western Indonesia Time - Jakarta" => "Asia/Jakarta",
|
||||
"(GMT+07:00) Western Indonesia Time - Pontianak" => "Asia/Pontianak",
|
||||
"(GMT+08:00) Australian Western Standard Time - Casey" => "Antarctica/Casey",
|
||||
"(GMT+08:00) Australian Western Standard Time - Perth" => "Australia/Perth",
|
||||
"(GMT+08:00) Brunei Darussalam Time" => "Asia/Brunei",
|
||||
"(GMT+08:00) Central Indonesia Time" => "Asia/Makassar",
|
||||
"(GMT+08:00) China Standard Time - Macao" => "Asia/Macau",
|
||||
"(GMT+08:00) China Standard Time - Shanghai" => "Asia/Shanghai",
|
||||
"(GMT+08:00) Hong Kong Standard Time" => "Asia/Hong_Kong",
|
||||
"(GMT+08:00) Irkutsk Standard Time" => "Asia/Irkutsk",
|
||||
"(GMT+08:00) Malaysia Time - Kuala Lumpur" => "Asia/Kuala_Lumpur",
|
||||
"(GMT+08:00) Malaysia Time - Kuching" => "Asia/Kuching",
|
||||
"(GMT+08:00) Philippine Standard Time" => "Asia/Manila",
|
||||
"(GMT+08:00) Singapore Standard Time" => "Asia/Singapore",
|
||||
"(GMT+08:00) Taipei Standard Time" => "Asia/Taipei",
|
||||
"(GMT+08:00) Ulaanbaatar Standard Time - Choibalsan" => "Asia/Choibalsan",
|
||||
"(GMT+08:00) Ulaanbaatar Standard Time - Ulaanbaatar" => "Asia/Ulaanbaatar",
|
||||
"(GMT+08:45) Australian Central Western Standard Time" => "Australia/Eucla",
|
||||
"(GMT+09:00) East Timor Time" => "Asia/Dili",
|
||||
"(GMT+09:00) Eastern Indonesia Time" => "Asia/Jayapura",
|
||||
"(GMT+09:00) Japan Standard Time" => "Asia/Tokyo",
|
||||
"(GMT+09:00) Korean Standard Time - Pyongyang" => "Asia/Pyongyang",
|
||||
"(GMT+09:00) Korean Standard Time - Seoul" => "Asia/Seoul",
|
||||
"(GMT+09:00) Palau Time" => "Pacific/Palau",
|
||||
"(GMT+09:00) Yakutsk Standard Time - Chita" => "Asia/Chita",
|
||||
"(GMT+09:00) Yakutsk Standard Time - Khandyga" => "Asia/Khandyga",
|
||||
"(GMT+09:00) Yakutsk Standard Time - Yakutsk" => "Asia/Yakutsk",
|
||||
"(GMT+09:30) Australian Central Standard Time" => "Australia/Darwin",
|
||||
"(GMT+09:30) Central Australia Time - Adelaide" => "Australia/Adelaide",
|
||||
"(GMT+09:30) Central Australia Time - Broken Hill" => "Australia/Broken_Hill",
|
||||
"(GMT+10:00) Australian Eastern Standard Time - Brisbane" => "Australia/Brisbane",
|
||||
"(GMT+10:00) Australian Eastern Standard Time - Lindeman" => "Australia/Lindeman",
|
||||
"(GMT+10:00) Chamorro Standard Time" => "Pacific/Guam",
|
||||
"(GMT+10:00) Chuuk Time" => "Pacific/Chuuk",
|
||||
"(GMT+10:00) Dumont-d’Urville Time" => "Antarctica/DumontDUrville",
|
||||
"(GMT+10:00) Eastern Australia Time - Currie" => "Australia/Currie",
|
||||
"(GMT+10:00) Eastern Australia Time - Hobart" => "Australia/Hobart",
|
||||
"(GMT+10:00) Eastern Australia Time - Melbourne" => "Australia/Melbourne",
|
||||
"(GMT+10:00) Eastern Australia Time - Sydney" => "Australia/Sydney",
|
||||
"(GMT+10:00) Papua New Guinea Time" => "Pacific/Port_Moresby",
|
||||
"(GMT+10:00) Vladivostok Standard Time - Ust-Nera" => "Asia/Ust-Nera",
|
||||
"(GMT+10:00) Vladivostok Standard Time - Vladivostok" => "Asia/Vladivostok",
|
||||
"(GMT+10:30) Lord Howe Time" => "Australia/Lord_Howe",
|
||||
"(GMT+11:00) Bougainville Time" => "Pacific/Bougainville",
|
||||
"(GMT+11:00) Kosrae Time" => "Pacific/Kosrae",
|
||||
"(GMT+11:00) Macquarie Island Time" => "Antarctica/Macquarie",
|
||||
"(GMT+11:00) Magadan Standard Time" => "Asia/Magadan",
|
||||
"(GMT+11:00) New Caledonia Standard Time" => "Pacific/Noumea",
|
||||
"(GMT+11:00) Norfolk Island Time" => "Pacific/Norfolk",
|
||||
"(GMT+11:00) Ponape Time" => "Pacific/Pohnpei",
|
||||
"(GMT+11:00) Sakhalin Standard Time" => "Asia/Sakhalin",
|
||||
"(GMT+11:00) Solomon Islands Time" => "Pacific/Guadalcanal",
|
||||
"(GMT+11:00) Srednekolymsk Time" => "Asia/Srednekolymsk",
|
||||
"(GMT+11:00) Vanuatu Standard Time" => "Pacific/Efate",
|
||||
"(GMT+12:00) Anadyr Standard Time" => "Asia/Anadyr",
|
||||
"(GMT+12:00) Fiji Time" => "Pacific/Fiji",
|
||||
"(GMT+12:00) Gilbert Islands Time" => "Pacific/Tarawa",
|
||||
"(GMT+12:00) Marshall Islands Time - Kwajalein" => "Pacific/Kwajalein",
|
||||
"(GMT+12:00) Marshall Islands Time - Majuro" => "Pacific/Majuro",
|
||||
"(GMT+12:00) Nauru Time" => "Pacific/Nauru",
|
||||
"(GMT+12:00) New Zealand Time" => "Pacific/Auckland",
|
||||
"(GMT+12:00) Petropavlovsk-Kamchatski Standard Time" => "Asia/Kamchatka",
|
||||
"(GMT+12:00) Tuvalu Time" => "Pacific/Funafuti",
|
||||
"(GMT+12:00) Wake Island Time" => "Pacific/Wake",
|
||||
"(GMT+12:00) Wallis & Futuna Time" => "Pacific/Wallis",
|
||||
"(GMT+12:45) Chatham Time" => "Pacific/Chatham",
|
||||
"(GMT+13:00) Apia Time" => "Pacific/Apia",
|
||||
"(GMT+13:00) Phoenix Islands Time" => "Pacific/Enderbury",
|
||||
"(GMT+13:00) Tokelau Time" => "Pacific/Fakaofo",
|
||||
"(GMT+13:00) Tonga Standard Time" => "Pacific/Tongatapu",
|
||||
"(GMT+14:00) Line Islands Time" => "Pacific/Kiritimati"
|
||||
);
|
||||
}
|
||||
47
core/lib/saml2/xmlseclibs.php
Normal file
47
core/lib/saml2/xmlseclibs.php
Normal file
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
/**
|
||||
* xmlseclibs.php
|
||||
*
|
||||
* Copyright (c) 2007-2016, 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-2017 Robert Richards <rrichards@cdatazone.org>
|
||||
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
|
||||
* @version 3.0.1
|
||||
*/
|
||||
|
||||
$xmlseclibs_srcdir = dirname(__FILE__) . '/SAML2Core';
|
||||
require $xmlseclibs_srcdir . '/MoXMLSecurityKey.php';
|
||||
require $xmlseclibs_srcdir . '/MoXMLSecurityDSig.php';
|
||||
require $xmlseclibs_srcdir . '/MoXMLSecEnc.php';
|
||||
require $xmlseclibs_srcdir . '/Utils/MoXPath.php';
|
||||
Reference in New Issue
Block a user