Подтвердить что ты не робот

Rewrite Rijndael 256 С# Код шифрования в PHP

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

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

? >

4b9b3361

Ответ 1

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

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

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

PBKDF1 (P, S, c, dkLen)

Опции: Хэш
основная хеш-функция

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

Выход: DK key, строка 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

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

            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-реализацию, ваш опубликованный код С# имеет серьезные проблемы. Вы смешиваете байт-массивы с помехами, представляющими шестнадцатеричные дампы, вы используете жестко закодированный IV вместо того, чтобы выводить его из кодовой фразы и соли, просто неправильно. Пожалуйста, используйте схему шифрования вне поля, например SSL или S-MIME, и не заново создавайте свои собственные. Вы получите неправильный.

Ответ 2

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

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

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

Ответ 4

Проверяйте подпрограммы OpenSSL на PHP, они должны иметь возможность обрабатывать то, что вам нужно делать.