将 TripleDESCryptoServiceProvider 从 C# 移植到 PHP
Posted
技术标签:
【中文标题】将 TripleDESCryptoServiceProvider 从 C# 移植到 PHP【英文标题】:Porting TripleDESCryptoServiceProvider from C# to PHP 【发布时间】:2018-03-30 01:05:22 【问题描述】:我正在尝试将以下 C# 方法移植到 php,但无法正确输出。
public static string Encrypt()
String text = "123456";
String key = "1r1ppl3x";
byte[] arrText = UTF8Encoding.UTF8.GetBytes(text);
TripleDESCryptoServiceProvider tdes = new TripleDESCryptoServiceProvider();
tdes.Key = (new UnicodeEncoding()).GetBytes(key);
tdes.Mode = CipherMode.ECB;
tdes.Padding = PaddingMode.PKCS7;
ICryptoTransform cTransform = tdes.CreateEncryptor();
byte[] resultArray = cTransform.TransformFinalBlock(arrText, 0, arrText.Length);
tdes.Clear();
String encrypted = Convert.ToBase64String(resultArray, 0, resultArray.Length);
return encrypted;
到目前为止我在 PHP 中的尝试:
function encrypt()
$key = utf8_encode("1r1ppl3x\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0");
$data = utf8_encode("123456");
$blocksize = mcrypt_get_block_size(MCRYPT_TRIPLEDES, MCRYPT_MODE_ECB);
$tripleKey = substr($key, 0, mcrypt_get_key_size(MCRYPT_TRIPLEDES, MCRYPT_MODE_ECB));
$paddingSize = $blocksize - (strlen($data) % $blocksize);
$data .= str_repeat(chr($paddingSize), $paddingSize);
$encodedText = mcrypt_encrypt(MCRYPT_TRIPLEDES, $tripleKey, $data, MCRYPT_MODE_ECB);
return base64_encode($encodedText);
由于某种原因,我无法获得与 C# 中相同的 PHP 代码输出。
另外,我知道mcrypt_get_block_size
和其他mcrypt
相关方法在 PHP 7.1 中已被弃用,不建议使用它。
【问题讨论】:
为什么要使用带有 8 字节密钥的三重 DES?那只是恢复到不安全的DES。 ]ECB 模式不安全,请参阅ECB mode,向下滚动至 Penguin。 最好不要使用PHP mcrypt,它是废弃软件,多年未更新,不支持标准PKCS#7(née PKCS#5)填充,只有非标准甚至不能用于二进制数据的空填充。 mcrypt 有许多出色的 bugs 可以追溯到 2003 年。不推荐使用的 mcrypt-extension 将在 PHP 7.2 中删除。而是考虑使用defuse 或RNCryptor,它们提供了一个完整的解决方案并且正在维护并且是正确的。 感谢您的建议,但我无法更改 C# 代码,因为它不是我的。它是我们客户遗留软件的一部分。我的任务是制作与现有 C# 代码等效的 PHP。 @zaph 谢谢你看看。 有一个选择,安全重要吗? 【参考方案1】:tdes.Key = (new UnicodeEncoding()).GetBytes(key);
UnicodeEncoding
构造函数用于 UTF-16、little-endian、“带 BOM”。 “with BOM”无关紧要,因为没有任何东西称为 GetPreamble()
,因此无论出于何种目的和目的,它都是 UTF-16LE。
这意味着您的密钥 ("1r1ppl3x"
) 实际上是:
31 00 72 00 31 00 70 00 70 00 6C 00 33 00 78 00
这是一个 2DEA 密钥
k1
= 31 00 72 00 31 00 70 00
k2
= 70 00 6C 00 33 00 78 00
这是非法的。除了 .NET,和其他所有东西一样,它不关心 DES 奇偶校验位,所以它只是假装每个字节的最低有效位是正确的。 PHP 中的任何库都可能会这样做。如果没有,您可能想correct the parity manually。
2DEA 密钥具有等效的 3DEA 密钥 k3
=k1
。因此,如果您的 PHP 库不支持 2DEA 键,您可以通过将前 8 个字节复制到末尾来人为地扩展它(在正确使用 UTF-16LE 而不是 UTF-8 之后),构建一个 24 字节的值。
如果 utf8_encode
是 PHP 中最好的“将文本转换为二进制数据”,那么您想使用输入字符串 "1\0r\01\0p\0p\0l\03\0x\01\0r\01\0p\0"
。
您似乎已经找到How to add/remove PKCS7 padding from an AES encrypted string?,因此更正密钥应该会使您进入“等效功能”状态。
现在,您的所有问题都是您使用 56 位密钥(所有这些零都很容易猜到),就好像它是 168 位密钥一样,并且使用 ECB 模式,并且使用了已弃用的库,并且(可以说)您使用的是 3DES 而不是 AES,并且(个人意见)从强类型语言转向脚本语言。
【讨论】:
【参考方案2】:C# 代码使用 PKCS#7 填充,PHP MCRYPT 代码使用空填充。
假设密钥应该用空值扩展。
将三重 DES 与 8 字节的单个 DES 密钥一起使用是没有意义的。此外,仅使用了 8 位 DES 密钥的 56 位。 56 位密钥不被认为是安全的。
【讨论】:
以上是关于将 TripleDESCryptoServiceProvider 从 C# 移植到 PHP的主要内容,如果未能解决你的问题,请参考以下文章
Javascript 将正则表达式 \\n 替换为 \n,将 \\t 替换为 \t,将 \\r 替换为 \r 等等