簡體   English   中英

C#AES解密代碼

[英]C# AES Decryption code

我有使用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;
    }
}

我做了一些更改,並將在下面發布一些源代碼。 若要使其工作,只需在Form1上放置3個文本框,然后將此代碼用於Form1.cs。 上面我提到過,我認為一個主要問題是如何在不指定編碼的情況下以字符串形式從StreamReader讀取數據。 我的更改使用BinaryReader讀取為字節數組,然后在讀取后轉換為UTF-8字符串。 我還刪除了XOR循環。 我認為您正在嘗試自己實現CBC(CBC是一種XOR的反饋循環),但不是必需的-當您指定CBC模式時,.NET會為您提供CBC模式。 為了使我的版本盡可能地接近您的版本,我沒有做更多更改。 但是請牢記上述一些評論。 例如,當.NET提供DeriveBytes函數時,請不要使用簡單的哈希。 將IV像紙巾一樣對待-永遠只使用一次。 等等。 無論如何,如果不是“最佳實踐”代碼,則功能如下:

編輯:對不起,我忘了提起我也將純文本類型更改為常規字符串,刪除了與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;
        }
    }
}

上面的注釋(關於這個問題)具有最重要的意義:您需要在解密時使用與加密相同的IV ,否則恢復的前16個字節的純文本將被丟棄。 (在第一個程序段之后,CBC模式將自動更正。)

您可以在發送前簡單地將IV與密文結合起來,然后在解密之前在另一端將它們分開。

只要IV是具有加密強度的隨機數據,並且每次加密都不同,則IV是否為公共知識並不重要。 但是您需要在另一端使用相同的相應的單獨的IV進行解密。

同樣,正如WDS指出的那樣,您不應執行該XOR操作。 (但我看不到WDS的代碼如何在不保留IV的情況下恢復前16個字節。)

同樣,恕我直言,使用SHA1而不是密碼來生成密鑰也存在安全風險。 您應該使用PBKDF2或類似的密碼來導出密碼。

編輯:正如Artjom B.指出的那樣,由於您的原始代碼XOR是針對第一個明文塊的IV,因此可以通過使用解密時所有0x00字節的IV來恢復前16個字節。 但這僅在密文至少為兩個塊(32字節)的情況下才有效,否則,將出現填充錯誤,因為這會破壞填充。 嘗試在解密中使用全零字節的IV,並確定輸入是否始終至少為16個字節-如果不是,則加密中總會出現陣列末端錯誤。 (您仍然有一個不安全的IV,但是至少您可以使用解密。)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM