是否可以从PEAR Crypt迁移到PHPSecLib RSA?

时间:2017-11-10 21:20:33

标签: php encryption rsa pear phpseclib

我们刚刚迁移到PHPSecLib,但没有迁移旧的PEAR \ Crypt_RSA库加密的旧数据。我们已经达到了将数据迁移到PHPSecLib的RSA格式的程度。在调查这个问题时,我遇到了this old forum thread。我试图在响应中应用建议,但无法成功解密我们的数据。它没有错误或任何错误,它似乎仍然是加密或编码的。我们目前正在运行PHPSecLib 2.0.6,我怀疑这些说明适用于1.x.

这是我改编的解密流程的粗略版本(基于论坛帖子):

$rsaDecryptor = new RSA();

// The Private Key is encrypted based off a password
$mc      = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_ECB, '');
$iv      = mcrypt_create_iv(mcrypt_enc_get_iv_size($mc), MCRYPT_DEV_URANDOM);
$keySize = mcrypt_enc_get_key_size($mc);
$key     = substr($rsaDecryptor->password, 0, $keySize);

mcrypt_generic_init($mc, $key, $iv);
$privateKey = mdecrypt_generic($mc, base64_decode($privateKey));
mcrypt_generic_deinit($mc);
mcrypt_module_close($mc);

list($privateKeyModulus, $privateKeyExponent) = unserialize(base64_decode($privateKey));

$privateKeyExponent = new BigInteger(strrev($privateKeyExponent), 256);
$privateKeyModulus = new BigInteger(strrev($privateKeyModulus), 256);

$rsaDecryptor->modulus        = $privateKeyModulus;
$rsaDecryptor->exponent       = $privateKeyExponent;
$rsaDecryptor->publicExponent = $privateKeyExponent;
$rsaDecryptor->k              = strlen($this->decRSA->modulus->toBytes());

// ciphertext is the raw encrypted string created by PEAR\Crypt_RSA
$value = base64_decode($ciphertext);
$value = new BigInteger($value, 256);
$value = $rsaDecryptor->_exponentiate($value)->toBytes();
$value = substr($value, 1);

1 个答案:

答案 0 :(得分:1)

PEAR的Crypt_RSA中的错误

所以我正在玩这个。 PEAR的Crypt_RSA(最新版本)中存在一个错误,可能会阻止它工作。以下代码演示了:

$key_pair = new Crypt_RSA_KeyPair(1024);
$privkey = $key_pair->getPrivateKey();
$pubkey = $key_pair->getPublicKey();
$a = $privkey->toString();
$b = $pubkey->toString();

echo $a == $b ? 'same' : 'different';

你希望$ a和$ b不同,不是吗?好吧,他们不是。这是因为RSA / KeyPair.php执行此操作:

$this->_public_key = &$obj;
...
$this->_private_key = &$obj;

如果你删除&符号它可以正常工作,但默认情况下它们在代码中。

https://pear.php.net/bugs/bug.php?id=15900

开始,这似乎是一个未解决的问题

也许它在PHP4上表现不同但我不知道。

解密数据

假设上面的错误对您来说不是问题,那么以下内容对我有用(使用phpseclib 2.0):

function loadKey($key) // for keys genereated with $key->toString() vs $key->toPEMString()
{
    if (!($key = base64_decode($key))) {
        return false;
    }
    if (!($key = unserialize($key))) {
        return false;
    }
    list($modulus, $exponent) = $key;
    $modulus = new BigInteger(strrev($modulus), 256);
    $exponent = new BigInteger(strrev($exponent), 256);
    $rsa = new RSA();
    $rsa->loadKey(compact('modulus', 'exponent'));
    return $rsa;
}

function decrypt($key, $ciphertext)
{
    if (!($ciphertext = base64_decode($ciphertext))) {
        return false;
    }
    $key->setEncryptionMode(RSA::ENCRYPTION_NONE);
    $ciphertext = strrev($ciphertext);
    $plaintext = $key->decrypt($ciphertext);
    $plaintext = strrev($plaintext);
    $plaintext = substr($plaintext, 0, strpos($plaintext, "\0"));
    return $plaintext[strlen($plaintext) - 1] == "\1" ?
        substr($plaintext, 0, -1) : false;
}

$key = loadKey($private_key);
$plaintext = decrypt($key, $ciphertext);
echo $plaintext;

使用toPEMString()生成的私钥

使用PEAR的Crypt_RSA,您可以使用另一种方式生成私钥:

$key_pair->toPEMString();

此方法无需更改代码即可运行。如果您使用此方法生成私钥,则私钥从-----BEGIN RSA PRIVATE KEY-----开始。如果是这种情况,那么您不需要使用我编写的loadKey函数。你可以这样做:

$key = new RSA();
$key->loadKey('...');
$plaintext = decrypt($key, $ciphertext);
echo $plaintext;