8

这是我用来加密/解密数据的代码:

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

继续刷新页面,您会在某个时候收到错误消息。

4

3 回答 3

16

当你生成一个随机 IV 时,你会得到raw binary。二进制字符串包含一个:\0字符的可能性非零,您使用它来将 IV 与密文分开。这样做explode()会给你一个更短的字符串。演示:https ://3v4l.org/3ObfJ

简单的解决方案是在此过程中添加 base64 编码/解码。


也就是说,请不要推出自己的加密货币。特别是,未经身份验证的加密是危险的,并且已经有解决此问题的安全库

与其自己编写,不如考虑只使用defuse/php-encryption。这是安全的选择。

于 2016-05-25T14:36:06.433 回答
1

这是解决方案

我已经更新了第一篇文章中的代码并将其包装在一个类中。这是基于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);
于 2016-05-25T18:19:44.893 回答
0

这也发生在我身上。我得到了类似的错误

openssl_decrypt(): IV passed is only 14 bytes long, cipher expects an IV of precisely 16 bytes, padding with \0

我正在使用小写方法,例如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 相同。

于 2020-11-26T09:26:04.737 回答