PageRenderTime 67ms CodeModel.GetById 55ms app.highlight 8ms RepoModel.GetById 1ms app.codeStats 0ms

/s3db3.5.10/pearlib/RSACrypt/RSA.php

https://code.google.com/p/s3db/
PHP | 524 lines | 215 code | 32 blank | 277 comment | 49 complexity | 9b911d1a743cabcd2db6f3f5607c4f66 MD5 | raw file
  1<?php
  2/**
  3 * Crypt_RSA allows to do following operations:
  4 *     - key pair generation
  5 *     - encryption and decryption
  6 *     - signing and sign validation
  7 *
  8 * PHP versions 4 and 5
  9 *
 10 * LICENSE: This source file is subject to version 3.0 of the PHP license
 11 * that is available through the world-wide-web at the following URI:
 12 * http://www.php.net/license/3_0.txt.  If you did not receive a copy of
 13 * the PHP License and are unable to obtain it through the web, please
 14 * send a note to license@php.net so we can mail you a copy immediately.
 15 *
 16 * @category   Encryption
 17 * @package    Crypt_RSA
 18 * @author     Alexander Valyalkin <valyala@gmail.com>
 19 * @copyright  2005, 2006 Alexander Valyalkin
 20 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
 21 * @version    1.2.0b
 22 * @link       http://pear.php.net/package/Crypt_RSA
 23 */
 24
 25/**
 26 * RSA error handling facilities
 27 */
 28include_once 'pearlib/RSACrypt/Crypt/RSA/ErrorHandler.php';
 29
 30/**
 31 * loader for math wrappers
 32 */
 33require_once 'Crypt/RSA/MathLoader.php';
 34
 35/**
 36 * helper class for mange single key
 37 */
 38require_once 'Crypt/RSA/Key.php';
 39
 40/**
 41 * helper class for manage key pair
 42 */
 43require_once 'Crypt/RSA/KeyPair.php';
 44
 45/**
 46 * Crypt_RSA class, derived from Crypt_RSA_ErrorHandler
 47 *
 48 * Provides the following functions:
 49 *  - setParams($params) - sets parameters of current object
 50 *  - encrypt($plain_data, $key = null) - encrypts data
 51 *  - decrypt($enc_data, $key = null) - decrypts data
 52 *  - createSign($doc, $private_key = null) - signs document by private key
 53 *  - validateSign($doc, $signature, $public_key = null) - validates signature of document
 54 *
 55 * Example usage:
 56 *     // creating an error handler
 57 *     $error_handler = create_function('$obj', 'echo "error: ", $obj->getMessage(), "\n"');
 58 *
 59 *     // 1024-bit key pair generation
 60 *     $key_pair = new Crypt_RSA_KeyPair(1024);
 61 *
 62 *     // check consistence of Crypt_RSA_KeyPair object
 63 *     $error_handler($key_pair);
 64 *
 65 *     // creating Crypt_RSA object
 66 *     $rsa_obj = new Crypt_RSA;
 67 *
 68 *     // check consistence of Crypt_RSA object
 69 *     $error_handler($rsa_obj);
 70 *
 71 *     // set error handler on Crypt_RSA object ( see Crypt/RSA/ErrorHandler.php for details )
 72 *     $rsa_obj->setErrorHandler($error_handler);
 73 *
 74 *     // encryption (usually using public key)
 75 *     $enc_data = $rsa_obj->encrypt($plain_data, $key_pair->getPublicKey());
 76 *
 77 *     // decryption (usually using private key)
 78 *     $plain_data = $rsa_obj->decrypt($enc_data, $key_pair->getPrivateKey());
 79 *
 80 *     // signing
 81 *     $signature = $rsa_obj->createSign($document, $key_pair->getPrivateKey());
 82 *
 83 *     // signature checking
 84 *     $is_valid = $rsa_obj->validateSign($document, $signature, $key_pair->getPublicKey());
 85 *
 86 *     // signing many documents by one private key
 87 *     $rsa_obj = new Crypt_RSA(array('private_key' => $key_pair->getPrivateKey()));
 88 *     // check consistence of Crypt_RSA object
 89 *     $error_handler($rsa_obj);
 90 *     // set error handler ( see Crypt/RSA/ErrorHandler.php for details )
 91 *     $rsa_obj->setErrorHandler($error_handler);
 92 *     // sign many documents
 93 *     $sign_1 = $rsa_obj->sign($doc_1);
 94 *     $sign_2 = $rsa_obj->sign($doc_2);
 95 *     //...
 96 *     $sign_n = $rsa_obj->sign($doc_n);
 97 *
 98 *     // changing default hash function, which is used for sign
 99 *     // creating/validation
100 *     $rsa_obj->setParams(array('hash_func' => 'md5'));
101 *
102 *     // using factory() method instead of constructor (it returns PEAR_Error object on failure)
103 *     $rsa_obj = &Crypt_RSA::factory();
104 *     if (PEAR::isError($rsa_obj)) {
105 *         echo "error: ", $rsa_obj->getMessage(), "\n";
106 *     }
107 *
108 * @category   Encryption
109 * @package    Crypt_RSA
110 * @author     Alexander Valyalkin <valyala@gmail.com>
111 * @copyright  2005, 2006 Alexander Valyalkin
112 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
113 * @link       http://pear.php.net/package/Crypt_RSA
114 * @version    @package_version@
115 * @access     public
116 */
117class Crypt_RSA extends Crypt_RSA_ErrorHandler
118{
119    /**
120     * Reference to math wrapper, which is used to
121     * manipulate large integers in RSA algorithm.
122     *
123     * @var object of Crypt_RSA_Math_* class
124     * @access private
125     */
126    var $_math_obj;
127
128    /**
129     * key for encryption, which is used by encrypt() method
130     *
131     * @var object of Crypt_RSA_KEY class
132     * @access private
133     */
134    var $_enc_key;
135
136    /**
137     * key for decryption, which is used by decrypt() method
138     *
139     * @var object of Crypt_RSA_KEY class
140     * @access private
141     */
142    var $_dec_key;
143
144    /**
145     * public key, which is used by validateSign() method
146     *
147     * @var object of Crypt_RSA_KEY class
148     * @access private
149     */
150    var $_public_key;
151
152    /**
153     * private key, which is used by createSign() method
154     *
155     * @var object of Crypt_RSA_KEY class
156     * @access private
157     */
158    var $_private_key;
159
160    /**
161     * name of hash function, which is used by validateSign()
162     * and createSign() methods. Default hash function is SHA-1
163     *
164     * @var string
165     * @access private
166     */
167    var $_hash_func = 'sha1';
168
169    /**
170     * Crypt_RSA constructor.
171     *
172     * @param array $params
173     *        Optional associative array of parameters, such as:
174     *        enc_key, dec_key, private_key, public_key, hash_func.
175     *        See setParams() method for more detailed description of
176     *        these parameters.
177     * @param string $wrapper_name
178     *        Name of math wrapper, which will be used to
179     *        perform different operations with big integers.
180     *        See contents of Crypt/RSA/Math folder for examples of wrappers.
181     *        Read docs/Crypt_RSA/docs/math_wrappers.txt for details.
182     * @param string $error_handler   name of error handler function
183     *
184     * @access public
185     */
186    function Crypt_RSA($params = null, $wrapper_name = 'default', $error_handler = '')
187    {
188        // set error handler
189        $this->setErrorHandler($error_handler);
190        // try to load math wrapper
191        $obj = &Crypt_RSA_MathLoader::loadWrapper($wrapper_name);
192        if ($this->isError($obj)) {
193            // error during loading of math wrapper
194            // Crypt_RSA object is partially constructed.
195            $this->pushError($obj);
196            return;
197        }
198        $this->_math_obj = &$obj;
199
200        if (!is_null($params)) {
201            if (!$this->setParams($params)) {
202                // error in Crypt_RSA::setParams() function
203                return;
204            }
205        }
206    }
207
208    /**
209     * Crypt_RSA factory.
210     *
211     * @param array $params
212     *        Optional associative array of parameters, such as:
213     *        enc_key, dec_key, private_key, public_key, hash_func.
214     *        See setParams() method for more detailed description of
215     *        these parameters.
216     * @param string $wrapper_name
217     *        Name of math wrapper, which will be used to
218     *        perform different operations with big integers.
219     *        See contents of Crypt/RSA/Math folder for examples of wrappers.
220     *        Read docs/Crypt_RSA/docs/math_wrappers.txt for details.
221     * @param string $error_handler   name of error handler function
222     *
223     * @return object  new Crypt_RSA object on success or PEAR_Error object on failure
224     * @access public
225     */
226    function &factory($params = null, $wrapper_name = 'default', $error_handler = '')
227    {
228        $obj = &new Crypt_RSA($params, $wrapper_name, $error_handler);
229        if ($obj->isError()) {
230            // error during creating a new object. Retrurn PEAR_Error object
231            return $obj->getLastError();
232        }
233        // object created successfully. Return it
234        return $obj;
235    }
236
237    /**
238     * Accepts any combination of available parameters as associative array:
239     *     enc_key - encryption key for encrypt() method
240     *     dec_key - decryption key for decrypt() method
241     *     public_key - key for validateSign() method
242     *     private_key - key for createSign() method
243     *     hash_func - name of hash function, which will be used to create and validate sign
244     *
245     * @param array $params
246     *        associative array of permitted parameters (see above)
247     *
248     * @return bool   true on success or false on error
249     * @access public
250     */
251    function setParams($params)
252    {
253        if (!is_array($params)) {
254            $this->pushError('parameters must be passed to function as associative array', CRYPT_RSA_ERROR_WRONG_PARAMS);
255            return false;
256        }
257
258        if (isset($params['enc_key'])) {
259            if (Crypt_RSA_Key::isValid($params['enc_key'])) {
260                $this->_enc_key = $params['enc_key'];
261            }
262            else {
263                $this->pushError('wrong encryption key. It must be an object of Crypt_RSA_Key class', CRYPT_RSA_ERROR_WRONG_KEY);
264                return false;
265            }
266        }
267        if (isset($params['dec_key'])) {
268            if (Crypt_RSA_Key::isValid($params['dec_key'])) {
269                $this->_dec_key = $params['dec_key'];
270            }
271            else {
272                $this->pushError('wrong decryption key. It must be an object of Crypt_RSA_Key class', CRYPT_RSA_ERROR_WRONG_KEY);
273                return false;
274            }
275        }
276        if (isset($params['private_key'])) {
277            if (Crypt_RSA_Key::isValid($params['private_key'])) {
278                if ($params['private_key']->getKeyType() != 'private') {
279                    $this->pushError('private key must have "private" attribute', CRYPT_RSA_ERROR_WRONG_KEY_TYPE);
280                    return false;
281                }
282                $this->_private_key = $params['private_key'];
283            }
284            else {
285                $this->pushError('wrong private key. It must be an object of Crypt_RSA_Key class', CRYPT_RSA_ERROR_WRONG_KEY);
286                return false;
287            }
288        }
289        if (isset($params['public_key'])) {
290            if (Crypt_RSA_Key::isValid($params['public_key'])) {
291                if ($params['public_key']->getKeyType() != 'public') {
292                    $this->pushError('public key must have "public" attribute', CRYPT_RSA_ERROR_WRONG_KEY_TYPE);
293                    return false;
294                }
295                $this->_public_key = $params['public_key'];
296            }
297            else {
298                $this->pushError('wrong public key. It must be an object of Crypt_RSA_Key class', CRYPT_RSA_ERROR_WRONG_KEY);
299                return false;
300            }
301        }
302        if (isset($params['hash_func'])) {
303            if (!function_exists($params['hash_func'])) {
304                $this->pushError('cannot find hash function with name [' . $params['hash_func'] . ']', CRYPT_RSA_ERROR_WRONG_HASH_FUNC);
305                return false;
306            }
307            $this->_hash_func = $params['hash_func'];
308        }
309        return true; // all ok
310    }
311
312    /**
313     * Ecnrypts $plain_data by the key $this->_enc_key or $key.
314     *
315     * @param string $plain_data  data, which must be encrypted
316     * @param object $key         encryption key (object of Crypt_RSA_Key class)
317     * @return mixed
318     *         encrypted data as string on success or false on error
319     *
320     * @access public
321     */
322    function encrypt($plain_data, $key = null)
323    {
324        $enc_data = $this->encryptBinary($plain_data, $key);
325        if ($enc_data !== false) {
326            return base64_encode($enc_data);
327        }
328        // error during encripting data
329        return false;
330    }
331
332    /**
333     * Ecnrypts $plain_data by the key $this->_enc_key or $key.
334     *
335     * @param string $plain_data  data, which must be encrypted
336     * @param object $key         encryption key (object of Crypt_RSA_Key class)
337     * @return mixed
338     *         encrypted data as binary string on success or false on error
339     *
340     * @access public
341     */
342    function encryptBinary($plain_data, $key = null)
343    {
344        if (is_null($key)) {
345            // use current encryption key
346            $key = $this->_enc_key;
347        }
348        else if (!Crypt_RSA_Key::isValid($key)) {
349            $this->pushError('invalid encryption key. It must be an object of Crypt_RSA_Key class', CRYPT_RSA_ERROR_WRONG_KEY);
350            return false;
351        }
352
353        // append tail \x01 to plain data. It needs for correctly decrypting of data
354        $plain_data .= "\x01";
355
356        $plain_data = $this->_math_obj->bin2int($plain_data);
357        $exp = $this->_math_obj->bin2int($key->getExponent());
358        $modulus = $this->_math_obj->bin2int($key->getModulus());
359
360        // divide plain data into chunks
361        $data_len = $this->_math_obj->bitLen($plain_data);
362        $chunk_len = $key->getKeyLength() - 1;
363        $block_len = (int) ceil($chunk_len / 8);
364        $curr_pos = 0;
365        $enc_data = '';
366        while ($curr_pos < $data_len) {
367            $tmp = $this->_math_obj->subint($plain_data, $curr_pos, $chunk_len);
368            $enc_data .= str_pad(
369                $this->_math_obj->int2bin($this->_math_obj->powmod($tmp, $exp, $modulus)),
370                $block_len,
371                "\0"
372            );
373            $curr_pos += $chunk_len;
374        }
375        return $enc_data;
376    }
377
378    /**
379     * Decrypts $enc_data by the key $this->_dec_key or $key.
380     *
381     * @param string $enc_data  encrypted data as string
382     * @param object $key       decryption key (object of RSA_Crypt_Key class)
383     * @return mixed
384     *         decrypted data as string on success or false on error
385     *
386     * @access public
387     */
388    function decrypt($enc_data, $key = null)
389    {
390        $enc_data = base64_decode($enc_data);
391        return $this->decryptBinary($enc_data, $key);
392    }
393
394    /**
395     * Decrypts $enc_data by the key $this->_dec_key or $key.
396     *
397     * @param string $enc_data  encrypted data as binary string
398     * @param object $key       decryption key (object of RSA_Crypt_Key class)
399     * @return mixed
400     *         decrypted data as string on success or false on error
401     *
402     * @access public
403     */
404    function decryptBinary($enc_data, $key = null)
405    {
406        if (is_null($key)) {
407            // use current decryption key
408            $key = $this->_dec_key;
409        }
410        else if (!Crypt_RSA_Key::isValid($key)) {
411            $this->pushError('invalid decryption key. It must be an object of Crypt_RSA_Key class', CRYPT_RSA_ERROR_WRONG_KEY);
412            return false;
413        }
414
415        $exp = $this->_math_obj->bin2int($key->getExponent());
416        $modulus = $this->_math_obj->bin2int($key->getModulus());
417
418        $data_len = strlen($enc_data);
419        $chunk_len = $key->getKeyLength() - 1;
420        $block_len = (int) ceil($chunk_len / 8);
421        $curr_pos = 0;
422        $bit_pos = 0;
423        $plain_data = $this->_math_obj->bin2int("\0");
424        while ($curr_pos < $data_len) {
425            $tmp = $this->_math_obj->bin2int(substr($enc_data, $curr_pos, $block_len));
426            $tmp = $this->_math_obj->powmod($tmp, $exp, $modulus);
427            $plain_data = $this->_math_obj->bitOr($plain_data, $tmp, $bit_pos);
428            $bit_pos += $chunk_len;
429            $curr_pos += $block_len;
430        }
431        $result = $this->_math_obj->int2bin($plain_data);
432
433        // delete tail, containing of \x01
434        $tail = ord($result{strlen($result) - 1});
435        if ($tail != 1) {
436            $this->pushError("Error tail of decrypted text = {$tail}. Expected 1", CRYPT_RSA_ERROR_WRONG_TAIL);
437            return false;
438        }
439        return substr($result, 0, -1);
440    }
441
442    /**
443     * Creates sign for document $document, using $this->_private_key or $private_key
444     * as private key and $this->_hash_func or $hash_func as hash function.
445     *
446     * @param string $document     document, which must be signed
447     * @param object $private_key  private key (object of Crypt_RSA_Key type)
448     * @param string $hash_func    name of hash function, which will be used during signing
449     * @return mixed
450     *         signature of $document as string on success or false on error
451     *
452     * @access public
453     */
454    function createSign($document, $private_key = null, $hash_func = null)
455    {
456        // check private key
457        if (is_null($private_key)) {
458            $private_key = $this->_private_key;
459        }
460        else if (!Crypt_RSA_Key::isValid($private_key)) {
461            $this->pushError('invalid private key. It must be an object of Crypt_RSA_Key class', CRYPT_RSA_ERROR_WRONG_KEY);
462            return false;
463        }
464        if ($private_key->getKeyType() != 'private') {
465            $this->pushError('signing key must be private', CRYPT_RSA_ERROR_NEED_PRV_KEY);
466            return false;
467        }
468
469        // check hash_func
470        if (is_null($hash_func)) {
471            $hash_func = $this->_hash_func;
472        }
473        if (!function_exists($hash_func)) {
474            $this->pushError("cannot find hash function with name [$hash_func]", CRYPT_RSA_ERROR_WRONG_HASH_FUNC);
475            return false;
476        }
477
478        return $this->encrypt($hash_func($document), $private_key);
479    }
480
481    /**
482     * Validates $signature for document $document with public key $this->_public_key
483     * or $public_key and hash function $this->_hash_func or $hash_func.
484     *
485     * @param string $document    document, signature of which must be validated
486     * @param string $signature   signature, which must be validated
487     * @param object $public_key  public key (object of Crypt_RSA_Key class)
488     * @param string $hash_func   hash function, which will be used during validating signature
489     * @return mixed
490     *         true, if signature of document is valid
491     *         false, if signature of document is invalid
492     *         null on error
493     *
494     * @access public
495     */
496    function validateSign($document, $signature, $public_key = null, $hash_func = null)
497    {
498        // check public key
499        if (is_null($public_key)) {
500            $public_key = $this->_public_key;
501        }
502        else if (!Crypt_RSA_Key::isValid($public_key)) {
503            $this->pushError('invalid public key. It must be an object of Crypt_RSA_Key class', CRYPT_RSA_ERROR_WRONG_KEY);
504            return null;
505        }
506        if ($public_key->getKeyType() != 'public') {
507            $this->pushError('validating key must be public', CRYPT_RSA_ERROR_NEED_PUB_KEY);
508            return null;
509        }
510
511        // check hash_func
512        if (is_null($hash_func)) {
513            $hash_func = $this->_hash_func;
514        }
515        if (!function_exists($hash_func)) {
516            $this->pushError("cannot find hash function with name [$hash_func]", CRYPT_RSA_ERROR_WRONG_HASH_FUNC);
517            return null;
518        }
519
520        return $hash_func($document) == $this->decrypt($signature, $public_key);
521    }
522}
523
524?>