Шифрование C# для расшифровки PHP

Я пытаюсь зашифровать некоторые (cookie) данные на C#, а затем расшифровать их на PHP. Я решил использовать шифрование Rijndael. У меня почти получилось, за исключением того, что расшифрована только часть текста! Я начал работать с этого примера:расшифровать зашифрованную строку PHP в C#

вот текст (JSON), который я шифрую (конфиденциальная информация удалена):

{"DisplayName":"xxx", "Username": "yyy", "EmailAddress":"zzz"}

поэтому я вхожу в приложение c#, которое создает / кодирует cookie из сохраненного ключа и IV и затем перенаправляет на PHP-приложение, которое должно расшифровать / прочитать файл cookie. Когда я расшифровываю файл cookie, он выглядит так:

{"DisplayName":"xxx","F�A ;��HP=D�������4��z����ť���k�#E���R�j�5��t. t�D��" 

обновление: я получил немного дальше и теперь это результат

string(96) "{"DisplayName":"xxx","Username":"yyy","EmailAddress"�)ق��-�J��k/VV-v� �9�B`7^" 

как вы можете видеть, он начинает расшифровывать, но потом испортится...

при расшифровке строки она выходит правильной (с заполнением, которое у меня есть функция для удаления заполнения), но если я изменю тестовая строка одним символом я снова получаю мусор:

B�nHL�Ek    �¿?�UΣlO����OЏ�M��NO/�f.M���Lƾ�CC�Y>F��~�qd�+

вот код C#, который я использую для генерации случайного ключа и IV:

обновление: сейчас я просто использую статический ключ/IV, вот они:

Key: lkirwf897+22#bbtrm8814z5qq=498j5
IV: 741952hheeyy66#cs!9hjv887mxx7@8y

RijndaelManaged symmetricKey = new RijndaelManaged();
symmetricKey.BlockSize = 256;
symmetricKey.KeySize = 256;
symmetricKey.Padding = PaddingMode.Zeros;
symmetricKey.Mode = CipherMode.CBC;
string key = Convert.ToBase64String(symmetricKey.Key);
string IV = Convert.ToBase64String(symmetricKey.IV);

Затем я сохраняю ключ и IV в базе данных, которая будет получена позже для кодирования/декодирования.

это полное шифрование класс:

public static class Encryption
    {
        public static string Encrypt(string prm_text_to_encrypt, string prm_key, string prm_iv)
        {
            var sToEncrypt = prm_text_to_encrypt;

            var rj = new RijndaelManaged()
            {
                Padding = PaddingMode.PKCS7,
                Mode = CipherMode.CBC,
                KeySize = 256,
                BlockSize = 256,
                //FeedbackSize = 256
            };

            var key = Encoding.ASCII.GetBytes(prm_key);
            var IV = Encoding.ASCII.GetBytes(prm_iv);
            //var key = Convert.FromBase64String(prm_key);
            //var IV = Convert.FromBase64String(prm_iv);

            var encryptor = rj.CreateEncryptor(key, IV);

            var msEncrypt = new MemoryStream();
            var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write);

            var toEncrypt = Encoding.ASCII.GetBytes(sToEncrypt);

            csEncrypt.Write(toEncrypt, 0, toEncrypt.Length);
            csEncrypt.FlushFinalBlock();

            var encrypted = msEncrypt.ToArray();

            return (Convert.ToBase64String(encrypted));
        }

        public static string Decrypt(string prm_text_to_decrypt, string prm_key, string prm_iv)
        {

            var sEncryptedString = prm_text_to_decrypt;

            var rj = new RijndaelManaged()
            {
                Padding = PaddingMode.PKCS7,
                Mode = CipherMode.CBC,
                KeySize = 256,
                BlockSize = 256,
                //FeedbackSize = 256
            };

            var key = Encoding.ASCII.GetBytes(prm_key);
            var IV = Encoding.ASCII.GetBytes(prm_iv);
            //var key = Convert.FromBase64String(prm_key);
            //var IV = Convert.FromBase64String(prm_iv);

            var decryptor = rj.CreateDecryptor(key, IV);

            var sEncrypted = Convert.FromBase64String(sEncryptedString);

            var fromEncrypt = new byte[sEncrypted.Length];

            var msDecrypt = new MemoryStream(sEncrypted);
            var csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read);

            csDecrypt.Read(fromEncrypt, 0, fromEncrypt.Length);

            return (Encoding.ASCII.GetString(fromEncrypt));
        }

        public static void GenerateKeyIV(out string key, out string IV)
        {
            var rj = new RijndaelManaged()
            {
                Padding = PaddingMode.PKCS7,
                Mode = CipherMode.CBC,
                KeySize = 256,
                BlockSize = 256,
                //FeedbackSize = 256
            };
            rj.GenerateKey();
            rj.GenerateIV();

            key = Convert.ToBase64String(rj.Key);
            IV = Convert.ToBase64String(rj.IV);
        }
    }

вот код PHP, который я использую для расшифровки данных:

    function decryptRJ256($key,$iv,$string_to_decrypt)
{
    $string_to_decrypt = base64_decode($string_to_decrypt);
    $rtn = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, $string_to_decrypt, MCRYPT_MODE_CBC, $iv);
    //$rtn = rtrim($rtn, "");
    $rtn = unpad($rtn);
    return($rtn);
}

function unpad($value)
{
    $blockSize = mcrypt_get_block_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC);
    //apply pkcs7 padding removal
    $packing = ord($value[strlen($value) - 1]);
    if($packing && $packing < $blockSize){
        for($P = strlen($value) - 1; $P >= strlen($value) - $packing; $P--){
            if(ord($value{$P}) != $packing){
                $packing = 0;
            }//end if
        }//end for
    }//end if 

    return substr($value, 0, strlen($value) - $packing); 
}

$ky = 'lkirwf897+22#bbtrm8814z5qq=498j5'; // 32 * 8 = 256 bit key
$iv = '741952hheeyy66#cs!9hjv887mxx7@8y'; // 32 * 8 = 256 bit iv

$enc = $_COOKIE["MyCookie"];

$dtext = decryptRJ256($ky, $iv, $enc);
var_dump($dtext);

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

обновление: я использую ключи ASCII в формат, необходимый PHP. Если я создаю ключи из класса RijndaelManaged, они не работают на стороне PHP, но я могу использовать ключи, которые, как известно, работают на стороне PHP и используют их на стороне RijndaelManaged c#.

пожалуйста, дайте мне знать, если я пропустил какой-либо информации. ТИА!

2 ответов


поскольку строка частично в порядке, но в конце есть тарабарщина, это предполагает проблему заполнения в шифровании, которое ожидает точные блоки 256 байтов. Я предлагаю установить заполнение как pkcs7 в (PaddingMode.Pkcs7 в) Вместо нули на стороне C#, которую PHP поймет без проблем (так как это режим по умолчанию для этого парсера).

Edit: упс, я не заметил, что у вас были следующие PHP:

$enc = $_COOKIE["MyCookie"];

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

Edit2:

преобразуйте пробелы в отсутствующие символы + из файла cookie, добавив следующее:

str_replace(' ', '+', $enc);

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

Класс Шифрования C#

public static class Encryption
{
    public static string Encrypt(string prm_text_to_encrypt, string prm_key, string prm_iv)
    {
        var sToEncrypt = prm_text_to_encrypt;

        var rj = new RijndaelManaged()
        {
            Padding = PaddingMode.PKCS7,
            Mode = CipherMode.CBC,
            KeySize = 256,
            BlockSize = 256,
        };

        var key = Convert.FromBase64String(prm_key);
        var IV = Convert.FromBase64String(prm_iv);

        var encryptor = rj.CreateEncryptor(key, IV);

        var msEncrypt = new MemoryStream();
        var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write);

        var toEncrypt = Encoding.ASCII.GetBytes(sToEncrypt);

        csEncrypt.Write(toEncrypt, 0, toEncrypt.Length);
        csEncrypt.FlushFinalBlock();

        var encrypted = msEncrypt.ToArray();

        return (Convert.ToBase64String(encrypted));
        }

    public static string Decrypt(string prm_text_to_decrypt, string prm_key, string prm_iv)
    {

        var sEncryptedString = prm_text_to_decrypt;

        var rj = new RijndaelManaged()
        {
            Padding = PaddingMode.PKCS7,
            Mode = CipherMode.CBC,
            KeySize = 256,
            BlockSize = 256,
        };

        var key = Convert.FromBase64String(prm_key);
        var IV = Convert.FromBase64String(prm_iv);

        var decryptor = rj.CreateDecryptor(key, IV);

        var sEncrypted = Convert.FromBase64String(sEncryptedString);

        var fromEncrypt = new byte[sEncrypted.Length];

        var msDecrypt = new MemoryStream(sEncrypted);
        var csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read);

        csDecrypt.Read(fromEncrypt, 0, fromEncrypt.Length);

        return (Encoding.ASCII.GetString(fromEncrypt));
        }

    public static void GenerateKeyIV(out string key, out string IV)
    {
        var rj = new RijndaelManaged()
        {
            Padding = PaddingMode.PKCS7,
            Mode = CipherMode.CBC,
            KeySize = 256,
            BlockSize = 256,
        };
        rj.GenerateKey();
        rj.GenerateIV();

        key = Convert.ToBase64String(rj.Key);
        IV = Convert.ToBase64String(rj.IV);
    }
}

PHP фрагмент расшифровки

<?php
function decryptRJ256($key,$iv,$encrypted)
{
    //PHP strips "+" and replaces with " ", but we need "+" so add it back in...
    $encrypted = str_replace(' ', '+', $encrypted);

    //get all the bits
    $key = base64_decode($key);
    $iv = base64_decode($iv);
    $encrypted = base64_decode($encrypted);

    $rtn = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, $encrypted, MCRYPT_MODE_CBC, $iv);
    $rtn = unpad($rtn);
    return($rtn);
}

//removes PKCS7 padding
function unpad($value)
{
    $blockSize = mcrypt_get_block_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC);
    $packing = ord($value[strlen($value) - 1]);
    if($packing && $packing < $blockSize)
    {
        for($P = strlen($value) - 1; $P >= strlen($value) - $packing; $P--)
        {
            if(ord($value{$P}) != $packing)
            {
                $packing = 0;
            }
        }
    }

    return substr($value, 0, strlen($value) - $packing); 
}
?>
<pre>
<?php

$enc = $_COOKIE["MyCookie"];

$ky = ""; //INSERT THE KEY GENERATED BY THE C# CLASS HERE
$iv = ""; //INSERT THE IV GENERATED BY THE C# CLASS HERE

$json_user = json_decode(decryptRJ256($ky, $iv, $enc), true);

var_dump($json_user);

?>