Как мне преобразовать это шифрование C # Rijndael в PHP? - PullRequest
3 голосов
/ 26 января 2012

На 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, которое будет взаимодействовать с той же базой данных MySQL, что и существующее приложение C #. Я намерен зашифровать пароль и сравнить полученный хеш с тем, который хранится в базе данных для аутентификации.

Любые указатели будут наиболее цениться.

Edit:

Я понимаю, что в функции C # вызывается PasswordDeriveBytes и передается байтовый массив в качестве аргумента, для которого у меня нет аналога в версии PHP. Я обнаружил, что это происходит из примера Codeproject и что массив байтов в ASCII записывает «Иван Медведев», которого я предполагаю в качестве автора примера. К сожалению, я не могу это изменить.

Ответы [ 3 ]

3 голосов
/ 07 апреля 2014

Ниже может быть полезен фрагмент, который ищет точное преобразование из 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; 
    }
2 голосов
/ 26 января 2012

Я думаю, что версия PHP может фактически добавить 00h значащие байты к ключу и IV. Они оба имеют неправильный размер: 8 байтов для каждого. Они должны быть расширены до 16 байтов для AES-128. В вашем коде C # вы используете 32 байта для ключа, который, следовательно, будет использовать AES с размером ключа из 256 бит.

Кроме того, вы не указываете число итераций в PasswordDeriveBytes, вы должны указать его, поскольку класс не определяет количество итераций по умолчанию - согласно вашим комментариям это будет 100, давайте предположим, что это так.

О, и вы используете неправильный метод шифрования. MCRYPT_RIJNDAEL_256 определяет алгоритм Rijndael с использованием размера блока из 256 битов, а не ключей из 256 битов. Предположительно, битовый размер ключей - это просто число байтов ключа, умноженное на 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 верна?

UPDATE: Здесь - дополнительная информация о подпрограммах M $ PBKDF1 в PasswordDeriveBytes (включая код Java, который вы можете попробовать преобразовать):

ха, я понимаю вашу точку зрения.

Интересно, используя .NET: результаты разные при звонке 48 или позвонив 32, а затем 16:

.NET GetBytes (32 +16): 04DD9D139DCB9DE889946D3662B319682159FF9C9B47FA15ED205C7CAF890922655D8DD89AE1CAAC60A8041FCD7E8DA4

.NET GetBytes (32) 04DD9D139DCB9DE889946D3662B319682159FF9C9B47FA15ED205C7CAF890922 Затем следует GetBytes (16) 89946D3662B3196860A8041FCD7E8DA4

Настоящий код Microsoft, и они не могут его изменить, поскольку он может нарушить работу приложений на местах. Обратите внимание, что они также будут возвращать разные результаты при вызове его с 16, а затем с 8 байтами или напрямую с 24 байтами с дизайном . Вам лучше перейти на PBKDF2 и ограничить PBKDF1 максимум 20 байтами, как определено в стандартах.

0 голосов
/ 26 января 2012

PHP уже имеет эту возможность, встроенную в его модуль Mcrypt.

Попробуйте это: http://www.php.net/manual/en/book.mcrypt.php

...