以 SagePay 形式将 mcrypt 升级为 OpenSSL 加密
Posted
技术标签:
【中文标题】以 SagePay 形式将 mcrypt 升级为 OpenSSL 加密【英文标题】:Upgrade mcrypt to OpenSSL encryption in SagePay form 【发布时间】:2020-04-12 10:52:37 【问题描述】:我有一个收集数据的表单,然后将其提交给 SagePay,传递数据。在我们需要更新到 php 7.2 之前,这一直很好,并且由于不再支持 mcrypt,我们正在切换到 OpenSSL。
收集数据的表单工作正常,但传输数据没有加密,交易失败。
函数文件中的代码:
$protx_encryption_password = "my_password";
$protx_vendor = "my_vendor";
$data = "";
while(list($key,$value) = each($protx))
$data .= $key."=".$value."&";
$data = trim($data," &");
$crypt = openssl_encrypt($data, $protx_encryption_password);
function openssl_encrypt($string, $key)
$iv = substr($value, 0, 16);
$ciphertext = substr($value, 16);
$key = hash('sha256', $key, true);
$crypt = openssl_encrypt(
$ciphertext, 'AES-256-CBC', $key,
OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING,
$iv
);
return rtrim($crypt, "\0");
$crypt 数据像以前在 mcrypt 版本中一样在隐藏字段中发送,我只需要一些帮助来使加密工作。
以前用mcrypt方式:
$crypt = encryptAes($data, $protx_encryption_password);
function encryptAes($string, $key)
// AES encryption, CBC blocking with PKCS5 padding then HEX encoding.
// Add PKCS5 padding to the text to be encypted.
$string = addPKCS5Padding($string);
// Perform encryption with PHP's MCRYPT module.
$crypt = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $string, MCRYPT_MODE_CBC, $key);
// Perform hex encoding and return.
return "@" . strtoupper(bin2hex($crypt));
function addPKCS5Padding($input)
$blockSize = 16;
$padd = "";
// Pad input to an even block size boundary.
$length = $blockSize - (strlen($input) % $blockSize);
for ($i = 1; $i <= $length; $i++)
$padd .= chr($length);
return $input . $padd;
这是我完成的第一个 OpenSSL 加密,因此我将不胜感激。 SagePay 需要在带有 PCKS#5 (AES-128-CBC-PKCS#5) 的 CBC 模式下使用 128 位块大小的 AES-128 加密进行加密。任何帮助都会非常有帮助。
【问题讨论】:
如果你想要填充,不要指定 OPENSSL_ZERO_PADDING。 openssl_encrypt 默认使用 PKCS#7,相当于 PKCS#5 用于 AES。并且不要弄乱密文。如果你有尾随零字节,它们应该在那里。 【参考方案1】:首先,这是一个糟糕的方案。使用(真实)密码作为现代密码学的密钥,即使在可能的情况下,也几乎总是不安全的。密码和密钥是不同的东西,它们的设计和指定方式不同。当然,有些不知道自己在做什么的人称实际上是密钥的东西是密码,因为我不知道 SagePay(当然你也不会给出真实的值)我不知道这里是不是这样。此外,使用密钥(或密码)作为 IV 是非常不标准的。 IV 的全部意义在于对于给定密钥下的每个加密(或至少每个数据值)不同,而根据定义,给定密钥与其自身相同。这太愚蠢了,我不记得看到任何关于它是否启用以及启用什么攻击的分析,尽管作为一般规则,违反“安全合同”通常会导致问题。对 IV 使用 any 固定值,这是一个相当常见的错误,对于 CBC 模式只有中等程度的不利影响,尽管对于某些其他模式来说更糟。
但是,您显然没有选择更改此方案的选项,如果您这样做了,它就不是真正的编程 Q,而是属于 security.SX 或者可能是 crypto.SX,具体取决于。
另外仅供参考,说“带有 128 位块的 AES-128”是毫无意义的。 AES 总是 使用 128 位块;这是建立它的竞争规则的一部分。 Rijndael 确实有其他块大小的选项,这就是为什么 mcrypt,它或多或少地实现了 Rijndael,指定了块大小,但 OpenSSL 实现了 AES,它不需要也没有指定块大小 -- 仅 密钥大小。
其次,您提出的新代码是无稽之谈。首先,您尝试重新定义一个不允许的标准函数,然后您提出的逻辑显然旨在解密而不是加密,这是一种完全不同的方案,而不是您提供的方案,然后被破坏了。
mcrypt_encrypt
对明文进行零填充,在旧版本中,根据需要填充密钥和 iv。对于 Rijndael,它还根据提供的密钥选择密钥大小;既然您说您想要 AES-128(即 128 位密钥),所谓的“密码”不得超过 128 位,但可能更少。您发布的代码对数据进行了显式 PKCS5 填充,因此不使用 mcrypt 的零填充,正如 Peter 评论的那样,openssl_encrypt
(以及它在下面使用的 OpenSSL 例程)默认情况下会进行 PCKS5/7 填充,所以您只需要是:
function encryptAes_new ($string, $key)
$key = str_pad($key,16,"\0"); # if supplied key is, or may be, less than 16 bytes
$crypt = openssl_encrypt($string, 'aes-128-cbc', $key, OPENSSL_RAW_DATA, $key);
// Perform hex encoding and return.
return "@" . strtoupper(bin2hex($crypt));
(注意:原始 PKCS5 填充仅处理 64 位/8 字节块,PKCS7 将其扩展为其他大小,但 2017 年的 PKCS5v2.1 也将其扩展为引用 CMS,即 PKCS7 的 IETF 版本,因此它们是现在一样。)
【讨论】:
感谢 Dave 的解释和代码 sn-p,我得以实现并工作。以上是关于以 SagePay 形式将 mcrypt 升级为 OpenSSL 加密的主要内容,如果未能解决你的问题,请参考以下文章
Laravel 升级到 4.2 - 需要 mcrypt 扩展
如何在 MacOS 12 上为 php 7.1 安装 mcrypt 扩展以进行 Laravel 开发?