Как преобразовать это шифрование C # Rijndael в PHP?

На SO есть уже некоторые полезные вопросы:

  • Rijndael 256 Шифровать / расшифровывать между c # и php?
  • Rewrite Rijndael 256 C # Код шифрования в PHP
  • Rijndael / AES расшифровка C # для преобразования PHP

Однако у меня все еще возникают трудности с моим конкретным случаем.

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

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

Редактировать:

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

Я думаю, что 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 верна?

ОБНОВЛЕНИЕ: Ниже приведена дополнительная информация о процедурах M $ PBKDF1 в PasswordDeriveBytes (включая код Java, который вы можете попробовать и конвертировать):

ha, я вижу вашу точку зрения.

Интересно, что с использованием .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

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

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

Попробуйте следующее: http://www.php.net/manual/en/book.mcrypt.php