简体   繁体   English

使用PHP解密使用RijndaelManaged类在C#中加密的字符串

[英]Decrypting string encrypted in C# with RijndaelManaged class using PHP

Here's some C# code (I've modified it slightly to modify some of the hard coded values in it): 这是一些C#代码(我对其进行了一些修改,以修改其中的一些硬编码值):

public static string Decrypt(string InputFile)
{
    string outstr = null;

    if ((InputFile != null))
    {
        if (File.Exists(InputFile))
        {

            FileStream fsIn = null;
            CryptoStream cstream = null;

            try
            {
                byte[] _b = { 94, 120, 102, 204, 199, 246, 243, 104, 185, 115, 76, 48, 220, 182, 112, 101 };
                fsIn = File.Open(InputFile, FileMode.Open, System.IO.FileAccess.Read);

                SymmetricAlgorithm symm = new RijndaelManaged();
                PasswordDeriveBytes Key = new PasswordDeriveBytes(System.Environment.MachineName, System.Text.Encoding.Default.GetBytes("G:MFX62rlABW:IUYAX(i"));
                ICryptoTransform transform = symm.CreateDecryptor(Key.GetBytes(24), _b);
                cstream = new CryptoStream(fsIn, transform, CryptoStreamMode.Read);

                StreamReader sr = new StreamReader(cstream);

                char[] buff = new char[1000];
                sr.Read(buff, 0, 1000);

                outstr = new string(buff);
            }
            finally
            {
                if (cstream != null)
                {
                    cstream.Close();
                }
                if (fsIn != null)
                {
                    fsIn.Close();
                }
            }
        }
    }
    return outstr;
}

I need to come up with a function to do the same in PHP. 我需要拿出一个函数在PHP中执行相同的操作。 Bear in mind, I did not write the C# code and I cannot modify it, so even if it's bad, I'm stuck with it. 请记住,我没有编写C#代码,也无法修改它,因此即使它很糟糕,我也坚持使用它。 I've searched all over and have found bits and pieces around, but nothing that works so far. 我到处搜索,发现周围有些零散,但到目前为止没有任何效果。 All examples I've found use mcrypt, which seems to be frowned upon these days, but I'm probably stuck using it. 我发现的所有示例都使用mcrypt,这些天来似乎对此不满意,但我可能会坚持使用它。 Next, I found the following post which has some useful info: Rewrite Rijndael 256 C# Encryption Code in PHP 接下来,我发现了以下帖子,其中包含一些有用的信息: 用PHP重写Rijndael 256 C#加密代码

So looks like the PasswordDeriveBytes class is the key to this. 因此,看起来PasswordDeriveBytes类是实现此目的的关键。 I created the following PHP code to try to decrypt: 我创建了以下PHP代码以尝试解密:

function PBKDF1($pass,$salt,$count,$dklen) { 
    $t = $pass.$salt;
    //echo 'S||P: '.bin2hex($t).'<br/>';
    $t = sha1($t, true); 
    //echo 'T1:' . bin2hex($t) . '<br/>';
    for($i=2; $i <= $count; $i++) { 
        $t = sha1($t, true); 
        //echo 'T'.$i.':' . bin2hex($t) . '<br/>';
    } 
    $t = substr($t,0,$dklen); 
    return $t;      
}

$input = 'Ry5WdjGS8rpA9eA+iQ3aPw==';
$key   = "win7x64";
$salt  = implode(unpack('C*', "G:MFX62rlABW:IUYAX(i"));

$salt   = pack("H*", $salt); 
$it     = 1000; 
$keyLen = 16; 
$key    = PBKDF1($key, $salt, $it, $keyLen);
$key    = bin2hex(substr($key, 0, 8));
$iv     = bin2hex(substr($key, 8, 8));

echo trim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, base64_decode($input), MCRYPT_MODE_CBC, $iv));

You'll note that for what I believe to be System.Environment.MachineName, I put in a fixed value for now which is the computer name of the machine I'm on, so should be equivalent of what the C# code is doing. 您会注意到,对于我认为是System.Environment.MachineName的东西,我现在输入一个固定值,它是我所在机器的计算机名称,因此应等效于C#代码正在执行的操作。 Other than that, I've noticed that using MCRYPT_RIJNDAEL_256 doesn't work, it throws the error "The IV parameter must be as long as the blocksize". 除此之外,我注意到使用MCRYPT_RIJNDAEL_256不起作用,它会引发错误“ IV参数必须与块大小一样长”。 If I use MCRYPT_RIJNDAEL_128, I don't get that error, but decryption still fails. 如果我使用MCRYPT_RIJNDAEL_128,则不会收到该错误,但解密仍然会失败。 I assume I'm missing the piece for the byte array _b that's used by the CreateDecryptor function, I have no idea where that's supposed to fit in. Any help is appreciated. 我以为我缺少CreateDecryptor函数使用的字节数组_b的那一部分,我不知道该放在哪里。任何帮助都将受到赞赏。

UPDATE 更新

This is the solution, which was made possible by the answer marked correct. 这是解决方案,答案是正确的。 Note that the code for the PBKDF1 function is not mine, it was linked to in the answer. 请注意,PBKDF1函数的代码不是我的,而是在答案中链接到的。

function PBKDF1($pass, $salt, $count, $cb) {
  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 = "";

  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; 
}

$input  = 'base 64 encoded string to decrypt here';
$key    = strtoupper(gethostname());
$salt   = 'G:MFX62rlABW:IUYAX(i';
$it     = 100; 
$keyLen = 24;
$key    = PBKDF1($key, $salt, $it, $keyLen);
$iv     = implode(array_map('chr', [94, 120, 102, 204, 199, 246, 243, 104, 185, 115, 76, 48, 220, 182, 112, 101]));

_b is a static value that is used as the IV ( CreateDecryptor takes a key and IV parameter). _b是用作IV的静态值( CreateDecryptor带有密钥和IV参数)。 Since it is 16 bytes long, this means that you're using Rijndael-128 or more commonly known AES. 由于它的长度为16个字节,这意味着您正在使用Rijndael-128或更常见的AES。

Key.GetBytes(24) suggests that a 24 byte key is derived and not a 16 byte key. Key.GetBytes(24)建议派生一个24字节的密钥,而不是16字节的密钥。

Make sure that 确保

  • System.Text.Encoding.Default is equivalent with implode(unpack('C*', ... , System.Text.Encoding.Defaultimplode(unpack('C*', ...
  • Default value for iterations of PasswordDeriveBytes is 1000, PasswordDeriveBytes迭代的默认值为1000,
  • Default value for hash of PasswordDeriveBytes is SHA-1 PasswordDeriveBytes哈希的默认值是SHA-1

Security problems: 安全问题:

  • PBKDF1 is obsolete and PBKDF2 isn't that much better. PBKDF1已过时,而PBKDF2并没有那么好。 Use up-to-date key derivation algorithms like Argon2 or scrypt. 使用最新的密钥派生算法,例如Argon2或scrypt。
  • The IV must be randomly chosen to achieve semantic security. 必须随机选择IV以实现语义安全。 It doesn't have to be secret, so it can be sent along with the ciphertext. 它不必是秘密的,因此可以与密文一起发送。
  • Stretching a key by encoding it to hex doesn't provide any security (don't use bin2hex ). 通过将密钥编码为十六进制来拉伸密钥不会提供任何安全性(请勿使用bin2hex )。
  • The ciphertext is not authenticated, which means that you cannot detect (malicious) manipulation of encrypted messages. 密文未经身份验证,这意味着您无法检测到(恶意)对加密消息的操作。 Employ encrypt-then-MAC. 采用先加密然后再MAC。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM