Это код, который я использую для шифрования / дешифрования данных:
// Set the method
$method = 'AES-128-CBC';
// Set the encryption key
$encryption_key = 'myencryptionkey';
// Generet a random initialisation vector
$iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length($method));
// Define the date to be encrypted
$data = "Encrypt me, please!";
var_dump("Before encryption: $data");
// Encrypt the data
$encrypted = openssl_encrypt($data, $method, $encryption_key, 0, $iv);
var_dump("Encrypted: ${encrypted}");
// Append the vector at the end of the encrypted string
$encrypted = $encrypted . ':' . $iv;
// Explode the string using the `:` separator.
$parts = explode(':', $encrypted);
// Decrypt the data
$decrypted = openssl_decrypt($parts[0], $method, $encryption_key, 0, $parts[1]);
var_dump("Decrypted: ${decrypted}");
Обычно он работает нормально, но иногда (1 из 10 или даже реже) не получается. Когда это терпит неудачу, тогда текст только частично зашифрован:
Это сообщение об ошибке, когда это происходит:
Warning: openssl_decrypt(): IV passed is only 10 bytes long, cipher expects an IV of precisely 16 bytes, padding with \0
И когда это происходит, зашифрованный текст выглядит так:
Encrypt me���L�se!
Я думал, что это может быть вызвано ошибкой в PHP, но я тестировал на разных хостах: PHP 7.0.6 и PHP 5.6. Я также пробовал несколько онлайн-анализаторов PHP, таких как phpfidle.org или 3v4l.org.
Кажется, что openssl_random_pseudo_bytes
не всегда возвращает строку правильной длины, но я понятия не имею, почему.
Вот образец: https://3v4l.org/RZV8d
Продолжайте обновлять страницу, вы получите ошибку в какой-то момент.
Когда вы генерируете случайный IV, вы получаете сырой двоичный файл. Существует ненулевой шанс того, что двоичные строки будут содержать :
или же \0
персонаж, который вы используете, чтобы отделить IV от зашифрованного текста. Это делает explode()
дать вам более короткую строку. Демо-версия: https://3v4l.org/3ObfJ
Тривиальным решением было бы добавить кодирование / декодирование base64 к этому процессу.
Это сказало, пожалуйста, не катите свой собственный крипто. Особенно, шифрование без аутентификации опасно а также уже есть безопасные библиотеки, которые решают эту проблему.
Вместо того, чтобы писать свой, рассмотрите возможность использования разрядить / PHP-шифрование. Это безопасный выбор.
Я обновил код из первого поста и обернул его в классе. Это фиксированный код, основанный на решении, предоставленном Скотт Аркишевский.
class Encryptor
{
/**
* Holds the Encryptor instance
* @var Encryptor
*/
private static $instance;
/**
* @var string
*/
private $method;
/**
* @var string
*/
private $key;
/**
* @var string
*/
private $separator;
/**
* Encryptor constructor.
*/
private function __construct()
{
$app = App::getInstance();
$this->method = $app->getConfig('encryption_method');
$this->key = $app->getConfig('encryption_key');
$this->separator = ':';
}
private function __clone()
{
}
/**
* Returns an instance of the Encryptor class or creates the new instance if the instance is not created yet.
* @return Encryptor
*/
public static function getInstance()
{
if (self::$instance === null) {
self::$instance = new Encryptor();
}
return self::$instance;
}
/**
* Generates the initialization vector
* @return string
*/
private function getIv()
{
return openssl_random_pseudo_bytes(openssl_cipher_iv_length($this->method));
}
/**
* @param string $data
* @return string
*/
public function encrypt($data)
{
$iv = $this->getIv();
return base64_encode(openssl_encrypt($data, $this->method, $this->key, 0, $iv) . $this->separator . base64_encode($iv));
}
/**
* @param string $dataAndVector
* @return string
*/
public function decrypt($dataAndVector)
{
$parts = explode($this->separator, base64_decode($dataAndVector));
// $parts[0] = encrypted data
// $parts[1] = initialization vector
return openssl_decrypt($parts[0], $this->method, $this->key, 0, base64_decode($parts[1]));
}
}
$encryptor = Encryptor::getInstance();
$encryptedData = $encryptor->encrypt('Encrypt me please!');
var_dump($encryptedData);
$decryptedData = $encryptor->decrypt($encryptedData);
var_dump($decryptedData);