File indexing completed on 2024-12-22 05:36:41
0001 <?php 0002 /** 0003 * Zend Framework 0004 * 0005 * LICENSE 0006 * 0007 * This source file is subject to the new BSD license that is bundled 0008 * with this package in the file LICENSE.txt. 0009 * It is also available through the world-wide-web at this URL: 0010 * http://framework.zend.com/license/new-bsd 0011 * If you did not receive a copy of the license and are unable to 0012 * obtain it through the world-wide-web, please send an email 0013 * to license@zend.com so we can send you a copy immediately. 0014 * 0015 * @category Zend 0016 * @package Zend_Filter 0017 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 0018 * @license http://framework.zend.com/license/new-bsd New BSD License 0019 * @version $Id$ 0020 */ 0021 0022 /** 0023 * @see Zend_Filter_Encrypt_Interface 0024 */ 0025 // require_once 'Zend/Filter/Encrypt/Interface.php'; 0026 0027 /** 0028 * Encryption adapter for openssl 0029 * 0030 * @category Zend 0031 * @package Zend_Filter 0032 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 0033 * @license http://framework.zend.com/license/new-bsd New BSD License 0034 */ 0035 class Zend_Filter_Encrypt_Openssl implements Zend_Filter_Encrypt_Interface 0036 { 0037 /** 0038 * Definitions for encryption 0039 * array( 0040 * 'public' => public keys 0041 * 'private' => private keys 0042 * 'envelope' => resulting envelope keys 0043 * ) 0044 */ 0045 protected $_keys = array( 0046 'public' => array(), 0047 'private' => array(), 0048 'envelope' => array() 0049 ); 0050 0051 /** 0052 * Internal passphrase 0053 * 0054 * @var string 0055 */ 0056 protected $_passphrase; 0057 0058 /** 0059 * Internal compression 0060 * 0061 * @var array 0062 */ 0063 protected $_compression; 0064 0065 /** 0066 * Internal create package 0067 * 0068 * @var boolean 0069 */ 0070 protected $_package = false; 0071 0072 /** 0073 * Class constructor 0074 * Available options 0075 * 'public' => public key 0076 * 'private' => private key 0077 * 'envelope' => envelope key 0078 * 'passphrase' => passphrase 0079 * 'compression' => compress value with this compression adapter 0080 * 'package' => pack envelope keys into encrypted string, simplifies decryption 0081 * 0082 * @param string|array $options Options for this adapter 0083 */ 0084 public function __construct($options = array()) 0085 { 0086 if (!extension_loaded('openssl')) { 0087 // require_once 'Zend/Filter/Exception.php'; 0088 throw new Zend_Filter_Exception('This filter needs the openssl extension'); 0089 } 0090 0091 if ($options instanceof Zend_Config) { 0092 $options = $options->toArray(); 0093 } 0094 0095 if (!is_array($options)) { 0096 $options = array('public' => $options); 0097 } 0098 0099 if (array_key_exists('passphrase', $options)) { 0100 $this->setPassphrase($options['passphrase']); 0101 unset($options['passphrase']); 0102 } 0103 0104 if (array_key_exists('compression', $options)) { 0105 $this->setCompression($options['compression']); 0106 unset($options['compress']); 0107 } 0108 0109 if (array_key_exists('package', $options)) { 0110 $this->setPackage($options['package']); 0111 unset($options['package']); 0112 } 0113 0114 $this->_setKeys($options); 0115 } 0116 0117 /** 0118 * Sets the encryption keys 0119 * 0120 * @param string|array $keys Key with type association 0121 * @return Zend_Filter_Encrypt_Openssl 0122 */ 0123 protected function _setKeys($keys) 0124 { 0125 if (!is_array($keys)) { 0126 // require_once 'Zend/Filter/Exception.php'; 0127 throw new Zend_Filter_Exception('Invalid options argument provided to filter'); 0128 } 0129 0130 foreach ($keys as $type => $key) { 0131 if (ctype_print($key) && is_file(realpath($key)) && is_readable($key)) { 0132 $file = fopen($key, 'r'); 0133 $cert = fread($file, 8192); 0134 fclose($file); 0135 } else { 0136 $cert = $key; 0137 $key = count($this->_keys[$type]); 0138 } 0139 0140 switch ($type) { 0141 case 'public': 0142 $test = openssl_pkey_get_public($cert); 0143 if ($test === false) { 0144 // require_once 'Zend/Filter/Exception.php'; 0145 throw new Zend_Filter_Exception("Public key '{$cert}' not valid"); 0146 } 0147 0148 openssl_free_key($test); 0149 $this->_keys['public'][$key] = $cert; 0150 break; 0151 case 'private': 0152 $test = openssl_pkey_get_private($cert, $this->_passphrase); 0153 if ($test === false) { 0154 // require_once 'Zend/Filter/Exception.php'; 0155 throw new Zend_Filter_Exception("Private key '{$cert}' not valid"); 0156 } 0157 0158 openssl_free_key($test); 0159 $this->_keys['private'][$key] = $cert; 0160 break; 0161 case 'envelope': 0162 $this->_keys['envelope'][$key] = $cert; 0163 break; 0164 default: 0165 break; 0166 } 0167 } 0168 0169 return $this; 0170 } 0171 0172 /** 0173 * Returns all public keys 0174 * 0175 * @return array 0176 */ 0177 public function getPublicKey() 0178 { 0179 $key = $this->_keys['public']; 0180 return $key; 0181 } 0182 0183 /** 0184 * Sets public keys 0185 * 0186 * @param string|array $key Public keys 0187 * @return Zend_Filter_Encrypt_Openssl 0188 */ 0189 public function setPublicKey($key) 0190 { 0191 if (is_array($key)) { 0192 foreach($key as $type => $option) { 0193 if ($type !== 'public') { 0194 $key['public'] = $option; 0195 unset($key[$type]); 0196 } 0197 } 0198 } else { 0199 $key = array('public' => $key); 0200 } 0201 0202 return $this->_setKeys($key); 0203 } 0204 0205 /** 0206 * Returns all private keys 0207 * 0208 * @return array 0209 */ 0210 public function getPrivateKey() 0211 { 0212 $key = $this->_keys['private']; 0213 return $key; 0214 } 0215 0216 /** 0217 * Sets private keys 0218 * 0219 * @param string $key Private key 0220 * @param string $passphrase 0221 * @return Zend_Filter_Encrypt_Openssl 0222 */ 0223 public function setPrivateKey($key, $passphrase = null) 0224 { 0225 if (is_array($key)) { 0226 foreach($key as $type => $option) { 0227 if ($type !== 'private') { 0228 $key['private'] = $option; 0229 unset($key[$type]); 0230 } 0231 } 0232 } else { 0233 $key = array('private' => $key); 0234 } 0235 0236 if ($passphrase !== null) { 0237 $this->setPassphrase($passphrase); 0238 } 0239 0240 return $this->_setKeys($key); 0241 } 0242 0243 /** 0244 * Returns all envelope keys 0245 * 0246 * @return array 0247 */ 0248 public function getEnvelopeKey() 0249 { 0250 $key = $this->_keys['envelope']; 0251 return $key; 0252 } 0253 0254 /** 0255 * Sets envelope keys 0256 * 0257 * @param string|array $options Envelope keys 0258 * @return Zend_Filter_Encrypt_Openssl 0259 */ 0260 public function setEnvelopeKey($key) 0261 { 0262 if (is_array($key)) { 0263 foreach($key as $type => $option) { 0264 if ($type !== 'envelope') { 0265 $key['envelope'] = $option; 0266 unset($key[$type]); 0267 } 0268 } 0269 } else { 0270 $key = array('envelope' => $key); 0271 } 0272 0273 return $this->_setKeys($key); 0274 } 0275 0276 /** 0277 * Returns the passphrase 0278 * 0279 * @return string 0280 */ 0281 public function getPassphrase() 0282 { 0283 return $this->_passphrase; 0284 } 0285 0286 /** 0287 * Sets a new passphrase 0288 * 0289 * @param string $passphrase 0290 * @return Zend_Filter_Encrypt_Openssl 0291 */ 0292 public function setPassphrase($passphrase) 0293 { 0294 $this->_passphrase = $passphrase; 0295 return $this; 0296 } 0297 0298 /** 0299 * Returns the compression 0300 * 0301 * @return array 0302 */ 0303 public function getCompression() 0304 { 0305 return $this->_compression; 0306 } 0307 0308 /** 0309 * Sets a internal compression for values to encrypt 0310 * 0311 * @param string|array $compression 0312 * @return Zend_Filter_Encrypt_Openssl 0313 */ 0314 public function setCompression($compression) 0315 { 0316 if (is_string($this->_compression)) { 0317 $compression = array('adapter' => $compression); 0318 } 0319 0320 $this->_compression = $compression; 0321 return $this; 0322 } 0323 0324 /** 0325 * Returns if header should be packaged 0326 * 0327 * @return boolean 0328 */ 0329 public function getPackage() 0330 { 0331 return $this->_package; 0332 } 0333 0334 /** 0335 * Sets if the envelope keys should be included in the encrypted value 0336 * 0337 * @param boolean $package 0338 * @return Zend_Filter_Encrypt_Openssl 0339 */ 0340 public function setPackage($package) 0341 { 0342 $this->_package = (boolean) $package; 0343 return $this; 0344 } 0345 0346 /** 0347 * Encrypts $value with the defined settings 0348 * Note that you also need the "encrypted" keys to be able to decrypt 0349 * 0350 * @param string $value Content to encrypt 0351 * @return string The encrypted content 0352 * @throws Zend_Filter_Exception 0353 */ 0354 public function encrypt($value) 0355 { 0356 $encrypted = array(); 0357 $encryptedkeys = array(); 0358 0359 if (count($this->_keys['public']) == 0) { 0360 // require_once 'Zend/Filter/Exception.php'; 0361 throw new Zend_Filter_Exception('Openssl can not encrypt without public keys'); 0362 } 0363 0364 $keys = array(); 0365 $fingerprints = array(); 0366 $count = -1; 0367 foreach($this->_keys['public'] as $key => $cert) { 0368 $keys[$key] = openssl_pkey_get_public($cert); 0369 if ($this->_package) { 0370 $details = openssl_pkey_get_details($keys[$key]); 0371 if ($details === false) { 0372 $details = array('key' => 'ZendFramework'); 0373 } 0374 0375 ++$count; 0376 $fingerprints[$count] = md5($details['key']); 0377 } 0378 } 0379 0380 // compress prior to encryption 0381 if (!empty($this->_compression)) { 0382 // require_once 'Zend/Filter/Compress.php'; 0383 $compress = new Zend_Filter_Compress($this->_compression); 0384 $value = $compress->filter($value); 0385 } 0386 0387 $crypt = openssl_seal($value, $encrypted, $encryptedkeys, $keys); 0388 foreach ($keys as $key) { 0389 openssl_free_key($key); 0390 } 0391 0392 if ($crypt === false) { 0393 // require_once 'Zend/Filter/Exception.php'; 0394 throw new Zend_Filter_Exception('Openssl was not able to encrypt your content with the given options'); 0395 } 0396 0397 $this->_keys['envelope'] = $encryptedkeys; 0398 0399 // Pack data and envelope keys into single string 0400 if ($this->_package) { 0401 $header = pack('n', count($this->_keys['envelope'])); 0402 foreach($this->_keys['envelope'] as $key => $envKey) { 0403 $header .= pack('H32n', $fingerprints[$key], strlen($envKey)) . $envKey; 0404 } 0405 0406 $encrypted = $header . $encrypted; 0407 } 0408 0409 return $encrypted; 0410 } 0411 0412 /** 0413 * Defined by Zend_Filter_Interface 0414 * 0415 * Decrypts $value with the defined settings 0416 * 0417 * @param string $value Content to decrypt 0418 * @return string The decrypted content 0419 * @throws Zend_Filter_Exception 0420 */ 0421 public function decrypt($value) 0422 { 0423 $decrypted = ""; 0424 $envelope = current($this->getEnvelopeKey()); 0425 0426 if (count($this->_keys['private']) !== 1) { 0427 // require_once 'Zend/Filter/Exception.php'; 0428 throw new Zend_Filter_Exception('Please give a private key for decryption with Openssl'); 0429 } 0430 0431 if (!$this->_package && empty($envelope)) { 0432 // require_once 'Zend/Filter/Exception.php'; 0433 throw new Zend_Filter_Exception('Please give a envelope key for decryption with Openssl'); 0434 } 0435 0436 foreach($this->_keys['private'] as $key => $cert) { 0437 $keys = openssl_pkey_get_private($cert, $this->getPassphrase()); 0438 } 0439 0440 if ($this->_package) { 0441 $details = openssl_pkey_get_details($keys); 0442 if ($details !== false) { 0443 $fingerprint = md5($details['key']); 0444 } else { 0445 $fingerprint = md5("ZendFramework"); 0446 } 0447 0448 $count = unpack('ncount', $value); 0449 $count = $count['count']; 0450 $length = 2; 0451 for($i = $count; $i > 0; --$i) { 0452 $header = unpack('H32print/nsize', substr($value, $length, 18)); 0453 $length += 18; 0454 if ($header['print'] == $fingerprint) { 0455 $envelope = substr($value, $length, $header['size']); 0456 } 0457 0458 $length += $header['size']; 0459 } 0460 0461 // remainder of string is the value to decrypt 0462 $value = substr($value, $length); 0463 } 0464 0465 $crypt = openssl_open($value, $decrypted, $envelope, $keys); 0466 openssl_free_key($keys); 0467 0468 if ($crypt === false) { 0469 // require_once 'Zend/Filter/Exception.php'; 0470 throw new Zend_Filter_Exception('Openssl was not able to decrypt you content with the given options'); 0471 } 0472 0473 // decompress after decryption 0474 if (!empty($this->_compression)) { 0475 // require_once 'Zend/Filter/Decompress.php'; 0476 $decompress = new Zend_Filter_Decompress($this->_compression); 0477 $decrypted = $decompress->filter($decrypted); 0478 } 0479 0480 return $decrypted; 0481 } 0482 0483 /** 0484 * Returns the adapter name 0485 * 0486 * @return string 0487 */ 0488 public function toString() 0489 { 0490 return 'Openssl'; 0491 } 0492 }