00001 <?php
00025 require_once 'Zend/InfoCard/Xml/EncryptedData.php';
00026
00030 require_once 'Zend/InfoCard/Xml/Assertion.php';
00031
00035 require_once 'Zend/InfoCard/Cipher.php';
00036
00040 require_once 'Zend/InfoCard/Xml/Security.php';
00041
00045 require_once 'Zend/InfoCard/Adapter/Interface.php';
00046
00050 require_once 'Zend/InfoCard/Claims.php';
00051
00058 class Zend_InfoCard
00059 {
00063 const DIGEST_SHA1 = 'http://www.w3.org/2000/09/xmldsig#sha1';
00064
00071 protected $_keyPairs;
00072
00078 protected $_pkiCipherObj;
00079
00085 protected $_symCipherObj;
00086
00093 protected $_adapter;
00094
00095
00101 public function __construct()
00102 {
00103 $this->_keyPairs = array();
00104
00105 if(!extension_loaded('mcrypt')) {
00106 require_once 'Zend/InfoCard/Exception.php';
00107 throw new Zend_InfoCard_Exception("Use of the Zend_InfoCard component requires the mcrypt extension to be enabled in PHP");
00108 }
00109
00110 if(!extension_loaded('openssl')) {
00111 require_once 'Zend/InfoCard/Exception.php';
00112 throw new Zend_InfoCard_Exception("Use of the Zend_InfoCard component requires the openssl extension to be enabled in PHP");
00113 }
00114 }
00115
00123 public function setAdapter(Zend_InfoCard_Adapter_Interface $a)
00124 {
00125 $this->_adapter = $a;
00126 return $this;
00127 }
00128
00135 public function getAdapter()
00136 {
00137 if($this->_adapter === null) {
00138 require_once 'Zend/InfoCard/Adapter/Default.php';
00139 $this->setAdapter(new Zend_InfoCard_Adapter_Default());
00140 }
00141
00142 return $this->_adapter;
00143 }
00144
00150 public function getPkiCipherObject()
00151 {
00152 return $this->_pkiCipherObj;
00153 }
00154
00161 public function setPkiCipherObject(Zend_InfoCard_Cipher_Pki_Interface $cipherObj)
00162 {
00163 $this->_pkiCipherObj = $cipherObj;
00164 return $this;
00165 }
00166
00172 public function getSymCipherObject()
00173 {
00174 return $this->_symCipherObj;
00175 }
00176
00183 public function setSymCipherObject($cipherObj)
00184 {
00185 $this->_symCipherObj = $cipherObj;
00186 return $this;
00187 }
00188
00196 public function removeCertificatePair($key_id)
00197 {
00198
00199 if(!key_exists($key_id, $this->_keyPairs)) {
00200 require_once 'Zend/InfoCard/Exception.php';
00201 throw new Zend_InfoCard_Exception("Attempted to remove unknown key id: $key_id");
00202 }
00203
00204 unset($this->_keyPairs[$key_id]);
00205 return $this;
00206 }
00207
00218 public function addCertificatePair($private_key_file, $public_key_file, $type = Zend_InfoCard_Cipher::ENC_RSA_OAEP_MGF1P, $password = null)
00219 {
00220 if(!file_exists($private_key_file) ||
00221 !file_exists($public_key_file)) {
00222 require_once 'Zend/InfoCard/Exception.php';
00223 throw new Zend_InfoCard_Exception("Could not locate the public and private certificate pair files: $private_key_file, $public_key_file");
00224 }
00225
00226 if(!is_readable($private_key_file) ||
00227 !is_readable($public_key_file)) {
00228 require_once 'Zend/InfoCard/Exception.php';
00229 throw new Zend_InfoCard_Exception("Could not read the public and private certificate pair files (check permissions): $private_key_file, $public_key_file");
00230 }
00231
00232 $key_id = md5($private_key_file.$public_key_file);
00233
00234 if(key_exists($key_id, $this->_keyPairs)) {
00235 require_once 'Zend/InfoCard/Exception.php';
00236 throw new Zend_InfoCard_Exception("Attempted to add previously existing certificate pair: $private_key_file, $public_key_file");
00237 }
00238
00239 switch($type) {
00240 case Zend_InfoCard_Cipher::ENC_RSA:
00241 case Zend_InfoCard_Cipher::ENC_RSA_OAEP_MGF1P:
00242 $this->_keyPairs[$key_id] = array('private' => $private_key_file,
00243 'public' => $public_key_file,
00244 'type_uri' => $type);
00245
00246 if($password !== null) {
00247 $this->_keyPairs[$key_id]['password'] = $password;
00248 } else {
00249 $this->_keyPairs[$key_id]['password'] = null;
00250 }
00251
00252 return $key_id;
00253 break;
00254 default:
00255 require_once 'Zend/InfoCard/Exception.php';
00256 throw new Zend_InfoCard_Exception("Invalid Certificate Pair Type specified: $type");
00257 }
00258 }
00259
00268 public function getCertificatePair($key_id)
00269 {
00270 if(key_exists($key_id, $this->_keyPairs)) {
00271 return $this->_keyPairs[$key_id];
00272 }
00273
00274 require_once 'Zend/InfoCard/Exception.php';
00275 throw new Zend_InfoCard_Exception("Invalid Certificate Pair ID provided: $key_id");
00276 }
00277
00287 protected function _getPublicKeyDigest($key_id, $digestMethod = self::DIGEST_SHA1)
00288 {
00289 $certificatePair = $this->getCertificatePair($key_id);
00290
00291 $temp = file($certificatePair['public']);
00292 unset($temp[count($temp)-1]);
00293 unset($temp[0]);
00294 $certificateData = base64_decode(implode("\n", $temp));
00295
00296 switch($digestMethod) {
00297 case self::DIGEST_SHA1:
00298 $digest_retval = sha1($certificateData, true);
00299 break;
00300 default:
00301 require_once 'Zend/InfoCard/Exception.php';
00302 throw new Zend_InfoCard_Exception("Invalid Digest Type Provided: $digestMethod");
00303 }
00304
00305 return $digest_retval;
00306 }
00307
00315 protected function _findCertifiatePairByDigest($digest, $digestMethod = self::DIGEST_SHA1)
00316 {
00317
00318 foreach($this->_keyPairs as $key_id => $certificate_data) {
00319
00320 $cert_digest = $this->_getPublicKeyDigest($key_id, $digestMethod);
00321
00322 if($cert_digest == $digest) {
00323 return $key_id;
00324 }
00325 }
00326
00327 return false;
00328 }
00329
00337 protected function _extractSignedToken($strXmlToken)
00338 {
00339 $encryptedData = Zend_InfoCard_Xml_EncryptedData::getInstance($strXmlToken);
00340
00341
00342
00343 switch($encryptedData->getEncryptionMethod()) {
00344 case Zend_InfoCard_Cipher::ENC_AES128CBC:
00345 case Zend_InfoCard_Cipher::ENC_AES256CBC:
00346 break;
00347 default:
00348 require_once 'Zend/InfoCard/Exception.php';
00349 throw new Zend_InfoCard_Exception("Unknown Encryption Method used in the secure token");
00350 }
00351
00352
00353
00354 $keyinfo = $encryptedData->getKeyInfo();
00355
00356 if(!($keyinfo instanceof Zend_InfoCard_Xml_KeyInfo_XmlDSig)) {
00357 require_once 'Zend/InfoCard/Exception.php';
00358 throw new Zend_InfoCard_Exception("Expected a XML digital signature KeyInfo, but was not found");
00359 }
00360
00361
00362 $encryptedKey = $keyinfo->getEncryptedKey();
00363
00364 switch($encryptedKey->getEncryptionMethod()) {
00365 case Zend_InfoCard_Cipher::ENC_RSA:
00366 case Zend_InfoCard_Cipher::ENC_RSA_OAEP_MGF1P:
00367 break;
00368 default:
00369 require_once 'Zend/InfoCard/Exception.php';
00370 throw new Zend_InfoCard_Exception("Unknown Key Encryption Method used in secure token");
00371 }
00372
00373 $securityTokenRef = $encryptedKey->getKeyInfo()->getSecurityTokenReference();
00374
00375 $key_id = $this->_findCertifiatePairByDigest($securityTokenRef->getKeyReference());
00376
00377 if(!$key_id) {
00378 require_once 'Zend/InfoCard/Exception.php';
00379 throw new Zend_InfoCard_Exception("Unable to find key pair used to encrypt symmetric InfoCard Key");
00380 }
00381
00382 $certificate_pair = $this->getCertificatePair($key_id);
00383
00384
00385
00386 if($certificate_pair['type_uri'] != $encryptedKey->getEncryptionMethod()) {
00387 require_once 'Zend/InfoCard/Exception.php';
00388 throw new Zend_InfoCard_Exception("Certificate Pair which matches digest is not of same algorithm type as document, check addCertificate()");
00389 }
00390
00391 $PKcipher = Zend_InfoCard_Cipher::getInstanceByURI($encryptedKey->getEncryptionMethod());
00392
00393 $base64DecodeSupportsStrictParam = version_compare(PHP_VERSION, '5.2.0', '>=');
00394
00395 if ($base64DecodeSupportsStrictParam) {
00396 $keyCipherValueBase64Decoded = base64_decode($encryptedKey->getCipherValue(), true);
00397 } else {
00398 $keyCipherValueBase64Decoded = base64_decode($encryptedKey->getCipherValue());
00399 }
00400
00401 $symmetricKey = $PKcipher->decrypt(
00402 $keyCipherValueBase64Decoded,
00403 file_get_contents($certificate_pair['private']),
00404 $certificate_pair['password']
00405 );
00406
00407 $symCipher = Zend_InfoCard_Cipher::getInstanceByURI($encryptedData->getEncryptionMethod());
00408
00409 if ($base64DecodeSupportsStrictParam) {
00410 $dataCipherValueBase64Decoded = base64_decode($encryptedData->getCipherValue(), true);
00411 } else {
00412 $dataCipherValueBase64Decoded = base64_decode($encryptedData->getCipherValue());
00413 }
00414
00415 $signedToken = $symCipher->decrypt($dataCipherValueBase64Decoded, $symmetricKey);
00416
00417 return $signedToken;
00418 }
00419
00427 public function process($strXmlToken)
00428 {
00429
00430 $retval = new Zend_InfoCard_Claims();
00431
00432 require_once 'Zend/InfoCard/Exception.php';
00433 try {
00434 $signedAssertionsXml = $this->_extractSignedToken($strXmlToken);
00435 } catch(Zend_InfoCard_Exception $e) {
00436 $retval->setError('Failed to extract assertion document');
00437 $retval->setCode(Zend_InfoCard_Claims::RESULT_PROCESSING_FAILURE);
00438 return $retval;
00439 }
00440
00441 try {
00442 $assertions = Zend_InfoCard_Xml_Assertion::getInstance($signedAssertionsXml);
00443 } catch(Zend_InfoCard_Exception $e) {
00444 $retval->setError('Failure processing assertion document');
00445 $retval->setCode(Zend_InfoCard_Claims::RESULT_PROCESSING_FAILURE);
00446 return $retval;
00447 }
00448
00449 if(!($assertions instanceof Zend_InfoCard_Xml_Assertion_Interface)) {
00450 throw new Zend_InfoCard_Exception("Invalid Assertion Object returned");
00451 }
00452
00453 if(!($reference_id = Zend_InfoCard_Xml_Security::validateXMLSignature($assertions->asXML()))) {
00454 $retval->setError("Failure Validating the Signature of the assertion document");
00455 $retval->setCode(Zend_InfoCard_Claims::RESULT_VALIDATION_FAILURE);
00456 return $retval;
00457 }
00458
00459
00460 if($reference_id[0] == '#') {
00461 $reference_id = substr($reference_id, 1);
00462 } else {
00463 $retval->setError("Reference of document signature does not reference the local document");
00464 $retval->setCode(Zend_InfoCard_Claims::RESULT_VALIDATION_FAILURE);
00465 return $retval;
00466 }
00467
00468
00469 if($reference_id != $assertions->getAssertionID()) {
00470 $retval->setError("Reference of document signature does not reference the local document");
00471 $retval->setCode(Zend_InfoCard_Claims::RESULT_VALIDATION_FAILURE);
00472 }
00473
00474
00475 $conditions = $this->getAdapter()->retrieveAssertion($assertions->getAssertionURI(), $assertions->getAssertionID());
00476
00477 if($conditions === false) {
00478 $conditions = $assertions->getConditions();
00479 }
00480
00481
00482 if(is_array($condition_error = $assertions->validateConditions($conditions))) {
00483 $retval->setError("Conditions of assertion document are not met: {$condition_error[1]} ({$condition_error[0]})");
00484 $retval->setCode(Zend_InfoCard_Claims::RESULT_VALIDATION_FAILURE);
00485 }
00486
00487 $attributes = $assertions->getAttributes();
00488
00489 $retval->setClaims($attributes);
00490
00491 if($retval->getCode() == 0) {
00492 $retval->setCode(Zend_InfoCard_Claims::RESULT_SUCCESS);
00493 }
00494
00495 return $retval;
00496 }
00497 }