C#AES解密代码

时间:2015-08-28 15:20:34

标签: c# encryption cryptography aes

我有AES算法的字符串加密/解密代码。加密工作正常,但我解密后无法获得正确的纯文本。解密代码中肯定有问题。

我复制了以下代码 请帮忙。感谢。

以下是加密和解密代码。

public static class EncryptionHelper
{
    private static int BlockSize = 16;

    private static byte[] Key
    {
        get
        {
            byte[] hash = SHA1.Create().ComputeHash(Encoding.UTF8.GetBytes("BlahBlahBlah"));
            byte[] key = new byte[BlockSize];
            Array.Copy(hash, 0, key, 0, BlockSize);
            return key;
        }
    }

    private static byte[] IV
    {
        get
        {
            StringBuilder builder = new StringBuilder();
            Random random = new Random();
            for (int i = 0; i < BlockSize; i++)
            {
                char ch = Convert.ToChar(Convert.ToInt32(Math.Floor(26 * random.NextDouble() + 65)));
                builder.Append(ch);
            }
            return Encoding.UTF8.GetBytes(builder.ToString());
        }
    }

    public static string DecodeAndDecrypt(string cipherText)
    {
        string DecodeAndDecrypt = AesDecrypt(Convert.FromBase64String(HttpUtility.UrlDecode(cipherText)));
        return (DecodeAndDecrypt);
    }

    public static string EncryptAndEncode(string plaintext)
    {
        return HttpUtility.UrlEncode(Convert.ToBase64String(AesEncrypt(plaintext))); 
    }

    public static string AesDecrypt(Byte[] inputBytes)
    {
        Byte[] outputBytes = inputBytes;

        string plaintext = string.Empty;

        using (MemoryStream memoryStream = new MemoryStream(outputBytes))
        {
            using (CryptoStream cryptoStream = new CryptoStream(memoryStream, GetCryptoAlgorithm().CreateDecryptor(Key, IV), CryptoStreamMode.Read))
            {
                using (StreamReader srDecrypt = new StreamReader(cryptoStream))
                {
                    plaintext = srDecrypt.ReadToEnd();
                }
            }
        }

        return plaintext;
    }

    public static byte[] AesEncrypt(string inputText)
    {
        byte[] inputBytes = UTF8Encoding.UTF8.GetBytes(inputText); 

        for (int i = 0; i < BlockSize; i++)
        {
            inputBytes[i] ^= IV[i];
        }

        byte[] result = null;
        using (MemoryStream memoryStream = new MemoryStream())
        {
            using (CryptoStream cryptoStream = new CryptoStream(memoryStream, GetCryptoAlgorithm().CreateEncryptor(Key, IV), CryptoStreamMode.Write))
            {
                cryptoStream.Write(inputBytes, 0, inputBytes.Length);
                cryptoStream.FlushFinalBlock();
                result = memoryStream.ToArray();
            }
        }
        return result;
    }

    private static RijndaelManaged GetCryptoAlgorithm()
    {
        RijndaelManaged algorithm = new RijndaelManaged();
        algorithm.Padding = PaddingMode.PKCS7;
        algorithm.Mode = CipherMode.CBC;
        algorithm.KeySize = 128;
        algorithm.BlockSize = 128;
        return algorithm;
    }
}

2 个答案:

答案 0 :(得分:1)

我做了一些更改,并将在下面发布一些源代码。要使其工作,只需在Form1上放置3个textBox并将此代码用于Form1.cs。我在上面提到过,我认为一个主要问题是如何在不指定编码的情况下将StreamReader作为字符串读取。我的更改使用BinaryReader作为字节数组读取,然后在读取后转换为UTF-8字符串。我也删除了那个XOR循环。我认为你试图自己实现CBC(CBC是一种XOR的反馈循环)但不需要 - 当你指定CBC模式时,.NET会为你做CBC模式。为了让我的版本尽可能接近您的版本,我没有做出更多更改。但请记住以上一些评论。例如,当.NET提供DeriveBytes函数时,不要使用简单的哈希。像纸巾一样处理静脉注射 - 只使用一次,使用一次。等等。无论如何,如果没有&#34;最佳实践&#34;代码如下:

编辑:抱歉,我忘了提及我还将纯文本类型更改为常规字符串,删除了与HTTP相关的代码。这只是为了让我更容易合作。它应该与您的HTTP方法一样好用,就像使用常规字符串一样。

using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using System.Windows.Forms;

namespace StackOverflow
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void textBox3_TextChanged(object sender, EventArgs e)
        {
            textBox1.Text = EncryptionHelper.EncryptAndEncode(textBox3.Text);
            textBox2.Text = EncryptionHelper.DecodeAndDecrypt(textBox1.Text);
        }
    }

    public static class EncryptionHelper
    {
        private static int BlockSize = 16;

        private static byte[] Key
        {
            get
            {
                byte[] hash = SHA1.Create().ComputeHash(Encoding.UTF8.GetBytes("BlahBlahBlah"));
                byte[] key = new byte[BlockSize];
                Array.Copy(hash, 0, key, 0, BlockSize);
                return key;
            }
        }

        private static byte[] IV
        {
            get
            {
                StringBuilder builder = new StringBuilder();
                Random random = new Random();
                for (int i = 0; i < BlockSize; i++)
                {
                    char ch = Convert.ToChar(Convert.ToInt32(Math.Floor(26 * random.NextDouble() + 65)));
                    builder.Append(ch);
                }
                return Encoding.UTF8.GetBytes(builder.ToString());
            }
        }

        public static string DecodeAndDecrypt(string cipherText)
        {
            byte[] cipherBytes = Convert.FromBase64String(cipherText);
            string decodeAndDecrypt = AesDecrypt(cipherBytes);
            return decodeAndDecrypt;
        }

        public static string EncryptAndEncode(string plaintext)
        {
            return Convert.ToBase64String(AesEncrypt(plaintext));
        }

        public static string AesDecrypt(Byte[] inputBytes)
        {
            Byte[] outputBytes = inputBytes;

            string plaintext = string.Empty;

            using (MemoryStream memoryStream = new MemoryStream(outputBytes))
            {
                using (CryptoStream cryptoStream = new CryptoStream(memoryStream, GetCryptoAlgorithm().CreateDecryptor(Key, IV), CryptoStreamMode.Read))
                {
                    using (BinaryReader srDecrypt = new BinaryReader(cryptoStream))
                    {
                        outputBytes = srDecrypt.ReadBytes(inputBytes.Length);
                        plaintext = Encoding.UTF8.GetString(outputBytes);
                    }
                }
            }
            return plaintext;
        }

        public static byte[] AesEncrypt(string inputText)
        {
            byte[] inputBytes = UTF8Encoding.UTF8.GetBytes(inputText);
            byte[] result = null;
            using (MemoryStream memoryStream = new MemoryStream())
            {
                using (CryptoStream cryptoStream = new CryptoStream(memoryStream, GetCryptoAlgorithm().CreateEncryptor(Key, IV), CryptoStreamMode.Write))
                {
                    cryptoStream.Write(inputBytes, 0, inputBytes.Length);
                    cryptoStream.FlushFinalBlock();
                    result = memoryStream.ToArray();
                }
            }
            return result;
        }

        private static RijndaelManaged GetCryptoAlgorithm()
        {
            RijndaelManaged algorithm = new RijndaelManaged();
            algorithm.Padding = PaddingMode.PKCS7;
            algorithm.Mode = CipherMode.CBC;
            algorithm.KeySize = 128;
            algorithm.BlockSize = 128;
            return algorithm;
        }
    }
}

答案 1 :(得分:1)

上面的评论(关于这个问题)最重要的一点是:你需要在加密上使用解密时使用相同的IV,否则你的前16个字节的恢复明文将被删除。 (CBC模式将在第一个块后自动更正。)

您可以在发送之前简单地将IV与密文结合起来,然后在解密之前将它们分开在另一端。

如果IV是公共知识,只要它是加密强,随机数据,每个加密都不同,这没关系。但是你需要相同的相应的,另一端的个人IV才能解密。

此外,正如WDS指出的那样,您不应该执行该XOR操作。 (但我无法看到WDS的代码如何在不保留IV的情况下恢复前16个字节。)

此外,恕我直言,使用SHA1而不是密码来生成密钥是一种安全风险。您应该使用PBKDF2或类似的方法来获取密码。

编辑:正如Artjom B.指出的那样,由于您的原始代码XOR是针对第一个明文块的IV,您可以通过在解密时使用所有0x00字节的IV来恢复前16个字节。但这只有在密文至少是两个块(32个字节)时才有效,否则,你会得到填充错误,因为这会破坏填充。尝试在解密时使用所有零字节的IV,并查明输入是否始终至少为16个字节 - 如果它不是&#39,那么无论如何你在加密中都有一个非现成的数组错误。 ;吨。 (你还会有一个不安全的IV,但至少你会解密工作。)