C# 和 PHP 中的 TripleDES 加密结果不一样(PKCS7、ECB)?

发布于 2024-10-12 06:10:03 字数 1695 浏览 6 评论 0原文

我现在花了几个小时试图解决这个问题,但我就是无法让它发挥作用。我有一个 C# 加密例程,需要在 php.ini 中进行匹配。我无法更改 C# 版本,这不是一个选项(第 3 方对此很坚定)。

这是 C# 代码:

//In C#
// Console.WriteLine(ApiEncode("testing", "56dsfkj3kj23asdf83kseegflkj43458afdl"));
// Results in: 
//     XvHbR/CsLTo=
public static string ApiEncode(string data, string secret)
{
  byte[] clear;

  var encoding = new UTF8Encoding();
  var md5 = new MD5CryptoServiceProvider();

  byte[] key = md5.ComputeHash(encoding.GetBytes(secret));

  TripleDESCryptoServiceProvider des = new TripleDESCryptoServiceProvider();
  des.Key = key;
  des.Mode = CipherMode.ECB;
  des.Padding = PaddingMode.PKCS7;

  byte[] input = encoding.GetBytes(data);
  try { clear = des.CreateEncryptor().TransformFinalBlock(input, 0, input.Length); }
  finally
  {
    des.Clear();
    md5.Clear();
  }

  return Convert.ToBase64String(clear);
}

这是我在 PHP 中想到的最好的代码:

//In PHP
// echo apiEncode("testing", "56dsfkj3kj23asdf83kseegflkj43458afdl");
// Results in: 
//    5aqvY6q1T54=
function apiEncode($data, $secret)
{    
  //Generate a key from a hash
  $key = md5(utf8_encode($secret), true);
  //Create init vector  
  $iv = mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_3DES, MCRYPT_MODE_ecb), MCRYPT_RAND); 

  //Pad for PKCS7
  $blockSize = mcrypt_get_block_size('tripledes', 'ecb');
  $len = strlen($data);
  $pad = $blockSize - ($len % $blockSize);
  $data .= str_repeat(chr($pad), $pad);

  //Encrypt data
  $encData = mcrypt_encrypt('tripledes', $key, $data, 'ecb'); //, $iv);
  return base64_encode($encData);
}

据我所知,我在 PHP 端正确处理了 PKCS7 填充。我不知道还能尝试什么。

需要注意的一件事是,C# 发生在 Windows 上,而 PHP 发生在 Linux 上,不确定这是否会产生影响。

I've spent a couple hours now trying to figure this out, but I just can't get it to work. I've got a C# encryption routine that I need to match in php. I can't change the C# version, that's not an option (3rd party is firm on this).

Here's the C# code:

//In C#
// Console.WriteLine(ApiEncode("testing", "56dsfkj3kj23asdf83kseegflkj43458afdl"));
// Results in: 
//     XvHbR/CsLTo=
public static string ApiEncode(string data, string secret)
{
  byte[] clear;

  var encoding = new UTF8Encoding();
  var md5 = new MD5CryptoServiceProvider();

  byte[] key = md5.ComputeHash(encoding.GetBytes(secret));

  TripleDESCryptoServiceProvider des = new TripleDESCryptoServiceProvider();
  des.Key = key;
  des.Mode = CipherMode.ECB;
  des.Padding = PaddingMode.PKCS7;

  byte[] input = encoding.GetBytes(data);
  try { clear = des.CreateEncryptor().TransformFinalBlock(input, 0, input.Length); }
  finally
  {
    des.Clear();
    md5.Clear();
  }

  return Convert.ToBase64String(clear);
}

Here's the best of what I've come up with in PHP:

//In PHP
// echo apiEncode("testing", "56dsfkj3kj23asdf83kseegflkj43458afdl");
// Results in: 
//    5aqvY6q1T54=
function apiEncode($data, $secret)
{    
  //Generate a key from a hash
  $key = md5(utf8_encode($secret), true);
  //Create init vector  
  $iv = mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_3DES, MCRYPT_MODE_ecb), MCRYPT_RAND); 

  //Pad for PKCS7
  $blockSize = mcrypt_get_block_size('tripledes', 'ecb');
  $len = strlen($data);
  $pad = $blockSize - ($len % $blockSize);
  $data .= str_repeat(chr($pad), $pad);

  //Encrypt data
  $encData = mcrypt_encrypt('tripledes', $key, $data, 'ecb'); //, $iv);
  return base64_encode($encData);
}

To the best of my knowledge, I'm handling the PKCS7 padding properly on the PHP side. I'm not sure what else to try.

One thing to note, the C# is happening on windows, and the PHP on linux, not sure that should make a difference.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

浊酒尽余欢 2024-10-19 06:10:03

PHP 版本中的填充长度基于密码的长度。这是不正确的。它应该基于您的消息的长度。

尝试将 strlen($password) 替换为 strlen($data)


第二个问题是 mcrypt 库需要 24 字节密钥。 Triple DES 将常规 DES 应用三次,因此您可以将每轮 DES 使用的 8 字节密钥称为 K1、K2 和 K3< /子>。有不同的方法来选择这些键。最安全的方法是选择三个不同的密钥。另一种方法是将 K3 设置为等于 K1。最不安全的方法(相当于 DES)是使 K1 = K2 = K3。

大多数库都足够“智能”,可以将 16 字节 3DES 密钥解释为上面的第二个选项:K3 = K1。 .NET 实现正在为您执行此操作,但 mcrypt 库则不然;相反,它设置 K3 = 0。您需要自己修复此问题,并传递 mcrypt 24 字节密钥。

计算 MD5 哈希后,取出 $key 的前 8 个字节,并将它们附加到 $key 的末尾,这样就有一个 24 字节的值要传递到mcrypt_encrypt()

The padding length in your PHP version is based on the length of the password. This is incorrect. It should be based on the length of your message instead.

Try replacing strlen($password) with strlen($data).


The second problem is that the mcrypt library requires 24-byte keys. Triple DES applies regular DES three times, so you can call the 8-byte key used in each round of DES K1, K2, and K3. There are different ways to choose these keys. The most secure is to choose three distinct keys. Another way is to set K3 equal to K1. The least secure method (equivalent to DES) is to make K1 = K2 = K3.

Most libraries are "smart" enough to interpret a 16-byte 3DES key as the second option above: K3 = K1. The .NET implementation is doing this for you, but the mcrypt library is not; instead, it's setting K3 = 0. You'll need to fix this yourself, and pass mcrypt a 24-byte key.

After computing the MD5 hash, take the first 8 bytes of $key, and append them to the end of $key, so that you have a 24-byte value to pass to mcrypt_encrypt().

烙印 2024-10-19 06:10:03

我找到了解决方案,请查看此链接,可能会对您有所帮助。 http://sanity-free.com/131/triple_des_ Between_php_and_csharp.html

这是解密函数以防万一:

    public static string Decrypt(string cypherString)
    {

        byte[] key = Encoding.ASCII.GetBytes("icatalogDR0wSS@P6660juht");
        byte[] iv = Encoding.ASCII.GetBytes("iCatalog");
        byte[] data = Convert.FromBase64String(cypherString);
        byte[] enc = new byte[0];
        TripleDES tdes = TripleDES.Create();
        tdes.IV = iv;
        tdes.Key = key;
        tdes.Mode = CipherMode.CBC;
        tdes.Padding = PaddingMode.Zeros;
        ICryptoTransform ict = tdes.CreateDecryptor();
        enc = ict.TransformFinalBlock(data, 0, data.Length);
        return UTF8Encoding.UTF8.GetString(enc, 0, enc.Length);
    }

I found a solution, check this link, may help you. http://sanity-free.com/131/triple_des_between_php_and_csharp.html

And here is the decrypt function just in case:

    public static string Decrypt(string cypherString)
    {

        byte[] key = Encoding.ASCII.GetBytes("icatalogDR0wSS@P6660juht");
        byte[] iv = Encoding.ASCII.GetBytes("iCatalog");
        byte[] data = Convert.FromBase64String(cypherString);
        byte[] enc = new byte[0];
        TripleDES tdes = TripleDES.Create();
        tdes.IV = iv;
        tdes.Key = key;
        tdes.Mode = CipherMode.CBC;
        tdes.Padding = PaddingMode.Zeros;
        ICryptoTransform ict = tdes.CreateDecryptor();
        enc = ict.TransformFinalBlock(data, 0, data.Length);
        return UTF8Encoding.UTF8.GetString(enc, 0, enc.Length);
    }
坦然微笑 2024-10-19 06:10:03

看看encoding.getBytes,你需要来自UTF8的秘密密钥Bytes...

Take a look at encoding.getBytes, you need the secret key Bytes from UTF8...

德意的啸 2024-10-19 06:10:03

C# 版本似乎未设置IV.如果您不知道这是什么,这可能是一个问题,因为 msdn 说:

每当您创建 SymmetricAlgorithm 类之一的新实例或手动调用GenerateIV 方法时,IV 属性都会自动设置为新的随机值。

看起来在 PHP 版本中,您正在使用 IV。您可以尝试不提供 IV,并希望 C# 版本也使用零。

编辑:看起来对于 ECB,IV 被忽略。

您可能还需要像 C# 版本中那样使用 utf8-encode

It appears the C# version does not set the IV. This could be an issue if you dont know what it is because msdn says:

The IV property is automatically set to a new random value whenever you create a new instance of one of the SymmetricAlgorithm classes or when you manually call the GenerateIV method.

It looks like in the PHP version, you are using an IV. You could try not supplying the IV and hope the C# version also uses zeros.

Edit: Looks like for ECB, the IV is ignored.

You might also need to encoding the key like in the C# version using utf8-encode

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文