【问题标题】:How do I convert this C# Rijndael encryption to PHP?如何将此 C# Rijndael 加密转换为 PHP?
【发布时间】:2012-02-19 03:55:46
【问题描述】:

已经有一些关于 SO 的有用问题:

但是我在处理我的特殊情况时仍然遇到困难。

我尝试了各种方法,但最终收到错误 "The IV parameter must be as long as the blocksize" 或与生成的哈希不匹配的文本。

我对加密的理解不足以弄清楚我做错了什么。

这里是php版本:

$pass = 'hello';
$salt = 'application-salt';

echo Encrypt('hello', 'application-salt');

function Encrypt($pass, $salt)
{
    $derived = PBKDF1($pass, $salt, 100, 16);
    $key = bin2hex(substr($derived, 0, 8));
    $iv = bin2hex(substr($derived, 8, 8));
    return mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $pass, MCRYPT_MODE_CBC, $iv);
}

function PBKDF1($pass, $salt, $count, $dklen)
{
    $t = $pass.$salt;
    $t = sha1($t, true);
    for($i=2; $i <= $count; $i++)
    {
        $t = sha1($t, true);
    }
    $t = substr($t,0,$dklen-1);
    return $t;
}

还有 C# 版本:

Console.WriteLine(Encrypt("hello", "application-salt"));
// output: "Hk4he+qKGsO5BcL2HDtbkA=="

public static string Encrypt(string clearText, string Password)
{
    byte[] clearData = System.Text.Encoding.Unicode.GetBytes(clearText);
    PasswordDeriveBytes pdb = new PasswordDeriveBytes(Password,
        new byte[] { 0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76 });

    MemoryStream ms = new MemoryStream();
    Rijndael alg = Rijndael.Create();
    alg.Key = pdb.GetBytes(32);
    alg.IV = pdb.GetBytes(16);
    CryptoStream cs = new CryptoStream(ms, alg.CreateEncryptor(), CryptoStreamMode.Write);
    cs.Write(clearData, 0, clearData.Length);
    cs.Close();
    byte[] encryptedData = ms.ToArray();

    return Convert.ToBase64String(encryptedData);
}

我希望能够在一个新的基于 php 的应用程序中验证用户登录,该应用程序将与现有 C# 应用程序相同的 MySQL 数据库进行通信。我打算加密密码并将生成的哈希与存储在数据库中的哈希进行比较以进行身份​​验证。

任何指针将不胜感激。

编辑:

我意识到在 C# 函数中,PasswordDeriveBytes 被调用并传递了一个字节数组作为我在 PHP 版本中没有模拟的参数。我发现这源自Codeproject example,并且 ASCII 中的字节数组拼写为“Ivan Medvedev”,我认为他是示例作者。不幸的是,我无法改变这一点。

【问题讨论】:

  • 你能把这个 .net 加密转换成 PHP 吗?

标签: c# php encryption rijndael


【解决方案1】:

PHP 已经在其 Mcrypt 模块中内置了此功能。

试试这个:http://www.php.net/manual/en/book.mcrypt.php

【讨论】:

  • 这个页面已经在另一个选项卡上打开,我在我的函数中使用了mcrypt_encrypt。但是,无法生成正确的密钥和初始化向量。需要额外的帮助! :)
  • 抱歉,我没有仔细阅读您的问题。您是否查看过 Mcrypt 页面上的任何示例?他们有做你想做的事的好例子。
  • 是的,这两个示例特定于 TripleDES 和 ECB;尽管如此,我认为我可能需要使用示例 2 中所示的 mcrypt_create_iv。然而,正是在这一点上,我在加密领域感到非常过分。
  • 它们应该是相似的。不幸的是,当涉及到特定算法时,我也不太了解加密。希望其他人可以处理具体问题。对不起:(
【解决方案2】:

我认为 PHP 版本实际上可能会在密钥和 IV 中添加 00h 值字节。它们都有一个无效的大小:每个 8 个字节。对于 AES-128,它们需要扩展到 16 个字节。在您的 C# 代码中,您使用 32 个字节作为密钥,因此将使用 密钥大小 为 256 位的 AES。

此外,您没有在PasswordDeriveBytes 中指定迭代次数,您应该指定它,因为该类没有指定默认的迭代次数 - 根据您的 cmets,这将是 100,假设是。

哦,你使用了不正确的加密方法。 MCRYPT_RIJNDAEL_256 使用 256 位的 blocksize 指定 Rijndael 算法,而不是 256 位的 keys。想必,key的bitsize就是key的字节数乘以8。

你能用这个替换你的加密函数并重试吗?

function Encrypt($pass, $salt)
{
     $derived = PBKDF1($pass, $salt, 100, 48);
     $key = bin2hex(substr($derived, 0, 32));
     $iv = bin2hex(substr($derived, 32, 16));
     return mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $pass, MCRYPT_MODE_CBC, $iv);
}

最后,在进行加密或解密之前,请检查生成的IV和密钥是否与PHP中的匹配。你确定那个 PHP PBKDF1 函数是正确的吗?

更新: Here 是有关 PasswordDeriveBytes 中 M$ PBKDF1 例程的更多信息(包括您可以尝试转换的 Java 代码):

哈,我明白你的意思了。

有趣的是,使用 .NET:调用 48 时结果不同 或调用 32 后接 16:

.NET GetBytes(32 +16): 04DD9D139DCB9DE889946D3662B319682159FF9C9B47FA15ED205C7CAF890922655D8DD89AE1CAAC60A8041FCD7E8DA4

.NET GetBytes(32) 04DD9D139DCB9DE889946D3662B319682159FF9C9B47FA15ED205C7CAF890922 其次是GetBytes(16) 89946D3662B3196860A8041FCD7E8DA4

真正的 Microsoft 代码,他们无法更改它,因为它可能会破坏该领域的应用程序。请注意,当使用 16 字节然后 8 字节或直接按 24 字节调用时,它们也会返回不同的结果设计。您最好升级到 PBKDF2,并将 PBKDF1 限制在标准中定义的最大 20 字节。

【讨论】:

  • 您的信息很有帮助,但我认为您误解了一部分:我正在尝试从 C# 转换为 PHP(而不是相反)。 PasswordDeriveBytes 显然默认为 100 次迭代 (reference)。由于使用了 AES-256,我是否应该将 PHP 中的密钥和 IV 长度从 8 扩展到 32?谢谢。
  • 尽量避免使用默认值(如果您可以更改 C# 代码)。我认为这些论点成立......但是推理将是相反的。我会相应地重写我的答案。
  • 如果密钥正确而 IV 不正确,您可能想尝试调用 PBKDF1 两次,一次用于请求 32 字节的密钥,第二次使用 16 字节用于 IV,但我认为上面的代码应该是正确的。如果是,我会删除此评论。
  • 有关加密方法的信息很有帮助,谢谢!我不能 100% 确定 PBKDF1 函数是否正确,它取自 another answer。我还没有成功获得 PBKDF1 函数来生成正确的密钥。我最后一次尝试是根据 C# 应用程序的输出手动设置密钥和 IV。然而,加密仍然导致不同的结果。 (例如,我通过设置 $key = base64_decode('key_from_c#_base64_encoded'); 来做到这一点,加密令人沮丧!
  • 报错如下:'Size of key is too large for this algorithm'
【解决方案3】:

下面的 sn-p 可能对正在寻找从 C# 到 PHP 的精确转换的人有所帮助

<?php
    class Foo {
      protected $mcrypt_cipher = MCRYPT_RIJNDAEL_128;
      protected $mcrypt_mode = MCRYPT_MODE_CBC;

      public function decrypt($key, $iv, $encrypted)
      {
        return mcrypt_decrypt($this->mcrypt_cipher, $key, base64_decode($encrypted), $this->mcrypt_mode, $iv);
      }

      public function encrypt($key, $iv, $password)
      {
        $block = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_ECB);
          $padding = $block - (strlen($password) % $block);
          $password .= str_repeat(chr($padding), $padding);
        return mcrypt_encrypt($this->mcrypt_cipher, $key, $password, $this->mcrypt_mode, $iv);
      }
    }
    $foo = new Foo;
    $pass = 'p@ss';
    $salt = 's@1t';

    $key = PBKDF1($pass, $salt, 2, 32);

    $iv = "@1B2c3D4e5F6g7H8";

    $encrypted = $foo->encrypt($key,$iv,'test@123');

    $encrypted = base64_encode($encrypted);

    echo 'Encrypted: '.$encrypted.'</br>';
    echo 'Decrypted: '.$foo->decrypt($key, $iv, $encrypted);



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

【讨论】:

  • 对 128 或 256 keySize 进行少量修改,如果使用 MCRYPT_RIJNDAEL_128,则需要使用 PBKDF1($pass, $salt, 2, 16),其中 2 是迭代次数,16128/8,如果你想使用MCRYPT_RIJNDAEL_256 然后将16 更改为32 因为256/8=32
猜你喜欢
  • 2011-02-09
  • 1970-01-01
  • 2019-10-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-07-26
  • 2019-11-10
  • 2015-09-23
相关资源
最近更新 更多