openssl_encrypt () случайно завершается неудачно — переданный IV имеет длину всего $ {x} байтов, шифр ожидает IV ровно 16 байтов

Это код, который я использую для шифрования / дешифрования данных:

// 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

Продолжайте обновлять страницу, вы получите ошибку в какой-то момент.

5

Решение

Когда вы генерируете случайный IV, вы получаете сырой двоичный файл. Существует ненулевой шанс того, что двоичные строки будут содержать : или же \0 персонаж, который вы используете, чтобы отделить IV от зашифрованного текста. Это делает explode() дать вам более короткую строку. Демо-версия: https://3v4l.org/3ObfJ

Тривиальным решением было бы добавить кодирование / декодирование base64 к этому процессу.


Это сказало, пожалуйста, не катите свой собственный крипто. Особенно, шифрование без аутентификации опасно а также уже есть безопасные библиотеки, которые решают эту проблему.

Вместо того, чтобы писать свой, рассмотрите возможность использования разрядить / PHP-шифрование. Это безопасный выбор.

10

Другие решения

Вот решение

Я обновил код из первого поста и обернул его в классе. Это фиксированный код, основанный на решении, предоставленном Скотт Аркишевский.

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);
1

По вопросам рекламы [email protected]