每个人都应该已经停止使用PHP Mcrypt扩展程序进行新的工作,并且应该计划将其现有的应用程序关闭,因为libmcrypt在2003年被放弃,并且不被保护。
我认为最好的选择是OpenSSL。近年来热度很高,但我认为对称块加密是一个很好的选择。 (我可能会解释为什么我这么想呢)
PHP中的 openssl_encrypt()和openssl_decrypt()的文档有点缺乏。本文旨在填补一些空白。这是两者的签名。
string openssl_encrypt ( string $data , string $method , string $key [, int $options = 0 [, string $iv = “” ]] )
string openssl_decrypt ( string $data , string $method , string $key [, int $options = 0 [, string $iv = “” ]] )
我们将始终使用一个初始化向量,结果是$options有很大的不同,所以我们可以简化:
string openssl_encrypt ( string $data , string $method , string $key , int $options, string $iv )
string openssl_decrypt ( string $data , string $method , string $password , int $options, string $iv )
其中$opention将是OPENSSL_RAW_DATA
或OPENSSL_ZERO_PADDING
它们还需要为您使用的密码算法准备好密钥。我只使用密钥大小为16字节的AES-128。如果您通过openssl_en/decrypt()
一个密钥(在$ password参数中)长于密码的本质密钥大小,则会将该余数丢弃。如果通过一个短于预期的键,则填充为零,即\x00字节。
所以你需要仔细准备你的钥匙。如果用户提供密码,请使用类似PBKDF2的特殊盐。如果您有输入加密密钥,则可以使用HKDF导出openssl_en/decrypt()
的密钥。
所以让我们再简化一下:
string openssl_encrypt ( string $data , string $method , string $key , int $options, string $iv )
string openssl_decrypt ( string $data , string $method , string $key , int $options, string $iv )
如果您正在生成密钥,而不是从用户输入密钥或密码中导出密钥,则应使用从加密安全伪随机数生成器或CSPRNG中提取的随机字节的二进制字符串.(我也许会博客介绍如何在PHP中获取这样的字符串,实际上并不像调用openssl_random_pseudo_bytes()
一样简单。)
初始化向量 $iv
初始化向量$iv
的要求类似于$password
的要求。它应该是与密码块大小相同长度的二进制字符串。过多的字节被丢弃,并且太短的$iv
被填充到零字节的块大小。
初始化向量应该是来自CSPRNG的随机字节的二进制串。不要重复使用IV。
密码规范 $method
$method
允许值由PHP平台上的openssl_get_cipher_methods()
函数列出,并在openssl enc文档中更详细地列出。
我不打算讨论这些的相对优点。我唯一使用的是CBC模式的AES。除了RC5(由RAS获得专利但未被广泛使用)外,OpenSSL提供的所有其他对称块密码都具有64位或更小的块。
我总是使用AES-128,它有一个16字节的键。所以这意味着我会选择“AES-128-CBC”。我想你可以安全地选择’AES-192-CBC’或’AES-256-CBC’,但是我不会被引用到最好的论点。
在任何情况下,$method
都必须是openssl_get_cipher_methods()
返回的字符串之一,您必须使用适当长度的$iv
和$key
才能使用该密码。
编码、填充和OPENSSL_RAW_DATA
与OPENSSL_ZERO_PADDING
在Mcrypt中,加密和解密的输入和输出编码是原始的二进制字符串。 Paintext和ciphertext都是二进制字符串。
在CBC或ECB模式下使用块密码加密之前,Mcrypt会自动将零字节填充添加到明文中,并在解密之后返回零字节填充的明文(即,Mcrypt在解密之后不删除填充)。
注意:以下是错误,请参阅下面My1的注释。选项表示两个标志:将OPENSSL_RAW_DATA位设置为使用二进制字符串,并将其设置为base64。设置OPENSSL_ZERO_PADDING位来进行自己的填充或取消设置,openssl_en/decrypt()做PKCS7填充。
OpenSSL有两种模式:
– $options = OPENSSL_RAW_DATA
– openssl_encrypt()和openssl_decrypt()的输入和输出编码都是二进制字符串,即plaintext和cipertext都是二进制字符串。
– openssl_encrypt()在CBC或ECB模式下使用块密码加密之前,将PKCS7填充添加到明文中。 –
– openssl_decrypt()解密之后剥离填充。
– $ options = OPENSSL_ZERO_PADDING
– openssl_encrypt()的输入编码和openssl_decrypt()的输出编码是原始的二进制字符串,即明文是原始的二进制字符串。
– 输出编码fron openssl_encrypt()和对openssl_decrypt()的输入编码是base64,即密文是base64编码的。
– openssl_encrypt()的输入大小必须是块大小的整数倍,否则返回错误代码。 –
– openssl_encrypt()不添加填充,或者由openssl_decrypt()删除,
可以做任何工作,但我更喜欢OPENSSL_RAW_DATA
。如果我需要密文的特定编码,那我宁愿自己做。而PKCS7
将是我的填充选择,实际上这是我用于Mcrypt的。
总结
已经弄清了这两个功能的所有这些未记录的功能,结果是我打算使用OpenSSL进行对称加密的唯一方法是这样的:
$ciphertext = openssl_encrypt($plaintext, 'AES-128-CBC', $key, OPENSSL_RAW_DATA, $iv);
$plaintext = openssl_decrypt($ciphertext, 'AES-128-CBC', $key, OPENSSL_RAW_DATA, $iv);
其中:
– $plaintext是任何字符串,任何长度。
– $key是16字节长的二进制字符串形式的加密密钥(因为AES-128的密钥大小为16字节)
– $iv是一个16位字节长的加密安全随机二进制字符串(因为AES具有16个字节的块大小),我将只使用一次
– $ciphertext是一个比$plaintext长1到16个字节之间的二进制字符串(因为PKCS7填充到16字节块大小)
并记住加密密钥必须从输入密钥材料或密码中正确派生或由CSPRNG生成。
结语
记住,如果您需要加密数据,那么您几乎肯定也需要进行身份验证。为此,您可以使用HMAC签名。使用SHA-2算法之一。 还要记住最后签名并进行认证,即HMAC需要覆盖包含加密数据的整个消息