为什么在解密此字节数组期间出现System.Security.Cryptography.CryptographicException

时间:2019-01-08 12:25:26

标签: c# encryption stream cryptography des

我刚开始使用C#中的密码学,试图先加密然后解密文件。但是在解密过程中      私有静态字节[]解密(byte [] inputBuffer) , 在 byte [] outputBuffer = transform.TransformFinalBlock(inputBuffer,0,inputBuffer.Length); 我收到以下异常: System.Security.Cryptography.CryptographicException:“无效的数据”。 但是为什么呢?

这是读取文件的功能:

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;
}

2 个答案:

答案 0 :(得分:2)

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

尽管理想情况下,这不是“分别加密每个块”的要求。在这种情况下,请一次创建transform对象 ,而不是每次循环都创建一次。然后使用Transform而不是TransformFinalBlock,直到您知道文件的末尾为止(请注意,它们返回的内容非常不同)。

您还忽略了bytesRead,它告诉您您的缓冲区有多少被有用数据填充。您还需要使用它,并且不要使最后的加密回合x个字节(即文件的最后一个字节)和{em> previous 块的bufferSize - x个字节。文件中的数据。

我可能想创建一个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将得到类似的简化。

答案 1 :(得分:0)

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

私有枚举 EncryptMode { ENCRYPT, DECRYPT };

public void encryptDecryptChunkByChunk(string _inputPath, string _outputPath, string _encryptionKey, EncryptMode _mode, string _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();
    }