3 * Crypt_RSA allows to do following operations:
4 * - key pair generation
5 * - encryption and decryption
6 * - signing and sign validation
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.
16 * @category Encryption
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
22 * @link http://pear.php.net/package/Crypt_RSA
26 * RSA error handling facilities
28 require_once 'Crypt/RSA/ErrorHandler.php';
31 * loader for math wrappers
33 require_once 'Crypt/RSA/MathLoader.php';
36 * helper class for mange single key
38 require_once 'Crypt/RSA/Key.php';
41 * helper class for manage key pair
43 require_once 'Crypt/RSA/KeyPair.php';
46 * Crypt_RSA class, derived from Crypt_RSA_ErrorHandler
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
56 * // creating an error handler
57 * $error_handler = create_function('$obj', 'echo "error: ", $obj->getMessage(), "\n"');
59 * // 1024-bit key pair generation
60 * $key_pair = new Crypt_RSA_KeyPair(1024);
62 * // check consistence of Crypt_RSA_KeyPair object
63 * $error_handler($key_pair);
65 * // creating Crypt_RSA object
66 * $rsa_obj = new Crypt_RSA;
68 * // check consistence of Crypt_RSA object
69 * $error_handler($rsa_obj);
71 * // set error handler on Crypt_RSA object ( see Crypt/RSA/ErrorHandler.php for details )
72 * $rsa_obj->setErrorHandler($error_handler);
74 * // encryption (usually using public key)
75 * $enc_data = $rsa_obj->encrypt($plain_data, $key_pair->getPublicKey());
77 * // decryption (usually using private key)
78 * $plain_data = $rsa_obj->decrypt($enc_data, $key_pair->getPrivateKey());
81 * $signature = $rsa_obj->createSign($document, $key_pair->getPrivateKey());
83 * // signature checking
84 * $is_valid = $rsa_obj->validateSign($document, $signature, $key_pair->getPublicKey());
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);
96 * $sign_n = $rsa_obj->sign($doc_n);
98 * // changing default hash function, which is used for sign
99 * // creating/validation
100 * $rsa_obj->setParams(array('hash_func' => 'md5'));
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";
108 * @category Encryption
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@
117 class Crypt_RSA extends Crypt_RSA_ErrorHandler
120 * Reference to math wrapper, which is used to
121 * manipulate large integers in RSA algorithm.
123 * @var object of Crypt_RSA_Math_* class
129 * key for encryption, which is used by encrypt() method
131 * @var object of Crypt_RSA_KEY class
137 * key for decryption, which is used by decrypt() method
139 * @var object of Crypt_RSA_KEY class
145 * public key, which is used by validateSign() method
147 * @var object of Crypt_RSA_KEY class
153 * private key, which is used by createSign() method
155 * @var object of Crypt_RSA_KEY class
161 * name of hash function, which is used by validateSign()
162 * and createSign() methods. Default hash function is SHA-1
167 var $_hash_func = 'sha1';
170 * Crypt_RSA constructor.
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
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
186 function Crypt_RSA($params = null, $wrapper_name = 'default', $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);
198 $this->_math_obj = &$obj;
200 if (!is_null($params)) {
201 if (!$this->setParams($params)) {
202 // error in Crypt_RSA::setParams() function
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
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
223 * @return object new Crypt_RSA object on success or PEAR_Error object on failure
226 function &factory($params = null, $wrapper_name = 'default', $error_handler = '')
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();
233 // object created successfully. Return it
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
245 * @param array $params
246 * associative array of permitted parameters (see above)
248 * @return bool true on success or false on error
251 function setParams($params)
253 if (!is_array($params)) {
254 $this->pushError('parameters must be passed to function as associative array', CRYPT_RSA_ERROR_WRONG_PARAMS);
258 if (isset($params['enc_key'])) {
259 if (Crypt_RSA_Key::isValid($params['enc_key'])) {
260 $this->_enc_key = $params['enc_key'];
263 $this->pushError('wrong encryption key. It must be an object of Crypt_RSA_Key class', CRYPT_RSA_ERROR_WRONG_KEY);
267 if (isset($params['dec_key'])) {
268 if (Crypt_RSA_Key::isValid($params['dec_key'])) {
269 $this->_dec_key = $params['dec_key'];
272 $this->pushError('wrong decryption key. It must be an object of Crypt_RSA_Key class', CRYPT_RSA_ERROR_WRONG_KEY);
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);
282 $this->_private_key = $params['private_key'];
285 $this->pushError('wrong private key. It must be an object of Crypt_RSA_Key class', CRYPT_RSA_ERROR_WRONG_KEY);
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);
295 $this->_public_key = $params['public_key'];
298 $this->pushError('wrong public key. It must be an object of Crypt_RSA_Key class', CRYPT_RSA_ERROR_WRONG_KEY);
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);
307 $this->_hash_func = $params['hash_func'];
309 return true; // all ok
313 * Ecnrypts $plain_data by the key $this->_enc_key or $key.
315 * @param string $plain_data data, which must be encrypted
316 * @param object $key encryption key (object of Crypt_RSA_Key class)
318 * encrypted data as string on success or false on error
322 function encrypt($plain_data, $key = null)
324 $enc_data = $this->encryptBinary($plain_data, $key);
325 if ($enc_data !== false) {
326 return base64_encode($enc_data);
328 // error during encripting data
333 * Ecnrypts $plain_data by the key $this->_enc_key or $key.
335 * @param string $plain_data data, which must be encrypted
336 * @param object $key encryption key (object of Crypt_RSA_Key class)
338 * encrypted data as binary string on success or false on error
342 function encryptBinary($plain_data, $key = null)
345 // use current encryption key
346 $key = $this->_enc_key;
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);
353 // append tail \x01 to plain data. It needs for correctly decrypting of data
354 $plain_data .= "\x01";
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());
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);
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)),
373 $curr_pos += $chunk_len;
379 * Decrypts $enc_data by the key $this->_dec_key or $key.
381 * @param string $enc_data encrypted data as string
382 * @param object $key decryption key (object of RSA_Crypt_Key class)
384 * decrypted data as string on success or false on error
388 function decrypt($enc_data, $key = null)
390 $enc_data = base64_decode($enc_data);
391 return $this->decryptBinary($enc_data, $key);
395 * Decrypts $enc_data by the key $this->_dec_key or $key.
397 * @param string $enc_data encrypted data as binary string
398 * @param object $key decryption key (object of RSA_Crypt_Key class)
400 * decrypted data as string on success or false on error
404 function decryptBinary($enc_data, $key = null)
407 // use current decryption key
408 $key = $this->_dec_key;
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);
415 $exp = $this->_math_obj->bin2int($key->getExponent());
416 $modulus = $this->_math_obj->bin2int($key->getModulus());
418 $data_len = strlen($enc_data);
419 $chunk_len = $key->getKeyLength() - 1;
420 $block_len = (int) ceil($chunk_len / 8);
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;
431 $result = $this->_math_obj->int2bin($plain_data);
433 // delete tail, containing of \x01
434 $tail = ord($result{strlen($result) - 1});
436 $this->pushError("Error tail of decrypted text = {$tail}. Expected 1", CRYPT_RSA_ERROR_WRONG_TAIL);
439 return substr($result, 0, -1);
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.
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
450 * signature of $document as string on success or false on error
454 function createSign($document, $private_key = null, $hash_func = null)
457 if (is_null($private_key)) {
458 $private_key = $this->_private_key;
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);
464 if ($private_key->getKeyType() != 'private') {
465 $this->pushError('signing key must be private', CRYPT_RSA_ERROR_NEED_PRV_KEY);
470 if (is_null($hash_func)) {
471 $hash_func = $this->_hash_func;
473 if (!function_exists($hash_func)) {
474 $this->pushError("cannot find hash function with name [$hash_func]", CRYPT_RSA_ERROR_WRONG_HASH_FUNC);
478 return $this->encrypt($hash_func($document), $private_key);
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.
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
490 * true, if signature of document is valid
491 * false, if signature of document is invalid
496 function validateSign($document, $signature, $public_key = null, $hash_func = null)
499 if (is_null($public_key)) {
500 $public_key = $this->_public_key;
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);
506 if ($public_key->getKeyType() != 'public') {
507 $this->pushError('validating key must be public', CRYPT_RSA_ERROR_NEED_PUB_KEY);
512 if (is_null($hash_func)) {
513 $hash_func = $this->_hash_func;
515 if (!function_exists($hash_func)) {
516 $this->pushError("cannot find hash function with name [$hash_func]", CRYPT_RSA_ERROR_WRONG_HASH_FUNC);
520 return $hash_func($document) == $this->decrypt($signature, $public_key);