【问题标题】:Different results in C# and PHP when decrypting with Rijndael [closed]使用 Rijndael 解密时 C# 和 PHP 的不同结果 [关闭]
【发布时间】:2013-08-02 07:00:07
【问题描述】:

下面是我的 C# 代码,它解密 C# 中的编码字符串。

RijndaelManaged RijndaelCipher = new RijndaelManaged(); 
string DecryptedData; 
byte[] EncryptedData = Convert.FromBase64String(TextToBeDecrypted); 
byte[] Salt = Encoding.ASCII.GetBytes(Password.Length.ToString()); 
//Making of the key for decryption 
PasswordDeriveBytes SecretKey = new PasswordDeriveBytes(Password, Salt); 
//Creates a symmetric Rijndael decryptor object. 
ICryptoTransform Decryptor = RijndaelCipher.CreateDecryptor(SecretKey.GetBytes(32), SecretKey.GetBytes(16)); 
byte[] plainText = Decryptor.TransformFinalBlock(EncryptedData, 0, EncryptedData.Length); 

//Converting to string 
DecryptedData = Encoding.Unicode.GetString(plainText); 

但我想在 PHP 中使用相同的代码。我尝试了下面的代码,但它没有给我正确的输出。

function Decrypt( $encrypted, $key, $iv ){
$encrypted=base64_decode($encrypted);
return mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, $encrypted, MCRYPT_MODE_CFB, $iv);
}

在这里我得到了输出$)íqyZiG¤õ¡¹¡ 我通过了$s = 'cJ4ZJD3Vkf3Dv5uxrWiTQg=='$key = '123'(相同,在 C# 中加密时使用)。

我应该将什么传递给$iv,以便我得到解密后的输出为“Snehal”?

【问题讨论】:

  • 尝试 mcrypt_decrypt 而不是加密
  • $s 是盐,但cJ4ZJD3Vkf3Dv5uxrWiTQg== 不是盐,它是以 Base 64 格式编码的盐。试试base64_decode('cJ4ZJD3Vkf3Dv5uxrWiTQg==')

标签: c# php


【解决方案1】:

首先你需要 MCRYPT_RIJNDAEL_128 而不是 256 和 CBC 模式。但是,更大的问题是 PasswordDeriveBytes。我认为这不适用于 PHP。但是......通过一些工作浏览 MS 源和 Mono 源,可以创建类似的东西。

简单的一点是:

<?php
$encb64 = "cJ4ZJD3Vkf3Dv5uxrWiTQg==";
$pwd = "123";
$salt = "3";

$enc = base64_decode($encb64);
$decpad = Decrypt($enc, $pwd, $salt);
// Remove the padding
$pad = ord($decpad[($len = strlen($decpad)) - 1]);
$dec = substr($decpad, 0, strlen($decpad) - $pad);

echo "Enc: " . bin2hex($enc) . "\r\n";
echo "Dec: " . $dec . "\r\n";

function Decrypt($ciphertext, $password, $salt)
{
  $key = PBKDF1($password, $salt, 100, 32);
  $iv = PBKDF1($password, $salt, 100, 16);

  // NB: Need 128 not 256 and CBC mode to be compatible
  return mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $ciphertext, MCRYPT_MODE_CBC, $iv);
}

?>

然后使用this post 中的 PBKDF1 函数作为起点,查看 Mono 和 Microsoft 源代码,我们可以得出以下函数。

请注意警告。出于多种原因,这是一种非常糟糕的做事方式,但足以说明如何实现所要求的目标。

<?php
function PBKDF1($pass, $salt, $count, $cb)
{
  // This is very approximately the way that the Microsoft version of 
  // PasswordDeriveBytes works.

  ///
  /// !!!WARNING!!!
  ///
  // This is a BAD function!
  // Irrespective of the fact that the use of PBKDF1 is not recommended anyway.
  //
  // This really should be put into a class with a constructor taking the 
  // $pass, $salt and $count.
  // Then there should be a Reset() method to start from scratch each time a new pwd/salt is used.
  // And there should be a GetBytes(int) method to get the required info.
  // But for the sake of simplicity we are assuming the same pwd and salt for each call to 
  // this function. This will not stand up to any scrutiny!

  static $base;
  static $extra;
  static $extracount= 0;
  static $hashno;
  static $state = 0;

  if ($state == 0)
  {
    $hashno = 0;
    $state = 1;

    $key = $pass . $salt;
    $base = sha1($key, true);
    for($i = 2; $i < $count; $i++)
    {
      $base = sha1($base, true);
    }
  }

  $result = "";

  // Check if we have any bytes left over from a previous iteration.
  // This is the way MS appears to do it. To me it looks very badly wrong
  // in the line: "$result = substr($extra, $rlen, $rlen);"
  // I'm sure it should be more like "$result = substr($extra, $extracount, $rlen);"
  // Mono have provided what looks like a fixed version at
  // https://github.com/mono/mono/blob/master/mcs/class/corlib/System.Security.Cryptography/PasswordDeriveBytes.cs
  // But I'm no cryptographer so I might be wrong.
  // But this seems to work for low values of $hashno and seems to work
  // with C# implementations.

  if ($extracount > 0)
  {
    $rlen = strlen($extra) - $extracount;
    if ($rlen >= $cb)
    {
      $result = substr($extra, $extracount, $cb);
      if ($rlen > $cb)
      {
        $extracount += $cb;
      }
      else
      {
        $extra = null;
        $extracount = 0;
      }
      return $result;
    }
    $result = substr($extra, $rlen, $rlen);
  }

  $current = "";
  $clen = 0;
  $remain = $cb - strlen($result);
  while ($remain > $clen)
  {
    if ($hashno == 0)
    {
      $current = sha1($base, true);
    }
    else if ($hashno < 1000)
    {
      $n = sprintf("%d", $hashno);
      $tmp = $n . $base;
      $current .= sha1($tmp, true);
    }
    $hashno++;
    $clen = strlen($current);     
  }

  // $current now holds at least as many bytes as we need
  $result .= substr($current, 0, $remain);

  // Save any left over bytes for any future requests
  if ($clen > $remain)
  {
    $extra = $current;
    $extracount = $remain;
  }

  return $result; 
}
?>

【讨论】:

  • 感谢您的帮助。这是我正在寻找的代码。感谢您的快速回复。
【解决方案2】:

问题似乎出在您使用的 C# 代码上。

为了使用 RindjaelManaged 加密/解密,您需要执行以下操作(取自 MSDN 示例):

myRijndael = new RijndaelManaged();

myRijndael.GenerateKey();
myRijndael.GenerateIV();

// Encrypt the string to an array of bytes.
byte[] encrypted = EncryptStringToBytes(original, myRijndael.Key, myRijndael.IV);

// Decrypt the bytes to a string.
string roundtrip = DecryptStringFromBytes(encrypted, myRijndael.Key, myRijndael.IV);

您需要在您展示的那段 C# 代码中检索使用 RindjaelManaged 的​​ IV 属性创建的 IV(初始化向量)GenerateIV(),以便将其用于您的 PHP 例程。即使您必须使用ICryptoTransform 而不是上述方法。 AFAIK,当只给出盐和密钥时,无法检索它。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-18
    相关资源
    最近更新 更多