524 lines
18 KiB
PHP
524 lines
18 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, 2006 Alexander Valyalkin
|
||
|
* @license http://www.php.net/license/3_0.txt PHP License 3.0
|
||
|
* @version 1.2.0b
|
||
|
* @link http://pear.php.net/package/Crypt_RSA
|
||
|
*/
|
||
|
|
||
|
/**
|
||
|
* RSA error handling facilities
|
||
|
*/
|
||
|
require_once 'Crypt/RSA/ErrorHandler.php';
|
||
|
|
||
|
/**
|
||
|
* loader for math wrappers
|
||
|
*/
|
||
|
require_once 'Crypt/RSA/MathLoader.php';
|
||
|
|
||
|
/**
|
||
|
* helper class for mange single key
|
||
|
*/
|
||
|
require_once 'Crypt/RSA/Key.php';
|
||
|
|
||
|
/**
|
||
|
* helper class for manage key pair
|
||
|
*/
|
||
|
require_once 'Crypt/RSA/KeyPair.php';
|
||
|
|
||
|
/**
|
||
|
* Crypt_RSA class, derived from Crypt_RSA_ErrorHandler
|
||
|
*
|
||
|
* Provides the following functions:
|
||
|
* - setParams($params) - sets parameters of current object
|
||
|
* - encrypt($plain_data, $key = null) - encrypts data
|
||
|
* - decrypt($enc_data, $key = null) - decrypts data
|
||
|
* - createSign($doc, $private_key = null) - signs document by private key
|
||
|
* - validateSign($doc, $signature, $public_key = null) - validates signature of document
|
||
|
*
|
||
|
* Example usage:
|
||
|
* // creating an error handler
|
||
|
* $error_handler = create_function('$obj', 'echo "error: ", $obj->getMessage(), "\n"');
|
||
|
*
|
||
|
* // 1024-bit key pair generation
|
||
|
* $key_pair = new Crypt_RSA_KeyPair(1024);
|
||
|
*
|
||
|
* // check consistence of Crypt_RSA_KeyPair object
|
||
|
* $error_handler($key_pair);
|
||
|
*
|
||
|
* // creating Crypt_RSA object
|
||
|
* $rsa_obj = new Crypt_RSA;
|
||
|
*
|
||
|
* // check consistence of Crypt_RSA object
|
||
|
* $error_handler($rsa_obj);
|
||
|
*
|
||
|
* // set error handler on Crypt_RSA object ( see Crypt/RSA/ErrorHandler.php for details )
|
||
|
* $rsa_obj->setErrorHandler($error_handler);
|
||
|
*
|
||
|
* // encryption (usually using public key)
|
||
|
* $enc_data = $rsa_obj->encrypt($plain_data, $key_pair->getPublicKey());
|
||
|
*
|
||
|
* // decryption (usually using private key)
|
||
|
* $plain_data = $rsa_obj->decrypt($enc_data, $key_pair->getPrivateKey());
|
||
|
*
|
||
|
* // signing
|
||
|
* $signature = $rsa_obj->createSign($document, $key_pair->getPrivateKey());
|
||
|
*
|
||
|
* // signature checking
|
||
|
* $is_valid = $rsa_obj->validateSign($document, $signature, $key_pair->getPublicKey());
|
||
|
*
|
||
|
* // signing many documents by one private key
|
||
|
* $rsa_obj = new Crypt_RSA(array('private_key' => $key_pair->getPrivateKey()));
|
||
|
* // check consistence of Crypt_RSA object
|
||
|
* $error_handler($rsa_obj);
|
||
|
* // set error handler ( see Crypt/RSA/ErrorHandler.php for details )
|
||
|
* $rsa_obj->setErrorHandler($error_handler);
|
||
|
* // sign many documents
|
||
|
* $sign_1 = $rsa_obj->sign($doc_1);
|
||
|
* $sign_2 = $rsa_obj->sign($doc_2);
|
||
|
* //...
|
||
|
* $sign_n = $rsa_obj->sign($doc_n);
|
||
|
*
|
||
|
* // changing default hash function, which is used for sign
|
||
|
* // creating/validation
|
||
|
* $rsa_obj->setParams(array('hash_func' => 'md5'));
|
||
|
*
|
||
|
* // using factory() method instead of constructor (it returns PEAR_Error object on failure)
|
||
|
* $rsa_obj = &Crypt_RSA::factory();
|
||
|
* if (PEAR::isError($rsa_obj)) {
|
||
|
* echo "error: ", $rsa_obj->getMessage(), "\n";
|
||
|
* }
|
||
|
*
|
||
|
* @category Encryption
|
||
|
* @package Crypt_RSA
|
||
|
* @author Alexander Valyalkin <valyala@gmail.com>
|
||
|
* @copyright 2005, 2006 Alexander Valyalkin
|
||
|
* @license http://www.php.net/license/3_0.txt PHP License 3.0
|
||
|
* @link http://pear.php.net/package/Crypt_RSA
|
||
|
* @version @package_version@
|
||
|
* @access public
|
||
|
*/
|
||
|
class Crypt_RSA extends Crypt_RSA_ErrorHandler
|
||
|
{
|
||
|
/**
|
||
|
* Reference to math wrapper, which is used to
|
||
|
* manipulate large integers in RSA algorithm.
|
||
|
*
|
||
|
* @var object of Crypt_RSA_Math_* class
|
||
|
* @access private
|
||
|
*/
|
||
|
var $_math_obj;
|
||
|
|
||
|
/**
|
||
|
* key for encryption, which is used by encrypt() method
|
||
|
*
|
||
|
* @var object of Crypt_RSA_KEY class
|
||
|
* @access private
|
||
|
*/
|
||
|
var $_enc_key;
|
||
|
|
||
|
/**
|
||
|
* key for decryption, which is used by decrypt() method
|
||
|
*
|
||
|
* @var object of Crypt_RSA_KEY class
|
||
|
* @access private
|
||
|
*/
|
||
|
var $_dec_key;
|
||
|
|
||
|
/**
|
||
|
* public key, which is used by validateSign() method
|
||
|
*
|
||
|
* @var object of Crypt_RSA_KEY class
|
||
|
* @access private
|
||
|
*/
|
||
|
var $_public_key;
|
||
|
|
||
|
/**
|
||
|
* private key, which is used by createSign() method
|
||
|
*
|
||
|
* @var object of Crypt_RSA_KEY class
|
||
|
* @access private
|
||
|
*/
|
||
|
var $_private_key;
|
||
|
|
||
|
/**
|
||
|
* name of hash function, which is used by validateSign()
|
||
|
* and createSign() methods. Default hash function is SHA-1
|
||
|
*
|
||
|
* @var string
|
||
|
* @access private
|
||
|
*/
|
||
|
var $_hash_func = 'sha1';
|
||
|
|
||
|
/**
|
||
|
* Crypt_RSA constructor.
|
||
|
*
|
||
|
* @param array $params
|
||
|
* Optional associative array of parameters, such as:
|
||
|
* enc_key, dec_key, private_key, public_key, hash_func.
|
||
|
* See setParams() method for more detailed description of
|
||
|
* these parameters.
|
||
|
* @param string $wrapper_name
|
||
|
* 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 $error_handler name of error handler function
|
||
|
*
|
||
|
* @access public
|
||
|
*/
|
||
|
function Crypt_RSA($params = null, $wrapper_name = 'default', $error_handler = '')
|
||
|
{
|
||
|
// 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
|
||
|
// Crypt_RSA object is partially constructed.
|
||
|
$this->pushError($obj);
|
||
|
return;
|
||
|
}
|
||
|
$this->_math_obj = &$obj;
|
||
|
|
||
|
if (!is_null($params)) {
|
||
|
if (!$this->setParams($params)) {
|
||
|
// error in Crypt_RSA::setParams() function
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Crypt_RSA factory.
|
||
|
*
|
||
|
* @param array $params
|
||
|
* Optional associative array of parameters, such as:
|
||
|
* enc_key, dec_key, private_key, public_key, hash_func.
|
||
|
* See setParams() method for more detailed description of
|
||
|
* these parameters.
|
||
|
* @param string $wrapper_name
|
||
|
* 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 $error_handler name of error handler function
|
||
|
*
|
||
|
* @return object new Crypt_RSA object on success or PEAR_Error object on failure
|
||
|
* @access public
|
||
|
*/
|
||
|
function &factory($params = null, $wrapper_name = 'default', $error_handler = '')
|
||
|
{
|
||
|
$obj = &new Crypt_RSA($params, $wrapper_name, $error_handler);
|
||
|
if ($obj->isError()) {
|
||
|
// error during creating a new object. Retrurn PEAR_Error object
|
||
|
return $obj->getLastError();
|
||
|
}
|
||
|
// object created successfully. Return it
|
||
|
return $obj;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Accepts any combination of available parameters as associative array:
|
||
|
* enc_key - encryption key for encrypt() method
|
||
|
* dec_key - decryption key for decrypt() method
|
||
|
* public_key - key for validateSign() method
|
||
|
* private_key - key for createSign() method
|
||
|
* hash_func - name of hash function, which will be used to create and validate sign
|
||
|
*
|
||
|
* @param array $params
|
||
|
* associative array of permitted parameters (see above)
|
||
|
*
|
||
|
* @return bool true on success or false on error
|
||
|
* @access public
|
||
|
*/
|
||
|
function setParams($params)
|
||
|
{
|
||
|
if (!is_array($params)) {
|
||
|
$this->pushError('parameters must be passed to function as associative array', CRYPT_RSA_ERROR_WRONG_PARAMS);
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if (isset($params['enc_key'])) {
|
||
|
if (Crypt_RSA_Key::isValid($params['enc_key'])) {
|
||
|
$this->_enc_key = $params['enc_key'];
|
||
|
}
|
||
|
else {
|
||
|
$this->pushError('wrong encryption key. It must be an object of Crypt_RSA_Key class', CRYPT_RSA_ERROR_WRONG_KEY);
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
if (isset($params['dec_key'])) {
|
||
|
if (Crypt_RSA_Key::isValid($params['dec_key'])) {
|
||
|
$this->_dec_key = $params['dec_key'];
|
||
|
}
|
||
|
else {
|
||
|
$this->pushError('wrong decryption key. It must be an object of Crypt_RSA_Key class', CRYPT_RSA_ERROR_WRONG_KEY);
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
if (isset($params['private_key'])) {
|
||
|
if (Crypt_RSA_Key::isValid($params['private_key'])) {
|
||
|
if ($params['private_key']->getKeyType() != 'private') {
|
||
|
$this->pushError('private key must have "private" attribute', CRYPT_RSA_ERROR_WRONG_KEY_TYPE);
|
||
|
return false;
|
||
|
}
|
||
|
$this->_private_key = $params['private_key'];
|
||
|
}
|
||
|
else {
|
||
|
$this->pushError('wrong private key. It must be an object of Crypt_RSA_Key class', CRYPT_RSA_ERROR_WRONG_KEY);
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
if (isset($params['public_key'])) {
|
||
|
if (Crypt_RSA_Key::isValid($params['public_key'])) {
|
||
|
if ($params['public_key']->getKeyType() != 'public') {
|
||
|
$this->pushError('public key must have "public" attribute', CRYPT_RSA_ERROR_WRONG_KEY_TYPE);
|
||
|
return false;
|
||
|
}
|
||
|
$this->_public_key = $params['public_key'];
|
||
|
}
|
||
|
else {
|
||
|
$this->pushError('wrong public key. It must be an object of Crypt_RSA_Key class', CRYPT_RSA_ERROR_WRONG_KEY);
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
if (isset($params['hash_func'])) {
|
||
|
if (!function_exists($params['hash_func'])) {
|
||
|
$this->pushError('cannot find hash function with name [' . $params['hash_func'] . ']', CRYPT_RSA_ERROR_WRONG_HASH_FUNC);
|
||
|
return false;
|
||
|
}
|
||
|
$this->_hash_func = $params['hash_func'];
|
||
|
}
|
||
|
return true; // all ok
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Ecnrypts $plain_data by the key $this->_enc_key or $key.
|
||
|
*
|
||
|
* @param string $plain_data data, which must be encrypted
|
||
|
* @param object $key encryption key (object of Crypt_RSA_Key class)
|
||
|
* @return mixed
|
||
|
* encrypted data as string on success or false on error
|
||
|
*
|
||
|
* @access public
|
||
|
*/
|
||
|
function encrypt($plain_data, $key = null)
|
||
|
{
|
||
|
$enc_data = $this->encryptBinary($plain_data, $key);
|
||
|
if ($enc_data !== false) {
|
||
|
return base64_encode($enc_data);
|
||
|
}
|
||
|
// error during encripting data
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Ecnrypts $plain_data by the key $this->_enc_key or $key.
|
||
|
*
|
||
|
* @param string $plain_data data, which must be encrypted
|
||
|
* @param object $key encryption key (object of Crypt_RSA_Key class)
|
||
|
* @return mixed
|
||
|
* encrypted data as binary string on success or false on error
|
||
|
*
|
||
|
* @access public
|
||
|
*/
|
||
|
function encryptBinary($plain_data, $key = null)
|
||
|
{
|
||
|
if (is_null($key)) {
|
||
|
// use current encryption key
|
||
|
$key = $this->_enc_key;
|
||
|
}
|
||
|
else if (!Crypt_RSA_Key::isValid($key)) {
|
||
|
$this->pushError('invalid encryption key. It must be an object of Crypt_RSA_Key class', CRYPT_RSA_ERROR_WRONG_KEY);
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// append tail \x01 to plain data. It needs for correctly decrypting of data
|
||
|
$plain_data .= "\x01";
|
||
|
|
||
|
$plain_data = $this->_math_obj->bin2int($plain_data);
|
||
|
$exp = $this->_math_obj->bin2int($key->getExponent());
|
||
|
$modulus = $this->_math_obj->bin2int($key->getModulus());
|
||
|
|
||
|
// divide plain data into chunks
|
||
|
$data_len = $this->_math_obj->bitLen($plain_data);
|
||
|
$chunk_len = $key->getKeyLength() - 1;
|
||
|
$block_len = (int) ceil($chunk_len / 8);
|
||
|
$curr_pos = 0;
|
||
|
$enc_data = '';
|
||
|
while ($curr_pos < $data_len) {
|
||
|
$tmp = $this->_math_obj->subint($plain_data, $curr_pos, $chunk_len);
|
||
|
$enc_data .= str_pad(
|
||
|
$this->_math_obj->int2bin($this->_math_obj->powmod($tmp, $exp, $modulus)),
|
||
|
$block_len,
|
||
|
"\0"
|
||
|
);
|
||
|
$curr_pos += $chunk_len;
|
||
|
}
|
||
|
return $enc_data;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Decrypts $enc_data by the key $this->_dec_key or $key.
|
||
|
*
|
||
|
* @param string $enc_data encrypted data as string
|
||
|
* @param object $key decryption key (object of RSA_Crypt_Key class)
|
||
|
* @return mixed
|
||
|
* decrypted data as string on success or false on error
|
||
|
*
|
||
|
* @access public
|
||
|
*/
|
||
|
function decrypt($enc_data, $key = null)
|
||
|
{
|
||
|
$enc_data = base64_decode($enc_data);
|
||
|
return $this->decryptBinary($enc_data, $key);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Decrypts $enc_data by the key $this->_dec_key or $key.
|
||
|
*
|
||
|
* @param string $enc_data encrypted data as binary string
|
||
|
* @param object $key decryption key (object of RSA_Crypt_Key class)
|
||
|
* @return mixed
|
||
|
* decrypted data as string on success or false on error
|
||
|
*
|
||
|
* @access public
|
||
|
*/
|
||
|
function decryptBinary($enc_data, $key = null)
|
||
|
{
|
||
|
if (is_null($key)) {
|
||
|
// use current decryption key
|
||
|
$key = $this->_dec_key;
|
||
|
}
|
||
|
else if (!Crypt_RSA_Key::isValid($key)) {
|
||
|
$this->pushError('invalid decryption key. It must be an object of Crypt_RSA_Key class', CRYPT_RSA_ERROR_WRONG_KEY);
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
$exp = $this->_math_obj->bin2int($key->getExponent());
|
||
|
$modulus = $this->_math_obj->bin2int($key->getModulus());
|
||
|
|
||
|
$data_len = strlen($enc_data);
|
||
|
$chunk_len = $key->getKeyLength() - 1;
|
||
|
$block_len = (int) ceil($chunk_len / 8);
|
||
|
$curr_pos = 0;
|
||
|
$bit_pos = 0;
|
||
|
$plain_data = $this->_math_obj->bin2int("\0");
|
||
|
while ($curr_pos < $data_len) {
|
||
|
$tmp = $this->_math_obj->bin2int(substr($enc_data, $curr_pos, $block_len));
|
||
|
$tmp = $this->_math_obj->powmod($tmp, $exp, $modulus);
|
||
|
$plain_data = $this->_math_obj->bitOr($plain_data, $tmp, $bit_pos);
|
||
|
$bit_pos += $chunk_len;
|
||
|
$curr_pos += $block_len;
|
||
|
}
|
||
|
$result = $this->_math_obj->int2bin($plain_data);
|
||
|
|
||
|
// delete tail, containing of \x01
|
||
|
$tail = ord($result{strlen($result) - 1});
|
||
|
if ($tail != 1) {
|
||
|
$this->pushError("Error tail of decrypted text = {$tail}. Expected 1", CRYPT_RSA_ERROR_WRONG_TAIL);
|
||
|
return false;
|
||
|
}
|
||
|
return substr($result, 0, -1);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Creates sign for document $document, using $this->_private_key or $private_key
|
||
|
* as private key and $this->_hash_func or $hash_func as hash function.
|
||
|
*
|
||
|
* @param string $document document, which must be signed
|
||
|
* @param object $private_key private key (object of Crypt_RSA_Key type)
|
||
|
* @param string $hash_func name of hash function, which will be used during signing
|
||
|
* @return mixed
|
||
|
* signature of $document as string on success or false on error
|
||
|
*
|
||
|
* @access public
|
||
|
*/
|
||
|
function createSign($document, $private_key = null, $hash_func = null)
|
||
|
{
|
||
|
// check private key
|
||
|
if (is_null($private_key)) {
|
||
|
$private_key = $this->_private_key;
|
||
|
}
|
||
|
else if (!Crypt_RSA_Key::isValid($private_key)) {
|
||
|
$this->pushError('invalid private key. It must be an object of Crypt_RSA_Key class', CRYPT_RSA_ERROR_WRONG_KEY);
|
||
|
return false;
|
||
|
}
|
||
|
if ($private_key->getKeyType() != 'private') {
|
||
|
$this->pushError('signing key must be private', CRYPT_RSA_ERROR_NEED_PRV_KEY);
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// check hash_func
|
||
|
if (is_null($hash_func)) {
|
||
|
$hash_func = $this->_hash_func;
|
||
|
}
|
||
|
if (!function_exists($hash_func)) {
|
||
|
$this->pushError("cannot find hash function with name [$hash_func]", CRYPT_RSA_ERROR_WRONG_HASH_FUNC);
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
return $this->encrypt($hash_func($document), $private_key);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Validates $signature for document $document with public key $this->_public_key
|
||
|
* or $public_key and hash function $this->_hash_func or $hash_func.
|
||
|
*
|
||
|
* @param string $document document, signature of which must be validated
|
||
|
* @param string $signature signature, which must be validated
|
||
|
* @param object $public_key public key (object of Crypt_RSA_Key class)
|
||
|
* @param string $hash_func hash function, which will be used during validating signature
|
||
|
* @return mixed
|
||
|
* true, if signature of document is valid
|
||
|
* false, if signature of document is invalid
|
||
|
* null on error
|
||
|
*
|
||
|
* @access public
|
||
|
*/
|
||
|
function validateSign($document, $signature, $public_key = null, $hash_func = null)
|
||
|
{
|
||
|
// check public key
|
||
|
if (is_null($public_key)) {
|
||
|
$public_key = $this->_public_key;
|
||
|
}
|
||
|
else if (!Crypt_RSA_Key::isValid($public_key)) {
|
||
|
$this->pushError('invalid public key. It must be an object of Crypt_RSA_Key class', CRYPT_RSA_ERROR_WRONG_KEY);
|
||
|
return null;
|
||
|
}
|
||
|
if ($public_key->getKeyType() != 'public') {
|
||
|
$this->pushError('validating key must be public', CRYPT_RSA_ERROR_NEED_PUB_KEY);
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
// check hash_func
|
||
|
if (is_null($hash_func)) {
|
||
|
$hash_func = $this->_hash_func;
|
||
|
}
|
||
|
if (!function_exists($hash_func)) {
|
||
|
$this->pushError("cannot find hash function with name [$hash_func]", CRYPT_RSA_ERROR_WRONG_HASH_FUNC);
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
return $hash_func($document) == $this->decrypt($signature, $public_key);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
?>
|