PageRenderTime 35ms CodeModel.GetById 16ms app.highlight 14ms RepoModel.GetById 1ms app.codeStats 0ms

/library/Zend/Filter/Encrypt/Openssl.php

https://bitbucket.org/nosen/jelly2
PHP | 492 lines | 269 code | 59 blank | 164 comment | 41 complexity | 7bbc706600822519e70cefa45f9f9233 MD5 | raw file
  1<?php
  2/**
  3 * Zend Framework
  4 *
  5 * LICENSE
  6 *
  7 * This source file is subject to the new BSD license that is bundled
  8 * with this package in the file LICENSE.txt.
  9 * It is also available through the world-wide-web at this URL:
 10 * http://framework.zend.com/license/new-bsd
 11 * If you did not receive a copy of the license and are unable to
 12 * obtain it through the world-wide-web, please send an email
 13 * to license@zend.com so we can send you a copy immediately.
 14 *
 15 * @category   Zend
 16 * @package    Zend_Filter
 17 * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
 18 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 19 * @version    $Id: Openssl.php 23775 2011-03-01 17:25:24Z ralph $
 20 */
 21
 22/**
 23 * @see Zend_Filter_Encrypt_Interface
 24 */
 25require_once 'Zend/Filter/Encrypt/Interface.php';
 26
 27/**
 28 * Encryption adapter for openssl
 29 *
 30 * @category   Zend
 31 * @package    Zend_Filter
 32 * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
 33 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 34 */
 35class Zend_Filter_Encrypt_Openssl implements Zend_Filter_Encrypt_Interface
 36{
 37    /**
 38     * Definitions for encryption
 39     * array(
 40     *     'public'   => public keys
 41     *     'private'  => private keys
 42     *     'envelope' => resulting envelope keys
 43     * )
 44     */
 45    protected $_keys = array(
 46        'public'   => array(),
 47        'private'  => array(),
 48        'envelope' => array()
 49    );
 50
 51    /**
 52     * Internal passphrase
 53     *
 54     * @var string
 55     */
 56    protected $_passphrase;
 57
 58    /**
 59     * Internal compression
 60     *
 61     * @var array
 62     */
 63    protected $_compression;
 64
 65    /**
 66     * Internal create package
 67     *
 68     * @var boolean
 69     */
 70    protected $_package = false;
 71
 72    /**
 73     * Class constructor
 74     * Available options
 75     *   'public'      => public key
 76     *   'private'     => private key
 77     *   'envelope'    => envelope key
 78     *   'passphrase'  => passphrase
 79     *   'compression' => compress value with this compression adapter
 80     *   'package'     => pack envelope keys into encrypted string, simplifies decryption
 81     *
 82     * @param string|array $options Options for this adapter
 83     */
 84    public function __construct($options = array())
 85    {
 86        if (!extension_loaded('openssl')) {
 87            require_once 'Zend/Filter/Exception.php';
 88            throw new Zend_Filter_Exception('This filter needs the openssl extension');
 89        }
 90
 91        if ($options instanceof Zend_Config) {
 92            $options = $options->toArray();
 93        }
 94
 95        if (!is_array($options)) {
 96            $options = array('public' => $options);
 97        }
 98
 99        if (array_key_exists('passphrase', $options)) {
100            $this->setPassphrase($options['passphrase']);
101            unset($options['passphrase']);
102        }
103
104        if (array_key_exists('compression', $options)) {
105            $this->setCompression($options['compression']);
106            unset($options['compress']);
107        }
108
109        if (array_key_exists('package', $options)) {
110            $this->setPackage($options['package']);
111            unset($options['package']);
112        }
113
114        $this->_setKeys($options);
115    }
116
117    /**
118     * Sets the encryption keys
119     *
120     * @param  string|array $keys Key with type association
121     * @return Zend_Filter_Encrypt_Openssl
122     */
123    protected function _setKeys($keys)
124    {
125        if (!is_array($keys)) {
126            require_once 'Zend/Filter/Exception.php';
127            throw new Zend_Filter_Exception('Invalid options argument provided to filter');
128        }
129
130        foreach ($keys as $type => $key) {
131            if (is_file($key) and is_readable($key)) {
132                $file = fopen($key, 'r');
133                $cert = fread($file, 8192);
134                fclose($file);
135            } else {
136                $cert = $key;
137                $key  = count($this->_keys[$type]);
138            }
139
140            switch ($type) {
141                case 'public':
142                    $test = openssl_pkey_get_public($cert);
143                    if ($test === false) {
144                        require_once 'Zend/Filter/Exception.php';
145                        throw new Zend_Filter_Exception("Public key '{$cert}' not valid");
146                    }
147
148                    openssl_free_key($test);
149                    $this->_keys['public'][$key] = $cert;
150                    break;
151                case 'private':
152                    $test = openssl_pkey_get_private($cert, $this->_passphrase);
153                    if ($test === false) {
154                        require_once 'Zend/Filter/Exception.php';
155                        throw new Zend_Filter_Exception("Private key '{$cert}' not valid");
156                    }
157
158                    openssl_free_key($test);
159                    $this->_keys['private'][$key] = $cert;
160                    break;
161                case 'envelope':
162                    $this->_keys['envelope'][$key] = $cert;
163                    break;
164                default:
165                    break;
166            }
167        }
168
169        return $this;
170    }
171
172    /**
173     * Returns all public keys
174     *
175     * @return array
176     */
177    public function getPublicKey()
178    {
179        $key = $this->_keys['public'];
180        return $key;
181    }
182
183    /**
184     * Sets public keys
185     *
186     * @param  string|array $key Public keys
187     * @return Zend_Filter_Encrypt_Openssl
188     */
189    public function setPublicKey($key)
190    {
191        if (is_array($key)) {
192            foreach($key as $type => $option) {
193                if ($type !== 'public') {
194                    $key['public'] = $option;
195                    unset($key[$type]);
196                }
197            }
198        } else {
199            $key = array('public' => $key);
200        }
201
202        return $this->_setKeys($key);
203    }
204
205    /**
206     * Returns all private keys
207     *
208     * @return array
209     */
210    public function getPrivateKey()
211    {
212        $key = $this->_keys['private'];
213        return $key;
214    }
215
216    /**
217     * Sets private keys
218     *
219     * @param  string $key Private key
220     * @param  string $passphrase
221     * @return Zend_Filter_Encrypt_Openssl
222     */
223    public function setPrivateKey($key, $passphrase = null)
224    {
225        if (is_array($key)) {
226            foreach($key as $type => $option) {
227                if ($type !== 'private') {
228                    $key['private'] = $option;
229                    unset($key[$type]);
230                }
231            }
232        } else {
233            $key = array('private' => $key);
234        }
235
236        if ($passphrase !== null) {
237            $this->setPassphrase($passphrase);
238        }
239
240        return $this->_setKeys($key);
241    }
242
243    /**
244     * Returns all envelope keys
245     *
246     * @return array
247     */
248    public function getEnvelopeKey()
249    {
250        $key = $this->_keys['envelope'];
251        return $key;
252    }
253
254    /**
255     * Sets envelope keys
256     *
257     * @param  string|array $options Envelope keys
258     * @return Zend_Filter_Encrypt_Openssl
259     */
260    public function setEnvelopeKey($key)
261    {
262        if (is_array($key)) {
263            foreach($key as $type => $option) {
264                if ($type !== 'envelope') {
265                    $key['envelope'] = $option;
266                    unset($key[$type]);
267                }
268            }
269        } else {
270            $key = array('envelope' => $key);
271        }
272
273        return $this->_setKeys($key);
274    }
275
276    /**
277     * Returns the passphrase
278     *
279     * @return string
280     */
281    public function getPassphrase()
282    {
283        return $this->_passphrase;
284    }
285
286    /**
287     * Sets a new passphrase
288     *
289     * @param string $passphrase
290     * @return Zend_Filter_Encrypt_Openssl
291     */
292    public function setPassphrase($passphrase)
293    {
294        $this->_passphrase = $passphrase;
295        return $this;
296    }
297
298    /**
299     * Returns the compression
300     *
301     * @return array
302     */
303    public function getCompression()
304    {
305        return $this->_compression;
306    }
307
308    /**
309     * Sets a internal compression for values to encrypt
310     *
311     * @param string|array $compression
312     * @return Zend_Filter_Encrypt_Openssl
313     */
314    public function setCompression($compression)
315    {
316        if (is_string($this->_compression)) {
317            $compression = array('adapter' => $compression);
318        }
319
320        $this->_compression = $compression;
321        return $this;
322    }
323
324    /**
325     * Returns if header should be packaged
326     *
327     * @return boolean
328     */
329    public function getPackage()
330    {
331        return $this->_package;
332    }
333
334    /**
335     * Sets if the envelope keys should be included in the encrypted value
336     *
337     * @param boolean $package
338     * @return Zend_Filter_Encrypt_Openssl
339     */
340    public function setPackage($package)
341    {
342        $this->_package = (boolean) $package;
343        return $this;
344    }
345
346    /**
347     * Encrypts $value with the defined settings
348     * Note that you also need the "encrypted" keys to be able to decrypt
349     *
350     * @param  string $value Content to encrypt
351     * @return string The encrypted content
352     * @throws Zend_Filter_Exception
353     */
354    public function encrypt($value)
355    {
356        $encrypted     = array();
357        $encryptedkeys = array();
358
359        if (count($this->_keys['public']) == 0) {
360            require_once 'Zend/Filter/Exception.php';
361            throw new Zend_Filter_Exception('Openssl can not encrypt without public keys');
362        }
363
364        $keys         = array();
365        $fingerprints = array();
366        $count        = -1;
367        foreach($this->_keys['public'] as $key => $cert) {
368            $keys[$key] = openssl_pkey_get_public($cert);
369            if ($this->_package) {
370                $details = openssl_pkey_get_details($keys[$key]);
371                if ($details === false) {
372                    $details = array('key' => 'ZendFramework');
373                }
374
375                ++$count;
376                $fingerprints[$count] = md5($details['key']);
377            }
378        }
379
380        // compress prior to encryption
381        if (!empty($this->_compression)) {
382            require_once 'Zend/Filter/Compress.php';
383            $compress = new Zend_Filter_Compress($this->_compression);
384            $value    = $compress->filter($value);
385        }
386
387        $crypt  = openssl_seal($value, $encrypted, $encryptedkeys, $keys);
388        foreach ($keys as $key) {
389            openssl_free_key($key);
390        }
391
392        if ($crypt === false) {
393            require_once 'Zend/Filter/Exception.php';
394            throw new Zend_Filter_Exception('Openssl was not able to encrypt your content with the given options');
395        }
396
397        $this->_keys['envelope'] = $encryptedkeys;
398
399        // Pack data and envelope keys into single string
400        if ($this->_package) {
401            $header = pack('n', count($this->_keys['envelope']));
402            foreach($this->_keys['envelope'] as $key => $envKey) {
403                $header .= pack('H32n', $fingerprints[$key], strlen($envKey)) . $envKey;
404            }
405
406            $encrypted = $header . $encrypted;
407        }
408
409        return $encrypted;
410    }
411
412    /**
413     * Defined by Zend_Filter_Interface
414     *
415     * Decrypts $value with the defined settings
416     *
417     * @param  string $value Content to decrypt
418     * @return string The decrypted content
419     * @throws Zend_Filter_Exception
420     */
421    public function decrypt($value)
422    {
423        $decrypted = "";
424        $envelope  = current($this->getEnvelopeKey());
425
426        if (count($this->_keys['private']) !== 1) {
427            require_once 'Zend/Filter/Exception.php';
428            throw new Zend_Filter_Exception('Please give a private key for decryption with Openssl');
429        }
430
431        if (!$this->_package && empty($envelope)) {
432            require_once 'Zend/Filter/Exception.php';
433            throw new Zend_Filter_Exception('Please give a envelope key for decryption with Openssl');
434        }
435
436        foreach($this->_keys['private'] as $key => $cert) {
437            $keys = openssl_pkey_get_private($cert, $this->getPassphrase());
438        }
439
440        if ($this->_package) {
441            $details = openssl_pkey_get_details($keys);
442            if ($details !== false) {
443                $fingerprint = md5($details['key']);
444            } else {
445                $fingerprint = md5("ZendFramework");
446            }
447
448            $count = unpack('ncount', $value);
449            $count = $count['count'];
450            $length  = 2;
451            for($i = $count; $i > 0; --$i) {
452                $header = unpack('H32print/nsize', substr($value, $length, 18));
453                $length  += 18;
454                if ($header['print'] == $fingerprint) {
455                    $envelope = substr($value, $length, $header['size']);
456                }
457
458                $length += $header['size'];
459            }
460
461            // remainder of string is the value to decrypt
462            $value = substr($value, $length);
463        }
464
465        $crypt  = openssl_open($value, $decrypted, $envelope, $keys);
466        openssl_free_key($keys);
467
468        if ($crypt === false) {
469            require_once 'Zend/Filter/Exception.php';
470            throw new Zend_Filter_Exception('Openssl was not able to decrypt you content with the given options');
471        }
472
473        // decompress after decryption
474        if (!empty($this->_compression)) {
475            require_once 'Zend/Filter/Decompress.php';
476            $decompress = new Zend_Filter_Decompress($this->_compression);
477            $decrypted  = $decompress->filter($decrypted);
478        }
479
480        return $decrypted;
481    }
482
483    /**
484     * Returns the adapter name
485     *
486     * @return string
487     */
488    public function toString()
489    {
490        return 'Openssl';
491    }
492}