C # - проблема с расшифровкой AES - всегда становится нулевой - PullRequest
0 голосов
/ 17 ноября 2018

Я пытаюсь реализовать стеганографию изображений с помощью LSB, и все работает, кроме дешифрования.

Ниже приведен мой класс, отвечающий за шифрование и дешифрование строк.Шифрование работает нормально, но метод Decrypt всегда возвращает ноль:

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

namespace WindowsFormsApp1
{
   class Encryptor {
    //text to encrypt or already decrypted
    private String decryptedText = "";
    //text to decrypt or already encrypted
    private String encryptedText = "";
    private String key = "";

    public Encryptor setDecryptedText(String text)
    {
        decryptedText = text;

        return this;
    }

    public Encryptor setEncryptedText(String text)
    {
        encryptedText = text;

        return this;
    }
    public Encryptor setKey(String text)
    {
        key = text;

        return this;
    }

    Byte[] getHash(Byte[] hash)
    {
        Byte[] newHash = new Byte[32];
        for (int i = 0; i < 32; i++)
        {
            newHash[i] = hash[i];
        }

        return newHash;
    }

    Byte[] getIV(Byte[] hash)
    {
        Byte[] newHash = new Byte[16];
        int j = 0;
        for (int i = 32; i < 48; i++)
        {
            newHash[j++] = hash[i];
        }

        return newHash;
    }

    String EncryptAesManaged()
    {
        SHA512 shaM = new SHA512Managed();

        Byte[] data = Encoding.UTF8.GetBytes(key);
        Byte[] hash = shaM.ComputeHash(data);

        try
        {
            return Encrypt(decryptedText, getHash(hash), getIV(hash));
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }

        return null;
    }

    String DecryptAesManaged()
    {
        SHA512 shaM = new SHA512Managed();
        var data = Encoding.UTF8.GetBytes(key);
        Byte[] hash = shaM.ComputeHash(data);
        try
        {
            return Decrypt(Convert.FromBase64String(encryptedText), getHash(hash), getIV(hash));
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
        return "";
    }

    String Encrypt(string plainText, byte[] Key, byte[] IV)
    {
        Byte[] encrypted;
        using (RijndaelManaged aes = new RijndaelManaged())
        {
            aes.Mode = CipherMode.CBC;
            aes.BlockSize = 128;
            aes.KeySize = 256;
            ICryptoTransform encryptor = aes.CreateEncryptor(Key, IV);
            using (MemoryStream ms = new MemoryStream())
            {
                using (CryptoStream cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
                {
                    using (StreamWriter sw = new StreamWriter(cs)) { 
                        sw.Write(Encoding.UTF8.GetBytes(plainText));
                        cs.FlushFinalBlock();
                        encrypted = ms.ToArray();
                    }
                }
            }
            aes.Clear();
        }

        return  Convert.ToBase64String(encrypted);
    }
    string Decrypt(byte[] cipherText, byte[] Key, byte[] IV)
    {
        string plaintext = null;
        using (RijndaelManaged aes = new RijndaelManaged())
        {
            aes.Mode = CipherMode.CBC;
            aes.BlockSize = 128;
            aes.KeySize = 256;
            ICryptoTransform decryptor = aes.CreateDecryptor(Key, IV);
            try
            {
                using (MemoryStream ms = new MemoryStream(cipherText))
                using (CryptoStream cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read))
                using (StreamReader reader = new StreamReader(cs))
                {
                    plaintext = reader.ReadToEnd(); //Here get null
                }

                aes.Clear();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }

        return plaintext;
    }

    public String getEncrypted()
    {
         return EncryptAesManaged();

    }

    public String getDecrypted()
    {
         return DecryptAesManaged();

    }
  }
}

Почему Decrypt() возвращает null, а не изначально зашифрованную строку?

1 Ответ

0 голосов
/ 17 ноября 2018

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

public static void Test()
{
    var key = "my key";
    var plainText = "hello";

    var encryptor = new Encryptor();

    encryptor.setDecryptedText(plainText);
    encryptor.setKey(key);

    var encrypted = encryptor.getEncrypted();

    Console.WriteLine(encrypted);

    var deecryptor = new Encryptor();

    deecryptor.setEncryptedText(encrypted);
    deecryptor.setKey(key);

    var decrypted = deecryptor.getDecrypted();

    Console.WriteLine(decrypted);

    Assert.IsTrue(plainText == decrypted);
}

Демонстрационная скрипка # 1 здесь .

Учитывая, что в вашем коде есть 2 проблемы, обе из них на самом деле находятся в шифровании, а не в дешифровании.

Сначала , в Encrypt(string plainText, byte[] Key, byte[] IV) вы пишете в StreamWriter sw, затем очищаете CryptoStream и возвращаете содержимое MemoryStream - но вы никогда не очищаете и не удаляете sw, поэтому его буферизованное содержимое никогда не пересылается в основной поток (ы).

Чтобы это исправить, ваш код должен выглядеть примерно так:

using (MemoryStream ms = new MemoryStream())
{
    using (CryptoStream cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
    {
        using (StreamWriter sw = new StreamWriter(cs)) 
        { 
            sw.Write(Encoding.UTF8.GetBytes(plainText));
        }
    }
    encrypted = ms.ToArray();
}

Теперь getDecrypted() больше не возвращает результат null - вместо этого он возвращает неверный результат "System.Byte[]", как показано в демонстрационной скрипте # 2 здесь .

Во-вторых , снова в Encrypt(...), вы эффективно кодируете свой plainText дважды в этой строке:

sw.Write(Encoding.UTF8.GetBytes(plainText));

Encoding.UTF8.GetBytes(plainText) преобразует простой текст в байтовый массив, но StreamWriter также предназначен для выполнения этой работы, преобразования строк в байты и передачи их в базовый поток. Таким образом, поскольку вы не передаете строку в Write(), вызывается перегрузка StreamWriter.Write(Object):

Записывает текстовое представление объекта в текстовую строку или поток, вызывая метод ToString() для этого объекта.

Таким образом, в действительности шифруется значение ToString() байтового массива, равное "System.Byte[]".

Чтобы это исправить, просто удалите вызов Encoding.UTF8.GetBytes(plainText) и напишите строку напрямую. Таким образом, ваш метод Encrypt() должен выглядеть следующим образом:

static String Encrypt(string plainText, byte[] Key, byte[] IV)
{
    string encrypted;
    using (var aes = new RijndaelManaged())
    {
        aes.Mode = CipherMode.CBC;
        aes.BlockSize = 128;
        aes.KeySize = 256;
        ICryptoTransform encryptor = aes.CreateEncryptor(Key, IV);
        using (var ms = new MemoryStream())
        {
            using (var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write, true))
            {
                using (var sw = new StreamWriter(cs)) 
                { 
                    sw.Write(plainText);
                }
            }                   
            // Calling GetBuffer() avoids the extra allocation of ToArray().
            encrypted = Convert.ToBase64String(ms.GetBuffer(), 0, checked((int)ms.Length)); 
        }
        aes.Clear();
    }

    return encrypted;
}

Демонстрационная скрипка # 3 здесь , которая теперь успешно проходит.

Отказ от ответственности: в этом ответе не предпринимается попытка проверить ваш код на предмет соответствия рекомендациям по безопасности, таким как безопасная установка соли и IV .

...