1<?php 2 3/** 4 * Determine if the SAML response is valid using a provided x509 certificate. 5 */ 6class OneLogin_Saml_XmlSec 7{ 8 /** 9 * A SamlResponse class provided to the constructor. 10 * @var OneLogin_Saml_Settings 11 */ 12 protected $_settings; 13 14 /** 15 * The document to be tested. 16 * @var DomDocument 17 */ 18 protected $_document; 19 20 /** 21 * Construct the SamlXmlSec object. 22 * 23 * @param OneLogin_Saml_Settings $settings A SamlResponse settings object containing the necessary 24 * x509 certicate to test the document. 25 * @param OneLogin_Saml_Response $response The document to test. 26 */ 27 public function __construct(OneLogin_Saml_Settings $settings, OneLogin_Saml_Response $response) 28 { 29 $this->_settings = $settings; 30 $this->_document = clone $response->document; 31 } 32 33 /** 34 * Verify that the document only contains a single Assertion 35 * 36 * @return bool TRUE if the document passes. 37 */ 38 public function validateNumAssertions() 39 { 40 $rootNode = $this->_document; 41 $assertionNodes = $rootNode->getElementsByTagName('Assertion'); 42 return ($assertionNodes->length == 1); 43 } 44 45 /** 46 * Verify that the document is still valid according 47 * 48 * @return bool 49 */ 50 public function validateTimestamps() 51 { 52 $rootNode = $this->_document; 53 $timestampNodes = $rootNode->getElementsByTagName('Conditions'); 54 for ($i = 0; $i < $timestampNodes->length; $i++) { 55 $nbAttribute = $timestampNodes->item($i)->attributes->getNamedItem("NotBefore"); 56 $naAttribute = $timestampNodes->item($i)->attributes->getNamedItem("NotOnOrAfter"); 57 if ($nbAttribute && strtotime($nbAttribute->textContent) > time()) { 58 return false; 59 } 60 if ($naAttribute && strtotime($naAttribute->textContent) <= time()) { 61 return false; 62 } 63 } 64 return true; 65 } 66 67 /** 68 * @return bool 69 * 70 * @throws Exception 71 */ 72 public function isValid() 73 { 74 $singleAssertion = $this->validateNumAssertions(); 75 if (!$singleAssertion) { 76 throw new Exception('Multiple assertions are not supported'); 77 } 78 79 $validTimestamps = $this->validateTimestamps(); 80 if (!$validTimestamps) { 81 throw new Exception('Timing issues (please check your clock settings)'); 82 } 83 84 $objXMLSecDSig = new XMLSecurityDSig(); 85 86 $objDSig = $objXMLSecDSig->locateSignature($this->_document); 87 if (!$objDSig) { 88 throw new Exception('Cannot locate Signature Node'); 89 } 90 $objXMLSecDSig->canonicalizeSignedInfo(); 91 $objXMLSecDSig->idKeys = array('ID'); 92 93 $objKey = $objXMLSecDSig->locateKey(); 94 if (!$objKey) { 95 throw new Exception('We have no idea about the key'); 96 } 97 98 try { 99 $objXMLSecDSig->validateReference(); 100 } catch (Exception $e) { 101 throw new Exception('Reference Validation Failed'); 102 } 103 104 XMLSecEnc::staticLocateKeyInfo($objKey, $objDSig); 105 106 $objKey->loadKey($this->_settings->idpPublicCertificate, false, true); 107 108 return ($objXMLSecDSig->verify($objKey) === 1); 109 } 110} 111