Rewrite Rijndael 256 C# Encryption Code in PHP

I have an encryption/decryption algorithm written in C# - I need to be able to produce the same encryption in PHP so I can send the encrypted text over HTTP to be decrypted on the C# side. Here is the C# code for the encryption.

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

I have some similar PHP code that may help. It doesn't do exactly as needed, but I think it may be a good place to start.

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

?>

12
задан Derek Armstrong 17 August 2010 в 17:59
поделиться

4 ответа

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

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

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

4
ответ дан 2 December 2019 в 07:20
поделиться

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

0
ответ дан 2 December 2019 в 07:20
поделиться

Вам необходимо получить ключ из парольной фразы так же, как код 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

Шаги:

  1. Если dkLen> 16 для MD2 и MD5 или dkLen> 20 для SHA-1, выведите
 "полученный ключ слишком длинный" и остановитесь.

2. Примените базовую хеш-функцию Hash для итераций c к
соединение пароля P и соли S, затем извлечь
первые октеты dkLen для создания производного ключа DK:

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

3. Выведите производный ключ 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, и не изобретайте заново свою собственную. Вы получите неверно .

17
ответ дан 2 December 2019 в 07:20
поделиться

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

0
ответ дан 2 December 2019 в 07:20
поделиться
Другие вопросы по тегам:

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