openssl_encrypt()随机失败 - IV传递的只有$ {x}个字节长,cipher期望一个精确到16字节的IV

wub*_*ube 6 php encryption aes encryption-symmetric php-openssl

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

// 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}");
Run Code Online (Sandbox Code Playgroud)

它ususaly工作正常,但有时(十分之一,甚至更少)它失败.如果失败,则文本仅部分加密:

这是发生时的错误消息:

Warning: openssl_decrypt(): IV passed is only 10 bytes long, cipher expects an IV of precisely 16 bytes, padding with \0
Run Code Online (Sandbox Code Playgroud)

当它发生时,加密文本看起来像:

Encrypt me???L?se!
Run Code Online (Sandbox Code Playgroud)

我认为它可能是由PHP中的错误引起的,但我已经在不同的主机上测试过:PHP 7.0.6和PHP 5.6.我也尝试了多个在线PHP解析器,如phpfidle.org或3v4l.org.

似乎openssl_random_pseudo_bytes并不总是返回一个适当长度的字符串,但我不知道为什么.

以下是示例:https://3v4l.org/RZV8d

继续刷新页面,你会在某些时候得到错误.

Sco*_*ski 12

当您生成随机IV时,您将获得原始二进制文件.二进制字符串将包含一个:或一个\0字符的非零机会,您将其用于将IV与密文分开.这样做可以explode()为您提供更短的字符串.演示:https://3v4l.org/3ObfJ

简单的解决方案是将base64编码/解码添加到此过程.


也就是说,请不要滚动自己的加密.特别是,未经身份验证的加密是危险的,并且已经存在可以解决此问题的安全库.

不要自己编写,而是考虑使用defuse/php-encryption.这是安全的选择.