openssl_encrypt() 随机失败 - 传递的 IV 仅为 ${x} 字节长,密码预计 IV 恰好为 16 字节
openssl_encrypt() randomly fails - IV passed is only ${x} bytes long, cipher expects an IV of precisely 16 bytes
这是我用来 encrypt/decrypt 数据的代码:
// 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}");
它通常工作正常,但有时(十分之一或更少)它会失败。当它失败时,文本仅被部分加密:
这是发生时的错误信息:
Warning: openssl_decrypt(): IV passed is only 10 bytes long, cipher expects an IV of precisely 16 bytes, padding with [=12=]
当它发生时,加密的文本看起来像:
Encrypt me���L�se!
我认为这可能是由 PHP 中的错误引起的,但我在不同的主机上进行了测试:PHP 7.0.6 和 PHP 5.6。我还尝试了多个在线 PHP 解析器,例如 phpfidle.org 或 3v4l.org.
似乎openssl_random_pseudo_bytes
并不总是returns一个适当长度的字符串,但我不知道为什么。
继续刷新页面,有时会报错
当您生成随机 IV 时,您会得到 raw binary. There's a nonzero chance that the binary strings will contain a :
or [=11=]
character, which you're using to separate the IV from the ciphertext. Doing so makes explode()
give you a shorter string. Demo: https://3v4l.org/3ObfJ
简单的解决方案是将 base64 encoding/decoding 添加到此过程。
也就是说,please don't roll your own crypto. In particular, unauthenticated encryption is dangerous and there are already secure libraries that solve this problem。
与其自己编写,不如考虑使用 defuse/php-encryption。这是安全的选择。
这是解决方案
我已经更新了第一个 post 的代码并将其封装在 class 中。这是基于 Scott Arciszewski.
提供的解决方案的固定代码
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);
这也发生在我身上。我收到类似
的错误
openssl_decrypt(): IV passed is only 14 bytes long, cipher expects an IV of precisely 16 bytes, padding with [=10=]
我使用的是像 openssl_cipher_iv_length('aes-128-cbc')
这样的小写方法
小写 aes-- 方法给出的结果长度在 12 到 16 之间变化。参考:https://www.php.net/manual/en/function.openssl-cipher-iv-length.php
将方法设为大写 openssl_cipher_iv_length('AES-128-CBC')
将给出一致的值,即 16。
因此在加密和解密时 IV 长度保持与 16 相同。
这是我用来 encrypt/decrypt 数据的代码:
// 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}");
它通常工作正常,但有时(十分之一或更少)它会失败。当它失败时,文本仅被部分加密:
这是发生时的错误信息:
Warning: openssl_decrypt(): IV passed is only 10 bytes long, cipher expects an IV of precisely 16 bytes, padding with [=12=]
当它发生时,加密的文本看起来像:
Encrypt me���L�se!
我认为这可能是由 PHP 中的错误引起的,但我在不同的主机上进行了测试:PHP 7.0.6 和 PHP 5.6。我还尝试了多个在线 PHP 解析器,例如 phpfidle.org 或 3v4l.org.
似乎openssl_random_pseudo_bytes
并不总是returns一个适当长度的字符串,但我不知道为什么。
继续刷新页面,有时会报错
当您生成随机 IV 时,您会得到 raw binary. There's a nonzero chance that the binary strings will contain a :
or [=11=]
character, which you're using to separate the IV from the ciphertext. Doing so makes explode()
give you a shorter string. Demo: https://3v4l.org/3ObfJ
简单的解决方案是将 base64 encoding/decoding 添加到此过程。
也就是说,please don't roll your own crypto. In particular, unauthenticated encryption is dangerous and there are already secure libraries that solve this problem。
与其自己编写,不如考虑使用 defuse/php-encryption。这是安全的选择。
这是解决方案
我已经更新了第一个 post 的代码并将其封装在 class 中。这是基于 Scott Arciszewski.
提供的解决方案的固定代码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);
这也发生在我身上。我收到类似
的错误openssl_decrypt(): IV passed is only 14 bytes long, cipher expects an IV of precisely 16 bytes, padding with [=10=]
我使用的是像 openssl_cipher_iv_length('aes-128-cbc')
小写 aes-- 方法给出的结果长度在 12 到 16 之间变化。参考:https://www.php.net/manual/en/function.openssl-cipher-iv-length.php
将方法设为大写 openssl_cipher_iv_length('AES-128-CBC')
将给出一致的值,即 16。
因此在加密和解密时 IV 长度保持与 16 相同。