2010-08-17 2 views
12

У меня есть алгоритм шифрования/дешифрования, написанный на C# - мне нужно иметь возможность производить такое же шифрование в PHP, чтобы я мог отправлять зашифрованный текст через HTTP, чтобы быть расшифрованным на стороне C# , Вот код C# для шифрования.Rewrite Rijndael 256 C# Код шифрования в PHP

this.m_plainText = string.Empty; 
this.m_passPhrase = "passpharse"; 
this.m_saltValue = "saltvalue"; 
this.m_hashAlgorithm = "SHA1"; 
this.m_passwordIterations = 2; 
this.m_initVector = "1a2b3c4d5e6f7g8h"; 
this.m_keySize = 256; 

public string Encrypt() 
{ 
    string plainText = this.m_plainText; 
    string passPhrase = this.m_passPhrase; 
    string saltValue = this.m_saltValue; 
    string hashAlgorithm = this.m_hashAlgorithm; 
    int passwordIterations = this.m_passwordIterations; 
    string initVector = this.m_initVector; 
    int keySize = this.m_keySize; 

    // Convert strings into byte arrays. 
    // Let us assume that strings only contain ASCII codes. 
    // If strings include Unicode characters, use Unicode, UTF7, or UTF8 
    // encoding. 
    byte[] initVectorBytes = Encoding.ASCII.GetBytes(initVector); 
    byte[] saltValueBytes = Encoding.ASCII.GetBytes(saltValue); 

    // Convert our plaintext into a byte array. 
    // Let us assume that plaintext contains UTF8-encoded characters. 
    byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText); 

    // First, we must create a password, from which the key will be derived. 
    // This password will be generated from the specified passphrase and 
    // salt value. The password will be created using the specified hash 
    // algorithm. Password creation can be done in several iterations. 
    PasswordDeriveBytes password = new PasswordDeriveBytes(
                passPhrase, 
                saltValueBytes, 
                hashAlgorithm, 
                passwordIterations); 

    // Use the password to generate pseudo-random bytes for the encryption 
    // key. Specify the size of the key in bytes (instead of bits). 
    byte[] keyBytes = password.GetBytes(keySize/8); 

    // Create uninitialized Rijndael encryption object. 
    RijndaelManaged symmetricKey = new RijndaelManaged(); 

    // It is reasonable to set encryption mode to Cipher Block Chaining 
    // (CBC). Use default options for other symmetric key parameters. 
    symmetricKey.Mode = CipherMode.CBC; 

    // Generate encryptor from the existing key bytes and initialization 
    // vector. Key size will be defined based on the number of the key 
    // bytes. 
    ICryptoTransform encryptor = symmetricKey.CreateEncryptor(
                keyBytes, 
                initVectorBytes); 

    // Define memory stream which will be used to hold encrypted data. 
    MemoryStream memoryStream = new MemoryStream(); 

    // Define cryptographic stream (always use Write mode for encryption). 
    CryptoStream cryptoStream = new CryptoStream(memoryStream, 
               encryptor, 
               CryptoStreamMode.Write); 
    // Start encrypting. 
    cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length); 

    // Finish encrypting. 
    cryptoStream.FlushFinalBlock(); 

    // Convert our encrypted data from a memory stream into a byte array. 
    byte[] cipherTextBytes = memoryStream.ToArray(); 

    // Close both streams. 
    memoryStream.Close(); 
    cryptoStream.Close(); 

    // Convert encrypted data into a base64-encoded string. 
    string cipherText = Convert.ToBase64String(cipherTextBytes); 

    // Return encrypted string. 
    return cipherText; 
} 

У меня есть похожий PHP-код, который может помочь. Он работает не так, как нужно, но я думаю, что это может быть хорошим местом для начала.

<?php 

/* 
* DEFINE CONSTANTS 
*/ 
$HashPassPhrase = "passpharse"; 
$HashSalt = "saltvalue"; 
$HashAlgorithm = "SHA1"; 
$HashIterations = "2"; 
$InitVector = "1a2b3c4d5e6f7g8h";  // Must be 16 bytes 
$keySize = "256"; 

class Cipher { 
    private $securekey, $iv; 
    function __construct($textkey) { 
     $this->securekey = hash($HashAlgorithm,$textkey,TRUE); 
     $this->iv = $InitVector; 
    } 
    function encrypt($input) { 
     return base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $this->securekey, $input, MCRYPT_MODE_CBC, $this->iv)); 
    } 
    function decrypt($input) { 
     return trim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $this->securekey, base64_decode($input), MCRYPT_MODE_CBC, $this->iv)); 
    } 
} 

$cipher = new Cipher($HashPassPhrase); 

$encryptedtext = $cipher->encrypt("Text To Encrypt"); 
echo "->encrypt = $encryptedtext<br />"; 

$decryptedtext = $cipher->decrypt($encryptedtext); 
echo "->decrypt = $decryptedtext<br />"; 

var_dump($cipher); 

?>

ответ

17

Вы должны получить ключ от кодовой фразы точно так же, как C# код делают в PasswordDeriveBytes. Это документально сделать PBKDF1 ключ вывод, согласно RFC2898:

Этот класс использует расширение алгоритма PBKDF1, определенный в PKCS # 5 v2.0 стандарта вывести байт подходящие для использования в качестве ключевого материала от пароля . Стандарт документально в IETF РНЦ 2898.

есть PHP библиотеки, реализующие PBKDF1 там, но на самом деле просто написать с нуля на основе ОНТ он RFC:

PBKDF1 (P, S, C, dkLen)

Опции: хэш
, лежащий в основе хэш-функция

Входной сигнал: Р
пароль, строка октетов S соль, восемь-строка октетов с счетчиком цикла, положительное целым число dkLen предназначена длиной в октетах производного ключа, положительного целого числа, не более 16 для MD2 или MD5 и 20 для SHA -1

Выход: DK, полученный ключ, dkLen-октет строку

шаги:

1. If dkLen > 16 for MD2 and MD5, or dkLen > 20 for SHA-1, output 
    "derived key too long" and stop. 

    2. Apply the underlying hash function Hash for c iterations to the 
    concatenation of the password P and the salt S, then extract 
    the first dkLen octets to produce a derived key DK: 

       T_1 = Hash (P || S) , 
       T_2 = Hash (T_1) , 
       ... 
       T_c = Hash (T_{c-1}) , 
       DK = Tc<0..dkLen-1> 

    3. Output the derived key DK. 

Обновлено

Когда вы найдете в этом самостоятельно, вы ситуации, вы обычно искать пример, который показывает деле ввода значения на каждом шагу. например, один в http://www.di-mgt.com.au/cryptoKDFs.html#examplespbkdf:

Password = "password" 
     = (0x)70617373776F7264 
Salt  = (0x)78578E5A5D63CB06 
Count = 1000 
kLen  = 16 
Key  = PBKDF1(Password, Salt, Count, kLen) 
     = (0x)DC19847E05C64D2FAF10EBFB4A3D2A20 

P || S = 70617373776F726478578E5A5D63CB06 
T_1=  D1F94C4D447039B034494400F2E7DF9DCB67C308 
T_2=  2BB479C1D369EA74BB976BBA2629744E8259C6F5 
... 
T_999= 6663F4611D61571068B5DA168974C6FF2C9775AC 
T_1000= DC19847E05C64D2FAF10EBFB4A3D2A20B4E35EFE 
Key=  DC19847E05C64D2FAF10EBFB4A3D2A20 

Так что теперь позволяет написать функцию 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;  
} 

Теперь вы можете увидеть заблуждается о ваших путях: вы не указали все важные raw=true параметр sha1.Давайте посмотрим, что наша функция выхода:

$HashPassPhrase = pack("H*","70617373776F7264"); 
$HashSalt = pack("H*","78578E5A5D63CB06"); 
$HashIterations = 1000; 
$devkeylength = 16; 
$devkey = PBKDF1($HashPassPhrase,$HashSalt,$HashIterations,$devkeylength); 
echo 'Key:' . bin2hex(substr($devkey, 0, 8)) . '<br/>'; 
echo 'IV:' . bin2hex(substr($devkey, 8, 8)) .'<br/>'; 
echo 'Expected: DC19847E05C64D2FAF10EBFB4A3D2A20<br/>'; 

этот вывод именно ожидаемый результат:

Key:dc19847e05c64d2f 
IV:af10ebfb4a3d2a20 
Expected: DC19847E05C64D2FAF10EBFB4A3D2A20 

Далее, мы можем утверждать, что C# функция делает то же самое:

  byte[] password = Encoding.ASCII.GetBytes("password"); 
      byte[] salt = new byte[] { 0x78, 0x57, 0x8e, 0x5a, 0x5d, 0x63, 0xcb, 0x06}; 

      PasswordDeriveBytes pdb = new PasswordDeriveBytes(
       password, salt, "SHA1", 1000); 

      byte[] key = pdb.GetBytes(8); 
      byte[] iv = pdb.GetBytes(8); 

      Console.Out.Write("Key: "); 
      foreach (byte b in key) 
      { 
       Console.Out.Write("{0:x} ", b); 
      } 
      Console.Out.WriteLine(); 

      Console.Out.Write("IV: "); 
      foreach (byte b in iv) 
      { 
       Console.Out.Write("{0:x} ", b); 
      } 
      Console.Out.WriteLine(); 

это производит тот же самый результат:

Key: dc 19 84 7e 5 c6 4d 2f 
IV: af 10 eb fb 4a 3d 2a 20 

QED

бонус объяснение

Пожалуйста, не делайте криптографию, если вы не знаете точно , что вы делаете. Даже после того, как вы правильно исправите PHP-реализацию, ваш опубликованный код C# имеет серьезные проблемы. Вы смешиваете байт-массивы с помехами, представляющими шестнадцатеричные дампы, вы используете жестко закодированный IV вместо того, чтобы выводить его из кодовой фразы и соли, просто неправильно. Пожалуйста, используйте схему шифрования вне поля, например SSL или S-MIME, и не заново создавайте свои собственные. Вы получите неправильный.

+0

Спасибо за ответ, я думаю, что понимаю, что вы пытаетесь сказать здесь, но недостаточно, чтобы фактически использовать его. Есть ли возможный полностью закодированный пример? –

+0

Это PHP-функция, с которой я столкнулся для PBKDF1 $ HashPassPhrase = "passpharse"; $ HashSalt = "saltvalue"; $ HashIterations = 2; $ devkeylength = 32; $ devkey = PBKDF1 ($ HashPassPhrase, $ Hashsalt, $ HashIterations, $ devkeylength); \t функция PBKDF1 ($ проход, $ соль, $ граф, $ dklen) \t { \t \t $ т = SHA1 ($ проход.$ Соль); \t \t для ($ = 1; $ г <$ рассчитывать; $ я ++) \t \t { \t \t \t $ т = SHA1 ($ т); \t \t} \t \t $ t = substr ($ t, 0, $ dklen-1); \t \t return $ t; \t} Но я не думаю, что получаю правильные результаты. –

+0

Ваша функция PBKDF1 использует $ dklen-1 ... почему ??? –

4

Похоже, ваша основная проблема заключается в том, что вы используете PHP hash() вместо этапа PasswordDeriveBytes() со стороны C#. Эти два метода не эквивалентны. Последний реализует алгоритм дешифрования пароля PBKDF1, а hash() - это просто хэш. Он выглядит как PEAR might have a PBKDF1 implementation, но в противном случае вам, возможно, придется написать его самостоятельно.

Вам также необходимо убедиться, что кодировка текста согласована с обеих сторон, если вы еще этого не сделали.

Наконец, вы должны не делать то, что делаете, потому что cryptography is harder than it looks. Поскольку вы используете HTTP, вы можете использовать протокол SSL вместо написания собственного. Это обеспечит вам гораздо лучшую безопасность и меньше хлопот на низкоуровневых деталях, таких как сохранение инкрементных IVs в синхронизации и еще много чего.

+0

Спасибо за этот ответ. Да, я понял, что основная проблема связана с PasswordDeriveBytes(). Вы говорите, что передача четкого текстового пароля через HTTPS с помощью SSL более безопасна, чем кодирование с одной стороны, и декодирование на другом? –

+0

@Derek Armstrong: да! SSL предназначен для защиты таких вещей, как пароли открытого текста, как и ваша схема. Основное различие заключается в том, что SSL был пересмотрен и пересмотрен лучшими криптографами на планете в течение последних 15 лет. Я считаю, что это будет намного проще в использовании, тем более, что вы уже говорите по HTTP. – ladenedge

+1

Я добавил примечание к ответу Ремуса, PasswordDeriveBytes - это несогласованная, недокументированная [собственная схема] (http://bouncy-castle.1462172.n4.nabble.com/NET-PasswordDeriveBytes-td1462616.html) для всех байты, запрошенные у него по максимальному выходу PBKDF1 (20 байтов). –

0

Есть ли веская причина, почему вы не можете просто использовать http://php.net/manual/en/function.mcrypt-module-open.php и использовать rijndael-256 в качестве алгоритма ????

+0

Этот модуль еще более низкоуровневый, чем тот, который у него уже есть! – ladenedge

+0

Да, это не сработает. Мне нужно СОЛЬ, между прочим. –

0

Проверьте процедуры OpenSSL на PHP, они должны уметь обрабатывать то, что вам нужно.

 Смежные вопросы

  • Нет связанных вопросов^_^