класс дешифрования шифрования c# MCRYPT RIJNDAEL 256 в php


Я пытаюсь преобразовать приложение c# в php, но застрял в месте, где C# предоставляет класс безопасности для шифрования и дешифрования на основе RIJNDAEL algo. Я пытаюсь преобразовать в php.

Примечание: Я использую php 7.2, поэтому mcrypt устарел для этой версии.

Код на C#

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;

namespace pharmarackencryption
{
    class Program
    {
        private const string initVector = "aw90rela942f65u2";

        // This constant is used to determine the keysize of the encryption algorithm.
        private const int keysize = 256;

        public static string Encrypt(string plainText, string passPhrase = "testing")
        {
            byte[] initVectorBytes = Encoding.UTF8.GetBytes(initVector);
            byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText);
            PasswordDeriveBytes password = new PasswordDeriveBytes(passPhrase, null);
            byte[] keyBytes = password.GetBytes(keysize / 8);
            RijndaelManaged symmetricKey = new RijndaelManaged();
            symmetricKey.Mode = CipherMode.CBC;
            ICryptoTransform encryptor = symmetricKey.CreateEncryptor(keyBytes, initVectorBytes);
            MemoryStream memoryStream = new MemoryStream();
            CryptoStream cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write);
            cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);
            cryptoStream.FlushFinalBlock();
            byte[] cipherTextBytes = memoryStream.ToArray();
            memoryStream.Close();
            cryptoStream.Close();
            return Convert.ToBase64String(cipherTextBytes);
        }

        public static string Decrypt(string cipherText, string passPhrase = "testing")
        {
            byte[] initVectorBytes = Encoding.ASCII.GetBytes(initVector);
            byte[] cipherTextBytes = Convert.FromBase64String(cipherText);
            PasswordDeriveBytes password = new PasswordDeriveBytes(passPhrase, null);
            byte[] keyBytes = password.GetBytes(keysize / 8);
            RijndaelManaged symmetricKey = new RijndaelManaged();
            symmetricKey.Mode = CipherMode.CBC;
            ICryptoTransform decryptor = symmetricKey.CreateDecryptor(keyBytes, initVectorBytes);
            MemoryStream memoryStream = new MemoryStream(cipherTextBytes);
            CryptoStream cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read);
            byte[] plainTextBytes = new byte[cipherTextBytes.Length];
            int decryptedByteCount = cryptoStream.Read(plainTextBytes, 0, plainTextBytes.Length);
            memoryStream.Close();
            cryptoStream.Close();
            return Encoding.UTF8.GetString(plainTextBytes, 0, decryptedByteCount);
        }

        static void Main(string[] args)
        {
            Program p = new Program();
            string enc_password = Encrypt("437217");
            string dec_password = Decrypt("C9xJGa03dRQx9ePm0nLnHg==");
            Console.WriteLine(enc_password);
            Console.WriteLine(dec_password);
        }
    }
}

Шифрование: C9xJGa03dRQx9ePm0nLnHg==

Я нашел кое-что похожее на код в php, например

PHP-код:

<?php 
    // key/iv in ASCII binary data, $str base64
    function decrypt_stuff($key, $str, $iv) {
        // $plaintext_dec = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, base64_decode($str), MCRYPT_MODE_CBC, $iv);
        $plaintext_dec = openssl_decrypt(base64_decode($str), "aes-256-cbc", $key,  OPENSSL_RAW_DATA|OPENSSL_ZERO_PADDING, $iv);
        return $plaintext_dec;
    }

    // key/iv in ascii binary data, $str ascii
    function encrypt_stuff($key, $str, $iv) {
        // $ciphertext = base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $str, MCRYPT_MODE_CBC, $iv));
        if (($l = (strlen($str) & 15)) > 0) { $str .= str_repeat(chr(0), 16 - $l); }
        $ciphertext = base64_encode(openssl_encrypt($str, "aes-256-cbc", $key,  OPENSSL_RAW_DATA|OPENSSL_ZERO_PADDING, $iv));
        return $ciphertext;
    }

    echo encrypt_stuff("testing","437217","aw90rela942f65u2");
    //Result : LTbhEHjFgfa5PDJQXJEdKQ==

Оба происходит то же самое, но все равно результат другой

 3
Author: Juned Ansari, 2018-09-05

4 answers

Ваш PHP-код дает разные результаты, потому что вы не используете один и тот же ключ. В своем коде C# вы используете PasswordDeriveBytes для создания ключа, который является реализацией PBKDF1 от Microsoft.

PHP не поддерживает PBKDF1, но мы могли бы написать функцию, совместимую с C#. Приведенный ниже код вдохновлен этим замечательным ответом Мартена Бодевеса, переведенным на PHP.

function passwordDeriveBytes($password, $salt, $iterations = 100, $len = 32) {
    $key = $password . $salt;
    for($i = 0; $i < $iterations; $i++) {
        $key = sha1($key, true);
    }
    if (strlen($key) < $len) {
        $hx = passwordDeriveBytes($password, $salt, $iterations - 1, 20);
        $counter = 0;
        while (strlen($key) < $len) {
            $counter += 1;
            $key .= sha1($counter . $hx, true);
        }
    }
    return substr($key, 0, $len);
}

Кроме того, вы кодируете bese64 и заполняете свои данные вручную. Ты готовишься заполнение нулевым байтом, но в коде C# вы используете заполнение PKCS7 (по умолчанию и предпочтительнее). Лучше всего позволить openssl заполнять и кодировать ваши данные.

function encrypt_stuff($key, $str, $iv) {
    return openssl_encrypt($str, "aes-256-cbc", $key, 0, $iv);
}

function decrypt_stuff($key, $str, $iv) {
    return openssl_decrypt($str, "aes-256-cbc", $key, 0, $iv);
}

Используя ключ, полученный из passwordDeriveBytes, этот PHP-код дает те же результаты, что и ваш код на C#.

$key = passwordDeriveBytes("testing", null);
$enc = encrypt_stuff($key,"437217","aw90rela942f65u2");
echo $enc;
//C9xJGa03dRQx9ePm0nLnHg==

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

  • Лучше всего использовать PBKDF2 для вашего ключа. Вы можете использовать Rfc2898DeriveBytes в C#:

    Rfc2898DeriveBytes kdf = new Rfc2898DeriveBytes(password, salt, iterations);
    byte[] key = kdf.GetBytes(32);
    

    И hash_pbkdf2 в PHP:

    $key = hash_pbkdf2("sha1", $password, $salt, $iterations, 32, true);
    

    Длина соли должна быть не менее 8 байт, а количество итераций должно быть не менее 10 000.

  • Вы не используете соль. Вы должны использовать случайную соль для каждого пароля, это делает ваши ключи более надежными.

  • Вы используете статическую капельницу. Капельница должна быть уникальной и непредсказуемой. Вы можете создать случайную капельницу с RNGCryptoServiceProvider:

    byte[] iv = new byte[16];
    RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider(); 
    rng.GetBytes(iv);
    

    И openssl_random_pseudo_bytes:

    $iv = openssl_random_pseudo_bytes(16);
    

    Вы могли бы используйте этот код и для соли.

  • Вы не используете аутентифицированное шифрование. Если вы используете PHP 7, вы можете использовать GCM, но, к сожалению, .NET не предоставляет никаких алгоритмов AEAD. Однако вы можете использовать bouncycastle, если вы решите использовать аутентифицированное шифрование.


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

Если бы вы использовали mcrypt с MCRYPT_RIJNDAEL_256, вы все равно могли бы сделать свой PHP-код совместимым с C#, так как RijndaelManaged поддерживает размер блока 256 бит.

symmetricKey.BlockSize = 256;

Однако вы не должны использовать mcrypt, потому что он не поддерживается, и он устарел в PHP 7.

 4
Author: t.m.adam, 2018-09-10 11:35:31

Я не знаком с концом этого вопроса на языке C#. Однако я укажу на некоторую информацию, которая может иметь отношение к вашей проблеме.

  • Описания в функциях RIJNDAEL относятся к размеру блока, а не к размеру ключа. Например, MCRYPT_RIJNDAEL_256 указывает, что вы используете размер блока 256. В нем не указан размер ключа.
  • Для функций openssl AES вы указываете размер ключа. В качестве примера aes-256-cbc указывает, что вы используете 256-битный ключ.
  • Размер блока для всех функций AES составляет 128 бит. Таким образом, вы можете использовать MCRYPT_RIJNDAEL_128, который соответствует размеру блока AES. Вы можете использовать 256-битный ключ как с MCRYPT_RIJNDAEL_128, так и с aes-256-cbc, поскольку они оба используют размер блока 128 бит.
  • Поэтому по этим причинам aes-256-cbc нельзя использовать с MCRYPT_RIJNDAEL_256. Они просто не одно и то же.

  • Это лишнее. Если вы используете aes-256-cbc, вам нужно убедиться, что вы используете 256-битный ключ. Очевидно, убедитесь, что что вы используете один и тот же ключ для шифрования и дешифрования. Убедитесь, что ваша капельница - правильная капельница. Я бы сделал их обоих статичными для тестирования. Как только вы заставите его работать, повозитесь с добавлением IV в строку шифрования при шифровании и отделением IV от зашифрованного текста при расшифровке

  • Используйте openssl_random_pseudo_bytes() для создания вашего 256-битного (32-байтового) ключа. Вы также можете использовать openssl_random_pseudo_bytes() для создания вашего IV.

На заметке сбоку. Я бы настоятельно рекомендовал использовать Libsodium библиотека. Теперь он встроен в последние версии PHP и также имеет библиотеку C#. Вы можете найти его на Github достаточно легко.

Как только вы начнете это делать, я бы посмотрел, как научиться аутентифицировать/проверять ваше шифрование. Вот хорошая отправная ссылка для этого. Проверка подлинности Считана

Надеюсь, это поможет.

 1
Author: Joseph_J, 2018-09-09 08:18:00

Не рекомендуется хранить пароли в базе данных с использованием обратимого алгоритма, пароли следует хранить с использованием дорогостоящих хэшей. Вам следует рассмотреть возможность переключения хранилища паролей на хэш, такой как Argon2.

Проверьте это: http://php.net/manual/en/function.password-hash.php

 0
Author: nhlm, 2018-09-08 14:13:18

Mcrypt был перемещен. не удален. вы можете попробовать установить это

sudo apt-get -y install gcc make autoconf libc-dev pkg-config
sudo apt-get -y install php7.2-dev
sudo apt-get -y install libmcrypt-dev
sudo pecl install mcrypt-1.0.1

PS: не проверено

 -1
Author: sabkaraja, 2018-09-05 09:48:38