如何解决将Mcrypt升级为SagePay网关形式的OpenSSL加密
当前,我们以SagePay网关形式具有mcyrpt加密,PHP 7.4中不提供或不支持该加密。
对于我们如何将下面的代码从mcyrpt更改为OpenSSL,会有任何想法吗?
// AES encryption,CBC blocking with PKCS5 padding then HEX encoding.
// Add PKCS5 padding to the text to be encypted.
$string = self::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));
}
/**
* Decode a returned string from SagePay.
*
* @param string $strIn The encrypted String.
* @param string $password The encyption password used to encrypt the string.
*
* @return string The unecrypted string.
* @throws SagepayApiException
*/
static public function decryptAes($strIn,$password)
{
// HEX decoding then AES decryption,CBC blocking with PKCS5 padding.
// Use initialization vector (IV) set from $str_encryption_password.
$strInitVector = $password;
// Remove the first char which is @ to flag this is AES encrypted and HEX decoding.
$hex = substr($strIn,1);
// Throw exception if string is malformed
if (!preg_match('/^[0-9a-fA-F]+$/',$hex))
{
throw new SagepayApiException('Invalid encryption string');
}
$strIn = pack('H*',$hex);
// Perform decryption with PHP's MCRYPT module.
$string = mcrypt_decrypt(MCRYPT_RIJNDAEL_128,$password,$strIn,$strInitVector);
return self::removePKCS5Padding($string);
}
下面是我们正在尝试的OpenSSL代码,但需要进行一些调整,对于需要为使OpenSSL代码代替上面的mcyrpt而需要更改的任何建议,将不胜感激!
解决方法
发布的 openssl 代码有效,即可以用encrypt_openssl
解密用decrypt_openssl
加密的纯文本。但是, openssl 代码与 mcrypt 代码在功能上并不相同,即,用 mcrypt 代码加密的数据无法用解密。 openssl 代码,反之亦然。
我对工作流程不太清楚,但我怀疑SagePay网关还使用了 mcrypt 代码的加密和解密逻辑,因此进行通信时,{ {1}}代码在功能上与openssl
代码相同(尽管mcrypt
代码的实现比 mcrypt 代码更安全)。
不幸的是,代码的重要部分没有发布,例如加密方法不完整,openssl
和addPKCS5Padding
完全丢失,因此必须使用部分假设。
关于 mcrypt 代码适用:
- AES-128用于CBC模式。 AES-128需要一个16个字节的密钥。
- 使用IV作为密钥。请注意,这通常是不安全的,here。
- mcrypt 本身在隐式应用零填充,如果纯文本的长度已经对应于块大小的整数倍,则不执行填充。由于首先完成了使用PKCS5(实际上是PKCS7)的自定义填充,因此 mcrypt 填充不会生效,因此有效地应用了PKCS5填充。不幸的是, addPKCS5Padding 和 removePKCS5Padding 的实现未发布,因此无法检查它们是否都符合标准。解密后,将删除PKCS5填充(不同于 mcrypt 的内置零填充)。
- 加密使用大写字母对密文进行十六进制编码,并以
removePKCS5Padding
为前缀。
关于 openssl ,必须考虑以下几点:
- openssl 默认情况下使用PKCS7填充(在解密过程中默认情况下删除),其功能与 mcrypt 代码中使用的填充相同(至少在 mcrypt 代码中使用的自定义PKCS5填充与标准匹配)。
-
@
返回默认编码的密文Base64(并且 openssl_decrypt 也希望采用这种编码)。标志openssl_encrypt
导致 openssl_encrypt 将数据作为原始数据返回(并且 openssl_decrypt 也期望原始数据)
在所假设的范围内,以下 openssl 实现在功能上与 mcrypt 实现相同。必须使用16个字节的密钥:
OPENSSL_RAW_DATA
编辑:
两种方法function encrypt_openssl($msg,$key,$iv) {
$encryptedMessage = openssl_encrypt($msg,'AES-128-CBC',OPENSSL_RAW_DATA,$iv);
return '@' . strtoupper(bin2hex($encryptedMessage));
}
function decrypt_openssl($encMsg,$iv) {
$encMsg = substr($encMsg,1);
// your validation stuff
$encMsg = hex2bin($encMsg);
return openssl_decrypt($encMsg,$iv);
}
和addPKCS5Padding
符合PKCS7标准。无需修改。这些功能不得在 openssl 函数中使用,因为 openssl 隐式使用PKCS7填充,因此将其填充两次,这将导致不同的密文。
SagePay 代码的加密与我发布的 openssl 函数的加密可以很容易地测试:
使用 SagePay 代码进行加密(可以执行该代码,例如here选择V 7.0.x或更低版本)
removePKCS5Padding
输出:
<?php
class SagepayApiException extends Exception {}
class SagePayTest {
static public function encryptAes($string,$key)
{
$string = self::addPKCS5Padding($string);
$crypt = mcrypt_encrypt(MCRYPT_RIJNDAEL_128,$string,MCRYPT_MODE_CBC,$key);
return "@" . strtoupper(bin2hex($crypt));
}
static public function decryptAes($strIn,$password)
{
$strInitVector = $password;
$hex = substr($strIn,1);
if (!preg_match('/^[0-9a-fA-F]+$/',$hex))
{
throw new SagepayApiException('Invalid encryption string');
}
$strIn = pack('H*',$hex);
$string = mcrypt_decrypt(MCRYPT_RIJNDAEL_128,$password,$strIn,$strInitVector);
return self::removePKCS5Padding($string);
}
static protected function addPKCS5Padding($input)
{
$blockSize = 16;
$padd = "";
$length = $blockSize - (strlen($input) % $blockSize);
for ($i = 1; $i <= $length; $i++)
{
$padd .= chr($length);
}
return $input . $padd;
}
static protected function removePKCS5Padding($input)
{
$blockSize = 16;
$padChar = ord($input[strlen($input) - 1]);
if ($padChar > $blockSize)
{
throw new SagepayApiException('Invalid encryption string');
}
if (strspn($input,chr($padChar),strlen($input) - $padChar) != $padChar)
{
throw new SagepayApiException('Invalid encryption string');
}
$unpadded = substr($input,(-1) * $padChar);
if (preg_match('/[[:^print:]]/',$unpadded))
{
throw new SagepayApiException('Invalid encryption string');
}
return $unpadded;
}
}
$plaintext = "The quick brown fox jumps over the lazy dog";
$testkey = "95tjbs763khd9zh7";
$encrypted = SagePayTest::encryptAes($plaintext,$testkey);
$decrypted = SagePayTest::decryptAes($encrypted,$testkey);
print("Ciphertext (hex): " . $encrypted . PHP_EOL);
print("Decrypted (hex): " . bin2hex($decrypted) . PHP_EOL);
print("Decrypted : " . $decrypted . PHP_EOL);
?>
使用 openssl 函数进行加密(可以执行代码here)
Ciphertext (hex): @67D4D4E0019BA7D0E75CC92C41488EF13613C1ED8A95B60E56347D1514AC5D7AF780F4314CA047A487AB67DF2D94174A
Decrypted (hex): 54686520717569636b2062726f776e20666f78206a756d7073206f76657220746865206c617a7920646f67
Decrypted : The quick brown fox jumps over the lazy dog
输出:
<?php
function encrypt_openssl($msg,$iv);
}
$plaintext = "The quick brown fox jumps over the lazy dog";
$testkey = "95tjbs763khd9zh7";
$encrypted = encrypt_openssl($plaintext,$testkey,$testkey);
$decrypted = decrypt_openssl($encrypted,$testkey);
print("Ciphertext (hex): " . $encrypted . PHP_EOL);
print("Decrypted (hex): " . bin2hex($decrypted) . PHP_EOL);
print("Decrypted : " . $decrypted . PHP_EOL);
?>
加密和解密都匹配。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。