C# AES加密字节数组

16

我想要加密一个字节数组。所以我首先在这个网站上尝试了一下。

  • 密钥 = 00000000000000000000000000000000
  • IV = 00000000000000000000000000000000
  • 输入数据 = 1EA0353A7D2947D8BBC6AD6FB52FCA84
  • 类型 = CBC

它计算出以下结果

  • 加密输出 = C5537C8EFFFCC7E152C27831AFD383BA

然后我使用了 System.Security.Cryptography 库进行计算,但是它给了我不同的结果。你能帮我解决这个问题吗?

代码

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

using System.IO;
using System.Security.Cryptography;

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

        byte key_no = 0x00;
        byte[] key = new byte[16] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
        byte[] IV = new byte[16] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
        byte[] rndB = new byte[16] { 0x1E,0xA0,0x35,0x3A,0x7D,0x29,0x47,0xD8,0xBB,0xC6,0xAD,0x6F,0xB5,0x2F,0xCA,0x84 };

        private void Form1_Load(object sender, EventArgs e)
        {
            try
            {
                byte[] res=EncryptStringToBytes_Aes(BitConverter.ToString(rndB), key, IV);
                string res_txt = BitConverter.ToString(res);
                Console.WriteLine(res_txt);
            }
            catch (Exception ex)
            {
                Console.WriteLine("Error: {0}", ex.Message);
            }
        }

        static byte[] EncryptStringToBytes_Aes(byte[] Data, byte[] Key, byte[] IV)
        {
            // Check arguments.
            if (Key == null || Key.Length <= 0)
                throw new ArgumentNullException("Key");
            if (IV == null || IV.Length <= 0)
                throw new ArgumentNullException("IV");
            byte[] encrypted;

            // Create an Aes object
            // with the specified key and IV.
            using (Aes aesAlg = Aes.Create())
            {
                aesAlg.Key = Key;
                aesAlg.IV = IV;
                aesAlg.Mode = CipherMode.CBC;
                aesAlg.BlockSize = 128;
                aesAlg.FeedbackSize = 128;
                aesAlg.KeySize = 128;

                // Create an encryptor to perform the stream transform.
                ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);

                // Create the streams used for encryption.
                using (MemoryStream msEncrypt = new MemoryStream())
                {
                    using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
                    {
                        using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
                        {
                            //Write all data to the stream.
                             swEncrypt.Write(Data);
                        }
                        encrypted = msEncrypt.ToArray();
                    }
                }
            }

            // Return the encrypted bytes from the memory stream.
            return encrypted;    
        }
    }
}

2
为什么要将输入的字节数组转换为字符串? - Magnus
@Magnus 我在这里找到了一个例子 https://learn.microsoft.com/zh-cn/dotnet/api/system.security.cryptography.aes?view=netframework-4.7.2 - BK52
@Magnus 我已经更改了字节数组的函数。这解决了返回长度数据长度的问题。但它仍然与示例有不同的答案。 - BK52
不,只有这一个。 - eocron
@eocron,我更改了代码和数据。你能试着对这个版本进行加密并告诉我结果吗? - BK52
显示剩余3条评论
2个回答

39
该网站声明:
Input Data (It will be padded with zeroes if necessary.)

在密码学中,填充(padding)非常重要。

所以请确保您使用以下代码:

aes.Padding = PaddingMode.Zeros;

没有填充的话,对于此案例你会得到更长的结果,因为需要填补字节。

编辑:对于真实情况,你应该保持默认值:PKCS #7。 @WimCoenen有很好的观点和建议,请查看评论区。

你的代码还有另一个问题:您在设置密钥和IV之前设置它们的大小。

这是错误的

        aesAlg.Key = Key;
        aesAlg.IV = IV;
        aesAlg.Mode = CipherMode.CBC;
        aesAlg.BlockSize = 128;
        aesAlg.FeedbackSize = 128;
        aesAlg.KeySize = 128;

这是正确的顺序:

        aesAlg.Mode = CipherMode.CBC;
        aesAlg.KeySize = 128;
        aesAlg.BlockSize = 128;
        aesAlg.FeedbackSize = 128;
        aesAlg.Padding = PaddingMode.Zeros;
        aesAlg.Key = key;
        aesAlg.IV = iv;

你的代码还有一个问题,就是你正在使用StreamWriter来写入加密流:

using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
{
    //Write all data to the stream.
    swEncrypt.Write(Data);
}

StreamWriter 可能会搞砸一切。它是为特定编码的文本编写而设计的。

请查看下面的代码,以获取适用于您情况的实现。

public class AesCryptographyService 
{
    public byte[] Encrypt(byte[] data, byte[] key, byte[] iv)
    {
        using (var aes = Aes.Create())
        {
            aes.KeySize = 128;
            aes.BlockSize = 128;
            aes.Padding = PaddingMode.Zeros;

            aes.Key = key;
            aes.IV = iv;

            using (var encryptor = aes.CreateEncryptor(aes.Key, aes.IV))
            {
                return PerformCryptography(data, encryptor);
            }
        }
    }

    public byte[] Decrypt(byte[] data, byte[] key, byte[] iv)
    {
        using (var aes = Aes.Create())
        {
            aes.KeySize = 128;
            aes.BlockSize = 128;
            aes.Padding = PaddingMode.Zeros;

            aes.Key = key;
            aes.IV = iv;

            using (var decryptor = aes.CreateDecryptor(aes.Key, aes.IV))
            {
                return PerformCryptography(data, decryptor);
            }
        }
    }

    private byte[] PerformCryptography(byte[] data, ICryptoTransform cryptoTransform)
    {
        using (var ms = new MemoryStream())
        using (var cryptoStream = new CryptoStream(ms, cryptoTransform, CryptoStreamMode.Write))
        {
            cryptoStream.Write(data, 0, data.Length);
            cryptoStream.FlushFinalBlock();

            return ms.ToArray();
        }
    }
}

var key = new byte[16] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
var iv = new byte[16] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
var input = new byte[16] { 0x1E,0xA0,0x35,0x3A,0x7D,0x29,0x47,0xD8,0xBB,0xC6,0xAD,0x6F,0xB5,0x2F,0xCA,0x84 };

var crypto = new AesCryptographyService();

var encrypted = crypto.Encrypt(input, key, iv);
var str = BitConverter.ToString(encrypted).Replace("-", "");
Console.WriteLine(str);

它将输出结果:
C5537C8EFFFCC7E152C27831AFD383BA

这与您引用的网站上的相同:

image

编辑:

我已更改您的函数,以便正确输出结果:

static byte[] EncryptStringToBytes_Aes(byte[] data, byte[] key, byte[] iv)
{
    // Check arguments.
    if (key == null || key.Length <= 0)
        throw new ArgumentNullException("key");
    if (iv == null || iv.Length <= 0)
        throw new ArgumentNullException("iv");
    byte[] encrypted;

    // Create an Aes object
    // with the specified key and IV.
    using (Aes aesAlg = Aes.Create())
    {
        aesAlg.Mode = CipherMode.CBC;
        aesAlg.KeySize = 128;
        aesAlg.BlockSize = 128;
        aesAlg.FeedbackSize = 128;
        aesAlg.Padding = PaddingMode.Zeros;
        aesAlg.Key = key;
        aesAlg.IV = iv;

        // Create an encryptor to perform the stream transform.
        ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);

        // Create the streams used for encryption.
        using (MemoryStream msEncrypt = new MemoryStream())
        {
            using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
            {
                csEncrypt.Write(data, 0, data.Length);
                csEncrypt.FlushFinalBlock();

                encrypted = msEncrypt.ToArray();
            }
        }
    }

    // Return the encrypted bytes from the memory stream.
    return encrypted;    
}

6
+1 很好的发现。但请注意,使用PKCS #7作为默认填充方式是有充分理由的:如果使用零填充,则解密时会得到额外的零字节,因为无法区分明文末尾的零字节和填充添加的零字节,除非在加密前自己添加包装结构来对明文进行处理。 - Wim Coenen
@WimCoenen没错。已经把你的建议加入回答中了。 - user6440521
@BART 我已经实现了代码,但是在解密byte[]时我收到了填充的零(我的数据是11个字节,块大小为128):原始数据:0A0963657274206E616D65,回路数据:0A0963657274206E616D650000000000。请问如何避免这种情况? - omriman12
为什么你要显式调用FlushFinalBlock(),当使用"using"时隐式Dispose()也会执行这个操作呢? - tipa

1
输入数据不同,因此结果也不同。 在网站上,您的纯文本为'C5537C8EFFFCC7E152C27831AFD383BA',而在代码中为'B969FDFE56FD91FC9DE6F6F213B8FD1E'。

感谢您的关注和关心。我已经编辑了代码和示例,但是示例和我的代码仍然有不同的结果。 - BK52

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接