/** * 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($in[$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); }
/** * 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; }
/** * Loads RSA math wrapper with name $wrapper_name. * Implemented wrappers can be found at Crypt/RSA/Math folder. * Read docs/Crypt_RSA/docs/math_wrappers.txt for details * * This is a static function: * // load BigInt wrapper * $big_int_wrapper = &Crypt_RSA_MathLoader::loadWrapper('BigInt'); * * // load BCMath wrapper * $bcmath_wrapper = &Crypt_RSA_MathLoader::loadWrapper('BCMath'); * * @param string $wrapper_name Name of wrapper * * @return object * Reference to object of wrapper with name $wrapper_name on success * or PEAR_Error object on error * * @access public */ function loadWrapper($wrapper_name = 'default') { static $math_objects = array(); // ordered by performance. GMP is the fastest math library, BCMath - the slowest. static $math_wrappers = array('GMP', 'BigInt', 'BCMath'); if (isset($math_objects[$wrapper_name])) { /* wrapper with name $wrapper_name is already loaded and created. Return reference to existing copy of wrapper */ return $math_objects[$wrapper_name]; } $err_handler = new Crypt_RSA_ErrorHandler(); if ($wrapper_name === 'default') { // try to load the most suitable wrapper $n = sizeof($math_wrappers); for ($i = 0; $i < $n; $i++) { $obj = Crypt_RSA_MathLoader::loadWrapper($math_wrappers[$i]); if (!$err_handler->isError($obj)) { // wrapper for $math_wrappers[$i] successfully loaded // register it as default wrapper and return reference to it return $math_objects['default'] = $obj; } } // can't load any wrapper $err_handler->pushError("can't load any wrapper for existing math libraries", CRYPT_RSA_ERROR_NO_WRAPPERS); return $err_handler->getLastError(); } $class_name = 'Crypt_RSA_Math_' . $wrapper_name; $class_filename = dirname(__FILE__) . '/Math/' . $wrapper_name . '.php'; if (!is_file($class_filename)) { $err_handler->pushError("can't find file [{$class_filename}] for RSA math wrapper [{$wrapper_name}]", CRYPT_RSA_ERROR_NO_FILE); return $err_handler->getLastError(); } include_once $class_filename; if (!class_exists($class_name)) { $err_handler->pushError("can't find class [{$class_name}] in file [{$class_filename}]", CRYPT_RSA_ERROR_NO_CLASS); return $err_handler->getLastError(); } // create and return wrapper object on success or PEAR_Error object on error $obj = new $class_name(); if ($obj->errstr) { // cannot load required extension for math wrapper $err_handler->pushError($obj->errstr, CRYPT_RSA_ERROR_NO_EXT); return $err_handler->getLastError(); } return $math_objects[$wrapper_name] = $obj; }