. * 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 * @copyright 2007 Robert Richards * @license http://www.opensource.org/licenses/bsd-license.php BSD License * @version 1.0.0 */ class ICardEndpoint { private $address; private $secpolicy; private $identityCert = array('cert'=>NULL, 'isPem'=>True, 'isURL'=>False); const WSANS = 'http://www.w3.org/2005/08/addressing'; const WSAPFX = 'wsa'; const MEXNS = 'http://schemas.xmlsoap.org/ws/2004/09/mex'; const MEXPFX = 'mex'; const WSIDNS = 'http://schemas.xmlsoap.org/ws/2006/02/addressingidentity'; const WSIDPFX = 'wsai'; public function __construct($address, $secpolicyAddress) { if (empty($address)) { throw new Exception("Address must be supplied"); } if (empty($secpolicyAddress)) { throw new Exception("Security Policy Address must be supplied"); } $this->address = $address; $this->secpolicy = $secpolicyAddress; } public function setIdentityX509($cert, $isPEMFormat=TRUE, $isURL=False) { $this->identityCert['cert'] = $cert; $this->identityCert['isPem'] = $isPEMFormat; $this->identityCert['isURL'] = $isURL; } public function appendToDoc($parent) { if (! $parent instanceof DOMElement) { throw new Exception("Parent must be a DOMElement"); } $doc = $parent->ownerDocument; $endpoint = $doc->createElementNS(self::WSANS, self::WSAPFX.':EndpointReference'); $parent->appendChild($endpoint); $addrNode = $doc->createElementNS(self::WSANS, self::WSAPFX.':Address', $this->address); $endpoint->appendChild($addrNode); $WSAMetaNode = $doc->createElementNS(self::WSANS, self::WSAPFX.':Metadata'); $endpoint->appendChild($WSAMetaNode); $MEXNode = $doc->createElementNS(self::MEXNS, self::MEXPFX.':Metadata'); $WSAMetaNode->appendChild($MEXNode); $MEXSection = $doc->createElementNS(self::MEXNS, self::MEXPFX.':MetadataSection'); $MEXNode->appendChild($MEXSection); $MEXSection->setAttribute('Dialect', 'http://schemas.xmlsoap.org/ws/2004/09/mex'); $MEXRef = $doc->createElementNS(self::MEXNS, self::MEXPFX.':MetadataReference'); $MEXSection->appendChild($MEXRef); $addrNode = $doc->createElementNS(self::WSANS, self::WSAPFX.':Address', $this->secpolicy); $MEXRef->appendChild($addrNode); if (! empty($this->identityCert['cert'])) { $idNode = $doc->createElementNS(self::WSIDNS, self::WSIDPFX.':Identity'); $endpoint->appendChild($idNode); XMLSecurityDSig::staticAdd509Cert($idNode, $this->identityCert['cert'], $this->identityCert['isPem'], $this->identityCert['isURL']); } } } class ICardSAML { private $samlDoc; private $attributes = array(); private $conditions = array(); private $identityCert = array('cert'=>NULL, 'isPem'=>True, 'isURL'=>False); private $samlTmpl = ''; const SAML10 = 'urn:oasis:names:tc:SAML:1.0:assertion'; const SAML10PFX = 'saml'; const NOTBEFORE = 'NotBefore'; const NOTONORAFTER = 'NotOnOrAfter'; const AUDIENCE = 'Audience'; public function __construct($issuer) { $this->samlDoc = new DOMDocument(); $this->samlDoc->loadXML($this->samlTmpl); $root = $this->samlDoc->documentElement; $root->setAttribute('Issuer', $issuer); $root->setAttribute('IssueInstant', gmdate("Y-m-d\TH:i:s", time()).'Z'); } public function addAttribute($objAttr) { if (! $objAttr instanceof ICardClaimType) { throw new Exception('Attribute must be of ICardClaimType'); } if (empty($objAttr->name)) { throw new Exception('Attribute does not have name/value set'); } $this->attributes[] = $objAttr; } public function addCondition($condition, $value) { switch ($condition) { case (self::NOTBEFORE): case (self::NOTONORAFTER): /* check date */ $xmlVal = gmdate("Y-m-d\TH:i:s", $value).'Z'; break; case (self::AUDIENCE): $xmlVal = $value; break; default: throw new Exception('Supplied condition not supported'); } $this->conditions['condition'] = $xmlVal; } private function createAttributes($parent) { foreach ($this->attributes AS $objAttr) { $attrNode = $this->samlDoc->createElementNS(self::SAML10, self::SAML10PFX.':Attribute'); $parent->appendChild($attrNode); $attrNode->setAttribute('AttributeName', $objAttr->name); $attrNode->setAttribute('AttributeNamespace', $objAttr->URI); $attrValue = $this->samlDoc->createElementNS(self::SAML10, self::SAML10PFX.':AttributeValue', $objAttr->value); $attrNode->appendChild($attrValue); } } public function setX509Cert($cert, $isPEMFormat=TRUE, $isURL=False) { $this->identityCert['cert'] = $cert; $this->identityCert['isPem'] = $isPEMFormat; $this->identityCert['isURL'] = $isURL; } public function getXMLDisplay() { $doc = new DOMDocument(); $doc->loadXML(''); $token = $doc->documentElement; foreach ($this->attributes AS $objAttr) { $claimNode = $doc->createElementNS('http://schemas.xmlsoap.org/ws/2005/05/identity', 'wsi:DisplayClaim'); $token->appendChild($claimNode); $claimNode->setAttribute('Uri', $objAttr->URI); $dispNode = $doc->createElementNS('http://schemas.xmlsoap.org/ws/2005/05/identity', 'wsi:DisplayTag', $objAttr->displayName); $claimNode->appendChild($dispNode); $dispNode = $doc->createElementNS('http://schemas.xmlsoap.org/ws/2005/05/identity', 'wsi:Description', $objAttr->description); $claimNode->appendChild($dispNode); $dispNode = $doc->createElementNS('http://schemas.xmlsoap.org/ws/2005/05/identity', 'wsi:DisplayValue', $objAttr->value); $claimNode->appendChild($dispNode); } return $doc->saveXML($token); } public function asXML($strKey) { if (count($this->attributes) == 0) { throw new Exception('No attributes have been defined'); } $root = $this->samlDoc->documentElement; if (count($this->conditions) > 0) { $condNode = $this->samlDoc->createElementNS(self::SAML10, self::SAML10PFX.':Conditions'); $this->samlDoc->documentElement->appendChild($condNode); foreach ($this->conditions AS $condition=>$value) { if ($condition != self::AUDIENCE) { $condNode->setAttribute($condition, $value); } else { $audRest = $this->samlDoc->createElementNS(self::SAML10, self::SAML10PFX.':AudienceRestrictionCondition'); $condNode->appendChild($audRest); $audience = $this->samlDoc->createElementNS(self::SAML10, self::SAML10PFX.':Audience', $value); $audRest->appendChild($audience); } } } $child = $root->firstChild; while ($child) { if ($child->localName == 'AttributeStatement') { $this->createAttributes($child); } $child = $child->nextSibling; } $objDSig = new XMLSecurityDSig(); $objDSig->setCanonicalMethod(XMLSecurityDSig::EXC_C14N); $arOptions = array('id_name'=>'AssertionID'); $objDSig->addReference($this->samlDoc->documentElement, XMLSecurityDSig::SHA1, array('http://www.w3.org/2000/09/xmldsig#enveloped-signature'), $arOptions); $objKey = new XMLSecurityKey(XMLSecurityKey::RSA_SHA1, array('type'=>'private')); $objKey->loadKey($strKey, False); $objDSig->sign($objKey); $objDSig->add509Cert($this->identityCert['cert'], $this->identityCert['isPem'], $this->identityCert['isURL']); $objDSig->appendSignature($root); return $this->samlDoc->saveXML($root); } } class ICardUserCredential { private $type; private $displayHint = NULL; private $userName = NULL; const UsernamePasswordCredential = 1; const KerberosV5Credential = 2; const X509V3Credential = 3; const SelfIssuedCredential = 4; public function __construct($type, $dispHint=NULL) { if ($type != self::UsernamePasswordCredential) { throw new Exception('Only UsernamePassword credential currently implemented'); } $this->displayHint = $dispHint; $this->type = $type; } public function setUserName($Username) { $this->userName = $Username; } public function appendToDoc($parent) { if (! $parent instanceof DOMElement) { throw new Exception("Parent must be a DOMElement"); } $doc = $parent->ownerDocument; $usercred = $doc->createElementNS(ICard::ICARDNS, ICard::ICARDNS_PFX.':UserCredential'); $parent->appendChild($usercred); if (! empty($this->displayHint)) { $disphint = $doc->createElementNS(ICard::ICARDNS, ICard::ICARDNS_PFX.':DisplayCredentialHint', $this->displayHint); $usercred->appendChild($disphint); } switch ($this->type) { case (self::UsernamePasswordCredential): $cred = $doc->createElementNS(ICard::ICARDNS, ICard::ICARDNS_PFX.':UsernamePasswordCredential'); $usercred->appendChild($cred); if (! empty($this->userName)) { $uname = $doc->createElementNS(ICard::ICARDNS, ICard::ICARDNS_PFX.':Username', $this->userName); $cred->appendChild($uname); } break; default: break; } } } class ICardTokenService { public $endPointRef = NULL; private $userCredential = NULL; public function __construct($endPoint, $userCred) { if (! $endPoint instanceof ICardEndpoint) { throw new Exception("EndPoint must be an ICardEndpoint"); } if (! $userCred instanceof ICardUserCredential) { throw new Exception("UserCredential must be an ICardUserCredential"); } $this->endPointRef = $endPoint; $this->userCredential = $userCred; } public function appendToDoc($parent) { if (! $parent instanceof DOMElement) { throw new Exception("Parent must be a DOMElement"); } $doc = $parent->ownerDocument; $tokensvc = $doc->createElementNS(ICard::ICARDNS, ICard::ICARDNS_PFX.':TokenService'); $parent->appendChild($tokensvc); $this->endPointRef->appendToDoc($tokensvc); $this->userCredential->appendToDoc($tokensvc); } } class ICardClaimType { public $URI = NULL; public $displayName = NULL; public $description = NULL; public $name = NULL; public $value = NULL; public function __construct($URI, $displayName = NULL, $description = NULL) { $this->URI = $URI; $this->displayName = $displayName; $this->description = $description; } public function setValue($name, $value = NULL) { $this->name = $name; $this->value = $value; } } class ICard { public $lang = 'en-us'; public $CardId; public $CardVersion; public $CardName = NULL; private $CardImage = NULL; private $CardImageMime = NULL; public $Issuer; public $TimeExpires; public $RequireAppliesTo = NULL; private $PrivacyURI = NULL; private $PrivacyVersion = NULL; // change this later private $cardDoc = NULL; public $cardDoc = NULL; private $tokenServices = array(); private $tokenTypes = NULL; private $claimTypes = NULL; const ICARDNS = 'http://schemas.xmlsoap.org/ws/2005/05/identity'; const ICARDNS_PFX = 'wsi'; const WSTRUSTNS = 'http://schemas.xmlsoap.org/ws/2005/02/trust'; const WSTRUSTNS_PFX = 'wst'; public function __construct($CardId, $CardVersion, $Issuer, $cardLang=NULL) { if (! empty($cardLang)) { $this->lang = $cardLang; } $cardDoc = new DOMDocument(); $root = $cardDoc->createElementNS(self::ICARDNS, self::ICARDNS_PFX.':InformationCard'); $cardDoc->appendChild($root); $root->setAttribute('xml:lang', $this->lang); $iref = $cardDoc->createElementNS(self::ICARDNS, self::ICARDNS_PFX.':InformationCardReference'); $root->appendChild($iref); $id = $cardDoc->createElementNS(self::ICARDNS, self::ICARDNS_PFX.':CardId', $CardId); $iref->appendChild($id); $id = $cardDoc->createElementNS(self::ICARDNS, self::ICARDNS_PFX.':CardVersion', $CardVersion); $iref->appendChild($id); $this->Issuer = $Issuer; $this->cardDoc = $cardDoc; } public function setExpireTime($expireDate) { if (($timestamp = strtotime($expireDate)) === false) { throw new Exception("Invalid ExpireTime"); } if ($timestamp < time()) { throw new Exception("ExpireTime cannot be in the past"); } $this->TimeExpires = $timestamp; } private function buildRelyingParty() { if (! is_null($this->RequireAppliesTo)) { $root = $this->cardDoc->documentElement; $uri = $this->cardDoc->createElementNS(self::ICARDNS, self::ICARDNS_PFX.':RequireAppliesTo'); $root->appendChild($uri); if ($this->RequireAppliesTo) { $uri->setAttribute('Optional', 'false'); } else { $uri->setAttribute('Optional', 'true'); } } } public function requireRelyingParty($RequireAppliesTo=NULL) { if (! is_null($RequireAppliesTo)) { $this->RequireAppliesTo = (bool)$RequireAppliesTo; } else { $this->RequireAppliesTo = NULL; } } public function addClaimType($objClaim) { if (! $objClaim instanceof ICardClaimType) { throw new Exception('Claim must be of ICardClaimType'); } if (! is_array($this->claimTypes)) { $this->claimTypes = array(); } $this->claimTypes[] = $objClaim; } public function setTokenTypes($arTokenTypes = NULL) { if (is_null($arTokenTypes)) { $this->tokenTypes = NULL; return True; } if (! is_array($arTokenTypes)) { throw new Exception('NULL or array of Token Types required for input parameter'); } $this->tokenTypes = $arTokenTypes; } public function setCardImage($imgFile, $imgMime = NULL) { $this->CardImage = $imgFile; $this->CardImageMime = $imgMime; } public function setPrivacyNotice($URI, $version=NULL) { $this->PrivacyURI = $URI; $this->PrivacyVersion = $version; } public function addService($objSvc) { if (! $objSvc instanceof ICardTokenService) { throw new Exception('Service must be an ICardTokenService'); } $this->tokenServices[] = $objSvc; } public function getCard() { $doc = $this->cardDoc; $root = $doc->documentElement; if (! empty($this->CardName)) { $iref = $doc->createElementNS(self::ICARDNS, self::ICARDNS_PFX.':CardName', $this->CardName); $root->appendChild($iref); } if (! empty($this->CardImage)) { $image = file_get_contents($this->CardImage); $encodedImage = base64_encode($image); $iref = $doc->createElementNS(self::ICARDNS, self::ICARDNS_PFX.':CardImage', $encodedImage); $root->appendChild($iref); if (! empty($this->CardImageMime)) { $iref->setAttribute('MimeType', $this->CardImageMime); } } $current = $doc->createElementNS(self::ICARDNS, self::ICARDNS_PFX.':Issuer', $this->Issuer); $root->appendChild($current); $currentTime = time(); $current = $doc->createElementNS(self::ICARDNS, self::ICARDNS_PFX.':TimeIssued', gmdate("Y-m-d\TH:i:s", $currentTime).'Z'); $root->appendChild($current); if (! empty($this->TimeExpires)) { $expires = $doc->createElementNS(self::ICARDNS, self::ICARDNS_PFX.':TimeExpires', gmdate("Y-m-d\TH:i:s", $this->TimeExpires).'Z'); $root->appendChild($expires); } $tokensvclist = $doc->createElementNS(self::ICARDNS, self::ICARDNS_PFX.':TokenServiceList'); $root->appendChild($tokensvclist); foreach ($this->tokenServices AS $tokensvc) { $tokensvc->appendToDoc($tokensvclist); } if (! empty($this->tokenTypes)) { $tokenlist = $doc->createElementNS(self::ICARDNS, self::ICARDNS_PFX.':SupportedTokenTypeList'); $root->appendChild($tokenlist); foreach ($this->tokenTypes AS $tokenType) { $token = $doc->createElementNS(self::WSTRUSTNS, self::WSTRUSTNS_PFX.':TokenType', $tokenType); $tokenlist->appendChild($token); } } if (is_array($this->claimTypes)) { $claimlist = $doc->createElementNS(self::ICARDNS, self::ICARDNS_PFX.':SupportedClaimTypeList'); $root->appendChild($claimlist); foreach ($this->claimTypes AS $objClaim) { $claimtype = $doc->createElementNS(self::ICARDNS, self::ICARDNS_PFX.':SupportedClaimType'); $claimlist->appendChild($claimtype); $claimtype->setAttribute('Uri', $objClaim->URI); if (! empty($objClaim->displayName)) { $claimdisp = $doc->createElementNS(self::ICARDNS, self::ICARDNS_PFX.':DisplayTag', $objClaim->displayName); $claimtype->appendChild($claimdisp); } if (! empty($objClaim->description)) { $claimdesc = $doc->createElementNS(self::ICARDNS, self::ICARDNS_PFX.':Description', $objClaim->description); $claimtype->appendChild($claimdesc); } } } $this->buildRelyingParty(); if (! empty($this->PrivacyURI)) { $uri = $doc->createElementNS(self::ICARDNS, self::ICARDNS_PFX.':PrivacyNotice', $this->PrivacyURI); $root->appendChild($uri); if (! is_null($this->PrivacyVersion)) { $uri->setAttribute('Version', $this->PrivacyVersion); } } $doc->formatOutput = True; return $doc->saveXML(); } } ?>