将 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的主要内容,如果未能解决你的问题,请参考以下文章

如何将Ios文件上传到

Javascript 将正则表达式 \\n 替换为 \n,将 \\t 替换为 \t,将 \\r 替换为 \r 等等

如何将视频文件转换格式

sh 一个将生成CA的脚本,将CA导入到钥匙串中,然后它将创建一个证书并与CA签名,然后将其导入到

python怎么将0写入文件?

如何将CMD窗口背景改成透明?