Вопрос по aes, encryption, php, rijndael, c# – Переписать код шифрования Rijndael 256 C # в PHP

12

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

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

?>

Ваш Ответ

4   ответа
4

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

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

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

Я также добавил примечание к ответу Remus, PasswordDeriveBytes является непоследовательным, недокументированнымзапатентованная схема для всех запрошенных байтов сверх максимальной производительности PBKDF1 (20 байтов). Maarten Bodewes
@ Дерек Армстронг: да! SSL предназначен для защиты таких вещей, как незашифрованные пароли, как и ваша схема. Основным отличием является то, что SSL был рассмотрен и пересмотрен лучшими криптографами на планете за последние 15 лет. Я считаю, что это будет намного проще в использовании, тем более что вы уже общаетесь по HTTP. ladenedge
Спасибо за этот ответ. Да, я понял, что основная проблема заключается в PasswordDeriveBytes (). Вы говорите, что передача открытого текстового пароля через HTTPS с SSL более безопасна, чем кодирование его с одной стороны и дешифрование с другой? Derek Armstrong
0

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

Да, это не сработает. Мне нужно СОЛЬ среди прочего. Derek Armstrong
Этот модуль даже более низкого уровня, чем тот, который он уже получил! ladenedge
0

они должны быть в состоянии обработать то, что вам нужно сделать.

17

как это делает код C # в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, строка dkLen-octet

шаги:

  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, Давайте посмотрим, что выводит наша функция: <, / p>

$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, и не изобретайте заново свою собственную. Ты получишь этонеправильно.

// warning: no checking on $dklen, must be 0..20 function PBKDF1($pass,$salt,$count,$dklen) { $t = $pass.$salt;; for($i=0; $i < $count; $i++) { $t = sha1($t, true); } return substr($t,0,$dklen); } Maarten Bodewes
Спасибо за ответ, я думаю, что понимаю, что вы пытаетесь сказать здесь, но недостаточно для того, чтобы фактически использовать его. Возможен ли полностью закодированный пример? Derek Armstrong
@RemusRusanu возможно, но функция принимает смещение,длина Maarten Bodewes
Это функция PHP, которую я придумал для PBKDF1 $ HashPassPhrase = "passpharse"; $ HashSalt = "saltvalue"; $ HashIterations = 2; $ devkeylength = 32; $ devkey = PBKDF1 ($ HashPassPhrase, $ Hashsalt, $ HashIterations, $ devkeylength); функция PBKDF1 ($ pass, $ salt, $ count, $ dklen) {$ t = sha1 ($ pass. $ salt); для ($ i = 1; $ i <$ count; $ i ++) {$ t = sha1 ($ t); } $ t = substr ($ t, 0, $ dklen-1); вернуть $ t; } Но я не думаю, что получаю правильные результаты. Derek Armstrong

Похожие вопросы