为什么我在解密这个字节数组时得到 System.Security.Cryptography.CryptographicException

Why do I get a System.Security.Cryptography.CryptographicException during decrypting this byte array

我刚开始使用 C# 中的密码学,并尝试先加密然后解密文件。但是在解密过程中,在函数中 private static byte[] Decrypt(byte[] inputBuffer) , 在 byte[] outputBuffer = transform.TransformFinalBlock(inputBuffer, 0, inputBuffer.Length); 我收到以下异常: System.Security.Cryptography.CryptographicException:"Invalid data"。 但这是为什么呢?

这是读取文件的函数:

private static void DecryptFile()
{
    byte[] buffer = new byte[4096];
    byte[] decryptionBuffer = new byte[4096];
    int bytesRead = 0;
    using (FileStream inputStream = File.OpenRead(Environment.CurrentDirectory + "\Encrypted.txt"))
    {
        using (FileStream outputStream = File.Create(Environment.CurrentDirectory + "\Decrypt.mp3"))
        {
            while ((bytesRead = inputStream.Read(buffer, 0, buffer.Length)) > 0)
            {
                decryptionBuffer = Decrypt(buffer);
                outputStream .Write(decryptionBuffer, 0, decryptionBuffer.Length);
            }
        }
        Console.WriteLine("Done.");
    }
}

这是解密文件、密钥和初始化向量的函数:

private static byte[] key = new byte[8] { 1, 2, 3, 4, 5, 6, 7, 8 };
private static byte[] iv = new byte[8] { 1, 2, 3, 4, 5, 6, 7, 8 };

private static byte[] Decrypt(byte[] inputBuffer)
{
    SymmetricAlgorithm algorithm = DES.Create();
    ICryptoTransform transform = algorithm.CreateDecryptor(key, iv);
    //here the exception is triggered
    byte[] outputBuffer = transform.TransformFinalBlock(inputBuffer, 0, inputBuffer.Length);
    return outputBuffer;
}

文件是这样加密的:

private static void EncryptFile()
{
    byte[] buffer = new byte[4096];
    byte[] enryptionBuffer = new byte[4096];
    int bytesRead = 0;
    using (FileStream inputStream = File.OpenRead(Environment.CurrentDirectory + "\Test.txt"))
    {
        using (FileStream outputStream = File.Create(Environment.CurrentDirectory + "\Encrypted.txt"))
        {
            while ((bytesRead = inputStream.Read(buffer, 0, buffer.Length)) > 0)
            {
                encryptionBuffer = Encrypt(buffer);
                outputStream .Write(encryptionBuffer, 0, encryptionBuffer.Length);
            }
        }
        Console.WriteLine("Done.");
    }
}

//Key and initialization vector are the same
private static byte[] key = new byte[8] { 1, 2, 3, 4, 5, 6, 7, 8 };
private static byte[] iv = new byte[8] { 1, 2, 3, 4, 5, 6, 7, 8 };

private static byte[] Encrypt(byte[] inputBuffer)
{
    SymmetricAlgorithm algorithm = DES.Create();
    ICryptoTransform transform = algorithm.CreateEncryptor(key, iv);
    byte[] outputBuffer = transform.TransformFinalBlock(inputBuffer, 0, inputBuffer.Length);
    return outputBuffer;
}

您的加密代码产生比输入缓冲区更大的输出缓冲区。如果你一次独立加密4096字节,它会产生4104字节的输出1。如果您希望继续独立加密文件的每个块,则需要将解密代码更改为在分块工作时使用 4104 字节缓冲区。

理想情况下,这 "encrypt each block separately" 不是必需的。如果是这种情况,请创建 transform 对象 一次 ,而不是每次通过循环创建一次。并使用 Transform 而不是 TransformFinalBlock 直到你知道你已经到达文件的末尾(注意它们 return 但是非常不同的东西)。

您还忽略了 bytesRead,它告诉您 您的缓冲区中有多少是有用的数据。你也需要使用它并且不要让你的最终加密轮是 x 很多字节,这是文件的最后字节和 bufferSize - x 字节 previous 文件中的数据块。

我可能会转而创建一个 CryptoStream 来包装您的 FileStream 对象之一,然后使用 Stream.CopyTo 或道德等价物来完成这项工作。让图书馆担心管理缓冲区、循环等。

最后,理想情况下,您认识到现在是 2019 年,编写使用 DES 进行加密的新代码还很不合适2


1这个程序,如果你在Console.ReadLine行设置断点,c包含4104字节:

using System;
using System.Security.Cryptography;
using System.Threading.Tasks;

class Program
{
    static async Task Main(string[] args)
    {
        var b = new byte[4096];
        var c = Encrypt(b);
        Console.ReadLine();
    }

    private static byte[] key = new byte[8] { 1, 2, 3, 4, 5, 6, 7, 8 };
    private static byte[] iv = new byte[8] { 1, 2, 3, 4, 5, 6, 7, 8 };

    private static byte[] Encrypt(byte[] inputBuffer)
    {
        SymmetricAlgorithm algorithm = DES.Create();
        ICryptoTransform transform = algorithm.CreateEncryptor(key, iv);
        byte[] outputBuffer = transform.TransformFinalBlock(
                                inputBuffer,
                                0,
                                inputBuffer.Length);
        return outputBuffer;
    }
}

2所以我的 EnryptFile 整体是:

private static void EncryptFile()
{
    using (var inputStream = File.OpenRead(Environment.CurrentDirectory + "\Test.txt"))
    using (var outputStream = File.Create(Environment.CurrentDirectory + "\Encrypted.txt"))
    using (var aes = Aes.Create())
    using (var cStream = new CryptoStream(
                               inputStream,
                               aes.CreateEncryptor(key, iv),
                               CryptoStreamMode.Read))
    {
        cStream.CopyTo(outputStream);
    }
}

或者一个 async 变体,它使用 await cStream.CopyToAsync(outputStream); 作为最内层的语句。 DecryptFile 将得到类似的简化。

我遇到了同样的问题然后我为 encryption/decryption 创建了一个自定义函数并且使用这个函数还支持大文件 因为我们正在读写一个文件块按块。有一个 EncryptMode 就是你想要通过这种方法做什么,如果你想加密然后发送 _mode 作为 _mode.ENCRYPT 如果你想要解密然后发送 _mode 作为 _mode.DECRYPT.

私有枚举 EncryptMode {加密,解密};

public void encryptDecryptChunkByChunk(字符串_inputPath,字符串_outputPath,字符串_encryptionKey,EncryptMode _mode,字符串_initVector) {

        string _out = "";// output string
        //_encryptionKey = MD5Hash (_encryptionKey);
        _pwd = Encoding.UTF8.GetBytes(_encryptionKey);
        _ivBytes = Encoding.UTF8.GetBytes(_initVector);

        int len = _pwd.Length;
        if (len > _key.Length)
        {
            len = _key.Length;
        }
        int ivLenth = _ivBytes.Length;
        if (ivLenth > _iv.Length)
        {
            ivLenth = _iv.Length;
        }

        Array.Copy(_pwd, _key, len);
        Array.Copy(_ivBytes, _iv, ivLenth);
        _rcipher.Key = _key;
        _rcipher.IV = _iv;

        if (_mode.Equals(EncryptMode.ENCRYPT))
        {
            //encrypt
            using (FileStream fs = new FileStream(_inputPath, FileMode.Open, FileAccess.Read))
            {
                using (BinaryReader br = new BinaryReader(fs, new ASCIIEncoding()))
                {
                    System.IO.StreamWriter file = new System.IO.StreamWriter(_outputPath);
                    try
                    {
                        byte[] chunk;

                        chunk = br.ReadBytes(CHUNK_SIZE);
                        while (chunk.Length > 0)
                        {
                            var base641 = Convert.ToBase64String(chunk);
                            //DumpBytes(chunk, chunk.Length);
                            chunk = br.ReadBytes(CHUNK_SIZE);
                            var base64 = Convert.ToBase64String(chunk);

                            byte[] plainText = _rcipher.CreateEncryptor().TransformFinalBlock(_enc.GetBytes(base641), 0, base641.Length);
                            var bas64Encrypted = Convert.ToBase64String(plainText);
                            //fsCrypt.Write(bas64Encrypted);
                            file.WriteLine(bas64Encrypted);
                        }
                        file.Close();
                    }
                    catch (Exception ex)
                    {
                        file.Close();
                    }
                }
            }
        }
        if (_mode.Equals(EncryptMode.DECRYPT))
        {
            FileStream fsOut = new FileStream(_outputPath, FileMode.OpenOrCreate, FileAccess.Write);
            try
            {
                foreach (string line in File.ReadLines(_inputPath))
                {
                    // Process your line here....
                    var p = line;
                    var x2 = Convert.FromBase64String(p);
                    byte[] plainText = _rcipher.CreateDecryptor().TransformFinalBlock(x2, 0, x2.Length);
                    var y1 = _enc.GetString(plainText);
                    var y2 = Convert.FromBase64String(y1);
                    fsOut.Write(y2, 0, y2.Length);
                }
                fsOut.Close();
            }
            catch (Exception ex)
            {
                fsOut.Close();
            }
        }
        _rcipher.Dispose();
    }