diff --git a/lib/SimpleSAML/Bindings/Shib13/HTTPPost.php b/lib/SimpleSAML/Bindings/Shib13/HTTPPost.php
index edd4b2c126cb628625338892a89b6c7b2682633b..e57968c569e83647f05f0693b05eed1870841b07 100644
--- a/lib/SimpleSAML/Bindings/Shib13/HTTPPost.php
+++ b/lib/SimpleSAML/Bindings/Shib13/HTTPPost.php
@@ -80,7 +80,9 @@ class SimpleSAML_Bindings_Shib13_HTTPPost {
$privatekey = $this->configuration->getBaseDir() . '/cert/' . $idpmd['privatekey'];
$publiccert = $this->configuration->getBaseDir() . '/cert/' . $idpmd['certificate'];
+ $certchain_pem_file = $this->configuration->getBaseDir() . '/cert/' . $idpmd['certificatechain'];
+ $privatek = file_get_contents($privatekey);
if (strstr($claimedacs, $destination) == 0) {
$destination = $claimedacs;
@@ -89,6 +91,8 @@ class SimpleSAML_Bindings_Shib13_HTTPPost {
}
+
+
/*
* XMLDSig. Sign the complete request with the key stored in cert/server.pem
*/
@@ -107,7 +111,14 @@ class SimpleSAML_Bindings_Shib13_HTTPPost {
//$assertionroot = $responsedom->getElementsByTagName('Assertion')->item(1);
$firstassertionroot = $responsedom->getElementsByTagName('Assertion')->item(0);
- $objXMLSecDSig->addReferenceList(array($responseroot), XMLSecurityDSig::SHA1, array('http://www.w3.org/2000/09/xmldsig#enveloped-signature'), null, 'ResponseID');
+ #$objXMLSecDSig->addReferenceList(array($responseroot), XMLSecurityDSig::SHA1, #array('http://www.w3.org/2000/09/xmldsig#enveloped-signature'), null, 'ResponseID');
+
+ $objXMLSecDSig->addReferenceList(array($responseroot), XMLSecurityDSig::SHA1,
+ array('http://www.w3.org/2000/09/xmldsig#enveloped-signature'),
+ array('id_name' => 'ResponseID'));
+
+ // TODO: Add option to sign assertion versus response
+
#$objXMLSecDSig->addReferenceList(array($firstassertionroot), XMLSecurityDSig::SHA1, array('http://www.w3.org/2000/09/xmldsig#enveloped-signature',
# 'http://www.w3.org/2001/10/xml-exc-c14n#'));
@@ -116,17 +127,33 @@ class SimpleSAML_Bindings_Shib13_HTTPPost {
/* create new XMLSecKey using RSA-SHA-1 and type is private key */
$objKey = new XMLSecurityKey(XMLSecurityKey::RSA_SHA1, array('type'=>'private'));
+ //$objKey->passphrase = '1234';
+
/* load the private key from file - last arg is bool if key in file (TRUE) or is string (FALSE) */
- $objKey->loadKey($privatekey,TRUE);
+ #$objKey->loadKey($privatekey_pem,false);
+ $objKey->loadKey($privatek,false);
+ // TODO: Check for whether cert files exists or not.
+ $objXMLSecDSig->sign($objKey);
+ $public_cert = file_get_contents($publiccert);
+ //echo '<pre>publiccert:' . $public_cert . '</pre>';
- $objXMLSecDSig->sign($objKey);
- $public_cert = file_get_contents($publiccert);
$objXMLSecDSig->add509Cert($public_cert, true);
+
+ if (isset($certchain_pem_file)) {
+ $certchain_pem = file_get_contents($certchain_pem_file);
+
+ //echo '<pre>chain:' . $certchain_pem . '</pre>';
+ $certchain = XMLSecurityDSig::staticGet509XCerts($certchain_pem);
+# foreach ($certchain AS $scert) {
+ $objXMLSecDSig->add509Cert($certchain_pem, true);
+# }
+ }
+
/*
$public_cert = file_get_contents("cert/edugain/public2.pem");
$objXMLSecDSig->add509Cert($public_cert, true);
diff --git a/lib/xmlseclibs.php b/lib/xmlseclibs.php
index e78e2ea9615c2995cde02c1e69843758fbf5ae7f..eab4a9c06e4bb5a814325dfe771976e23606dd06 100644
--- a/lib/xmlseclibs.php
+++ b/lib/xmlseclibs.php
@@ -1,1430 +1,1468 @@
-<?php
-/**
- * xmlseclibs.php
- *
- * Copyright (c) 2007, Robert Richards <rrichards@ctindustries.net>.
- * 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@ctindustries.net>
- * @copyright 2007 Robert Richards <rrichards@ctindustries.net>
- * @license http://www.opensource.org/licenses/bsd-license.php BSD License
- * @version 1.0.0
- */
-
-/*
-Functions to generate simple cases of Exclusive Canonical XML - Callable function is C14NGeneral()
-i.e.: $canonical = C14NGeneral($domelement, TRUE);
-*/
-
-/* helper function */
-function sortAndAddAttrs($element, $arAtts) {
- $newAtts = array();
- foreach ($arAtts AS $attnode) {
- $newAtts[$attnode->nodeName] = $attnode;
- }
- ksort($newAtts);
- foreach ($newAtts as $attnode) {
- $element->setAttribute($attnode->nodeName, $attnode->nodeValue);
- }
-}
-
-/* helper function */
-function canonical($tree, $element, $withcomments) {
- if ($tree->nodeType != XML_DOCUMENT_NODE) {
- $dom = $tree->ownerDocument;
- } else {
- $dom = $tree;
- }
- if ($element->nodeType != XML_ELEMENT_NODE) {
- if ($element->nodeType == XML_COMMENT_NODE && ! $withcomments) {
- return;
- }
- $tree->appendChild($dom->importNode($element, TRUE));
- return;
- }
- $arNS = array();
- if ($element->namespaceURI != "") {
- if ($element->prefix == "") {
- $elCopy = $dom->createElementNS($element->namespaceURI, $element->nodeName);
- } else {
- $prefix = $tree->lookupPrefix($element->namespaceURI);
- if ($prefix == $element->prefix) {
- $elCopy = $dom->createElementNS($element->namespaceURI, $element->nodeName);
- } else {
- $elCopy = $dom->createElement($element->nodeName);
- $arNS[$element->namespaceURI] = $element->prefix;
- }
- }
- } else {
- $elCopy = $dom->createElement($element->nodeName);
- }
- $tree->appendChild($elCopy);
-
- /* Create DOMXPath based on original document */
- $xPath = new DOMXPath($element->ownerDocument);
-
- /* Get namespaced attributes */
- $arAtts = $xPath->query('attribute::*[namespace-uri(.) != ""]', $element);
-
- /* Create an array with namespace URIs as keys, and sort them */
- foreach ($arAtts AS $attnode) {
- if (array_key_exists($attnode->namespaceURI, $arNS) &&
- ($arNS[$attnode->namespaceURI] == $attnode->prefix)) {
- continue;
- }
- $prefix = $tree->lookupPrefix($attnode->namespaceURI);
- if ($prefix != $attnode->prefix) {
- $arNS[$attnode->namespaceURI] = $attnode->prefix;
- } else {
- $arNS[$attnode->namespaceURI] = NULL;
- }
- }
- if (count($arNS) > 0) {
- asort($arNS);
- }
-
- /* Add namespace nodes */
- foreach ($arNS AS $namespaceURI=>$prefix) {
- if ($prefix != NULL) {
- $elCopy->setAttributeNS("http://www.w3.org/2000/xmlns/",
- "xmlns:".$prefix, $namespaceURI);
- }
- }
- if (count($arNS) > 0) {
- ksort($arNS);
- }
-
- /* Get attributes not in a namespace, and then sort and add them */
- $arAtts = $xPath->query('attribute::*[namespace-uri(.) = ""]', $element);
- sortAndAddAttrs($elCopy, $arAtts);
-
- /* Loop through the URIs, and then sort and add attributes within that namespace */
- foreach ($arNS as $nsURI=>$prefix) {
- $arAtts = $xPath->query('attribute::*[namespace-uri(.) = "'.$nsURI.'"]', $element);
- sortAndAddAttrs($elCopy, $arAtts);
- }
-
- foreach ($element->childNodes AS $node) {
- canonical($elCopy, $node, $withcomments);
- }
-}
-
-/*
-$element - DOMElement for which to produce the canonical version of
-$exclusive - boolean to indicate exclusive canonicalization (must pass TRUE)
-$withcomments - boolean indicating wether or not to include comments in canonicalized form
-*/
-function C14NGeneral($element, $exclusive=FALSE, $withcomments=FALSE) {
- /* IF PHP 5.2+ then use built in canonical functionality */
- $php_version = explode('.', PHP_VERSION);
- if (($php_version[0] > 5) || ($php_version[0] == 5 && $php_version[1] >= 2) ) {
- return $element->C14N($exclusive, $withcomments);
- }
-
- /* Must be element */
- if (! $element instanceof DOMElement) {
- return NULL;
- }
- /* Currently only exclusive XML is supported */
- if ($exclusive == FALSE) {
- throw new Exception("Only exclusive canonicalization is supported in this version of PHP");
- }
-
- $copyDoc = new DOMDocument();
- canonical($copyDoc, $element, $withcomments);
- return $copyDoc->saveXML($copyDoc->documentElement, LIBXML_NOEMPTYTAG);
-}
-
-class XMLSecurityKey {
- const TRIPLEDES_CBC = 'http://www.w3.org/2001/04/xmlenc#tripledes-cbc';
- const AES128_CBC = 'http://www.w3.org/2001/04/xmlenc#aes128-cbc';
- const AES192_CBC = 'http://www.w3.org/2001/04/xmlenc#aes192-cbc';
- const AES256_CBC = 'http://www.w3.org/2001/04/xmlenc#aes256-cbc';
- const RSA_1_5 = 'http://www.w3.org/2001/04/xmlenc#rsa-1_5';
- const RSA_OAEP_MGF1P = 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p';
- const RSA_SHA1 = 'http://www.w3.org/2000/09/xmldsig#rsa-sha1';
- const DSA_SHA1 = 'http://www.w3.org/2000/09/xmldsig#dsa-sha1';
-
- private $cryptParams = array();
- public $type = 0;
- public $key = NULL;
- public $passphrase = "";
- public $iv = NULL;
- public $name = NULL;
- public $keyChain = NULL;
- public $isEncrypted = FALSE;
- public $encryptedCtx = NULL;
- public $guid = NULL;
-
- public function __construct($type, $params=NULL) {
- switch ($type) {
- case (XMLSecurityKey::TRIPLEDES_CBC):
- $this->cryptParams['library'] = 'mcrypt';
- $this->cryptParams['cipher'] = MCRYPT_TRIPLEDES;
- $this->cryptParams['mode'] = MCRYPT_MODE_CBC;
- $this->cryptParams['method'] = 'http://www.w3.org/2001/04/xmlenc#tripledes-cbc';
- break;
- case (XMLSecurityKey::AES128_CBC):
- $this->cryptParams['library'] = 'mcrypt';
- $this->cryptParams['cipher'] = MCRYPT_RIJNDAEL_128;
- $this->cryptParams['mode'] = MCRYPT_MODE_CBC;
- $this->cryptParams['method'] = 'http://www.w3.org/2001/04/xmlenc#aes128-cbc';
- break;
- case (XMLSecurityKey::AES192_CBC):
- $this->cryptParams['library'] = 'mcrypt';
- $this->cryptParams['cipher'] = MCRYPT_RIJNDAEL_128;
- $this->cryptParams['mode'] = MCRYPT_MODE_CBC;
- $this->cryptParams['method'] = 'http://www.w3.org/2001/04/xmlenc#aes192-cbc';
- break;
- case (XMLSecurityKey::AES256_CBC):
- $this->cryptParams['library'] = 'mcrypt';
- $this->cryptParams['cipher'] = MCRYPT_RIJNDAEL_128;
- $this->cryptParams['mode'] = MCRYPT_MODE_CBC;
- $this->cryptParams['method'] = 'http://www.w3.org/2001/04/xmlenc#aes256-cbc';
- break;
- case (XMLSecurityKey::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');
- return;
- case (XMLSecurityKey::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');
- return;
- case (XMLSecurityKey::RSA_SHA1):
- $this->cryptParams['library'] = 'openssl';
- $this->cryptParams['method'] = 'http://www.w3.org/2000/09/xmldsig#rsa-sha1';
- 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');
- break;
- default:
- throw new Exception('Invalid Key Type');
- return;
- }
- $this->type = $type;
- }
-
- public function generateSessionKey() {
- $key = '';
- if (! empty($this->cryptParams['cipher']) && ! empty($this->cryptParams['mode'])) {
- $keysize = mcrypt_module_get_algo_key_size($this->cryptParams['cipher']);
- /* Generating random key using iv generation routines */
- if (($keysize > 0) && ($td = mcrypt_module_open(MCRYPT_RIJNDAEL_256, '',$this->cryptParams['mode'], ''))) {
- if ($this->cryptParams['cipher'] == MCRYPT_RIJNDAEL_128) {
- $keysize = 16;
- if ($this->type == XMLSecurityKey::AES256_CBC) {
- $keysize = 32;
- } elseif ($this->type == XMLSecurityKey::AES192_CBC) {
- $keysize = 24;
- }
- }
- while (strlen($key) < $keysize) {
- $key .= mcrypt_create_iv(mcrypt_enc_get_iv_size ($td),MCRYPT_RAND);
- }
- mcrypt_module_close($td);
- $key = substr($key, 0, $keysize);
- $this->key = $key;
- }
- }
- return $key;
- }
-
- 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->key = $str_cert;
- }
- if ($this->cryptParams['library'] == 'openssl') {
- if ($this->cryptParams['type'] == 'public') {
- $this->key = openssl_get_publickey($this->key);
- } else {
- $this->key = openssl_get_privatekey($this->key, $this->passphrase);
- }
- } else if ($this->cryptParams['cipher'] == MCRYPT_RIJNDAEL_128) {
- /* Check key length */
- switch ($this->type) {
- case (XMLSecurityKey::AES256_CBC):
- if (strlen($this->key) < 25) {
- throw new Exception('Key must contain at least 25 characters for this cipher');
- }
- break;
- case (XMLSecurityKey::AES192_CBC):
- if (strlen($this->key) < 17) {
- throw new Exception('Key must contain at least 17 characters for this cipher');
- }
- break;
- }
- }
- }
-
- private function encryptMcrypt($data) {
- $td = mcrypt_module_open($this->cryptParams['cipher'], '', $this->cryptParams['mode'], '');
- $this->iv = mcrypt_create_iv (mcrypt_enc_get_iv_size($td), MCRYPT_RAND);
- mcrypt_generic_init($td, $this->key, $this->iv);
- $encrypted_data = $this->iv.mcrypt_generic($td, $data);
- mcrypt_generic_deinit($td);
- mcrypt_module_close($td);
- return $encrypted_data;
- }
-
- private function decryptMcrypt($data) {
- $td = mcrypt_module_open($this->cryptParams['cipher'], '', $this->cryptParams['mode'], '');
- $iv_length = mcrypt_enc_get_iv_size($td);
-
- $this->iv = substr($data, 0, $iv_length);
- $data = substr($data, $iv_length);
-
- mcrypt_generic_init($td, $this->key, $this->iv);
- $decrypted_data = mdecrypt_generic($td, $data);
- mcrypt_generic_deinit($td);
- mcrypt_module_close($td);
- if ($this->cryptParams['mode'] == MCRYPT_MODE_CBC) {
- $dataLen = strlen($decrypted_data);
- $paddingLength = substr($decrypted_data, $dataLen - 1, 1);
- $decrypted_data = substr($decrypted_data, 0, $dataLen - ord($paddingLength));
- }
- return $decrypted_data;
- }
-
- private function encryptOpenSSL($data) {
- if ($this->cryptParams['type'] == 'public') {
- if (! openssl_public_encrypt($data, $encrypted_data, $this->key, $this->cryptParams['padding'])) {
- throw new Exception('Failure encrypting Data');
- return;
- }
- } else {
- if (! openssl_private_encrypt($data, $encrypted_data, $this->key, $this->cryptParams['padding'])) {
- throw new Exception('Failure encrypting Data');
- return;
- }
- }
- return $encrypted_data;
- }
-
- private function decryptOpenSSL($data) {
- if ($this->cryptParams['type'] == 'public') {
- if (! openssl_public_decrypt($data, $decrypted, $this->key, $this->cryptParams['padding'])) {
- throw new Exception('Failure decrypting Data');
- return;
- }
- } else {
- if (! openssl_private_decrypt($data, $decrypted, $this->key, $this->cryptParams['padding'])) {
- throw new Exception('Failure decrypting Data');
- return;
- }
- }
- return $decrypted;
- }
-
- private function signOpenSSL($data) {
- if (! openssl_sign ($data, $signature, $this->key)) {
- throw new Exception('Failure Signing Data');
- return;
- }
- return $signature;
- }
-
- private function verifyOpenSSL($data, $signature) {
- return openssl_verify ($data, $signature, $this->key);
- }
-
- public function encryptData($data) {
- switch ($this->cryptParams['library']) {
- case 'mcrypt':
- return $this->encryptMcrypt($data);
- break;
- case 'openssl':
- return $this->encryptOpenSSL($data);
- break;
- }
- }
-
- public function decryptData($data) {
- switch ($this->cryptParams['library']) {
- case 'mcrypt':
- return $this->decryptMcrypt($data);
- break;
- case 'openssl':
- return $this->decryptOpenSSL($data);
- break;
- }
- }
-
- public function signData($data) {
- switch ($this->cryptParams['library']) {
- case 'openssl':
- return $this->signOpenSSL($data);
- break;
- }
- }
-
- public function verifySignature($data, $signature) {
- switch ($this->cryptParams['library']) {
- case 'openssl':
- return $this->verifyOpenSSL($data, $signature);
- break;
- }
- }
-
- public function getAlgorith() {
- return $this->cryptParams['method'];
- }
-
- 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);
- }
-
- /* Modulus and Exponent must already be base64 decoded */
- static function convertRSA($modulus, $exponent) {
- /* make an ASN publicKeyInfo */
- $exponentEncoding = XMLSecurityKey::makeAsnSegment(0x02, $exponent);
- $modulusEncoding = XMLSecurityKey::makeAsnSegment(0x02, $modulus);
- $sequenceEncoding = XMLSecurityKey:: makeAsnSegment(0x30, $modulusEncoding.$exponentEncoding);
- $bitstringEncoding = XMLSecurityKey::makeAsnSegment(0x03, $sequenceEncoding);
- $rsaAlgorithmIdentifier = pack("H*", "300D06092A864886F70D0101010500");
- $publicKeyInfo = XMLSecurityKey::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";
- }
-
- public function serializeKey($parent) {
-
- }
-}
-
-class XMLSecurityDSig {
- const XMLDSIGNS = 'http://www.w3.org/2000/09/xmldsig#';
- const SHA1 = 'http://www.w3.org/2000/09/xmldsig#sha1';
- const SHA256 = 'http://www.w3.org/2001/04/xmlenc#sha256';
- const SHA512 = 'http://www.w3.org/2001/04/xmlenc#sha512';
- const RIPEMD160 = 'http://www.w3.org/2001/04/xmlenc#ripemd160';
-
- const C14N = 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315';
- const C14N_COMMENTS = 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments';
- const EXC_C14N = 'http://www.w3.org/2001/10/xml-exc-c14n#';
- const EXC_C14N_COMMENTS = 'http://www.w3.org/2001/10/xml-exc-c14n#WithComments';
-
- const template = '<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
- <ds:SignedInfo>
- <ds:SignatureMethod />
- </ds:SignedInfo>
-</ds:Signature>';
-
- public $sigNode = NULL;
- public $idKeys = array();
- public $idNS = array();
- private $signedInfo = NULL;
- private $xPathCtx = NULL;
- private $canonicalMethod = NULL;
- private $prefix = 'ds';
- private $searchpfx = 'secdsig';
-
- public function __construct() {
- $sigdoc = new DOMDocument();
- $sigdoc->loadXML(XMLSecurityDSig::template);
- $this->sigNode = $sigdoc->documentElement;
- }
-
- private function getXPathObj() {
- if (empty($this->xPathCtx) && ! empty($this->sigNode)) {
- $xpath = new DOMXPath($this->sigNode->ownerDocument);
- $xpath->registerNamespace('secdsig', XMLSecurityDSig::XMLDSIGNS);
-
- $this->xPathCtx = $xpath;
- }
- return $this->xPathCtx;
- }
-
- static function generate_GUID($prefix='_pfx') {
- $uuid = md5(uniqid(rand(), true));
- $guid = $prefix.substr($uuid,0,8)."-".
- substr($uuid,8,4)."-".
- substr($uuid,12,4)."-".
- substr($uuid,16,4)."-".
- substr($uuid,20,12);
- return $guid;
- }
-
- public function locateSignature($objDoc) {
- if ($objDoc instanceof DOMDocument) {
- $doc = $objDoc;
- } else {
- $doc = $objDoc->ownerDocument;
- }
- if ($doc) {
- $xpath = new DOMXPath($doc);
- $xpath->registerNamespace('secdsig', XMLSecurityDSig::XMLDSIGNS);
- $query = ".//secdsig:Signature";
- $nodeset = $xpath->query($query, $objDoc);
-
-
- $this->sigNode = $nodeset->item(0);
- return $this->sigNode;
- }
- return NULL;
- }
-
- public function createNewSignNode($name, $value=NULL) {
- $doc = $this->sigNode->ownerDocument;
- if (! is_null($value)) {
- $node = $doc->createElementNS(XMLSecurityDSig::XMLDSIGNS, $this->prefix.':'.$name, $value);
- } else {
- $node = $doc->createElementNS(XMLSecurityDSig::XMLDSIGNS, $this->prefix.':'.$name);
- }
- return $node;
- }
-
- public function setCanonicalMethod($method) {
- switch ($method) {
- case 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315':
- case 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments':
- case 'http://www.w3.org/2001/10/xml-exc-c14n#':
- case 'http://www.w3.org/2001/10/xml-exc-c14n#WithComments':
- $this->canonicalMethod = $method;
- break;
- default:
- throw new Exception('Invalid Canonical Method');
- }
- if ($xpath = $this->getXPathObj()) {
- $query = './'.$this->searchpfx.':SignedInfo';
- $nodeset = $xpath->query($query, $this->sigNode);
- if ($sinfo = $nodeset->item(0)) {
- $query = './'.$this->searchpfx.'CanonicalizationMethod';
- $nodeset = $xpath->query($query, $sinfo);
- if (! ($canonNode = $nodeset->item(0))) {
- $canonNode = $this->createNewSignNode('CanonicalizationMethod');
- $sinfo->insertBefore($canonNode, $sinfo->firstChild);
- }
- $canonNode->setAttribute('Algorithm', $this->canonicalMethod);
- }
- }
- }
-
- private function canonicalizeData($node, $canonicalmethod, $arXPath=NULL, $prefixList=NULL) {
- $exclusive = FALSE;
- $withComments = FALSE;
- switch ($canonicalmethod) {
- case 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315':
- $exclusive = FALSE;
- $withComments = FALSE;
- break;
- case 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments':
- $withComments = TRUE;
- break;
- case 'http://www.w3.org/2001/10/xml-exc-c14n#':
- $exclusive = TRUE;
- break;
- case 'http://www.w3.org/2001/10/xml-exc-c14n#WithComments':
- $exclusive = TRUE;
- $withComments = TRUE;
- break;
- }
-/* Support PHP versions < 5.2 not containing C14N methods in DOM extension */
- $php_version = explode('.', PHP_VERSION);
- if (($php_version[0] < 5) || ($php_version[0] == 5 && $php_version[1] < 2) ) {
- if (! is_null($arXPath)) {
- throw new Exception("PHP 5.2.0 or higher is required to perform XPath Transformations");
- }
- return C14NGeneral($node, $exclusive, $withComments);
- }
- return $node->C14N($exclusive, $withComments, $arXPath, $prefixList);
- }
-
- public function canonicalizeSignedInfo() {
-
- $doc = $this->sigNode->ownerDocument;
- $canonicalmethod = NULL;
- if ($doc) {
- $xpath = $this->getXPathObj();
- $query = "./secdsig:SignedInfo";
- $nodeset = $xpath->query($query, $this->sigNode);
- if ($signInfoNode = $nodeset->item(0)) {
- $query = "./secdsig:CanonicalizationMethod";
- $nodeset = $xpath->query($query, $signInfoNode);
- if ($canonNode = $nodeset->item(0)) {
- $canonicalmethod = $canonNode->getAttribute('Algorithm');
- }
- $this->signedInfo = $this->canonicalizeData($signInfoNode, $canonicalmethod);
- return $this->signedInfo;
- }
- }
- return NULL;
- }
-
- public function calculateDigest ($digestAlgorithm, $data) {
- switch ($digestAlgorithm) {
- case XMLSecurityDSig::SHA1:
- $alg = 'sha1';
- break;
- case XMLSecurityDSig::SHA256:
- $alg = 'sha256';
- break;
- case XMLSecurityDSig::SHA512:
- $alg = 'sha512';
- break;
- case XMLSecurityDSig::RIPEMD160:
- $alg = 'ripemd160';
- break;
- default:
- throw new Exception("Cannot validate digest: Unsupported Algorith <$digestAlgorithm>");
- }
- return base64_encode(hash($alg, $data, TRUE));
- }
-
- public function validateDigest($refNode, $data) {
- $xpath = new DOMXPath($refNode->ownerDocument);
- $xpath->registerNamespace('secdsig', XMLSecurityDSig::XMLDSIGNS);
- $query = 'string(./secdsig:DigestMethod/@Algorithm)';
- $digestAlgorithm = $xpath->evaluate($query, $refNode);
- $digValue = $this->calculateDigest($digestAlgorithm, $data);
- $query = 'string(./secdsig:DigestValue)';
- $digestValue = $xpath->evaluate($query, $refNode);
- return ($digValue == $digestValue);
- }
-
- public function processTransforms($refNode, $objData) {
- $data = $objData;
- $xpath = new DOMXPath($refNode->ownerDocument);
- $xpath->registerNamespace('secdsig', XMLSecurityDSig::XMLDSIGNS);
- $query = './secdsig:Transforms/secdsig:Transform';
- $nodelist = $xpath->query($query, $refNode);
- $canonicalMethod = 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315';
- $arXPath = NULL;
- $prefixList = NULL;
- foreach ($nodelist AS $transform) {
- $algorithm = $transform->getAttribute("Algorithm");
- switch ($algorithm) {
- case 'http://www.w3.org/2001/10/xml-exc-c14n#':
- case 'http://www.w3.org/2001/10/xml-exc-c14n#WithComments':
- $node = $transform->firstChild;
- while ($node) {
- if ($node->localName == 'InclusiveNamespaces') {
- if ($pfx = $node->getAttribute('PrefixList')) {
- $arpfx = array();
- $pfxlist = split(" ", $pfx);
- foreach ($pfxlist AS $pfx) {
- $val = trim($pfx);
- if (! empty($val)) {
- $arpfx[] = $val;
- }
- }
- if (count($arpfx) > 0) {
- $prefixList = $arpfx;
- }
- }
- break;
- }
- $node = $node->nextSibling;
- }
- case 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315':
- case 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments':
- $canonicalMethod = $algorithm;
- break;
- case 'http://www.w3.org/TR/1999/REC-xpath-19991116':
- $node = $transform->firstChild;
- while ($node) {
- if ($node->localName == 'XPath') {
- $arXPath = array();
- $arXPath['query'] = '(.//. | .//@* | .//namespace::*)['.$node->nodeValue.']';
- $arXpath['namespaces'] = array();
- $nslist = $xpath->query('./namespace::*', $node);
- foreach ($nslist AS $nsnode) {
- if ($nsnode->localName != "xml") {
- $arXPath['namespaces'][$nsnode->localName] = $nsnode->nodeValue;
- }
- }
- break;
- }
- $node = $node->nextSibling;
- }
- break;
- }
- }
- if ($data instanceof DOMNode) {
- $data = $this->canonicalizeData($objData, $canonicalMethod, $arXPath, $prefixList);
- }
- return $data;
- }
-
- public function processRefNode($refNode) {
- $dataObject = NULL;
- if ($uri = $refNode->getAttribute("URI")) {
- $arUrl = parse_url($uri);
- if (empty($arUrl['path'])) {
- if ($identifier = $arUrl['fragment']) {
- $xPath = new DOMXPath($refNode->ownerDocument);
- if ($this->idNS && is_array($this->idNS)) {
- foreach ($this->idNS AS $nspf=>$ns) {
- $xPath->registerNamespace($nspf, $ns);
- }
- }
- $iDlist = '@Id="'.$identifier.'"';
- if (is_array($this->idKeys)) {
- foreach ($this->idKeys AS $idKey) {
- $iDlist .= " or @$idKey='$identifier'";
- }
- }
- $query = '//*['.$iDlist.']';
- $dataObject = $xPath->query($query)->item(0);
- } else {
- $dataObject = $refNode->ownerDocument;
- }
- } else {
- $dataObject = file_get_contents($arUrl);
- }
- } else {
- $dataObject = $refNode->ownerDocument;
- }
- $data = $this->processTransforms($refNode, $dataObject);
- return $this->validateDigest($refNode, $data);
- }
-
- public function getRefNodeID($refNode) {
- if ($uri = $refNode->getAttribute("URI")) {
- $arUrl = parse_url($uri);
- if (empty($arUrl['path'])) {
- if ($identifier = $arUrl['fragment']) {
- return $identifier;
- }
- }
- }
- return null;
- }
-
- public function getRefIDs() {
- $refids = array();
- $doc = $this->sigNode->ownerDocument;
-
- $xpath = $this->getXPathObj();
- $query = "./secdsig:SignedInfo/secdsig:Reference";
- $nodeset = $xpath->query($query, $this->sigNode);
- if ($nodeset->length == 0) {
- throw new Exception("Reference nodes not found");
- }
- foreach ($nodeset AS $refNode) {
- $refids[] = $this->getRefNodeID($refNode);
- }
- return $refids;
- }
-
- public function validateReference() {
- $doc = $this->sigNode->ownerDocument;
- if (! $doc->isSameNode($this->sigNode)) {
- $this->sigNode->parentNode->removeChild($this->sigNode);
- }
- $xpath = $this->getXPathObj();
- $query = "./secdsig:SignedInfo/secdsig:Reference";
- $nodeset = $xpath->query($query, $this->sigNode);
- if ($nodeset->length == 0) {
- throw new Exception("Reference nodes not found");
- }
- foreach ($nodeset AS $refNode) {
- if (! $this->processRefNode($refNode)) {
- throw new Exception("Reference validation failed");
- }
- }
- return TRUE;
- }
-
- private function addRefInternal($sinfoNode, $node, $algorithm, $arTransforms=NULL, $options=NULL, $id_name = 'ID') {
- $prefix = NULL;
- $prefix_ns = NULL;
-
-
- if (is_array($options)) {
- $prefix = empty($options['prefix'])?NULL:$options['prefix'];
- $prefix_ns = empty($options['prefix_ns'])?NULL:$options['prefix_ns'];
- $id_name = empty($options['id_name'])?'Id':$options['id_name'];
- }
-
- $refNode = $this->createNewSignNode('Reference');
- $sinfoNode->appendChild($refNode);
-
- if ($node instanceof DOMDocument) {
- $uri = NULL;
- } else {
-/* Do wer really need to set a prefix? */
- $uri = XMLSecurityDSig::generate_GUID();
- $refNode->setAttribute("URI", '#'.$uri);
- //$refNode->setAttribute("URI", '');
- }
-
- $transNodes = $this->createNewSignNode('Transforms');
- $refNode->appendChild($transNodes);
-
- if (is_array($arTransforms)) {
- foreach ($arTransforms AS $transform) {
- $transNode = $this->createNewSignNode('Transform');
- $transNodes->appendChild($transNode);
- $transNode->setAttribute('Algorithm', $transform);
- }
- } elseif (! empty($this->canonicalMethod)) {
- $transNode = $this->createNewSignNode('Transform');
- $transNodes->appendChild($transNode);
- $transNode->setAttribute('Algorithm', $this->canonicalMethod);
- }
-
- if (! empty($uri)) {
- $attname = $id_name;
- if (! empty($prefix)) {
- $attname = $prefix.':'.$attname;
- }
- $node->setAttributeNS($prefix_ns, $attname, $uri);
- }
-
- $canonicalData = $this->processTransforms($refNode, $node);
- $digValue = $this->calculateDigest($algorithm, $canonicalData);
-
- $digestMethod = $this->createNewSignNode('DigestMethod');
- $refNode->appendChild($digestMethod);
- $digestMethod->setAttribute('Algorithm', $algorithm);
-
- $digestValue = $this->createNewSignNode('DigestValue', $digValue);
- $refNode->appendChild($digestValue);
- }
-
- public function addReference($node, $algorithm, $arTransforms=NULL, $options=NULL, $idname = 'ID') {
- if ($xpath = $this->getXPathObj()) {
- $query = "./secdsig:SignedInfo";
- $nodeset = $xpath->query($query, $this->sigNode);
- if ($sInfo = $nodeset->item(0)) {
- $this->addRefInternal($sInfo, $node, $algorithm, $arTransforms, $options, $idname);
- }
- }
- }
-
- public function addReferenceList($arNodes, $algorithm, $arTransforms=NULL, $options=NULL, $idname = 'ID') {
- if ($xpath = $this->getXPathObj()) {
- $query = "./secdsig:SignedInfo";
- $nodeset = $xpath->query($query, $this->sigNode);
- if ($sInfo = $nodeset->item(0)) {
- foreach ($arNodes AS $node) {
- $this->addRefInternal($sInfo, $node, $algorithm, $arTransforms, $options, $idname);
- }
- }
- }
- }
-
- public function addObject($data, $mimetype=NULL, $encoding=NULL) {
- $objNode = $this->createNewSignNode('Object');
- $this->sigNode->appendChild($objNode);
- if (! empty($mimetype)) {
- $objNode->setAtribute('MimeType', $mimetype);
- }
- if (! empty($encoding)) {
- $objNode->setAttribute('Encoding', $encoding);
- }
-
- if ($data instanceof DOMElement) {
- $newData = $this->sigNode->ownerDocument->importNode($data, TRUE);
- } else {
- $newData = $this->sigNode->ownerDocument->createTextNode($data);
- }
- $objNode->appendChild($newData);
-
- return $objNode;
- }
-
- public function locateKey($node=NULL) {
- if (empty($node)) {
- $node = $this->sigNode;
- }
- if (! $node instanceof DOMNode) {
- return NULL;
- }
- if ($doc = $node->ownerDocument) {
- $xpath = new DOMXPath($doc);
- $xpath->registerNamespace('secdsig', XMLSecurityDSig::XMLDSIGNS);
- $query = "string(./secdsig:SignedInfo/secdsig:SignatureMethod/@Algorithm)";
- $algorithm = $xpath->evaluate($query, $node);
- if ($algorithm) {
- try {
- $objKey = new XMLSecurityKey($algorithm, array('type'=>'public'));
- } catch (Exception $e) {
- return NULL;
- }
- return $objKey;
- }
- }
- return NULL;
- }
-
- public function verify($objKey) {
- $doc = $this->sigNode->ownerDocument;
- $xpath = new DOMXPath($doc);
- $xpath->registerNamespace('secdsig', XMLSecurityDSig::XMLDSIGNS);
- $query = "string(./secdsig:SignatureValue)";
- $sigValue = $xpath->evaluate($query, $this->sigNode);
- if (empty($sigValue)) {
- throw new Exception("Unable to locate SignatureValue");
- }
- return $objKey->verifySignature($this->signedInfo, base64_decode($sigValue));
- }
-
- public function signData($objKey, $data) {
- return $objKey->signData($data);
- }
-
- public function sign($objKey) {
- if ($xpath = $this->getXPathObj()) {
- $query = "./secdsig:SignedInfo";
- $nodeset = $xpath->query($query, $this->sigNode);
- if ($sInfo = $nodeset->item(0)) {
- $query = "./secdsig:SignatureMethod";
- $nodeset = $xpath->query($query, $sInfo);
- $sMethod = $nodeset->item(0);
- $sMethod->setAttribute('Algorithm', $objKey->type);
- $data = $this->canonicalizeData($sInfo, $this->canonicalMethod);
- $sigValue = base64_encode($this->signData($objKey, $data));
- $sigValueNode = $this->createNewSignNode('SignatureValue', $sigValue);
- if ($infoSibling = $sInfo->nextSibling) {
- $infoSibling->parentNode->insertBefore($sigValueNode, $infoSibling);
- } else {
- $this->sigNode->appendChild($sigValueNode);
- }
- }
- }
- }
-
- public function appendCert() {
-
- }
-
- public function appendKey($objKey, $parent=NULL) {
- $objKey->serializeKey($parent);
- }
-
-
- public function appendSignatureShib($parentNode, $insertBefore = FALSE, $assertion = false) {
- $baseDoc = ($parentNode instanceof DOMDocument)?$parentNode:$parentNode->ownerDocument;
- $newSig = $baseDoc->importNode($this->sigNode, TRUE);
-
-
-
- $xnode = null;
-
- $xpath = new DOMXPath($baseDoc);
- $xpath->registerNamespace('secdsig', XMLSecurityDSig::XMLDSIGNS);
- $xpath->registerNamespace('samlp', 'urn:oasis:names:tc:SAML:1.0:protocol');
- $xpath->registerNamespace('saml', 'urn:oasis:names:tc:SAML:1.0:assertion');
-
-
- if ($insertBefore && !$assertion) {
-
- $query = "//samlp:Status";
- $nodeset = $xpath->query($query, $parentNode);
-
- $xnode = $nodeset->item(0);
- if (!$xnode)
- throw new Exception("Could not find node to sign before (Root signing mode)");
-
- $parentNode->insertBefore($newSig, $xnode);
-
- } elseif ($insertBefore) {
-
- $query = "//saml:Assertion/saml:Subject";
- $nodeset = $xpath->query($query, $parentNode);
-
- $xnode = $nodeset->item(0);
- if (!$xnode)
- throw new Exception("Could not find node to sign before (Assertion signing mode)");
-
- $parentNode->insertBefore($newSig, $xnode);
- } else {
- $parentNode->appendChild($newSig);
- }
- }
-
-
-
- public function appendSignature($parentNode, $insertBefore = false, $assertion = false) {
- $baseDoc = ($parentNode instanceof DOMDocument)?$parentNode:$parentNode->ownerDocument;
- $newSig = $baseDoc->importNode($this->sigNode, TRUE);
-
-
-
- $xnode = null;
-
- $xpath = new DOMXPath($baseDoc);
- $xpath->registerNamespace('secdsig', XMLSecurityDSig::XMLDSIGNS);
- $xpath->registerNamespace('samlp', 'urn:oasis:names:tc:SAML:2.0:protocol');
- $xpath->registerNamespace('saml', 'urn:oasis:names:tc:SAML:2.0:assertion');
-
-
- if ($insertBefore && !$assertion) {
-
- $query = "//samlp:Status";
- $nodeset = $xpath->query($query, $parentNode);
-
- $xnode = $nodeset->item(0);
- if (!$xnode)
- throw new Exception("Could not find node to sign before (Root signing mode)");
-
- $parentNode->insertBefore($newSig, $xnode);
-
- } elseif ($insertBefore) {
-
- $query = "//saml:Assertion/saml:Subject";
- $nodeset = $xpath->query($query, $parentNode);
-
- $xnode = $nodeset->item(0);
- if (!$xnode)
- throw new Exception("Could not find node to sign before (Assertion signing mode)");
-
- $parentNode->insertBefore($newSig, $xnode);
- } else {
- $parentNode->appendChild($newSig);
- }
- }
-
- static function get509XCert($cert, $isPEMFormat=TRUE) {
- if ($isPEMFormat) {
- $data = '';
- $arCert = explode("\n", $cert);
- $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) . "\n";
- }
- }
- } else {
- $data = $cert;
- }
- return $data;
- }
-
- static function staticAdd509Cert($parentRef, $cert, $isPEMFormat=TRUE, $isURL=False, $xpath=NULL) {
- if ($isURL) {
- $cert = file_get_contents($cert);
- }
- if (! $parentRef instanceof DOMElement) {
- throw new Exception('Invalid parent Node parameter');
- }
- $baseDoc = $parentRef->ownerDocument;
-
- if (empty($xpath)) {
- $xpath = new DOMXPath($parentRef->ownerDocument);
- $xpath->registerNamespace('secdsig', XMLSecurityDSig::XMLDSIGNS);
- }
- $data = XMLSecurityDSig::get509XCert($cert, $isPEMFormat);
-
- $query = "./secdsig:KeyInfo";
- $nodeset = $xpath->query($query, $parentRef);
- $keyInfo = $nodeset->item(0);
- if (! $keyInfo) {
- $inserted = FALSE;
- $keyInfo = $baseDoc->createElementNS(XMLSecurityDSig::XMLDSIGNS, 'ds:KeyInfo');
-
- $query = "./secdsig:Object";
- $nodeset = $xpath->query($query, $parentRef);
- if ($sObject = $nodeset->item(0)) {
- $sObject->parentNode->insertBefore($keyInfo, $sObject);
- $inserted = TRUE;
- }
-
- if (! $inserted) {
- $parentRef->appendChild($keyInfo);
- }
- }
- $x509DataNode = $baseDoc->createElementNS(XMLSecurityDSig::XMLDSIGNS, 'ds:X509Data');
- $keyInfo->appendChild($x509DataNode);
- $x509CertNode = $baseDoc->createElementNS(XMLSecurityDSig::XMLDSIGNS, 'ds:X509Certificate', $data);
- $x509DataNode->appendChild($x509CertNode);
-
- }
-
- public function add509Cert($cert, $isPEMFormat=TRUE, $isURL=False) {
- if ($xpath = $this->getXPathObj()) {
- self::staticAdd509Cert($this->sigNode, $cert, $isPEMFormat, $isURL, $xpath);
- }
- }
-}
-
-class XMLSecEnc {
- const template = "<xenc:EncryptedData xmlns:xenc='http://www.w3.org/2001/04/xmlenc#'>
- <xenc:CipherData>
- <xenc:CipherValue></xenc:CipherValue>
- </xenc:CipherData>
-</xenc:EncryptedData>";
-
- const Element = 'http://www.w3.org/2001/04/xmlenc#Element';
- const Content = 'http://www.w3.org/2001/04/xmlenc#Content';
- const URI = 3;
- const XMLENCNS = 'http://www.w3.org/2001/04/xmlenc#';
-
- private $encdoc = NULL;
- private $rawNode = NULL;
- public $type = NULL;
- public $encKey = NULL;
-
- public function __construct() {
- $this->encdoc = new DOMDocument();
- $this->encdoc->loadXML(XMLSecEnc::template);
- }
-
- public function setNode($node) {
- $this->rawNode = $node;
- }
-
- public function encryptNode($objKey, $replace=TRUE) {
- $data = '';
- if (empty($this->rawNode)) {
- throw new Exception('Node to encrypt has not been set');
- }
- if (! $objKey instanceof XMLSecurityKey) {
- throw new Exception('Invalid Key');
- }
- $doc = $this->rawNode->ownerDocument;
- $xPath = new DOMXPath($this->encdoc);
- $objList = $xPath->query('/xenc:EncryptedData/xenc:CipherData/xenc:CipherValue');
- $cipherValue = $objList->item(0);
- if ($cipherValue == NULL) {
- throw new Exception('Error locating CipherValue element within template');
- }
- switch ($this->type) {
- case (XMLSecEnc::Element):
- $data = $doc->saveXML($this->rawNode);
- $this->encdoc->documentElement->setAttribute('Type', XMLSecEnc::Element);
- break;
- case (XMLSecEnc::Content):
- $children = $this->rawNode->childNodes;
- foreach ($children AS $child) {
- $data .= $doc->saveXML($child);
- }
- $this->encdoc->documentElement->setAttribute('Type', XMLSecEnc::Content);
- break;
- default:
- throw new Exception('Type is currently not supported');
- return;
- }
-
- $encMethod = $this->encdoc->documentElement->appendChild($this->encdoc->createElementNS(XMLSecEnc::XMLENCNS, 'xenc:EncryptionMethod'));
- $encMethod->setAttribute('Algorithm', $objKey->getAlgorith());
- $cipherValue->parentNode->parentNode->insertBefore($encMethod, $cipherValue->parentNode);
-
- $strEncrypt = base64_encode($objKey->encryptData($data));
- $value = $this->encdoc->createTextNode($strEncrypt);
- $cipherValue->appendChild($value);
-
- if ($replace) {
- switch ($this->type) {
- case (XMLSecEnc::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;
- break;
- case (XMLSecEnc::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;
- break;
- }
- }
- }
-
- public function decryptNode($objKey, $replace=TRUE) {
- $data = '';
- if (empty($this->rawNode)) {
- throw new Exception('Node to decrypt has not been set');
- }
- if (! $objKey instanceof XMLSecurityKey) {
- throw new Exception('Invalid Key');
- }
- $doc = $this->rawNode->ownerDocument;
- $xPath = new DOMXPath($doc);
- $xPath->registerNamespace('xmlencr', XMLSecEnc::XMLENCNS);
- /* Only handles embedded content right now and not a reference */
- $query = "./xmlencr:CipherData/xmlencr:CipherValue";
- $nodeset = $xPath->query($query, $this->rawNode);
-
- if ($node = $nodeset->item(0)) {
- $encryptedData = base64_decode($node->nodeValue);
- $decrypted = $objKey->decryptData($encryptedData);
- if ($replace) {
- switch ($this->type) {
- case (XMLSecEnc::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;
- break;
- case (XMLSecEnc::Content):
- if ($this->rawNode->nodeType == XML_DOCUMENT_NODE) {
- $doc = $this->rawNode;
- } else {
- $doc = $this->rawNode->ownerDocument;
- }
- $newFrag = $doc->createDOMDocumentFragment();
- $newFrag->appendXML($decrypted);
- $this->rawNode->parentNode->replaceChild($newFrag, $this->rawNode);
- return $this->rawNode->parentNode;
- break;
- default:
- return $decrypted;
- }
- } else {
- return $decrypted;
- }
- } else {
- throw new Exception("Cannot locate encrypted data");
- }
- }
-
- public function encryptKey($srcKey, $rawKey, $append=TRUE) {
- if ((! $srcKey instanceof XMLSecurityKey) || (! $rawKey instanceof XMLSecurityKey)) {
- throw new Exception('Invalid Key');
- }
- $strEncKey = base64_encode($srcKey->encryptData($rawKey->key));
- $root = $this->encdoc->documentElement;
- $encKey = $this->encdoc->createElementNS(XMLSecEnc::XMLENCNS, 'xenc:EncryptedKey');
- if ($append) {
- $keyInfo = $root->appendChild($this->encdoc->createElementNS('http://www.w3.org/2000/09/xmldsig#', 'dsig:KeyInfo'));
- $keyInfo->appendChild($encKey);
- } else {
- $this->encKey = $encKey;
- }
- $encMethod = $encKey->appendChild($this->encdoc->createElementNS(XMLSecEnc::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(XMLSecEnc::XMLENCNS, 'xenc:CipherData'));
- $cipherData->appendChild($this->encdoc->createElementNS(XMLSecEnc::XMLENCNS, 'xenc:CipherValue', $strEncKey));
- return;
- }
-
- 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);
- }
-
- 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()='".XMLSecEnc::XMLENCNS."']";
- $nodeset = $xpath->query($query);
- return $nodeset->item(0);
- }
- return NULL;
- }
-
- 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', XMLSecEnc::XMLENCNS);
- $query = ".//xmlsecenc:EncryptionMethod";
- $nodeset = $xpath->query($query, $node);
- if ($encmeth = $nodeset->item(0)) {
- $attrAlgorithm = $encmeth->getAttribute("Algorithm");
- try {
- $objKey = new XMLSecurityKey($attrAlgorithm, array('type'=>'private'));
- } catch (Exception $e) {
- return NULL;
- }
- return $objKey;
- }
- }
- return NULL;
- }
-
- static function staticLocateKeyInfo($objBaseKey=NULL, $node=NULL) {
- if (empty($node) || (! $node instanceof DOMNode)) {
- return NULL;
- }
- if ($doc = $node->ownerDocument) {
- $xpath = new DOMXPath($doc);
- $xpath->registerNamespace('xmlsecenc', XMLSecEnc::XMLENCNS);
- $xpath->registerNamespace('xmlsecdsig', XMLSecurityDSig::XMLDSIGNS);
- $query = "./xmlsecdsig:KeyInfo";
- $nodeset = $xpath->query($query, $node);
- if ($encmeth = $nodeset->item(0)) {
- 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");
- break;
- case 'RSAKeyValue':
- $modulus = NULL;
- $exponent = NULL;
- if ($modulusNode = $keyval->getElementsByTagName('Modulus')->item(0)) {
- $modulus = base64_decode($modulusNode->nodeValue);
- }
- if ($exponentNode = $keyval->getElementsByTagName('Exponent')->item(0)) {
- $exponent = base64_decode($exponentNode->nodeValue);
- }
- if (empty($modulus) || empty($exponent)) {
- throw new Exception("Missing Modulus or Exponent");
- }
- $publicKey = XMLSecurityKey::convertRSA($modulus, $exponent);
- $objBaseKey->loadKey($publicKey);
- break;
- }
- }
- break;
- case 'RetrievalMethod':
- /* Not currently supported */
- break;
- case 'EncryptedKey':
- $objenc = new XMLSecEnc();
- $objenc->setNode($child);
- if (! $objKey = $objenc->locateKey()) {
- throw new Exception("Unable to locate algorithm for this Encrypted Key");
- }
- $objKey->isEncrypted = TRUE;
- $objKey->encryptedCtx = $objenc;
- XMLSecEnc::staticLocateKeyInfo($objKey, $child);
- return $objKey;
- break;
- 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);
- }
- }
- break;
- }
- }
- }
- return $objBaseKey;
- }
- return NULL;
- }
-
- public function locateKeyInfo($objBaseKey=NULL, $node=NULL) {
- if (empty($node)) {
- $node = $this->rawNode;
- }
- return XMLSecEnc::staticLocateKeyInfo($objBaseKey, $node);
- }
-}
+<?php
+/**
+ * xmlseclibs.php
+ *
+ * Copyright (c) 2007, 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 Robert Richards <rrichards@cdatazone.org>
+ * @license http://www.opensource.org/licenses/bsd-license.php BSD License
+ * @version 1.2.0
+ */
+
+/*
+Functions to generate simple cases of Exclusive Canonical XML - Callable function is C14NGeneral()
+i.e.: $canonical = C14NGeneral($domelement, TRUE);
+*/
+
+/* helper function */
+function sortAndAddAttrs($element, $arAtts) {
+ $newAtts = array();
+ foreach ($arAtts AS $attnode) {
+ $newAtts[$attnode->nodeName] = $attnode;
+ }
+ ksort($newAtts);
+ foreach ($newAtts as $attnode) {
+ $element->setAttribute($attnode->nodeName, $attnode->nodeValue);
+ }
+}
+
+/* helper function */
+function canonical($tree, $element, $withcomments) {
+ if ($tree->nodeType != XML_DOCUMENT_NODE) {
+ $dom = $tree->ownerDocument;
+ } else {
+ $dom = $tree;
+ }
+ if ($element->nodeType != XML_ELEMENT_NODE) {
+ if ($element->nodeType == XML_COMMENT_NODE && ! $withcomments) {
+ return;
+ }
+ $tree->appendChild($dom->importNode($element, TRUE));
+ return;
+ }
+ $arNS = array();
+ if ($element->namespaceURI != "") {
+ if ($element->prefix == "") {
+ $elCopy = $dom->createElementNS($element->namespaceURI, $element->nodeName);
+ } else {
+ $prefix = $tree->lookupPrefix($element->namespaceURI);
+ if ($prefix == $element->prefix) {
+ $elCopy = $dom->createElementNS($element->namespaceURI, $element->nodeName);
+ } else {
+ $elCopy = $dom->createElement($element->nodeName);
+ $arNS[$element->namespaceURI] = $element->prefix;
+ }
+ }
+ } else {
+ $elCopy = $dom->createElement($element->nodeName);
+ }
+ $tree->appendChild($elCopy);
+
+ /* Create DOMXPath based on original document */
+ $xPath = new DOMXPath($element->ownerDocument);
+
+ /* Get namespaced attributes */
+ $arAtts = $xPath->query('attribute::*[namespace-uri(.) != ""]', $element);
+
+ /* Create an array with namespace URIs as keys, and sort them */
+ foreach ($arAtts AS $attnode) {
+ if (array_key_exists($attnode->namespaceURI, $arNS) &&
+ ($arNS[$attnode->namespaceURI] == $attnode->prefix)) {
+ continue;
+ }
+ $prefix = $tree->lookupPrefix($attnode->namespaceURI);
+ if ($prefix != $attnode->prefix) {
+ $arNS[$attnode->namespaceURI] = $attnode->prefix;
+ } else {
+ $arNS[$attnode->namespaceURI] = NULL;
+ }
+ }
+ if (count($arNS) > 0) {
+ asort($arNS);
+ }
+
+ /* Add namespace nodes */
+ foreach ($arNS AS $namespaceURI=>$prefix) {
+ if ($prefix != NULL) {
+ $elCopy->setAttributeNS("http://www.w3.org/2000/xmlns/",
+ "xmlns:".$prefix, $namespaceURI);
+ }
+ }
+ if (count($arNS) > 0) {
+ ksort($arNS);
+ }
+
+ /* Get attributes not in a namespace, and then sort and add them */
+ $arAtts = $xPath->query('attribute::*[namespace-uri(.) = ""]', $element);
+ sortAndAddAttrs($elCopy, $arAtts);
+
+ /* Loop through the URIs, and then sort and add attributes within that namespace */
+ foreach ($arNS as $nsURI=>$prefix) {
+ $arAtts = $xPath->query('attribute::*[namespace-uri(.) = "'.$nsURI.'"]', $element);
+ sortAndAddAttrs($elCopy, $arAtts);
+ }
+
+ foreach ($element->childNodes AS $node) {
+ canonical($elCopy, $node, $withcomments);
+ }
+}
+
+/*
+$element - DOMElement for which to produce the canonical version of
+$exclusive - boolean to indicate exclusive canonicalization (must pass TRUE)
+$withcomments - boolean indicating wether or not to include comments in canonicalized form
+*/
+function C14NGeneral($element, $exclusive=FALSE, $withcomments=FALSE) {
+ /* IF PHP 5.2+ then use built in canonical functionality */
+ $php_version = explode('.', PHP_VERSION);
+ if (($php_version[0] > 5) || ($php_version[0] == 5 && $php_version[1] >= 2) ) {
+ return $element->C14N($exclusive, $withcomments);
+ }
+
+ /* Must be element */
+ if (! $element instanceof DOMElement) {
+ return NULL;
+ }
+ /* Currently only exclusive XML is supported */
+ if ($exclusive == FALSE) {
+ throw new Exception("Only exclusive canonicalization is supported in this version of PHP");
+ }
+
+ $copyDoc = new DOMDocument();
+ canonical($copyDoc, $element, $withcomments);
+ return $copyDoc->saveXML($copyDoc->documentElement, LIBXML_NOEMPTYTAG);
+}
+
+class XMLSecurityKey {
+ const TRIPLEDES_CBC = 'http://www.w3.org/2001/04/xmlenc#tripledes-cbc';
+ const AES128_CBC = 'http://www.w3.org/2001/04/xmlenc#aes128-cbc';
+ const AES192_CBC = 'http://www.w3.org/2001/04/xmlenc#aes192-cbc';
+ const AES256_CBC = 'http://www.w3.org/2001/04/xmlenc#aes256-cbc';
+ const RSA_1_5 = 'http://www.w3.org/2001/04/xmlenc#rsa-1_5';
+ const RSA_OAEP_MGF1P = 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p';
+ const RSA_SHA1 = 'http://www.w3.org/2000/09/xmldsig#rsa-sha1';
+ const DSA_SHA1 = 'http://www.w3.org/2000/09/xmldsig#dsa-sha1';
+
+ private $cryptParams = array();
+ public $type = 0;
+ public $key = NULL;
+ public $passphrase = "";
+ public $iv = NULL;
+ public $name = NULL;
+ public $keyChain = NULL;
+ public $isEncrypted = FALSE;
+ public $encryptedCtx = NULL;
+ public $guid = NULL;
+
+ public function __construct($type, $params=NULL) {
+ srand();
+ switch ($type) {
+ case (XMLSecurityKey::TRIPLEDES_CBC):
+ $this->cryptParams['library'] = 'mcrypt';
+ $this->cryptParams['cipher'] = MCRYPT_TRIPLEDES;
+ $this->cryptParams['mode'] = MCRYPT_MODE_CBC;
+ $this->cryptParams['method'] = 'http://www.w3.org/2001/04/xmlenc#tripledes-cbc';
+ break;
+ case (XMLSecurityKey::AES128_CBC):
+ $this->cryptParams['library'] = 'mcrypt';
+ $this->cryptParams['cipher'] = MCRYPT_RIJNDAEL_128;
+ $this->cryptParams['mode'] = MCRYPT_MODE_CBC;
+ $this->cryptParams['method'] = 'http://www.w3.org/2001/04/xmlenc#aes128-cbc';
+ break;
+ case (XMLSecurityKey::AES192_CBC):
+ $this->cryptParams['library'] = 'mcrypt';
+ $this->cryptParams['cipher'] = MCRYPT_RIJNDAEL_128;
+ $this->cryptParams['mode'] = MCRYPT_MODE_CBC;
+ $this->cryptParams['method'] = 'http://www.w3.org/2001/04/xmlenc#aes192-cbc';
+ break;
+ case (XMLSecurityKey::AES256_CBC):
+ $this->cryptParams['library'] = 'mcrypt';
+ $this->cryptParams['cipher'] = MCRYPT_RIJNDAEL_128;
+ $this->cryptParams['mode'] = MCRYPT_MODE_CBC;
+ $this->cryptParams['method'] = 'http://www.w3.org/2001/04/xmlenc#aes256-cbc';
+ break;
+ case (XMLSecurityKey::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');
+ return;
+ case (XMLSecurityKey::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');
+ return;
+ case (XMLSecurityKey::RSA_SHA1):
+ $this->cryptParams['library'] = 'openssl';
+ $this->cryptParams['method'] = 'http://www.w3.org/2000/09/xmldsig#rsa-sha1';
+ 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');
+ break;
+ default:
+ throw new Exception('Invalid Key Type');
+ return;
+ }
+ $this->type = $type;
+ }
+
+ public function generateSessionKey() {
+ $key = '';
+ if (! empty($this->cryptParams['cipher']) && ! empty($this->cryptParams['mode'])) {
+ $keysize = mcrypt_module_get_algo_key_size($this->cryptParams['cipher']);
+ /* Generating random key using iv generation routines */
+ if (($keysize > 0) && ($td = mcrypt_module_open(MCRYPT_RIJNDAEL_256, '',$this->cryptParams['mode'], ''))) {
+ if ($this->cryptParams['cipher'] == MCRYPT_RIJNDAEL_128) {
+ $keysize = 16;
+ if ($this->type == XMLSecurityKey::AES256_CBC) {
+ $keysize = 32;
+ } elseif ($this->type == XMLSecurityKey::AES192_CBC) {
+ $keysize = 24;
+ }
+ }
+ while (strlen($key) < $keysize) {
+ $key .= mcrypt_create_iv(mcrypt_enc_get_iv_size ($td),MCRYPT_RAND);
+ }
+ mcrypt_module_close($td);
+ $key = substr($key, 0, $keysize);
+ $this->key = $key;
+ }
+ }
+ return $key;
+ }
+
+ 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->key = $str_cert;
+ }
+ if ($this->cryptParams['library'] == 'openssl') {
+ if ($this->cryptParams['type'] == 'public') {
+ $this->key = openssl_get_publickey($this->key);
+ } else {
+ $this->key = openssl_get_privatekey($this->key, $this->passphrase);
+ }
+ } else if ($this->cryptParams['cipher'] == MCRYPT_RIJNDAEL_128) {
+ /* Check key length */
+ switch ($this->type) {
+ case (XMLSecurityKey::AES256_CBC):
+ if (strlen($this->key) < 25) {
+ throw new Exception('Key must contain at least 25 characters for this cipher');
+ }
+ break;
+ case (XMLSecurityKey::AES192_CBC):
+ if (strlen($this->key) < 17) {
+ throw new Exception('Key must contain at least 17 characters for this cipher');
+ }
+ break;
+ }
+ }
+ }
+
+ private function encryptMcrypt($data) {
+ $td = mcrypt_module_open($this->cryptParams['cipher'], '', $this->cryptParams['mode'], '');
+ $this->iv = mcrypt_create_iv (mcrypt_enc_get_iv_size($td), MCRYPT_RAND);
+ mcrypt_generic_init($td, $this->key, $this->iv);
+ if ($this->cryptParams['mode'] == MCRYPT_MODE_CBC) {
+ $bs = mcrypt_enc_get_block_size($td);
+ for ($datalen0=$datalen=strlen($data); (($datalen%$bs)!=($bs-1)); $datalen++)
+ $data.=chr(rand(1, 127));
+ $data.=chr($datalen-$datalen0+1);
+ }
+ $encrypted_data = $this->iv.mcrypt_generic($td, $data);
+ mcrypt_generic_deinit($td);
+ mcrypt_module_close($td);
+ return $encrypted_data;
+ }
+
+ private function decryptMcrypt($data) {
+ $td = mcrypt_module_open($this->cryptParams['cipher'], '', $this->cryptParams['mode'], '');
+ $iv_length = mcrypt_enc_get_iv_size($td);
+
+ $this->iv = substr($data, 0, $iv_length);
+ $data = substr($data, $iv_length);
+
+ mcrypt_generic_init($td, $this->key, $this->iv);
+ $decrypted_data = mdecrypt_generic($td, $data);
+ mcrypt_generic_deinit($td);
+ mcrypt_module_close($td);
+ if ($this->cryptParams['mode'] == MCRYPT_MODE_CBC) {
+ $dataLen = strlen($decrypted_data);
+ $paddingLength = substr($decrypted_data, $dataLen - 1, 1);
+ $decrypted_data = substr($decrypted_data, 0, $dataLen - ord($paddingLength));
+ }
+ return $decrypted_data;
+ }
+
+ private function encryptOpenSSL($data) {
+ if ($this->cryptParams['type'] == 'public') {
+ if (! openssl_public_encrypt($data, $encrypted_data, $this->key, $this->cryptParams['padding'])) {
+ throw new Exception('Failure encrypting Data');
+ return;
+ }
+ } else {
+ if (! openssl_private_encrypt($data, $encrypted_data, $this->key, $this->cryptParams['padding'])) {
+ throw new Exception('Failure encrypting Data');
+ return;
+ }
+ }
+ return $encrypted_data;
+ }
+
+ private function decryptOpenSSL($data) {
+ if ($this->cryptParams['type'] == 'public') {
+ if (! openssl_public_decrypt($data, $decrypted, $this->key, $this->cryptParams['padding'])) {
+ throw new Exception('Failure decrypting Data');
+ return;
+ }
+ } else {
+ if (! openssl_private_decrypt($data, $decrypted, $this->key, $this->cryptParams['padding'])) {
+ throw new Exception('Failure decrypting Data');
+ return;
+ }
+ }
+ return $decrypted;
+ }
+
+ private function signOpenSSL($data) {
+ if (! openssl_sign ($data, $signature, $this->key)) {
+ throw new Exception('Failure Signing Data');
+ return;
+ }
+ return $signature;
+ }
+
+ private function verifyOpenSSL($data, $signature) {
+ return openssl_verify ($data, $signature, $this->key);
+ }
+
+ public function encryptData($data) {
+ switch ($this->cryptParams['library']) {
+ case 'mcrypt':
+ return $this->encryptMcrypt($data);
+ break;
+ case 'openssl':
+ return $this->encryptOpenSSL($data);
+ break;
+ }
+ }
+
+ public function decryptData($data) {
+ switch ($this->cryptParams['library']) {
+ case 'mcrypt':
+ return $this->decryptMcrypt($data);
+ break;
+ case 'openssl':
+ return $this->decryptOpenSSL($data);
+ break;
+ }
+ }
+
+ public function signData($data) {
+ switch ($this->cryptParams['library']) {
+ case 'openssl':
+ return $this->signOpenSSL($data);
+ break;
+ }
+ }
+
+ public function verifySignature($data, $signature) {
+ switch ($this->cryptParams['library']) {
+ case 'openssl':
+ return $this->verifyOpenSSL($data, $signature);
+ break;
+ }
+ }
+
+ public function getAlgorith() {
+ return $this->cryptParams['method'];
+ }
+
+ 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);
+ }
+
+ /* Modulus and Exponent must already be base64 decoded */
+ static function convertRSA($modulus, $exponent) {
+ /* make an ASN publicKeyInfo */
+ $exponentEncoding = XMLSecurityKey::makeAsnSegment(0x02, $exponent);
+ $modulusEncoding = XMLSecurityKey::makeAsnSegment(0x02, $modulus);
+ $sequenceEncoding = XMLSecurityKey:: makeAsnSegment(0x30, $modulusEncoding.$exponentEncoding);
+ $bitstringEncoding = XMLSecurityKey::makeAsnSegment(0x03, $sequenceEncoding);
+ $rsaAlgorithmIdentifier = pack("H*", "300D06092A864886F70D0101010500");
+ $publicKeyInfo = XMLSecurityKey::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";
+ }
+
+ public function serializeKey($parent) {
+
+ }
+}
+
+class XMLSecurityDSig {
+ const XMLDSIGNS = 'http://www.w3.org/2000/09/xmldsig#';
+ const SHA1 = 'http://www.w3.org/2000/09/xmldsig#sha1';
+ const SHA256 = 'http://www.w3.org/2001/04/xmlenc#sha256';
+ const SHA512 = 'http://www.w3.org/2001/04/xmlenc#sha512';
+ const RIPEMD160 = 'http://www.w3.org/2001/04/xmlenc#ripemd160';
+
+ const C14N = 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315';
+ const C14N_COMMENTS = 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments';
+ const EXC_C14N = 'http://www.w3.org/2001/10/xml-exc-c14n#';
+ const EXC_C14N_COMMENTS = 'http://www.w3.org/2001/10/xml-exc-c14n#WithComments';
+
+ const template = '<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
+ <ds:SignedInfo>
+ <ds:SignatureMethod />
+ </ds:SignedInfo>
+</ds:Signature>';
+
+ public $sigNode = NULL;
+ public $idKeys = array();
+ public $idNS = array();
+ private $signedInfo = NULL;
+ private $xPathCtx = NULL;
+ private $canonicalMethod = NULL;
+ private $prefix = 'ds';
+ private $searchpfx = 'secdsig';
+
+ public function __construct() {
+ $sigdoc = new DOMDocument();
+ $sigdoc->loadXML(XMLSecurityDSig::template);
+ $this->sigNode = $sigdoc->documentElement;
+ }
+
+ private function getXPathObj() {
+ if (empty($this->xPathCtx) && ! empty($this->sigNode)) {
+ $xpath = new DOMXPath($this->sigNode->ownerDocument);
+ $xpath->registerNamespace('secdsig', XMLSecurityDSig::XMLDSIGNS);
+ $this->xPathCtx = $xpath;
+ }
+ return $this->xPathCtx;
+ }
+
+ static function generate_GUID($prefix='pfx') {
+ $uuid = md5(uniqid(rand(), true));
+ $guid = $prefix.substr($uuid,0,8)."-".
+ substr($uuid,8,4)."-".
+ substr($uuid,12,4)."-".
+ substr($uuid,16,4)."-".
+ substr($uuid,20,12);
+ return $guid;
+ }
+
+ public function locateSignature($objDoc) {
+ if ($objDoc instanceof DOMDocument) {
+ $doc = $objDoc;
+ } else {
+ $doc = $objDoc->ownerDocument;
+ }
+ if ($doc) {
+ $xpath = new DOMXPath($doc);
+ $xpath->registerNamespace('secdsig', XMLSecurityDSig::XMLDSIGNS);
+ $query = ".//secdsig:Signature";
+ $nodeset = $xpath->query($query, $objDoc);
+ $this->sigNode = $nodeset->item(0);
+ return $this->sigNode;
+ }
+ return NULL;
+ }
+
+ public function createNewSignNode($name, $value=NULL) {
+ $doc = $this->sigNode->ownerDocument;
+ if (! is_null($value)) {
+ $node = $doc->createElementNS(XMLSecurityDSig::XMLDSIGNS, $this->prefix.':'.$name, $value);
+ } else {
+ $node = $doc->createElementNS(XMLSecurityDSig::XMLDSIGNS, $this->prefix.':'.$name);
+ }
+ return $node;
+ }
+
+ public function setCanonicalMethod($method) {
+ switch ($method) {
+ case 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315':
+ case 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments':
+ case 'http://www.w3.org/2001/10/xml-exc-c14n#':
+ case 'http://www.w3.org/2001/10/xml-exc-c14n#WithComments':
+ $this->canonicalMethod = $method;
+ break;
+ default:
+ throw new Exception('Invalid Canonical Method');
+ }
+ if ($xpath = $this->getXPathObj()) {
+ $query = './'.$this->searchpfx.':SignedInfo';
+ $nodeset = $xpath->query($query, $this->sigNode);
+ if ($sinfo = $nodeset->item(0)) {
+ $query = './'.$this->searchpfx.'CanonicalizationMethod';
+ $nodeset = $xpath->query($query, $sinfo);
+ if (! ($canonNode = $nodeset->item(0))) {
+ $canonNode = $this->createNewSignNode('CanonicalizationMethod');
+ $sinfo->insertBefore($canonNode, $sinfo->firstChild);
+ }
+ $canonNode->setAttribute('Algorithm', $this->canonicalMethod);
+ }
+ }
+ }
+
+ private function canonicalizeData($node, $canonicalmethod, $arXPath=NULL, $prefixList=NULL) {
+ $exclusive = FALSE;
+ $withComments = FALSE;
+ switch ($canonicalmethod) {
+ case 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315':
+ $exclusive = FALSE;
+ $withComments = FALSE;
+ break;
+ case 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments':
+ $withComments = TRUE;
+ break;
+ case 'http://www.w3.org/2001/10/xml-exc-c14n#':
+ $exclusive = TRUE;
+ break;
+ case 'http://www.w3.org/2001/10/xml-exc-c14n#WithComments':
+ $exclusive = TRUE;
+ $withComments = TRUE;
+ break;
+ }
+/* Support PHP versions < 5.2 not containing C14N methods in DOM extension */
+ $php_version = explode('.', PHP_VERSION);
+ if (($php_version[0] < 5) || ($php_version[0] == 5 && $php_version[1] < 2) ) {
+ if (! is_null($arXPath)) {
+ throw new Exception("PHP 5.2.0 or higher is required to perform XPath Transformations");
+ }
+ return C14NGeneral($node, $exclusive, $withComments);
+ }
+ return $node->C14N($exclusive, $withComments, $arXPath, $prefixList);
+ }
+
+ public function canonicalizeSignedInfo() {
+
+ $doc = $this->sigNode->ownerDocument;
+ $canonicalmethod = NULL;
+ if ($doc) {
+ $xpath = $this->getXPathObj();
+ $query = "./secdsig:SignedInfo";
+ $nodeset = $xpath->query($query, $this->sigNode);
+ if ($signInfoNode = $nodeset->item(0)) {
+ $query = "./secdsig:CanonicalizationMethod";
+ $nodeset = $xpath->query($query, $signInfoNode);
+ if ($canonNode = $nodeset->item(0)) {
+ $canonicalmethod = $canonNode->getAttribute('Algorithm');
+ }
+ $this->signedInfo = $this->canonicalizeData($signInfoNode, $canonicalmethod);
+ return $this->signedInfo;
+ }
+ }
+ return NULL;
+ }
+
+ public function calculateDigest ($digestAlgorithm, $data) {
+ switch ($digestAlgorithm) {
+ case XMLSecurityDSig::SHA1:
+ $alg = 'sha1';
+ break;
+ case XMLSecurityDSig::SHA256:
+ $alg = 'sha256';
+ break;
+ case XMLSecurityDSig::SHA512:
+ $alg = 'sha512';
+ break;
+ case XMLSecurityDSig::RIPEMD160:
+ $alg = 'ripemd160';
+ break;
+ default:
+ throw new Exception("Cannot validate digest: Unsupported Algorith <$digestAlgorithm>");
+ }
+ return base64_encode(hash($alg, $data, TRUE));
+ }
+
+ public function validateDigest($refNode, $data) {
+ $xpath = new DOMXPath($refNode->ownerDocument);
+ $xpath->registerNamespace('secdsig', XMLSecurityDSig::XMLDSIGNS);
+ $query = 'string(./secdsig:DigestMethod/@Algorithm)';
+ $digestAlgorithm = $xpath->evaluate($query, $refNode);
+ $digValue = $this->calculateDigest($digestAlgorithm, $data);
+ $query = 'string(./secdsig:DigestValue)';
+ $digestValue = $xpath->evaluate($query, $refNode);
+ return ($digValue == $digestValue);
+ }
+
+ public function processTransforms($refNode, $objData) {
+ $data = $objData;
+ $xpath = new DOMXPath($refNode->ownerDocument);
+ $xpath->registerNamespace('secdsig', XMLSecurityDSig::XMLDSIGNS);
+ $query = './secdsig:Transforms/secdsig:Transform';
+ $nodelist = $xpath->query($query, $refNode);
+ $canonicalMethod = 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315';
+ $arXPath = NULL;
+ $prefixList = NULL;
+ foreach ($nodelist AS $transform) {
+ $algorithm = $transform->getAttribute("Algorithm");
+ switch ($algorithm) {
+ case 'http://www.w3.org/2001/10/xml-exc-c14n#':
+ case 'http://www.w3.org/2001/10/xml-exc-c14n#WithComments':
+ $node = $transform->firstChild;
+ while ($node) {
+ if ($node->localName == 'InclusiveNamespaces') {
+ if ($pfx = $node->getAttribute('PrefixList')) {
+ $arpfx = array();
+ $pfxlist = split(" ", $pfx);
+ foreach ($pfxlist AS $pfx) {
+ $val = trim($pfx);
+ if (! empty($val)) {
+ $arpfx[] = $val;
+ }
+ }
+ if (count($arpfx) > 0) {
+ $prefixList = $arpfx;
+ }
+ }
+ break;
+ }
+ $node = $node->nextSibling;
+ }
+ case 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315':
+ case 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments':
+ $canonicalMethod = $algorithm;
+ break;
+ case 'http://www.w3.org/TR/1999/REC-xpath-19991116':
+ $node = $transform->firstChild;
+ while ($node) {
+ if ($node->localName == 'XPath') {
+ $arXPath = array();
+ $arXPath['query'] = '(.//. | .//@* | .//namespace::*)['.$node->nodeValue.']';
+ $arXpath['namespaces'] = array();
+ $nslist = $xpath->query('./namespace::*', $node);
+ foreach ($nslist AS $nsnode) {
+ if ($nsnode->localName != "xml") {
+ $arXPath['namespaces'][$nsnode->localName] = $nsnode->nodeValue;
+ }
+ }
+ break;
+ }
+ $node = $node->nextSibling;
+ }
+ break;
+ }
+ }
+ if ($data instanceof DOMNode) {
+ $data = $this->canonicalizeData($objData, $canonicalMethod, $arXPath, $prefixList);
+ }
+ return $data;
+ }
+
+ public function processRefNode($refNode) {
+ $dataObject = NULL;
+ if ($uri = $refNode->getAttribute("URI")) {
+ $arUrl = parse_url($uri);
+ if (empty($arUrl['path'])) {
+ if ($identifier = $arUrl['fragment']) {
+ $xPath = new DOMXPath($refNode->ownerDocument);
+ if ($this->idNS && is_array($this->idNS)) {
+ foreach ($this->idNS AS $nspf=>$ns) {
+ $xPath->registerNamespace($nspf, $ns);
+ }
+ }
+ $iDlist = '@Id="'.$identifier.'"';
+ if (is_array($this->idKeys)) {
+ foreach ($this->idKeys AS $idKey) {
+ $iDlist .= " or @$idKey='$identifier'";
+ }
+ }
+ $query = '//*['.$iDlist.']';
+ $dataObject = $xPath->query($query)->item(0);
+ } else {
+ $dataObject = $refNode->ownerDocument;
+ }
+ } else {
+ $dataObject = file_get_contents($arUrl);
+ }
+ } else {
+ $dataObject = $refNode->ownerDocument;
+ }
+ $data = $this->processTransforms($refNode, $dataObject);
+ return $this->validateDigest($refNode, $data);
+ }
+
+ public function getRefNodeID($refNode) {
+ if ($uri = $refNode->getAttribute("URI")) {
+ $arUrl = parse_url($uri);
+ if (empty($arUrl['path'])) {
+ if ($identifier = $arUrl['fragment']) {
+ return $identifier;
+ }
+ }
+ }
+ return null;
+ }
+
+ public function getRefIDs() {
+ $refids = array();
+ $doc = $this->sigNode->ownerDocument;
+
+ $xpath = $this->getXPathObj();
+ $query = "./secdsig:SignedInfo/secdsig:Reference";
+ $nodeset = $xpath->query($query, $this->sigNode);
+ if ($nodeset->length == 0) {
+ throw new Exception("Reference nodes not found");
+ }
+ foreach ($nodeset AS $refNode) {
+ $refids[] = $this->getRefNodeID($refNode);
+ }
+ return $refids;
+ }
+
+ public function validateReference() {
+ $doc = $this->sigNode->ownerDocument;
+ if (! $doc->isSameNode($this->sigNode)) {
+ $this->sigNode->parentNode->removeChild($this->sigNode);
+ }
+ $xpath = $this->getXPathObj();
+ $query = "./secdsig:SignedInfo/secdsig:Reference";
+ $nodeset = $xpath->query($query, $this->sigNode);
+ if ($nodeset->length == 0) {
+ throw new Exception("Reference nodes not found");
+ }
+ foreach ($nodeset AS $refNode) {
+ if (! $this->processRefNode($refNode)) {
+ throw new Exception("Reference validation failed");
+ }
+ }
+ return TRUE;
+ }
+
+ private function addRefInternal($sinfoNode, $node, $algorithm, $arTransforms=NULL, $options=NULL) {
+ $prefix = NULL;
+ $prefix_ns = NULL;
+ $id_name = 'Id';
+ $overwrite_id = TRUE;
+
+ if (is_array($options)) {
+ $prefix = empty($options['prefix'])?NULL:$options['prefix'];
+ $prefix_ns = empty($options['prefix_ns'])?NULL:$options['prefix_ns'];
+ $id_name = empty($options['id_name'])?'Id':$options['id_name'];
+ $overwrite_id = empty($options['overwrite'])?TRUE:(bool)$options['overwrite'];
+ }
+
+ $attname = $id_name;
+ if (! empty($prefix)) {
+ $attname = $prefix.':'.$attname;
+ }
+
+ $refNode = $this->createNewSignNode('Reference');
+ $sinfoNode->appendChild($refNode);
+
+ if (! $node instanceof DOMDocument) {
+ $uri = NULL;
+ if (! $overwrite_id) {
+ $uri = $node->getAttributeNS($prefix_ns, $attname);
+ }
+ if (empty($uri)) {
+ $uri = XMLSecurityDSig::generate_GUID();
+ $node->setAttributeNS($prefix_ns, $attname, $uri);
+ }
+ $refNode->setAttribute("URI", '#'.$uri);
+ }
+
+ $transNodes = $this->createNewSignNode('Transforms');
+ $refNode->appendChild($transNodes);
+
+ if (is_array($arTransforms)) {
+ foreach ($arTransforms AS $transform) {
+ $transNode = $this->createNewSignNode('Transform');
+ $transNodes->appendChild($transNode);
+ $transNode->setAttribute('Algorithm', $transform);
+ }
+ } elseif (! empty($this->canonicalMethod)) {
+ $transNode = $this->createNewSignNode('Transform');
+ $transNodes->appendChild($transNode);
+ $transNode->setAttribute('Algorithm', $this->canonicalMethod);
+ }
+
+ $canonicalData = $this->processTransforms($refNode, $node);
+ $digValue = $this->calculateDigest($algorithm, $canonicalData);
+
+ $digestMethod = $this->createNewSignNode('DigestMethod');
+ $refNode->appendChild($digestMethod);
+ $digestMethod->setAttribute('Algorithm', $algorithm);
+
+ $digestValue = $this->createNewSignNode('DigestValue', $digValue);
+ $refNode->appendChild($digestValue);
+ }
+
+ public function addReference($node, $algorithm, $arTransforms=NULL, $options=NULL) {
+ if ($xpath = $this->getXPathObj()) {
+ $query = "./secdsig:SignedInfo";
+ $nodeset = $xpath->query($query, $this->sigNode);
+ if ($sInfo = $nodeset->item(0)) {
+ $this->addRefInternal($sInfo, $node, $algorithm, $arTransforms, $options);
+ }
+ }
+ }
+
+ public function addReferenceList($arNodes, $algorithm, $arTransforms=NULL, $options=NULL) {
+ if ($xpath = $this->getXPathObj()) {
+ $query = "./secdsig:SignedInfo";
+ $nodeset = $xpath->query($query, $this->sigNode);
+ if ($sInfo = $nodeset->item(0)) {
+ foreach ($arNodes AS $node) {
+ $this->addRefInternal($sInfo, $node, $algorithm, $arTransforms, $options);
+ }
+ }
+ }
+ }
+
+ public function addObject($data, $mimetype=NULL, $encoding=NULL) {
+ $objNode = $this->createNewSignNode('Object');
+ $this->sigNode->appendChild($objNode);
+ if (! empty($mimetype)) {
+ $objNode->setAtribute('MimeType', $mimetype);
+ }
+ if (! empty($encoding)) {
+ $objNode->setAttribute('Encoding', $encoding);
+ }
+
+ if ($data instanceof DOMElement) {
+ $newData = $this->sigNode->ownerDocument->importNode($data, TRUE);
+ } else {
+ $newData = $this->sigNode->ownerDocument->createTextNode($data);
+ }
+ $objNode->appendChild($newData);
+
+ return $objNode;
+ }
+
+ public function locateKey($node=NULL) {
+ if (empty($node)) {
+ $node = $this->sigNode;
+ }
+ if (! $node instanceof DOMNode) {
+ return NULL;
+ }
+ if ($doc = $node->ownerDocument) {
+ $xpath = new DOMXPath($doc);
+ $xpath->registerNamespace('secdsig', XMLSecurityDSig::XMLDSIGNS);
+ $query = "string(./secdsig:SignedInfo/secdsig:SignatureMethod/@Algorithm)";
+ $algorithm = $xpath->evaluate($query, $node);
+ if ($algorithm) {
+ try {
+ $objKey = new XMLSecurityKey($algorithm, array('type'=>'public'));
+ } catch (Exception $e) {
+ return NULL;
+ }
+ return $objKey;
+ }
+ }
+ return NULL;
+ }
+
+ public function verify($objKey) {
+ $doc = $this->sigNode->ownerDocument;
+ $xpath = new DOMXPath($doc);
+ $xpath->registerNamespace('secdsig', XMLSecurityDSig::XMLDSIGNS);
+ $query = "string(./secdsig:SignatureValue)";
+ $sigValue = $xpath->evaluate($query, $this->sigNode);
+ if (empty($sigValue)) {
+ throw new Exception("Unable to locate SignatureValue");
+ }
+ return $objKey->verifySignature($this->signedInfo, base64_decode($sigValue));
+ }
+
+ public function signData($objKey, $data) {
+ return $objKey->signData($data);
+ }
+
+ public function sign($objKey) {
+ if ($xpath = $this->getXPathObj()) {
+ $query = "./secdsig:SignedInfo";
+ $nodeset = $xpath->query($query, $this->sigNode);
+ if ($sInfo = $nodeset->item(0)) {
+ $query = "./secdsig:SignatureMethod";
+ $nodeset = $xpath->query($query, $sInfo);
+ $sMethod = $nodeset->item(0);
+ $sMethod->setAttribute('Algorithm', $objKey->type);
+ $data = $this->canonicalizeData($sInfo, $this->canonicalMethod);
+ $sigValue = base64_encode($this->signData($objKey, $data));
+ $sigValueNode = $this->createNewSignNode('SignatureValue', $sigValue);
+ if ($infoSibling = $sInfo->nextSibling) {
+ $infoSibling->parentNode->insertBefore($sigValueNode, $infoSibling);
+ } else {
+ $this->sigNode->appendChild($sigValueNode);
+ }
+ }
+ }
+ }
+
+ public function appendCert() {
+
+ }
+
+ public function appendKey($objKey, $parent=NULL) {
+ $objKey->serializeKey($parent);
+ }
+
+
+ /*
+ public function appendSignature($parentNode, $insertBefore = FALSE) {
+ $baseDoc = ($parentNode instanceof DOMDocument)?$parentNode:$parentNode->ownerDocument;
+ $newSig = $baseDoc->importNode($this->sigNode, TRUE);
+ if ($insertBefore) {
+ $parentNode->insertBefore($newSig, $parentNode->firstChild);
+ } else {
+ $parentNode->appendChild($newSig);
+ }
+ }
+ */
+
+
+ public function appendSignatureShib($parentNode, $insertBefore = FALSE, $assertion = false) {
+ $baseDoc = ($parentNode instanceof DOMDocument)?$parentNode:$parentNode->ownerDocument;
+ $newSig = $baseDoc->importNode($this->sigNode, TRUE);
+
+
+
+ $xnode = null;
+
+ $xpath = new DOMXPath($baseDoc);
+ $xpath->registerNamespace('secdsig', XMLSecurityDSig::XMLDSIGNS);
+ $xpath->registerNamespace('samlp', 'urn:oasis:names:tc:SAML:1.0:protocol');
+ $xpath->registerNamespace('saml', 'urn:oasis:names:tc:SAML:1.0:assertion');
+
+
+ if ($insertBefore && !$assertion) {
+
+ $query = "//samlp:Status";
+ $nodeset = $xpath->query($query, $parentNode);
+
+ $xnode = $nodeset->item(0);
+ if (!$xnode)
+ throw new Exception("Could not find node to sign before (Root signing mode)");
+
+ $parentNode->insertBefore($newSig, $xnode);
+
+ } elseif ($insertBefore) {
+
+ $query = "//saml:Assertion/saml:Subject";
+ $nodeset = $xpath->query($query, $parentNode);
+
+ $xnode = $nodeset->item(0);
+ if (!$xnode)
+ throw new Exception("Could not find node to sign before (Assertion signing mode)");
+
+ $parentNode->insertBefore($newSig, $xnode);
+ } else {
+ $parentNode->appendChild($newSig);
+ }
+ }
+
+
+
+ public function appendSignature($parentNode, $insertBefore = false, $assertion = false) {
+ $baseDoc = ($parentNode instanceof DOMDocument)?$parentNode:$parentNode->ownerDocument;
+ $newSig = $baseDoc->importNode($this->sigNode, TRUE);
+
+
+
+ $xnode = null;
+
+ $xpath = new DOMXPath($baseDoc);
+ $xpath->registerNamespace('secdsig', XMLSecurityDSig::XMLDSIGNS);
+ $xpath->registerNamespace('samlp', 'urn:oasis:names:tc:SAML:2.0:protocol');
+ $xpath->registerNamespace('saml', 'urn:oasis:names:tc:SAML:2.0:assertion');
+
+
+ if ($insertBefore && !$assertion) {
+
+ $query = "//samlp:Status";
+ $nodeset = $xpath->query($query, $parentNode);
+
+ $xnode = $nodeset->item(0);
+ if (!$xnode)
+ throw new Exception("Could not find node to sign before (Root signing mode)");
+
+ $parentNode->insertBefore($newSig, $xnode);
+
+ } elseif ($insertBefore) {
+
+ $query = "//saml:Assertion/saml:Subject";
+ $nodeset = $xpath->query($query, $parentNode);
+
+ $xnode = $nodeset->item(0);
+ if (!$xnode)
+ throw new Exception("Could not find node to sign before (Assertion signing mode)");
+
+ $parentNode->insertBefore($newSig, $xnode);
+ } else {
+ $parentNode->appendChild($newSig);
+ }
+ }
+
+ static function get509XCert($cert, $isPEMFormat=TRUE) {
+ $certs = XMLSecurityDSig::staticGet509XCerts($cert, $isPEMFormat);
+ if (! empty($certs)) {
+ return $certs[0];
+ }
+ return '';
+ }
+
+ static function staticGet509XCerts($certs, $isPEMFormat=TRUE) {
+ if ($isPEMFormat) {
+ $data = '';
+ $certlist = array();
+ $arCert = explode("\n", $certs);
+ $inData = FALSE;
+ foreach ($arCert AS $curData) {
+ if (! $inData) {
+ if (strncmp($curData, '-----BEGIN CERTIFICATE', 22) == 0) {
+ $inData = TRUE;
+ }
+ } else {
+ if (strncmp($curData, '-----END CERTIFICATE', 20) == 0) {
+ $inData = FALSE;
+ $certlist[] = $data;
+ $data = '';
+ continue;
+ }
+ $data .= trim($curData);
+ }
+ }
+ return $certlist;
+ } else {
+ return array($certs);
+ }
+ }
+
+ static function staticAdd509Cert($parentRef, $cert, $isPEMFormat=TRUE, $isURL=False, $xpath=NULL) {
+ if ($isURL) {
+ $cert = file_get_contents($cert);
+ }
+ if (! $parentRef instanceof DOMElement) {
+ throw new Exception('Invalid parent Node parameter');
+ }
+ $baseDoc = $parentRef->ownerDocument;
+
+ if (empty($xpath)) {
+ $xpath = new DOMXPath($parentRef->ownerDocument);
+ $xpath->registerNamespace('secdsig', XMLSecurityDSig::XMLDSIGNS);
+ }
+
+ $query = "./secdsig:KeyInfo";
+ $nodeset = $xpath->query($query, $parentRef);
+ $keyInfo = $nodeset->item(0);
+ if (! $keyInfo) {
+ $inserted = FALSE;
+ $keyInfo = $baseDoc->createElementNS(XMLSecurityDSig::XMLDSIGNS, 'ds:KeyInfo');
+
+ $query = "./secdsig:Object";
+ $nodeset = $xpath->query($query, $parentRef);
+ if ($sObject = $nodeset->item(0)) {
+ $sObject->parentNode->insertBefore($keyInfo, $sObject);
+ $inserted = TRUE;
+ }
+
+ if (! $inserted) {
+ $parentRef->appendChild($keyInfo);
+ }
+ }
+
+ // Add all certs if there are more than one
+ $certs = XMLSecurityDSig::staticGet509XCerts($cert, $isPEMFormat);
+
+ // Atach X509 data node
+ $x509DataNode = $baseDoc->createElementNS(XMLSecurityDSig::XMLDSIGNS, 'ds:X509Data');
+ $keyInfo->appendChild($x509DataNode);
+
+ // Atach all certificate nodes
+ foreach ($certs as $X509Cert){
+ $x509CertNode = $baseDoc->createElementNS(XMLSecurityDSig::XMLDSIGNS, 'ds:X509Certificate', $X509Cert);
+ $x509DataNode->appendChild($x509CertNode);
+ }
+ }
+
+ public function add509Cert($cert, $isPEMFormat=TRUE, $isURL=False) {
+ if ($xpath = $this->getXPathObj()) {
+ self::staticAdd509Cert($this->sigNode, $cert, $isPEMFormat, $isURL, $xpath);
+ }
+ }
+}
+
+class XMLSecEnc {
+ const template = "<xenc:EncryptedData xmlns:xenc='http://www.w3.org/2001/04/xmlenc#'>
+ <xenc:CipherData>
+ <xenc:CipherValue></xenc:CipherValue>
+ </xenc:CipherData>
+</xenc:EncryptedData>";
+
+ const Element = 'http://www.w3.org/2001/04/xmlenc#Element';
+ const Content = 'http://www.w3.org/2001/04/xmlenc#Content';
+ const URI = 3;
+ const XMLENCNS = 'http://www.w3.org/2001/04/xmlenc#';
+
+ private $encdoc = NULL;
+ private $rawNode = NULL;
+ public $type = NULL;
+ public $encKey = NULL;
+
+ public function __construct() {
+ $this->encdoc = new DOMDocument();
+ $this->encdoc->loadXML(XMLSecEnc::template);
+ }
+
+ public function setNode($node) {
+ $this->rawNode = $node;
+ }
+
+ public function encryptNode($objKey, $replace=TRUE) {
+ $data = '';
+ if (empty($this->rawNode)) {
+ throw new Exception('Node to encrypt has not been set');
+ }
+ if (! $objKey instanceof XMLSecurityKey) {
+ throw new Exception('Invalid Key');
+ }
+ $doc = $this->rawNode->ownerDocument;
+ $xPath = new DOMXPath($this->encdoc);
+ $objList = $xPath->query('/xenc:EncryptedData/xenc:CipherData/xenc:CipherValue');
+ $cipherValue = $objList->item(0);
+ if ($cipherValue == NULL) {
+ throw new Exception('Error locating CipherValue element within template');
+ }
+ switch ($this->type) {
+ case (XMLSecEnc::Element):
+ $data = $doc->saveXML($this->rawNode);
+ $this->encdoc->documentElement->setAttribute('Type', XMLSecEnc::Element);
+ break;
+ case (XMLSecEnc::Content):
+ $children = $this->rawNode->childNodes;
+ foreach ($children AS $child) {
+ $data .= $doc->saveXML($child);
+ }
+ $this->encdoc->documentElement->setAttribute('Type', XMLSecEnc::Content);
+ break;
+ default:
+ throw new Exception('Type is currently not supported');
+ return;
+ }
+
+ $encMethod = $this->encdoc->documentElement->appendChild($this->encdoc->createElementNS(XMLSecEnc::XMLENCNS, 'xenc:EncryptionMethod'));
+ $encMethod->setAttribute('Algorithm', $objKey->getAlgorith());
+ $cipherValue->parentNode->parentNode->insertBefore($encMethod, $cipherValue->parentNode);
+
+ $strEncrypt = base64_encode($objKey->encryptData($data));
+ $value = $this->encdoc->createTextNode($strEncrypt);
+ $cipherValue->appendChild($value);
+
+ if ($replace) {
+ switch ($this->type) {
+ case (XMLSecEnc::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;
+ break;
+ case (XMLSecEnc::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;
+ break;
+ }
+ }
+ }
+
+ public function decryptNode($objKey, $replace=TRUE) {
+ $data = '';
+ if (empty($this->rawNode)) {
+ throw new Exception('Node to decrypt has not been set');
+ }
+ if (! $objKey instanceof XMLSecurityKey) {
+ throw new Exception('Invalid Key');
+ }
+ $doc = $this->rawNode->ownerDocument;
+ $xPath = new DOMXPath($doc);
+ $xPath->registerNamespace('xmlencr', XMLSecEnc::XMLENCNS);
+ /* Only handles embedded content right now and not a reference */
+ $query = "./xmlencr:CipherData/xmlencr:CipherValue";
+ $nodeset = $xPath->query($query, $this->rawNode);
+
+ if ($node = $nodeset->item(0)) {
+ $encryptedData = base64_decode($node->nodeValue);
+ $decrypted = $objKey->decryptData($encryptedData);
+ if ($replace) {
+ switch ($this->type) {
+ case (XMLSecEnc::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;
+ break;
+ case (XMLSecEnc::Content):
+ if ($this->rawNode->nodeType == XML_DOCUMENT_NODE) {
+ $doc = $this->rawNode;
+ } else {
+ $doc = $this->rawNode->ownerDocument;
+ }
+ $newFrag = $doc->createDOMDocumentFragment();
+ $newFrag->appendXML($decrypted);
+ $this->rawNode->parentNode->replaceChild($newFrag, $this->rawNode);
+ return $this->rawNode->parentNode;
+ break;
+ default:
+ return $decrypted;
+ }
+ } else {
+ return $decrypted;
+ }
+ } else {
+ throw new Exception("Cannot locate encrypted data");
+ }
+ }
+
+ public function encryptKey($srcKey, $rawKey, $append=TRUE) {
+ if ((! $srcKey instanceof XMLSecurityKey) || (! $rawKey instanceof XMLSecurityKey)) {
+ throw new Exception('Invalid Key');
+ }
+ $strEncKey = base64_encode($srcKey->encryptData($rawKey->key));
+ $root = $this->encdoc->documentElement;
+ $encKey = $this->encdoc->createElementNS(XMLSecEnc::XMLENCNS, 'xenc:EncryptedKey');
+ if ($append) {
+ $keyInfo = $root->appendChild($this->encdoc->createElementNS('http://www.w3.org/2000/09/xmldsig#', 'dsig:KeyInfo'));
+ $keyInfo->appendChild($encKey);
+ } else {
+ $this->encKey = $encKey;
+ }
+ $encMethod = $encKey->appendChild($this->encdoc->createElementNS(XMLSecEnc::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(XMLSecEnc::XMLENCNS, 'xenc:CipherData'));
+ $cipherData->appendChild($this->encdoc->createElementNS(XMLSecEnc::XMLENCNS, 'xenc:CipherValue', $strEncKey));
+ return;
+ }
+
+ 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);
+ }
+
+ 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()='".XMLSecEnc::XMLENCNS."']";
+ $nodeset = $xpath->query($query);
+ return $nodeset->item(0);
+ }
+ return NULL;
+ }
+
+ 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', XMLSecEnc::XMLENCNS);
+ $query = ".//xmlsecenc:EncryptionMethod";
+ $nodeset = $xpath->query($query, $node);
+ if ($encmeth = $nodeset->item(0)) {
+ $attrAlgorithm = $encmeth->getAttribute("Algorithm");
+ try {
+ $objKey = new XMLSecurityKey($attrAlgorithm, array('type'=>'private'));
+ } catch (Exception $e) {
+ return NULL;
+ }
+ return $objKey;
+ }
+ }
+ return NULL;
+ }
+
+ static function staticLocateKeyInfo($objBaseKey=NULL, $node=NULL) {
+ if (empty($node) || (! $node instanceof DOMNode)) {
+ return NULL;
+ }
+ if ($doc = $node->ownerDocument) {
+ $xpath = new DOMXPath($doc);
+ $xpath->registerNamespace('xmlsecenc', XMLSecEnc::XMLENCNS);
+ $xpath->registerNamespace('xmlsecdsig', XMLSecurityDSig::XMLDSIGNS);
+ $query = "./xmlsecdsig:KeyInfo";
+ $nodeset = $xpath->query($query, $node);
+ if ($encmeth = $nodeset->item(0)) {
+ 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");
+ break;
+ case 'RSAKeyValue':
+ $modulus = NULL;
+ $exponent = NULL;
+ if ($modulusNode = $keyval->getElementsByTagName('Modulus')->item(0)) {
+ $modulus = base64_decode($modulusNode->nodeValue);
+ }
+ if ($exponentNode = $keyval->getElementsByTagName('Exponent')->item(0)) {
+ $exponent = base64_decode($exponentNode->nodeValue);
+ }
+ if (empty($modulus) || empty($exponent)) {
+ throw new Exception("Missing Modulus or Exponent");
+ }
+ $publicKey = XMLSecurityKey::convertRSA($modulus, $exponent);
+ $objBaseKey->loadKey($publicKey);
+ break;
+ }
+ }
+ break;
+ case 'RetrievalMethod':
+ /* Not currently supported */
+ break;
+ case 'EncryptedKey':
+ $objenc = new XMLSecEnc();
+ $objenc->setNode($child);
+ if (! $objKey = $objenc->locateKey()) {
+ throw new Exception("Unable to locate algorithm for this Encrypted Key");
+ }
+ $objKey->isEncrypted = TRUE;
+ $objKey->encryptedCtx = $objenc;
+ XMLSecEnc::staticLocateKeyInfo($objKey, $child);
+ return $objKey;
+ break;
+ 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);
+ }
+ }
+ break;
+ }
+ }
+ }
+ return $objBaseKey;
+ }
+ return NULL;
+ }
+
+ public function locateKeyInfo($objBaseKey=NULL, $node=NULL) {
+ if (empty($node)) {
+ $node = $this->rawNode;
+ }
+ return XMLSecEnc::staticLocateKeyInfo($objBaseKey, $node);
+ }
+}
?>
\ No newline at end of file