C#-AES解密问题-始终为null

时间:2018-11-17 15:04:21

标签: c# encryption aes

我正在尝试使用LSB实现图像隐写术,除解密外,其他所有方法均有效。

下面有我的班级负责字符串的加密和解密。加密工作正常,但是Decrypt方法始终返回null:

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 :(得分:3)

您没有显示如何使用Encryptor类,因此您的问题中并没有包含Minimal, Complete, and Verifiable example。我可以使用以下测试工具重现该问题:

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 here

鉴于此,您的代码有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 here中所示。

其次,再次在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 here现在成功通过。

免责声明::此答案不会尝试查看您的代码以获取安全性最佳做法,例如salt and IV的安全设置。