【问题标题】:C# - Problem with AES Decryption - always get nullC# - AES 解密问题 - 总是为空
【发布时间】:2018-11-20 05:57:10
【问题描述】:

我正在尝试使用 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而不是原来的加密字符串?

【问题讨论】:

  • (CryptoStream cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read),这应该使用 (CryptoStream cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Write)
  • @kelalaka - CryptoStreamMode.Read 是在Decrypt() 中使用的正确模式,因为解密方法需要对加密流的读取访问权限。

标签: c# encryption aes


【解决方案1】:

你没有展示你如何使用你的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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-04-24
    • 1970-01-01
    • 1970-01-01
    • 2012-11-10
    • 1970-01-01
    • 1970-01-01
    • 2012-07-20
    • 2011-04-11
    相关资源
    最近更新 更多