【问题标题】:C# Send byte[] to server, decrypt content and send it as byte[] back to clientC#将字节[]发送到服务器,解密内容并将其作为字节[]发送回客户端
【发布时间】:2018-12-07 07:40:34
【问题描述】:

我的问题如下:

我有一个通过套接字连接的客户端/服务器应用程序。我的客户的任务是按字节向服务器发送文件。服务器获取字节,解密它们,将其发送回客户端,然后将它们写入磁盘上的新文件中。

每次在这行代码中出现服务器端异常(System.Security.Cryptography.Exception: Padding is invalid and cannot be removed):plaintext = sr.ReadToEnd();

有人可以帮我解决我的问题吗?

这里是解密代码:

public byte[] Dec(byte[] content, byte[] Key, byte[] IV, int fileLength, string filepath, int chunkSize, int bytesToRead)
    {
        byte[] contentDec;
        string plaintext = null;
        System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
        if (Key == null || Key.Length <= 0)
            throw new ArgumentNullException("Key");
        if (IV == null || IV.Length <= 0)
            throw new ArgumentNullException("IV");

        using (RijndaelManaged rijAlg = new RijndaelManaged())
        {
            rijAlg.Key = Key;
            rijAlg.IV = IV;

            ICryptoTransform decryptor = rijAlg.CreateDecryptor(rijAlg.Key, rijAlg.IV);


            using (MemoryStream ms = new MemoryStream(content))
            {
                using (CryptoStream cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read))
                {
                    using (StreamReader sr = new StreamReader(cs))
                    {
                        plaintext = sr.ReadToEnd();
                        cs.FlushFinalBlock();
                    }
                    contentDec = encoding.GetBytes(plaintext);
                }
            }
        }
        return contentDec;
    }

这是我的加密代码:

    public byte[] Enc(byte[] content,byte[] Key, byte[] IV, int fileLength,string filepath, int chunkSize, int bytesToRead)
    {
        byte[] contentEnc;
        if (Key == null || Key.Length <= 0)
            throw new ArgumentNullException("Key");
        if (IV == null || IV.Length <= 0)
            throw new ArgumentNullException("IV");
        using (RijndaelManaged rijAlg = new RijndaelManaged())
        {
            rijAlg.Key = Key;
            rijAlg.IV = IV;
            ICryptoTransform encryptor = rijAlg.CreateEncryptor(rijAlg.Key, rijAlg.IV);
            using (MemoryStream ms = new MemoryStream())
            {
                using (CryptoStream cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
                {
                    using (StreamWriter sw = new StreamWriter(cs))
                    {
                        sw.Write(content);
                    }
                    contentEnc = ms.ToArray();
                }
            }
        }
        return contentEnc;
    }

在客户端我这样调用加密方法

        int chunkSize = 1024;
        byte[] chunk = new byte[chunkSize];
        using (FileStream fileReader = new FileStream(plainPath, FileMode.Open, FileAccess.Read))
        using (FileStream filewriter = new FileStream(pathEncrypt, FileMode.Create, FileAccess.ReadWrite))
        using (BinaryReader binaryReader = new BinaryReader(fileReader))
        using (RijndaelManaged myRijndael = new RijndaelManaged())
        {
            myRijndael.GenerateKey();
            myRijndael.GenerateIV();
            Key = myRijndael.Key;
            IV = myRijndael.IV;
            int bytesToRead = (int)fileReader.Length;
            do
            {
                chunk = service.Enc(binaryReader.ReadBytes(chunkSize), Key, IV,(int)fileReader.Length, 
                    fileReader.Name, chunkSize, bytesToRead);
                filewriter.Write(chunk, 0, chunk.Length);
                bytesToRead -= chunkSize;
            } while (bytesToRead > 0);
        }

Key 和 IV 被声明为私有字节[]

在客户端我这样调用解密方法

        int chunkSize = 1024;
        byte[] chunk = new byte[chunkSize];
        using (FileStream fileReader = new FileStream(pathEncrypt, FileMode.Open, FileAccess.Read))
        using (FileStream filewriter = new FileStream(pathDecrypt, FileMode.Create, FileAccess.ReadWrite))
        using (BinaryReader binaryReader = new BinaryReader(fileReader))
        {
            int bytesToRead = (int)fileReader.Length;

            do
            {
                chunk = service.Dec(binaryReader.ReadBytes(chunkSize), Key, IV, (int)fileReader.Length, 
                    fileReader.Name, chunkSize, bytesToRead);
                filewriter.Write(chunk, 0, chunk.Length);
                bytesToRead -= chunkSize;
            } while (bytesToRead > 0);
        }

编辑:这是我在客户端和服务器之间建立的连接。

服务器: var host = new ServiceHost(typeof(Service), 新的 Uri("net.pipe://localhost"));

        host.AddServiceEndpoint(typeof(TiService),
                                new NetNamedPipeBinding(), "TestService");
        host.Open();
        Console.WriteLine("Server connection established...");
        Console.ReadKey();

客户:

var callback = new Callback();
var context = new InstanceContext(callback);
var pipeFactory =
new DuplexChannelFactory<TiService>(context,
new NetNamedPipeBinding(),
new EndpointAddress("net.pipe://localhost/TestService"));

service = pipeFactory.CreateChannel();
service.Connect();

【问题讨论】:

  • 编辑您的问题以包含加密代码。
  • 编辑我的问题
  • 您的加密代码不一定正确,因为在提交数据之前调用ToArray 可能会导致部分流...
  • 我该如何解决或绕过它?
  • 您可能需要显式设置填充策略来解决此问题。查看this question on MSDN的答案

标签: c# sockets encryption client-server file-transfer


【解决方案1】:

您的问题始于在加密中使用StreamWriter。它用于编写文本文件,而不是任意文件。当您调用 sw.Write(content) 时,它只是调用 content.ToString(),它返回“System.Byte[]”,而不是您可能期望的,数组的每个字节。要修复它,只需编写 CryptoStream,无需使用 StreamWriter,如下所示:

using (var rijAlg = new AesCng())
{
    rijAlg.Key = Key;
    rijAlg.IV = IV;
    ICryptoTransform encryptor = rijAlg.CreateEncryptor();
    using (MemoryStream ms = new MemoryStream())
    {
        using (CryptoStream cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
        {
            cs.Write(content, 0, content.Length);               
        }
        contentEnc = ms.ToArray();
    }
}

您可能注意到我使用 AesCng 而不是 RijndaelManaged。为什么?因为它在我的测试中要快得多,而且除非你真的需要非标准块,否则使用 RijndaelManaged 没有任何好处。另外,我使用无参数的 CreateEncryptor,因为您已经在前面的行中设置了 Key & IV。

解密过程相同。您不应该将它们视为文本,因此:

var buffer = new byte[content.Length]; //at first its size is actual size+padding

using (var rijAlg = new AesCng())
{
    rijAlg.Key = Key;
    rijAlg.IV = IV;

    ICryptoTransform decryptor = rijAlg.CreateDecryptor();

    using (MemoryStream ms = new MemoryStream(content))
    {
        using (CryptoStream cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read))
        {
            var actualSize = cs.Read(buffer, 0, content.Length);
            //we write the decrypted content to the buffer, and get the actual size

            Array.Resize(ref buffer, actualSize);
            //then we resize the buffer to the actual size
        }
    }
}
return buffer;

此外,您对 Enc 和 Dec 的使用是不必要的复杂。它已经能够自己处理整个文件。所以要加密文件,只需使用

var original = File.ReadAllBytes("originalPath");
var enc = Enc(original, rM.Key, rM.IV);
File.WriteAllBytes("encryptedPath", enc);

要解密文件,只需使用

var enc = File.ReadAllBytes("encryptedPath");
var dec = Dec(enc, rM.Key, rM.IV);
File.WriteAllBytes("decryptedPath", dec);

如您所见,我在 Enc 和 Dec 上丢弃了 fileLength、filepath、chunkSize 和 bytesToRead,因为您当前的代码实际上并没有使用它们。我已经尝试过使用 ASCII、Unicode 和 UTF-8 上的短文本文件以及大型二进制文件的代码,所有加密和解密都成功地使用最终解密文件上的相同哈希值。

编辑:

将代码转换为直接文件流写入事件实际上使一切变得如此简单。

public static void Transform(string source, string target, ICryptoTransform transf)
{
    var bufferSize = 65536;
    var buffer = new byte[bufferSize];


    using (var sourceStream = new FileStream(source, FileMode.Open))
    {
        using (var targetStream = new FileStream(target, FileMode.OpenOrCreate))
        {
            using (CryptoStream cs = new CryptoStream(targetStream, transf, CryptoStreamMode.Write))
            {
                var bytesRead = 0;
                do
                {
                    bytesRead = sourceStream.Read(buffer, 0, bufferSize);
                    cs.Write(buffer, 0, bytesRead);
                } while (bytesRead != 0);
            }
        }

    }


}

public static void Enc(string source, byte[] Key, byte[] IV, string target)
{
    using (var rijAlg = new AesCng())
    {
        rijAlg.Key = Key;
        rijAlg.IV = IV;
        ICryptoTransform encryptor = rijAlg.CreateEncryptor();
        Transform(source, target, encryptor);
    }

}
public static void Dec(string source, byte[] Key, byte[] IV, string target)
{
    using (var rijAlg = new AesCng())
    {
        rijAlg.Key = Key;
        rijAlg.IV = IV;
        ICryptoTransform decryptor = rijAlg.CreateDecryptor();
        Transform(source, target, decryptor);
    }

}

用法是:

Enc(@"originalPath", key, iv, @"encryptedPath");
Dec(@"encrypedPath", key, iv, @"decryptedPath");

【讨论】:

  • 感谢您的回答!我会测试它,但我的问题是:大文件 > 10GB 看起来如何?因此,我需要一个解决方案将其按字节发送到服务器并且不读取所有字节(您的代码:var originial = FIle.ReadAllBytes("originalPath"); 或者我的想法错了吗?
  • 我实现了 System.Security.Cryptography 但我的程序没有得到 AesCng()...
  • Enc(original, rM.Key, rM.IV) ==> 你在哪里设置rM,这是哪个Object?
  • 如果您没有获得 AesCng,也许您是 .NET 4.6 之前的版本?只需替换为 AesCryptoServiceProvider 即可。至于 rM,忽略它们,使用你自己的 key & iv 就像你原来使用的那样
  • 谢谢!你的答案对我有用!但是,如果我想加密一个 .jpg,那么我会得到以下异常: System.ServiceModel.CommunicationException:写入管道时出错:管道正在关闭。 (232, 0xe8)。我该如何解决这个问题?
猜你喜欢
  • 2012-05-14
  • 1970-01-01
  • 2011-10-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多