2017-10-18 153 views
0

我试图将以下C#方法移植到PHP,但无法获得正确的输出。将C#中的TripleDESCryptoServiceProvider移植到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); 
} 

由于某种原因,我不能让PHP代码相同,一个在C#中的输出。

此外,我知道mcrypt_get_block_size和其他mcrypt相关方法在PHP 7.1中已弃用,建议不要使用它。

+0

为什么使用Triple DES和8字节密钥?这只是恢复到不安全的DES。 ] ECB模式不安全,请参阅[ECB模式](https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Electronic_Codebook_.28ECB.29),向下滚动到企鹅。 – zaph

+0

最好不要使用PHP mcrypt,它是弃用的,多年未更新,不支持标准的PKCS#7(néePKCS#5)填充,只有**非标准的null填充**不能甚至可以用于二进制数据。 mcrypt有很多优秀的[bug](https://sourceforge.net/p/mcrypt/bugs/),可以追溯到2003年.mcrypt-extension已被弃用,将在PHP 7.2中被删除。请考虑使用[defuse](https://github.com/defuse/php-encryption)或[RNCryptor](https://github.com/RNCryptor),他们提供了一个完整的解决方案,并且正在维护和正确。 – zaph

+0

我很欣赏这个建议,但我不能更改C#代码,因为它不是我的。它是我们客户的遗留软件的一部分。我的任务是使现有C#代码的PHP等效。 – xar

回答

0
  1. C#代码使用PKCS#7填充,PHP MCRYPT代码使用null填充。

  2. 有一个假设,关键应扩展为空值。

  3. 使用Triple DES和8字节的单个DES密钥没有意义。此外,仅使用8位DES密钥的56位。一个56位密钥不被认为是安全的。

2
tdes.Key = (new UnicodeEncoding()).GetBytes(key); 

UnicodeEncoding构造是UTF-16,小尾数 “与BOM”。 “带有BOM”并不重要,因为没有任何东西叫做GetPreamble(),所以对于所有的意图和目的,它都是UTF-16LE。

这意味着你的钥匙("1r1ppl3x")居然是:

31 00 72 00 31 00 70 00 70 00 6C 00 33 00 78 00 

这与

  • ​​= { 31 00 72 00 31 00 70 00 }
  • k2 = { 70 00 6C 00 33 00 78 00 }

瓦一2DEA关键这是违法的。除.NET之外,与其他所有内容一样,不关心DES奇偶校验位,所以它只是假设每个字节的最低有效位是正确的。无论在PHP中的图书馆可能会做同样的事情。如果没有,您可能需要correct the parity manually

2DEA密钥具有等效的3DEA密钥k3 =​​。因此,如果您的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,以及(个人观点)从强类型语言转换为脚本语言。