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