805 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
		
		
			
		
	
	
			805 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
|   | <?php | ||
|  | /** | ||
|  |  * Crypt_RSA allows to do following operations: | ||
|  |  *     - key pair generation | ||
|  |  *     - encryption and decryption | ||
|  |  *     - signing and sign validation | ||
|  |  * | ||
|  |  * PHP versions 4 and 5 | ||
|  |  * | ||
|  |  * LICENSE: This source file is subject to version 3.0 of the PHP license | ||
|  |  * that is available through the world-wide-web at the following URI: | ||
|  |  * http://www.php.net/license/3_0.txt.  If you did not receive a copy of | ||
|  |  * the PHP License and are unable to obtain it through the web, please | ||
|  |  * send a note to license@php.net so we can mail you a copy immediately. | ||
|  |  * | ||
|  |  * @category  Encryption | ||
|  |  * @package   Crypt_RSA | ||
|  |  * @author    Alexander Valyalkin <valyala@gmail.com> | ||
|  |  * @copyright 2005 Alexander Valyalkin | ||
|  |  * @license   http://www.php.net/license/3_0.txt  PHP License 3.0 | ||
|  |  * @version   CVS: $Id: KeyPair.php,v 1.7 2009/01/05 08:30:29 clockwerx Exp $ | ||
|  |  * @link      http://pear.php.net/package/Crypt_RSA | ||
|  |  */ | ||
|  | 
 | ||
|  | /** | ||
|  |  * RSA error handling facilities | ||
|  |  */ | ||
|  | require_once 'Crypt/RSA/ErrorHandler.php'; | ||
|  | 
 | ||
|  | /** | ||
|  |  * loader for RSA math wrappers | ||
|  |  */ | ||
|  | require_once 'Crypt/RSA/MathLoader.php'; | ||
|  | 
 | ||
|  | /** | ||
|  |  * helper class for single key managing | ||
|  |  */ | ||
|  | require_once 'Crypt/RSA/Key.php'; | ||
|  | 
 | ||
|  | /** | ||
|  |  * Crypt_RSA_KeyPair class, derived from Crypt_RSA_ErrorHandler | ||
|  |  * | ||
|  |  * Provides the following functions: | ||
|  |  *  - generate($key) - generates new key pair | ||
|  |  *  - getPublicKey() - returns public key | ||
|  |  *  - getPrivateKey() - returns private key | ||
|  |  *  - getKeyLength() - returns bit key length | ||
|  |  *  - setRandomGenerator($func_name) - sets random generator to $func_name | ||
|  |  *  - fromPEMString($str) - retrieves keypair from PEM-encoded string | ||
|  |  *  - toPEMString() - stores keypair to PEM-encoded string | ||
|  |  *  - isEqual($keypair2) - compares current keypair to $keypair2 | ||
|  |  * | ||
|  |  * Example usage: | ||
|  |  *    // create new 1024-bit key pair
 | ||
|  |  *    $key_pair = new Crypt_RSA_KeyPair(1024); | ||
|  |  * | ||
|  |  *    // error check
 | ||
|  |  *    if ($key_pair->isError()) { | ||
|  |  *        echo "error while initializing Crypt_RSA_KeyPair object:\n"; | ||
|  |  *        $erorr = $key_pair->getLastError(); | ||
|  |  *        echo $error->getMessage(), "\n"; | ||
|  |  *    } | ||
|  |  * | ||
|  |  *    // get public key
 | ||
|  |  *    $public_key = $key_pair->getPublicKey(); | ||
|  |  *  | ||
|  |  *    // get private key
 | ||
|  |  *    $private_key = $key_pair->getPrivateKey(); | ||
|  |  *  | ||
|  |  *    // generate new 512-bit key pair
 | ||
|  |  *    $key_pair->generate(512); | ||
|  |  * | ||
|  |  *    // error check
 | ||
|  |  *    if ($key_pair->isError()) { | ||
|  |  *        echo "error while generating key pair:\n"; | ||
|  |  *        $erorr = $key_pair->getLastError(); | ||
|  |  *        echo $error->getMessage(), "\n"; | ||
|  |  *    } | ||
|  |  * | ||
|  |  *    // get key pair length
 | ||
|  |  *    $length = $key_pair->getKeyLength(); | ||
|  |  * | ||
|  |  *    // set random generator to $func_name, where $func_name
 | ||
|  |  *    // consists name of random generator function. See comments
 | ||
|  |  *    // before setRandomGenerator() method for details
 | ||
|  |  *    $key_pair->setRandomGenerator($func_name); | ||
|  |  * | ||
|  |  *    // error check
 | ||
|  |  *    if ($key_pair->isError()) { | ||
|  |  *        echo "error while changing random generator:\n"; | ||
|  |  *        $erorr = $key_pair->getLastError(); | ||
|  |  *        echo $error->getMessage(), "\n"; | ||
|  |  *    } | ||
|  |  * | ||
|  |  *    // using factory() method instead of constructor (it returns PEAR_Error object on failure)
 | ||
|  |  *    $rsa_obj = &Crypt_RSA_KeyPair::factory($key_len); | ||
|  |  *    if (PEAR::isError($rsa_obj)) { | ||
|  |  *        echo "error: ", $rsa_obj->getMessage(), "\n"; | ||
|  |  *    } | ||
|  |  * | ||
|  |  *    // read key pair from PEM-encoded string:
 | ||
|  |  *    $str = "-----BEGIN RSA PRIVATE KEY-----" | ||
|  |  *         . "MCsCAQACBHr5LDkCAwEAAQIEBc6jbQIDAOCfAgMAjCcCAk3pAgJMawIDAL41" | ||
|  |  *         . "-----END RSA PRIVATE KEY-----"; | ||
|  |  *    $keypair = Crypt_RSA_KeyPair::fromPEMString($str); | ||
|  |  * | ||
|  |  *    // read key pair from .pem file 'private.pem':
 | ||
|  |  *    $str = file_get_contents('private.pem'); | ||
|  |  *    $keypair = Crypt_RSA_KeyPair::fromPEMString($str); | ||
|  |  * | ||
|  |  *    // generate and write 1024-bit key pair to .pem file 'private_new.pem'
 | ||
|  |  *    $keypair = new Crypt_RSA_KeyPair(1024); | ||
|  |  *    $str = $keypair->toPEMString(); | ||
|  |  *    file_put_contents('private_new.pem', $str); | ||
|  |  * | ||
|  |  *    // compare $keypair1 to $keypair2
 | ||
|  |  *    if ($keypair1->isEqual($keypair2)) { | ||
|  |  *        echo "keypair1 = keypair2\n"; | ||
|  |  *    } | ||
|  |  *    else { | ||
|  |  *        echo "keypair1 != keypair2\n"; | ||
|  |  *    } | ||
|  |  * | ||
|  |  * @category  Encryption | ||
|  |  * @package   Crypt_RSA | ||
|  |  * @author    Alexander Valyalkin <valyala@gmail.com> | ||
|  |  * @copyright 2005 Alexander Valyalkin | ||
|  |  * @license   http://www.php.net/license/3_0.txt  PHP License 3.0 | ||
|  |  * @version   Release: @package_version@ | ||
|  |  * @link      http://pear.php.net/package/Crypt_RSA | ||
|  |  * @access    public | ||
|  |  */ | ||
|  | class Crypt_RSA_KeyPair extends Crypt_RSA_ErrorHandler | ||
|  | { | ||
|  |     /** | ||
|  |      * Reference to math wrapper object, which is used to | ||
|  |      * manipulate large integers in RSA algorithm. | ||
|  |      * | ||
|  |      * @var object of Crypt_RSA_Math_* class | ||
|  |      * @access private | ||
|  |      */ | ||
|  |     var $_math_obj; | ||
|  | 
 | ||
|  |     /** | ||
|  |      * length of each key in the key pair | ||
|  |      * | ||
|  |      * @var int | ||
|  |      * @access private | ||
|  |      */ | ||
|  |     var $_key_len; | ||
|  | 
 | ||
|  |     /** | ||
|  |      * public key | ||
|  |      * | ||
|  |      * @var object of Crypt_RSA_KEY class | ||
|  |      * @access private | ||
|  |      */ | ||
|  |     var $_public_key; | ||
|  | 
 | ||
|  |     /** | ||
|  |      * private key | ||
|  |      * | ||
|  |      * @var object of Crypt_RSA_KEY class | ||
|  |      * @access private | ||
|  |      */ | ||
|  |     var $_private_key; | ||
|  | 
 | ||
|  |     /** | ||
|  |      * name of function, which is used as random generator | ||
|  |      * | ||
|  |      * @var string | ||
|  |      * @access private | ||
|  |      */ | ||
|  |     var $_random_generator; | ||
|  | 
 | ||
|  |     /** | ||
|  |      * RSA keypair attributes [version, n, e, d, p, q, dmp1, dmq1, iqmp] as associative array | ||
|  |      * | ||
|  |      * @var array | ||
|  |      * @access private | ||
|  |      */ | ||
|  |     var $_attrs; | ||
|  | 
 | ||
|  |     /** | ||
|  |      * Returns names of keypair attributes from $this->_attrs array | ||
|  |      * | ||
|  |      * @return array  Array of keypair attributes names | ||
|  |      * @access private | ||
|  |      */ | ||
|  |     function _get_attr_names()  | ||
|  |     { | ||
|  |         return array('version', 'n', 'e', 'd', 'p', 'q', 'dmp1', 'dmq1', 'iqmp'); | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * Parses ASN.1 string [$str] starting form position [$pos]. | ||
|  |      * Returns tag and string value of parsed object. | ||
|  |      * | ||
|  |      * @param string                 $str | ||
|  |      * @param int                    &$pos | ||
|  |      * @param Crypt_RSA_ErrorHandler &$err_handler | ||
|  |      * | ||
|  |      * @return mixed    Array('tag' => ..., 'str' => ...) on success, false on error | ||
|  |      * @access private | ||
|  |      */ | ||
|  |     function _ASN1Parse($str, &$pos, &$err_handler) | ||
|  |     { | ||
|  |         $max_pos = strlen($str); | ||
|  |         if ($max_pos < 2) { | ||
|  |             $err_handler->pushError("ASN.1 string too short"); | ||
|  |             return false; | ||
|  |         } | ||
|  | 
 | ||
|  |         // get ASN.1 tag value
 | ||
|  |         $tag = ord($str[$pos++]) & 0x1f; | ||
|  |         if ($tag == 0x1f) { | ||
|  |             $tag = 0; | ||
|  |             do { | ||
|  |                 $n = ord($str[$pos++]); | ||
|  |                 $tag <<= 7; | ||
|  |                 $tag |= $n & 0x7f; | ||
|  |             } while (($n & 0x80) && $pos < $max_pos); | ||
|  |         } | ||
|  |         if ($pos >= $max_pos) { | ||
|  |             $err_handler->pushError("ASN.1 string too short"); | ||
|  |             return false; | ||
|  |         } | ||
|  | 
 | ||
|  |         // get ASN.1 object length
 | ||
|  |         $len = ord($str[$pos++]); | ||
|  |         if ($len & 0x80) { | ||
|  |             $n = $len & 0x1f; | ||
|  |             $len = 0; | ||
|  |             while ($n-- && $pos < $max_pos) { | ||
|  |                 $len <<= 8; | ||
|  |                 $len |= ord($str[$pos++]); | ||
|  |             } | ||
|  |         } | ||
|  |         if ($pos >= $max_pos || $len > $max_pos - $pos) { | ||
|  |             $err_handler->pushError("ASN.1 string too short"); | ||
|  |             return false; | ||
|  |         } | ||
|  | 
 | ||
|  |         // get string value of ASN.1 object
 | ||
|  |         $str = substr($str, $pos, $len); | ||
|  | 
 | ||
|  |         return array( | ||
|  |             'tag' => $tag, | ||
|  |             'str' => $str, | ||
|  |         ); | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * Parses ASN.1 sting [$str] starting from position [$pos]. | ||
|  |      * Returns string representation of number, which can be passed | ||
|  |      * in bin2int() function of math wrapper. | ||
|  |      * | ||
|  |      * @param string                 $str | ||
|  |      * @param int                    &$pos | ||
|  |      * @param Crypt_RSA_ErrorHandler &$err_handler | ||
|  |      * | ||
|  |      * @return mixed   string representation of parsed number on success, false on error | ||
|  |      * @access private | ||
|  |      */ | ||
|  |     function _ASN1ParseInt($str, &$pos, &$err_handler) | ||
|  |     { | ||
|  |         $tmp = Crypt_RSA_KeyPair::_ASN1Parse($str, $pos, $err_handler); | ||
|  |         if ($err_handler->isError()) { | ||
|  |             return false; | ||
|  |         } | ||
|  |         if ($tmp['tag'] != 0x02) { | ||
|  |             $errstr = sprintf("wrong ASN tag value: 0x%02x. Expected 0x02 (INTEGER)", $tmp['tag']); | ||
|  |             $err_handler->pushError($errstr); | ||
|  |             return false; | ||
|  |         } | ||
|  |         $pos += strlen($tmp['str']); | ||
|  | 
 | ||
|  |         return strrev($tmp['str']); | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * Constructs ASN.1 string from tag $tag and object $str | ||
|  |      * | ||
|  |      * @param string $str            ASN.1 object string | ||
|  |      * @param int    $tag            ASN.1 tag value | ||
|  |      * @param bool   $is_constructed  | ||
|  |      * @param bool   $is_private  | ||
|  |      * | ||
|  |      * @return ASN.1-encoded string | ||
|  |      * @access private | ||
|  |      */ | ||
|  |     function _ASN1Store($str, $tag, $is_constructed = false, $is_private = false) | ||
|  |     { | ||
|  |         $out = ''; | ||
|  | 
 | ||
|  |         // encode ASN.1 tag value
 | ||
|  |         $tag_ext = ($is_constructed ? 0x20 : 0) | ($is_private ? 0xc0 : 0); | ||
|  |         if ($tag < 0x1f) { | ||
|  |             $out .= chr($tag | $tag_ext); | ||
|  |         } else { | ||
|  |             $out .= chr($tag_ext | 0x1f); | ||
|  |             $tmp = chr($tag & 0x7f); | ||
|  |             $tag >>= 7; | ||
|  |             while ($tag) { | ||
|  |                 $tmp .= chr(($tag & 0x7f) | 0x80); | ||
|  |                 $tag >>= 7; | ||
|  |             } | ||
|  |             $out .= strrev($tmp); | ||
|  |         } | ||
|  | 
 | ||
|  |         // encode ASN.1 object length
 | ||
|  |         $len = strlen($str); | ||
|  |         if ($len < 0x7f) { | ||
|  |             $out .= chr($len); | ||
|  |         } else { | ||
|  |             $tmp = ''; | ||
|  |             $n = 0; | ||
|  |             while ($len) { | ||
|  |                 $tmp .= chr($len & 0xff); | ||
|  |                 $len >>= 8; | ||
|  |                 $n++; | ||
|  |             } | ||
|  |             $out .= chr($n | 0x80); | ||
|  |             $out .= strrev($tmp); | ||
|  |         } | ||
|  | 
 | ||
|  |         return $out . $str; | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * Constructs ASN.1 string from binary representation of big integer | ||
|  |      * | ||
|  |      * @param string $str binary representation of big integer | ||
|  |      * | ||
|  |      * @return ASN.1-encoded string | ||
|  |      * @access private | ||
|  |      */ | ||
|  |     function _ASN1StoreInt($str) | ||
|  |     { | ||
|  |         $str = strrev($str); | ||
|  |         return Crypt_RSA_KeyPair::_ASN1Store($str, 0x02); | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * Crypt_RSA_KeyPair constructor. | ||
|  |      * | ||
|  |      * Wrapper: name of math wrapper, which will be used to | ||
|  |      *        perform different operations with big integers. | ||
|  |      *        See contents of Crypt/RSA/Math folder for examples of wrappers. | ||
|  |      *        Read docs/Crypt_RSA/docs/math_wrappers.txt for details. | ||
|  |      * | ||
|  |      * @param int      $key_len          bit length of key pair, which will be generated in constructor | ||
|  |      * @param string   $wrapper_name     wrapper name | ||
|  |      * @param string   $error_handler    name of error handler function
 | ||
|  |      * @param callback $random_generator function which will be used as random generator | ||
|  |      * | ||
|  |      * @access public | ||
|  |      */ | ||
|  |     function Crypt_RSA_KeyPair($key_len, $wrapper_name = 'default', $error_handler = '', $random_generator = null) | ||
|  |     { | ||
|  |         // set error handler
 | ||
|  |         $this->setErrorHandler($error_handler); | ||
|  |         // try to load math wrapper
 | ||
|  |         $obj = &Crypt_RSA_MathLoader::loadWrapper($wrapper_name); | ||
|  |         if ($this->isError($obj)) { | ||
|  |             // error during loading of math wrapper
 | ||
|  |             $this->pushError($obj); | ||
|  |             return; | ||
|  |         } | ||
|  |         $this->_math_obj = &$obj; | ||
|  | 
 | ||
|  |         // set random generator
 | ||
|  |         if (!$this->setRandomGenerator($random_generator)) { | ||
|  |             // error in setRandomGenerator() function
 | ||
|  |             return; | ||
|  |         } | ||
|  | 
 | ||
|  |         if (is_array($key_len)) { | ||
|  |             // ugly BC hack - it is possible to pass RSA private key attributes [version, n, e, d, p, q, dmp1, dmq1, iqmp]
 | ||
|  |             // as associative array instead of key length to Crypt_RSA_KeyPair constructor
 | ||
|  |             $rsa_attrs = $key_len; | ||
|  | 
 | ||
|  |             // convert attributes to big integers
 | ||
|  |             $attr_names = $this->_get_attr_names(); | ||
|  |             foreach ($attr_names as $attr) { | ||
|  |                 if (!isset($rsa_attrs[$attr])) { | ||
|  |                     $this->pushError("missing required RSA attribute [$attr]"); | ||
|  |                     return; | ||
|  |                 } | ||
|  |                 ${$attr} = $this->_math_obj->bin2int($rsa_attrs[$attr]); | ||
|  |             } | ||
|  | 
 | ||
|  |             // check primality of p and q
 | ||
|  |             if (!$this->_math_obj->isPrime($p)) { | ||
|  |                 $this->pushError("[p] must be prime"); | ||
|  |                 return; | ||
|  |             } | ||
|  |             if (!$this->_math_obj->isPrime($q)) { | ||
|  |                 $this->pushError("[q] must be prime"); | ||
|  |                 return; | ||
|  |             } | ||
|  | 
 | ||
|  |             // check n = p * q
 | ||
|  |             $n1 = $this->_math_obj->mul($p, $q); | ||
|  |             if ($this->_math_obj->cmpAbs($n, $n1)) { | ||
|  |                 $this->pushError("n != p * q"); | ||
|  |                 return; | ||
|  |             } | ||
|  | 
 | ||
|  |             // check e * d = 1 mod (p-1) * (q-1)
 | ||
|  |             $p1 = $this->_math_obj->dec($p); | ||
|  |             $q1 = $this->_math_obj->dec($q); | ||
|  |             $p1q1 = $this->_math_obj->mul($p1, $q1); | ||
|  |             $ed = $this->_math_obj->mul($e, $d); | ||
|  |             $one = $this->_math_obj->mod($ed, $p1q1); | ||
|  |             if (!$this->_math_obj->isOne($one)) { | ||
|  |                 $this->pushError("e * d != 1 mod (p-1)*(q-1)"); | ||
|  |                 return; | ||
|  |             } | ||
|  | 
 | ||
|  |             // check dmp1 = d mod (p-1)
 | ||
|  |             $dmp = $this->_math_obj->mod($d, $p1); | ||
|  |             if ($this->_math_obj->cmpAbs($dmp, $dmp1)) { | ||
|  |                 $this->pushError("dmp1 != d mod (p-1)"); | ||
|  |                 return; | ||
|  |             } | ||
|  | 
 | ||
|  |             // check dmq1 = d mod (q-1)
 | ||
|  |             $dmq = $this->_math_obj->mod($d, $q1); | ||
|  |             if ($this->_math_obj->cmpAbs($dmq, $dmq1)) { | ||
|  |                 $this->pushError("dmq1 != d mod (q-1)"); | ||
|  |                 return; | ||
|  |             } | ||
|  | 
 | ||
|  |             // check iqmp = 1/q mod p
 | ||
|  |             $q1 = $this->_math_obj->invmod($iqmp, $p); | ||
|  |             if ($this->_math_obj->cmpAbs($q, $q1)) { | ||
|  |                 $this->pushError("iqmp != 1/q mod p"); | ||
|  |                 return; | ||
|  |             } | ||
|  | 
 | ||
|  |             // try to create public key object
 | ||
|  |             $public_key = &new Crypt_RSA_Key($rsa_attrs['n'], $rsa_attrs['e'], 'public', $wrapper_name, $error_handler); | ||
|  |             if ($public_key->isError()) { | ||
|  |                 // error during creating public object
 | ||
|  |                 $this->pushError($public_key->getLastError()); | ||
|  |                 return; | ||
|  |             } | ||
|  | 
 | ||
|  |             // try to create private key object
 | ||
|  |             $private_key = &new Crypt_RSA_Key($rsa_attrs['n'], $rsa_attrs['d'], 'private', $wrapper_name, $error_handler); | ||
|  |             if ($private_key->isError()) { | ||
|  |                 // error during creating private key object
 | ||
|  |                 $this->pushError($private_key->getLastError()); | ||
|  |                 return; | ||
|  |             } | ||
|  | 
 | ||
|  |             $this->_public_key = $public_key; | ||
|  |             $this->_private_key = $private_key; | ||
|  |             $this->_key_len = $public_key->getKeyLength(); | ||
|  |             $this->_attrs = $rsa_attrs; | ||
|  |         } else { | ||
|  |             // generate key pair
 | ||
|  |             if (!$this->generate($key_len)) { | ||
|  |                 // error during generating key pair
 | ||
|  |                 return; | ||
|  |             } | ||
|  |         } | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * Crypt_RSA_KeyPair factory. | ||
|  |      * | ||
|  |      * Wrapper - Name of math wrapper, which will be used to | ||
|  |      *        perform different operations with big integers. | ||
|  |      *        See contents of Crypt/RSA/Math folder for examples of wrappers. | ||
|  |      *        Read docs/Crypt_RSA/docs/math_wrappers.txt for details. | ||
|  |      * | ||
|  |      * @param int      $key_len          bit length of key pair, which will be generated in constructor | ||
|  |      * @param string   $wrapper_name     wrapper name | ||
|  |      * @param string   $error_handler    name of error handler function
 | ||
|  |      * @param callback $random_generator function which will be used as random generator | ||
|  |      * | ||
|  |      * @return object   new Crypt_RSA_KeyPair object on success or PEAR_Error object on failure | ||
|  |      * @access public | ||
|  |      */ | ||
|  |     function &factory($key_len, $wrapper_name = 'default', $error_handler = '', $random_generator = null) | ||
|  |     { | ||
|  |         $obj = &new Crypt_RSA_KeyPair($key_len, $wrapper_name, $error_handler, $random_generator); | ||
|  |         if ($obj->isError()) { | ||
|  |             // error during creating a new object. Return PEAR_Error object
 | ||
|  |             return $obj->getLastError(); | ||
|  |         } | ||
|  |         // object created successfully. Return it
 | ||
|  |         return $obj; | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * Generates new Crypt_RSA key pair with length $key_len. | ||
|  |      * If $key_len is missed, use an old key length from $this->_key_len | ||
|  |      * | ||
|  |      * @param int $key_len bit length of key pair, which will be generated | ||
|  |      * | ||
|  |      * @return bool         true on success or false on error | ||
|  |      * @access public | ||
|  |      */ | ||
|  |     function generate($key_len = null) | ||
|  |     { | ||
|  |         if (is_null($key_len)) { | ||
|  |             // use an old key length
 | ||
|  |             $key_len = $this->_key_len; | ||
|  |             if (is_null($key_len)) { | ||
|  |                 $this->pushError('missing key_len parameter', CRYPT_RSA_ERROR_MISSING_KEY_LEN); | ||
|  |                 return false; | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         // minimal key length is 8 bit ;)
 | ||
|  |         if ($key_len < 8) { | ||
|  |             $key_len = 8; | ||
|  |         } | ||
|  |         // store key length in the _key_len property
 | ||
|  |         $this->_key_len = $key_len; | ||
|  | 
 | ||
|  |         // set [e] to 0x10001 (65537)
 | ||
|  |         $e = $this->_math_obj->bin2int("\x01\x00\x01"); | ||
|  | 
 | ||
|  |         // generate [p], [q] and [n]
 | ||
|  |         $p_len = intval(($key_len + 1) / 2); | ||
|  |         $q_len = $key_len - $p_len; | ||
|  |         $p1 = $q1 = 0; | ||
|  |         do { | ||
|  |             // generate prime number [$p] with length [$p_len] with the following condition:
 | ||
|  |             // GCD($e, $p - 1) = 1
 | ||
|  |             do { | ||
|  |                 $p = $this->_math_obj->getPrime($p_len, $this->_random_generator); | ||
|  |                 $p1 = $this->_math_obj->dec($p); | ||
|  |                 $tmp = $this->_math_obj->GCD($e, $p1); | ||
|  |             } while (!$this->_math_obj->isOne($tmp)); | ||
|  |             // generate prime number [$q] with length [$q_len] with the following conditions:
 | ||
|  |             // GCD($e, $q - 1) = 1
 | ||
|  |             // $q != $p
 | ||
|  |             do { | ||
|  |                 $q = $this->_math_obj->getPrime($q_len, $this->_random_generator); | ||
|  |                 $q1 = $this->_math_obj->dec($q); | ||
|  |                 $tmp = $this->_math_obj->GCD($e, $q1); | ||
|  |             } while (!$this->_math_obj->isOne($tmp) && !$this->_math_obj->cmpAbs($q, $p)); | ||
|  |             // if (p < q), then exchange them
 | ||
|  |             if ($this->_math_obj->cmpAbs($p, $q) < 0) { | ||
|  |                 $tmp = $p; | ||
|  |                 $p = $q; | ||
|  |                 $q = $tmp; | ||
|  |                 $tmp = $p1; | ||
|  |                 $p1 = $q1; | ||
|  |                 $q1 = $tmp; | ||
|  |             } | ||
|  |             // calculate n = p * q
 | ||
|  |             $n = $this->_math_obj->mul($p, $q); | ||
|  |         } while ($this->_math_obj->bitLen($n) != $key_len); | ||
|  | 
 | ||
|  |         // calculate d = 1/e mod (p - 1) * (q - 1)
 | ||
|  |         $pq = $this->_math_obj->mul($p1, $q1); | ||
|  |         $d = $this->_math_obj->invmod($e, $pq); | ||
|  | 
 | ||
|  |         // calculate dmp1 = d mod (p - 1)
 | ||
|  |         $dmp1 = $this->_math_obj->mod($d, $p1); | ||
|  | 
 | ||
|  |         // calculate dmq1 = d mod (q - 1)
 | ||
|  |         $dmq1 = $this->_math_obj->mod($d, $q1); | ||
|  | 
 | ||
|  |         // calculate iqmp = 1/q mod p
 | ||
|  |         $iqmp = $this->_math_obj->invmod($q, $p); | ||
|  | 
 | ||
|  |         // store RSA keypair attributes
 | ||
|  |         $this->_attrs = array( | ||
|  |             'version' => "\x00", | ||
|  |             'n' => $this->_math_obj->int2bin($n), | ||
|  |             'e' => $this->_math_obj->int2bin($e), | ||
|  |             'd' => $this->_math_obj->int2bin($d), | ||
|  |             'p' => $this->_math_obj->int2bin($p), | ||
|  |             'q' => $this->_math_obj->int2bin($q), | ||
|  |             'dmp1' => $this->_math_obj->int2bin($dmp1), | ||
|  |             'dmq1' => $this->_math_obj->int2bin($dmq1), | ||
|  |             'iqmp' => $this->_math_obj->int2bin($iqmp), | ||
|  |         ); | ||
|  | 
 | ||
|  |         $n = $this->_attrs['n']; | ||
|  |         $e = $this->_attrs['e']; | ||
|  |         $d = $this->_attrs['d']; | ||
|  | 
 | ||
|  |         // try to create public key object
 | ||
|  |         $obj = &new Crypt_RSA_Key($n, $e, 'public', $this->_math_obj->getWrapperName(), $this->_error_handler); | ||
|  |         if ($obj->isError()) { | ||
|  |             // error during creating public object
 | ||
|  |             $this->pushError($obj->getLastError()); | ||
|  |             return false; | ||
|  |         } | ||
|  |         $this->_public_key = &$obj; | ||
|  | 
 | ||
|  |         // try to create private key object
 | ||
|  |         $obj = &new Crypt_RSA_Key($n, $d, 'private', $this->_math_obj->getWrapperName(), $this->_error_handler); | ||
|  |         if ($obj->isError()) { | ||
|  |             // error during creating private key object
 | ||
|  |             $this->pushError($obj->getLastError()); | ||
|  |             return false; | ||
|  |         } | ||
|  |         $this->_private_key = &$obj; | ||
|  | 
 | ||
|  |         return true; // key pair successfully generated
 | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * Returns public key from the pair | ||
|  |      * | ||
|  |      * @return object  public key object of class Crypt_RSA_Key | ||
|  |      * @access public | ||
|  |      */ | ||
|  |     function getPublicKey() | ||
|  |     { | ||
|  |         return $this->_public_key; | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * Returns private key from the pair | ||
|  |      * | ||
|  |      * @return object   private key object of class Crypt_RSA_Key | ||
|  |      * @access public | ||
|  |      */ | ||
|  |     function getPrivateKey() | ||
|  |     { | ||
|  |         return $this->_private_key; | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * Sets name of random generator function for key generation. | ||
|  |      * If parameter is skipped, then sets to default random generator. | ||
|  |      * | ||
|  |      * Random generator function must return integer with at least 8 lower | ||
|  |      * significant bits, which will be used as random values. | ||
|  |      * | ||
|  |      * @param string $random_generator name of random generator function
 | ||
|  |      * | ||
|  |      * @return bool                     true on success or false on error | ||
|  |      * @access public | ||
|  |      */ | ||
|  |     function setRandomGenerator($random_generator = null) | ||
|  |     { | ||
|  |         static $default_random_generator = null; | ||
|  | 
 | ||
|  |         if (is_string($random_generator)) { | ||
|  |             // set user's random generator
 | ||
|  |             if (!function_exists($random_generator)) { | ||
|  |                 $this->pushError("can't find random generator function with name [{$random_generator}]"); | ||
|  |                 return false; | ||
|  |             } | ||
|  |             $this->_random_generator = $random_generator; | ||
|  |         } else { | ||
|  |             // set default random generator
 | ||
|  |             $this->_random_generator = is_null($default_random_generator) ? | ||
|  |                 ($default_random_generator = create_function('', '$a=explode(" ",microtime());return(int)($a[0]*1000000);')) : | ||
|  |                 $default_random_generator; | ||
|  |         } | ||
|  |         return true; | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * Returns length of each key in the key pair | ||
|  |      * | ||
|  |      * @return int  bit length of each key in key pair | ||
|  |      * @access public | ||
|  |      */ | ||
|  |     function getKeyLength() | ||
|  |     { | ||
|  |         return $this->_key_len; | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * Retrieves RSA keypair from PEM-encoded string, containing RSA private key. | ||
|  |      * Example of such string: | ||
|  |      * -----BEGIN RSA PRIVATE KEY----- | ||
|  |      * MCsCAQACBHtvbSECAwEAAQIEeYrk3QIDAOF3AgMAjCcCAmdnAgJMawIDALEk | ||
|  |      * -----END RSA PRIVATE KEY----- | ||
|  |      * | ||
|  |      * Wrapper: Name of math wrapper, which will be used to | ||
|  |      * perform different operations with big integers. | ||
|  |      * See contents of Crypt/RSA/Math folder for examples of wrappers. | ||
|  |      * Read docs/Crypt_RSA/docs/math_wrappers.txt for details. | ||
|  |      * | ||
|  |      * @param string $str           PEM-encoded string | ||
|  |      * @param string $wrapper_name  Wrapper name | ||
|  |      * @param string $error_handler name of error handler function
 | ||
|  |      * | ||
|  |      * @return Crypt_RSA_KeyPair object on success, PEAR_Error object on error | ||
|  |      * @access public | ||
|  |      * @static | ||
|  |      */ | ||
|  |     function &fromPEMString($str, $wrapper_name = 'default', $error_handler = '') | ||
|  |     { | ||
|  |         if (isset($this)) { | ||
|  |             if ($wrapper_name == 'default') { | ||
|  |                 $wrapper_name = $this->_math_obj->getWrapperName(); | ||
|  |             } | ||
|  |             if ($error_handler == '') { | ||
|  |                 $error_handler = $this->_error_handler; | ||
|  |             } | ||
|  |         } | ||
|  |         $err_handler = &new Crypt_RSA_ErrorHandler; | ||
|  |         $err_handler->setErrorHandler($error_handler); | ||
|  | 
 | ||
|  |         // search for base64-encoded private key
 | ||
|  |         if (!preg_match('/-----BEGIN RSA PRIVATE KEY-----([^-]+)-----END RSA PRIVATE KEY-----/', $str, $matches)) { | ||
|  |             $err_handler->pushError("can't find RSA private key in the string [{$str}]"); | ||
|  |             return $err_handler->getLastError(); | ||
|  |         } | ||
|  | 
 | ||
|  |         // parse private key. It is ASN.1-encoded
 | ||
|  |         $str = base64_decode($matches[1]); | ||
|  |         $pos = 0; | ||
|  |         $tmp = Crypt_RSA_KeyPair::_ASN1Parse($str, $pos, $err_handler); | ||
|  |         if ($err_handler->isError()) { | ||
|  |             return $err_handler->getLastError(); | ||
|  |         } | ||
|  |         if ($tmp['tag'] != 0x10) { | ||
|  |             $errstr = sprintf("wrong ASN tag value: 0x%02x. Expected 0x10 (SEQUENCE)", $tmp['tag']); | ||
|  |             $err_handler->pushError($errstr); | ||
|  |             return $err_handler->getLastError(); | ||
|  |         } | ||
|  | 
 | ||
|  |         // parse ASN.1 SEQUENCE for RSA private key
 | ||
|  |         $attr_names = Crypt_RSA_KeyPair::_get_attr_names(); | ||
|  |         $n = sizeof($attr_names); | ||
|  |         $rsa_attrs = array(); | ||
|  |         for ($i = 0; $i < $n; $i++) { | ||
|  |             $tmp = Crypt_RSA_KeyPair::_ASN1ParseInt($str, $pos, $err_handler); | ||
|  |             if ($err_handler->isError()) { | ||
|  |                 return $err_handler->getLastError(); | ||
|  |             } | ||
|  |             $attr = $attr_names[$i]; | ||
|  |             $rsa_attrs[$attr] = $tmp; | ||
|  |         } | ||
|  | 
 | ||
|  |         // create Crypt_RSA_KeyPair object.
 | ||
|  |         $keypair = &new Crypt_RSA_KeyPair($rsa_attrs, $wrapper_name, $error_handler); | ||
|  |         if ($keypair->isError()) { | ||
|  |             return $keypair->getLastError(); | ||
|  |         } | ||
|  | 
 | ||
|  |         return $keypair; | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * converts keypair to PEM-encoded string, which can be stroed in  | ||
|  |      * .pem compatible files, contianing RSA private key. | ||
|  |      * | ||
|  |      * @return string PEM-encoded keypair on success, false on error | ||
|  |      * @access public | ||
|  |      */ | ||
|  |     function toPEMString() | ||
|  |     { | ||
|  |         // store RSA private key attributes into ASN.1 string
 | ||
|  |         $str = ''; | ||
|  |         $attr_names = $this->_get_attr_names(); | ||
|  |         $n = sizeof($attr_names); | ||
|  |         $rsa_attrs = $this->_attrs; | ||
|  |         for ($i = 0; $i < $n; $i++) { | ||
|  |             $attr = $attr_names[$i]; | ||
|  |             if (!isset($rsa_attrs[$attr])) { | ||
|  |                 $this->pushError("Cannot find value for ASN.1 attribute [$attr]"); | ||
|  |                 return false; | ||
|  |             } | ||
|  |             $tmp = $rsa_attrs[$attr]; | ||
|  |             $str .= Crypt_RSA_KeyPair::_ASN1StoreInt($tmp); | ||
|  |         } | ||
|  | 
 | ||
|  |         // prepend $str by ASN.1 SEQUENCE (0x10) header
 | ||
|  |         $str = Crypt_RSA_KeyPair::_ASN1Store($str, 0x10, true); | ||
|  | 
 | ||
|  |         // encode and format PEM string
 | ||
|  |         $str = base64_encode($str); | ||
|  |         $str = chunk_split($str, 64, "\n"); | ||
|  |         return "-----BEGIN RSA PRIVATE KEY-----\n$str-----END RSA PRIVATE KEY-----\n"; | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * Compares keypairs in Crypt_RSA_KeyPair objects $this and $key_pair | ||
|  |      * | ||
|  |      * @param Crypt_RSA_KeyPair $key_pair  keypair to compare | ||
|  |      * | ||
|  |      * @return bool  true, if keypair stored in $this equal to keypair stored in $key_pair | ||
|  |      * @access public | ||
|  |      */ | ||
|  |     function isEqual($key_pair) | ||
|  |     { | ||
|  |         $attr_names = $this->_get_attr_names(); | ||
|  |         foreach ($attr_names as $attr) { | ||
|  |             if ($this->_attrs[$attr] != $key_pair->_attrs[$attr]) { | ||
|  |                 return false; | ||
|  |             } | ||
|  |         } | ||
|  |         return true; | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | ?>
 |